diff -Nur linux-2.4.30/arch/mips/au1000/common/au1xxx_irqmap.c linux-2.4.30-linux-mips.org/arch/mips/au1000/common/au1xxx_irqmap.c
--- linux-2.4.30/arch/mips/au1000/common/au1xxx_irqmap.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/common/au1xxx_irqmap.c	2005-05-12 17:57:05.000000000 +0200
@@ -172,14 +172,14 @@
 	{ AU1550_PSC1_INT, INTC_INT_HIGH_LEVEL, 0},
 	{ AU1550_PSC2_INT, INTC_INT_HIGH_LEVEL, 0},
 	{ AU1550_PSC3_INT, INTC_INT_HIGH_LEVEL, 0},
-	{ AU1550_TOY_INT, INTC_INT_RISE_EDGE, 0 },
-	{ AU1550_TOY_MATCH0_INT, INTC_INT_RISE_EDGE, 0 },
-	{ AU1550_TOY_MATCH1_INT, INTC_INT_RISE_EDGE, 0 },
-	{ AU1550_TOY_MATCH2_INT, INTC_INT_RISE_EDGE, 1 },
-	{ AU1550_RTC_INT, INTC_INT_RISE_EDGE, 0 },
-	{ AU1550_RTC_MATCH0_INT, INTC_INT_RISE_EDGE, 0 },
-	{ AU1550_RTC_MATCH1_INT, INTC_INT_RISE_EDGE, 0 },
-	{ AU1550_RTC_MATCH2_INT, INTC_INT_RISE_EDGE, 0 },
+	{ AU1000_TOY_INT, INTC_INT_RISE_EDGE, 0 },
+	{ AU1000_TOY_MATCH0_INT, INTC_INT_RISE_EDGE, 0 },
+	{ AU1000_TOY_MATCH1_INT, INTC_INT_RISE_EDGE, 0 },
+	{ AU1000_TOY_MATCH2_INT, INTC_INT_RISE_EDGE, 1 },
+	{ AU1000_RTC_INT, INTC_INT_RISE_EDGE, 0 },
+	{ AU1000_RTC_MATCH0_INT, INTC_INT_RISE_EDGE, 0 },
+	{ AU1000_RTC_MATCH1_INT, INTC_INT_RISE_EDGE, 0 },
+	{ AU1000_RTC_MATCH2_INT, INTC_INT_RISE_EDGE, 0 },
 	{ AU1550_NAND_INT, INTC_INT_RISE_EDGE, 0},
 	{ AU1550_USB_DEV_REQ_INT, INTC_INT_HIGH_LEVEL, 0 },
 	{ AU1550_USB_DEV_SUS_INT, INTC_INT_RISE_EDGE, 0 },
@@ -200,14 +200,14 @@
 	{ AU1200_PSC1_INT, INTC_INT_HIGH_LEVEL, 0},
 	{ AU1200_AES_INT, INTC_INT_HIGH_LEVEL, 0},
 	{ AU1200_CAMERA_INT, INTC_INT_HIGH_LEVEL, 0},
-	{ AU1200_TOY_INT, INTC_INT_RISE_EDGE, 0 },
-	{ AU1200_TOY_MATCH0_INT, INTC_INT_RISE_EDGE, 0 },
-	{ AU1200_TOY_MATCH1_INT, INTC_INT_RISE_EDGE, 0 },
-	{ AU1200_TOY_MATCH2_INT, INTC_INT_RISE_EDGE, 1 },
-	{ AU1200_RTC_INT, INTC_INT_RISE_EDGE, 0 },
-	{ AU1200_RTC_MATCH0_INT, INTC_INT_RISE_EDGE, 0 },
-	{ AU1200_RTC_MATCH1_INT, INTC_INT_RISE_EDGE, 0 },
-	{ AU1200_RTC_MATCH2_INT, INTC_INT_RISE_EDGE, 0 },
+	{ AU1000_TOY_INT, INTC_INT_RISE_EDGE, 0 },
+	{ AU1000_TOY_MATCH0_INT, INTC_INT_RISE_EDGE, 0 },
+	{ AU1000_TOY_MATCH1_INT, INTC_INT_RISE_EDGE, 0 },
+	{ AU1000_TOY_MATCH2_INT, INTC_INT_RISE_EDGE, 1 },
+	{ AU1000_RTC_INT, INTC_INT_RISE_EDGE, 0 },
+	{ AU1000_RTC_MATCH0_INT, INTC_INT_RISE_EDGE, 0 },
+	{ AU1000_RTC_MATCH1_INT, INTC_INT_RISE_EDGE, 0 },
+	{ AU1000_RTC_MATCH2_INT, INTC_INT_RISE_EDGE, 0 },
 	{ AU1200_NAND_INT, INTC_INT_RISE_EDGE, 0},
 	{ AU1200_USB_INT, INTC_INT_HIGH_LEVEL, 0 },
 	{ AU1200_LCD_INT, INTC_INT_HIGH_LEVEL, 0},
diff -Nur linux-2.4.30/arch/mips/au1000/common/cputable.c linux-2.4.30-linux-mips.org/arch/mips/au1000/common/cputable.c
--- linux-2.4.30/arch/mips/au1000/common/cputable.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/common/cputable.c	2005-05-12 17:57:05.000000000 +0200
@@ -39,7 +39,8 @@
     { 0xffffffff, 0x02030203, "Au1100 BD", 0, 1 },
     { 0xffffffff, 0x02030204, "Au1100 BE", 0, 1 },
     { 0xffffffff, 0x03030200, "Au1550 AA", 0, 1 },
-    { 0xffffffff, 0x04030200, "Au1200 AA", 0, 1 },
+    { 0xffffffff, 0x04030200, "Au1200 AB", 0, 0 },
+    { 0xffffffff, 0x04030201, "Au1200 AC", 0, 0 },
     { 0x00000000, 0x00000000, "Unknown Au1xxx", 1, 0 },
 };
 
diff -Nur linux-2.4.30/arch/mips/au1000/common/dbdma.c linux-2.4.30-linux-mips.org/arch/mips/au1000/common/dbdma.c
--- linux-2.4.30/arch/mips/au1000/common/dbdma.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/common/dbdma.c	2005-05-12 17:57:05.000000000 +0200
@@ -41,6 +41,8 @@
 #include <asm/au1xxx_dbdma.h>
 #include <asm/system.h>
 
+#include <linux/module.h>
+
 #if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200)
 
 /*
@@ -60,37 +62,10 @@
 */
 #define ALIGN_ADDR(x, a)	((((u32)(x)) + (a-1)) & ~(a-1))
 
-static volatile dbdma_global_t *dbdma_gptr = (dbdma_global_t *)DDMA_GLOBAL_BASE;
-static int dbdma_initialized;
+static dbdma_global_t *dbdma_gptr = (dbdma_global_t *)DDMA_GLOBAL_BASE;
+static int dbdma_initialized=0;
 static void au1xxx_dbdma_init(void);
 
-typedef struct dbdma_device_table {
-	u32		dev_id;
-	u32		dev_flags;
-	u32		dev_tsize;
-	u32		dev_devwidth;
-	u32		dev_physaddr;		/* If FIFO */
-	u32		dev_intlevel;
-	u32		dev_intpolarity;
-} dbdev_tab_t;
-
-typedef struct dbdma_chan_config {
-	u32			chan_flags;
-	u32			chan_index;
-	dbdev_tab_t		*chan_src;
-	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;
-	void			*chan_callparam;
-	void (*chan_callback)(int, void *, struct pt_regs *);
-} chan_tab_t;
-
-#define	DEV_FLAGS_INUSE		(1 << 0)
-#define	DEV_FLAGS_ANYUSE	(1 << 1)
-#define DEV_FLAGS_OUT		(1 << 2)
-#define DEV_FLAGS_IN		(1 << 3)
-
 static dbdev_tab_t dbdev_tab[] = {
 #ifdef CONFIG_SOC_AU1550
 	/* UARTS */
@@ -156,13 +131,13 @@
 	{ DSCR_CMD0_MAE_BOTH, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
 	{ DSCR_CMD0_LCD, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
 
-	{ DSCR_CMD0_SDMS_TX0, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 },
-	{ DSCR_CMD0_SDMS_RX0, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
-	{ DSCR_CMD0_SDMS_TX1, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 },
-	{ DSCR_CMD0_SDMS_RX1, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
+	{ DSCR_CMD0_SDMS_TX0, DEV_FLAGS_OUT, 4, 8, 0x10600000, 0, 0 },
+	{ DSCR_CMD0_SDMS_RX0, DEV_FLAGS_IN, 4, 8, 0x10600004, 0, 0 },
+	{ DSCR_CMD0_SDMS_TX1, DEV_FLAGS_OUT, 4, 8, 0x10680000, 0, 0 },
+	{ DSCR_CMD0_SDMS_RX1, DEV_FLAGS_IN, 4, 8, 0x10680004, 0, 0 },
 
-	{ DSCR_CMD0_AES_TX, DEV_FLAGS_OUT, 0, 0, 0x00000000, 0, 0 },
-	{ DSCR_CMD0_AES_RX, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
+	{ DSCR_CMD0_AES_RX, DEV_FLAGS_IN , 4, 32, 0x10300008, 0, 0 },
+	{ DSCR_CMD0_AES_TX, DEV_FLAGS_OUT, 4, 32, 0x10300004, 0, 0 },
 
 	{ DSCR_CMD0_PSC0_TX, DEV_FLAGS_OUT, 0, 0, 0x11a0001c, 0, 0 },
 	{ DSCR_CMD0_PSC0_RX, DEV_FLAGS_IN, 0, 0, 0x11a0001c, 0, 0 },
@@ -172,9 +147,9 @@
 	{ DSCR_CMD0_PSC1_RX, DEV_FLAGS_IN, 0, 0, 0x11b0001c, 0, 0 },
 	{ DSCR_CMD0_PSC1_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
 
-	{ DSCR_CMD0_CIM_RXA, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
-	{ DSCR_CMD0_CIM_RXB, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
-	{ DSCR_CMD0_CIM_RXC, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
+	{ DSCR_CMD0_CIM_RXA, DEV_FLAGS_IN, 0, 32, 0x14004020, 0, 0 },
+	{ DSCR_CMD0_CIM_RXB, DEV_FLAGS_IN, 0, 32, 0x14004040, 0, 0 },
+	{ DSCR_CMD0_CIM_RXC, DEV_FLAGS_IN, 0, 32, 0x14004060, 0, 0 },
 	{ DSCR_CMD0_CIM_SYNC, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
 
 	{ DSCR_CMD0_NAND_FLASH, DEV_FLAGS_IN, 0, 0, 0x00000000, 0, 0 },
@@ -183,6 +158,24 @@
 
 	{ DSCR_CMD0_THROTTLE, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
 	{ DSCR_CMD0_ALWAYS, DEV_FLAGS_ANYUSE, 0, 0, 0x00000000, 0, 0 },
+
+	/* Provide 16 user definable device types */
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
+	{ 0, 0, 0, 0, 0, 0, 0 },
 };
 
 #define DBDEV_TAB_SIZE (sizeof(dbdev_tab) / sizeof(dbdev_tab_t))
@@ -202,6 +195,30 @@
 	return NULL;
 }
 
+u32
+au1xxx_ddma_add_device(dbdev_tab_t *dev)
+{
+	u32 ret = 0;
+	dbdev_tab_t *p=NULL;
+	static u16 new_id=0x1000;
+
+	p = find_dbdev_id(0);
+	if ( NULL != p )
+	{
+		memcpy(p, dev, sizeof(dbdev_tab_t));
+ 		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;
+}
+EXPORT_SYMBOL(au1xxx_ddma_add_device);
+
 /* Allocate a channel and return a non-zero descriptor if successful.
 */
 u32
@@ -214,7 +231,7 @@
 	int		i;
 	dbdev_tab_t	*stp, *dtp;
 	chan_tab_t	*ctp;
-	volatile au1x_dma_chan_t *cp;
+	au1x_dma_chan_t *cp;
 
 	/* We do the intialization on the first channel allocation.
 	 * We have to wait because of the interrupt handler initialization
@@ -224,9 +241,6 @@
 		au1xxx_dbdma_init();
 	dbdma_initialized = 1;
 
-	if ((srcid > DSCR_NDEV_IDS) || (destid > DSCR_NDEV_IDS))
-		return 0;
-	
 	if ((stp = find_dbdev_id(srcid)) == NULL) return 0;
 	if ((dtp = find_dbdev_id(destid)) == NULL) return 0;
 
@@ -268,9 +282,9 @@
 				/* If kmalloc fails, it is caught below same
 				 * as a channel not available.
 				 */
-				ctp = (chan_tab_t *)kmalloc(sizeof(chan_tab_t), GFP_KERNEL);
+				ctp = (chan_tab_t *)
+					kmalloc(sizeof(chan_tab_t), GFP_KERNEL);
 				chan_tab_ptr[i] = ctp;
-				ctp->chan_index = chan = i;
 				break;
 			}
 		}
@@ -278,10 +292,11 @@
 
 		if (ctp != NULL) {
 			memset(ctp, 0, sizeof(chan_tab_t));
+			ctp->chan_index = chan = i;
 			dcp = DDMA_CHANNEL_BASE;
 			dcp += (0x0100 * chan);
 			ctp->chan_ptr = (au1x_dma_chan_t *)dcp;
-			cp = (volatile au1x_dma_chan_t *)dcp;
+			cp = (au1x_dma_chan_t *)dcp;
 			ctp->chan_src = stp;
 			ctp->chan_dest = dtp;
 			ctp->chan_callback = callback;
@@ -298,6 +313,9 @@
 				i |= DDMA_CFG_DED;
 			if (dtp->dev_intpolarity)
 				i |= DDMA_CFG_DP;
+			if ((stp->dev_flags & DEV_FLAGS_SYNC) ||
+				(dtp->dev_flags & DEV_FLAGS_SYNC))
+					i |= DDMA_CFG_SYNC;
 			cp->ddma_cfg = i;
 			au_sync();
 
@@ -308,14 +326,14 @@
 			rv = (u32)(&chan_tab_ptr[chan]);
 		}
 		else {
-			/* Release devices.
-			*/
+			/* Release devices */
 			stp->dev_flags &= ~DEV_FLAGS_INUSE;
 			dtp->dev_flags &= ~DEV_FLAGS_INUSE;
 		}
 	}
 	return rv;
 }
+EXPORT_SYMBOL(au1xxx_dbdma_chan_alloc);
 
 /* Set the device width if source or destination is a FIFO.
  * Should be 8, 16, or 32 bits.
@@ -343,6 +361,7 @@
 
 	return rv;
 }
+EXPORT_SYMBOL(au1xxx_dbdma_set_devwidth);
 
 /* Allocate a descriptor ring, initializing as much as possible.
 */
@@ -369,7 +388,8 @@
 	 * and if we try that first we are likely to not waste larger
 	 * slabs of memory.
 	 */
-	desc_base = (u32)kmalloc(entries * sizeof(au1x_ddma_desc_t), GFP_KERNEL);
+	desc_base = (u32)kmalloc(entries * sizeof(au1x_ddma_desc_t), 
+			GFP_KERNEL|GFP_DMA);
 	if (desc_base == 0)
 		return 0;
 	
@@ -380,7 +400,7 @@
 		kfree((const void *)desc_base);
 		i = entries * sizeof(au1x_ddma_desc_t);
 		i += (sizeof(au1x_ddma_desc_t) - 1);
-		if ((desc_base = (u32)kmalloc(i, GFP_KERNEL)) == 0)
+		if ((desc_base = (u32)kmalloc(i, GFP_KERNEL|GFP_DMA)) == 0)
 			return 0;
 
 		desc_base = ALIGN_ADDR(desc_base, sizeof(au1x_ddma_desc_t));
@@ -460,9 +480,14 @@
 	/* If source input is fifo, set static address.
 	*/
 	if (stp->dev_flags & DEV_FLAGS_IN) {
-		src0 = stp->dev_physaddr;
-		src1 |= DSCR_SRC1_SAM(DSCR_xAM_STATIC);
+		if ( stp->dev_flags & DEV_FLAGS_BURSTABLE ) 
+			src1 |= DSCR_SRC1_SAM(DSCR_xAM_BURST);
+		else 
+			src1 |= DSCR_SRC1_SAM(DSCR_xAM_STATIC);
+
 	}
+	if (stp->dev_physaddr) 
+		src0 = stp->dev_physaddr;
 
 	/* Set up dest1.  For now, assume no stride and increment.
 	 * A channel attribute update can change this later.
@@ -486,10 +511,18 @@
 	/* If destination output is fifo, set static address.
 	*/
 	if (dtp->dev_flags & DEV_FLAGS_OUT) {
-		dest0 = dtp->dev_physaddr;
+		if ( dtp->dev_flags & DEV_FLAGS_BURSTABLE )
+	                dest1 |= DSCR_DEST1_DAM(DSCR_xAM_BURST);
+				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;
@@ -498,6 +531,7 @@
 		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));
 		dp++;
 	}
@@ -510,13 +544,14 @@
 
 	return (u32)(ctp->chan_desc_base);
 }
+EXPORT_SYMBOL(au1xxx_dbdma_ring_alloc);
 
 /* Put a source buffer into the DMA ring.
  * This updates the source pointer and byte count.  Normally used
  * for memory to fifo transfers.
  */
 u32
-au1xxx_dbdma_put_source(u32 chanid, void *buf, int nbytes)
+_au1xxx_dbdma_put_source(u32 chanid, void *buf, int nbytes, u32 flags)
 {
 	chan_tab_t		*ctp;
 	au1x_ddma_desc_t	*dp;
@@ -543,24 +578,40 @@
 	*/
 	dp->dscr_source0 = virt_to_phys(buf);
 	dp->dscr_cmd1 = nbytes;
-	dp->dscr_cmd0 |= DSCR_CMD0_V;	/* Let it rip */
-	ctp->chan_ptr->ddma_dbell = 0xffffffff;	/* Make it go */
-	
+	/* Check flags  */
+	if (flags & DDMA_FLAGS_IE) 
+		dp->dscr_cmd0 |= DSCR_CMD0_IE;
+	if (flags & DDMA_FLAGS_NOIE)
+		dp->dscr_cmd0 &= ~DSCR_CMD0_IE;
 	/* Get next descriptor pointer.
 	*/
 	ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
 
+	/*
+	 * 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 dache eviction buffer.  NONCOHERENT_IO is on by default for 
+	 * 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);
+        dp->dscr_cmd0 |= DSCR_CMD0_V;        /* Let it rip */
+	au_sync();
+	dma_cache_wback_inv(dp, sizeof(dp));
+        ctp->chan_ptr->ddma_dbell = 0;
+
 	/* return something not zero.
 	*/
 	return nbytes;
 }
+EXPORT_SYMBOL(_au1xxx_dbdma_put_source);
 
 /* Put a destination buffer into the DMA ring.
  * This updates the destination pointer and byte count.  Normally used
  * to place an empty buffer into the ring for fifo to memory transfers.
  */
 u32
-au1xxx_dbdma_put_dest(u32 chanid, void *buf, int nbytes)
+_au1xxx_dbdma_put_dest(u32 chanid, void *buf, int nbytes, u32 flags)
 {
 	chan_tab_t		*ctp;
 	au1x_ddma_desc_t	*dp;
@@ -582,11 +633,33 @@
 	if (dp->dscr_cmd0 & DSCR_CMD0_V)
 		return 0;
 	
-	/* Load up buffer address and byte count.
-	*/
+	/* Load up buffer address and byte count */
+
+	/* Check flags  */
+	if (flags & DDMA_FLAGS_IE) 
+		dp->dscr_cmd0 |= DSCR_CMD0_IE;
+	if (flags & DDMA_FLAGS_NOIE)
+		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
+	/*
+	 * 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 
+	 * dache eviction buffer. NONCOHERENT_IO is on by default for 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_inv(buf,nbytes);
 	dp->dscr_cmd0 |= DSCR_CMD0_V;	/* Let it rip */
+	au_sync();
+	dma_cache_wback_inv(dp, sizeof(dp));
+        ctp->chan_ptr->ddma_dbell = 0;
 	
 	/* Get next descriptor pointer.
 	*/
@@ -596,6 +669,7 @@
 	*/
 	return nbytes;
 }
+EXPORT_SYMBOL(_au1xxx_dbdma_put_dest);
 
 /* Get a destination buffer into the DMA ring.
  * Normally used to get a full buffer from the ring during fifo
@@ -645,7 +719,7 @@
 au1xxx_dbdma_stop(u32 chanid)
 {
 	chan_tab_t	*ctp;
-	volatile au1x_dma_chan_t *cp;
+	au1x_dma_chan_t *cp;
 	int halt_timeout = 0;
 
 	ctp = *((chan_tab_t **)chanid);
@@ -665,6 +739,7 @@
 	cp->ddma_stat |= (DDMA_STAT_DB | DDMA_STAT_V);
 	au_sync();
 }
+EXPORT_SYMBOL(au1xxx_dbdma_stop);
 
 /* Start using the current descriptor pointer.  If the dbdma encounters
  * a not valid descriptor, it will stop.  In this case, we can just
@@ -674,17 +749,17 @@
 au1xxx_dbdma_start(u32 chanid)
 {
 	chan_tab_t	*ctp;
-	volatile au1x_dma_chan_t *cp;
+	au1x_dma_chan_t *cp;
 
 	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 = 0xffffffff;	/* Make it go */
+	cp->ddma_dbell = 0;
 	au_sync();
 }
+EXPORT_SYMBOL(au1xxx_dbdma_start);
 
 void
 au1xxx_dbdma_reset(u32 chanid)
@@ -703,15 +778,21 @@
 
 	do {
 		dp->dscr_cmd0 &= ~DSCR_CMD0_V;
+		/* 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;
 		dp = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
 	} while (dp != ctp->chan_desc_base);
 }
+EXPORT_SYMBOL(au1xxx_dbdma_reset);
 
 u32
 au1xxx_get_dma_residue(u32 chanid)
 {
 	chan_tab_t	*ctp;
-	volatile au1x_dma_chan_t *cp;
+	au1x_dma_chan_t *cp;
 	u32		rv;
 
 	ctp = *((chan_tab_t **)chanid);
@@ -746,15 +827,16 @@
 
 	kfree(ctp);
 }
+EXPORT_SYMBOL(au1xxx_dbdma_chan_free);
 
 static void
 dbdma_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
-	u32	intstat;
+	u32        				intstat, flags;
 	u32	chan_index;
 	chan_tab_t		*ctp;
 	au1x_ddma_desc_t	*dp;
-	volatile au1x_dma_chan_t *cp;
+	au1x_dma_chan_t *cp;
 
 	intstat = dbdma_gptr->ddma_intstat;
 	au_sync();
@@ -773,18 +855,26 @@
 		(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)
+static void au1xxx_dbdma_init(void)
 {
+	int irq_nr;
+
 	dbdma_gptr->ddma_config = 0;
 	dbdma_gptr->ddma_throttle = 0;
 	dbdma_gptr->ddma_inten = 0xffff;
 	au_sync();
 
-	if (request_irq(AU1550_DDMA_INT, dbdma_interrupt, SA_INTERRUPT,
+#if defined(CONFIG_SOC_AU1550)
+	irq_nr = AU1550_DDMA_INT;
+#elif defined(CONFIG_SOC_AU1200)
+	irq_nr = AU1200_DDMA_INT;
+#else
+	#error Unknown Au1x00 SOC
+#endif
+
+	if (request_irq(irq_nr, dbdma_interrupt, SA_INTERRUPT,
 			"Au1xxx dbdma", (void *)dbdma_gptr))
 		printk("Can't get 1550 dbdma irq");
 }
@@ -795,7 +885,8 @@
 	chan_tab_t		*ctp;
 	au1x_ddma_desc_t	*dp;
 	dbdev_tab_t		*stp, *dtp;
-	volatile au1x_dma_chan_t *cp;
+	au1x_dma_chan_t *cp;
+		u32			i = 0;
 
 	ctp = *((chan_tab_t **)chanid);
 	stp = ctp->chan_src;
@@ -820,15 +911,64 @@
 	dp = ctp->chan_desc_base;
 
 	do {
-		printk("dp %08x, cmd0 %08x, cmd1 %08x\n",
-			(u32)dp, dp->dscr_cmd0, dp->dscr_cmd1);
-		printk("src0 %08x, src1 %08x, dest0 %08x\n",
-			dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0);
-		printk("dest1 %08x, stat %08x, nxtptr %08x\n",
-			dp->dscr_dest1, dp->dscr_stat, dp->dscr_nxtptr);
+                printk("Dp[%d]= %08x, cmd0 %08x, cmd1 %08x\n",
+                        i++, (u32)dp, dp->dscr_cmd0, dp->dscr_cmd1);
+                printk("src0 %08x, src1 %08x, dest0 %08x, dest1 %08x\n",
+                        dp->dscr_source0, dp->dscr_source1, dp->dscr_dest0, dp->dscr_dest1);
+                printk("stat %08x, nxtptr %08x\n",
+                        dp->dscr_stat, dp->dscr_nxtptr);
 		dp = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
 	} while (dp != ctp->chan_desc_base);
 }
 
+/* Put a descriptor into the DMA ring.
+ * This updates the source/destination pointers and byte count.
+ */
+u32
+au1xxx_dbdma_put_dscr(u32 chanid, au1x_ddma_desc_t *dscr )
+{
+	chan_tab_t *ctp;
+	au1x_ddma_desc_t *dp;
+	u32 nbytes=0;
+
+	/* 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->put_ptr;
+
+	/* 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;
+
+	/* Load up buffer addresses and byte count.
+	*/
+	dp->dscr_dest0 = dscr->dscr_dest0;
+	dp->dscr_source0 = dscr->dscr_source0;
+	dp->dscr_dest1 = dscr->dscr_dest1;
+	dp->dscr_source1 = dscr->dscr_source1;
+	dp->dscr_cmd1 = dscr->dscr_cmd1;
+	nbytes = dscr->dscr_cmd1;
+	/* 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;
+	ctp->chan_ptr->ddma_dbell = 0;
+
+	/* Get next descriptor pointer.
+	*/
+	ctp->put_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+
+	/* return something not zero.
+	*/
+	return nbytes;
+}
+
 #endif /* defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200) */
 
diff -Nur linux-2.4.30/arch/mips/au1000/common/gpio.c linux-2.4.30-linux-mips.org/arch/mips/au1000/common/gpio.c
--- linux-2.4.30/arch/mips/au1000/common/gpio.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/common/gpio.c	2005-05-12 17:57:05.000000000 +0200
@@ -0,0 +1,118 @@
+/*
+ *  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.
+ */
+
+#include <asm/au1000.h>
+#include <asm/au1xxx_gpio.h>
+
+#define gpio1 sys
+#if !defined(CONFIG_SOC_AU1000)
+static AU1X00_GPIO2 * const gpio2 = (AU1X00_GPIO2 *)GPIO2_BASE;
+
+#define GPIO2_OUTPUT_ENABLE_MASK 0x00010000
+
+int au1xxx_gpio2_read(int signal)
+{
+	signal -= 200;
+/*	gpio2->dir &= ~(0x01 << signal);						//Set GPIO to input */
+	return ((gpio2->pinstate >> signal) & 0x01);
+}
+
+void au1xxx_gpio2_write(int signal, int value)
+{
+	signal -= 200;
+
+	gpio2->output = (GPIO2_OUTPUT_ENABLE_MASK << signal) | 
+		(value << signal);
+}
+
+void au1xxx_gpio2_tristate(int signal)
+{
+	signal -= 200;
+	gpio2->dir &= ~(0x01 << signal); 	/* Set GPIO to input */
+}
+#endif
+
+int au1xxx_gpio1_read(int signal)
+{
+/*	gpio1->trioutclr |= (0x01 << signal); */
+	return ((gpio1->pinstaterd >> signal) & 0x01);
+}
+
+void au1xxx_gpio1_write(int signal, int value)
+{
+	if(value)
+		gpio1->outputset = (0x01 << signal);
+	else
+		gpio1->outputclr = (0x01 << signal);	/* Output a Zero */
+}
+
+void au1xxx_gpio1_tristate(int signal)
+{
+	gpio1->trioutclr = (0x01 << signal);		/* Tristate signal */
+}
+
+
+int au1xxx_gpio_read(int signal)
+{
+	if(signal >= 200)
+#if defined(CONFIG_SOC_AU1000)
+		return 0;
+#else
+		return au1xxx_gpio2_read(signal);
+#endif
+	else
+		return au1xxx_gpio1_read(signal);
+}
+
+void au1xxx_gpio_write(int signal, int value)
+{
+	if(signal >= 200)
+#if defined(CONFIG_SOC_AU1000)
+		;
+#else
+		au1xxx_gpio2_write(signal, value);
+#endif
+	else
+		au1xxx_gpio1_write(signal, value);
+}
+
+void au1xxx_gpio_tristate(int signal)
+{
+	if(signal >= 200)
+#if defined(CONFIG_SOC_AU1000)
+		;
+#else
+		au1xxx_gpio2_tristate(signal);
+#endif
+	else
+		au1xxx_gpio1_tristate(signal);
+}
+
+void au1xxx_gpio1_set_inputs(void)
+{
+	gpio1->pininputen = 0;
+}
+
+EXPORT_SYMBOL(au1xxx_gpio1_set_inputs);
+EXPORT_SYMBOL(au1xxx_gpio_tristate);
+EXPORT_SYMBOL(au1xxx_gpio_write);
+EXPORT_SYMBOL(au1xxx_gpio_read);
diff -Nur linux-2.4.30/arch/mips/au1000/common/irq.c linux-2.4.30-linux-mips.org/arch/mips/au1000/common/irq.c
--- linux-2.4.30/arch/mips/au1000/common/irq.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/common/irq.c	2005-05-12 18:01:29.000000000 +0200
@@ -88,9 +88,6 @@
 extern void __init init_generic_irq(void);
 void	(*board_init_irq)(void);
 
-#ifdef CONFIG_PM
-extern void counter0_irq(int irq, void *dev_id, struct pt_regs *regs);
-#endif
 
 static spinlock_t irq_lock = SPIN_LOCK_UNLOCKED;
 
@@ -259,7 +256,7 @@
 
 
 static struct hw_interrupt_type rise_edge_irq_type = {
-	"Au1000 Rise Edge",
+	"Au1 Rise Edge",
 	startup_irq,
 	shutdown_irq,
 	local_enable_irq,
@@ -270,7 +267,7 @@
 };
 
 static struct hw_interrupt_type fall_edge_irq_type = {
-	"Au1000 Fall Edge",
+	"Au1 Fall Edge",
 	startup_irq,
 	shutdown_irq,
 	local_enable_irq,
@@ -281,7 +278,7 @@
 };
 
 static struct hw_interrupt_type either_edge_irq_type = {
-	"Au1000 Rise or Fall Edge",
+	"Au1 Rise or Fall Edge",
 	startup_irq,
 	shutdown_irq,
 	local_enable_irq,
@@ -292,7 +289,7 @@
 };
 
 static struct hw_interrupt_type level_irq_type = {
-	"Au1000 Level",
+	"Au1 Level",
 	startup_irq,
 	shutdown_irq,
 	local_enable_irq,
@@ -302,12 +299,35 @@
 	NULL
 };
 
-#ifdef CONFIG_PM
-void startup_match20_interrupt(void)
+#ifdef CONFIG_SOC_AU1X00_32KHZ_TIMER
+void hook_rtcm1_interrupt(void)
 {
-	local_enable_irq(AU1000_TOY_MATCH2_INT);
+	extern void rtcm1_irq(int irq, void *dev_id, struct pt_regs *regs);
+	static struct irqaction action;
+	/* This is a big problem.... since we didn't use request_irq
+	   when kernel/irq.c calls probe_irq_xxx this interrupt will
+	   be probed for usage. This will end up disabling the device :(
+
+       Give it a bogus "action" pointer -- this will keep it from
+	   getting auto-probed!
+
+       By setting the status to match that of request_irq() we
+       can avoid it.  --cgray
+	*/
+	action.dev_id = rtcm1_irq;
+	action.flags = 0;
+	action.mask = 0;
+	action.name = "Au1 RTCM1 timer";
+	action.handler = rtcm1_irq;
+	action.next = NULL;
+
+	irq_desc[AU1000_RTC_MATCH1_INT].action = &action; 
+	irq_desc[AU1000_RTC_MATCH1_INT].status 
+		 &= ~(IRQ_DISABLED | IRQ_AUTODETECT | IRQ_WAITING | IRQ_INPROGRESS);
+
+	local_enable_irq(AU1000_RTC_MATCH1_INT);
 }
-#endif
+#endif /* CONFIG_SOC_AU1X00_32KHZ_TIMER */
 
 static void setup_local_irq(unsigned int irq_nr, int type, int int_req)
 {
@@ -477,7 +497,7 @@
 		imp++;
 	}
 
-	set_c0_status(ALLINTS);
+	set_c0_status(IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5);
 
 	/* Board specific IRQ initialization.
 	*/
@@ -508,6 +528,7 @@
 
 	if (!intc0_req0) return;
 
+#ifdef AU1000_USB_DEV_REQ_INT
 	/*
 	 * Because of the tight timing of SETUP token to reply
 	 * transactions, the USB devices-side packet complete
@@ -518,6 +539,7 @@
 		do_IRQ(AU1000_USB_DEV_REQ_INT, regs);
 		return;
 	}
+#endif
 
 	irq = au_ffs(intc0_req0) - 1;
 	intc0_req0 &= ~(1<<irq);
@@ -536,17 +558,7 @@
 
 	irq = au_ffs(intc0_req1) - 1;
 	intc0_req1 &= ~(1<<irq);
-#ifdef CONFIG_PM
-	if (irq == AU1000_TOY_MATCH2_INT) {
-		mask_and_ack_rise_edge_irq(irq);
-		counter0_irq(irq, NULL, regs);
-		local_enable_irq(irq);
-	}
-	else
-#endif
-	{
-		do_IRQ(irq, regs);
-	}
+	do_IRQ(irq, regs);
 }
 
 
diff -Nur linux-2.4.30/arch/mips/au1000/common/Makefile linux-2.4.30-linux-mips.org/arch/mips/au1000/common/Makefile
--- linux-2.4.30/arch/mips/au1000/common/Makefile	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/common/Makefile	2005-05-12 17:57:05.000000000 +0200
@@ -19,9 +19,9 @@
 export-objs		= prom.o clocks.o power.o usbdev.o
 
 obj-y := prom.o int-handler.o irq.o puts.o time.o reset.o cputable.o \
-	au1xxx_irqmap.o clocks.o power.o setup.o sleeper.o dma.o dbdma.o
+	au1xxx_irqmap.o clocks.o power.o setup.o sleeper.o dma.o dbdma.o gpio.o
   
-export-objs += dma.o dbdma.o
+export-objs += dma.o dbdma.o gpio.o
 
 obj-$(CONFIG_AU1X00_USB_DEVICE) += usbdev.o
 obj-$(CONFIG_KGDB) += dbg_io.o
diff -Nur linux-2.4.30/arch/mips/au1000/common/pci_fixup.c linux-2.4.30-linux-mips.org/arch/mips/au1000/common/pci_fixup.c
--- linux-2.4.30/arch/mips/au1000/common/pci_fixup.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/common/pci_fixup.c	2005-05-12 17:57:05.000000000 +0200
@@ -75,9 +75,13 @@
 
 #ifdef CONFIG_NONCOHERENT_IO
 	/* 
-	 *  Set the NC bit in controller for pre-AC silicon
+         *  Set the NC bit in controller for Au1500 pre-AC silicon
 	 */
-	au_writel( 1<<16 | au_readl(Au1500_PCI_CFG), Au1500_PCI_CFG);
+	u32 prid = read_c0_prid();
+	if ( (prid & 0xFF000000) == 0x01000000 && prid < 0x01030202) {
+	       au_writel( 1<<16 | au_readl(Au1500_PCI_CFG), Au1500_PCI_CFG);
+	       printk("Non-coherent PCI accesses enabled\n");
+	}
 	printk("Non-coherent PCI accesses enabled\n");
 #endif
 
diff -Nur linux-2.4.30/arch/mips/au1000/common/pci_ops.c linux-2.4.30-linux-mips.org/arch/mips/au1000/common/pci_ops.c
--- linux-2.4.30/arch/mips/au1000/common/pci_ops.c	2004-02-18 14:36:30.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/common/pci_ops.c	2005-05-12 17:57:05.000000000 +0200
@@ -162,6 +162,7 @@
 static int config_access(unsigned char access_type, struct pci_dev *dev, 
 			 unsigned char where, u32 * data)
 {
+	int error = PCIBIOS_SUCCESSFUL;
 #if defined( CONFIG_SOC_AU1500 ) || defined( CONFIG_SOC_AU1550 )
 	unsigned char bus = dev->bus->number;
 	unsigned int dev_fn = dev->devfn;
@@ -170,7 +171,6 @@
 	unsigned long offset, status;
 	unsigned long cfg_base;
 	unsigned long flags;
-	int error = PCIBIOS_SUCCESSFUL;
 	unsigned long entryLo0, entryLo1;
 
 	if (device > 19) {
@@ -205,9 +205,8 @@
 		last_entryLo0  = last_entryLo1 = 0xffffffff;
 	}
 
-	/* Since the Au1xxx doesn't do the idsel timing exactly to spec,
-	 * many board vendors implement their own off-chip idsel, so call
-	 * it now.  If it doesn't succeed, may as well bail out at this point.
+	/* Allow board vendors to implement their own off-chip idsel.  
+	 * If it doesn't succeed, may as well bail out at this point.
 	 */
 	if (board_pci_idsel) {
 		if (board_pci_idsel(device, 1) == 0) {
@@ -271,8 +270,11 @@
 	}
 
 	local_irq_restore(flags);
-	return error;
+#else
+	/* Fake out Config space access with no responder */
+	*data = 0xFFFFFFFF;
 #endif
+	return error;
 }
 #endif
 
diff -Nur linux-2.4.30/arch/mips/au1000/common/power.c linux-2.4.30-linux-mips.org/arch/mips/au1000/common/power.c
--- linux-2.4.30/arch/mips/au1000/common/power.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/common/power.c	2005-05-12 18:01:29.000000000 +0200
@@ -50,7 +50,6 @@
 
 static void calibrate_delay(void);
 
-extern void set_au1x00_speed(unsigned int new_freq);
 extern unsigned int get_au1x00_speed(void);
 extern unsigned long get_au1x00_uart_baud_base(void);
 extern void set_au1x00_uart_baud_base(unsigned long new_baud_base);
@@ -96,7 +95,7 @@
 #define SLEEP_TEST_TIMEOUT 1
 #ifdef SLEEP_TEST_TIMEOUT
 static	int	sleep_ticks;
-void wakeup_counter0_set(int ticks);
+void wakeup_toym2_set(int ticks);
 #endif
 
 static void
@@ -116,6 +115,7 @@
 	sleep_uart0_clkdiv = au_readl(UART0_ADDR + UART_CLK);
 	sleep_uart0_enable = au_readl(UART0_ADDR + UART_MOD_CNTRL);
 
+#ifndef CONFIG_SOC_AU1200
 	/* Shutdown USB host/device.
 	*/
 	sleep_usbhost_enable = au_readl(USB_HOST_CONFIG);
@@ -127,6 +127,7 @@
 
 	sleep_usbdev_enable = au_readl(USBD_ENABLE);
 	au_writel(0, USBD_ENABLE); au_sync();
+#endif
 
 	/* Save interrupt controller state.
 	*/
@@ -164,7 +165,7 @@
 restore_core_regs(void)
 {
 	extern void restore_au1xxx_intctl(void);
-	extern void wakeup_counter0_adjust(void);
+	extern void wakeup_kernel_timer(void);
 
 	au_writel(sleep_aux_pll_cntrl, SYS_AUXPLL); au_sync();
 	au_writel(sleep_cpu_pll_cntrl, SYS_CPUPLL); au_sync();
@@ -199,7 +200,7 @@
 	}
 
 	restore_au1xxx_intctl();
-	wakeup_counter0_adjust();
+	wakeup_kernel_timer();
 }
 
 unsigned long suspend_mode;
@@ -212,14 +213,12 @@
 int au_sleep(void)
 {
 	unsigned long wakeup, flags;
-	extern	void	save_and_sleep(void);
+	extern unsigned int save_and_sleep(void);
 
 	spin_lock_irqsave(&pm_lock,flags);
 
 	save_core_regs();
 
-	flush_cache_all();
-
 	/** The code below is all system dependent and we should probably
 	 ** have a function call out of here to set this up.  You need
 	 ** to configure the GPIO or timer interrupts that will bring
@@ -227,27 +226,26 @@
 	 ** For testing, the TOY counter wakeup is useful.
 	 **/
 
-#if 0
+#if 1
 	au_writel(au_readl(SYS_PINSTATERD) & ~(1 << 11), SYS_PINSTATERD);
 
 	/* gpio 6 can cause a wake up event */
 	wakeup = au_readl(SYS_WAKEMSK);
 	wakeup &= ~(1 << 8);	/* turn off match20 wakeup */
-	wakeup |= 1 << 6;	/* turn on gpio 6 wakeup   */
+	wakeup = 1 << 5;	/* turn on gpio 6 wakeup   */
 #else
-	/* For testing, allow match20 to wake us up.
-	*/
+	/* For testing, allow match20 to wake us up.  */
 #ifdef SLEEP_TEST_TIMEOUT
-	wakeup_counter0_set(sleep_ticks);
+	wakeup_toym2_set(sleep_ticks);
 #endif
 	wakeup = 1 << 8;	/* turn on match20 wakeup   */
 	wakeup = 0;
 #endif
-	au_writel(1, SYS_WAKESRC);	/* clear cause */
+	au_writel(0, SYS_WAKESRC);	/* clear cause */
 	au_sync();
 	au_writel(wakeup, SYS_WAKEMSK);
 	au_sync();
-
+	DPRINTK("Entering sleep!\n");
 	save_and_sleep();
 
 	/* after a wakeup, the cpu vectors back to 0x1fc00000 so
@@ -255,6 +253,7 @@
 	 */
 	restore_core_regs();
 	spin_unlock_irqrestore(&pm_lock, flags);
+	DPRINTK("Leaving sleep!\n");
 	return 0;
 }
 
@@ -285,7 +284,6 @@
 
 		if (retval)
 			return retval;
-
 		au_sleep();
 		retval = pm_send_all(PM_RESUME, (void *) 0);
 	}
@@ -296,7 +294,6 @@
 			 void *buffer, size_t * len)
 {
 	int retval = 0;
-	void	au1k_wait(void);
 
 	if (!write) {
 		*len = 0;
@@ -305,119 +302,9 @@
 		if (retval)
 			return retval;
 		suspend_mode = 1;
-		au1k_wait();
-		retval = pm_send_all(PM_RESUME, (void *) 0);
-	}
-	return retval;
-}
 
-
-static int pm_do_freq(ctl_table * ctl, int write, struct file *file,
-		      void *buffer, size_t * len)
-{
-	int retval = 0, i;
-	unsigned long val, pll;
-#define TMPBUFLEN 64
-#define MAX_CPU_FREQ 396
-	char buf[TMPBUFLEN], *p;
-	unsigned long flags, intc0_mask, intc1_mask;
-	unsigned long old_baud_base, old_cpu_freq, baud_rate, old_clk,
-	    old_refresh;
-	unsigned long new_baud_base, new_cpu_freq, new_clk, new_refresh;
-
-	spin_lock_irqsave(&pm_lock, flags);
-	if (!write) {
-		*len = 0;
-	} else {
-		/* Parse the new frequency */
-		if (*len > TMPBUFLEN - 1) {
-			spin_unlock_irqrestore(&pm_lock, flags);
-			return -EFAULT;
-		}
-		if (copy_from_user(buf, buffer, *len)) {
-			spin_unlock_irqrestore(&pm_lock, flags);
-			return -EFAULT;
-		}
-		buf[*len] = 0;
-		p = buf;
-		val = simple_strtoul(p, &p, 0);
-		if (val > MAX_CPU_FREQ) {
-			spin_unlock_irqrestore(&pm_lock, flags);
-			return -EFAULT;
-		}
-
-		pll = val / 12;
-		if ((pll > 33) || (pll < 7)) {	/* 396 MHz max, 84 MHz min */
-			/* revisit this for higher speed cpus */
-			spin_unlock_irqrestore(&pm_lock, flags);
-			return -EFAULT;
-		}
-
-		old_baud_base = get_au1x00_uart_baud_base();
-		old_cpu_freq = get_au1x00_speed();
-
-		new_cpu_freq = pll * 12 * 1000000;
-	        new_baud_base =  (new_cpu_freq / (2 * ((int)(au_readl(SYS_POWERCTRL)&0x03) + 2) * 16));
-		set_au1x00_speed(new_cpu_freq);
-		set_au1x00_uart_baud_base(new_baud_base);
-
-		old_refresh = au_readl(MEM_SDREFCFG) & 0x1ffffff;
-		new_refresh =
-		    ((old_refresh * new_cpu_freq) /
-		     old_cpu_freq) | (au_readl(MEM_SDREFCFG) & ~0x1ffffff);
-
-		au_writel(pll, SYS_CPUPLL);
-		au_sync_delay(1);
-		au_writel(new_refresh, MEM_SDREFCFG);
-		au_sync_delay(1);
-
-		for (i = 0; i < 4; i++) {
-			if (au_readl
-			    (UART_BASE + UART_MOD_CNTRL +
-			     i * 0x00100000) == 3) {
-				old_clk =
-				    au_readl(UART_BASE + UART_CLK +
-					  i * 0x00100000);
-				// baud_rate = baud_base/clk
-				baud_rate = old_baud_base / old_clk;
-				/* we won't get an exact baud rate and the error
-				 * could be significant enough that our new
-				 * calculation will result in a clock that will
-				 * give us a baud rate that's too far off from
-				 * what we really want.
-				 */
-				if (baud_rate > 100000)
-					baud_rate = 115200;
-				else if (baud_rate > 50000)
-					baud_rate = 57600;
-				else if (baud_rate > 30000)
-					baud_rate = 38400;
-				else if (baud_rate > 17000)
-					baud_rate = 19200;
-				else
-					(baud_rate = 9600);
-				// new_clk = new_baud_base/baud_rate
-				new_clk = new_baud_base / baud_rate;
-				au_writel(new_clk,
-				       UART_BASE + UART_CLK +
-				       i * 0x00100000);
-				au_sync_delay(10);
-			}
-		}
+		retval = pm_send_all(PM_RESUME, (void *) 0);
 	}
-
-
-	/* We don't want _any_ interrupts other than
-	 * match20. Otherwise our calibrate_delay()
-	 * calculation will be off, potentially a lot.
-	 */
-	intc0_mask = save_local_and_disable(0);
-	intc1_mask = save_local_and_disable(1);
-	local_enable_irq(AU1000_TOY_MATCH2_INT);
-	spin_unlock_irqrestore(&pm_lock, flags);
-	calibrate_delay();
-	restore_local_and_enable(0, intc0_mask);
-	restore_local_and_enable(1, intc1_mask);
 	return retval;
 }
 
@@ -425,7 +312,6 @@
 static struct ctl_table pm_table[] = {
 	{ACPI_S1_SLP_TYP, "suspend", NULL, 0, 0600, NULL, &pm_do_suspend},
 	{ACPI_SLEEP, "sleep", NULL, 0, 0600, NULL, &pm_do_sleep},
-	{CTL_ACPI, "freq", NULL, 0, 0600, NULL, &pm_do_freq},
 	{0}
 };
 
diff -Nur linux-2.4.30/arch/mips/au1000/common/reset.c linux-2.4.30-linux-mips.org/arch/mips/au1000/common/reset.c
--- linux-2.4.30/arch/mips/au1000/common/reset.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/common/reset.c	2005-05-12 17:57:05.000000000 +0200
@@ -37,8 +37,6 @@
 #include <asm/system.h>
 #include <asm/au1000.h>
 
-extern int au_sleep(void);
-
 void au1000_restart(char *command)
 {
 	/* Set all integrated peripherals to disabled states */
@@ -144,6 +142,26 @@
 		au_writel(0x00, 0xb1900064); /* sys_auxpll */
 		au_writel(0x00, 0xb1900100); /* sys_pininputen */
 		break;
+	case 0x04000000: /* Au1200 */
+		au_writel(0x00, 0xb400300c); /* ddma */
+		au_writel(0x00, 0xb1a00004); /* psc 0 */
+		au_writel(0x00, 0xb1b00004); /* psc 1 */
+		au_writel(0x00d02000, 0xb4020004); /* ehci, ohci, udc, otg */
+		au_writel(0x00, 0xb5000004); /* lcd */
+		au_writel(0x00, 0xb060000c); /* sd0 */
+		au_writel(0x00, 0xb068000c); /* sd1 */
+		au_writel(0x00, 0xb1100100); /* swcnt */
+		au_writel(0x00, 0xb0300000); /* aes */
+		au_writel(0x00, 0xb4004000); /* cim */
+		au_writel(0x00, 0xb1100100); /* uart0_enable */
+		au_writel(0x00, 0xb1200100); /* uart1_enable */
+		au_writel(0x00, 0xb1900020); /* sys_freqctrl0 */
+		au_writel(0x00, 0xb1900024); /* sys_freqctrl1 */
+		au_writel(0x00, 0xb1900028); /* sys_clksrc */
+		au_writel(0x10, 0xb1900060); /* sys_cpupll */
+		au_writel(0x00, 0xb1900064); /* sys_auxpll */
+		au_writel(0x00, 0xb1900100); /* sys_pininputen */
+		break;
 
 	default:
 		break;
@@ -163,32 +181,23 @@
 
 void au1000_halt(void)
 {
-#if defined(CONFIG_MIPS_PB1550)
-	/* power off system */
-	printk("\n** Powering off Pb1550\n");
-	au_writew(au_readw(0xAF00001C) | (3<<14), 0xAF00001C); 
-	au_sync();
-	while(1); /* should not get here */
-#endif
-	printk(KERN_NOTICE "\n** You can safely turn off the power\n");
-#ifdef CONFIG_MIPS_MIRAGE
-	au_writel((1 << 26) | (1 << 10), GPIO2_OUTPUT);
-#endif
-#ifdef CONFIG_PM
-	au_sleep();
-
-	/* should not get here */
-	printk(KERN_ERR "Unable to put cpu in sleep mode\n");
-	while(1);
-#else
-	while (1)
+	/* Use WAIT in a low-power infinite spin loop */
+	while (1) {
 		__asm__(".set\tmips3\n\t"
 	                "wait\n\t"
 			".set\tmips0");
-#endif
+	}
 }
 
 void au1000_power_off(void)
 {
+	extern void board_power_off (void);
+
+	printk(KERN_NOTICE "\n** You can safely turn off the power\n");
+
+	/* Give board a chance to power-off */
+	board_power_off();
+
+	/* If board can't power-off, spin forever */
 	au1000_halt();
 }
diff -Nur linux-2.4.30/arch/mips/au1000/common/setup.c linux-2.4.30-linux-mips.org/arch/mips/au1000/common/setup.c
--- linux-2.4.30/arch/mips/au1000/common/setup.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/common/setup.c	2005-05-12 18:01:29.000000000 +0200
@@ -174,6 +174,40 @@
 	initrd_end = (unsigned long)&__rd_end;
 #endif
 
+#if defined(CONFIG_SOC_AU1200)
+#ifdef CONFIG_USB_EHCI_HCD
+	if ((argptr = strstr(argptr, "usb_ehci=")) == NULL) {
+	        char usb_args[80];
+		argptr = prom_getcmdline();
+		memset(usb_args, 0, sizeof(usb_args));
+		sprintf(usb_args, " usb_ehci=base:0x%x,len:0x%x,irq:%d",
+			USB_EHCI_BASE, USB_EHCI_LEN, AU1000_USB_HOST_INT);
+		strcat(argptr, usb_args);
+	}
+#ifdef CONFIG_USB_AMD5536UDC
+	/* enable EHC + OHC + UDC clocks, memory and bus mastering */
+/*	au_writel( 0x00DF207F, USB_MSR_BASE + 4); */
+	au_writel( 0xC0DF207F, USB_MSR_BASE + 4);  // incl. prefetch
+#else
+	/* enable EHC + OHC clocks, memory and bus mastering */
+/*	au_writel( 0x00DB200F, USB_MSR_BASE + 4); */
+	au_writel( 0xC0DB200F, USB_MSR_BASE + 4);  /* incl. prefetch */
+#endif
+	udelay(1000);
+
+#else /* CONFIG_USB_EHCI_HCD */
+
+#ifdef CONFIG_USB_AMD5536UDC
+#ifndef CONFIG_USB_OHCI
+	/* enable UDC clocks, memory and bus mastering */
+/*	au_writel( 0x00DC2070, USB_MSR_BASE + 4); */
+	au_writel( 0xC0DC2070, USB_MSR_BASE + 4);  // incl. prefetch
+	udelay(1000);
+#endif
+#endif
+#endif /* CONFIG_USB_EHCI_HCD */
+#endif /* CONFIG_SOC_AU1200 */
+
 #if defined (CONFIG_USB_OHCI) || defined (CONFIG_AU1X00_USB_DEVICE)
 #ifdef CONFIG_USB_OHCI
 	if ((argptr = strstr(argptr, "usb_ohci=")) == NULL) {
@@ -187,19 +221,38 @@
 #endif
 
 #ifdef CONFIG_USB_OHCI
-	// enable host controller and wait for reset done
+#if defined(CONFIG_SOC_AU1200)
+#ifndef CONFIG_USB_EHCI_HCD
+#ifdef CONFIG_USB_AMD5536UDC
+	/* enable OHC + UDC clocks, memory and bus mastering */
+/*	au_writel( 0x00DD2073, USB_MSR_BASE + 4); */
+	au_writel( 0xC0DD2073, USB_MSR_BASE + 4);  // incl. prefetch
+#else
+	/* enable OHC clocks, memory and bus mastering */
+	au_writel( 0x00D12003, USB_MSR_BASE + 4);
+#endif
+	udelay(1000);
+printk("DEBUG: Reading Au1200 USB2 reg 0x%x\n", au_readl(USB_MSR_BASE + 4));
+#endif
+#else
+	/* Au1000, Au1500, Au1100, Au1550 */
+	/* enable host controller and wait for reset done */
 	au_writel(0x08, USB_HOST_CONFIG);
 	udelay(1000);
 	au_writel(0x0E, USB_HOST_CONFIG);
 	udelay(1000);
-	au_readl(USB_HOST_CONFIG); // throw away first read
+	au_readl(USB_HOST_CONFIG); /* throw away first read */
 	while (!(au_readl(USB_HOST_CONFIG) & 0x10))
 		au_readl(USB_HOST_CONFIG);
+#endif /* CONFIG_SOC_AU1200 */
 #endif
-#endif // defined (CONFIG_USB_OHCI) || defined (CONFIG_AU1X00_USB_DEVICE)
+#else
+
+#endif /* defined (CONFIG_USB_OHCI) || defined (CONFIG_AU1X00_USB_DEVICE) */
+
 
 #ifdef CONFIG_FB
-	// Needed if PCI video card in use
+	/* Needed if PCI video card in use */
 	conswitchp = &dummy_con;
 #endif
 
@@ -209,17 +262,11 @@
 #endif
 
 #ifdef CONFIG_BLK_DEV_IDE
-	/* Board setup takes precedence for unique devices.
-	*/
+	/* Board setup takes precedence for unique devices.  */
 	if ((ide_ops == NULL) || (ide_ops == &no_ide_ops))
 		ide_ops = &std_ide_ops;
 #endif
 
-	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_E0S);
-	au_writel(SYS_CNTRL_E0 | SYS_CNTRL_EN0, SYS_COUNTER_CNTRL);
-	au_sync();
-	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T0S);
-	au_writel(0, SYS_TOYTRIM);
 }
 
 #if defined(CONFIG_64BIT_PHYS_ADDR) && (defined(CONFIG_SOC_AU1500) || defined(CONFIG_SOC_AU1550))
diff -Nur linux-2.4.30/arch/mips/au1000/common/sleeper.S linux-2.4.30-linux-mips.org/arch/mips/au1000/common/sleeper.S
--- linux-2.4.30/arch/mips/au1000/common/sleeper.S	2004-02-18 14:36:30.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/common/sleeper.S	2005-05-12 17:57:05.000000000 +0200
@@ -15,17 +15,48 @@
 #include <asm/addrspace.h>
 #include <asm/regdef.h>
 #include <asm/stackframe.h>
+#include <asm/au1000.h>
+
+/*
+ * Note: This file is *not* conditional on CONFIG_PM since Alchemy sleep 
+ * need not be tied to any particular power management scheme.
+ */
+
+	.extern ___flush_cache_all
 
 	.text
-	.set	macro
-	.set	noat
 	.align	5
 
-/* Save all of the processor general registers and go to sleep.
- * A wakeup condition will get us back here to restore the registers.
+/*
+ * Save the processor general registers and go to sleep. A wakeup
+ * condition will get us back here to restore the registers.
  */
-LEAF(save_and_sleep)
 
+/* still need to fix alignment issues here */
+save_and_sleep_frmsz = 48
+NESTED(save_and_sleep, save_and_sleep_frmsz, ra)
+	.set noreorder
+	.set nomacro
+	.set noat
+	subu sp, save_and_sleep_frmsz
+	sw ra, save_and_sleep_frmsz-4(sp)
+	sw s0, save_and_sleep_frmsz-8(sp)
+	sw s1, save_and_sleep_frmsz-12(sp)
+	sw s2, save_and_sleep_frmsz-16(sp)
+	sw s3, save_and_sleep_frmsz-20(sp)
+	sw s4, save_and_sleep_frmsz-24(sp)
+	sw s5, save_and_sleep_frmsz-28(sp)
+	sw s6, save_and_sleep_frmsz-32(sp)
+	sw s7, save_and_sleep_frmsz-36(sp)
+	sw s8, save_and_sleep_frmsz-40(sp)
+	sw gp, save_and_sleep_frmsz-44(sp)
+
+	/* We only need to save the registers that the calling function 
+	 * hasn't saved for us.  0 is always zero.  8 - 15, 24 and 25 are 
+	 * temporaries and can be used without saving. 26 and 27 are reserved 
+	 * for interrupt/trap handling and expected to change.  29 is the 
+	 * stack pointer which is handled as a special case here.
+	 */
 	subu	sp, PT_SIZE
 	sw	$1, PT_R1(sp)
 	sw	$2, PT_R2(sp)
@@ -34,14 +65,6 @@
 	sw	$5, PT_R5(sp)
 	sw	$6, PT_R6(sp)
 	sw	$7, PT_R7(sp)
-	sw	$8, PT_R8(sp)
-	sw	$9, PT_R9(sp)
-	sw	$10, PT_R10(sp)
-	sw	$11, PT_R11(sp)
-	sw	$12, PT_R12(sp)
-	sw	$13, PT_R13(sp)
-	sw	$14, PT_R14(sp)
-	sw	$15, PT_R15(sp)
 	sw	$16, PT_R16(sp)
 	sw	$17, PT_R17(sp)
 	sw	$18, PT_R18(sp)
@@ -50,32 +73,47 @@
 	sw	$21, PT_R21(sp)
 	sw	$22, PT_R22(sp)
 	sw	$23, PT_R23(sp)
-	sw	$24, PT_R24(sp)
-	sw	$25, PT_R25(sp)
-	sw	$26, PT_R26(sp)
-	sw	$27, PT_R27(sp)
 	sw	$28, PT_R28(sp)
-	sw	$29, PT_R29(sp)
 	sw	$30, PT_R30(sp)
 	sw	$31, PT_R31(sp)
+#define PT_C0STATUS PT_LO
+#define PT_CONTEXT PT_HI
+#define PT_PAGEMASK PT_EPC
+#define PT_CONFIG PT_BVADDR
 	mfc0	k0, CP0_STATUS
-	sw	k0, 0x20(sp)
+	sw	k0, PT_C0STATUS(sp) // 0x20
 	mfc0	k0, CP0_CONTEXT
-	sw	k0, 0x1c(sp)
+	sw	k0, PT_CONTEXT(sp) // 0x1c
 	mfc0	k0, CP0_PAGEMASK
-	sw	k0, 0x18(sp)
+	sw	k0, PT_PAGEMASK(sp) // 0x18
 	mfc0	k0, CP0_CONFIG
-	sw	k0, 0x14(sp)
+	sw	k0, PT_CONFIG(sp) // 0x14
+
+	.set macro
+	.set at
+
+	li t0, SYS_SLPPWR
+	sw	zero, 0(t0)	/* Get the processor ready to sleep */
+	sync
 
 	/* Now set up the scratch registers so the boot rom will
 	 * return to this point upon wakeup.
+	 * sys_scratch0 : SP
+	 * sys_scratch1 : RA
+	 */
+	li	t0, SYS_SCRATCH0
+	li	t1, SYS_SCRATCH1
+	sw	sp, 0(t0)
+	la	k0, resume_from_sleep
+	sw	k0, 0(t1)
+
+/*
+ * Flush DCACHE to make sure context is in memory
 	 */
-	la	k0, 1f
-	lui	k1, 0xb190
-	ori	k1, 0x18
-	sw	sp, 0(k1)
-	ori 	k1, 0x1c
-	sw	k0, 0(k1)
+	la 	t1,___flush_cache_all   /* _flush_cache_all is a function pointer */
+	lw 	t0,0(t1)
+	jal t0
+	nop
 
 /* Put SDRAM into self refresh.  Preload instructions into cache,
  * issue a precharge, then auto refresh, then sleep commands to it.
@@ -88,30 +126,65 @@
  	cache	0x14, 96(t0)
 	.set	mips0
 
+	/* Put SDRAM to sleep */
 sdsleep:
-	lui 	k0, 0xb400
-	sw	zero, 0x001c(k0)	/* Precharge */
-	sw	zero, 0x0020(k0)	/* Auto refresh */
-	sw	zero, 0x0030(k0)	/* SDRAM sleep */
+	li 	a0, MEM_PHYS_ADDR
+	or 	a0, a0, 0xA0000000
+#if defined(CONFIG_SOC_AU1000) || defined(CONFIG_SOC_AU1100) || defined(CONFIG_SOC_AU1500)
+	lw 	k0, MEM_SDMODE0(a0)
+	sw	zero, MEM_SDPRECMD(a0) 	/* Precharge */
+	sw	zero, MEM_SDAUTOREF(a0)	/* Auto Refresh */
+	sw	zero, MEM_SDSLEEP(a0)  	/* Sleep */
 	sync
-
-	lui 	k1, 0xb190
-	sw	zero, 0x0078(k1)	/* get ready  to sleep */
+#endif
+#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200)
+	sw	zero, MEM_SDPRECMD(a0) 	/* Precharge */
+	sw 	zero, MEM_SDSREF(a0)
+	
+	#lw 	t0, MEM_SDSTAT(a0)
+	#and t0, t0, 0x01000000
+	li 	t0, 0x01000000
+refresh_not_set:
+	lw 	t1, MEM_SDSTAT(a0)
+	and	t2, t1, t0
+	beq	zero, t2, refresh_not_set
+	nop
+
+	li	t0, ~0x30000000
+	lw 	t1, MEM_SDCONFIGA(a0)
+	and t1, t0, t1
+	sw 	t1, MEM_SDCONFIGA(a0)
 	sync
-	sw	zero, 0x007c(k1)	/* Put processor to sleep */
+#endif
+
+	li	t0, SYS_SLEEP
+	sw	zero, 0(t0)	/* Put processor to sleep */
 	sync
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+	nop
+
 
 	/* This is where we return upon wakeup.
 	 * Reload all of the registers and return.
 	 */
-1:	nop
-	lw	k0, 0x20(sp)
+resume_from_sleep:
+	nop
+	.set nomacro
+	.set noat
+
+	lw	k0, PT_C0STATUS(sp) // 0x20
 	mtc0	k0, CP0_STATUS
-	lw	k0, 0x1c(sp)
+	lw	k0, PT_CONTEXT(sp) // 0x1c
 	mtc0	k0, CP0_CONTEXT
-	lw	k0, 0x18(sp)
+	lw	k0, PT_PAGEMASK(sp) // 0x18
 	mtc0	k0, CP0_PAGEMASK
-	lw	k0, 0x14(sp)
+	lw	k0, PT_CONFIG(sp) // 0x14
 	mtc0	k0, CP0_CONFIG
 	lw	$1, PT_R1(sp)
 	lw	$2, PT_R2(sp)
@@ -120,14 +193,6 @@
 	lw	$5, PT_R5(sp)
 	lw	$6, PT_R6(sp)
 	lw	$7, PT_R7(sp)
-	lw	$8, PT_R8(sp)
-	lw	$9, PT_R9(sp)
-	lw	$10, PT_R10(sp)
-	lw	$11, PT_R11(sp)
-	lw	$12, PT_R12(sp)
-	lw	$13, PT_R13(sp)
-	lw	$14, PT_R14(sp)
-	lw	$15, PT_R15(sp)
 	lw	$16, PT_R16(sp)
 	lw	$17, PT_R17(sp)
 	lw	$18, PT_R18(sp)
@@ -136,15 +201,36 @@
 	lw	$21, PT_R21(sp)
 	lw	$22, PT_R22(sp)
 	lw	$23, PT_R23(sp)
-	lw	$24, PT_R24(sp)
-	lw	$25, PT_R25(sp)
-	lw	$26, PT_R26(sp)
-	lw	$27, PT_R27(sp)
 	lw	$28, PT_R28(sp)
-	lw	$29, PT_R29(sp)
 	lw	$30, PT_R30(sp)
 	lw	$31, PT_R31(sp)
+
+	.set macro
+	.set at
+
+	/* clear the wake source, but save it as the return value of the function */
+	li	t0, SYS_WAKESRC
+	lw v0, 0(t0)
+	sw v0, PT_R2(sp)
+	sw zero, 0(t0)
+
 	addiu	sp, PT_SIZE
 
+	lw gp, save_and_sleep_frmsz-44(sp)
+	lw s8, save_and_sleep_frmsz-40(sp)
+	lw s7, save_and_sleep_frmsz-36(sp)
+	lw s6, save_and_sleep_frmsz-32(sp)
+	lw s5, save_and_sleep_frmsz-28(sp)
+	lw s4, save_and_sleep_frmsz-24(sp)
+	lw s3, save_and_sleep_frmsz-20(sp)
+	lw s2, save_and_sleep_frmsz-16(sp)
+	lw s1, save_and_sleep_frmsz-12(sp)
+	lw s0, save_and_sleep_frmsz-8(sp)
+	lw ra, save_and_sleep_frmsz-4(sp)
+
+	addu sp, save_and_sleep_frmsz
 	jr	ra
+	nop
+	.set reorder
 END(save_and_sleep)
+
diff -Nur linux-2.4.30/arch/mips/au1000/common/time.c linux-2.4.30-linux-mips.org/arch/mips/au1000/common/time.c
--- linux-2.4.30/arch/mips/au1000/common/time.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/common/time.c	2005-05-12 18:01:29.000000000 +0200
@@ -23,13 +23,16 @@
  *
  * ########################################################################
  *
- * Setting up the clock on the MIPS boards.
+ * Setting up the clock on the Alchemy boards.
  *
  * Update.  Always configure the kernel with CONFIG_NEW_TIME_C.  This
  * will use the user interface gettimeofday() functions from the
  * arch/mips/kernel/time.c, and we provide the clock interrupt processing
- * and the timer offset compute functions.  If CONFIG_PM is selected,
- * we also ensure the 32KHz timer is available.   -- Dan
+ * and the timer offset compute functions. -- Dan
+ */
+
+/*
+ * See discussion on kernel timer in Docuemtation/mips/Alchemy.README
  */
 
 #include <linux/types.h>
@@ -38,433 +41,524 @@
 #include <linux/kernel_stat.h>
 #include <linux/sched.h>
 #include <linux/spinlock.h>
+#include <linux/kernel.h>
+#include <linux/fcntl.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
 
 #include <asm/compiler.h>
 #include <asm/mipsregs.h>
 #include <asm/ptrace.h>
 #include <asm/time.h>
 #include <asm/hardirq.h>
-#include <asm/div64.h>
+#include <asm/debug.h>
+#include <asm/time.h>
+#include <asm/uaccess.h>
 #include <asm/au1000.h>
-
-#include <linux/mc146818rtc.h>
 #include <linux/timex.h>
 
-extern void startup_match20_interrupt(void);
-extern void do_softirq(void);
-extern volatile unsigned long wall_jiffies;
-unsigned long missed_heart_beats = 0;
-
-static unsigned long r4k_offset; /* Amount to increment compare reg each time */
-static unsigned long r4k_cur;    /* What counter should be at next timer irq */
-extern rwlock_t xtime_lock;
-int	no_au1xxx_32khz;
-void	(*au1k_wait_ptr)(void);
-
-/* Cycle counter value at the previous timer interrupt.. */
-static unsigned int timerhi = 0, timerlo = 0;
-
-#ifdef CONFIG_PM
-#define MATCH20_INC 328
-extern void startup_match20_interrupt(void);
-static unsigned long last_pc0, last_match20;
+/* I haven't found anyone that doesn't use a 12 MHz source clock,
+ * but just in case.....
+ */
+#ifndef CONFIG_AU1000_SRC_CLK
+#define CONFIG_AU1000_SRC_CLK 12000000
+#endif
+
+/* I haven't found anyone that doesn't use a 32.768KHz crystal,
+ * but just in case.....
+ */
+#ifndef CONFIG_AU1000_32KHZ_CLK
+#define CONFIG_AU1000_32KHZ_CLK 32768
 #endif
 
 static spinlock_t time_lock = SPIN_LOCK_UNLOCKED;
 
-static inline void ack_r4ktimer(unsigned long newval)
-{
-	write_c0_compare(newval);
-}
+static u32 r4k_offset; /* Amount to increment compare reg each time */
+static u32 r4k_cur;    /* What counter should be at next timer irq */
 
-/*
- * There are a lot of conceptually broken versions of the MIPS timer interrupt
- * handler floating around.  This one is rather different, but the algorithm
- * is provably more robust.
- */
-unsigned long wtimer;
-void mips_timer_interrupt(struct pt_regs *regs)
-{
-	int irq = 63;
-	unsigned long count;
-	int cpu = smp_processor_id();
+extern int allow_au1k_wait; /* arch/mips/kernel/cpu-probe.c */
 
-	irq_enter(cpu, irq);
-	kstat.irqs[cpu][irq]++;
+#ifdef CONFIG_SOC_AU1X00_32KHZ_TIMER
+/* Use the integrated RTC timer as the system timer tick. RTC (counter1)
+match1 is used as the tick source */
 
-	if (r4k_offset == 0)
-		goto null;
+static volatile u32 rtcm1_offset; /* Amount to increment match reg each time */
+static volatile u32 rtcm1_cur;    /* What counter should be at next timer irq */
+static volatile u32 rtcm1_last;   /* What match/counter was at at the last timer irq */
+static volatile u32 rtcm1_drift;
 
-	do {
-		count = read_c0_count();
-		timerhi += (count < timerlo);   /* Wrap around */
-		timerlo = count;
-
-		kstat.irqs[0][irq]++;
-		do_timer(regs);
-		r4k_cur += r4k_offset;
-		ack_r4ktimer(r4k_cur);
+void rtcm1_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	/* This handler mirrors mips_timer_interrupt() below, except that
+	this handler is called by do_IRQ() */
+	static int jiffie_drift = 0;
+	int num_do_timer = 0;
+	u32 rtc;
 
-	} while (((unsigned long)read_c0_count()
-	         - r4k_cur) < 0x7fffffff);
+	/* disable all other IRQs. without this, this handler can be
+	interrupted which allows the RTC to advance *WAY* ahead of the RTCM1
+	value which causes the kernel to hang, waiting for rollover. */
+	spin_lock_irq(&time_lock);
+
+	/* check if RTC counter has rolled-over, but rtcm1_cur has not */
+	rtc = au_readl(SYS_RTCREAD);
+	while (rtc < rtcm1_cur) {
+		++num_do_timer;
+		rtcm1_last = rtcm1_cur;
+		rtcm1_cur += rtcm1_offset;
+	}
+	while (rtcm1_cur < rtc) {
+		++num_do_timer;
+		rtcm1_last = rtcm1_cur;
+		rtcm1_cur += rtcm1_offset;
+		/* check if rtcm1_cur has rolled over, and thus ahead of rtc */
+		if (rtcm1_last > rtcm1_cur)
+			break;
+	}
+	jiffie_drift += num_do_timer;
+	if ((rtcm1_drift != 0) && (jiffie_drift >= rtcm1_drift)) {
+		jiffie_drift -= rtcm1_drift;
+		++num_do_timer;
+	}
+
+	/* Check to make sure that the counter is not at the value we're about
+	to program into the match; if so, adjust accordingly. */
+	rtc = au_readl(SYS_RTCREAD);
+
+	/* Check to make sure that the value programmed into RTCM1 is no more
+	than rtcm1_offset ticks away. This is complicated by the need to handle
+	32-bit rollover of the counter, and the fact that the RTC may be ahead
+	of RTCM1. This value represents the number of RTC ticks needed for this
+	code to read the current RTC value, make any decisions, and write
+	to RTCMATCH1. */
+#define RTCM1_UPDATE_DELAY 8
+	rtc += RTCM1_UPDATE_DELAY;
+
+	if (rtcm1_cur > rtc) {
+		if ((rtcm1_cur - rtc) < (2 * rtcm1_offset))
+			/* rtcm1_cur is just ahead of rtc */
+			au_writel(rtcm1_cur, SYS_RTCMATCH1); /* normal update */
+		else
+			/* rtc has rolled-over, ahead of rtcm1_cur */
+			au_writel(rtc + 2, SYS_RTCMATCH1); /* special update */
+	} else {
+		/* rtc > rtcm1_cur */
+		if ((rtc - rtcm1_cur) < (2 * rtcm1_offset))
+			/* rtc is just ahead of rtcm1_cur */
+			au_writel(rtc + 2, SYS_RTCMATCH1); /* special update */
+		else
+			/* rtcm1_cur has rolled-over, ahead of rtc */
+			au_writel(rtcm1_cur, SYS_RTCMATCH1); /* normal update */
+	}
 
-	irq_exit(cpu, irq);
+	while (num_do_timer-- > 0)
+		do_timer(regs);
 
-	if (softirq_pending(cpu))
-		do_softirq();
-	return;
+	/* SYS_CNTRCTRL_RM1 shouldn't need to be examined since this register is
+	written no faster than HZ rate, which is plenty long for the updates to
+	occur. Nonetheless, in practice this is needed, but by doing it at the
+	end, where the update likely should have already occured, we allow the
+	update to RTCM1 to occur in parallel with the calls to do_timer(). 
+	This also guarantees that the next rtcm1_irq() write to RTCMATCH1 can
+	occur without polling. */
+	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_RM1);
+
+	spin_unlock_irq(&time_lock);
+}
+
+static void setup_rtcm1_interrupt(void)
+{
+	/* Setup match1 to interrupt HZ times per second.
+	TODO: These calculations should be transformed into an
+	integer-only sequence, rather than computing manually... */
+	rtcm1_offset = CONFIG_AU1000_32KHZ_CLK / HZ;
+	if ((rtcm1_offset * HZ) != CONFIG_AU1000_32KHZ_CLK)
+		rtcm1_offset += 1; /* round up */
+	rtcm1_drift = 0;
+	if (HZ == 100) rtcm1_drift = 2500;
+	if (HZ == 1000) rtcm1_drift = 154;
+	rtcm1_cur = au_readl(SYS_RTCREAD) + rtcm1_offset;
+	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_RM1);
+	au_writel(rtcm1_cur, SYS_RTCMATCH1);
+	while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_RM1);
+}
+
+static unsigned long do_fast_rtcm1_gettimeoffset(void)
+{
+	unsigned long rtc, offset;
+	static u32 num_huge_offsets = 0;
+
+	rtc = au_readl(SYS_RTCREAD);
+	if (rtc < rtcm1_last) /* RTC has rolled-over */
+		offset = (0 - rtcm1_last) + rtc;
+	else
+		offset = rtc - rtcm1_last;
+
+	/* NOTE: The huge offsets are unavoidable since Linux's interrupt
+	response time is highly irregular. */
+	if (offset > 2*rtcm1_offset) {
+		if (++num_huge_offsets > 100)
+		printk("huge offset %x, rtc %x, rtcm1_last %x rtcm1 %x rtcm1_cur %x\n",
+			offset, rtc, rtcm1_last, au_readl(SYS_RTCMATCH1), rtcm1_cur);
+	}
 
-null:
-	ack_r4ktimer(0);
+	/* What is the meaning of the magic numbers ??? */
+	offset = (unsigned long)((offset * 305) / 10);
+	return offset;
 }
+#endif /* CONFIG_SOC_AU1X00_32KHZ_TIMER */
 
-#ifdef CONFIG_PM
-void counter0_irq(int irq, void *dev_id, struct pt_regs *regs)
+#ifdef CONFIG_SOC_AU1X00_32KHZ
+static unsigned long rtc_au1xxx_get_time(void)
 {
-	unsigned long pc0;
-	int time_elapsed;
-	static int jiffie_drift = 0;
-
-	kstat.irqs[0][irq]++;
-	if (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20) {
-		/* should never happen! */
-		printk(KERN_WARNING "counter 0 w status eror\n");
-		return;
+	/* The TOY counter simply contains the number of seconds elapsed.
+	This is interpreted from the kernel viewpoint as the number of
+	seconds elapsed starting in 1970. */
+	return au_readl(SYS_TOYREAD);
 	}
 
-	pc0 = au_readl(SYS_TOYREAD);
-	if (pc0 < last_match20) {
-		/* counter overflowed */
-		time_elapsed = (0xffffffff - last_match20) + pc0;
+static int rtc_au1xxx_set_time(unsigned long t)
+{
+	/* Take the kernel's value and record it in the counter. There is
+	no need to spin on TS since this register is written infrequently. */
+	au_writel(t, SYS_TOYWRITE);
+	return 0;
 	}
-	else {
-		time_elapsed = pc0 - last_match20;
+
+static unsigned long rtc_au1xxx_get_toym2(void)
+{
+	return au_readl(SYS_TOYMATCH2);
 	}
 
-	while (time_elapsed > 0) {
-		do_timer(regs);
-		time_elapsed -= MATCH20_INC;
-		last_match20 += MATCH20_INC;
-		jiffie_drift++;
+static int rtc_au1xxx_set_toym2(unsigned long t)
+{
+	au_writel(t, SYS_TOYMATCH2);
+	return 0;
 	}
 
-	last_pc0 = pc0;
-	au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
-	au_sync();
+/********************************************************************
+The following is the /dev/rtc driver that utilizes the TOY.
+This code a blatant pilfer of ds1742.c */
 
-	/* our counter ticks at 10.009765625 ms/tick, we we're running
-	 * almost 10uS too slow per tick.
-	 */
+#define AU1XXX_RTC_VERSION "1.0"
+#define RTC_IS_OPEN		0x1
+
+static unsigned long rtc_status = 0;
+static spinlock_t rtc_lock;
+
+extern void to_tm(unsigned long tim, struct rtc_time * tm);
+
+static int rtc_au1xxx_ioctl(struct inode *inode, struct file *file,
+				unsigned int cmd, unsigned long arg)
+{
+	struct rtc_time rtc_tm;
+	ulong curr_time;
+
+	switch (cmd) {
+	case RTC_RD_TIME:	/* Read the time/date from TOY */
+		curr_time = rtc_au1xxx_get_time();
+		memset(&rtc_tm, 0, sizeof(struct rtc_time));
+		to_tm(curr_time, &rtc_tm);
+		rtc_tm.tm_year -= 1900;
+		return copy_to_user((void *) arg, &rtc_tm, sizeof(rtc_tm)) ? 
+			-EFAULT : 0;
+		break;
+
+	case RTC_SET_TIME:
+		if (!capable(CAP_SYS_TIME))
+			return -EACCES;
+
+		if (copy_from_user(&rtc_tm, 
+				   (struct rtc_time *) arg,
+		                   sizeof(struct rtc_time))) 
+			return -EFAULT;
+
+		curr_time = mktime(rtc_tm.tm_year + 1900,
+				   rtc_tm.tm_mon + 1, /* tm_mon starts from 0 */
+				   rtc_tm.tm_mday,
+				   rtc_tm.tm_hour,
+				   rtc_tm.tm_min, 
+				   rtc_tm.tm_sec);
+		return rtc_au1xxx_set_time(curr_time);
+		break;
 
-	if (jiffie_drift >= 999) {
-		jiffie_drift -= 999;
-		do_timer(regs); /* increment jiffies by one */
+	case RTC_WKALM_RD:	/* Read the wakeup alarm time/date from TOY M2 */
+		curr_time = rtc_au1xxx_get_toym2();
+		memset(&rtc_tm, 0, sizeof(struct rtc_time));
+		to_tm(curr_time, &rtc_tm);
+		rtc_tm.tm_year -= 1900;
+		return copy_to_user((void *) arg, &rtc_tm, sizeof(rtc_tm)) ? 
+			-EFAULT : 0;
+		break;
+
+	case RTC_WKALM_SET:
+		if (!capable(CAP_SYS_TIME))
+			return -EACCES;
+
+		if (copy_from_user(&rtc_tm, 
+				   (struct rtc_time *) arg,
+		                   sizeof(struct rtc_time))) 
+			return -EFAULT;
+
+		curr_time = mktime(rtc_tm.tm_year + 1900,
+				   rtc_tm.tm_mon + 1, /* tm_mon starts from 0 */
+				   rtc_tm.tm_mday,
+				   rtc_tm.tm_hour,
+				   rtc_tm.tm_min, 
+				   rtc_tm.tm_sec);
+		return rtc_au1xxx_set_toym2(curr_time);
+		break;
+
+	default:
+		return -EINVAL;
 	}
 }
 
-/* When we wakeup from sleep, we have to "catch up" on all of the
- * timer ticks we have missed.
- */
-void
-wakeup_counter0_adjust(void)
+static int rtc_au1xxx_open(struct inode *inode, struct file *file)
 {
-	unsigned long pc0;
-	int time_elapsed;
+	int err = 0;
 
-	pc0 = au_readl(SYS_TOYREAD);
-	if (pc0 < last_match20) {
-		/* counter overflowed */
-		time_elapsed = (0xffffffff - last_match20) + pc0;
-	}
-	else {
-		time_elapsed = pc0 - last_match20;
-	}
+	spin_lock_irq(&rtc_lock);
 
-	while (time_elapsed > 0) {
-		time_elapsed -= MATCH20_INC;
-		last_match20 += MATCH20_INC;
-	}
+	if (rtc_status & RTC_IS_OPEN)
+		err = -EBUSY;
+	else {
+		/* If oscillator isn't running, then don't open() */
+		if (!(au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_32S))
+			err = -EIO;
+		else
+			rtc_status |= RTC_IS_OPEN;
+}
 
-	last_pc0 = pc0;
-	au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
-	au_sync();
+	spin_unlock_irq(&rtc_lock);
 
+	return err;
 }
 
-/* This is just for debugging to set the timer for a sleep delay.
-*/
-void
-wakeup_counter0_set(int ticks)
+static int rtc_au1xxx_release(struct inode *inode, struct file *file)
 {
-	unsigned long pc0;
-
-	pc0 = au_readl(SYS_TOYREAD);
-	last_pc0 = pc0;
-	au_writel(last_match20 + (MATCH20_INC * ticks), SYS_TOYMATCH2);
-	au_sync();
+	spin_lock_irq(&rtc_lock);
+	rtc_status &= ~RTC_IS_OPEN;
+	spin_unlock_irq(&rtc_lock);
+	return 0;
 }
-#endif
 
-/* I haven't found anyone that doesn't use a 12 MHz source clock,
- * but just in case.....
- */
-#ifdef CONFIG_AU1000_SRC_CLK
-#define AU1000_SRC_CLK	CONFIG_AU1000_SRC_CLK
-#else
-#define AU1000_SRC_CLK	12000000
-#endif
+static struct file_operations rtc_au1xxx_fops = {
+	owner:THIS_MODULE,
+	llseek:no_llseek,
+	ioctl:rtc_au1xxx_ioctl,
+	open:rtc_au1xxx_open,
+	release:rtc_au1xxx_release,
+};
 
-/*
- * We read the real processor speed from the PLL.  This is important
- * because it is more accurate than computing it from the 32KHz
- * counter, if it exists.  If we don't have an accurate processor
- * speed, all of the peripherals that derive their clocks based on
- * this advertised speed will introduce error and sometimes not work
- * properly.  This function is futher convoluted to still allow configurations
- * to do that in case they have really, really old silicon with a
- * write-only PLL register, that we need the 32KHz when power management
- * "wait" is enabled, and we need to detect if the 32KHz isn't present
- * but requested......got it? :-)		-- Dan
- */
-unsigned long cal_r4koff(void)
+static struct miscdevice rtc_au1xxx_dev = {
+	RTC_MINOR,
+	"rtc",
+	&rtc_au1xxx_fops
+};
+
+static int __init rtc_au1xxx_init(void)
 {
-	unsigned long count;
-	unsigned long cpu_speed;
-	unsigned long flags;
-	unsigned long counter;
+	printk(KERN_INFO "Au1XXX Real Time Clock Driver v%s\n", AU1XXX_RTC_VERSION);
+	misc_register(&rtc_au1xxx_dev);
+	return 0;
+}
 
-	spin_lock_irqsave(&time_lock, flags);
+static void __exit rtc_au1xxx_exit(void)
+{
+	misc_deregister(&rtc_au1xxx_dev);
+}
 
-	/* Power management cares if we don't have a 32KHz counter.
-	*/
-	no_au1xxx_32khz = 0;
-	counter = au_readl(SYS_COUNTER_CNTRL);
-	if (counter & SYS_CNTRL_E0) {
-		int trim_divide = 16;
-
-		au_writel(counter | SYS_CNTRL_EN1, SYS_COUNTER_CNTRL);
-
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S);
-		/* RTC now ticks at 32.768/16 kHz */
-		au_writel(trim_divide-1, SYS_RTCTRIM);
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_T1S);
-
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S);
-		au_writel (0, SYS_TOYWRITE);
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C1S);
+module_init(rtc_au1xxx_init);
+module_exit(rtc_au1xxx_exit);
+EXPORT_NO_SYMBOLS;
+/*******************************************************************/
 
-#if defined(CONFIG_AU1000_USE32K)
+static int au1xxx_32khz_startup(void)
 		{
-			unsigned long start, end;
+	/* Setup the 32kHz oscillator for operation per scheme described above */
 
-			start = au_readl(SYS_RTCREAD);
-			start += 2;
-			/* wait for the beginning of a new tick
-			*/
-			while (au_readl(SYS_RTCREAD) < start);
-
-			/* Start r4k counter.
-			*/
-			write_c0_count(0);
-
-			/* Wait 0.5 seconds.
-			*/
-			end = start + (32768 / trim_divide)/2;
+	if (!(au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_32S)) {
+		int timeout = 500000;
+		/* Start the 32khz oscillator - NOTE: usually best to do this
+		in the board boot up code to give the oscillator plenty of
+		time to startup prior to this code */
+		au_writel(SYS_CNTRCTRL_E0, SYS_COUNTER_CNTRL);
+		/* Wait for it to start...can hang here if there is
+		a problem with the 32khz oscillator!!! */
+		while (--timeout > 0)
+			if (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_32S)
+				break; /* oscillator started */
+	}
+
+	/* 32khz oscillator present and running */
+	if (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_32S) {
+		/* Configure RTC counter */
+		/* Set trim to 0 for max clock resolution */
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_RTS);
+		au_writel(0, SYS_RTCTRIM);
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_RTS);
+
+		/* start RTC counter at zero */
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_RS);
+		au_writel(0, SYS_RTCWRITE);
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_RS);
+
+		/* Configure the TOY counter */
+		/* Set trim for 1 second clock resolution */
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_TTS);
+		au_writel(CONFIG_AU1000_32KHZ_CLK - 1, SYS_TOYTRIM);
+		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRCTRL_TTS);
 
-			while (end > au_readl(SYS_RTCREAD));
+		/* the TOY counter is not written since it contains date/time */
 
-			count = read_c0_count();
-			cpu_speed = count * 2;
+		return 1;
 		}
-#else
-		cpu_speed = (au_readl(SYS_CPUPLL) & 0x0000003f) * 
-			AU1000_SRC_CLK;
-		count = cpu_speed / 2;
-#endif
+	return 0;
 	}
-	else {
-		/* The 32KHz oscillator isn't running, so assume there
-		 * isn't one and grab the processor speed from the PLL.
-		 * NOTE: some old silicon doesn't allow reading the PLL.
-		 */
-		cpu_speed = (au_readl(SYS_CPUPLL) & 0x0000003f) * AU1000_SRC_CLK;
-		count = cpu_speed / 2;
-		no_au1xxx_32khz = 1;
-	}
-	mips_hpt_frequency = count;
-	// Equation: Baudrate = CPU / (SD * 2 * CLKDIV * 16)
-	set_au1x00_uart_baud_base(cpu_speed / (2 * ((int)(au_readl(SYS_POWERCTRL)&0x03) + 2) * 16));
-	spin_unlock_irqrestore(&time_lock, flags);
-	return (cpu_speed / HZ);
-}
-
-/* This is for machines which generate the exact clock. */
-#define USECS_PER_JIFFY (1000000/HZ)
-#define USECS_PER_JIFFY_FRAC (0x100000000*1000000/HZ&0xffffffff)
+#endif /* CONFIG_SOC_AU1X00_32KHZ */
 
-
-static unsigned long
-div64_32(unsigned long v1, unsigned long v2, unsigned long v3)
+static void setup_cp0_interrupt(void)
 {
-	unsigned long r0;
-	do_div64_32(r0, v1, v2, v3);
-	return r0;
+	/* Setup CP0 Count/Compare interrupt */
+	r4k_cur = (read_c0_count() + r4k_offset);
+	write_c0_compare(r4k_cur);
 }
 
-static unsigned long do_fast_cp0_gettimeoffset(void)
+void wakeup_kernel_timer(void)
 {
-	u32 count;
-	unsigned long res, tmp;
-	unsigned long r0;
+#ifdef CONFIG_SOC_AU1X00_32KHZ_TIMER
+	au1xxx_32khz_startup();
+	setup_rtcm1_interrupt();
+#else
+	setup_cp0_interrupt();
+#endif
+}
 
-	/* Last jiffy when do_fast_gettimeoffset() was called. */
-	static unsigned long last_jiffies=0;
-	unsigned long quotient;
+/* This is to set the timer upon resuming from sleep */
+void wakeup_toym2_set(int ticks)
+{
+#ifdef CONFIG_SOC_AU1X00_32KHZ
+	/* sets timeout from sleep mode */
+	u32 pc0 = au_readl(SYS_TOYREAD);
+	au_writel(pc0 + ticks, SYS_TOYMATCH2);
+#endif
+}
 
 	/*
-	 * Cached "1/(clocks per usec)*2^32" value.
-	 * It has to be recalculated once each jiffy.
+ * There are a lot of conceptually broken versions of the MIPS timer interrupt
+ * handler floating around.  This one is rather different, but the algorithm
+ * is provably more robust.
+ * This called directly by int-handler.S for Counter IRQ, as such, it must
+ * do more of what do_IRQ() does...
 	 */
-	static unsigned long cached_quotient=0;
-
-	tmp = jiffies;
-
-	quotient = cached_quotient;
+void mips_timer_interrupt(struct pt_regs *regs)
+{
+	extern void do_softirq(void);
+	int irq = 63; /* non-existant interrupt source??? */
+	int cpu = smp_processor_id();
 
-	if (tmp && last_jiffies != tmp) {
-		last_jiffies = tmp;
-		if (last_jiffies != 0) {
-			r0 = div64_32(timerhi, timerlo, tmp);
-			quotient = div64_32(USECS_PER_JIFFY, USECS_PER_JIFFY_FRAC, r0);
-			cached_quotient = quotient;
-		}
+	/* Count interrupt during init, or when 32khz timer enabled */
+	if (r4k_offset == 0) {
+		write_c0_compare(0); /* ack r4k timer */
+		return;
 	}
 
-	/* Get last timer tick in absolute kernel time */
-	count = read_c0_count();
-
-	/* .. relative to previous jiffy (32 bits is enough) */
-	count -= timerlo;
+	irq_enter(cpu, irq);
 
-	__asm__("multu\t%1,%2\n\t"
-		"mfhi\t%0"
-		: "=r" (res)
-		: "r" (count), "r" (quotient)
-		: "hi", "lo", GCC_REG_ACCUM);
+	do {
+		kstat.irqs[0][irq]++;
+		do_timer(regs);
+		r4k_cur += r4k_offset;
+		write_c0_compare(r4k_cur); /* ack r4k timer */
+	} while (((u32)read_c0_count() - r4k_cur) < 0x7fffffff);
 
-	/*
- 	 * Due to possible jiffies inconsistencies, we need to check 
-	 * the result so that we'll get a timer that is monotonic.
-	 */
-	if (res >= USECS_PER_JIFFY)
-		res = USECS_PER_JIFFY-1;
+	irq_exit(cpu, irq);
 
-	return res;
+	if (softirq_pending(cpu))
+		do_softirq();
 }
 
-#ifdef CONFIG_PM
-static unsigned long do_fast_pm_gettimeoffset(void)
+u32 cal_r4koff(void)
 {
-	unsigned long pc0;
-	unsigned long offset;
+	u32 cpu_freq, flags;
 
-	pc0 = au_readl(SYS_TOYREAD);
-	au_sync();
-	offset = pc0 - last_pc0;
-	if (offset > 2*MATCH20_INC) {
-		printk("huge offset %x, last_pc0 %x last_match20 %x pc0 %x\n", 
-				(unsigned)offset, (unsigned)last_pc0, 
-				(unsigned)last_match20, (unsigned)pc0);
-	}
-	offset = (unsigned long)((offset * 305) / 10);
-	return offset;
-}
-#endif
+	spin_lock_irqsave(&time_lock, flags);
 
-void __init au1xxx_timer_setup(void)
-{
-        unsigned int est_freq;
-	extern unsigned long (*do_gettimeoffset)(void);
-	extern void au1k_wait(void);
+#ifdef CONFIG_SOC_AU1000
+	/* On early Au1000s, sys_cpupll was write-only. Since these
+	silicon versions of Au1000 are not sold by AMD, we don't bend
+	over backwards trying to determine the frequency */
+#ifndef CONFIG_SOC_AU1000_FREQUENCY
+#define CONFIG_SOC_AU1000_FREQUENCY 396000000
+#endif
+	if ((read_c0_prid() == 0x00030100) || /* Au1000 DA */
+		(read_c0_prid() == 0x00030101) || /* Au1000 HA */
+		(read_c0_prid() == 0x00030102))   /* Au1000 HB */
+		cpu_freq = CONFIG_SOC_AU1000_FREQUENCY;
+	else
+#endif
+		cpu_freq = (au_readl(SYS_CPUPLL) & 0x0000003f) * CONFIG_AU1000_SRC_CLK;
 
-	printk("calculating r4koff... ");
-	r4k_offset = cal_r4koff();
-	printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset);
+	mips_hpt_frequency = cpu_freq; /* On Alchemy, Count is 1:1 CPU */
+	/* Equation: Baudrate = CPU / (SD * 2 * CLKDIV * 16) */
+	set_au1x00_uart_baud_base(cpu_freq / (2 * ((int)(au_readl(SYS_POWERCTRL)&0x03) + 2) * 16));
+ 	set_au1x00_speed(cpu_freq);
+ 	set_au1x00_lcd_clock(); /* program the LCD clock */
 
-	//est_freq = 2*r4k_offset*HZ;	
-	est_freq = r4k_offset*HZ;	
-	est_freq += 5000;    /* round */
-	est_freq -= est_freq%10000;
-	printk("CPU frequency %d.%02d MHz\n", est_freq/1000000, 
-	       (est_freq%1000000)*100/1000000);
- 	set_au1x00_speed(est_freq);
- 	set_au1x00_lcd_clock(); // program the LCD clock
+	spin_unlock_irqrestore(&time_lock, flags);
 
-	r4k_cur = (read_c0_count() + r4k_offset);
-	write_c0_compare(r4k_cur);
+	return (cpu_freq / HZ);
+}
 
-	/* no RTC on the pb1000 */
-	xtime.tv_sec = 0;
-	xtime.tv_usec = 0;
+void __init au1xxx_timer_setup(void)
+{
+	/*	This code called at the end of time_init() in arch/mips/kernel/time.c.
+	The Cp0 Counter has already been chosen as the timer source, here we
+	can override the choices made by time_init(). */
+
+	/* Setup first CP0 timer interrupt */
+	setup_cp0_interrupt();
+
+#ifdef CONFIG_SOC_AU1X00_32KHZ_TIMER
+	/* Setup the 32kHz oscillator as the kernel timer instead */
+	if (au1xxx_32khz_startup()) {
+		extern void hook_rtcm1_interrupt(void);
+
+		/* it's too early to call request_irq(), but need to hook it anyway */
+		setup_rtcm1_interrupt();
+		hook_rtcm1_interrupt();
 
-#ifdef CONFIG_PM
-	/*
-	 * setup counter 0, since it keeps ticking after a
-	 * 'wait' instruction has been executed. The CP0 timer and
-	 * counter 1 do NOT continue running after 'wait'
-	 *
-	 * It's too early to call request_irq() here, so we handle
-	 * counter 0 interrupt as a special irq and it doesn't show
-	 * up under /proc/interrupts.
-	 *
-	 * Check to ensure we really have a 32KHz oscillator before
-	 * we do this.
-	 */
-	if (no_au1xxx_32khz) {
-		unsigned int c0_status;
+		/* use 32khz flavor of do_fast_gettimeoffset() */
+		do_gettimeoffset = do_fast_rtcm1_gettimeoffset;
 
-		printk("WARNING: no 32KHz clock found.\n");
-		do_gettimeoffset = do_fast_cp0_gettimeoffset;
+		/* short-circuit CP0 Count timer handler */
+		r4k_offset = 0;
 
-		/* Ensure we get CPO_COUNTER interrupts.
-		*/
-		c0_status = read_c0_status();
-		c0_status |= IE_IRQ5;
-		write_c0_status(c0_status);
+		/* allow WAIT instruction in cpu-probe.c */
+		allow_au1k_wait = 1;
 	}
 	else {
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);
-		au_writel(0, SYS_TOYWRITE);
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_C0S);
-
-		au_writel(au_readl(SYS_WAKEMSK) | (1<<8), SYS_WAKEMSK);
-		au_writel(~0, SYS_WAKESRC);
-		au_sync();
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);
-
-		/* setup match20 to interrupt once every 10ms */
-		last_pc0 = last_match20 = au_readl(SYS_TOYREAD);
-		au_writel(last_match20 + MATCH20_INC, SYS_TOYMATCH2);
-		au_sync();
-		while (au_readl(SYS_COUNTER_CNTRL) & SYS_CNTRL_M20);
-		startup_match20_interrupt();
-
-		do_gettimeoffset = do_fast_pm_gettimeoffset;
-
-		/* We can use the real 'wait' instruction.
-		*/
-		au1k_wait_ptr = au1k_wait;
+		printk("WARNING: no 32KHz clock found...reverting to CP0 timer\n");
 	}
-
-#else
-	/* We have to do this here instead of in timer_init because
-	 * the generic code in arch/mips/kernel/time.c will write
-	 * over our function pointer.
-	 */
-	do_gettimeoffset = do_fast_cp0_gettimeoffset;
-#endif
+#endif /* CONFIG_SOC_AU1X00_32KHZ_TIMER */
 }
 
 void __init au1xxx_time_init(void)
 {
+	/* This called at the start of time_init() in arch/mips/kernel/time.c */
+	/* An RTC for use by time_init()/kernel is supposed to be setup here */
+
+#ifdef CONFIG_SOC_AU1X00_32KHZ
+	if (au1xxx_32khz_startup()) {
+		/* set the kernel function pointers */
+		rtc_get_time = rtc_au1xxx_get_time;
+		rtc_set_time = rtc_au1xxx_set_time;
+	}
+#endif /* CONFIG_SOC_AU1X00_32KHZ */
+
+	/* Determine CP0 timer values */
+	printk("calculating r4koff... ");
+	r4k_offset = cal_r4koff();
+	printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset);
 }
diff -Nur linux-2.4.30/arch/mips/au1000/db1x00/board_setup.c linux-2.4.30-linux-mips.org/arch/mips/au1000/db1x00/board_setup.c
--- linux-2.4.30/arch/mips/au1000/db1x00/board_setup.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/db1x00/board_setup.c	2005-05-12 17:57:05.000000000 +0200
@@ -46,10 +46,22 @@
 #include <asm/au1000.h>
 #include <asm/db1x00.h>
 
-extern struct rtc_ops no_rtc_ops;
+#if defined(CONFIG_BLK_DEV_IDE_AU1XXX) && defined(CONFIG_MIPS_DB1550)
+#include <asm/au1xxx_dbdma.h>
+extern struct ide_ops *ide_ops;
+extern struct ide_ops au1xxx_ide_ops;
+extern u32 au1xxx_ide_virtbase;
+extern u64 au1xxx_ide_physbase;
+extern int au1xxx_ide_irq;
+
+/* Ddma */
+chan_tab_t *ide_read_ch, *ide_write_ch;
+u32 au1xxx_ide_ddma_enable = 0, switch4ddma = 1; // PIO+ddma
+
+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 */
 
-/* not correct for db1550 */
-static BCSR * const bcsr = (BCSR *)0xAE000000;
+extern struct rtc_ops no_rtc_ops;
 
 void board_reset (void)
 {
@@ -57,6 +69,13 @@
 	au_writel(0x00000000, 0xAE00001C);
 }
 
+void board_power_off (void)
+{
+#ifdef CONFIG_MIPS_MIRAGE
+	au_writel((1 << 26) | (1 << 10), GPIO2_OUTPUT);
+#endif
+}
+
 void __init board_setup(void)
 {
 	u32 pin_func;
@@ -108,8 +127,42 @@
 	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);
+	}
+#endif /* end CONFIG_SMC91111 */
 	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;
+#endif
+
 #ifdef CONFIG_MIPS_DB1000
     printk("AMD Alchemy Au1000/Db1000 Board\n");
 #endif
diff -Nur linux-2.4.30/arch/mips/au1000/db1x00/irqmap.c linux-2.4.30-linux-mips.org/arch/mips/au1000/db1x00/irqmap.c
--- linux-2.4.30/arch/mips/au1000/db1x00/irqmap.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/db1x00/irqmap.c	2005-05-12 17:57:05.000000000 +0200
@@ -53,6 +53,7 @@
 #ifdef CONFIG_MIPS_DB1550
 	{ AU1000_GPIO_3, INTC_INT_LOW_LEVEL, 0 }, // PCMCIA Card 0 IRQ#
 	{ AU1000_GPIO_5, INTC_INT_LOW_LEVEL, 0 }, // PCMCIA Card 1 IRQ#
+	{ AU1000_GPIO_8, INTC_INT_LOW_LEVEL, 0 }, // Daughtercard IRQ#
 #else
 	{ AU1000_GPIO_0, INTC_INT_LOW_LEVEL, 0 }, // PCMCIA Card 0 Fully_Interted#
 	{ AU1000_GPIO_1, INTC_INT_LOW_LEVEL, 0 }, // PCMCIA Card 0 STSCHG#
diff -Nur linux-2.4.30/arch/mips/au1000/db1x00/Makefile linux-2.4.30-linux-mips.org/arch/mips/au1000/db1x00/Makefile
--- linux-2.4.30/arch/mips/au1000/db1x00/Makefile	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/db1x00/Makefile	2005-05-12 17:57:05.000000000 +0200
@@ -17,4 +17,11 @@
 obj-y := init.o board_setup.o irqmap.o
 obj-$(CONFIG_WM97XX_COMODULE) += mirage_ts.o
 
+ifdef CONFIG_MIPS_DB1100
+ifdef CONFIG_MMC
+obj-y += mmc_support.o
+export-objs += mmc_support.o
+endif
+endif
+
 include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/arch/mips/au1000/db1x00/mmc_support.c linux-2.4.30-linux-mips.org/arch/mips/au1000/db1x00/mmc_support.c
--- linux-2.4.30/arch/mips/au1000/db1x00/mmc_support.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/db1x00/mmc_support.c	2005-05-12 17:57:05.000000000 +0200
@@ -0,0 +1,126 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *
+ *	MMC support routines for DB1100.
+ *
+ *
+ * Copyright (c) 2003-2004 Embedded Edge, LLC.
+ * Author: Embedded Edge, LLC.
+ * Contact: dan@embeddededge.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  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.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/irq.h>
+#include <asm/au1000.h>
+#include <asm/au1100_mmc.h>
+#include <asm/db1x00.h>
+
+
+/* SD/MMC controller support functions */
+
+/*
+ * Detect card.
+ */
+void mmc_card_inserted(int _n_, int *_res_)
+{
+	u32 gpios = au_readl(SYS_PINSTATERD);
+	u32 emptybit = (_n_) ? (1<<20) : (1<<19);
+	*_res_ = ((gpios & emptybit) == 0);
+}
+
+/*
+ * Check card write protection.
+ */
+void mmc_card_writable(int _n_, int *_res_)
+{
+	BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+	unsigned long mmc_wp, board_specific;
+
+	if (_n_) {
+		mmc_wp = BCSR_BOARD_SD1_WP;
+	} else {
+		mmc_wp = BCSR_BOARD_SD0_WP;
+	}
+
+	board_specific = au_readl((unsigned long)(&bcsr->specific));
+
+	if (!(board_specific & mmc_wp)) {/* low means card writable */
+		*_res_ = 1;
+	} else {
+		*_res_ = 0;
+	}
+}
+
+/*
+ * Apply power to card slot.
+ */
+void mmc_power_on(int _n_)
+{
+	BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+	unsigned long mmc_pwr, board_specific;
+
+	if (_n_) {
+		mmc_pwr = BCSR_BOARD_SD1_PWR;
+	} else {
+		mmc_pwr = BCSR_BOARD_SD0_PWR;
+	}
+
+	board_specific = au_readl((unsigned long)(&bcsr->specific));
+	board_specific |= mmc_pwr;
+
+	au_writel(board_specific, (int)(&bcsr->specific));
+	au_sync_delay(1);
+}
+
+/*
+ * Remove power from card slot.
+ */
+void mmc_power_off(int _n_)
+{
+	BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+	unsigned long mmc_pwr, board_specific;
+
+	if (_n_) {
+		mmc_pwr = BCSR_BOARD_SD1_PWR;
+	} else {
+		mmc_pwr = BCSR_BOARD_SD0_PWR;
+	}
+
+	board_specific = au_readl((unsigned long)(&bcsr->specific));
+	board_specific &= ~mmc_pwr;
+
+	au_writel(board_specific, (int)(&bcsr->specific));
+	au_sync_delay(1);
+}
+
+EXPORT_SYMBOL(mmc_card_inserted);
+EXPORT_SYMBOL(mmc_card_writable);
+EXPORT_SYMBOL(mmc_power_on);
+EXPORT_SYMBOL(mmc_power_off);
+
diff -Nur linux-2.4.30/arch/mips/au1000/ficmmp/au1200_ibutton.c linux-2.4.30-linux-mips.org/arch/mips/au1000/ficmmp/au1200_ibutton.c
--- linux-2.4.30/arch/mips/au1000/ficmmp/au1200_ibutton.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/ficmmp/au1200_ibutton.c	2005-05-12 17:57:05.000000000 +0200
@@ -0,0 +1,270 @@
+/* ---------------------------------------------------------------------- 
+ *  mtwilson_keys.c
+ *
+ *  Copyright (C) 2003 Intrinsyc Software Inc.
+ *
+ *  Intel Personal Media Player buttons
+ *
+ *  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.
+ *
+ *  May 02, 2003 : Initial version [FB]
+ *
+ ------------------------------------------------------------------------*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <linux/delay.h>
+#include <linux/input.h>
+
+#include <asm/au1000.h>
+#include <asm/uaccess.h>
+#include <asm/au1xxx_gpio.h>
+#include <asm/irq.h>
+#include <asm/keyboard.h>
+#include <linux/time.h>
+
+#define DRIVER_VERSION	"V1.0"
+#define DRIVER_AUTHOR	"FIC"
+#define DRIVER_DESC		"FIC Travis Media Player Button Driver"
+#define DRIVER_NAME		"Au1200Button"
+
+#define BUTTON_MAIN		(1<<1)
+#define BUTTON_SELECT	(1<<6)
+#define BUTTON_GUIDE	(1<<12)
+#define BUTTON_DOWN		(1<<17)
+#define BUTTON_LEFT		(1<<19)
+#define BUTTON_RIGHT	(1<<26)
+#define BUTTON_UP		(1<<28)
+
+#define BUTTON_MASK (\
+    BUTTON_MAIN   \
+    | BUTTON_SELECT	\
+    | BUTTON_GUIDE	\
+    | BUTTON_DOWN	\
+    | BUTTON_LEFT	\
+    | BUTTON_RIGHT	\
+    | BUTTON_UP		\
+    )
+
+#define BUTTON_INVERT (\
+    BUTTON_MAIN   \
+    | 0				\
+    | BUTTON_GUIDE	\
+    | 0				\
+    | 0				\
+    | 0				\
+    | 0				\
+    )
+
+char button_map[32]={0,KEY_S,0,0,0,0,KEY_ENTER,0,0,0,0,0,KEY_G,0,0,0,0,KEY_DOWN,0,KEY_LEFT,0,0,0,0,0,0,KEY_RIGHT,0,KEY_UP,0,0,0};
+//char button_map[32]={0,0,0,0,0,0,KEY_ENTER,0,0,0,0,0,KEY_G,0,0,0,0,KEY_DOWN,0,KEY_LEFT,0,0,0,0,0,0,KEY_RIGHT,0,KEY_UP,0,0,0};
+
+//char button_map[32]={0,KEY_TAB,0,0,0,0,KEY_M,0,0,0,0,0,KEY_S,0,0,0,0,KEY_DOWN,0,KEY_LEFT,0,0,0,0,0,0,KEY_RIGHT,0,KEY_UP,0,0,0};
+//char button_map[32]={0,0,0,0,0,0,KEY_M,0,0,0,0,0,KEY_S,0,0,0,0,KEY_DOWN,0,KEY_LEFT,0,0,0,0,0,0,KEY_RIGHT,0,KEY_UP,0,0,0};
+
+#define BUTTON_COUNT (sizeof (button_map) / sizeof (button_map[0]))
+
+struct input_dev dev;
+struct timeval cur_tv;
+
+static unsigned int old_tv_usec = 0;
+
+static unsigned int read_button_state(void)
+{
+	unsigned int state;
+
+	state = au_readl(SYS_PINSTATERD) & BUTTON_MASK; /* get gpio status */
+
+	state ^= BUTTON_INVERT;		/* invert main & guide button */
+
+	/* printk("au1200_ibutton.c: button state [0x%X]\r\n",state); */
+	return state;
+}
+
+//This function returns 0 if the allowed microseconds have elapsed since the last call to ths function, otherwise it returns 1 to indicate a bounce condition
+static unsigned int bounce() 
+{
+
+	unsigned int elapsed_time;
+
+	do_gettimeofday (&cur_tv);    
+
+	if (!old_tv_usec) {
+		old_tv_usec = cur_tv.tv_usec;
+		return 0;
+	}
+
+	if(cur_tv.tv_usec > old_tv_usec) {
+		/* If there hasn't been rollover */
+		elapsed_time =  ((cur_tv.tv_usec - old_tv_usec));
+	}
+	else {
+		/* Accounting for rollover */
+		elapsed_time =  ((1000000 - old_tv_usec + cur_tv.tv_usec));
+	}
+
+	if (elapsed_time > 250000) {
+		old_tv_usec = 0;	/* reset the bounce time */
+		return 0;
+	}
+
+	return 1;
+}
+
+/* button interrupt handler */
+static void button_interrupt(int irq, void *dev, struct pt_regs *regs)
+{
+
+	unsigned int i,bit_mask, key_choice;
+	u32 button_state;
+	
+	/* Report state to upper level */
+	
+	button_state = read_button_state() & BUTTON_MASK; /* get new gpio status */
+
+	/* Return if this is a repeated (bouncing) event */
+	if(bounce())
+		return;
+
+	/* we want to make keystrokes */
+	for( i=0; i< BUTTON_COUNT; i++) {
+		bit_mask = 1<<i;
+		if (button_state & bit_mask) {
+			key_choice = button_map[i];
+			/* toggle key down */
+			input_report_key(dev, key_choice, 1);
+			/* toggle key up */
+			input_report_key(dev, key_choice, 0);
+			printk("ibutton gpio %d stat %x scan code %d\r\n", 
+					i, button_state, key_choice);
+			/* Only report the first key event; it doesn't make 
+			 * sense for two keys to be pressed at the same time, 
+			 * and causes problems with the directional keys 
+			 * return;	
+			 */
+		}
+	}
+}
+
+static int 
+button_translate(unsigned char scancode, unsigned char *keycode, char raw_mode) 
+{
+	static int prev_scancode;
+	
+	printk( "ibutton.c: translate: scancode=%x raw_mode=%x\n", 
+			scancode, raw_mode);
+
+	if (scancode == 0xe0 || scancode == 0xe1) {
+		prev_scancode = scancode;
+		return 0;
+	}
+
+	if (scancode == 0x00 || scancode == 0xff) {
+		prev_scancode = 0;
+		return 0;
+	}
+
+	*keycode = scancode;
+
+	return 1;
+}
+
+/* init button hardware */
+static int button_hw_init(void)
+{
+	unsigned int	ipinfunc=0;	
+
+	printk("au1200_ibutton.c: Initializing buttons hardware\n");
+
+	// initialize GPIO pin function assignments	
+
+	ipinfunc = au_readl(SYS_PINFUNC);
+
+	ipinfunc &= ~(SYS_PINFUNC_DMA | SYS_PINFUNC_S0A | SYS_PINFUNC_S0B);	
+	au_writel( ipinfunc ,SYS_PINFUNC);
+	
+	ipinfunc |=  (SYS_PINFUNC_S0C);
+	au_writel( ipinfunc ,SYS_PINFUNC);
+	
+	return 0;
+}
+
+/* button driver init */
+static int __init button_init(void)
+{
+	int ret, i;
+	unsigned int flag=0;
+
+	printk("au1200_ibutton.c: button_init()\r\n");
+	
+	button_hw_init();
+	
+	/* register all button irq handler */
+	
+	for(i=0; i< sizeof(button_map)/sizeof(button_map[0]); i++)
+	{
+		/* register irq <-- gpio 1 ,6 ,12 , 17 ,19 , 26 ,28 */
+		if(button_map[i] != 0)	
+		{
+			ret = request_irq(AU1000_GPIO_0 + i , 
+					&button_interrupt , SA_INTERRUPT , 
+					DRIVER_NAME , &dev);
+			if(ret) flag |= 1<<i;
+		}
+	}
+
+	printk("au1200_ibutton.c: request_irq,ret:0x%x\r\n",ret);
+	
+	if (ret) {
+		printk("au1200_ibutton.c: request_irq:%X failed\r\n",flag);
+		return ret;
+	}
+		
+	dev.name = DRIVER_NAME;
+	dev.evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+
+	for (i=0;i<sizeof(button_map)/sizeof(button_map[0]);i++)
+	{
+		dev.keybit[LONG(button_map[i])] |= BIT(button_map[i]);
+	}
+	
+	input_register_device(&dev);
+
+	/* ready to receive interrupts */
+
+	return 0;
+}
+
+/* button driver exit */
+static void __exit button_exit(void)
+{
+	int i;
+	
+	for(i=0;i<sizeof(button_map)/sizeof(button_map[0]);i++)
+	{
+		if(button_map[i] != 0)
+		{
+			free_irq( AU1000_GPIO_0 + i, &dev);
+		}
+	}
+	
+	input_unregister_device(&dev);
+	
+	printk("au1200_ibutton.c: button_exit()\r\n");
+}
+
+module_init(button_init);
+module_exit(button_exit);
+
+MODULE_AUTHOR( DRIVER_AUTHOR );
+MODULE_DESCRIPTION( DRIVER_DESC );
+MODULE_LICENSE("GPL");
diff -Nur linux-2.4.30/arch/mips/au1000/ficmmp/au1xxx_dock.c linux-2.4.30-linux-mips.org/arch/mips/au1000/ficmmp/au1xxx_dock.c
--- linux-2.4.30/arch/mips/au1000/ficmmp/au1xxx_dock.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/ficmmp/au1xxx_dock.c	2005-05-12 17:57:05.000000000 +0200
@@ -0,0 +1,261 @@
+/*
+ *  Copyright (C) 2003 Metrowerks, All Rights Reserved.
+ *
+ * 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/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <asm/au1000.h>
+#include <asm/uaccess.h>
+#include <asm/au1xxx_gpio.h>
+
+
+#if defined(CONFIG_MIPS_FICMMP)
+	#define DOCK_GPIO	215
+#else
+	#error Unsupported Au1xxx Platform
+#endif
+
+#define MAKE_FLAG 0x20
+
+#undef DEBUG
+
+#define DEBUG 0
+//#define DEBUG 1
+
+#if DEBUG
+#define DPRINTK(format, args...) printk(__FUNCTION__ ": "  format, ## args)
+#else
+#define DPRINTK(format, args...) do { } while (0)
+#endif
+
+/* Please note that this driver is based on a timer and is not interrupt
+ * driven.  If you are going to make use of this driver, you will need to have
+ * your application open the dock listing from the /dev directory first.
+ */
+
+struct au1xxx_dock {
+	struct fasync_struct *fasync;
+	wait_queue_head_t     read_wait;
+	int open_count;
+	unsigned int debounce;
+	unsigned int current;
+	unsigned int last;
+};
+
+static struct au1xxx_dock dock_info;
+
+
+static void dock_timer_periodic(void *data);
+
+static struct tq_struct dock_task = {
+	routine:	dock_timer_periodic,
+	data:		NULL
+};
+
+static int cleanup_flag = 0;
+static DECLARE_WAIT_QUEUE_HEAD(cleanup_wait_queue);
+
+
+static unsigned int read_dock_state(void)
+{
+	u32 state;
+
+	state = au1xxx_gpio_read(DOCK_GPIO);
+	
+	/* printk( "Current Dock State: %d\n", state ); */
+
+	return state;
+}
+
+
+static void dock_timer_periodic(void *data)
+{
+	struct au1xxx_dock *dock = (struct au1xxx_dock *)data;
+	unsigned long dock_state;
+	
+	/* If cleanup wants us to die */
+	if (cleanup_flag) {
+		/* now cleanup_module can return */
+		wake_up(&cleanup_wait_queue); 
+	} else {
+		/* put ourselves back in the task queue */
+		queue_task(&dock_task, &tq_timer);	
+	}
+
+	/* read current dock */
+	dock_state = read_dock_state();
+
+	/* if dock states hasn't changed */
+	/* save time and be done. */
+	if (dock_state == dock->current) {
+		return;
+	}
+	
+	if (dock_state == dock->debounce) {
+		dock->current = dock_state;
+	} else {
+		dock->debounce = dock_state;
+	}
+	if (dock->current != dock->last) {
+		if (waitqueue_active(&dock->read_wait)) {
+		    wake_up_interruptible(&dock->read_wait);
+		}
+	}
+}
+
+
+static ssize_t au1xxx_dock_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
+{
+	struct au1xxx_dock *dock = filp->private_data;
+	char event[3];
+	int last;
+	int cur;
+	int err;
+	
+try_again:
+
+	while (dock->current == dock->last) {
+		if (filp->f_flags & O_NONBLOCK) {
+			return -EAGAIN;
+		}
+		interruptible_sleep_on(&dock->read_wait);
+		if (signal_pending(current)) {
+			return -ERESTARTSYS;
+		}
+	}
+	
+	cur  = dock->current;
+	last = dock->last;
+
+	if(cur != last)
+	{
+		event[0] = cur ? 'D' : 'U';
+		event[1] = '\r';
+		event[2] = '\n';
+	}
+	else
+		goto try_again;
+	
+	dock->last = cur;
+	err = copy_to_user(buffer, &event, 3);
+	if (err) {
+		return err;
+	}
+	
+	return 3;
+}
+
+
+static int au1xxx_dock_open(struct inode *inode, struct file *filp)
+{
+	struct au1xxx_dock *dock = &dock_info;
+
+	MOD_INC_USE_COUNT;
+
+	filp->private_data = dock;
+
+	if (dock->open_count++ == 0) {
+		dock_task.data = dock;
+		cleanup_flag = 0;
+		queue_task(&dock_task, &tq_timer);
+	}
+
+	return 0;
+}
+
+
+static unsigned int au1xxx_dock_poll(struct file *filp, poll_table *wait)
+{
+	struct au1xxx_dock *dock = filp->private_data;
+	int ret = 0;
+
+	DPRINTK("start\n");
+	poll_wait(filp, &dock->read_wait, wait);
+	if (dock->current != dock->last) {
+		ret = POLLIN | POLLRDNORM;
+	}
+	return ret;
+}
+
+
+static int au1xxx_dock_release(struct inode *inode, struct file *filp)
+{
+	struct au1xxx_dock *dock = filp->private_data;
+
+	DPRINTK("start\n");
+
+	if (--dock->open_count == 0) {
+		cleanup_flag = 1;
+		sleep_on(&cleanup_wait_queue);
+	}
+	MOD_DEC_USE_COUNT;
+	
+	return 0;
+}
+
+
+
+static struct file_operations au1xxx_dock_fops = {
+	owner:		THIS_MODULE,
+	read:		au1xxx_dock_read,
+	poll:		au1xxx_dock_poll,
+	open:		au1xxx_dock_open,
+	release:	au1xxx_dock_release,
+};
+
+/*
+ * The au1xxx dock is a misc device:
+ * Major 10 char
+ * Minor 22        /dev/dock
+ * 
+ * This is /dev/misc/dock if devfs is used.
+ */
+
+static struct miscdevice au1xxx_dock_dev = {
+	minor:	23,
+	name:	"dock",
+	fops:	&au1xxx_dock_fops,
+};
+
+static int __init au1xxx_dock_init(void)
+{
+	struct au1xxx_dock *dock = &dock_info;
+	int ret;
+
+	DPRINTK("Initializing dock driver\n");
+	dock->open_count = 0;
+	cleanup_flag        = 0;
+	init_waitqueue_head(&dock->read_wait);
+
+
+	/* yamon configures GPIO pins for the dock
+	 * no initialization needed
+	 */
+
+	ret = misc_register(&au1xxx_dock_dev);
+
+	DPRINTK("dock driver fully initialized.\n");
+
+	return ret;
+}
+
+
+static void __exit au1xxx_dock_exit(void)
+{
+	DPRINTK("unloading dock driver\n");
+	misc_deregister(&au1xxx_dock_dev);
+}
+
+
+module_init(au1xxx_dock_init);
+module_exit(au1xxx_dock_exit);
diff -Nur linux-2.4.30/arch/mips/au1000/ficmmp/board_setup.c linux-2.4.30-linux-mips.org/arch/mips/au1000/ficmmp/board_setup.c
--- linux-2.4.30/arch/mips/au1000/ficmmp/board_setup.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/ficmmp/board_setup.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,201 @@
+/*
+ *
+ * BRIEF MODULE DESCRIPTION
+ *	Alchemy Pb1200 board setup.
+ *
+ *  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.
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+#include <linux/mc146818rtc.h>
+#include <linux/delay.h>
+#include <linux/ide.h>
+
+#if defined(CONFIG_BLK_DEV_IDE_AU1XXX)
+#include <linux/ide.h>
+#endif
+
+#include <asm/cpu.h>
+#include <asm/bootinfo.h>
+#include <asm/irq.h>
+#include <asm/keyboard.h>
+#include <asm/mipsregs.h>
+#include <asm/reboot.h>
+#include <asm/pgtable.h>
+#include <asm/au1000.h>
+#include <asm/ficmmp.h>
+#include <asm/au1xxx_dbdma.h>
+#include <asm/au1xxx_gpio.h>
+
+extern struct rtc_ops no_rtc_ops;
+
+/* value currently in the board configuration register */
+u16 ficmmp_config = 0;
+
+void board_reset (void)
+{
+	au_writel(0, 0xAD80001C);
+}
+
+void board_power_off (void)
+{
+}
+
+void __init board_setup(void)
+{
+	char *argptr = NULL;
+	u32 pin_func;
+	rtc_ops = &no_rtc_ops;
+
+	ficmmp_config_init();	//Initialize FIC control register
+	
+#if 0
+	/* Enable PSC1 SYNC for AC97.  Normaly done in audio driver,
+	 * but it is board specific code, so put it here.
+	 */
+	pin_func = au_readl(SYS_PINFUNC);
+	au_sync();
+	pin_func |= SYS_PF_MUST_BE_SET | SYS_PF_PSC1_S1;
+	au_writel(pin_func, SYS_PINFUNC);
+
+	au_writel(0, (u32)bcsr|0x10); /* turn off pcmcia power */
+	au_sync();
+#endif
+
+#if defined( CONFIG_I2C_ALGO_AU1550 )
+	{
+	u32 freq0, clksrc;
+
+	/* Select SMBUS in CPLD */
+	/* bcsr->resets &= ~(BCSR_RESETS_PCS0MUX); */
+
+	pin_func = au_readl(SYS_PINFUNC);
+	au_sync();
+	pin_func &= ~(3<<17 | 1<<4);
+	/* Set GPIOs correctly */
+	pin_func |= 2<<17;
+	au_writel(pin_func, SYS_PINFUNC);
+	au_sync();
+
+	/* The i2c driver depends on 50Mhz clock */
+	freq0 = au_readl(SYS_FREQCTRL0);
+	au_sync();
+	freq0 &= ~(SYS_FC_FRDIV1_MASK | SYS_FC_FS1 | SYS_FC_FE1);
+	freq0 |= (3<<SYS_FC_FRDIV1_BIT);
+	/* 396Mhz / (3+1)*2 == 49.5Mhz */
+	au_writel(freq0, SYS_FREQCTRL0);
+	au_sync();
+	freq0 |= SYS_FC_FE1;
+	au_writel(freq0, SYS_FREQCTRL0);
+	au_sync();
+
+	clksrc = au_readl(SYS_CLKSRC);
+	au_sync();
+	clksrc &= ~0x01f00000;
+	/* bit 22 is EXTCLK0 for PSC0 */
+	clksrc |= (0x3 << 22);
+	au_writel(clksrc, SYS_CLKSRC);
+	au_sync();
+	}
+#endif
+
+#ifdef CONFIG_FB_AU1200
+	argptr = prom_getcmdline();
+	strcat(argptr, " video=au1200fb:");
+#endif
+
+#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 scan);
+	extern int auide_probe (void);
+
+	au1xxx_gpio_write(9, 1);
+	printk("B4001010: %X\n", *((u32*)0xB4001010));
+	printk("B4001014: %X\n", *((u32*)0xB4001014));
+	printk("B4001018: %X\n", *((u32*)0xB4001018));
+	printk("B1900100: %X\n", *((u32*)0xB1900100));
+	
+	ide_ops = &auide_ops;
+	ide_register_driver( auide_probe );
+
+#if 0
+	ficmmp_config_clear(FICMMP_CONFIG_IDERST);
+	mdelay(100);
+	ficmmp_config_set(FICMMP_CONFIG_IDERST);
+	mdelay(100);
+#endif
+#endif
+
+	/* The Pb1200 development board uses external MUX for PSC0 to
+	support SMB/SPI. bcsr->resets bit 12: 0=SMB 1=SPI
+	*/
+#if defined(CONFIG_AU1550_PSC_SPI) && defined(CONFIG_I2C_ALGO_AU1550)
+	#error I2C and SPI are mutually exclusive. Both are physically connected to PSC0.\
+			Refer to Pb1200 documentation.
+#elif defined( CONFIG_AU1550_PSC_SPI )
+	//bcsr->resets |= BCSR_RESETS_PCS0MUX;
+#elif defined( CONFIG_I2C_ALGO_AU1550 )
+	//bcsr->resets &= (~BCSR_RESETS_PCS0MUX);
+#endif
+	au_sync();
+
+	printk("FIC Multimedia Player Board\n");
+	au1xxx_gpio_tristate(5);
+	printk("B1900100: %X\n", *((volatile u32*)0xB1900100));
+	printk("B190002C: %X\n", *((volatile u32*)0xB190002C));
+}
+
+int
+board_au1200fb_panel (void)
+{
+	au1xxx_gpio_tristate(6);
+
+	if (au1xxx_gpio_read(12) == 0)
+		return 9; /* FS453_640x480 (Composite/S-Video) */
+	else
+		return 7; /* Sharp 320x240 TFT */
+}
+
+int
+board_au1200fb_panel_init (void)
+{
+	/*Enable data buffers*/
+	ficmmp_config_clear(FICMMP_CONFIG_LCMDATAOUT);
+	/*Take LCD out of reset*/
+	ficmmp_config_set(FICMMP_CONFIG_LCMPWREN | FICMMP_CONFIG_LCMEN);
+	return 0;
+}
+
+int 
+board_au1200fb_panel_shutdown (void)
+{
+	/*Disable data buffers*/
+	ficmmp_config_set(FICMMP_CONFIG_LCMDATAOUT);
+	/*Put LCD in reset, remove power*/
+	ficmmp_config_clear(FICMMP_CONFIG_LCMEN | FICMMP_CONFIG_LCMPWREN);
+	return 0;
+}
+
diff -Nur linux-2.4.30/arch/mips/au1000/ficmmp/init.c linux-2.4.30-linux-mips.org/arch/mips/au1000/ficmmp/init.c
--- linux-2.4.30/arch/mips/au1000/ficmmp/init.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/ficmmp/init.c	2005-05-12 17:57:05.000000000 +0200
@@ -0,0 +1,76 @@
+/*
+ *
+ * BRIEF MODULE DESCRIPTION
+ *	PB1200 board setup
+ *
+ *  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.
+ */
+
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/bootmem.h>
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+int prom_argc;
+char **prom_argv, **prom_envp;
+extern void  __init prom_init_cmdline(void);
+extern char *prom_getenv(char *envname);
+
+const char *get_system_type(void)
+{
+	return "FIC Multimedia Player (Au1200)";
+}
+
+u32 mae_memsize = 0;
+
+int __init prom_init(int argc, char **argv, char **envp, int *prom_vec)
+{
+	unsigned char *memsize_str;
+	unsigned long memsize;
+
+	prom_argc = argc;
+	prom_argv = argv;
+	prom_envp = envp;
+
+	mips_machgroup = MACH_GROUP_ALCHEMY;
+	mips_machtype = MACH_PB1000;	/* set the platform # */   
+	prom_init_cmdline();
+
+	memsize_str = prom_getenv("memsize");
+	if (!memsize_str) {
+		memsize = 0x08000000;
+	} else {
+		memsize = simple_strtol(memsize_str, NULL, 0);
+	}
+
+	/* reserved 32MB for MAE driver */
+	memsize -= (32 * 1024 * 1024);
+	add_memory_region(0, memsize, BOOT_MEM_RAM);
+	mae_memsize = memsize; /* for drivers/char/au1xxx_mae.c */
+	return 0;
+}
+
diff -Nur linux-2.4.30/arch/mips/au1000/ficmmp/irqmap.c linux-2.4.30-linux-mips.org/arch/mips/au1000/ficmmp/irqmap.c
--- linux-2.4.30/arch/mips/au1000/ficmmp/irqmap.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/ficmmp/irqmap.c	2005-05-12 17:57:05.000000000 +0200
@@ -0,0 +1,61 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *	Au1xxx irq map table
+ *
+ *  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.
+ */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/kernel_stat.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/delay.h>
+
+#include <asm/bitops.h>
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <asm/mipsregs.h>
+#include <asm/system.h>
+#include <asm/au1000.h>
+#include <asm/ficmmp.h>
+
+au1xxx_irq_map_t au1xxx_irq_map[] = {
+	{ FICMMP_IDE_INT, INTC_INT_HIGH_LEVEL, 0 },
+	{ AU1XXX_SMC91111_IRQ, INTC_INT_HIGH_LEVEL, 0 },
+	{ AU1000_GPIO_1 , INTC_INT_FALL_EDGE, 0 },		// main button
+	{ AU1000_GPIO_6 , INTC_INT_RISE_EDGE, 0 },		// select button
+	{ AU1000_GPIO_12, INTC_INT_FALL_EDGE, 0 },		// guide button
+	{ AU1000_GPIO_17, INTC_INT_RISE_EDGE, 0 },		// down button
+	{ AU1000_GPIO_19, INTC_INT_RISE_EDGE, 0 },		// left button
+	{ AU1000_GPIO_26, INTC_INT_RISE_EDGE, 0 },		// right button
+	{ AU1000_GPIO_28, INTC_INT_RISE_EDGE, 0 },		// up button
+};
+
+int au1xxx_nr_irqs = sizeof(au1xxx_irq_map)/sizeof(au1xxx_irq_map_t);
+
diff -Nur linux-2.4.30/arch/mips/au1000/ficmmp/Makefile linux-2.4.30-linux-mips.org/arch/mips/au1000/ficmmp/Makefile
--- linux-2.4.30/arch/mips/au1000/ficmmp/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/ficmmp/Makefile	2005-05-12 17:57:05.000000000 +0200
@@ -0,0 +1,25 @@
+#
+#  Copyright 2000 MontaVista Software Inc.
+#  Author: MontaVista Software, Inc.
+#     	ppopov@mvista.com or source@mvista.com
+#
+# Makefile for the Alchemy Semiconductor FIC board.
+#
+# 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).
+#
+
+USE_STANDARD_AS_RULE := true
+
+O_TARGET := ficmmp.o
+
+obj-y := init.o board_setup.o irqmap.o au1200_ibutton.o au1xxx_dock.o
+
+ifdef CONFIG_MMC
+obj-y += mmc_support.o
+export-objs +=mmc_support.o
+endif
+
+
+include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/arch/mips/au1000/hydrogen3/board_setup.c linux-2.4.30-linux-mips.org/arch/mips/au1000/hydrogen3/board_setup.c
--- linux-2.4.30/arch/mips/au1000/hydrogen3/board_setup.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/hydrogen3/board_setup.c	2005-05-12 17:57:05.000000000 +0200
@@ -51,12 +51,19 @@
 {
 }
 
+void board_power_off (void)
+{
+}
+
 void __init board_setup(void)
 {
 	u32 pin_func;
 
 	rtc_ops = &no_rtc_ops;
 
+	/* Set GPIO14 high to make CD/DAT1 high for MMC to work */
+	au_writel(1<<14, SYS_OUTPUTSET);
+
 #ifdef CONFIG_AU1X00_USB_DEVICE
 	// 2nd USB port is USB device
 	pin_func = au_readl(SYS_PINFUNC) & (u32)(~0x8000);
diff -Nur linux-2.4.30/arch/mips/au1000/hydrogen3/buttons.c linux-2.4.30-linux-mips.org/arch/mips/au1000/hydrogen3/buttons.c
--- linux-2.4.30/arch/mips/au1000/hydrogen3/buttons.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/hydrogen3/buttons.c	2005-05-12 17:57:05.000000000 +0200
@@ -0,0 +1,308 @@
+/*
+ *  Copyright (C) 2003 Metrowerks, All Rights Reserved.
+ *
+ * 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/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/miscdevice.h>
+#include <linux/errno.h>
+#include <linux/poll.h>
+#include <asm/au1000.h>
+#include <asm/uaccess.h>
+
+#define BUTTON_SELECT	(1<<1)
+#define BUTTON_1	(1<<2)
+#define BUTTON_2	(1<<3)
+#define BUTTON_ONOFF	(1<<6)
+#define BUTTON_3	(1<<7)
+#define BUTTON_4	(1<<8)
+#define BUTTON_LEFT	(1<<9)
+#define BUTTON_DOWN	(1<<10)
+#define BUTTON_RIGHT	(1<<11)
+#define BUTTON_UP	(1<<12)
+
+#define BUTTON_MASK (\
+    BUTTON_SELECT	\
+    | BUTTON_1   	\
+    | BUTTON_2		\
+    | BUTTON_ONOFF	\
+    | BUTTON_3		\
+    | BUTTON_4		\
+    | BUTTON_LEFT	\
+    | BUTTON_DOWN	\
+    | BUTTON_RIGHT	\
+    | BUTTON_UP		\
+    )
+
+#define BUTTON_INVERT (\
+    BUTTON_SELECT	\
+    | BUTTON_1   	\
+    | BUTTON_2		\
+    | BUTTON_3		\
+    | BUTTON_4		\
+    | BUTTON_LEFT	\
+    | BUTTON_DOWN	\
+    | BUTTON_RIGHT	\
+    | BUTTON_UP		\
+    )
+
+
+
+#define MAKE_FLAG 0x20
+
+#undef DEBUG
+
+#define DEBUG 0
+//#define DEBUG 1
+
+#if DEBUG
+#define DPRINTK(format, args...) printk(__FUNCTION__ ": "  format, ## args)
+#else
+#define DPRINTK(format, args...) do { } while (0)
+#endif
+
+/* Please note that this driver is based on a timer and is not interrupt
+ * driven.  If you are going to make use of this driver, you will need to have
+ * your application open the buttons listing from the /dev directory first.
+ */
+
+struct hydrogen3_buttons {
+	struct fasync_struct *fasync;
+	wait_queue_head_t     read_wait;
+	int open_count;
+	unsigned int debounce;
+	unsigned int current;
+	unsigned int last;
+};
+
+static struct hydrogen3_buttons buttons_info;
+
+
+static void button_timer_periodic(void *data);
+
+static struct tq_struct button_task = {
+	routine:	button_timer_periodic,
+	data:		NULL
+};
+
+static int cleanup_flag = 0;
+static DECLARE_WAIT_QUEUE_HEAD(cleanup_wait_queue);
+
+
+static unsigned int read_button_state(void)
+{
+	unsigned long state;
+
+	state = inl(SYS_PINSTATERD) & BUTTON_MASK;
+	state ^= BUTTON_INVERT;
+	
+	DPRINTK( "Current Button State: %d\n", state );
+
+	return state;
+}
+
+
+static void button_timer_periodic(void *data)
+{
+	struct hydrogen3_buttons *buttons = (struct hydrogen3_buttons *)data;
+	unsigned long button_state;
+	
+	// If cleanup wants us to die
+	if (cleanup_flag) {
+		wake_up(&cleanup_wait_queue);		// now cleanup_module can return
+	} else {
+		queue_task(&button_task, &tq_timer);	// put ourselves back in the task queue
+	}
+
+	// read current buttons
+	button_state = read_button_state();
+
+	// if no buttons are down and nothing to do then
+	// save time and be done.
+	if ((button_state == 0) && (buttons->current == 0)) {
+		return;
+	}
+	
+	if (button_state == buttons->debounce) {
+		buttons->current = button_state;
+	} else {
+		buttons->debounce = button_state;
+	}
+//	printk("0x%04x\n", button_state);
+	if (buttons->current != buttons->last) {
+		if (waitqueue_active(&buttons->read_wait)) {
+		    wake_up_interruptible(&buttons->read_wait);
+		}
+	}
+}
+
+
+static ssize_t hydrogen3_buttons_read(struct file *filp, char *buffer, size_t count, loff_t *ppos)
+{
+	struct hydrogen3_buttons *buttons = filp->private_data;
+	char events[16];
+	int index;
+	int last;
+	int cur;
+	int bit;
+	int bit_mask;
+	int err;
+	
+	DPRINTK("start\n");
+
+try_again:
+
+	while (buttons->current == buttons->last) {
+		if (filp->f_flags & O_NONBLOCK) {
+			return -EAGAIN;
+		}
+		interruptible_sleep_on(&buttons->read_wait);
+		if (signal_pending(current)) {
+			return -ERESTARTSYS;
+		}
+	}
+	
+	cur  = buttons->current;
+	last = buttons->last;
+	
+	index    = 0;
+	bit_mask = 1;
+	for (bit = 0; (bit < 16) && count; bit++) {
+		if ((cur ^ last) & bit_mask) {
+			if (cur & bit_mask) {
+				events[index] = (bit | MAKE_FLAG) + 'A';
+				last |= bit_mask;
+			} else {
+				events[index] = bit + 'A';
+				last &= ~bit_mask;
+			}
+			index++;
+			count--;
+		}
+		bit_mask <<= 1;
+	}
+	buttons->last = last;
+	
+	if (index == 0) {
+		goto try_again;
+	}
+	
+	err = copy_to_user(buffer, events, index);
+	if (err) {
+		return err;
+	}
+	
+	return index;
+}
+
+
+static int hydrogen3_buttons_open(struct inode *inode, struct file *filp)
+{
+	struct hydrogen3_buttons *buttons = &buttons_info;
+
+	DPRINTK("start\n");
+	MOD_INC_USE_COUNT;
+
+	filp->private_data = buttons;
+
+	if (buttons->open_count++ == 0) {
+		button_task.data = buttons;
+		cleanup_flag = 0;
+		queue_task(&button_task, &tq_timer);
+	}
+
+	return 0;
+}
+
+
+static unsigned int hydrogen3_buttons_poll(struct file *filp, poll_table *wait)
+{
+	struct hydrogen3_buttons *buttons = filp->private_data;
+	int ret = 0;
+
+	DPRINTK("start\n");
+	poll_wait(filp, &buttons->read_wait, wait);
+	if (buttons->current != buttons->last) {
+		ret = POLLIN | POLLRDNORM;
+	}
+	return ret;
+}
+
+
+static int hydrogen3_buttons_release(struct inode *inode, struct file *filp)
+{
+	struct hydrogen3_buttons *buttons = filp->private_data;
+
+	DPRINTK("start\n");
+
+	if (--buttons->open_count == 0) {
+		cleanup_flag = 1;
+		sleep_on(&cleanup_wait_queue);
+	}
+	MOD_DEC_USE_COUNT;
+	
+	return 0;
+}
+
+
+
+static struct file_operations hydrogen3_buttons_fops = {
+	owner:		THIS_MODULE,
+	read:		hydrogen3_buttons_read,
+	poll:		hydrogen3_buttons_poll,
+	open:		hydrogen3_buttons_open,
+	release:	hydrogen3_buttons_release,
+};
+
+/*
+ * The hydrogen3 buttons is a misc device:
+ * Major 10 char
+ * Minor 22        /dev/buttons
+ * 
+ * This is /dev/misc/buttons if devfs is used.
+ */
+
+static struct miscdevice hydrogen3_buttons_dev = {
+	minor:	22,
+	name:	"buttons",
+	fops:	&hydrogen3_buttons_fops,
+};
+
+static int __init hydrogen3_buttons_init(void)
+{
+	struct hydrogen3_buttons *buttons = &buttons_info;
+	int ret;
+
+	DPRINTK("Initializing buttons driver\n");
+	buttons->open_count = 0;
+	cleanup_flag        = 0;
+	init_waitqueue_head(&buttons->read_wait);
+
+
+	// yamon configures GPIO pins for the buttons
+	// no initialization needed
+
+	ret = misc_register(&hydrogen3_buttons_dev);
+
+	DPRINTK("Buttons driver fully initialized.\n");
+
+	return ret;
+}
+
+
+static void __exit hydrogen3_buttons_exit(void)
+{
+	DPRINTK("unloading buttons driver\n");
+	misc_deregister(&hydrogen3_buttons_dev);
+}
+
+
+module_init(hydrogen3_buttons_init);
+module_exit(hydrogen3_buttons_exit);
diff -Nur linux-2.4.30/arch/mips/au1000/hydrogen3/Makefile linux-2.4.30-linux-mips.org/arch/mips/au1000/hydrogen3/Makefile
--- linux-2.4.30/arch/mips/au1000/hydrogen3/Makefile	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/hydrogen3/Makefile	2005-05-12 17:57:05.000000000 +0200
@@ -14,6 +14,11 @@
 
 O_TARGET := hydrogen3.o
 
-obj-y := init.o board_setup.o irqmap.o
+obj-y := init.o board_setup.o irqmap.o buttons.o
+
+ifdef CONFIG_MMC
+obj-y += mmc_support.o
+export-objs +=mmc_support.o
+endif
 
 include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/arch/mips/au1000/hydrogen3/mmc_support.c linux-2.4.30-linux-mips.org/arch/mips/au1000/hydrogen3/mmc_support.c
--- linux-2.4.30/arch/mips/au1000/hydrogen3/mmc_support.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/hydrogen3/mmc_support.c	2005-05-12 17:57:05.000000000 +0200
@@ -0,0 +1,89 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *
+ *	MMC support routines for Hydrogen3.
+ *
+ *
+ * Copyright (c) 2003-2004 Embedded Edge, LLC.
+ * Author: Embedded Edge, LLC.
+ * Contact: dan@embeddededge.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  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.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/irq.h>
+#include <asm/au1000.h>
+#include <asm/au1100_mmc.h>
+
+#define GPIO_17_WP 0x20000
+
+/* SD/MMC controller support functions */
+
+/*
+ * Detect card.
+ */
+void mmc_card_inserted(int _n_, int *_res_)
+{
+	u32 gpios = au_readl(SYS_PINSTATERD);
+	u32 emptybit = (1<<16);
+	*_res_ = ((gpios & emptybit) == 0);
+}
+
+/*
+ * Check card write protection.
+ */
+void mmc_card_writable(int _n_, int *_res_)
+{
+	unsigned long mmc_wp, board_specific; 
+		board_specific = au_readl(SYS_OUTPUTSET);
+		mmc_wp=GPIO_17_WP;
+		if (!(board_specific & mmc_wp)) {/* low means card writable */ 
+			*_res_ = 1; 
+		} else {
+			*_res_ = 0; 
+		} 
+}
+/*
+ * Apply power to card slot.
+ */
+void mmc_power_on(int _n_)
+{
+}
+
+/*
+ * Remove power from card slot.
+ */
+void mmc_power_off(int _n_)
+{
+}
+
+EXPORT_SYMBOL(mmc_card_inserted);
+EXPORT_SYMBOL(mmc_card_writable);
+EXPORT_SYMBOL(mmc_power_on);
+EXPORT_SYMBOL(mmc_power_off);
+
diff -Nur linux-2.4.30/arch/mips/au1000/mtx-1/board_setup.c linux-2.4.30-linux-mips.org/arch/mips/au1000/mtx-1/board_setup.c
--- linux-2.4.30/arch/mips/au1000/mtx-1/board_setup.c	2004-02-18 14:36:30.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/mtx-1/board_setup.c	2005-05-12 17:57:05.000000000 +0200
@@ -48,6 +48,12 @@
 
 extern struct rtc_ops no_rtc_ops;
 
+void board_reset (void)
+{
+	/* Hit BCSR.SYSTEM_CONTROL[SW_RST] */
+	au_writel(0x00000000, 0xAE00001C);
+}
+
 void __init board_setup(void)
 {
 	rtc_ops = &no_rtc_ops;
diff -Nur linux-2.4.30/arch/mips/au1000/mtx-1/irqmap.c linux-2.4.30-linux-mips.org/arch/mips/au1000/mtx-1/irqmap.c
--- linux-2.4.30/arch/mips/au1000/mtx-1/irqmap.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/mtx-1/irqmap.c	2005-05-12 17:57:05.000000000 +0200
@@ -72,10 +72,10 @@
 	 *	A       B       C       D
 	 */
 	{
-		{INTA, INTB, INTC, INTD},   /* IDSEL 0 */
-		{INTA, INTB, INTC, INTD},   /* IDSEL 1 */
-		{INTA, INTB, INTC, INTD},   /* IDSEL 2 */
-		{INTA, INTB, INTC, INTD},   /* IDSEL 3 */
+		{INTA, INTB, INTX, INTX},   /* IDSEL 0 */
+		{INTB, INTA, INTX, INTX},   /* IDSEL 1 */
+		{INTC, INTD, INTX, INTX},   /* IDSEL 2 */
+		{INTD, INTC, INTX, INTX},   /* IDSEL 3 */
 	};
 	const long min_idsel = 0, max_idsel = 3, irqs_per_slot = 4;
 	return PCI_IRQ_TABLE_LOOKUP;
diff -Nur linux-2.4.30/arch/mips/au1000/pb1000/board_setup.c linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1000/board_setup.c
--- linux-2.4.30/arch/mips/au1000/pb1000/board_setup.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1000/board_setup.c	2005-05-12 17:57:05.000000000 +0200
@@ -58,6 +58,10 @@
 {
 }
 
+void board_power_off (void)
+{
+}
+
 void __init board_setup(void)
 {
 	u32 pin_func, static_cfg0;
diff -Nur linux-2.4.30/arch/mips/au1000/pb1100/board_setup.c linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1100/board_setup.c
--- linux-2.4.30/arch/mips/au1000/pb1100/board_setup.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1100/board_setup.c	2005-05-12 17:57:05.000000000 +0200
@@ -62,6 +62,10 @@
     au_writel(0x00000000, 0xAE00001C);
 }
 
+void board_power_off (void)
+{
+}
+
 void __init board_setup(void)
 {
 	u32 pin_func;
diff -Nur linux-2.4.30/arch/mips/au1000/pb1100/Makefile linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1100/Makefile
--- linux-2.4.30/arch/mips/au1000/pb1100/Makefile	2003-08-25 13:44:39.000000000 +0200
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1100/Makefile	2005-05-12 17:57:05.000000000 +0200
@@ -16,4 +16,10 @@
 
 obj-y := init.o board_setup.o irqmap.o
 
+
+ifdef CONFIG_MMC
+obj-y += mmc_support.o
+export-objs += mmc_support.o
+endif
+
 include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/arch/mips/au1000/pb1100/mmc_support.c linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1100/mmc_support.c
--- linux-2.4.30/arch/mips/au1000/pb1100/mmc_support.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1100/mmc_support.c	2005-05-12 17:57:05.000000000 +0200
@@ -0,0 +1,126 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *
+ *	MMC support routines for PB1100.
+ *
+ *
+ * Copyright (c) 2003-2004 Embedded Edge, LLC.
+ * Author: Embedded Edge, LLC.
+ * Contact: dan@embeddededge.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  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.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/irq.h>
+#include <asm/au1000.h>
+#include <asm/au1100_mmc.h>
+#include <asm/pb1100.h>
+
+
+/* SD/MMC controller support functions */
+
+/*
+ * Detect card.
+ */
+void mmc_card_inserted(int _n_, int *_res_)
+{
+	u32 gpios = au_readl(SYS_PINSTATERD);
+	u32 emptybit = (_n_) ? (1<<15) : (1<<14);
+	*_res_ = ((gpios & emptybit) == 0);
+}
+
+/*
+ * Check card write protection.
+ */
+void mmc_card_writable(int _n_, int *_res_)
+{
+	BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+	unsigned long mmc_wp, board_specific;
+
+	if (_n_) {
+		mmc_wp = BCSR_PCMCIA_SD1_WP;
+	} else {
+		mmc_wp = BCSR_PCMCIA_SD0_WP;
+	}
+
+	board_specific = au_readl((unsigned long)(&bcsr->pcmcia));
+
+	if (!(board_specific & mmc_wp)) {/* low means card writable */
+		*_res_ = 1;
+	} else {
+		*_res_ = 0;
+	}
+}
+
+/*
+ * Apply power to card slot.
+ */
+void mmc_power_on(int _n_)
+{
+	BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+	unsigned long mmc_pwr, board_specific;
+
+	if (_n_) {
+		mmc_pwr = BCSR_PCMCIA_SD1_PWR;
+	} else {
+		mmc_pwr = BCSR_PCMCIA_SD0_PWR;
+	}
+
+	board_specific = au_readl((unsigned long)(&bcsr->pcmcia));
+	board_specific |= mmc_pwr;
+
+	au_writel(board_specific, (int)(&bcsr->pcmcia));
+	au_sync_delay(1);
+}
+
+/*
+ * Remove power from card slot.
+ */
+void mmc_power_off(int _n_)
+{
+	BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+	unsigned long mmc_pwr, board_specific;
+
+	if (_n_) {
+		mmc_pwr = BCSR_PCMCIA_SD1_PWR;
+	} else {
+		mmc_pwr = BCSR_PCMCIA_SD0_PWR;
+	}
+
+	board_specific = au_readl((unsigned long)(&bcsr->pcmcia));
+	board_specific &= ~mmc_pwr;
+
+	au_writel(board_specific, (int)(&bcsr->pcmcia));
+	au_sync_delay(1);
+}
+
+EXPORT_SYMBOL(mmc_card_inserted);
+EXPORT_SYMBOL(mmc_card_writable);
+EXPORT_SYMBOL(mmc_power_on);
+EXPORT_SYMBOL(mmc_power_off);
+
diff -Nur linux-2.4.30/arch/mips/au1000/pb1200/board_setup.c linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1200/board_setup.c
--- linux-2.4.30/arch/mips/au1000/pb1200/board_setup.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1200/board_setup.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,202 @@
+/*
+ *
+ * BRIEF MODULE DESCRIPTION
+ *	Alchemy Pb1200 board setup.
+ *
+ *  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.
+ */
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/console.h>
+#include <linux/mc146818rtc.h>
+#include <linux/delay.h>
+
+#if defined(CONFIG_BLK_DEV_IDE_AU1XXX)
+#include <linux/ide.h>
+#endif
+
+#include <asm/cpu.h>
+#include <asm/bootinfo.h>
+#include <asm/irq.h>
+#include <asm/keyboard.h>
+#include <asm/mipsregs.h>
+#include <asm/reboot.h>
+#include <asm/pgtable.h>
+#include <asm/au1000.h>
+#include <asm/au1xxx_dbdma.h>
+
+#ifdef CONFIG_MIPS_PB1200
+#include <asm/pb1200.h>
+#endif
+
+#ifdef CONFIG_MIPS_DB1200
+#include <asm/db1200.h>
+#define PB1200_ETH_INT DB1200_ETH_INT
+#define PB1200_IDE_INT DB1200_IDE_INT
+#endif
+
+extern struct rtc_ops no_rtc_ops;
+
+extern void _board_init_irq(void);
+extern void	(*board_init_irq)(void);
+
+void board_reset (void)
+{
+	bcsr->resets = 0;
+}
+
+void board_power_off (void)
+{
+	bcsr->resets = 0xC000;
+}
+
+void __init board_setup(void)
+{
+	char *argptr = NULL;
+	u32 pin_func;
+	rtc_ops = &no_rtc_ops;
+
+#if 0
+	/* Enable PSC1 SYNC for AC97.  Normaly done in audio driver,
+	 * but it is board specific code, so put it here.
+	 */
+	pin_func = au_readl(SYS_PINFUNC);
+	au_sync();
+	pin_func |= SYS_PF_MUST_BE_SET | SYS_PF_PSC1_S1;
+	au_writel(pin_func, SYS_PINFUNC);
+
+	au_writel(0, (u32)bcsr|0x10); /* turn off pcmcia power */
+	au_sync();
+#endif
+
+#if defined( CONFIG_I2C_ALGO_AU1550 )
+	{
+	u32 freq0, clksrc;
+
+	/* Select SMBUS in CPLD */
+	bcsr->resets &= ~(BCSR_RESETS_PCS0MUX);
+
+	pin_func = au_readl(SYS_PINFUNC);
+	au_sync();
+	pin_func &= ~(3<<17 | 1<<4);
+	/* Set GPIOs correctly */
+	pin_func |= 2<<17;
+	au_writel(pin_func, SYS_PINFUNC);
+	au_sync();
+
+	/* The i2c driver depends on 50Mhz clock */
+	freq0 = au_readl(SYS_FREQCTRL0);
+	au_sync();
+	freq0 &= ~(SYS_FC_FRDIV1_MASK | SYS_FC_FS1 | SYS_FC_FE1);
+	freq0 |= (3<<SYS_FC_FRDIV1_BIT);
+	/* 396Mhz / (3+1)*2 == 49.5Mhz */
+	au_writel(freq0, SYS_FREQCTRL0);
+	au_sync();
+	freq0 |= SYS_FC_FE1;
+	au_writel(freq0, SYS_FREQCTRL0);
+	au_sync();
+
+	clksrc = au_readl(SYS_CLKSRC);
+	au_sync();
+	clksrc &= ~0x01f00000;
+	/* bit 22 is EXTCLK0 for PSC0 */
+	clksrc |= (0x3 << 22);
+	au_writel(clksrc, SYS_CLKSRC);
+	au_sync();
+	}
+#endif
+
+#ifdef CONFIG_FB_AU1200
+	argptr = prom_getcmdline();
+	strcat(argptr, " video=au1200fb:");
+#endif
+
+#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 scan);
+ 	extern int auide_probe (void);
+
+ 	ide_ops = &auide_ops;
+ 	ide_register_driver( auide_probe );
+#endif
+
+	/* The Pb1200 development board uses external MUX for PSC0 to
+	support SMB/SPI. bcsr->resets bit 12: 0=SMB 1=SPI
+	*/
+#if defined(CONFIG_AU1550_PSC_SPI) && defined(CONFIG_I2C_ALGO_AU1550)
+	#error I2C and SPI are mutually exclusive. Both are physically connected to PSC0.\
+			Refer to Pb1200/Db1200 documentation.
+#elif defined( CONFIG_AU1550_PSC_SPI )
+	bcsr->resets |= BCSR_RESETS_PCS0MUX;
+#elif defined( CONFIG_I2C_ALGO_AU1550 )
+	bcsr->resets &= (~BCSR_RESETS_PCS0MUX);
+#endif
+	au_sync();
+
+#ifdef CONFIG_MIPS_PB1200
+	printk("AMD Alchemy Pb1200 Board\n");
+#endif
+#ifdef CONFIG_MIPS_DB1200
+	printk("AMD Alchemy Db1200 Board\n");
+#endif
+
+	/* Setup Pb1200 External Interrupt Controller */
+	{
+		extern void (*board_init_irq)(void);
+		extern void _board_init_irq(void);
+		board_init_irq = _board_init_irq;
+	}
+}
+
+int
+board_au1200fb_panel (void)
+{
+	BCSR *bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+	int p;
+
+	p = bcsr->switches;
+	p >>= 8;
+	p &= 0x0F;
+	return p;
+}
+
+int
+board_au1200fb_panel_init (void)
+{
+	/* Apply power */
+    BCSR *bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+	bcsr->board |= (BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD | BCSR_BOARD_LCDBL);
+	return 0;
+}
+
+int
+board_au1200fb_panel_shutdown (void)
+{
+	/* Remove power */
+    BCSR *bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+	bcsr->board &= ~(BCSR_BOARD_LCDVEE | BCSR_BOARD_LCDVDD | BCSR_BOARD_LCDBL);
+	return 0;
+}
+
diff -Nur linux-2.4.30/arch/mips/au1000/pb1200/init.c linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1200/init.c
--- linux-2.4.30/arch/mips/au1000/pb1200/init.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1200/init.c	2005-05-12 17:57:05.000000000 +0200
@@ -0,0 +1,72 @@
+/*
+ *
+ * BRIEF MODULE DESCRIPTION
+ *	PB1200 board setup
+ *
+ *  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.
+ */
+
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/bootmem.h>
+#include <asm/addrspace.h>
+#include <asm/bootinfo.h>
+#include <linux/config.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+
+int prom_argc;
+char **prom_argv, **prom_envp;
+extern void  __init prom_init_cmdline(void);
+extern char *prom_getenv(char *envname);
+
+const char *get_system_type(void)
+{
+	return "AMD Alchemy Au1200/Pb1200";
+}
+
+u32 mae_memsize = 0;
+
+int __init prom_init(int argc, char **argv, char **envp, int *prom_vec)
+{
+	unsigned char *memsize_str;
+	unsigned long memsize;
+
+	prom_argc = argc;
+	prom_argv = argv;
+	prom_envp = envp;
+
+	mips_machgroup = MACH_GROUP_ALCHEMY;
+	mips_machtype = MACH_PB1000;	/* set the platform # */   
+	prom_init_cmdline();
+
+	memsize_str = prom_getenv("memsize");
+	if (!memsize_str) {
+		memsize = 0x08000000;
+	} else {
+		memsize = simple_strtol(memsize_str, NULL, 0);
+	}
+	add_memory_region(0, memsize, BOOT_MEM_RAM);
+	return 0;
+}
+
diff -Nur linux-2.4.30/arch/mips/au1000/pb1200/irqmap.c linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1200/irqmap.c
--- linux-2.4.30/arch/mips/au1000/pb1200/irqmap.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1200/irqmap.c	2005-05-12 17:57:05.000000000 +0200
@@ -0,0 +1,180 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *	Au1xxx irq map table
+ *
+ *  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.
+ */
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/irq.h>
+#include <linux/kernel_stat.h>
+#include <linux/module.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <linux/timex.h>
+#include <linux/slab.h>
+#include <linux/random.h>
+#include <linux/delay.h>
+
+#include <asm/bitops.h>
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <asm/mipsregs.h>
+#include <asm/system.h>
+#include <asm/au1000.h>
+
+#ifdef CONFIG_MIPS_PB1200
+#include <asm/pb1200.h>
+#endif
+
+#ifdef CONFIG_MIPS_DB1200
+#include <asm/db1200.h>
+#define PB1200_INT_BEGIN DB1200_INT_BEGIN
+#define PB1200_INT_END DB1200_INT_END
+#endif
+
+au1xxx_irq_map_t au1xxx_irq_map[] = {
+	{ AU1000_GPIO_7, INTC_INT_LOW_LEVEL, 0 }, // This is exteranl interrupt cascade
+};
+
+int au1xxx_nr_irqs = sizeof(au1xxx_irq_map)/sizeof(au1xxx_irq_map_t);
+
+/*
+ *	Support for External interrupts on the PbAu1200 Development platform.
+ */
+static volatile int pb1200_cascade_en=0;
+
+void pb1200_cascade_handler( int irq, void *dev_id, struct pt_regs *regs)
+{
+	unsigned short bisr = bcsr->int_status;
+	int extirq_nr = 0;
+
+	/* Clear all the edge interrupts. This has no effect on level */
+	bcsr->int_status = bisr;
+	for( ; bisr; bisr &= (bisr-1) )
+	{
+		extirq_nr = (PB1200_INT_BEGIN-1) + au_ffs(bisr);
+		/* Ack and dispatch IRQ */
+		do_IRQ(extirq_nr,regs);
+	}
+}
+
+inline void pb1200_enable_irq(unsigned int irq_nr)
+{
+	bcsr->intset_mask = 1<<(irq_nr - PB1200_INT_BEGIN);
+	bcsr->intset = 1<<(irq_nr - PB1200_INT_BEGIN);
+}
+
+inline void pb1200_disable_irq(unsigned int irq_nr)
+{
+	bcsr->intclr_mask = 1<<(irq_nr - PB1200_INT_BEGIN);
+	bcsr->intclr = 1<<(irq_nr - PB1200_INT_BEGIN);
+}
+
+static unsigned int pb1200_startup_irq( unsigned int irq_nr )
+{
+	if (++pb1200_cascade_en == 1)
+	{
+		request_irq(AU1000_GPIO_7, &pb1200_cascade_handler,
+			0, "Pb1200 Cascade", &pb1200_cascade_handler );
+#ifdef CONFIG_MIPS_PB1200
+    /* We have a problem with CPLD rev3. Enable a workaround */
+	if( ((bcsr->whoami & BCSR_WHOAMI_CPLD)>>4) <= 3)
+	{
+		printk("\nWARNING!!!\n");
+		printk("\nWARNING!!!\n");
+		printk("\nWARNING!!!\n");
+		printk("\nWARNING!!!\n");
+		printk("\nWARNING!!!\n");
+		printk("\nWARNING!!!\n");
+		printk("Pb1200 must be at CPLD rev4. Please have Pb1200\n");
+		printk("updated to latest revision. This software will not\n");
+		printk("work on anything less than CPLD rev4\n");
+		printk("\nWARNING!!!\n");
+		printk("\nWARNING!!!\n");
+		printk("\nWARNING!!!\n");
+		printk("\nWARNING!!!\n");
+		printk("\nWARNING!!!\n");
+		printk("\nWARNING!!!\n");
+		while(1);
+	}
+#endif
+	}
+	pb1200_enable_irq(irq_nr);
+	return 0;
+}
+
+static void pb1200_shutdown_irq( unsigned int irq_nr )
+{
+	pb1200_disable_irq(irq_nr);
+	if (--pb1200_cascade_en == 0)
+	{
+		free_irq(AU1000_GPIO_7,&pb1200_cascade_handler );
+	}
+	return;
+}
+
+static inline void pb1200_mask_and_ack_irq(unsigned int irq_nr)
+{
+	pb1200_disable_irq( irq_nr );
+}
+
+static void pb1200_end_irq(unsigned int irq_nr)
+{
+	if (!(irq_desc[irq_nr].status & (IRQ_DISABLED|IRQ_INPROGRESS))) {
+		pb1200_enable_irq(irq_nr);
+	}
+}
+
+static struct hw_interrupt_type external_irq_type =
+{
+#ifdef CONFIG_MIPS_PB1200
+	"Pb1200 Ext",
+#endif
+#ifdef CONFIG_MIPS_DB1200
+	"Db1200 Ext",
+#endif
+	pb1200_startup_irq,
+	pb1200_shutdown_irq,
+	pb1200_enable_irq,
+	pb1200_disable_irq,
+	pb1200_mask_and_ack_irq,
+	pb1200_end_irq,
+	NULL
+};
+
+void _board_init_irq(void)
+{
+	int irq_nr;
+
+	for (irq_nr = PB1200_INT_BEGIN; irq_nr <= PB1200_INT_END; irq_nr++)
+	{
+		irq_desc[irq_nr].handler = &external_irq_type;
+		pb1200_disable_irq(irq_nr);
+	}
+
+	/* GPIO_7 can not be hooked here, so it is hooked upon first
+	request of any source attached to the cascade */
+}
+
diff -Nur linux-2.4.30/arch/mips/au1000/pb1200/Makefile linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1200/Makefile
--- linux-2.4.30/arch/mips/au1000/pb1200/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1200/Makefile	2005-05-12 17:57:05.000000000 +0200
@@ -0,0 +1,25 @@
+#
+#  Copyright 2000 MontaVista Software Inc.
+#  Author: MontaVista Software, Inc.
+#     	ppopov@mvista.com or source@mvista.com
+#
+# Makefile for the Alchemy Semiconductor PB1000 board.
+#
+# 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).
+#
+
+USE_STANDARD_AS_RULE := true
+
+O_TARGET := pb1200.o
+
+obj-y := init.o board_setup.o irqmap.o
+
+ifdef CONFIG_MMC
+obj-y += mmc_support.o
+export-objs +=mmc_support.o
+endif
+
+
+include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/arch/mips/au1000/pb1200/mmc_support.c linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1200/mmc_support.c
--- linux-2.4.30/arch/mips/au1000/pb1200/mmc_support.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1200/mmc_support.c	2005-05-12 17:57:05.000000000 +0200
@@ -0,0 +1,141 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *
+ *	MMC support routines for PB1200.
+ *
+ *
+ * Copyright (c) 2003-2004 Embedded Edge, LLC.
+ * Author: Embedded Edge, LLC.
+ * Contact: dan@embeddededge.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  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.
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+
+#include <asm/irq.h>
+#include <asm/au1000.h>
+#include <asm/au1100_mmc.h>
+
+#ifdef CONFIG_MIPS_PB1200
+#include <asm/pb1200.h>
+#endif
+
+#ifdef CONFIG_MIPS_DB1200
+/* NOTE: DB1200 only has SD0 pinned out and usable */
+#include <asm/db1200.h>
+#endif
+
+/* SD/MMC controller support functions */
+
+/*
+ * Detect card.
+ */
+void mmc_card_inserted(int socket, int *result)
+{
+	u16 mask;
+
+	if (socket)
+#ifdef CONFIG_MIPS_DB1200
+		mask = 0;
+#else
+		mask = BCSR_INT_SD1INSERT; 
+#endif
+	else
+		mask = BCSR_INT_SD0INSERT;
+
+	*result = ((bcsr->sig_status & mask) != 0);
+}
+
+/*
+ * Check card write protection.
+ */
+void mmc_card_writable(int socket, int *result)
+{
+	u16 mask;
+
+	if (socket)
+#ifdef CONFIG_MIPS_DB1200
+		mask = 0;
+#else
+		mask = BCSR_STATUS_SD1WP; 
+#endif
+	else
+		mask = BCSR_STATUS_SD0WP;
+
+	/* low means card writable */
+	if (!(bcsr->status & mask)) {
+		*result = 1;
+	} else {
+		*result = 0;
+	}
+}
+
+/*
+ * Apply power to card slot.
+ */
+void mmc_power_on(int socket)
+{
+	u16 mask;
+
+	if (socket)
+#ifdef CONFIG_MIPS_DB1200
+		mask = 0;
+#else
+		mask = BCSR_BOARD_SD1PWR;
+#endif
+	else
+		mask = BCSR_BOARD_SD0PWR;
+
+	bcsr->board |= mask;
+	au_sync_delay(1);
+}
+
+/*
+ * Remove power from card slot.
+ */
+void mmc_power_off(int socket)
+{
+	u16 mask;
+
+	if (socket)
+#ifdef CONFIG_MIPS_DB1200
+		mask = 0;
+#else
+		mask = BCSR_BOARD_SD1PWR;
+#endif
+	else
+		mask = BCSR_BOARD_SD0PWR;
+
+	bcsr->board &= ~mask;
+	au_sync_delay(1);
+}
+
+EXPORT_SYMBOL(mmc_card_inserted);
+EXPORT_SYMBOL(mmc_card_writable);
+EXPORT_SYMBOL(mmc_power_on);
+EXPORT_SYMBOL(mmc_power_off);
+
diff -Nur linux-2.4.30/arch/mips/au1000/pb1500/board_setup.c linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1500/board_setup.c
--- linux-2.4.30/arch/mips/au1000/pb1500/board_setup.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1500/board_setup.c	2005-05-12 17:57:05.000000000 +0200
@@ -62,6 +62,10 @@
     au_writel(0x00000000, 0xAE00001C);
 }
 
+void board_power_off (void)
+{
+}
+
 void __init board_setup(void)
 {
 	u32 pin_func;
diff -Nur linux-2.4.30/arch/mips/au1000/pb1550/board_setup.c linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1550/board_setup.c
--- linux-2.4.30/arch/mips/au1000/pb1550/board_setup.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1550/board_setup.c	2005-05-12 17:57:05.000000000 +0200
@@ -48,12 +48,31 @@
 
 extern struct rtc_ops no_rtc_ops;
 
+#if defined(CONFIG_BLK_DEV_IDE_AU1XXX)
+extern struct ide_ops *ide_ops;
+extern struct ide_ops au1xxx_ide_ops;
+extern u32 au1xxx_ide_virtbase;
+extern u64 au1xxx_ide_physbase;
+extern unsigned int au1xxx_ide_irq;
+
+u32 au1xxx_ide_ddma_enable = 0, switch4ddma = 1; // PIO+ddma
+#endif /* end CONFIG_BLK_DEV_IDE_AU1XXX */
+
 void board_reset (void)
 {
     /* Hit BCSR.SYSTEM_CONTROL[SW_RST] */
 	au_writew(au_readw(0xAF00001C) & ~(1<<15), 0xAF00001C);
 }
 
+void board_power_off (void)
+{
+	/* power off system */
+	printk("\n** Powering off Pb1550\n");
+	au_writew(au_readw(0xAF00001C) | (3<<14), 0xAF00001C); 
+	au_sync();
+	while(1); /* should not get here */
+}
+
 void __init board_setup(void)
 {
 	u32 pin_func;
@@ -78,5 +97,36 @@
 	au_writel(0, (u32)bcsr|0x10); /* turn off pcmcia power */
 	au_sync();
 
+#if defined(CONFIG_AU1XXX_SMC91111)
+#if defined(CONFIG_BLK_DEV_IDE_AU1XXX)
+#error "Resource conflict occured. Disable either Ethernet or IDE daughter card."
+#else
+#define CPLD_CONTROL (0xAF00000C)
+	{
+	/* set up the Static Bus timing */
+	/* only 396Mhz */
+	/* reset the DC */
+	au_writew(au_readw(CPLD_CONTROL) | 0x0f, CPLD_CONTROL);
+	au_writel(0x00010003, MEM_STCFG0);
+	au_writel(0x000c00c0, MEM_STCFG2);
+	au_writel(0x85E1900D, MEM_STTIME2);
+	}
+#endif
+#endif /* end CONFIG_SMC91111 */
+
+#if defined(CONFIG_BLK_DEV_IDE_AU1XXX)
+	/*
+	 * 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. pb1550:s15_dot
+	 */
+	switch4ddma = (au_readl(SYS_PINSTATERD) & (1 << 6)) ? 1 : 0;
+#endif
 	printk("AMD Alchemy Pb1550 Board\n");
 }
diff -Nur linux-2.4.30/arch/mips/au1000/pb1550/irqmap.c linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1550/irqmap.c
--- linux-2.4.30/arch/mips/au1000/pb1550/irqmap.c	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/au1000/pb1550/irqmap.c	2005-05-12 17:57:05.000000000 +0200
@@ -50,6 +50,9 @@
 au1xxx_irq_map_t au1xxx_irq_map[] = {
 	{ AU1000_GPIO_0, INTC_INT_LOW_LEVEL, 0 },
 	{ AU1000_GPIO_1, INTC_INT_LOW_LEVEL, 0 },
+#ifdef CONFIG_AU1XXX_SMC91111
+	{ AU1000_GPIO_3, INTC_INT_LOW_LEVEL, 0 },
+#endif
 };
 
 int au1xxx_nr_irqs = sizeof(au1xxx_irq_map)/sizeof(au1xxx_irq_map_t);
diff -Nur linux-2.4.30/arch/mips/config-shared.in linux-2.4.30-linux-mips.org/arch/mips/config-shared.in
--- linux-2.4.30/arch/mips/config-shared.in	2005-01-19 15:09:27.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/config-shared.in	2005-05-12 18:01:29.000000000 +0200
@@ -21,16 +21,19 @@
 comment 'Machine selection'
 dep_bool 'Support for Acer PICA 1 chipset (EXPERIMENTAL)' CONFIG_ACER_PICA_61 $CONFIG_EXPERIMENTAL
 dep_bool 'Support for Alchemy Bosporus board' CONFIG_MIPS_BOSPORUS $CONFIG_MIPS32
+dep_bool 'Support for FIC Multimedia Player board' CONFIG_MIPS_FICMMP $CONFIG_MIPS32
 dep_bool 'Support for Alchemy Mirage board' CONFIG_MIPS_MIRAGE $CONFIG_MIPS32
 dep_bool 'Support for Alchemy Db1000 board' CONFIG_MIPS_DB1000 $CONFIG_MIPS32
 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 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
 dep_bool 'Support for Alchemy PB1500 board' CONFIG_MIPS_PB1500 $CONFIG_MIPS32
-dep_bool 'Support for Alchemy Hydrogen3 board' CONFIG_MIPS_HYDROGEN3 $CONFIG_MIPS32
 dep_bool 'Support for Alchemy PB1550 board' CONFIG_MIPS_PB1550 $CONFIG_MIPS32
+dep_bool 'Support for Alchemy PB1200 board' CONFIG_MIPS_PB1200 $CONFIG_MIPS32
+dep_bool 'Support for Alchemy Hydrogen3 board' CONFIG_MIPS_HYDROGEN3 $CONFIG_MIPS32
 dep_bool 'Support for MyCable XXS1500 board' CONFIG_MIPS_XXS1500 $CONFIG_MIPS32
 dep_bool 'Support for 4G Systems MTX-1 board' CONFIG_MIPS_MTX1 $CONFIG_MIPS32
 dep_bool 'Support for Cogent CSB250 board' CONFIG_COGENT_CSB250 $CONFIG_MIPS32
@@ -249,6 +252,12 @@
    define_bool CONFIG_PC_KEYB y
    define_bool CONFIG_NONCOHERENT_IO y
 fi
+if [ "$CONFIG_MIPS_FICMMP" = "y" ]; then
+   define_bool CONFIG_SOC_AU1X00 y
+   define_bool CONFIG_SOC_AU1200 y
+   define_bool CONFIG_NONCOHERENT_IO y
+   define_bool CONFIG_PC_KEYB y
+fi
 if [ "$CONFIG_MIPS_BOSPORUS" = "y" ]; then
    define_bool CONFIG_SOC_AU1X00 y
    define_bool CONFIG_SOC_AU1500 y
@@ -258,28 +267,47 @@
 if [ "$CONFIG_MIPS_PB1000" = "y" ]; then
    define_bool CONFIG_SOC_AU1X00 y
    define_bool CONFIG_SOC_AU1000 y
+   define_bool CONFIG_SOC_AU1X00_32KHZ y
    define_bool CONFIG_NONCOHERENT_IO y
    define_bool CONFIG_PC_KEYB y
    define_bool CONFIG_SWAP_IO_SPACE_W y
    define_bool CONFIG_SWAP_IO_SPACE_L y
 fi
+if [ "$CONFIG_MIPS_PB1500" = "y" ]; then
+   define_bool CONFIG_SOC_AU1X00 y
+   define_bool CONFIG_SOC_AU1500 y
+   define_bool CONFIG_SOC_AU1X00_32KHZ y
+   define_bool CONFIG_NONCOHERENT_IO y
+   define_bool CONFIG_PC_KEYB y
+fi
 if [ "$CONFIG_MIPS_PB1100" = "y" ]; then
    define_bool CONFIG_SOC_AU1X00 y
    define_bool CONFIG_SOC_AU1100 y
+   define_bool CONFIG_SOC_AU1X00_32KHZ y
    define_bool CONFIG_NONCOHERENT_IO y
    define_bool CONFIG_PC_KEYB y
    define_bool CONFIG_SWAP_IO_SPACE_W y
    define_bool CONFIG_SWAP_IO_SPACE_L y
 fi
-if [ "$CONFIG_MIPS_PB1500" = "y" ]; then
+if [ "$CONFIG_MIPS_PB1550" = "y" ]; then
    define_bool CONFIG_SOC_AU1X00 y
-   define_bool CONFIG_SOC_AU1500 y
+   define_bool CONFIG_SOC_AU1550 y
+   define_bool CONFIG_SOC_AU1X00_32KHZ y
+   define_bool CONFIG_NONCOHERENT_IO n
+   define_bool CONFIG_PC_KEYB y
+fi
+if [ "$CONFIG_MIPS_PB1200" = "y" ]; then
+   define_bool CONFIG_SOC_AU1X00 y
+   define_bool CONFIG_SOC_AU1200 y
+   define_bool CONFIG_SOC_AU1X00_32KHZ y
+   define_bool CONFIG_SOC_AU1X00_32KHZ_TIMER y
    define_bool CONFIG_NONCOHERENT_IO y
    define_bool CONFIG_PC_KEYB y
 fi
 if [ "$CONFIG_MIPS_DB1000" = "y" ]; then
    define_bool CONFIG_SOC_AU1X00 y
    define_bool CONFIG_SOC_AU1000 y
+   define_bool CONFIG_SOC_AU1X00_32KHZ y
    define_bool CONFIG_NONCOHERENT_IO y
    define_bool CONFIG_PC_KEYB y
    define_bool CONFIG_SWAP_IO_SPACE y
@@ -287,25 +315,37 @@
 if [ "$CONFIG_MIPS_DB1500" = "y" ]; then
    define_bool CONFIG_SOC_AU1X00 y
    define_bool CONFIG_SOC_AU1500 y
+   define_bool CONFIG_SOC_AU1X00_32KHZ y
    define_bool CONFIG_NONCOHERENT_IO y
    define_bool CONFIG_PC_KEYB y
 fi
+if [ "$CONFIG_MIPS_DB1100" = "y" ]; then
+   define_bool CONFIG_SOC_AU1X00 y
+   define_bool CONFIG_SOC_AU1100 y
+   define_bool CONFIG_SOC_AU1X00_32KHZ y
+   define_bool CONFIG_NONCOHERENT_IO y
+   define_bool CONFIG_PC_KEYB y
+   define_bool CONFIG_SWAP_IO_SPACE y
+fi
 if [ "$CONFIG_MIPS_DB1550" = "y" ]; then
    define_bool CONFIG_SOC_AU1X00 y
    define_bool CONFIG_SOC_AU1550 y
+   define_bool CONFIG_SOC_AU1X00_32KHZ y
    define_bool CONFIG_NONCOHERENT_IO y
    define_bool CONFIG_PC_KEYB y
 fi
-if [ "$CONFIG_MIPS_DB1100" = "y" ]; then
+if [ "$CONFIG_MIPS_DB1200" = "y" ]; then
    define_bool CONFIG_SOC_AU1X00 y
-   define_bool CONFIG_SOC_AU1100 y
+   define_bool CONFIG_SOC_AU1200 y
+   define_bool CONFIG_SOC_AU1X00_32KHZ y
+   define_bool CONFIG_SOC_AU1X00_32KHZ_TIMER y
    define_bool CONFIG_NONCOHERENT_IO y
    define_bool CONFIG_PC_KEYB y
-   define_bool CONFIG_SWAP_IO_SPACE y
 fi
 if [ "$CONFIG_MIPS_HYDROGEN3" = "y" ]; then
    define_bool CONFIG_SOC_AU1X00 y
    define_bool CONFIG_SOC_AU1100 y
+   define_bool CONFIG_SOC_AU1X00_32KHZ y
    define_bool CONFIG_NONCOHERENT_IO y
    define_bool CONFIG_PC_KEYB y
    define_bool CONFIG_SWAP_IO_SPACE y
@@ -327,12 +367,6 @@
    define_bool CONFIG_NONCOHERENT_IO y
    define_bool CONFIG_PC_KEYB y
 fi
-if [ "$CONFIG_MIPS_PB1550" = "y" ]; then
-   define_bool CONFIG_SOC_AU1X00 y
-   define_bool CONFIG_SOC_AU1550 y
-   define_bool CONFIG_NONCOHERENT_IO n
-   define_bool CONFIG_PC_KEYB y
-fi
 if [ "$CONFIG_MIPS_COBALT" = "y" ]; then
    define_bool CONFIG_BOOT_ELF32 y
    define_bool CONFIG_COBALT_LCD y
@@ -729,6 +763,13 @@
      "$CONFIG_MIPS_PB1000" = "y" -o \
      "$CONFIG_MIPS_PB1100" = "y" -o \
      "$CONFIG_MIPS_PB1500" = "y" -o \
+     "$CONFIG_MIPS_PB1550" = "y" -o \
+     "$CONFIG_MIPS_PB1200" = "y" -o \
+     "$CONFIG_MIPS_DB1000" = "y" -o \
+     "$CONFIG_MIPS_DB1100" = "y" -o \
+     "$CONFIG_MIPS_DB1500" = "y" -o \
+     "$CONFIG_MIPS_DB1550" = "y" -o \
+     "$CONFIG_MIPS_DB1200" = "y" -o \
      "$CONFIG_NEC_OSPREY" = "y" -o \
      "$CONFIG_NEC_EAGLE" = "y" -o \
      "$CONFIG_NINO" = "y" -o \
@@ -1006,6 +1047,8 @@
 
 source net/bluetooth/Config.in
 
+source drivers/mmc/Config.in
+
 mainmenu_option next_comment
 comment 'Kernel hacking'
 
diff -Nur linux-2.4.30/arch/mips/defconfig linux-2.4.30-linux-mips.org/arch/mips/defconfig
--- linux-2.4.30/arch/mips/defconfig	2005-01-19 15:09:27.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -235,11 +235,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -319,9 +314,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -465,7 +462,6 @@
 # CONFIG_SERIAL is not set
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-atlas linux-2.4.30-linux-mips.org/arch/mips/defconfig-atlas
--- linux-2.4.30/arch/mips/defconfig-atlas	2005-01-19 15:09:27.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-atlas	2005-05-12 17:57:05.000000000 +0200
@@ -28,8 +28,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -235,11 +235,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -317,9 +312,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -528,7 +525,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-bosporus linux-2.4.30-linux-mips.org/arch/mips/defconfig-bosporus
--- linux-2.4.30/arch/mips/defconfig-bosporus	2005-01-19 15:09:27.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-bosporus	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 #
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
 #
 CONFIG_MIPS=y
 CONFIG_MIPS32=y
@@ -22,16 +22,19 @@
 #
 # CONFIG_ACER_PICA_61 is not set
 CONFIG_MIPS_BOSPORUS=y
+# CONFIG_MIPS_FICMMP is not set
 # CONFIG_MIPS_MIRAGE is not set
 # CONFIG_MIPS_DB1000 is not set
 # CONFIG_MIPS_DB1100 is not set
 # CONFIG_MIPS_DB1500 is not set
 # CONFIG_MIPS_DB1550 is not set
+# CONFIG_MIPS_DB1200 is not set
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_PB1200 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -171,14 +174,11 @@
 # CONFIG_MTD_CONCAT is not set
 # CONFIG_MTD_REDBOOT_PARTS is not set
 # CONFIG_MTD_CMDLINE_PARTS is not set
-
-#
-# User Modules And Translation Layers
-#
 CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 # CONFIG_FTL is not set
 # CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
 
 #
 # RAM/ROM/Flash chip drivers
@@ -190,6 +190,7 @@
 # CONFIG_MTD_CFI_INTELEXT is not set
 CONFIG_MTD_CFI_AMDSTD=y
 # CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
 # CONFIG_MTD_RAM is not set
 # CONFIG_MTD_ROM is not set
 # CONFIG_MTD_ABSENT is not set
@@ -201,17 +202,10 @@
 #
 # Mapping drivers for chip access
 #
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
 # CONFIG_MTD_PHYSMAP is not set
-# CONFIG_MTD_PB1000 is not set
-# CONFIG_MTD_PB1500 is not set
-# CONFIG_MTD_PB1100 is not set
-CONFIG_MTD_BOSPORUS=y
+CONFIG_MTD_ALCHEMY=y
 # CONFIG_MTD_XXS1500 is not set
-# CONFIG_MTD_MTX1 is not set
-# CONFIG_MTD_DB1X00 is not set
-# CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
-# CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
 # CONFIG_MTD_LASAT is not set
@@ -225,13 +219,9 @@
 # CONFIG_MTD_SLRAM is not set
 # CONFIG_MTD_MTDRAM is not set
 # CONFIG_MTD_BLKMTD is not set
-
-#
-# Disk-On-Chip Device Drivers
-#
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
 # CONFIG_MTD_DOCPROBE is not set
 
 #
@@ -367,17 +357,8 @@
 # CONFIG_IP_SCTP is not set
 # CONFIG_ATM is not set
 # CONFIG_VLAN_8021Q is not set
-
-#
-#  
-#
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -416,10 +397,6 @@
 # SCSI support
 #
 CONFIG_SCSI=m
-
-#
-# SCSI support type (disk, tape, CD-ROM)
-#
 CONFIG_BLK_DEV_SD=m
 CONFIG_SD_EXTRA_DEVS=40
 CONFIG_CHR_DEV_ST=m
@@ -428,10 +405,6 @@
 # CONFIG_BLK_DEV_SR_VENDOR is not set
 CONFIG_SR_EXTRA_DEVS=2
 # CONFIG_CHR_DEV_SG is not set
-
-#
-# Some SCSI devices (e.g. CD jukebox) support multiple LUNs
-#
 # CONFIG_SCSI_DEBUG_QUEUES is not set
 # CONFIG_SCSI_MULTI_LUN is not set
 CONFIG_SCSI_CONSTANTS=y
@@ -457,9 +430,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -545,6 +520,7 @@
 CONFIG_NET_ETHERNET=y
 CONFIG_MIPS_AU1X00_ENET=y
 # CONFIG_BCM5222_DUAL_PHY is not set
+# CONFIG_AU1XXX_SMC91111 is not set
 # CONFIG_SUNLANCE is not set
 # CONFIG_HAPPYMEAL is not set
 # CONFIG_SUNBMAC is not set
@@ -600,10 +576,6 @@
 # CONFIG_PLX_HERMES is not set
 # CONFIG_TMD_HERMES is not set
 # CONFIG_PCI_HERMES is not set
-
-#
-# Prism54 PCI/PCMCIA GT/Duette Driver - 802.11(a/b/g)
-#
 # CONFIG_PRISM54 is not set
 CONFIG_NET_WIRELESS=y
 
@@ -681,7 +653,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
@@ -707,10 +678,6 @@
 # CONFIG_INPUT_EMU10K1 is not set
 # CONFIG_INPUT_SERIO is not set
 # CONFIG_INPUT_SERPORT is not set
-
-#
-# Joysticks
-#
 # CONFIG_INPUT_ANALOG is not set
 # CONFIG_INPUT_A3D is not set
 # CONFIG_INPUT_ADI is not set
@@ -885,24 +852,14 @@
 #
 CONFIG_USB=y
 # CONFIG_USB_DEBUG is not set
-
-#
-# Miscellaneous USB options
-#
 # CONFIG_USB_DEVICEFS is not set
 # CONFIG_USB_BANDWIDTH is not set
-
-#
-# USB Host Controller Drivers
-#
 # CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_NON_PCI_EHCI is not set
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=y
-
-#
-# USB Device Class drivers
-#
+CONFIG_USB_NON_PCI_OHCI=y
 # CONFIG_USB_AUDIO is not set
 # CONFIG_USB_EMI26 is not set
 # CONFIG_USB_BLUETOOTH is not set
@@ -919,10 +876,6 @@
 # CONFIG_USB_STORAGE_JUMPSHOT is not set
 # CONFIG_USB_ACM is not set
 # CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
 CONFIG_USB_HID=y
 CONFIG_USB_HIDINPUT=y
 # CONFIG_USB_HIDDEV is not set
@@ -930,47 +883,23 @@
 # CONFIG_USB_WACOM is not set
 # CONFIG_USB_KBTAB is not set
 # CONFIG_USB_POWERMATE is not set
-
-#
-# USB Imaging devices
-#
 # CONFIG_USB_DC2XX is not set
 # CONFIG_USB_MDC800 is not set
 # CONFIG_USB_SCANNER is not set
 # CONFIG_USB_MICROTEK is not set
 # CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-
-#
-#   Video4Linux support is needed for USB Multimedia device support
-#
-
-#
-# USB Network adaptors
-#
 # CONFIG_USB_PEGASUS is not set
 # CONFIG_USB_RTL8150 is not set
 # CONFIG_USB_KAWETH is not set
 # CONFIG_USB_CATC is not set
 # CONFIG_USB_CDCETHER is not set
 # CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
 # CONFIG_USB_USS720 is not set
 
 #
 # USB Serial Converter support
 #
 # CONFIG_USB_SERIAL is not set
-
-#
-# USB Miscellaneous drivers
-#
 # CONFIG_USB_RIO500 is not set
 # CONFIG_USB_AUERSWALD is not set
 # CONFIG_USB_TIGL is not set
@@ -988,6 +917,12 @@
 # CONFIG_BLUEZ is not set
 
 #
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+# CONFIG_MMC_AU1100 is not set
+
+#
 # Kernel hacking
 #
 CONFIG_CROSSCOMPILE=y
diff -Nur linux-2.4.30/arch/mips/defconfig-capcella linux-2.4.30-linux-mips.org/arch/mips/defconfig-capcella
--- linux-2.4.30/arch/mips/defconfig-capcella	2005-01-19 15:09:27.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-capcella	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -228,11 +228,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -472,7 +467,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 # CONFIG_VR41XX_KIU is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
diff -Nur linux-2.4.30/arch/mips/defconfig-cobalt linux-2.4.30-linux-mips.org/arch/mips/defconfig-cobalt
--- linux-2.4.30/arch/mips/defconfig-cobalt	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-cobalt	2005-05-12 17:57:05.000000000 +0200
@@ -28,8 +28,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -222,11 +222,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -505,7 +500,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=16
 
diff -Nur linux-2.4.30/arch/mips/defconfig-csb250 linux-2.4.30-linux-mips.org/arch/mips/defconfig-csb250
--- linux-2.4.30/arch/mips/defconfig-csb250	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-csb250	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 CONFIG_COGENT_CSB250=y
@@ -268,11 +268,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -556,7 +551,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-db1000 linux-2.4.30-linux-mips.org/arch/mips/defconfig-db1000
--- linux-2.4.30/arch/mips/defconfig-db1000	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-db1000	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 #
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
 #
 CONFIG_MIPS=y
 CONFIG_MIPS32=y
@@ -22,16 +22,19 @@
 #
 # CONFIG_ACER_PICA_61 is not set
 # CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_FICMMP is not set
 # CONFIG_MIPS_MIRAGE is not set
 CONFIG_MIPS_DB1000=y
 # CONFIG_MIPS_DB1100 is not set
 # CONFIG_MIPS_DB1500 is not set
 # CONFIG_MIPS_DB1550 is not set
+# CONFIG_MIPS_DB1200 is not set
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_PB1200 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -79,6 +82,7 @@
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
 CONFIG_SOC_AU1X00=y
 CONFIG_SOC_AU1000=y
+CONFIG_SOC_AU1X00_32KHZ=y
 CONFIG_NONCOHERENT_IO=y
 CONFIG_PC_KEYB=y
 CONFIG_SWAP_IO_SPACE=y
@@ -177,14 +181,11 @@
 # CONFIG_MTD_CONCAT is not set
 # CONFIG_MTD_REDBOOT_PARTS is not set
 # CONFIG_MTD_CMDLINE_PARTS is not set
-
-#
-# User Modules And Translation Layers
-#
 CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 # CONFIG_FTL is not set
 # CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
 
 #
 # RAM/ROM/Flash chip drivers
@@ -196,6 +197,7 @@
 CONFIG_MTD_CFI_INTELEXT=y
 CONFIG_MTD_CFI_AMDSTD=y
 # CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
 # CONFIG_MTD_RAM is not set
 # CONFIG_MTD_ROM is not set
 # CONFIG_MTD_ABSENT is not set
@@ -207,19 +209,10 @@
 #
 # Mapping drivers for chip access
 #
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
 # CONFIG_MTD_PHYSMAP is not set
-# CONFIG_MTD_PB1000 is not set
-# CONFIG_MTD_PB1500 is not set
-# CONFIG_MTD_PB1100 is not set
-# CONFIG_MTD_BOSPORUS is not set
+# CONFIG_MTD_ALCHEMY is not set
 # CONFIG_MTD_XXS1500 is not set
-# CONFIG_MTD_MTX1 is not set
-CONFIG_MTD_DB1X00=y
-CONFIG_MTD_DB1X00_BOOT=y
-CONFIG_MTD_DB1X00_USER=y
-# CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
-# CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
 # CONFIG_MTD_LASAT is not set
@@ -233,13 +226,9 @@
 # CONFIG_MTD_SLRAM is not set
 # CONFIG_MTD_MTDRAM is not set
 # CONFIG_MTD_BLKMTD is not set
-
-#
-# Disk-On-Chip Device Drivers
-#
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
 # CONFIG_MTD_DOCPROBE is not set
 
 #
@@ -336,17 +325,8 @@
 # CONFIG_IP_SCTP is not set
 # CONFIG_ATM is not set
 # CONFIG_VLAN_8021Q is not set
-
-#
-#  
-#
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -384,10 +364,6 @@
 # IDE, ATA and ATAPI Block devices
 #
 CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
 # CONFIG_BLK_DEV_HD_IDE is not set
 # CONFIG_BLK_DEV_HD is not set
 # CONFIG_BLK_DEV_IDE_SATA is not set
@@ -401,14 +377,14 @@
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_IDE_TASK_IOCTL is not set
-
-#
-# IDE chipset support/bugfixes
-#
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
 # CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_BLK_DEV_IDE_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX is not set
 # CONFIG_IDE_CHIPSETS is not set
 # CONFIG_IDEDMA_AUTO is not set
 # CONFIG_DMA_NONPCI is not set
@@ -468,6 +444,7 @@
 CONFIG_NET_ETHERNET=y
 CONFIG_MIPS_AU1X00_ENET=y
 # CONFIG_BCM5222_DUAL_PHY is not set
+# CONFIG_AU1XXX_SMC91111 is not set
 # CONFIG_SUNLANCE is not set
 # CONFIG_HAPPYMEAL is not set
 # CONFIG_SUNBMAC is not set
@@ -540,18 +517,10 @@
 # IrDA (infrared) support
 #
 CONFIG_IRDA=y
-
-#
-# IrDA protocols
-#
 CONFIG_IRLAN=m
 # CONFIG_IRNET is not set
 CONFIG_IRCOMM=m
 # CONFIG_IRDA_ULTRA is not set
-
-#
-# IrDA options
-#
 CONFIG_IRDA_CACHE_LAST_LSAP=y
 CONFIG_IRDA_FAST_RR=y
 # CONFIG_IRDA_DEBUG is not set
@@ -559,21 +528,9 @@
 #
 # Infrared-port device drivers
 #
-
-#
-# SIR device drivers
-#
 # CONFIG_IRTTY_SIR is not set
 # CONFIG_IRPORT_SIR is not set
-
-#
-# Dongle support
-#
 # CONFIG_DONGLE is not set
-
-#
-# FIR device drivers
-#
 # CONFIG_USB_IRDA is not set
 # CONFIG_NSC_FIR is not set
 # CONFIG_WINBOND_FIR is not set
@@ -636,7 +593,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
@@ -662,10 +618,6 @@
 # CONFIG_INPUT_EMU10K1 is not set
 # CONFIG_INPUT_SERIO is not set
 # CONFIG_INPUT_SERPORT is not set
-
-#
-# Joysticks
-#
 # CONFIG_INPUT_ANALOG is not set
 # CONFIG_INPUT_A3D is not set
 # CONFIG_INPUT_ADI is not set
@@ -900,8 +852,6 @@
 # CONFIG_SOUND_RME96XX is not set
 # CONFIG_SOUND_SONICVIBES is not set
 CONFIG_SOUND_AU1X00=y
-# CONFIG_SOUND_AU1550_PSC is not set
-# CONFIG_SOUND_AU1550_I2S is not set
 # CONFIG_SOUND_TRIDENT is not set
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
@@ -917,32 +867,18 @@
 #
 CONFIG_USB=y
 # CONFIG_USB_DEBUG is not set
-
-#
-# Miscellaneous USB options
-#
 # CONFIG_USB_DEVICEFS is not set
 # CONFIG_USB_BANDWIDTH is not set
-
-#
-# USB Host Controller Drivers
-#
 # CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_NON_PCI_EHCI is not set
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=y
-
-#
-# USB Device Class drivers
-#
+CONFIG_USB_NON_PCI_OHCI=y
 # CONFIG_USB_AUDIO is not set
 # CONFIG_USB_EMI26 is not set
 # CONFIG_USB_BLUETOOTH is not set
 # CONFIG_USB_MIDI is not set
-
-#
-#   SCSI support is needed for USB Storage
-#
 # CONFIG_USB_STORAGE is not set
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DATAFAB is not set
@@ -955,10 +891,6 @@
 # CONFIG_USB_STORAGE_JUMPSHOT is not set
 # CONFIG_USB_ACM is not set
 # CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
 CONFIG_USB_HID=y
 CONFIG_USB_HIDINPUT=y
 # CONFIG_USB_HIDDEV is not set
@@ -966,47 +898,23 @@
 # CONFIG_USB_WACOM is not set
 # CONFIG_USB_KBTAB is not set
 # CONFIG_USB_POWERMATE is not set
-
-#
-# USB Imaging devices
-#
 # CONFIG_USB_DC2XX is not set
 # CONFIG_USB_MDC800 is not set
 # CONFIG_USB_SCANNER is not set
 # CONFIG_USB_MICROTEK is not set
 # CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-
-#
-#   Video4Linux support is needed for USB Multimedia device support
-#
-
-#
-# USB Network adaptors
-#
 # CONFIG_USB_PEGASUS is not set
 # CONFIG_USB_RTL8150 is not set
 # CONFIG_USB_KAWETH is not set
 # CONFIG_USB_CATC is not set
 # CONFIG_USB_CDCETHER is not set
 # CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
 # CONFIG_USB_USS720 is not set
 
 #
 # USB Serial Converter support
 #
 # CONFIG_USB_SERIAL is not set
-
-#
-# USB Miscellaneous drivers
-#
 # CONFIG_USB_RIO500 is not set
 # CONFIG_USB_AUERSWALD is not set
 # CONFIG_USB_TIGL is not set
@@ -1024,6 +932,12 @@
 # CONFIG_BLUEZ is not set
 
 #
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+# CONFIG_MMC_AU1100 is not set
+
+#
 # Kernel hacking
 #
 CONFIG_CROSSCOMPILE=y
diff -Nur linux-2.4.30/arch/mips/defconfig-db1100 linux-2.4.30-linux-mips.org/arch/mips/defconfig-db1100
--- linux-2.4.30/arch/mips/defconfig-db1100	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-db1100	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 #
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
 #
 CONFIG_MIPS=y
 CONFIG_MIPS32=y
@@ -22,16 +22,19 @@
 #
 # CONFIG_ACER_PICA_61 is not set
 # CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_FICMMP is not set
 # CONFIG_MIPS_MIRAGE is not set
 # CONFIG_MIPS_DB1000 is not set
 CONFIG_MIPS_DB1100=y
 # CONFIG_MIPS_DB1500 is not set
 # CONFIG_MIPS_DB1550 is not set
+# CONFIG_MIPS_DB1200 is not set
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_PB1200 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -79,6 +82,7 @@
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
 CONFIG_SOC_AU1X00=y
 CONFIG_SOC_AU1100=y
+CONFIG_SOC_AU1X00_32KHZ=y
 CONFIG_NONCOHERENT_IO=y
 CONFIG_PC_KEYB=y
 CONFIG_SWAP_IO_SPACE=y
@@ -177,14 +181,11 @@
 # CONFIG_MTD_CONCAT is not set
 # CONFIG_MTD_REDBOOT_PARTS is not set
 # CONFIG_MTD_CMDLINE_PARTS is not set
-
-#
-# User Modules And Translation Layers
-#
 CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 # CONFIG_FTL is not set
 # CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
 
 #
 # RAM/ROM/Flash chip drivers
@@ -196,6 +197,7 @@
 # CONFIG_MTD_CFI_INTELEXT is not set
 CONFIG_MTD_CFI_AMDSTD=y
 # CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
 # CONFIG_MTD_RAM is not set
 # CONFIG_MTD_ROM is not set
 # CONFIG_MTD_ABSENT is not set
@@ -207,19 +209,10 @@
 #
 # Mapping drivers for chip access
 #
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
 # CONFIG_MTD_PHYSMAP is not set
-# CONFIG_MTD_PB1000 is not set
-# CONFIG_MTD_PB1500 is not set
-# CONFIG_MTD_PB1100 is not set
-# CONFIG_MTD_BOSPORUS is not set
+CONFIG_MTD_ALCHEMY=y
 # CONFIG_MTD_XXS1500 is not set
-# CONFIG_MTD_MTX1 is not set
-CONFIG_MTD_DB1X00=y
-# CONFIG_MTD_DB1X00_BOOT is not set
-CONFIG_MTD_DB1X00_USER=y
-# CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
-# CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
 # CONFIG_MTD_LASAT is not set
@@ -233,13 +226,9 @@
 # CONFIG_MTD_SLRAM is not set
 # CONFIG_MTD_MTDRAM is not set
 # CONFIG_MTD_BLKMTD is not set
-
-#
-# Disk-On-Chip Device Drivers
-#
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
 # CONFIG_MTD_DOCPROBE is not set
 
 #
@@ -336,17 +325,8 @@
 # CONFIG_IP_SCTP is not set
 # CONFIG_ATM is not set
 # CONFIG_VLAN_8021Q is not set
-
-#
-#  
-#
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -384,10 +364,6 @@
 # IDE, ATA and ATAPI Block devices
 #
 CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
 # CONFIG_BLK_DEV_HD_IDE is not set
 # CONFIG_BLK_DEV_HD is not set
 # CONFIG_BLK_DEV_IDE_SATA is not set
@@ -401,14 +377,14 @@
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_IDE_TASK_IOCTL is not set
-
-#
-# IDE chipset support/bugfixes
-#
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
 # CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_BLK_DEV_IDE_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX is not set
 # CONFIG_IDE_CHIPSETS is not set
 # CONFIG_IDEDMA_AUTO is not set
 # CONFIG_DMA_NONPCI is not set
@@ -468,6 +444,7 @@
 CONFIG_NET_ETHERNET=y
 CONFIG_MIPS_AU1X00_ENET=y
 # CONFIG_BCM5222_DUAL_PHY is not set
+# CONFIG_AU1XXX_SMC91111 is not set
 # CONFIG_SUNLANCE is not set
 # CONFIG_HAPPYMEAL is not set
 # CONFIG_SUNBMAC is not set
@@ -540,18 +517,10 @@
 # IrDA (infrared) support
 #
 CONFIG_IRDA=y
-
-#
-# IrDA protocols
-#
 CONFIG_IRLAN=m
 # CONFIG_IRNET is not set
 CONFIG_IRCOMM=m
 # CONFIG_IRDA_ULTRA is not set
-
-#
-# IrDA options
-#
 CONFIG_IRDA_CACHE_LAST_LSAP=y
 CONFIG_IRDA_FAST_RR=y
 # CONFIG_IRDA_DEBUG is not set
@@ -559,21 +528,9 @@
 #
 # Infrared-port device drivers
 #
-
-#
-# SIR device drivers
-#
 # CONFIG_IRTTY_SIR is not set
 # CONFIG_IRPORT_SIR is not set
-
-#
-# Dongle support
-#
 # CONFIG_DONGLE is not set
-
-#
-# FIR device drivers
-#
 # CONFIG_USB_IRDA is not set
 # CONFIG_NSC_FIR is not set
 # CONFIG_WINBOND_FIR is not set
@@ -636,7 +593,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
@@ -662,10 +618,6 @@
 # CONFIG_INPUT_EMU10K1 is not set
 # CONFIG_INPUT_SERIO is not set
 # CONFIG_INPUT_SERPORT is not set
-
-#
-# Joysticks
-#
 # CONFIG_INPUT_ANALOG is not set
 # CONFIG_INPUT_A3D is not set
 # CONFIG_INPUT_ADI is not set
@@ -884,18 +836,19 @@
 # CONFIG_FB_PM2 is not set
 # CONFIG_FB_PM3 is not set
 # CONFIG_FB_CYBER2000 is not set
+CONFIG_FB_AU1100=y
 # CONFIG_FB_MATROX is not set
 # CONFIG_FB_ATY is not set
 # CONFIG_FB_RADEON is not set
 # CONFIG_FB_ATY128 is not set
 # CONFIG_FB_INTEL is not set
 # CONFIG_FB_SIS is not set
+# CONFIG_FB_SMI501 is not set
 # CONFIG_FB_NEOMAGIC is not set
 # CONFIG_FB_3DFX is not set
 # CONFIG_FB_VOODOO1 is not set
 # CONFIG_FB_TRIDENT is not set
 # CONFIG_FB_E1356 is not set
-CONFIG_FB_AU1100=y
 # CONFIG_FB_IT8181 is not set
 # CONFIG_FB_VIRTUAL is not set
 CONFIG_FBCON_ADVANCED=y
@@ -941,8 +894,6 @@
 # CONFIG_SOUND_RME96XX is not set
 # CONFIG_SOUND_SONICVIBES is not set
 CONFIG_SOUND_AU1X00=y
-# CONFIG_SOUND_AU1550_PSC is not set
-# CONFIG_SOUND_AU1550_I2S is not set
 # CONFIG_SOUND_TRIDENT is not set
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
@@ -958,32 +909,18 @@
 #
 CONFIG_USB=y
 # CONFIG_USB_DEBUG is not set
-
-#
-# Miscellaneous USB options
-#
 # CONFIG_USB_DEVICEFS is not set
 # CONFIG_USB_BANDWIDTH is not set
-
-#
-# USB Host Controller Drivers
-#
 # CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_NON_PCI_EHCI is not set
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=y
-
-#
-# USB Device Class drivers
-#
+CONFIG_USB_NON_PCI_OHCI=y
 # CONFIG_USB_AUDIO is not set
 # CONFIG_USB_EMI26 is not set
 # CONFIG_USB_BLUETOOTH is not set
 # CONFIG_USB_MIDI is not set
-
-#
-#   SCSI support is needed for USB Storage
-#
 # CONFIG_USB_STORAGE is not set
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DATAFAB is not set
@@ -996,10 +933,6 @@
 # CONFIG_USB_STORAGE_JUMPSHOT is not set
 # CONFIG_USB_ACM is not set
 # CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
 CONFIG_USB_HID=y
 CONFIG_USB_HIDINPUT=y
 # CONFIG_USB_HIDDEV is not set
@@ -1007,47 +940,23 @@
 # CONFIG_USB_WACOM is not set
 # CONFIG_USB_KBTAB is not set
 # CONFIG_USB_POWERMATE is not set
-
-#
-# USB Imaging devices
-#
 # CONFIG_USB_DC2XX is not set
 # CONFIG_USB_MDC800 is not set
 # CONFIG_USB_SCANNER is not set
 # CONFIG_USB_MICROTEK is not set
 # CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-
-#
-#   Video4Linux support is needed for USB Multimedia device support
-#
-
-#
-# USB Network adaptors
-#
 # CONFIG_USB_PEGASUS is not set
 # CONFIG_USB_RTL8150 is not set
 # CONFIG_USB_KAWETH is not set
 # CONFIG_USB_CATC is not set
 # CONFIG_USB_CDCETHER is not set
 # CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
 # CONFIG_USB_USS720 is not set
 
 #
 # USB Serial Converter support
 #
 # CONFIG_USB_SERIAL is not set
-
-#
-# USB Miscellaneous drivers
-#
 # CONFIG_USB_RIO500 is not set
 # CONFIG_USB_AUERSWALD is not set
 # CONFIG_USB_TIGL is not set
@@ -1065,6 +974,12 @@
 # CONFIG_BLUEZ is not set
 
 #
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+# CONFIG_MMC_AU1100 is not set
+
+#
 # Kernel hacking
 #
 CONFIG_CROSSCOMPILE=y
diff -Nur linux-2.4.30/arch/mips/defconfig-db1200 linux-2.4.30-linux-mips.org/arch/mips/defconfig-db1200
--- linux-2.4.30/arch/mips/defconfig-db1200	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-db1200	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,1114 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+CONFIG_MIPS=y
+CONFIG_MIPS32=y
+# CONFIG_MIPS64 is not set
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# Machine selection
+#
+# CONFIG_ACER_PICA_61 is not set
+# CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_FICMMP is not set
+# CONFIG_MIPS_MIRAGE is not set
+# CONFIG_MIPS_DB1000 is not set
+# CONFIG_MIPS_DB1100 is not set
+# CONFIG_MIPS_DB1500 is not set
+# CONFIG_MIPS_DB1550 is not set
+CONFIG_MIPS_DB1200=y
+# CONFIG_MIPS_PB1000 is not set
+# CONFIG_MIPS_PB1100 is not set
+# CONFIG_MIPS_PB1500 is not set
+# CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_PB1200 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
+# CONFIG_MIPS_XXS1500 is not set
+# CONFIG_MIPS_MTX1 is not set
+# CONFIG_COGENT_CSB250 is not set
+# CONFIG_BAGET_MIPS is not set
+# CONFIG_CASIO_E55 is not set
+# CONFIG_MIPS_COBALT is not set
+# CONFIG_DECSTATION is not set
+# CONFIG_MIPS_EV64120 is not set
+# CONFIG_MIPS_EV96100 is not set
+# CONFIG_MIPS_IVR is not set
+# CONFIG_HP_LASERJET is not set
+# CONFIG_IBM_WORKPAD is not set
+# CONFIG_LASAT is not set
+# CONFIG_MIPS_ITE8172 is not set
+# CONFIG_MIPS_ATLAS is not set
+# CONFIG_MIPS_MAGNUM_4000 is not set
+# CONFIG_MIPS_MALTA is not set
+# CONFIG_MIPS_SEAD is not set
+# CONFIG_MOMENCO_OCELOT is not set
+# CONFIG_MOMENCO_OCELOT_G is not set
+# CONFIG_MOMENCO_OCELOT_C is not set
+# CONFIG_MOMENCO_JAGUAR_ATX is not set
+# CONFIG_PMC_BIG_SUR is not set
+# CONFIG_PMC_STRETCH is not set
+# CONFIG_PMC_YOSEMITE is not set
+# CONFIG_DDB5074 is not set
+# CONFIG_DDB5476 is not set
+# CONFIG_DDB5477 is not set
+# CONFIG_NEC_OSPREY is not set
+# CONFIG_NEC_EAGLE is not set
+# CONFIG_OLIVETTI_M700 is not set
+# CONFIG_NINO is not set
+# CONFIG_SGI_IP22 is not set
+# CONFIG_SGI_IP27 is not set
+# CONFIG_SIBYTE_SB1xxx_SOC is not set
+# CONFIG_SNI_RM200_PCI is not set
+# CONFIG_TANBAC_TB0226 is not set
+# CONFIG_TANBAC_TB0229 is not set
+# CONFIG_TOSHIBA_JMR3927 is not set
+# CONFIG_TOSHIBA_RBTX4927 is not set
+# CONFIG_VICTOR_MPC30X is not set
+# CONFIG_ZAO_CAPCELLA is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
+CONFIG_SOC_AU1X00=y
+CONFIG_SOC_AU1200=y
+CONFIG_SOC_AU1X00_32KHZ=y
+CONFIG_SOC_AU1X00_32KHZ_TIMER=y
+CONFIG_NONCOHERENT_IO=y
+CONFIG_PC_KEYB=y
+# CONFIG_MIPS_AU1000 is not set
+
+#
+# CPU selection
+#
+CONFIG_CPU_MIPS32=y
+# CONFIG_CPU_MIPS64 is not set
+# CONFIG_CPU_R3000 is not set
+# CONFIG_CPU_TX39XX is not set
+# CONFIG_CPU_VR41XX is not set
+# CONFIG_CPU_R4300 is not set
+# CONFIG_CPU_R4X00 is not set
+# CONFIG_CPU_TX49XX is not set
+# CONFIG_CPU_R5000 is not set
+# CONFIG_CPU_R5432 is not set
+# CONFIG_CPU_R6000 is not set
+# CONFIG_CPU_NEVADA is not set
+# CONFIG_CPU_R8000 is not set
+# CONFIG_CPU_R10000 is not set
+# CONFIG_CPU_RM7000 is not set
+# CONFIG_CPU_RM9000 is not set
+# CONFIG_CPU_SB1 is not set
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_CPU_HAS_PREFETCH=y
+# CONFIG_VTAG_ICACHE is not set
+CONFIG_64BIT_PHYS_ADDR=y
+# CONFIG_CPU_ADVANCED is not set
+CONFIG_CPU_HAS_LLSC=y
+# CONFIG_CPU_HAS_LLDSCD is not set
+# CONFIG_CPU_HAS_WB is not set
+CONFIG_CPU_HAS_SYNC=y
+
+#
+# General setup
+#
+CONFIG_CPU_LITTLE_ENDIAN=y
+# CONFIG_BUILD_ELF64 is not set
+CONFIG_NET=y
+CONFIG_PCI=y
+CONFIG_PCI_NEW=y
+CONFIG_PCI_AUTO=y
+# CONFIG_PCI_NAMES is not set
+# CONFIG_ISA is not set
+# CONFIG_TC is not set
+# CONFIG_MCA is not set
+# CONFIG_SBUS is not set
+CONFIG_HOTPLUG=y
+
+#
+# PCMCIA/CardBus support
+#
+CONFIG_PCMCIA=m
+# CONFIG_CARDBUS is not set
+# CONFIG_TCIC is not set
+# CONFIG_I82092 is not set
+# CONFIG_I82365 is not set
+CONFIG_PCMCIA_AU1X00=m
+
+#
+# PCI Hotplug Support
+#
+# CONFIG_HOTPLUG_PCI is not set
+# CONFIG_HOTPLUG_PCI_COMPAQ is not set
+# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set
+# CONFIG_HOTPLUG_PCI_SHPC is not set
+# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set
+# CONFIG_HOTPLUG_PCI_PCIE is not set
+# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+CONFIG_KCORE_ELF=y
+# CONFIG_KCORE_AOUT is not set
+# CONFIG_BINFMT_AOUT is not set
+CONFIG_BINFMT_ELF=y
+# CONFIG_MIPS32_COMPAT is not set
+# CONFIG_MIPS32_O32 is not set
+# CONFIG_MIPS32_N32 is not set
+# CONFIG_BINFMT_ELF32 is not set
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_OOM_KILLER is not set
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="mem=96M"
+CONFIG_PM=y
+
+#
+# Memory Technology Devices (MTD)
+#
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_CONCAT is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+# CONFIG_MTD_OBSOLETE_CHIPS is not set
+# CONFIG_MTD_AMDSTD is not set
+# CONFIG_MTD_SHARP is not set
+# CONFIG_MTD_JEDEC is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_ALCHEMY=y
+# CONFIG_MTD_XXS1500 is not set
+# CONFIG_MTD_CSTM_MIPS_IXX is not set
+# CONFIG_MTD_OCELOT is not set
+# CONFIG_MTD_LASAT is not set
+# CONFIG_MTD_PCI is not set
+# CONFIG_MTD_PCMCIA is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLKMTD is not set
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_DOCPROBE is not set
+
+#
+# NAND Flash Device Drivers
+#
+CONFIG_MTD_NAND=y
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+CONFIG_MTD_NAND_IDS=y
+CONFIG_MTD_NAND_AU1550=y
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play configuration
+#
+# CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_CISS_SCSI_TAPE is not set
+# CONFIG_CISS_MONITOR_THREAD is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_SX8 is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_BLK_STATS is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_RAID0 is not set
+# CONFIG_MD_RAID1 is not set
+# CONFIG_MD_RAID5 is not set
+# CONFIG_MD_MULTIPATH is not set
+# CONFIG_BLK_DEV_LVM is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_FILTER=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_DHCP is not set
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_INET_ECN is not set
+# CONFIG_SYN_COOKIES is not set
+
+#
+#   IP: Netfilter Configuration
+#
+# CONFIG_IP_NF_CONNTRACK is not set
+# CONFIG_IP_NF_QUEUE is not set
+# CONFIG_IP_NF_IPTABLES is not set
+# CONFIG_IP_NF_ARPTABLES is not set
+# CONFIG_IP_NF_COMPAT_IPCHAINS is not set
+# CONFIG_IP_NF_COMPAT_IPFWADM is not set
+
+#
+#   IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+
+#
+#    SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+# CONFIG_ATM is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_LLC is not set
+# CONFIG_NET_DIVERT is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+# CONFIG_PHONE_IXJ is not set
+# CONFIG_PHONE_IXJ_PCMCIA is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+# CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_IDEDISK_MULTI_MODE=y
+CONFIG_IDEDISK_STROKE=y
+CONFIG_BLK_DEV_IDECS=m
+# CONFIG_BLK_DEV_DELKIN is not set
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+CONFIG_IDE_TASK_IOCTL=y
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_GENERIC=y
+# CONFIG_IDEPCI_SHARE_IRQ is not set
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
+# CONFIG_IDEDMA_PCI_AUTO is not set
+# CONFIG_IDEDMA_ONLYDISK is not set
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_BLK_DEV_ADMA100 is not set
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_WDC_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_AMD74XX_OVERRIDE is not set
+# CONFIG_BLK_DEV_ATIIXP is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_TRIFLEX is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_SC1200 is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+CONFIG_BLK_DEV_IDE_AU1XXX=y
+# CONFIG_BLK_DEV_IDE_PIO_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX is not set
+CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX=y
+CONFIG_BLK_DEV_IDE_SEQTS_PER_RQ_AU1XXX=128
+# CONFIG_IDE_CHIPSETS is not set
+# CONFIG_IDEDMA_AUTO is not set
+# CONFIG_IDEDMA_IVB is not set
+# CONFIG_DMA_NONPCI is not set
+# CONFIG_BLK_DEV_ATARAID is not set
+# CONFIG_BLK_DEV_ATARAID_PDC is not set
+# CONFIG_BLK_DEV_ATARAID_HPT is not set
+# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set
+# CONFIG_BLK_DEV_ATARAID_SII is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_SD_EXTRA_DEVS=40
+CONFIG_CHR_DEV_ST=y
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_SR_EXTRA_DEVS=2
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_SCSI_DEBUG_QUEUES is not set
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_MEGARAID2 is not set
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
+# CONFIG_SCSI_SATA_SVW is not set
+# CONFIG_SCSI_ATA_PIIX is not set
+# CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
+# CONFIG_SCSI_SATA_PROMISE is not set
+# CONFIG_SCSI_SATA_SX4 is not set
+# CONFIG_SCSI_SATA_SIL is not set
+# CONFIG_SCSI_SATA_SIS is not set
+# CONFIG_SCSI_SATA_ULI is not set
+# CONFIG_SCSI_SATA_VIA is not set
+# CONFIG_SCSI_SATA_VITESSE is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_CPQFCTS is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_NCR53C8XX is not set
+# CONFIG_SCSI_SYM53C8XX is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_SIM710 is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# PCMCIA SCSI adapter support
+#
+# CONFIG_SCSI_PCMCIA is not set
+
+#
+# Fusion MPT device support
+#
+# CONFIG_FUSION is not set
+# CONFIG_FUSION_BOOT is not set
+# CONFIG_FUSION_ISENSE is not set
+# CONFIG_FUSION_CTL is not set
+# CONFIG_FUSION_LAN is not set
+
+#
+# IEEE 1394 (FireWire) support (EXPERIMENTAL)
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+# CONFIG_I2O_PCI is not set
+# CONFIG_I2O_BLOCK is not set
+# CONFIG_I2O_LAN is not set
+# CONFIG_I2O_SCSI is not set
+# CONFIG_I2O_PROC is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MIPS_AU1X00_ENET is not set
+CONFIG_AU1XXX_SMC91111=y
+# CONFIG_SUNLANCE is not set
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNBMAC is not set
+# CONFIG_SUNQE is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+# CONFIG_E1000 is not set
+# CONFIG_MYRI_SBUS is not set
+# CONFIG_NS83820 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_TIGON3 is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# PCMCIA network device support
+#
+# CONFIG_NET_PCMCIA is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+# CONFIG_IRDA is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Input core support
+#
+CONFIG_INPUT=y
+CONFIG_INPUT_KEYBDEV=y
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_UINPUT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+# CONFIG_VT_CONSOLE is not set
+# CONFIG_SERIAL is not set
+# CONFIG_SERIAL_EXTENDED is not set
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_COMPUTONE is not set
+# CONFIG_ROCKETPORT is not set
+# CONFIG_CYCLADES is not set
+# CONFIG_DIGIEPCA is not set
+# CONFIG_DIGI is not set
+# CONFIG_ESPSERIAL is not set
+# CONFIG_MOXA_INTELLIO is not set
+# CONFIG_MOXA_SMARTIO is not set
+# CONFIG_ISI is not set
+# CONFIG_SYNCLINK is not set
+# CONFIG_SYNCLINKMP is not set
+# CONFIG_N_HDLC is not set
+# CONFIG_RISCOM8 is not set
+# CONFIG_SPECIALIX is not set
+# CONFIG_SX is not set
+# CONFIG_RIO is not set
+# CONFIG_STALDRV is not set
+# CONFIG_SERIAL_TX3912 is not set
+# CONFIG_SERIAL_TX3912_CONSOLE is not set
+# CONFIG_SERIAL_TXX9 is not set
+# CONFIG_SERIAL_TXX9_CONSOLE is not set
+CONFIG_AU1X00_UART=y
+CONFIG_AU1X00_SERIAL_CONSOLE=y
+# CONFIG_AU1X00_USB_TTY is not set
+# CONFIG_AU1X00_USB_RAW is not set
+# CONFIG_TXX927_SERIAL is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+
+#
+# I2C support
+#
+CONFIG_I2C=y
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_SCx200_ACB is not set
+# CONFIG_I2C_ALGOPCF is not set
+CONFIG_I2C_ALGO_AU1550=y
+# CONFIG_I2C_CHARDEV is not set
+# CONFIG_I2C_PROC is not set
+
+#
+# Mice
+#
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MOUSE is not set
+
+#
+# Joysticks
+#
+# CONFIG_INPUT_GAMEPORT is not set
+# CONFIG_INPUT_NS558 is not set
+# CONFIG_INPUT_LIGHTNING is not set
+# CONFIG_INPUT_PCIGAME is not set
+# CONFIG_INPUT_CS461X is not set
+# CONFIG_INPUT_EMU10K1 is not set
+# CONFIG_INPUT_SERIO is not set
+# CONFIG_INPUT_SERPORT is not set
+# CONFIG_INPUT_ANALOG is not set
+# CONFIG_INPUT_A3D is not set
+# CONFIG_INPUT_ADI is not set
+# CONFIG_INPUT_COBRA is not set
+# CONFIG_INPUT_GF2K is not set
+# CONFIG_INPUT_GRIP is not set
+# CONFIG_INPUT_INTERACT is not set
+# CONFIG_INPUT_TMDC is not set
+# CONFIG_INPUT_SIDEWINDER is not set
+# CONFIG_INPUT_IFORCE_USB is not set
+# CONFIG_INPUT_IFORCE_232 is not set
+# CONFIG_INPUT_WARRIOR is not set
+# CONFIG_INPUT_MAGELLAN is not set
+# CONFIG_INPUT_SPACEORB is not set
+# CONFIG_INPUT_SPACEBALL is not set
+# CONFIG_INPUT_STINGER is not set
+# CONFIG_INPUT_DB9 is not set
+# CONFIG_INPUT_GAMECON is not set
+# CONFIG_INPUT_TURBOGRAFX is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_IPMI_PANIC_EVENT is not set
+# CONFIG_IPMI_DEVICE_INTERFACE is not set
+# CONFIG_IPMI_KCS is not set
+# CONFIG_IPMI_WATCHDOG is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_SCx200 is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_AGP is not set
+
+#
+# Direct Rendering Manager (XFree86 DRI support)
+#
+# CONFIG_DRM is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_PCMCIA_SERIAL_CS is not set
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_AU1X00_GPIO is not set
+# CONFIG_TS_AU1X00_ADS7846 is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_AUTOFS_FS=y
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADFS_FS_RW is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BEFS_DEBUG is not set
+# CONFIG_BFS_FS is not set
+CONFIG_EXT3_FS=y
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=y
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+CONFIG_JFFS2_FS_NAND=y
+# CONFIG_CRAMFS is not set
+CONFIG_TMPFS=y
+CONFIG_RAMFS=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+# CONFIG_ZISOFS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX4FS_RW is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDF_RW is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_UFS_FS_WRITE is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_TRACE is not set
+# CONFIG_XFS_DEBUG is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+# CONFIG_NFSD_V3 is not set
+# CONFIG_NFSD_TCP is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+# CONFIG_NCPFS_NLS is not set
+# CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SMB_NLS is not set
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Console drivers
+#
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_MDA_CONSOLE is not set
+
+#
+# Frame-buffer support
+#
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_CLGEN is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_CYBER2000 is not set
+CONFIG_FB_AU1200=y
+CONFIG_FB_AU1200_DEVS=4
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_INTEL is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_SMI501 is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_E1356 is not set
+# CONFIG_FB_IT8181 is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_FBCON_ADVANCED=y
+# CONFIG_FBCON_MFB is not set
+# CONFIG_FBCON_CFB2 is not set
+# CONFIG_FBCON_CFB4 is not set
+# CONFIG_FBCON_CFB8 is not set
+CONFIG_FBCON_CFB16=y
+# CONFIG_FBCON_CFB24 is not set
+CONFIG_FBCON_CFB32=y
+# CONFIG_FBCON_AFB is not set
+# CONFIG_FBCON_ILBM is not set
+# CONFIG_FBCON_IPLAN2P2 is not set
+# CONFIG_FBCON_IPLAN2P4 is not set
+# CONFIG_FBCON_IPLAN2P8 is not set
+# CONFIG_FBCON_MAC is not set
+# CONFIG_FBCON_VGA_PLANES is not set
+# CONFIG_FBCON_VGA is not set
+# CONFIG_FBCON_HGA is not set
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+CONFIG_FBCON_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=y
+# CONFIG_SOUND_ALI5455 is not set
+# CONFIG_SOUND_BT878 is not set
+# CONFIG_SOUND_CMPCI is not set
+# CONFIG_SOUND_EMU10K1 is not set
+# CONFIG_MIDI_EMU10K1 is not set
+# CONFIG_SOUND_FUSION is not set
+# CONFIG_SOUND_CS4281 is not set
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_ESSSOLO1 is not set
+# CONFIG_SOUND_MAESTRO is not set
+# CONFIG_SOUND_MAESTRO3 is not set
+# CONFIG_SOUND_FORTE is not set
+# CONFIG_SOUND_ICH is not set
+# CONFIG_SOUND_RME96XX is not set
+# CONFIG_SOUND_SONICVIBES is not set
+CONFIG_SOUND_AU1550_PSC=y
+# CONFIG_SOUND_AU1550_I2S is not set
+# CONFIG_SOUND_TRIDENT is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+# CONFIG_SOUND_VIA82CXXX is not set
+# CONFIG_MIDI_VIA82CXXX is not set
+# CONFIG_SOUND_OSS is not set
+# CONFIG_SOUND_TVMIXER is not set
+# CONFIG_SOUND_AD1980 is not set
+# CONFIG_SOUND_WM97XX is not set
+
+#
+# USB support
+#
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_BANDWIDTH is not set
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_NON_PCI_EHCI=y
+# CONFIG_USB_UHCI is not set
+# CONFIG_USB_UHCI_ALT is not set
+CONFIG_USB_OHCI=y
+CONFIG_USB_NON_PCI_OHCI=y
+# CONFIG_USB_AUDIO is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_BLUETOOTH is not set
+# CONFIG_USB_MIDI is not set
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_HP8200e is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+CONFIG_USB_HID=y
+CONFIG_USB_HIDINPUT=y
+CONFIG_USB_HIDDEV=y
+# CONFIG_USB_AIPTEK is not set
+# CONFIG_USB_WACOM is not set
+# CONFIG_USB_KBTAB is not set
+# CONFIG_USB_POWERMATE is not set
+# CONFIG_USB_DC2XX is not set
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_SCANNER is not set
+# CONFIG_USB_MICROTEK is not set
+# CONFIG_USB_HPUSBSCSI is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_CDCETHER is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_USB_USS720 is not set
+
+#
+# USB Serial Converter support
+#
+# CONFIG_USB_SERIAL is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_AUERSWALD is not set
+# CONFIG_USB_TIGL is not set
+# CONFIG_USB_BRLVGER is not set
+# CONFIG_USB_LCD is not set
+
+#
+# Support for USB gadgets
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Bluetooth support
+#
+# CONFIG_BLUEZ is not set
+
+#
+# MMC/SD Card support
+#
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_AU1100=y
+
+#
+# Kernel hacking
+#
+CONFIG_CROSSCOMPILE=y
+# CONFIG_RUNTIME_DEBUG is not set
+# CONFIG_KGDB is not set
+# CONFIG_GDB_CONSOLE is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_MIPS_UNCACHED is not set
+CONFIG_LOG_BUF_SHIFT=0
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+# CONFIG_CRC32 is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+# CONFIG_FW_LOADER is not set
diff -Nur linux-2.4.30/arch/mips/defconfig-db1500 linux-2.4.30-linux-mips.org/arch/mips/defconfig-db1500
--- linux-2.4.30/arch/mips/defconfig-db1500	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-db1500	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 #
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
 #
 CONFIG_MIPS=y
 CONFIG_MIPS32=y
@@ -22,16 +22,19 @@
 #
 # CONFIG_ACER_PICA_61 is not set
 # CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_FICMMP is not set
 # CONFIG_MIPS_MIRAGE is not set
 # CONFIG_MIPS_DB1000 is not set
 # CONFIG_MIPS_DB1100 is not set
 CONFIG_MIPS_DB1500=y
 # CONFIG_MIPS_DB1550 is not set
+# CONFIG_MIPS_DB1200 is not set
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_PB1200 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -79,6 +82,7 @@
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
 CONFIG_SOC_AU1X00=y
 CONFIG_SOC_AU1500=y
+CONFIG_SOC_AU1X00_32KHZ=y
 CONFIG_NONCOHERENT_IO=y
 CONFIG_PC_KEYB=y
 # CONFIG_MIPS_AU1000 is not set
@@ -170,7 +174,66 @@
 #
 # Memory Technology Devices (MTD)
 #
-# CONFIG_MTD is not set
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_CONCAT is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+# CONFIG_MTD_OBSOLETE_CHIPS is not set
+# CONFIG_MTD_AMDSTD is not set
+# CONFIG_MTD_SHARP is not set
+# CONFIG_MTD_JEDEC is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_ALCHEMY=y
+# CONFIG_MTD_XXS1500 is not set
+# CONFIG_MTD_CSTM_MIPS_IXX is not set
+# CONFIG_MTD_OCELOT is not set
+# CONFIG_MTD_LASAT is not set
+# CONFIG_MTD_PCI is not set
+# CONFIG_MTD_PCMCIA is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLKMTD is not set
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_DOCPROBE is not set
+
+#
+# NAND Flash Device Drivers
+#
+# CONFIG_MTD_NAND is not set
 
 #
 # Parallel port support
@@ -261,17 +324,8 @@
 # CONFIG_IP_SCTP is not set
 # CONFIG_ATM is not set
 # CONFIG_VLAN_8021Q is not set
-
-#
-#  
-#
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -309,10 +363,6 @@
 # IDE, ATA and ATAPI Block devices
 #
 CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
 # CONFIG_BLK_DEV_HD_IDE is not set
 # CONFIG_BLK_DEV_HD is not set
 # CONFIG_BLK_DEV_IDE_SATA is not set
@@ -326,10 +376,6 @@
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_IDE_TASK_IOCTL is not set
-
-#
-# IDE chipset support/bugfixes
-#
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
@@ -371,6 +417,10 @@
 # CONFIG_BLK_DEV_SLC90E66 is not set
 # CONFIG_BLK_DEV_TRM290 is not set
 # CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_BLK_DEV_IDE_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX is not set
 # CONFIG_IDE_CHIPSETS is not set
 CONFIG_IDEDMA_AUTO=y
 # CONFIG_IDEDMA_IVB is not set
@@ -431,6 +481,7 @@
 CONFIG_NET_ETHERNET=y
 CONFIG_MIPS_AU1X00_ENET=y
 # CONFIG_BCM5222_DUAL_PHY is not set
+# CONFIG_AU1XXX_SMC91111 is not set
 # CONFIG_SUNLANCE is not set
 # CONFIG_HAPPYMEAL is not set
 # CONFIG_SUNBMAC is not set
@@ -555,7 +606,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
@@ -581,10 +631,6 @@
 # CONFIG_INPUT_EMU10K1 is not set
 # CONFIG_INPUT_SERIO is not set
 # CONFIG_INPUT_SERPORT is not set
-
-#
-# Joysticks
-#
 # CONFIG_INPUT_ANALOG is not set
 # CONFIG_INPUT_A3D is not set
 # CONFIG_INPUT_ADI is not set
@@ -776,8 +822,6 @@
 # CONFIG_SOUND_RME96XX is not set
 # CONFIG_SOUND_SONICVIBES is not set
 CONFIG_SOUND_AU1X00=y
-# CONFIG_SOUND_AU1550_PSC is not set
-# CONFIG_SOUND_AU1550_I2S is not set
 # CONFIG_SOUND_TRIDENT is not set
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
@@ -793,32 +837,18 @@
 #
 CONFIG_USB=y
 # CONFIG_USB_DEBUG is not set
-
-#
-# Miscellaneous USB options
-#
 # CONFIG_USB_DEVICEFS is not set
 # CONFIG_USB_BANDWIDTH is not set
-
-#
-# USB Host Controller Drivers
-#
 # CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_NON_PCI_EHCI is not set
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=y
-
-#
-# USB Device Class drivers
-#
+CONFIG_USB_NON_PCI_OHCI=y
 # CONFIG_USB_AUDIO is not set
 # CONFIG_USB_EMI26 is not set
 # CONFIG_USB_BLUETOOTH is not set
 # CONFIG_USB_MIDI is not set
-
-#
-#   SCSI support is needed for USB Storage
-#
 # CONFIG_USB_STORAGE is not set
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DATAFAB is not set
@@ -831,10 +861,6 @@
 # CONFIG_USB_STORAGE_JUMPSHOT is not set
 # CONFIG_USB_ACM is not set
 # CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
 CONFIG_USB_HID=y
 CONFIG_USB_HIDINPUT=y
 # CONFIG_USB_HIDDEV is not set
@@ -842,47 +868,23 @@
 # CONFIG_USB_WACOM is not set
 # CONFIG_USB_KBTAB is not set
 # CONFIG_USB_POWERMATE is not set
-
-#
-# USB Imaging devices
-#
 # CONFIG_USB_DC2XX is not set
 # CONFIG_USB_MDC800 is not set
 # CONFIG_USB_SCANNER is not set
 # CONFIG_USB_MICROTEK is not set
 # CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-
-#
-#   Video4Linux support is needed for USB Multimedia device support
-#
-
-#
-# USB Network adaptors
-#
 # CONFIG_USB_PEGASUS is not set
 # CONFIG_USB_RTL8150 is not set
 # CONFIG_USB_KAWETH is not set
 # CONFIG_USB_CATC is not set
 # CONFIG_USB_CDCETHER is not set
 # CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
 # CONFIG_USB_USS720 is not set
 
 #
 # USB Serial Converter support
 #
 # CONFIG_USB_SERIAL is not set
-
-#
-# USB Miscellaneous drivers
-#
 # CONFIG_USB_RIO500 is not set
 # CONFIG_USB_AUERSWALD is not set
 # CONFIG_USB_TIGL is not set
@@ -900,6 +902,12 @@
 # CONFIG_BLUEZ is not set
 
 #
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+# CONFIG_MMC_AU1100 is not set
+
+#
 # Kernel hacking
 #
 CONFIG_CROSSCOMPILE=y
diff -Nur linux-2.4.30/arch/mips/defconfig-db1550 linux-2.4.30-linux-mips.org/arch/mips/defconfig-db1550
--- linux-2.4.30/arch/mips/defconfig-db1550	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-db1550	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 #
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
 #
 CONFIG_MIPS=y
 CONFIG_MIPS32=y
@@ -22,16 +22,19 @@
 #
 # CONFIG_ACER_PICA_61 is not set
 # CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_FICMMP is not set
 # CONFIG_MIPS_MIRAGE is not set
 # CONFIG_MIPS_DB1000 is not set
 # CONFIG_MIPS_DB1100 is not set
 # CONFIG_MIPS_DB1500 is not set
 CONFIG_MIPS_DB1550=y
+# CONFIG_MIPS_DB1200 is not set
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_PB1200 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -79,6 +82,7 @@
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
 CONFIG_SOC_AU1X00=y
 CONFIG_SOC_AU1550=y
+CONFIG_SOC_AU1X00_32KHZ=y
 CONFIG_NONCOHERENT_IO=y
 CONFIG_PC_KEYB=y
 # CONFIG_MIPS_AU1000 is not set
@@ -176,14 +180,11 @@
 # CONFIG_MTD_CONCAT is not set
 # CONFIG_MTD_REDBOOT_PARTS is not set
 # CONFIG_MTD_CMDLINE_PARTS is not set
-
-#
-# User Modules And Translation Layers
-#
 CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 # CONFIG_FTL is not set
 # CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
 
 #
 # RAM/ROM/Flash chip drivers
@@ -192,9 +193,10 @@
 # CONFIG_MTD_JEDECPROBE is not set
 CONFIG_MTD_GEN_PROBE=y
 # CONFIG_MTD_CFI_ADV_OPTIONS is not set
-CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_INTELEXT is not set
 CONFIG_MTD_CFI_AMDSTD=y
 # CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
 # CONFIG_MTD_RAM is not set
 # CONFIG_MTD_ROM is not set
 # CONFIG_MTD_ABSENT is not set
@@ -206,19 +208,10 @@
 #
 # Mapping drivers for chip access
 #
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
 # CONFIG_MTD_PHYSMAP is not set
-# CONFIG_MTD_PB1000 is not set
-# CONFIG_MTD_PB1500 is not set
-# CONFIG_MTD_PB1100 is not set
-# CONFIG_MTD_BOSPORUS is not set
+CONFIG_MTD_ALCHEMY=y
 # CONFIG_MTD_XXS1500 is not set
-# CONFIG_MTD_MTX1 is not set
-# CONFIG_MTD_DB1X00 is not set
-CONFIG_MTD_PB1550=y
-CONFIG_MTD_PB1550_BOOT=y
-CONFIG_MTD_PB1550_USER=y
-# CONFIG_MTD_HYDROGEN3 is not set
-# CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
 # CONFIG_MTD_LASAT is not set
@@ -232,21 +225,16 @@
 # CONFIG_MTD_SLRAM is not set
 # CONFIG_MTD_MTDRAM is not set
 # CONFIG_MTD_BLKMTD is not set
-
-#
-# Disk-On-Chip Device Drivers
-#
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
 # CONFIG_MTD_DOCPROBE is not set
 
 #
 # NAND Flash Device Drivers
 #
-CONFIG_MTD_NAND=m
-# CONFIG_MTD_NAND_VERIFY_WRITE is not set
-CONFIG_MTD_NAND_IDS=m
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_NAND_AU1550 is not set
 
 #
 # Parallel port support
@@ -337,17 +325,8 @@
 # CONFIG_IP_SCTP is not set
 # CONFIG_ATM is not set
 # CONFIG_VLAN_8021Q is not set
-
-#
-#  
-#
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -385,10 +364,6 @@
 # IDE, ATA and ATAPI Block devices
 #
 CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
 # CONFIG_BLK_DEV_HD_IDE is not set
 # CONFIG_BLK_DEV_HD is not set
 # CONFIG_BLK_DEV_IDE_SATA is not set
@@ -402,10 +377,6 @@
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_IDE_TASK_IOCTL is not set
-
-#
-# IDE chipset support/bugfixes
-#
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
@@ -448,6 +419,10 @@
 # CONFIG_BLK_DEV_SLC90E66 is not set
 # CONFIG_BLK_DEV_TRM290 is not set
 # CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_BLK_DEV_IDE_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX is not set
 # CONFIG_IDE_CHIPSETS is not set
 # CONFIG_IDEDMA_AUTO is not set
 # CONFIG_IDEDMA_IVB is not set
@@ -509,6 +484,7 @@
 CONFIG_NET_ETHERNET=y
 CONFIG_MIPS_AU1X00_ENET=y
 # CONFIG_BCM5222_DUAL_PHY is not set
+# CONFIG_AU1XXX_SMC91111 is not set
 # CONFIG_SUNLANCE is not set
 # CONFIG_HAPPYMEAL is not set
 # CONFIG_SUNBMAC is not set
@@ -633,7 +609,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
@@ -659,10 +634,6 @@
 # CONFIG_INPUT_EMU10K1 is not set
 # CONFIG_INPUT_SERIO is not set
 # CONFIG_INPUT_SERPORT is not set
-
-#
-# Joysticks
-#
 # CONFIG_INPUT_ANALOG is not set
 # CONFIG_INPUT_A3D is not set
 # CONFIG_INPUT_ADI is not set
@@ -748,8 +719,7 @@
 CONFIG_VFAT_FS=y
 # CONFIG_EFS_FS is not set
 # CONFIG_JFFS_FS is not set
-CONFIG_JFFS2_FS=y
-CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS2_FS is not set
 # CONFIG_CRAMFS is not set
 CONFIG_TMPFS=y
 CONFIG_RAMFS=y
@@ -888,6 +858,7 @@
 # CONFIG_FB_ATY128 is not set
 # CONFIG_FB_INTEL is not set
 # CONFIG_FB_SIS is not set
+CONFIG_FB_SMI501=y
 # CONFIG_FB_NEOMAGIC is not set
 # CONFIG_FB_3DFX is not set
 # CONFIG_FB_VOODOO1 is not set
@@ -937,7 +908,6 @@
 # CONFIG_SOUND_ICH is not set
 # CONFIG_SOUND_RME96XX is not set
 # CONFIG_SOUND_SONICVIBES is not set
-# CONFIG_SOUND_AU1X00 is not set
 CONFIG_SOUND_AU1550_PSC=y
 # CONFIG_SOUND_AU1550_I2S is not set
 # CONFIG_SOUND_TRIDENT is not set
@@ -955,32 +925,18 @@
 #
 CONFIG_USB=y
 # CONFIG_USB_DEBUG is not set
-
-#
-# Miscellaneous USB options
-#
 # CONFIG_USB_DEVICEFS is not set
 # CONFIG_USB_BANDWIDTH is not set
-
-#
-# USB Host Controller Drivers
-#
 # CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_NON_PCI_EHCI is not set
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=y
-
-#
-# USB Device Class drivers
-#
+CONFIG_USB_NON_PCI_OHCI=y
 # CONFIG_USB_AUDIO is not set
 # CONFIG_USB_EMI26 is not set
 # CONFIG_USB_BLUETOOTH is not set
 # CONFIG_USB_MIDI is not set
-
-#
-#   SCSI support is needed for USB Storage
-#
 # CONFIG_USB_STORAGE is not set
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DATAFAB is not set
@@ -993,10 +949,6 @@
 # CONFIG_USB_STORAGE_JUMPSHOT is not set
 # CONFIG_USB_ACM is not set
 # CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
 CONFIG_USB_HID=y
 CONFIG_USB_HIDINPUT=y
 # CONFIG_USB_HIDDEV is not set
@@ -1004,47 +956,23 @@
 # CONFIG_USB_WACOM is not set
 # CONFIG_USB_KBTAB is not set
 # CONFIG_USB_POWERMATE is not set
-
-#
-# USB Imaging devices
-#
 # CONFIG_USB_DC2XX is not set
 # CONFIG_USB_MDC800 is not set
 # CONFIG_USB_SCANNER is not set
 # CONFIG_USB_MICROTEK is not set
 # CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-
-#
-#   Video4Linux support is needed for USB Multimedia device support
-#
-
-#
-# USB Network adaptors
-#
 # CONFIG_USB_PEGASUS is not set
 # CONFIG_USB_RTL8150 is not set
 # CONFIG_USB_KAWETH is not set
 # CONFIG_USB_CATC is not set
 # CONFIG_USB_CDCETHER is not set
 # CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
 # CONFIG_USB_USS720 is not set
 
 #
 # USB Serial Converter support
 #
 # CONFIG_USB_SERIAL is not set
-
-#
-# USB Miscellaneous drivers
-#
 # CONFIG_USB_RIO500 is not set
 # CONFIG_USB_AUERSWALD is not set
 # CONFIG_USB_TIGL is not set
@@ -1062,6 +990,12 @@
 # CONFIG_BLUEZ is not set
 
 #
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+# CONFIG_MMC_AU1100 is not set
+
+#
 # Kernel hacking
 #
 CONFIG_CROSSCOMPILE=y
@@ -1082,6 +1016,6 @@
 # Library routines
 #
 # CONFIG_CRC32 is not set
-CONFIG_ZLIB_INFLATE=y
-CONFIG_ZLIB_DEFLATE=y
+CONFIG_ZLIB_INFLATE=m
+CONFIG_ZLIB_DEFLATE=m
 # CONFIG_FW_LOADER is not set
diff -Nur linux-2.4.30/arch/mips/defconfig-ddb5476 linux-2.4.30-linux-mips.org/arch/mips/defconfig-ddb5476
--- linux-2.4.30/arch/mips/defconfig-ddb5476	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-ddb5476	2005-05-12 17:57:05.000000000 +0200
@@ -28,8 +28,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -226,11 +226,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -517,7 +512,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-ddb5477 linux-2.4.30-linux-mips.org/arch/mips/defconfig-ddb5477
--- linux-2.4.30/arch/mips/defconfig-ddb5477	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-ddb5477	2005-05-12 17:57:05.000000000 +0200
@@ -28,8 +28,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -226,11 +226,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -434,7 +429,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-decstation linux-2.4.30-linux-mips.org/arch/mips/defconfig-decstation
--- linux-2.4.30/arch/mips/defconfig-decstation	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-decstation	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -223,11 +223,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -306,9 +301,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -477,7 +474,6 @@
 CONFIG_SERIAL_DEC_CONSOLE=y
 CONFIG_DZ=y
 CONFIG_ZS=y
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-e55 linux-2.4.30-linux-mips.org/arch/mips/defconfig-e55
--- linux-2.4.30/arch/mips/defconfig-e55	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-e55	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -222,11 +222,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -426,7 +421,6 @@
 # CONFIG_SERIAL_MULTIPORT is not set
 # CONFIG_HUB6 is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 # CONFIG_VR41XX_KIU is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
diff -Nur linux-2.4.30/arch/mips/defconfig-eagle linux-2.4.30-linux-mips.org/arch/mips/defconfig-eagle
--- linux-2.4.30/arch/mips/defconfig-eagle	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-eagle	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -208,8 +208,8 @@
 # Mapping drivers for chip access
 #
 CONFIG_MTD_PHYSMAP=y
-CONFIG_MTD_PHYSMAP_START=1c000000
-CONFIG_MTD_PHYSMAP_LEN=2000000
+CONFIG_MTD_PHYSMAP_START=0x1c000000
+CONFIG_MTD_PHYSMAP_LEN=0x2000000
 CONFIG_MTD_PHYSMAP_BUSWIDTH=4
 # CONFIG_MTD_PB1000 is not set
 # CONFIG_MTD_PB1500 is not set
@@ -217,9 +217,7 @@
 # CONFIG_MTD_BOSPORUS is not set
 # CONFIG_MTD_XXS1500 is not set
 # CONFIG_MTD_MTX1 is not set
-# CONFIG_MTD_DB1X00 is not set
 # CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
 # CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
@@ -238,7 +236,6 @@
 #
 # Disk-On-Chip Device Drivers
 #
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
 # CONFIG_MTD_DOCPROBE is not set
@@ -327,11 +324,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -587,7 +579,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 # CONFIG_VR41XX_KIU is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
diff -Nur linux-2.4.30/arch/mips/defconfig-ev64120 linux-2.4.30-linux-mips.org/arch/mips/defconfig-ev64120
--- linux-2.4.30/arch/mips/defconfig-ev64120	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-ev64120	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -230,11 +230,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -443,7 +438,6 @@
 # CONFIG_SERIAL_CONSOLE is not set
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-ev96100 linux-2.4.30-linux-mips.org/arch/mips/defconfig-ev96100
--- linux-2.4.30/arch/mips/defconfig-ev96100	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-ev96100	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -232,11 +232,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -441,7 +436,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-ficmmp linux-2.4.30-linux-mips.org/arch/mips/defconfig-ficmmp
--- linux-2.4.30/arch/mips/defconfig-ficmmp	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-ficmmp	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,954 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+CONFIG_MIPS=y
+CONFIG_MIPS32=y
+# CONFIG_MIPS64 is not set
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# Machine selection
+#
+# CONFIG_ACER_PICA_61 is not set
+# CONFIG_MIPS_BOSPORUS is not set
+CONFIG_MIPS_FICMMP=y
+# CONFIG_MIPS_MIRAGE is not set
+# CONFIG_MIPS_DB1000 is not set
+# CONFIG_MIPS_DB1100 is not set
+# CONFIG_MIPS_DB1500 is not set
+# CONFIG_MIPS_DB1550 is not set
+# CONFIG_MIPS_DB1200 is not set
+# CONFIG_MIPS_PB1000 is not set
+# CONFIG_MIPS_PB1100 is not set
+# CONFIG_MIPS_PB1500 is not set
+# CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_PB1200 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
+# CONFIG_MIPS_XXS1500 is not set
+# CONFIG_MIPS_MTX1 is not set
+# CONFIG_COGENT_CSB250 is not set
+# CONFIG_BAGET_MIPS is not set
+# CONFIG_CASIO_E55 is not set
+# CONFIG_MIPS_COBALT is not set
+# CONFIG_DECSTATION is not set
+# CONFIG_MIPS_EV64120 is not set
+# CONFIG_MIPS_EV96100 is not set
+# CONFIG_MIPS_IVR is not set
+# CONFIG_HP_LASERJET is not set
+# CONFIG_IBM_WORKPAD is not set
+# CONFIG_LASAT is not set
+# CONFIG_MIPS_ITE8172 is not set
+# CONFIG_MIPS_ATLAS is not set
+# CONFIG_MIPS_MAGNUM_4000 is not set
+# CONFIG_MIPS_MALTA is not set
+# CONFIG_MIPS_SEAD is not set
+# CONFIG_MOMENCO_OCELOT is not set
+# CONFIG_MOMENCO_OCELOT_G is not set
+# CONFIG_MOMENCO_OCELOT_C is not set
+# CONFIG_MOMENCO_JAGUAR_ATX is not set
+# CONFIG_PMC_BIG_SUR is not set
+# CONFIG_PMC_STRETCH is not set
+# CONFIG_PMC_YOSEMITE is not set
+# CONFIG_DDB5074 is not set
+# CONFIG_DDB5476 is not set
+# CONFIG_DDB5477 is not set
+# CONFIG_NEC_OSPREY is not set
+# CONFIG_NEC_EAGLE is not set
+# CONFIG_OLIVETTI_M700 is not set
+# CONFIG_NINO is not set
+# CONFIG_SGI_IP22 is not set
+# CONFIG_SGI_IP27 is not set
+# CONFIG_SIBYTE_SB1xxx_SOC is not set
+# CONFIG_SNI_RM200_PCI is not set
+# CONFIG_TANBAC_TB0226 is not set
+# CONFIG_TANBAC_TB0229 is not set
+# CONFIG_TOSHIBA_JMR3927 is not set
+# CONFIG_TOSHIBA_RBTX4927 is not set
+# CONFIG_VICTOR_MPC30X is not set
+# CONFIG_ZAO_CAPCELLA is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
+CONFIG_SOC_AU1X00=y
+CONFIG_SOC_AU1200=y
+CONFIG_NONCOHERENT_IO=y
+CONFIG_PC_KEYB=y
+# CONFIG_MIPS_AU1000 is not set
+
+#
+# CPU selection
+#
+CONFIG_CPU_MIPS32=y
+# CONFIG_CPU_MIPS64 is not set
+# CONFIG_CPU_R3000 is not set
+# CONFIG_CPU_TX39XX is not set
+# CONFIG_CPU_VR41XX is not set
+# CONFIG_CPU_R4300 is not set
+# CONFIG_CPU_R4X00 is not set
+# CONFIG_CPU_TX49XX is not set
+# CONFIG_CPU_R5000 is not set
+# CONFIG_CPU_R5432 is not set
+# CONFIG_CPU_R6000 is not set
+# CONFIG_CPU_NEVADA is not set
+# CONFIG_CPU_R8000 is not set
+# CONFIG_CPU_R10000 is not set
+# CONFIG_CPU_RM7000 is not set
+# CONFIG_CPU_RM9000 is not set
+# CONFIG_CPU_SB1 is not set
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_CPU_HAS_PREFETCH=y
+# CONFIG_VTAG_ICACHE is not set
+CONFIG_64BIT_PHYS_ADDR=y
+# CONFIG_CPU_ADVANCED is not set
+CONFIG_CPU_HAS_LLSC=y
+# CONFIG_CPU_HAS_LLDSCD is not set
+# CONFIG_CPU_HAS_WB is not set
+CONFIG_CPU_HAS_SYNC=y
+
+#
+# General setup
+#
+CONFIG_CPU_LITTLE_ENDIAN=y
+# CONFIG_BUILD_ELF64 is not set
+CONFIG_NET=y
+CONFIG_PCI=y
+CONFIG_PCI_NEW=y
+CONFIG_PCI_AUTO=y
+# CONFIG_PCI_NAMES is not set
+# CONFIG_ISA is not set
+# CONFIG_TC is not set
+# CONFIG_MCA is not set
+# CONFIG_SBUS is not set
+# CONFIG_HOTPLUG is not set
+# CONFIG_PCMCIA is not set
+# CONFIG_HOTPLUG_PCI is not set
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+CONFIG_KCORE_ELF=y
+# CONFIG_KCORE_AOUT is not set
+# CONFIG_BINFMT_AOUT is not set
+CONFIG_BINFMT_ELF=y
+# CONFIG_MIPS32_COMPAT is not set
+# CONFIG_MIPS32_O32 is not set
+# CONFIG_MIPS32_N32 is not set
+# CONFIG_BINFMT_ELF32 is not set
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_OOM_KILLER is not set
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="ide3=dma mem=96M root=/dev/hda2 rootflags=data=journal"
+# CONFIG_PM is not set
+
+#
+# Memory Technology Devices (MTD)
+#
+# CONFIG_MTD is not set
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play configuration
+#
+# CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_CISS_SCSI_TAPE is not set
+# CONFIG_CISS_MONITOR_THREAD is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_SX8 is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_BLK_STATS is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_RAID0 is not set
+# CONFIG_MD_RAID1 is not set
+# CONFIG_MD_RAID5 is not set
+# CONFIG_MD_MULTIPATH is not set
+# CONFIG_BLK_DEV_LVM is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_FILTER=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_INET_ECN is not set
+# CONFIG_SYN_COOKIES is not set
+
+#
+#   IP: Netfilter Configuration
+#
+# CONFIG_IP_NF_CONNTRACK is not set
+# CONFIG_IP_NF_QUEUE is not set
+# CONFIG_IP_NF_IPTABLES is not set
+# CONFIG_IP_NF_ARPTABLES is not set
+# CONFIG_IP_NF_COMPAT_IPCHAINS is not set
+# CONFIG_IP_NF_COMPAT_IPFWADM is not set
+
+#
+#   IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+
+#
+#    SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+# CONFIG_ATM is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_LLC is not set
+# CONFIG_NET_DIVERT is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+# CONFIG_PHONE_IXJ is not set
+# CONFIG_PHONE_IXJ_PCMCIA is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+# CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_IDEDISK_MULTI_MODE=y
+CONFIG_IDEDISK_STROKE=y
+# CONFIG_BLK_DEV_IDECS is not set
+# CONFIG_BLK_DEV_DELKIN is not set
+# CONFIG_BLK_DEV_IDECD is not set
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_GENERIC=y
+# CONFIG_IDEPCI_SHARE_IRQ is not set
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
+# CONFIG_IDEDMA_PCI_AUTO is not set
+# CONFIG_IDEDMA_ONLYDISK is not set
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_BLK_DEV_ADMA100 is not set
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_WDC_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_AMD74XX_OVERRIDE is not set
+# CONFIG_BLK_DEV_ATIIXP is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_TRIFLEX is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_SC1200 is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+CONFIG_BLK_DEV_IDE_AU1XXX=y
+# CONFIG_BLK_DEV_IDE_PIO_AU1XXX is not set
+CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX=y
+# CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX is not set
+CONFIG_BLK_DEV_IDE_SEQTS_PER_RQ_AU1XXX=128
+# CONFIG_IDE_CHIPSETS is not set
+# CONFIG_IDEDMA_AUTO is not set
+# CONFIG_IDEDMA_IVB is not set
+# CONFIG_DMA_NONPCI is not set
+# CONFIG_BLK_DEV_ATARAID is not set
+# CONFIG_BLK_DEV_ATARAID_PDC is not set
+# CONFIG_BLK_DEV_ATARAID_HPT is not set
+# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set
+# CONFIG_BLK_DEV_ATARAID_SII is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_SD_EXTRA_DEVS=40
+CONFIG_CHR_DEV_ST=y
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_SR_EXTRA_DEVS=2
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_SCSI_DEBUG_QUEUES is not set
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_MEGARAID2 is not set
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
+# CONFIG_SCSI_SATA_SVW is not set
+# CONFIG_SCSI_ATA_PIIX is not set
+# CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
+# CONFIG_SCSI_SATA_PROMISE is not set
+# CONFIG_SCSI_SATA_SX4 is not set
+# CONFIG_SCSI_SATA_SIL is not set
+# CONFIG_SCSI_SATA_SIS is not set
+# CONFIG_SCSI_SATA_ULI is not set
+# CONFIG_SCSI_SATA_VIA is not set
+# CONFIG_SCSI_SATA_VITESSE is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_CPQFCTS is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_NCR53C8XX is not set
+# CONFIG_SCSI_SYM53C8XX is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_SIM710 is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# Fusion MPT device support
+#
+# CONFIG_FUSION is not set
+# CONFIG_FUSION_BOOT is not set
+# CONFIG_FUSION_ISENSE is not set
+# CONFIG_FUSION_CTL is not set
+# CONFIG_FUSION_LAN is not set
+
+#
+# IEEE 1394 (FireWire) support (EXPERIMENTAL)
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+# CONFIG_I2O_PCI is not set
+# CONFIG_I2O_BLOCK is not set
+# CONFIG_I2O_LAN is not set
+# CONFIG_I2O_SCSI is not set
+# CONFIG_I2O_PROC is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MIPS_AU1X00_ENET is not set
+CONFIG_AU1XXX_SMC91111=m
+# CONFIG_SUNLANCE is not set
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNBMAC is not set
+# CONFIG_SUNQE is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+# CONFIG_E1000 is not set
+# CONFIG_MYRI_SBUS is not set
+# CONFIG_NS83820 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_TIGON3 is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+# CONFIG_PPP is not set
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+# CONFIG_IRDA is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Input core support
+#
+CONFIG_INPUT=y
+CONFIG_INPUT_KEYBDEV=y
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_UINPUT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+# CONFIG_SERIAL is not set
+# CONFIG_SERIAL_EXTENDED is not set
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_COMPUTONE is not set
+# CONFIG_ROCKETPORT is not set
+# CONFIG_CYCLADES is not set
+# CONFIG_DIGIEPCA is not set
+# CONFIG_DIGI is not set
+# CONFIG_ESPSERIAL is not set
+# CONFIG_MOXA_INTELLIO is not set
+# CONFIG_MOXA_SMARTIO is not set
+# CONFIG_ISI is not set
+# CONFIG_SYNCLINK is not set
+# CONFIG_SYNCLINKMP is not set
+# CONFIG_N_HDLC is not set
+# CONFIG_RISCOM8 is not set
+# CONFIG_SPECIALIX is not set
+# CONFIG_SX is not set
+# CONFIG_RIO is not set
+# CONFIG_STALDRV is not set
+# CONFIG_SERIAL_TX3912 is not set
+# CONFIG_SERIAL_TX3912_CONSOLE is not set
+# CONFIG_SERIAL_TXX9 is not set
+# CONFIG_SERIAL_TXX9_CONSOLE is not set
+CONFIG_AU1X00_UART=y
+CONFIG_AU1X00_SERIAL_CONSOLE=y
+# CONFIG_AU1X00_USB_TTY is not set
+# CONFIG_AU1X00_USB_RAW is not set
+# CONFIG_TXX927_SERIAL is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+
+#
+# I2C support
+#
+CONFIG_I2C=y
+# CONFIG_I2C_ALGOBIT is not set
+# CONFIG_SCx200_ACB is not set
+# CONFIG_I2C_ALGOPCF is not set
+CONFIG_I2C_ALGO_AU1550=y
+# CONFIG_I2C_CHARDEV is not set
+# CONFIG_I2C_PROC is not set
+
+#
+# Mice
+#
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MOUSE is not set
+
+#
+# Joysticks
+#
+# CONFIG_INPUT_GAMEPORT is not set
+# CONFIG_INPUT_NS558 is not set
+# CONFIG_INPUT_LIGHTNING is not set
+# CONFIG_INPUT_PCIGAME is not set
+# CONFIG_INPUT_CS461X is not set
+# CONFIG_INPUT_EMU10K1 is not set
+# CONFIG_INPUT_SERIO is not set
+# CONFIG_INPUT_SERPORT is not set
+# CONFIG_INPUT_ANALOG is not set
+# CONFIG_INPUT_A3D is not set
+# CONFIG_INPUT_ADI is not set
+# CONFIG_INPUT_COBRA is not set
+# CONFIG_INPUT_GF2K is not set
+# CONFIG_INPUT_GRIP is not set
+# CONFIG_INPUT_INTERACT is not set
+# CONFIG_INPUT_TMDC is not set
+# CONFIG_INPUT_SIDEWINDER is not set
+# CONFIG_INPUT_IFORCE_USB is not set
+# CONFIG_INPUT_IFORCE_232 is not set
+# CONFIG_INPUT_WARRIOR is not set
+# CONFIG_INPUT_MAGELLAN is not set
+# CONFIG_INPUT_SPACEORB is not set
+# CONFIG_INPUT_SPACEBALL is not set
+# CONFIG_INPUT_STINGER is not set
+# CONFIG_INPUT_DB9 is not set
+# CONFIG_INPUT_GAMECON is not set
+# CONFIG_INPUT_TURBOGRAFX is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_IPMI_PANIC_EVENT is not set
+# CONFIG_IPMI_DEVICE_INTERFACE is not set
+# CONFIG_IPMI_KCS is not set
+# CONFIG_IPMI_WATCHDOG is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_SCx200 is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_AGP is not set
+
+#
+# Direct Rendering Manager (XFree86 DRI support)
+#
+# CONFIG_DRM is not set
+# CONFIG_AU1X00_GPIO is not set
+# CONFIG_TS_AU1X00_ADS7846 is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_AUTOFS_FS=y
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADFS_FS_RW is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BEFS_DEBUG is not set
+# CONFIG_BFS_FS is not set
+CONFIG_EXT3_FS=y
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=y
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+# CONFIG_CRAMFS is not set
+# CONFIG_TMPFS is not set
+CONFIG_RAMFS=y
+# CONFIG_ISO9660_FS is not set
+# CONFIG_JOLIET is not set
+# CONFIG_ZISOFS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX4FS_RW is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDF_RW is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_UFS_FS_WRITE is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_TRACE is not set
+# CONFIG_XFS_DEBUG is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
+# CONFIG_NFS_FS is not set
+# CONFIG_NFS_V3 is not set
+# CONFIG_NFS_DIRECTIO is not set
+# CONFIG_ROOT_NFS is not set
+# CONFIG_NFSD is not set
+# CONFIG_NFSD_V3 is not set
+# CONFIG_NFSD_TCP is not set
+# CONFIG_SUNRPC is not set
+# CONFIG_LOCKD is not set
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+# CONFIG_NCPFS_NLS is not set
+# CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SMB_NLS is not set
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Console drivers
+#
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_MDA_CONSOLE is not set
+
+#
+# Frame-buffer support
+#
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_CLGEN is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_CYBER2000 is not set
+CONFIG_FB_AU1200=y
+CONFIG_FB_AU1200_DEVS=4
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_INTEL is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_SMI501 is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_E1356 is not set
+# CONFIG_FB_IT8181 is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_FBCON_ADVANCED=y
+# CONFIG_FBCON_MFB is not set
+# CONFIG_FBCON_CFB2 is not set
+# CONFIG_FBCON_CFB4 is not set
+# CONFIG_FBCON_CFB8 is not set
+CONFIG_FBCON_CFB16=y
+# CONFIG_FBCON_CFB24 is not set
+# CONFIG_FBCON_CFB32 is not set
+# CONFIG_FBCON_AFB is not set
+# CONFIG_FBCON_ILBM is not set
+# CONFIG_FBCON_IPLAN2P2 is not set
+# CONFIG_FBCON_IPLAN2P4 is not set
+# CONFIG_FBCON_IPLAN2P8 is not set
+# CONFIG_FBCON_MAC is not set
+# CONFIG_FBCON_VGA_PLANES is not set
+# CONFIG_FBCON_VGA is not set
+# CONFIG_FBCON_HGA is not set
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+CONFIG_FBCON_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=y
+# CONFIG_SOUND_ALI5455 is not set
+# CONFIG_SOUND_BT878 is not set
+# CONFIG_SOUND_CMPCI is not set
+# CONFIG_SOUND_EMU10K1 is not set
+# CONFIG_MIDI_EMU10K1 is not set
+# CONFIG_SOUND_FUSION is not set
+# CONFIG_SOUND_CS4281 is not set
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_ESSSOLO1 is not set
+# CONFIG_SOUND_MAESTRO is not set
+# CONFIG_SOUND_MAESTRO3 is not set
+# CONFIG_SOUND_FORTE is not set
+# CONFIG_SOUND_ICH is not set
+# CONFIG_SOUND_RME96XX is not set
+# CONFIG_SOUND_SONICVIBES is not set
+# CONFIG_SOUND_AU1X00 is not set
+# CONFIG_SOUND_AU1550_PSC is not set
+CONFIG_SOUND_AU1550_I2S=y
+# CONFIG_SOUND_TRIDENT is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+# CONFIG_SOUND_VIA82CXXX is not set
+# CONFIG_MIDI_VIA82CXXX is not set
+# CONFIG_SOUND_OSS is not set
+# CONFIG_SOUND_TVMIXER is not set
+# CONFIG_SOUND_AD1980 is not set
+# CONFIG_SOUND_WM97XX is not set
+
+#
+# USB support
+#
+# CONFIG_USB is not set
+
+#
+# Support for USB gadgets
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Bluetooth support
+#
+# CONFIG_BLUEZ is not set
+
+#
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+# CONFIG_MMC_AU1100 is not set
+
+#
+# Kernel hacking
+#
+CONFIG_CROSSCOMPILE=y
+# CONFIG_RUNTIME_DEBUG is not set
+# CONFIG_KGDB is not set
+# CONFIG_GDB_CONSOLE is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_MIPS_UNCACHED is not set
+CONFIG_LOG_BUF_SHIFT=0
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+# CONFIG_CRC32 is not set
+CONFIG_ZLIB_INFLATE=m
+CONFIG_ZLIB_DEFLATE=m
diff -Nur linux-2.4.30/arch/mips/defconfig-hp-lj linux-2.4.30-linux-mips.org/arch/mips/defconfig-hp-lj
--- linux-2.4.30/arch/mips/defconfig-hp-lj	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-hp-lj	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -184,8 +184,8 @@
 # Mapping drivers for chip access
 #
 CONFIG_MTD_PHYSMAP=y
-CONFIG_MTD_PHYSMAP_START=10040000
-CONFIG_MTD_PHYSMAP_LEN=00fc0000
+CONFIG_MTD_PHYSMAP_START=0x10040000
+CONFIG_MTD_PHYSMAP_LEN=0x00fc0000
 CONFIG_MTD_PHYSMAP_BUSWIDTH=4
 # CONFIG_MTD_PB1000 is not set
 # CONFIG_MTD_PB1500 is not set
@@ -193,9 +193,7 @@
 # CONFIG_MTD_BOSPORUS is not set
 # CONFIG_MTD_XXS1500 is not set
 # CONFIG_MTD_MTX1 is not set
-# CONFIG_MTD_DB1X00 is not set
 # CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
 # CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
@@ -214,7 +212,6 @@
 #
 # Disk-On-Chip Device Drivers
 #
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
 # CONFIG_MTD_DOCPROBE is not set
@@ -304,11 +301,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -604,7 +596,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 # CONFIG_UNIX98_PTYS is not set
 
 #
diff -Nur linux-2.4.30/arch/mips/defconfig-hydrogen3 linux-2.4.30-linux-mips.org/arch/mips/defconfig-hydrogen3
--- linux-2.4.30/arch/mips/defconfig-hydrogen3	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-hydrogen3	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 #
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
 #
 CONFIG_MIPS=y
 CONFIG_MIPS32=y
@@ -22,16 +22,19 @@
 #
 # CONFIG_ACER_PICA_61 is not set
 # CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_FICMMP is not set
 # CONFIG_MIPS_MIRAGE is not set
 # CONFIG_MIPS_DB1000 is not set
 # CONFIG_MIPS_DB1100 is not set
 # CONFIG_MIPS_DB1500 is not set
 # CONFIG_MIPS_DB1550 is not set
+# CONFIG_MIPS_DB1200 is not set
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-CONFIG_MIPS_HYDROGEN3=y
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_PB1200 is not set
+CONFIG_MIPS_HYDROGEN3=y
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -79,6 +82,7 @@
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
 CONFIG_SOC_AU1X00=y
 CONFIG_SOC_AU1100=y
+CONFIG_SOC_AU1X00_32KHZ=y
 CONFIG_NONCOHERENT_IO=y
 CONFIG_PC_KEYB=y
 CONFIG_SWAP_IO_SPACE=y
@@ -171,79 +175,7 @@
 #
 # Memory Technology Devices (MTD)
 #
-CONFIG_MTD=y
-# CONFIG_MTD_DEBUG is not set
-CONFIG_MTD_PARTITIONS=y
-# CONFIG_MTD_CONCAT is not set
-# CONFIG_MTD_REDBOOT_PARTS is not set
-# CONFIG_MTD_CMDLINE_PARTS is not set
-
-#
-# User Modules And Translation Layers
-#
-CONFIG_MTD_CHAR=y
-CONFIG_MTD_BLOCK=y
-# CONFIG_FTL is not set
-# CONFIG_NFTL is not set
-
-#
-# RAM/ROM/Flash chip drivers
-#
-CONFIG_MTD_CFI=y
-# CONFIG_MTD_JEDECPROBE is not set
-CONFIG_MTD_GEN_PROBE=y
-# CONFIG_MTD_CFI_ADV_OPTIONS is not set
-# CONFIG_MTD_CFI_INTELEXT is not set
-CONFIG_MTD_CFI_AMDSTD=y
-# CONFIG_MTD_CFI_STAA is not set
-# CONFIG_MTD_RAM is not set
-# CONFIG_MTD_ROM is not set
-# CONFIG_MTD_ABSENT is not set
-# CONFIG_MTD_OBSOLETE_CHIPS is not set
-# CONFIG_MTD_AMDSTD is not set
-# CONFIG_MTD_SHARP is not set
-# CONFIG_MTD_JEDEC is not set
-
-#
-# Mapping drivers for chip access
-#
-# CONFIG_MTD_PHYSMAP is not set
-# CONFIG_MTD_PB1000 is not set
-# CONFIG_MTD_PB1500 is not set
-# CONFIG_MTD_PB1100 is not set
-# CONFIG_MTD_BOSPORUS is not set
-# CONFIG_MTD_XXS1500 is not set
-# CONFIG_MTD_MTX1 is not set
-# CONFIG_MTD_DB1X00 is not set
-# CONFIG_MTD_PB1550 is not set
-CONFIG_MTD_HYDROGEN3=y
-# CONFIG_MTD_MIRAGE is not set
-# CONFIG_MTD_CSTM_MIPS_IXX is not set
-# CONFIG_MTD_OCELOT is not set
-# CONFIG_MTD_LASAT is not set
-# CONFIG_MTD_PCI is not set
-# CONFIG_MTD_PCMCIA is not set
-
-#
-# Self-contained MTD device drivers
-#
-# CONFIG_MTD_PMC551 is not set
-# CONFIG_MTD_SLRAM is not set
-# CONFIG_MTD_MTDRAM is not set
-# CONFIG_MTD_BLKMTD is not set
-
-#
-# Disk-On-Chip Device Drivers
-#
-# CONFIG_MTD_DOC1000 is not set
-# CONFIG_MTD_DOC2000 is not set
-# CONFIG_MTD_DOC2001 is not set
-# CONFIG_MTD_DOCPROBE is not set
-
-#
-# NAND Flash Device Drivers
-#
-# CONFIG_MTD_NAND is not set
+# CONFIG_MTD is not set
 
 #
 # Parallel port support
@@ -334,17 +266,8 @@
 # CONFIG_IP_SCTP is not set
 # CONFIG_ATM is not set
 # CONFIG_VLAN_8021Q is not set
-
-#
-#  
-#
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -382,10 +305,6 @@
 # IDE, ATA and ATAPI Block devices
 #
 CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
 # CONFIG_BLK_DEV_HD_IDE is not set
 # CONFIG_BLK_DEV_HD is not set
 # CONFIG_BLK_DEV_IDE_SATA is not set
@@ -399,14 +318,14 @@
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_IDE_TASK_IOCTL is not set
-
-#
-# IDE chipset support/bugfixes
-#
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
 # CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_BLK_DEV_IDE_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX is not set
 # CONFIG_IDE_CHIPSETS is not set
 # CONFIG_IDEDMA_AUTO is not set
 # CONFIG_DMA_NONPCI is not set
@@ -466,6 +385,7 @@
 CONFIG_NET_ETHERNET=y
 CONFIG_MIPS_AU1X00_ENET=y
 # CONFIG_BCM5222_DUAL_PHY is not set
+# CONFIG_AU1XXX_SMC91111 is not set
 # CONFIG_SUNLANCE is not set
 # CONFIG_HAPPYMEAL is not set
 # CONFIG_SUNBMAC is not set
@@ -590,7 +510,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-CONFIG_MIPS_HYDROGEN3_BUTTONS=y
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
@@ -616,10 +535,6 @@
 # CONFIG_INPUT_EMU10K1 is not set
 # CONFIG_INPUT_SERIO is not set
 # CONFIG_INPUT_SERPORT is not set
-
-#
-# Joysticks
-#
 # CONFIG_INPUT_ANALOG is not set
 # CONFIG_INPUT_A3D is not set
 # CONFIG_INPUT_ADI is not set
@@ -838,18 +753,19 @@
 # CONFIG_FB_PM2 is not set
 # CONFIG_FB_PM3 is not set
 # CONFIG_FB_CYBER2000 is not set
+CONFIG_FB_AU1100=y
 # CONFIG_FB_MATROX is not set
 # CONFIG_FB_ATY is not set
 # CONFIG_FB_RADEON is not set
 # CONFIG_FB_ATY128 is not set
 # CONFIG_FB_INTEL is not set
 # CONFIG_FB_SIS is not set
+# CONFIG_FB_SMI501 is not set
 # CONFIG_FB_NEOMAGIC is not set
 # CONFIG_FB_3DFX is not set
 # CONFIG_FB_VOODOO1 is not set
 # CONFIG_FB_TRIDENT is not set
 # CONFIG_FB_E1356 is not set
-CONFIG_FB_AU1100=y
 # CONFIG_FB_IT8181 is not set
 # CONFIG_FB_VIRTUAL is not set
 CONFIG_FBCON_ADVANCED=y
@@ -895,8 +811,6 @@
 # CONFIG_SOUND_RME96XX is not set
 # CONFIG_SOUND_SONICVIBES is not set
 CONFIG_SOUND_AU1X00=y
-# CONFIG_SOUND_AU1550_PSC is not set
-# CONFIG_SOUND_AU1550_I2S is not set
 # CONFIG_SOUND_TRIDENT is not set
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
@@ -912,32 +826,18 @@
 #
 CONFIG_USB=y
 # CONFIG_USB_DEBUG is not set
-
-#
-# Miscellaneous USB options
-#
 # CONFIG_USB_DEVICEFS is not set
 # CONFIG_USB_BANDWIDTH is not set
-
-#
-# USB Host Controller Drivers
-#
 # CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_NON_PCI_EHCI is not set
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=y
-
-#
-# USB Device Class drivers
-#
+CONFIG_USB_NON_PCI_OHCI=y
 # CONFIG_USB_AUDIO is not set
 # CONFIG_USB_EMI26 is not set
 # CONFIG_USB_BLUETOOTH is not set
 # CONFIG_USB_MIDI is not set
-
-#
-#   SCSI support is needed for USB Storage
-#
 # CONFIG_USB_STORAGE is not set
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DATAFAB is not set
@@ -950,10 +850,6 @@
 # CONFIG_USB_STORAGE_JUMPSHOT is not set
 # CONFIG_USB_ACM is not set
 # CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
 CONFIG_USB_HID=y
 CONFIG_USB_HIDINPUT=y
 # CONFIG_USB_HIDDEV is not set
@@ -961,47 +857,23 @@
 # CONFIG_USB_WACOM is not set
 # CONFIG_USB_KBTAB is not set
 # CONFIG_USB_POWERMATE is not set
-
-#
-# USB Imaging devices
-#
 # CONFIG_USB_DC2XX is not set
 # CONFIG_USB_MDC800 is not set
 # CONFIG_USB_SCANNER is not set
 # CONFIG_USB_MICROTEK is not set
 # CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-
-#
-#   Video4Linux support is needed for USB Multimedia device support
-#
-
-#
-# USB Network adaptors
-#
 # CONFIG_USB_PEGASUS is not set
 # CONFIG_USB_RTL8150 is not set
 # CONFIG_USB_KAWETH is not set
 # CONFIG_USB_CATC is not set
 # CONFIG_USB_CDCETHER is not set
 # CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
 # CONFIG_USB_USS720 is not set
 
 #
 # USB Serial Converter support
 #
 # CONFIG_USB_SERIAL is not set
-
-#
-# USB Miscellaneous drivers
-#
 # CONFIG_USB_RIO500 is not set
 # CONFIG_USB_AUERSWALD is not set
 # CONFIG_USB_TIGL is not set
@@ -1019,6 +891,12 @@
 # CONFIG_BLUEZ is not set
 
 #
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+# CONFIG_MMC_AU1100 is not set
+
+#
 # Kernel hacking
 #
 CONFIG_CROSSCOMPILE=y
diff -Nur linux-2.4.30/arch/mips/defconfig-ip22 linux-2.4.30-linux-mips.org/arch/mips/defconfig-ip22
--- linux-2.4.30/arch/mips/defconfig-ip22	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-ip22	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -235,11 +235,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -319,9 +314,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -465,7 +462,6 @@
 # CONFIG_SERIAL is not set
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-it8172 linux-2.4.30-linux-mips.org/arch/mips/defconfig-it8172
--- linux-2.4.30/arch/mips/defconfig-it8172	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-it8172	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -186,8 +186,8 @@
 # Mapping drivers for chip access
 #
 CONFIG_MTD_PHYSMAP=y
-CONFIG_MTD_PHYSMAP_START=8000000
-CONFIG_MTD_PHYSMAP_LEN=2000000
+CONFIG_MTD_PHYSMAP_START=0x8000000
+CONFIG_MTD_PHYSMAP_LEN=0x2000000
 CONFIG_MTD_PHYSMAP_BUSWIDTH=4
 # CONFIG_MTD_PB1000 is not set
 # CONFIG_MTD_PB1500 is not set
@@ -195,9 +195,7 @@
 # CONFIG_MTD_BOSPORUS is not set
 # CONFIG_MTD_XXS1500 is not set
 # CONFIG_MTD_MTX1 is not set
-# CONFIG_MTD_DB1X00 is not set
 # CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
 # CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
@@ -216,7 +214,6 @@
 #
 # Disk-On-Chip Device Drivers
 #
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
 # CONFIG_MTD_DOCPROBE is not set
@@ -304,11 +301,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -592,7 +584,6 @@
 CONFIG_PC_KEYB=y
 # CONFIG_IT8172_SCR0 is not set
 # CONFIG_IT8172_SCR1 is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-ivr linux-2.4.30-linux-mips.org/arch/mips/defconfig-ivr
--- linux-2.4.30/arch/mips/defconfig-ivr	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-ivr	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -226,11 +226,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -516,7 +511,6 @@
 CONFIG_QTRONIX_KEYBOARD=y
 CONFIG_IT8172_CIR=y
 # CONFIG_IT8172_SCR0 is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-jmr3927 linux-2.4.30-linux-mips.org/arch/mips/defconfig-jmr3927
--- linux-2.4.30/arch/mips/defconfig-jmr3927	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-jmr3927	2005-05-12 17:57:05.000000000 +0200
@@ -28,8 +28,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -225,11 +225,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -454,7 +449,6 @@
 # CONFIG_SERIAL_TXX9_CONSOLE is not set
 CONFIG_TXX927_SERIAL=y
 CONFIG_TXX927_SERIAL_CONSOLE=y
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 # CONFIG_UNIX98_PTYS is not set
 
 #
diff -Nur linux-2.4.30/arch/mips/defconfig-lasat linux-2.4.30-linux-mips.org/arch/mips/defconfig-lasat
--- linux-2.4.30/arch/mips/defconfig-lasat	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-lasat	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -198,9 +198,7 @@
 # CONFIG_MTD_BOSPORUS is not set
 # CONFIG_MTD_XXS1500 is not set
 # CONFIG_MTD_MTX1 is not set
-# CONFIG_MTD_DB1X00 is not set
 # CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
 # CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
@@ -219,7 +217,6 @@
 #
 # Disk-On-Chip Device Drivers
 #
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
 # CONFIG_MTD_DOCPROBE is not set
@@ -303,11 +300,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -584,7 +576,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-malta linux-2.4.30-linux-mips.org/arch/mips/defconfig-malta
--- linux-2.4.30/arch/mips/defconfig-malta	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-malta	2005-05-12 17:57:05.000000000 +0200
@@ -22,16 +22,19 @@
 #
 # CONFIG_ACER_PICA_61 is not set
 # CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_FICMMP is not set
 # CONFIG_MIPS_MIRAGE is not set
 # CONFIG_MIPS_DB1000 is not set
 # CONFIG_MIPS_DB1100 is not set
 # CONFIG_MIPS_DB1500 is not set
 # CONFIG_MIPS_DB1550 is not set
+# CONFIG_MIPS_DB1200 is not set
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_PB1200 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -237,11 +240,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -273,8 +271,83 @@
 #
 # ATA/IDE/MFM/RLL support
 #
-# CONFIG_IDE is not set
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
 # CONFIG_BLK_DEV_HD is not set
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_IDEDISK_STROKE is not set
+# CONFIG_BLK_DEV_IDECS is not set
+# CONFIG_BLK_DEV_DELKIN is not set
+CONFIG_BLK_DEV_IDECD=y
+CONFIG_BLK_DEV_IDETAPE=y
+CONFIG_BLK_DEV_IDEFLOPPY=y
+CONFIG_BLK_DEV_IDESCSI=y
+# CONFIG_IDE_TASK_IOCTL is not set
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_GENERIC=y
+CONFIG_IDEPCI_SHARE_IRQ=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_BLK_DEV_IDEDMA_FORCED=y
+CONFIG_IDEDMA_PCI_AUTO=y
+# CONFIG_IDEDMA_ONLYDISK is not set
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_BLK_DEV_ADMA100 is not set
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_WDC_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_AMD74XX_OVERRIDE is not set
+# CONFIG_BLK_DEV_ATIIXP is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_TRIFLEX is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+CONFIG_BLK_DEV_PIIX=y
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_SC1200 is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_IDEDMA_IVB is not set
+# CONFIG_DMA_NONPCI is not set
+# CONFIG_BLK_DEV_ATARAID is not set
+# CONFIG_BLK_DEV_ATARAID_PDC is not set
+# CONFIG_BLK_DEV_ATARAID_HPT is not set
+# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set
+# CONFIG_BLK_DEV_ATARAID_SII is not set
 
 #
 # SCSI support
@@ -319,9 +392,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -524,7 +599,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-mirage linux-2.4.30-linux-mips.org/arch/mips/defconfig-mirage
--- linux-2.4.30/arch/mips/defconfig-mirage	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-mirage	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 #
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
 #
 CONFIG_MIPS=y
 CONFIG_MIPS32=y
@@ -22,16 +22,19 @@
 #
 # CONFIG_ACER_PICA_61 is not set
 # CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_FICMMP is not set
 CONFIG_MIPS_MIRAGE=y
 # CONFIG_MIPS_DB1000 is not set
 # CONFIG_MIPS_DB1100 is not set
 # CONFIG_MIPS_DB1500 is not set
 # CONFIG_MIPS_DB1550 is not set
+# CONFIG_MIPS_DB1200 is not set
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_PB1200 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -166,79 +169,7 @@
 #
 # Memory Technology Devices (MTD)
 #
-CONFIG_MTD=y
-# CONFIG_MTD_DEBUG is not set
-CONFIG_MTD_PARTITIONS=y
-# CONFIG_MTD_CONCAT is not set
-# CONFIG_MTD_REDBOOT_PARTS is not set
-# CONFIG_MTD_CMDLINE_PARTS is not set
-
-#
-# User Modules And Translation Layers
-#
-CONFIG_MTD_CHAR=y
-CONFIG_MTD_BLOCK=y
-# CONFIG_FTL is not set
-# CONFIG_NFTL is not set
-
-#
-# RAM/ROM/Flash chip drivers
-#
-CONFIG_MTD_CFI=y
-# CONFIG_MTD_JEDECPROBE is not set
-CONFIG_MTD_GEN_PROBE=y
-# CONFIG_MTD_CFI_ADV_OPTIONS is not set
-# CONFIG_MTD_CFI_INTELEXT is not set
-CONFIG_MTD_CFI_AMDSTD=y
-# CONFIG_MTD_CFI_STAA is not set
-# CONFIG_MTD_RAM is not set
-# CONFIG_MTD_ROM is not set
-# CONFIG_MTD_ABSENT is not set
-# CONFIG_MTD_OBSOLETE_CHIPS is not set
-# CONFIG_MTD_AMDSTD is not set
-# CONFIG_MTD_SHARP is not set
-# CONFIG_MTD_JEDEC is not set
-
-#
-# Mapping drivers for chip access
-#
-# CONFIG_MTD_PHYSMAP is not set
-# CONFIG_MTD_PB1000 is not set
-# CONFIG_MTD_PB1500 is not set
-# CONFIG_MTD_PB1100 is not set
-# CONFIG_MTD_BOSPORUS is not set
-# CONFIG_MTD_XXS1500 is not set
-# CONFIG_MTD_MTX1 is not set
-# CONFIG_MTD_DB1X00 is not set
-# CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
-CONFIG_MTD_MIRAGE=y
-# CONFIG_MTD_CSTM_MIPS_IXX is not set
-# CONFIG_MTD_OCELOT is not set
-# CONFIG_MTD_LASAT is not set
-# CONFIG_MTD_PCI is not set
-# CONFIG_MTD_PCMCIA is not set
-
-#
-# Self-contained MTD device drivers
-#
-# CONFIG_MTD_PMC551 is not set
-# CONFIG_MTD_SLRAM is not set
-# CONFIG_MTD_MTDRAM is not set
-# CONFIG_MTD_BLKMTD is not set
-
-#
-# Disk-On-Chip Device Drivers
-#
-# CONFIG_MTD_DOC1000 is not set
-# CONFIG_MTD_DOC2000 is not set
-# CONFIG_MTD_DOC2001 is not set
-# CONFIG_MTD_DOCPROBE is not set
-
-#
-# NAND Flash Device Drivers
-#
-# CONFIG_MTD_NAND is not set
+# CONFIG_MTD is not set
 
 #
 # Parallel port support
@@ -329,17 +260,8 @@
 # CONFIG_IP_SCTP is not set
 # CONFIG_ATM is not set
 # CONFIG_VLAN_8021Q is not set
-
-#
-#  
-#
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -424,6 +346,7 @@
 CONFIG_NET_ETHERNET=y
 CONFIG_MIPS_AU1X00_ENET=y
 # CONFIG_BCM5222_DUAL_PHY is not set
+# CONFIG_AU1XXX_SMC91111 is not set
 # CONFIG_SUNLANCE is not set
 # CONFIG_HAPPYMEAL is not set
 # CONFIG_SUNBMAC is not set
@@ -479,10 +402,6 @@
 # CONFIG_PLX_HERMES is not set
 # CONFIG_TMD_HERMES is not set
 # CONFIG_PCI_HERMES is not set
-
-#
-# Prism54 PCI/PCMCIA GT/Duette Driver - 802.11(a/b/g)
-#
 # CONFIG_PRISM54 is not set
 CONFIG_NET_WIRELESS=y
 
@@ -560,7 +479,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
@@ -599,10 +517,6 @@
 # CONFIG_INPUT_EMU10K1 is not set
 # CONFIG_INPUT_SERIO is not set
 # CONFIG_INPUT_SERPORT is not set
-
-#
-# Joysticks
-#
 # CONFIG_INPUT_ANALOG is not set
 # CONFIG_INPUT_A3D is not set
 # CONFIG_INPUT_ADI is not set
@@ -682,8 +596,7 @@
 # CONFIG_VFAT_FS is not set
 # CONFIG_EFS_FS is not set
 # CONFIG_JFFS_FS is not set
-CONFIG_JFFS2_FS=y
-CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS2_FS is not set
 # CONFIG_CRAMFS is not set
 CONFIG_TMPFS=y
 CONFIG_RAMFS=y
@@ -779,6 +692,7 @@
 # CONFIG_FB_ATY128 is not set
 # CONFIG_FB_INTEL is not set
 # CONFIG_FB_SIS is not set
+CONFIG_FB_SMI501=y
 # CONFIG_FB_NEOMAGIC is not set
 # CONFIG_FB_3DFX is not set
 # CONFIG_FB_VOODOO1 is not set
@@ -832,8 +746,6 @@
 # CONFIG_SOUND_RME96XX is not set
 # CONFIG_SOUND_SONICVIBES is not set
 CONFIG_SOUND_AU1X00=y
-# CONFIG_SOUND_AU1550_PSC is not set
-# CONFIG_SOUND_AU1550_I2S is not set
 # CONFIG_SOUND_TRIDENT is not set
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
@@ -849,32 +761,18 @@
 #
 CONFIG_USB=y
 # CONFIG_USB_DEBUG is not set
-
-#
-# Miscellaneous USB options
-#
 # CONFIG_USB_DEVICEFS is not set
 # CONFIG_USB_BANDWIDTH is not set
-
-#
-# USB Host Controller Drivers
-#
 # CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_NON_PCI_EHCI is not set
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=y
-
-#
-# USB Device Class drivers
-#
+CONFIG_USB_NON_PCI_OHCI=y
 # CONFIG_USB_AUDIO is not set
 # CONFIG_USB_EMI26 is not set
 # CONFIG_USB_BLUETOOTH is not set
 # CONFIG_USB_MIDI is not set
-
-#
-#   SCSI support is needed for USB Storage
-#
 # CONFIG_USB_STORAGE is not set
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DATAFAB is not set
@@ -887,10 +785,6 @@
 # CONFIG_USB_STORAGE_JUMPSHOT is not set
 # CONFIG_USB_ACM is not set
 # CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
 CONFIG_USB_HID=y
 CONFIG_USB_HIDINPUT=y
 # CONFIG_USB_HIDDEV is not set
@@ -898,47 +792,23 @@
 # CONFIG_USB_WACOM is not set
 # CONFIG_USB_KBTAB is not set
 # CONFIG_USB_POWERMATE is not set
-
-#
-# USB Imaging devices
-#
 # CONFIG_USB_DC2XX is not set
 # CONFIG_USB_MDC800 is not set
 # CONFIG_USB_SCANNER is not set
 # CONFIG_USB_MICROTEK is not set
 # CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-
-#
-#   Video4Linux support is needed for USB Multimedia device support
-#
-
-#
-# USB Network adaptors
-#
 # CONFIG_USB_PEGASUS is not set
 # CONFIG_USB_RTL8150 is not set
 # CONFIG_USB_KAWETH is not set
 # CONFIG_USB_CATC is not set
 # CONFIG_USB_CDCETHER is not set
 # CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
 # CONFIG_USB_USS720 is not set
 
 #
 # USB Serial Converter support
 #
 # CONFIG_USB_SERIAL is not set
-
-#
-# USB Miscellaneous drivers
-#
 # CONFIG_USB_RIO500 is not set
 # CONFIG_USB_AUERSWALD is not set
 # CONFIG_USB_TIGL is not set
@@ -956,6 +826,12 @@
 # CONFIG_BLUEZ is not set
 
 #
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+# CONFIG_MMC_AU1100 is not set
+
+#
 # Kernel hacking
 #
 CONFIG_CROSSCOMPILE=y
@@ -976,6 +852,6 @@
 # Library routines
 #
 # CONFIG_CRC32 is not set
-CONFIG_ZLIB_INFLATE=y
-CONFIG_ZLIB_DEFLATE=y
+CONFIG_ZLIB_INFLATE=m
+CONFIG_ZLIB_DEFLATE=m
 # CONFIG_FW_LOADER is not set
diff -Nur linux-2.4.30/arch/mips/defconfig-mpc30x linux-2.4.30-linux-mips.org/arch/mips/defconfig-mpc30x
--- linux-2.4.30/arch/mips/defconfig-mpc30x	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-mpc30x	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -228,11 +228,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -400,7 +395,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 # CONFIG_VR41XX_KIU is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
diff -Nur linux-2.4.30/arch/mips/defconfig-mtx-1 linux-2.4.30-linux-mips.org/arch/mips/defconfig-mtx-1
--- linux-2.4.30/arch/mips/defconfig-mtx-1	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-mtx-1	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 CONFIG_MIPS_MTX1=y
 # CONFIG_COGENT_CSB250 is not set
@@ -193,9 +193,7 @@
 # CONFIG_MTD_BOSPORUS is not set
 # CONFIG_MTD_XXS1500 is not set
 CONFIG_MTD_MTX1=y
-# CONFIG_MTD_DB1X00 is not set
 # CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
 # CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
@@ -214,7 +212,6 @@
 #
 # Disk-On-Chip Device Drivers
 #
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
 # CONFIG_MTD_DOCPROBE is not set
@@ -371,11 +368,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 CONFIG_BRIDGE=m
 # CONFIG_X25 is not set
@@ -479,9 +471,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -700,7 +694,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-nino linux-2.4.30-linux-mips.org/arch/mips/defconfig-nino
--- linux-2.4.30/arch/mips/defconfig-nino	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-nino	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -226,11 +226,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -339,7 +334,6 @@
 # CONFIG_SERIAL_TXX9 is not set
 # CONFIG_SERIAL_TXX9_CONSOLE is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 # CONFIG_UNIX98_PTYS is not set
 
 #
diff -Nur linux-2.4.30/arch/mips/defconfig-ocelot linux-2.4.30-linux-mips.org/arch/mips/defconfig-ocelot
--- linux-2.4.30/arch/mips/defconfig-ocelot	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-ocelot	2005-05-12 17:57:05.000000000 +0200
@@ -28,8 +28,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -194,9 +194,7 @@
 # CONFIG_MTD_BOSPORUS is not set
 # CONFIG_MTD_XXS1500 is not set
 # CONFIG_MTD_MTX1 is not set
-# CONFIG_MTD_DB1X00 is not set
 # CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
 # CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 CONFIG_MTD_OCELOT=y
@@ -215,7 +213,6 @@
 #
 # Disk-On-Chip Device Drivers
 #
-# CONFIG_MTD_DOC1000 is not set
 CONFIG_MTD_DOC2000=y
 # CONFIG_MTD_DOC2001 is not set
 CONFIG_MTD_DOCPROBE=y
@@ -307,11 +304,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -513,7 +505,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-osprey linux-2.4.30-linux-mips.org/arch/mips/defconfig-osprey
--- linux-2.4.30/arch/mips/defconfig-osprey	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-osprey	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -227,11 +227,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -388,7 +383,6 @@
 # CONFIG_SERIAL_MULTIPORT is not set
 # CONFIG_HUB6 is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 # CONFIG_VR41XX_KIU is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
diff -Nur linux-2.4.30/arch/mips/defconfig-pb1000 linux-2.4.30-linux-mips.org/arch/mips/defconfig-pb1000
--- linux-2.4.30/arch/mips/defconfig-pb1000	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-pb1000	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 #
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
 #
 CONFIG_MIPS=y
 CONFIG_MIPS32=y
@@ -22,16 +22,19 @@
 #
 # CONFIG_ACER_PICA_61 is not set
 # CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_FICMMP is not set
 # CONFIG_MIPS_MIRAGE is not set
 # CONFIG_MIPS_DB1000 is not set
 # CONFIG_MIPS_DB1100 is not set
 # CONFIG_MIPS_DB1500 is not set
 # CONFIG_MIPS_DB1550 is not set
+# CONFIG_MIPS_DB1200 is not set
 CONFIG_MIPS_PB1000=y
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_PB1200 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -79,6 +82,7 @@
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
 CONFIG_SOC_AU1X00=y
 CONFIG_SOC_AU1000=y
+CONFIG_SOC_AU1X00_32KHZ=y
 CONFIG_NONCOHERENT_IO=y
 CONFIG_PC_KEYB=y
 CONFIG_SWAP_IO_SPACE_W=y
@@ -178,14 +182,11 @@
 # CONFIG_MTD_CONCAT is not set
 # CONFIG_MTD_REDBOOT_PARTS is not set
 # CONFIG_MTD_CMDLINE_PARTS is not set
-
-#
-# User Modules And Translation Layers
-#
 CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 # CONFIG_FTL is not set
 # CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
 
 #
 # RAM/ROM/Flash chip drivers
@@ -197,6 +198,7 @@
 # CONFIG_MTD_CFI_INTELEXT is not set
 CONFIG_MTD_CFI_AMDSTD=y
 # CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
 # CONFIG_MTD_RAM is not set
 # CONFIG_MTD_ROM is not set
 # CONFIG_MTD_ABSENT is not set
@@ -208,17 +210,10 @@
 #
 # Mapping drivers for chip access
 #
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
 # CONFIG_MTD_PHYSMAP is not set
-CONFIG_MTD_PB1000=y
-# CONFIG_MTD_PB1500 is not set
-# CONFIG_MTD_PB1100 is not set
-# CONFIG_MTD_BOSPORUS is not set
+CONFIG_MTD_ALCHEMY=y
 # CONFIG_MTD_XXS1500 is not set
-# CONFIG_MTD_MTX1 is not set
-# CONFIG_MTD_DB1X00 is not set
-# CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
-# CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
 # CONFIG_MTD_LASAT is not set
@@ -232,13 +227,9 @@
 # CONFIG_MTD_SLRAM is not set
 # CONFIG_MTD_MTDRAM is not set
 # CONFIG_MTD_BLKMTD is not set
-
-#
-# Disk-On-Chip Device Drivers
-#
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
 # CONFIG_MTD_DOCPROBE is not set
 
 #
@@ -318,17 +309,8 @@
 # CONFIG_IP_SCTP is not set
 # CONFIG_ATM is not set
 # CONFIG_VLAN_8021Q is not set
-
-#
-#  
-#
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -366,10 +348,6 @@
 # IDE, ATA and ATAPI Block devices
 #
 CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
 # CONFIG_BLK_DEV_HD_IDE is not set
 # CONFIG_BLK_DEV_HD is not set
 # CONFIG_BLK_DEV_IDE_SATA is not set
@@ -383,14 +361,14 @@
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_IDE_TASK_IOCTL is not set
-
-#
-# IDE chipset support/bugfixes
-#
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
 # CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_BLK_DEV_IDE_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX is not set
 # CONFIG_IDE_CHIPSETS is not set
 # CONFIG_IDEDMA_AUTO is not set
 # CONFIG_DMA_NONPCI is not set
@@ -450,6 +428,7 @@
 CONFIG_NET_ETHERNET=y
 CONFIG_MIPS_AU1X00_ENET=y
 # CONFIG_BCM5222_DUAL_PHY is not set
+# CONFIG_AU1XXX_SMC91111 is not set
 # CONFIG_SUNLANCE is not set
 # CONFIG_HAPPYMEAL is not set
 # CONFIG_SUNBMAC is not set
@@ -526,18 +505,10 @@
 # IrDA (infrared) support
 #
 CONFIG_IRDA=y
-
-#
-# IrDA protocols
-#
 CONFIG_IRLAN=m
 # CONFIG_IRNET is not set
 CONFIG_IRCOMM=m
 # CONFIG_IRDA_ULTRA is not set
-
-#
-# IrDA options
-#
 CONFIG_IRDA_CACHE_LAST_LSAP=y
 CONFIG_IRDA_FAST_RR=y
 # CONFIG_IRDA_DEBUG is not set
@@ -545,21 +516,9 @@
 #
 # Infrared-port device drivers
 #
-
-#
-# SIR device drivers
-#
 # CONFIG_IRTTY_SIR is not set
 # CONFIG_IRPORT_SIR is not set
-
-#
-# Dongle support
-#
 # CONFIG_DONGLE is not set
-
-#
-# FIR device drivers
-#
 # CONFIG_USB_IRDA is not set
 # CONFIG_NSC_FIR is not set
 # CONFIG_WINBOND_FIR is not set
@@ -622,7 +581,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
@@ -648,10 +606,6 @@
 # CONFIG_INPUT_EMU10K1 is not set
 # CONFIG_INPUT_SERIO is not set
 # CONFIG_INPUT_SERPORT is not set
-
-#
-# Joysticks
-#
 # CONFIG_INPUT_ANALOG is not set
 # CONFIG_INPUT_A3D is not set
 # CONFIG_INPUT_ADI is not set
@@ -707,7 +661,7 @@
 #
 # CONFIG_PCMCIA_SERIAL_CS is not set
 # CONFIG_SYNCLINK_CS is not set
-CONFIG_AU1X00_GPIO=m
+# CONFIG_AU1X00_GPIO is not set
 # CONFIG_TS_AU1X00_ADS7846 is not set
 
 #
@@ -737,8 +691,7 @@
 # CONFIG_VFAT_FS is not set
 # CONFIG_EFS_FS is not set
 # CONFIG_JFFS_FS is not set
-CONFIG_JFFS2_FS=y
-CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS2_FS is not set
 # CONFIG_CRAMFS is not set
 # CONFIG_TMPFS is not set
 CONFIG_RAMFS=y
@@ -834,6 +787,7 @@
 # CONFIG_FB_ATY128 is not set
 # CONFIG_FB_INTEL is not set
 # CONFIG_FB_SIS is not set
+# CONFIG_FB_SMI501 is not set
 # CONFIG_FB_NEOMAGIC is not set
 # CONFIG_FB_3DFX is not set
 # CONFIG_FB_VOODOO1 is not set
@@ -887,8 +841,6 @@
 # CONFIG_SOUND_RME96XX is not set
 # CONFIG_SOUND_SONICVIBES is not set
 CONFIG_SOUND_AU1X00=y
-# CONFIG_SOUND_AU1550_PSC is not set
-# CONFIG_SOUND_AU1550_I2S is not set
 # CONFIG_SOUND_TRIDENT is not set
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
@@ -904,32 +856,18 @@
 #
 CONFIG_USB=y
 # CONFIG_USB_DEBUG is not set
-
-#
-# Miscellaneous USB options
-#
 # CONFIG_USB_DEVICEFS is not set
 # CONFIG_USB_BANDWIDTH is not set
-
-#
-# USB Host Controller Drivers
-#
 # CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_NON_PCI_EHCI is not set
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=y
-
-#
-# USB Device Class drivers
-#
+CONFIG_USB_NON_PCI_OHCI=y
 # CONFIG_USB_AUDIO is not set
 # CONFIG_USB_EMI26 is not set
 # CONFIG_USB_BLUETOOTH is not set
 # CONFIG_USB_MIDI is not set
-
-#
-#   SCSI support is needed for USB Storage
-#
 # CONFIG_USB_STORAGE is not set
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DATAFAB is not set
@@ -942,10 +880,6 @@
 # CONFIG_USB_STORAGE_JUMPSHOT is not set
 # CONFIG_USB_ACM is not set
 # CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
 CONFIG_USB_HID=y
 CONFIG_USB_HIDINPUT=y
 # CONFIG_USB_HIDDEV is not set
@@ -953,47 +887,23 @@
 # CONFIG_USB_WACOM is not set
 # CONFIG_USB_KBTAB is not set
 # CONFIG_USB_POWERMATE is not set
-
-#
-# USB Imaging devices
-#
 # CONFIG_USB_DC2XX is not set
 # CONFIG_USB_MDC800 is not set
 # CONFIG_USB_SCANNER is not set
 # CONFIG_USB_MICROTEK is not set
 # CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-
-#
-#   Video4Linux support is needed for USB Multimedia device support
-#
-
-#
-# USB Network adaptors
-#
 # CONFIG_USB_PEGASUS is not set
 # CONFIG_USB_RTL8150 is not set
 # CONFIG_USB_KAWETH is not set
 # CONFIG_USB_CATC is not set
 # CONFIG_USB_CDCETHER is not set
 # CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
 # CONFIG_USB_USS720 is not set
 
 #
 # USB Serial Converter support
 #
 # CONFIG_USB_SERIAL is not set
-
-#
-# USB Miscellaneous drivers
-#
 # CONFIG_USB_RIO500 is not set
 # CONFIG_USB_AUERSWALD is not set
 # CONFIG_USB_TIGL is not set
@@ -1011,6 +921,12 @@
 # CONFIG_BLUEZ is not set
 
 #
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+# CONFIG_MMC_AU1100 is not set
+
+#
 # Kernel hacking
 #
 CONFIG_CROSSCOMPILE=y
diff -Nur linux-2.4.30/arch/mips/defconfig-pb1100 linux-2.4.30-linux-mips.org/arch/mips/defconfig-pb1100
--- linux-2.4.30/arch/mips/defconfig-pb1100	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-pb1100	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 #
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
 #
 CONFIG_MIPS=y
 CONFIG_MIPS32=y
@@ -22,16 +22,19 @@
 #
 # CONFIG_ACER_PICA_61 is not set
 # CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_FICMMP is not set
 # CONFIG_MIPS_MIRAGE is not set
 # CONFIG_MIPS_DB1000 is not set
 # CONFIG_MIPS_DB1100 is not set
 # CONFIG_MIPS_DB1500 is not set
 # CONFIG_MIPS_DB1550 is not set
+# CONFIG_MIPS_DB1200 is not set
 # CONFIG_MIPS_PB1000 is not set
 CONFIG_MIPS_PB1100=y
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_PB1200 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -79,6 +82,7 @@
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
 CONFIG_SOC_AU1X00=y
 CONFIG_SOC_AU1100=y
+CONFIG_SOC_AU1X00_32KHZ=y
 CONFIG_NONCOHERENT_IO=y
 CONFIG_PC_KEYB=y
 CONFIG_SWAP_IO_SPACE_W=y
@@ -159,14 +163,11 @@
 # CONFIG_MTD_CONCAT is not set
 # CONFIG_MTD_REDBOOT_PARTS is not set
 # CONFIG_MTD_CMDLINE_PARTS is not set
-
-#
-# User Modules And Translation Layers
-#
 CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 # CONFIG_FTL is not set
 # CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
 
 #
 # RAM/ROM/Flash chip drivers
@@ -178,6 +179,7 @@
 CONFIG_MTD_CFI_INTELEXT=y
 # CONFIG_MTD_CFI_AMDSTD is not set
 # CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
 # CONFIG_MTD_RAM is not set
 # CONFIG_MTD_ROM is not set
 # CONFIG_MTD_ABSENT is not set
@@ -189,19 +191,10 @@
 #
 # Mapping drivers for chip access
 #
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
 # CONFIG_MTD_PHYSMAP is not set
-# CONFIG_MTD_PB1000 is not set
-# CONFIG_MTD_PB1500 is not set
-CONFIG_MTD_PB1100=y
-# CONFIG_MTD_BOSPORUS is not set
+CONFIG_MTD_ALCHEMY=y
 # CONFIG_MTD_XXS1500 is not set
-# CONFIG_MTD_MTX1 is not set
-CONFIG_MTD_PB1500_BOOT=y
-CONFIG_MTD_PB1500_USER=y
-# CONFIG_MTD_DB1X00 is not set
-# CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
-# CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
 # CONFIG_MTD_LASAT is not set
@@ -215,13 +208,9 @@
 # CONFIG_MTD_SLRAM is not set
 # CONFIG_MTD_MTDRAM is not set
 # CONFIG_MTD_BLKMTD is not set
-
-#
-# Disk-On-Chip Device Drivers
-#
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
 # CONFIG_MTD_DOCPROBE is not set
 
 #
@@ -318,17 +307,8 @@
 # CONFIG_IP_SCTP is not set
 # CONFIG_ATM is not set
 # CONFIG_VLAN_8021Q is not set
-
-#
-#  
-#
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -366,10 +346,6 @@
 # IDE, ATA and ATAPI Block devices
 #
 CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
 # CONFIG_BLK_DEV_HD_IDE is not set
 # CONFIG_BLK_DEV_HD is not set
 # CONFIG_BLK_DEV_IDE_SATA is not set
@@ -383,14 +359,14 @@
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_IDE_TASK_IOCTL is not set
-
-#
-# IDE chipset support/bugfixes
-#
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
 # CONFIG_BLK_DEV_IDEPCI is not set
+# CONFIG_BLK_DEV_IDE_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX is not set
 # CONFIG_IDE_CHIPSETS is not set
 # CONFIG_IDEDMA_AUTO is not set
 # CONFIG_DMA_NONPCI is not set
@@ -450,6 +426,7 @@
 CONFIG_NET_ETHERNET=y
 CONFIG_MIPS_AU1X00_ENET=y
 # CONFIG_BCM5222_DUAL_PHY is not set
+# CONFIG_AU1XXX_SMC91111 is not set
 # CONFIG_SUNLANCE is not set
 # CONFIG_HAPPYMEAL is not set
 # CONFIG_SUNBMAC is not set
@@ -517,18 +494,10 @@
 # IrDA (infrared) support
 #
 CONFIG_IRDA=y
-
-#
-# IrDA protocols
-#
 CONFIG_IRLAN=m
 # CONFIG_IRNET is not set
 CONFIG_IRCOMM=m
 # CONFIG_IRDA_ULTRA is not set
-
-#
-# IrDA options
-#
 CONFIG_IRDA_CACHE_LAST_LSAP=y
 CONFIG_IRDA_FAST_RR=y
 # CONFIG_IRDA_DEBUG is not set
@@ -536,21 +505,9 @@
 #
 # Infrared-port device drivers
 #
-
-#
-# SIR device drivers
-#
 # CONFIG_IRTTY_SIR is not set
 # CONFIG_IRPORT_SIR is not set
-
-#
-# Dongle support
-#
 # CONFIG_DONGLE is not set
-
-#
-# FIR device drivers
-#
 # CONFIG_USB_IRDA is not set
 # CONFIG_NSC_FIR is not set
 # CONFIG_WINBOND_FIR is not set
@@ -613,7 +570,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
@@ -639,10 +595,6 @@
 # CONFIG_INPUT_EMU10K1 is not set
 # CONFIG_INPUT_SERIO is not set
 # CONFIG_INPUT_SERPORT is not set
-
-#
-# Joysticks
-#
 # CONFIG_INPUT_ANALOG is not set
 # CONFIG_INPUT_A3D is not set
 # CONFIG_INPUT_ADI is not set
@@ -721,11 +673,8 @@
 # CONFIG_UMSDOS_FS is not set
 # CONFIG_VFAT_FS is not set
 # CONFIG_EFS_FS is not set
-CONFIG_JFFS_FS=m
-CONFIG_JFFS_FS_VERBOSE=0
-CONFIG_JFFS_PROC_FS=y
-CONFIG_JFFS2_FS=m
-CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS_FS is not set
+# CONFIG_JFFS2_FS is not set
 CONFIG_CRAMFS=m
 CONFIG_TMPFS=y
 CONFIG_RAMFS=y
@@ -859,18 +808,19 @@
 # CONFIG_FB_PM2 is not set
 # CONFIG_FB_PM3 is not set
 # CONFIG_FB_CYBER2000 is not set
+CONFIG_FB_AU1100=y
 # CONFIG_FB_MATROX is not set
 # CONFIG_FB_ATY is not set
 # CONFIG_FB_RADEON is not set
 # CONFIG_FB_ATY128 is not set
 # CONFIG_FB_INTEL is not set
 # CONFIG_FB_SIS is not set
+# CONFIG_FB_SMI501 is not set
 # CONFIG_FB_NEOMAGIC is not set
 # CONFIG_FB_3DFX is not set
 # CONFIG_FB_VOODOO1 is not set
 # CONFIG_FB_TRIDENT is not set
 # CONFIG_FB_E1356 is not set
-CONFIG_FB_AU1100=y
 # CONFIG_FB_IT8181 is not set
 # CONFIG_FB_VIRTUAL is not set
 CONFIG_FBCON_ADVANCED=y
@@ -916,8 +866,6 @@
 # CONFIG_SOUND_RME96XX is not set
 # CONFIG_SOUND_SONICVIBES is not set
 # CONFIG_SOUND_AU1X00 is not set
-# CONFIG_SOUND_AU1550_PSC is not set
-# CONFIG_SOUND_AU1550_I2S is not set
 # CONFIG_SOUND_TRIDENT is not set
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
@@ -933,32 +881,18 @@
 #
 CONFIG_USB=y
 # CONFIG_USB_DEBUG is not set
-
-#
-# Miscellaneous USB options
-#
 # CONFIG_USB_DEVICEFS is not set
 # CONFIG_USB_BANDWIDTH is not set
-
-#
-# USB Host Controller Drivers
-#
 # CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_NON_PCI_EHCI is not set
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=y
-
-#
-# USB Device Class drivers
-#
+CONFIG_USB_NON_PCI_OHCI=y
 # CONFIG_USB_AUDIO is not set
 # CONFIG_USB_EMI26 is not set
 # CONFIG_USB_BLUETOOTH is not set
 # CONFIG_USB_MIDI is not set
-
-#
-#   SCSI support is needed for USB Storage
-#
 # CONFIG_USB_STORAGE is not set
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DATAFAB is not set
@@ -971,10 +905,6 @@
 # CONFIG_USB_STORAGE_JUMPSHOT is not set
 # CONFIG_USB_ACM is not set
 # CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
 CONFIG_USB_HID=y
 CONFIG_USB_HIDINPUT=y
 # CONFIG_USB_HIDDEV is not set
@@ -982,47 +912,23 @@
 # CONFIG_USB_WACOM is not set
 # CONFIG_USB_KBTAB is not set
 # CONFIG_USB_POWERMATE is not set
-
-#
-# USB Imaging devices
-#
 # CONFIG_USB_DC2XX is not set
 # CONFIG_USB_MDC800 is not set
 # CONFIG_USB_SCANNER is not set
 # CONFIG_USB_MICROTEK is not set
 # CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-
-#
-#   Video4Linux support is needed for USB Multimedia device support
-#
-
-#
-# USB Network adaptors
-#
 # CONFIG_USB_PEGASUS is not set
 # CONFIG_USB_RTL8150 is not set
 # CONFIG_USB_KAWETH is not set
 # CONFIG_USB_CATC is not set
 # CONFIG_USB_CDCETHER is not set
 # CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
 # CONFIG_USB_USS720 is not set
 
 #
 # USB Serial Converter support
 #
 # CONFIG_USB_SERIAL is not set
-
-#
-# USB Miscellaneous drivers
-#
 # CONFIG_USB_RIO500 is not set
 # CONFIG_USB_AUERSWALD is not set
 # CONFIG_USB_TIGL is not set
@@ -1040,6 +946,12 @@
 # CONFIG_BLUEZ is not set
 
 #
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+# CONFIG_MMC_AU1100 is not set
+
+#
 # Kernel hacking
 #
 CONFIG_CROSSCOMPILE=y
diff -Nur linux-2.4.30/arch/mips/defconfig-pb1200 linux-2.4.30-linux-mips.org/arch/mips/defconfig-pb1200
--- linux-2.4.30/arch/mips/defconfig-pb1200	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-pb1200	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,1115 @@
+#
+# Automatically generated by make menuconfig: don't edit
+#
+CONFIG_MIPS=y
+CONFIG_MIPS32=y
+# CONFIG_MIPS64 is not set
+
+#
+# Code maturity level options
+#
+CONFIG_EXPERIMENTAL=y
+
+#
+# Loadable module support
+#
+CONFIG_MODULES=y
+# CONFIG_MODVERSIONS is not set
+CONFIG_KMOD=y
+
+#
+# Machine selection
+#
+# CONFIG_ACER_PICA_61 is not set
+# CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_FICMMP is not set
+# CONFIG_MIPS_MIRAGE is not set
+# CONFIG_MIPS_DB1000 is not set
+# CONFIG_MIPS_DB1100 is not set
+# CONFIG_MIPS_DB1500 is not set
+# CONFIG_MIPS_DB1550 is not set
+# CONFIG_MIPS_DB1200 is not set
+# CONFIG_MIPS_PB1000 is not set
+# CONFIG_MIPS_PB1100 is not set
+# CONFIG_MIPS_PB1500 is not set
+# CONFIG_MIPS_PB1550 is not set
+CONFIG_MIPS_PB1200=y
+# CONFIG_MIPS_HYDROGEN3 is not set
+# CONFIG_MIPS_XXS1500 is not set
+# CONFIG_MIPS_MTX1 is not set
+# CONFIG_COGENT_CSB250 is not set
+# CONFIG_BAGET_MIPS is not set
+# CONFIG_CASIO_E55 is not set
+# CONFIG_MIPS_COBALT is not set
+# CONFIG_DECSTATION is not set
+# CONFIG_MIPS_EV64120 is not set
+# CONFIG_MIPS_EV96100 is not set
+# CONFIG_MIPS_IVR is not set
+# CONFIG_HP_LASERJET is not set
+# CONFIG_IBM_WORKPAD is not set
+# CONFIG_LASAT is not set
+# CONFIG_MIPS_ITE8172 is not set
+# CONFIG_MIPS_ATLAS is not set
+# CONFIG_MIPS_MAGNUM_4000 is not set
+# CONFIG_MIPS_MALTA is not set
+# CONFIG_MIPS_SEAD is not set
+# CONFIG_MOMENCO_OCELOT is not set
+# CONFIG_MOMENCO_OCELOT_G is not set
+# CONFIG_MOMENCO_OCELOT_C is not set
+# CONFIG_MOMENCO_JAGUAR_ATX is not set
+# CONFIG_PMC_BIG_SUR is not set
+# CONFIG_PMC_STRETCH is not set
+# CONFIG_PMC_YOSEMITE is not set
+# CONFIG_DDB5074 is not set
+# CONFIG_DDB5476 is not set
+# CONFIG_DDB5477 is not set
+# CONFIG_NEC_OSPREY is not set
+# CONFIG_NEC_EAGLE is not set
+# CONFIG_OLIVETTI_M700 is not set
+# CONFIG_NINO is not set
+# CONFIG_SGI_IP22 is not set
+# CONFIG_SGI_IP27 is not set
+# CONFIG_SIBYTE_SB1xxx_SOC is not set
+# CONFIG_SNI_RM200_PCI is not set
+# CONFIG_TANBAC_TB0226 is not set
+# CONFIG_TANBAC_TB0229 is not set
+# CONFIG_TOSHIBA_JMR3927 is not set
+# CONFIG_TOSHIBA_RBTX4927 is not set
+# CONFIG_VICTOR_MPC30X is not set
+# CONFIG_ZAO_CAPCELLA is not set
+# CONFIG_HIGHMEM is not set
+CONFIG_RWSEM_GENERIC_SPINLOCK=y
+# CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
+CONFIG_SOC_AU1X00=y
+CONFIG_SOC_AU1200=y
+CONFIG_SOC_AU1X00_32KHZ=y
+CONFIG_SOC_AU1X00_32KHZ_TIMER=y
+CONFIG_NONCOHERENT_IO=y
+CONFIG_PC_KEYB=y
+# CONFIG_MIPS_AU1000 is not set
+
+#
+# CPU selection
+#
+CONFIG_CPU_MIPS32=y
+# CONFIG_CPU_MIPS64 is not set
+# CONFIG_CPU_R3000 is not set
+# CONFIG_CPU_TX39XX is not set
+# CONFIG_CPU_VR41XX is not set
+# CONFIG_CPU_R4300 is not set
+# CONFIG_CPU_R4X00 is not set
+# CONFIG_CPU_TX49XX is not set
+# CONFIG_CPU_R5000 is not set
+# CONFIG_CPU_R5432 is not set
+# CONFIG_CPU_R6000 is not set
+# CONFIG_CPU_NEVADA is not set
+# CONFIG_CPU_R8000 is not set
+# CONFIG_CPU_R10000 is not set
+# CONFIG_CPU_RM7000 is not set
+# CONFIG_CPU_RM9000 is not set
+# CONFIG_CPU_SB1 is not set
+CONFIG_PAGE_SIZE_4KB=y
+# CONFIG_PAGE_SIZE_16KB is not set
+# CONFIG_PAGE_SIZE_64KB is not set
+CONFIG_CPU_HAS_PREFETCH=y
+# CONFIG_VTAG_ICACHE is not set
+CONFIG_64BIT_PHYS_ADDR=y
+# CONFIG_CPU_ADVANCED is not set
+CONFIG_CPU_HAS_LLSC=y
+# CONFIG_CPU_HAS_LLDSCD is not set
+# CONFIG_CPU_HAS_WB is not set
+CONFIG_CPU_HAS_SYNC=y
+
+#
+# General setup
+#
+CONFIG_CPU_LITTLE_ENDIAN=y
+# CONFIG_BUILD_ELF64 is not set
+CONFIG_NET=y
+CONFIG_PCI=y
+CONFIG_PCI_NEW=y
+CONFIG_PCI_AUTO=y
+# CONFIG_PCI_NAMES is not set
+# CONFIG_ISA is not set
+# CONFIG_TC is not set
+# CONFIG_MCA is not set
+# CONFIG_SBUS is not set
+CONFIG_HOTPLUG=y
+
+#
+# PCMCIA/CardBus support
+#
+CONFIG_PCMCIA=m
+# CONFIG_CARDBUS is not set
+# CONFIG_TCIC is not set
+# CONFIG_I82092 is not set
+# CONFIG_I82365 is not set
+CONFIG_PCMCIA_AU1X00=m
+
+#
+# PCI Hotplug Support
+#
+# CONFIG_HOTPLUG_PCI is not set
+# CONFIG_HOTPLUG_PCI_COMPAQ is not set
+# CONFIG_HOTPLUG_PCI_COMPAQ_NVRAM is not set
+# CONFIG_HOTPLUG_PCI_SHPC is not set
+# CONFIG_HOTPLUG_PCI_SHPC_POLL_EVENT_MODE is not set
+# CONFIG_HOTPLUG_PCI_PCIE is not set
+# CONFIG_HOTPLUG_PCI_PCIE_POLL_EVENT_MODE is not set
+CONFIG_SYSVIPC=y
+# CONFIG_BSD_PROCESS_ACCT is not set
+CONFIG_SYSCTL=y
+CONFIG_KCORE_ELF=y
+# CONFIG_KCORE_AOUT is not set
+# CONFIG_BINFMT_AOUT is not set
+CONFIG_BINFMT_ELF=y
+# CONFIG_MIPS32_COMPAT is not set
+# CONFIG_MIPS32_O32 is not set
+# CONFIG_MIPS32_N32 is not set
+# CONFIG_BINFMT_ELF32 is not set
+# CONFIG_BINFMT_MISC is not set
+# CONFIG_OOM_KILLER is not set
+CONFIG_CMDLINE_BOOL=y
+CONFIG_CMDLINE="mem=96M"
+CONFIG_PM=y
+
+#
+# Memory Technology Devices (MTD)
+#
+CONFIG_MTD=y
+# CONFIG_MTD_DEBUG is not set
+CONFIG_MTD_PARTITIONS=y
+# CONFIG_MTD_CONCAT is not set
+# CONFIG_MTD_REDBOOT_PARTS is not set
+# CONFIG_MTD_CMDLINE_PARTS is not set
+CONFIG_MTD_CHAR=y
+CONFIG_MTD_BLOCK=y
+# CONFIG_FTL is not set
+# CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+CONFIG_MTD_CFI=y
+# CONFIG_MTD_JEDECPROBE is not set
+CONFIG_MTD_GEN_PROBE=y
+# CONFIG_MTD_CFI_ADV_OPTIONS is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+CONFIG_MTD_CFI_AMDSTD=y
+# CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
+# CONFIG_MTD_RAM is not set
+# CONFIG_MTD_ROM is not set
+# CONFIG_MTD_ABSENT is not set
+# CONFIG_MTD_OBSOLETE_CHIPS is not set
+# CONFIG_MTD_AMDSTD is not set
+# CONFIG_MTD_SHARP is not set
+# CONFIG_MTD_JEDEC is not set
+
+#
+# Mapping drivers for chip access
+#
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PHYSMAP is not set
+CONFIG_MTD_ALCHEMY=y
+# CONFIG_MTD_XXS1500 is not set
+# CONFIG_MTD_CSTM_MIPS_IXX is not set
+# CONFIG_MTD_OCELOT is not set
+# CONFIG_MTD_LASAT is not set
+# CONFIG_MTD_PCI is not set
+# CONFIG_MTD_PCMCIA is not set
+
+#
+# Self-contained MTD device drivers
+#
+# CONFIG_MTD_PMC551 is not set
+# CONFIG_MTD_SLRAM is not set
+# CONFIG_MTD_MTDRAM is not set
+# CONFIG_MTD_BLKMTD is not set
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_DOCPROBE is not set
+
+#
+# NAND Flash Device Drivers
+#
+CONFIG_MTD_NAND=y
+# CONFIG_MTD_NAND_VERIFY_WRITE is not set
+CONFIG_MTD_NAND_IDS=y
+CONFIG_MTD_NAND_AU1550=y
+
+#
+# Parallel port support
+#
+# CONFIG_PARPORT is not set
+
+#
+# Plug and Play configuration
+#
+# CONFIG_PNP is not set
+# CONFIG_ISAPNP is not set
+
+#
+# Block devices
+#
+# CONFIG_BLK_DEV_FD is not set
+# CONFIG_BLK_DEV_XD is not set
+# CONFIG_PARIDE is not set
+# CONFIG_BLK_CPQ_DA is not set
+# CONFIG_BLK_CPQ_CISS_DA is not set
+# CONFIG_CISS_SCSI_TAPE is not set
+# CONFIG_CISS_MONITOR_THREAD is not set
+# CONFIG_BLK_DEV_DAC960 is not set
+# CONFIG_BLK_DEV_UMEM is not set
+# CONFIG_BLK_DEV_SX8 is not set
+CONFIG_BLK_DEV_LOOP=y
+# CONFIG_BLK_DEV_NBD is not set
+# CONFIG_BLK_DEV_RAM is not set
+# CONFIG_BLK_DEV_INITRD is not set
+# CONFIG_BLK_STATS is not set
+
+#
+# Multi-device support (RAID and LVM)
+#
+# CONFIG_MD is not set
+# CONFIG_BLK_DEV_MD is not set
+# CONFIG_MD_LINEAR is not set
+# CONFIG_MD_RAID0 is not set
+# CONFIG_MD_RAID1 is not set
+# CONFIG_MD_RAID5 is not set
+# CONFIG_MD_MULTIPATH is not set
+# CONFIG_BLK_DEV_LVM is not set
+
+#
+# Networking options
+#
+CONFIG_PACKET=y
+# CONFIG_PACKET_MMAP is not set
+# CONFIG_NETLINK_DEV is not set
+CONFIG_NETFILTER=y
+# CONFIG_NETFILTER_DEBUG is not set
+CONFIG_FILTER=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+# CONFIG_IP_ADVANCED_ROUTER is not set
+CONFIG_IP_PNP=y
+# CONFIG_IP_PNP_DHCP is not set
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_IP_PNP_RARP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE is not set
+# CONFIG_IP_MROUTE is not set
+# CONFIG_ARPD is not set
+# CONFIG_INET_ECN is not set
+# CONFIG_SYN_COOKIES is not set
+
+#
+#   IP: Netfilter Configuration
+#
+# CONFIG_IP_NF_CONNTRACK is not set
+# CONFIG_IP_NF_QUEUE is not set
+# CONFIG_IP_NF_IPTABLES is not set
+# CONFIG_IP_NF_ARPTABLES is not set
+# CONFIG_IP_NF_COMPAT_IPCHAINS is not set
+# CONFIG_IP_NF_COMPAT_IPFWADM is not set
+
+#
+#   IP: Virtual Server Configuration
+#
+# CONFIG_IP_VS is not set
+# CONFIG_IPV6 is not set
+# CONFIG_KHTTPD is not set
+
+#
+#    SCTP Configuration (EXPERIMENTAL)
+#
+# CONFIG_IP_SCTP is not set
+# CONFIG_ATM is not set
+# CONFIG_VLAN_8021Q is not set
+# CONFIG_IPX is not set
+# CONFIG_ATALK is not set
+# CONFIG_DECNET is not set
+# CONFIG_BRIDGE is not set
+# CONFIG_X25 is not set
+# CONFIG_LAPB is not set
+# CONFIG_LLC is not set
+# CONFIG_NET_DIVERT is not set
+# CONFIG_ECONET is not set
+# CONFIG_WAN_ROUTER is not set
+# CONFIG_NET_FASTROUTE is not set
+# CONFIG_NET_HW_FLOWCONTROL is not set
+
+#
+# QoS and/or fair queueing
+#
+# CONFIG_NET_SCHED is not set
+
+#
+# Network testing
+#
+# CONFIG_NET_PKTGEN is not set
+
+#
+# Telephony Support
+#
+# CONFIG_PHONE is not set
+# CONFIG_PHONE_IXJ is not set
+# CONFIG_PHONE_IXJ_PCMCIA is not set
+
+#
+# ATA/IDE/MFM/RLL support
+#
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+# CONFIG_BLK_DEV_HD_IDE is not set
+# CONFIG_BLK_DEV_HD is not set
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+CONFIG_IDEDISK_MULTI_MODE=y
+CONFIG_IDEDISK_STROKE=y
+CONFIG_BLK_DEV_IDECS=m
+# CONFIG_BLK_DEV_DELKIN is not set
+CONFIG_BLK_DEV_IDECD=y
+# CONFIG_BLK_DEV_IDETAPE is not set
+# CONFIG_BLK_DEV_IDEFLOPPY is not set
+# CONFIG_BLK_DEV_IDESCSI is not set
+CONFIG_IDE_TASK_IOCTL=y
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+CONFIG_BLK_DEV_IDEPCI=y
+# CONFIG_BLK_DEV_GENERIC is not set
+# CONFIG_IDEPCI_SHARE_IRQ is not set
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+# CONFIG_BLK_DEV_IDEDMA_FORCED is not set
+# CONFIG_IDEDMA_PCI_AUTO is not set
+# CONFIG_IDEDMA_ONLYDISK is not set
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_BLK_DEV_ADMA100 is not set
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_WDC_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_AMD74XX_OVERRIDE is not set
+# CONFIG_BLK_DEV_ATIIXP is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_TRIFLEX is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+# CONFIG_BLK_DEV_PIIX is not set
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_SC1200 is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+CONFIG_BLK_DEV_IDE_AU1XXX=y
+# CONFIG_BLK_DEV_IDE_PIO_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX is not set
+CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX=y
+CONFIG_BLK_DEV_IDE_SEQTS_PER_RQ_AU1XXX=128
+# CONFIG_IDE_CHIPSETS is not set
+# CONFIG_IDEDMA_AUTO is not set
+# CONFIG_IDEDMA_IVB is not set
+# CONFIG_DMA_NONPCI is not set
+# CONFIG_BLK_DEV_ATARAID is not set
+# CONFIG_BLK_DEV_ATARAID_PDC is not set
+# CONFIG_BLK_DEV_ATARAID_HPT is not set
+# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set
+# CONFIG_BLK_DEV_ATARAID_SII is not set
+
+#
+# SCSI support
+#
+CONFIG_SCSI=y
+CONFIG_BLK_DEV_SD=y
+CONFIG_SD_EXTRA_DEVS=40
+CONFIG_CHR_DEV_ST=y
+# CONFIG_CHR_DEV_OSST is not set
+CONFIG_BLK_DEV_SR=y
+# CONFIG_BLK_DEV_SR_VENDOR is not set
+CONFIG_SR_EXTRA_DEVS=2
+# CONFIG_CHR_DEV_SG is not set
+# CONFIG_SCSI_DEBUG_QUEUES is not set
+# CONFIG_SCSI_MULTI_LUN is not set
+CONFIG_SCSI_CONSTANTS=y
+# CONFIG_SCSI_LOGGING is not set
+
+#
+# SCSI low-level drivers
+#
+# CONFIG_BLK_DEV_3W_XXXX_RAID is not set
+# CONFIG_SCSI_7000FASST is not set
+# CONFIG_SCSI_ACARD is not set
+# CONFIG_SCSI_AHA152X is not set
+# CONFIG_SCSI_AHA1542 is not set
+# CONFIG_SCSI_AHA1740 is not set
+# CONFIG_SCSI_AACRAID is not set
+# CONFIG_SCSI_AIC7XXX is not set
+# CONFIG_SCSI_AIC79XX is not set
+# CONFIG_SCSI_AIC7XXX_OLD is not set
+# CONFIG_SCSI_DPT_I2O is not set
+# CONFIG_SCSI_ADVANSYS is not set
+# CONFIG_SCSI_IN2000 is not set
+# CONFIG_SCSI_AM53C974 is not set
+# CONFIG_SCSI_MEGARAID is not set
+# CONFIG_SCSI_MEGARAID2 is not set
+# CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
+# CONFIG_SCSI_SATA_SVW is not set
+# CONFIG_SCSI_ATA_PIIX is not set
+# CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
+# CONFIG_SCSI_SATA_PROMISE is not set
+# CONFIG_SCSI_SATA_SX4 is not set
+# CONFIG_SCSI_SATA_SIL is not set
+# CONFIG_SCSI_SATA_SIS is not set
+# CONFIG_SCSI_SATA_ULI is not set
+# CONFIG_SCSI_SATA_VIA is not set
+# CONFIG_SCSI_SATA_VITESSE is not set
+# CONFIG_SCSI_BUSLOGIC is not set
+# CONFIG_SCSI_CPQFCTS is not set
+# CONFIG_SCSI_DMX3191D is not set
+# CONFIG_SCSI_DTC3280 is not set
+# CONFIG_SCSI_EATA is not set
+# CONFIG_SCSI_EATA_DMA is not set
+# CONFIG_SCSI_EATA_PIO is not set
+# CONFIG_SCSI_FUTURE_DOMAIN is not set
+# CONFIG_SCSI_GDTH is not set
+# CONFIG_SCSI_GENERIC_NCR5380 is not set
+# CONFIG_SCSI_INITIO is not set
+# CONFIG_SCSI_INIA100 is not set
+# CONFIG_SCSI_NCR53C406A is not set
+# CONFIG_SCSI_NCR53C7xx is not set
+# CONFIG_SCSI_SYM53C8XX_2 is not set
+# CONFIG_SCSI_NCR53C8XX is not set
+# CONFIG_SCSI_SYM53C8XX is not set
+# CONFIG_SCSI_PAS16 is not set
+# CONFIG_SCSI_PCI2000 is not set
+# CONFIG_SCSI_PCI2220I is not set
+# CONFIG_SCSI_PSI240I is not set
+# CONFIG_SCSI_QLOGIC_FAS is not set
+# CONFIG_SCSI_QLOGIC_ISP is not set
+# CONFIG_SCSI_QLOGIC_FC is not set
+# CONFIG_SCSI_QLOGIC_1280 is not set
+# CONFIG_SCSI_SIM710 is not set
+# CONFIG_SCSI_SYM53C416 is not set
+# CONFIG_SCSI_DC390T is not set
+# CONFIG_SCSI_T128 is not set
+# CONFIG_SCSI_U14_34F is not set
+# CONFIG_SCSI_NSP32 is not set
+# CONFIG_SCSI_DEBUG is not set
+
+#
+# PCMCIA SCSI adapter support
+#
+# CONFIG_SCSI_PCMCIA is not set
+
+#
+# Fusion MPT device support
+#
+# CONFIG_FUSION is not set
+# CONFIG_FUSION_BOOT is not set
+# CONFIG_FUSION_ISENSE is not set
+# CONFIG_FUSION_CTL is not set
+# CONFIG_FUSION_LAN is not set
+
+#
+# IEEE 1394 (FireWire) support (EXPERIMENTAL)
+#
+# CONFIG_IEEE1394 is not set
+
+#
+# I2O device support
+#
+# CONFIG_I2O is not set
+# CONFIG_I2O_PCI is not set
+# CONFIG_I2O_BLOCK is not set
+# CONFIG_I2O_LAN is not set
+# CONFIG_I2O_SCSI is not set
+# CONFIG_I2O_PROC is not set
+
+#
+# Network device support
+#
+CONFIG_NETDEVICES=y
+
+#
+# ARCnet devices
+#
+# CONFIG_ARCNET is not set
+# CONFIG_DUMMY is not set
+# CONFIG_BONDING is not set
+# CONFIG_EQUALIZER is not set
+# CONFIG_TUN is not set
+# CONFIG_ETHERTAP is not set
+
+#
+# Ethernet (10 or 100Mbit)
+#
+CONFIG_NET_ETHERNET=y
+# CONFIG_MIPS_AU1X00_ENET is not set
+CONFIG_AU1XXX_SMC91111=y
+# CONFIG_SUNLANCE is not set
+# CONFIG_HAPPYMEAL is not set
+# CONFIG_SUNBMAC is not set
+# CONFIG_SUNQE is not set
+# CONFIG_SUNGEM is not set
+# CONFIG_NET_VENDOR_3COM is not set
+# CONFIG_LANCE is not set
+# CONFIG_NET_VENDOR_SMC is not set
+# CONFIG_NET_VENDOR_RACAL is not set
+# CONFIG_HP100 is not set
+# CONFIG_NET_ISA is not set
+# CONFIG_NET_PCI is not set
+# CONFIG_NET_POCKET is not set
+
+#
+# Ethernet (1000 Mbit)
+#
+# CONFIG_ACENIC is not set
+# CONFIG_DL2K is not set
+# CONFIG_E1000 is not set
+# CONFIG_MYRI_SBUS is not set
+# CONFIG_NS83820 is not set
+# CONFIG_HAMACHI is not set
+# CONFIG_YELLOWFIN is not set
+# CONFIG_R8169 is not set
+# CONFIG_SK98LIN is not set
+# CONFIG_TIGON3 is not set
+# CONFIG_FDDI is not set
+# CONFIG_HIPPI is not set
+# CONFIG_PLIP is not set
+CONFIG_PPP=m
+CONFIG_PPP_MULTILINK=y
+# CONFIG_PPP_FILTER is not set
+CONFIG_PPP_ASYNC=m
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=m
+# CONFIG_PPP_BSDCOMP is not set
+CONFIG_PPPOE=m
+# CONFIG_SLIP is not set
+
+#
+# Wireless LAN (non-hamradio)
+#
+# CONFIG_NET_RADIO is not set
+
+#
+# Token Ring devices
+#
+# CONFIG_TR is not set
+# CONFIG_NET_FC is not set
+# CONFIG_RCPCI is not set
+# CONFIG_SHAPER is not set
+
+#
+# Wan interfaces
+#
+# CONFIG_WAN is not set
+
+#
+# PCMCIA network device support
+#
+# CONFIG_NET_PCMCIA is not set
+
+#
+# Amateur Radio support
+#
+# CONFIG_HAMRADIO is not set
+
+#
+# IrDA (infrared) support
+#
+# CONFIG_IRDA is not set
+
+#
+# ISDN subsystem
+#
+# CONFIG_ISDN is not set
+
+#
+# Input core support
+#
+CONFIG_INPUT=y
+CONFIG_INPUT_KEYBDEV=y
+CONFIG_INPUT_MOUSEDEV=y
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=1024
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=768
+# CONFIG_INPUT_JOYDEV is not set
+CONFIG_INPUT_EVDEV=y
+# CONFIG_INPUT_UINPUT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+# CONFIG_VT_CONSOLE is not set
+# CONFIG_SERIAL is not set
+# CONFIG_SERIAL_EXTENDED is not set
+CONFIG_SERIAL_NONSTANDARD=y
+# CONFIG_COMPUTONE is not set
+# CONFIG_ROCKETPORT is not set
+# CONFIG_CYCLADES is not set
+# CONFIG_DIGIEPCA is not set
+# CONFIG_DIGI is not set
+# CONFIG_ESPSERIAL is not set
+# CONFIG_MOXA_INTELLIO is not set
+# CONFIG_MOXA_SMARTIO is not set
+# CONFIG_ISI is not set
+# CONFIG_SYNCLINK is not set
+# CONFIG_SYNCLINKMP is not set
+# CONFIG_N_HDLC is not set
+# CONFIG_RISCOM8 is not set
+# CONFIG_SPECIALIX is not set
+# CONFIG_SX is not set
+# CONFIG_RIO is not set
+# CONFIG_STALDRV is not set
+# CONFIG_SERIAL_TX3912 is not set
+# CONFIG_SERIAL_TX3912_CONSOLE is not set
+# CONFIG_SERIAL_TXX9 is not set
+# CONFIG_SERIAL_TXX9_CONSOLE is not set
+CONFIG_AU1X00_UART=y
+CONFIG_AU1X00_SERIAL_CONSOLE=y
+# CONFIG_AU1X00_USB_TTY is not set
+# CONFIG_AU1X00_USB_RAW is not set
+# CONFIG_TXX927_SERIAL is not set
+CONFIG_UNIX98_PTYS=y
+CONFIG_UNIX98_PTY_COUNT=256
+
+#
+# I2C support
+#
+# CONFIG_I2C is not set
+
+#
+# Mice
+#
+# CONFIG_BUSMOUSE is not set
+# CONFIG_MOUSE is not set
+
+#
+# Joysticks
+#
+# CONFIG_INPUT_GAMEPORT is not set
+# CONFIG_INPUT_NS558 is not set
+# CONFIG_INPUT_LIGHTNING is not set
+# CONFIG_INPUT_PCIGAME is not set
+# CONFIG_INPUT_CS461X is not set
+# CONFIG_INPUT_EMU10K1 is not set
+# CONFIG_INPUT_SERIO is not set
+# CONFIG_INPUT_SERPORT is not set
+# CONFIG_INPUT_ANALOG is not set
+# CONFIG_INPUT_A3D is not set
+# CONFIG_INPUT_ADI is not set
+# CONFIG_INPUT_COBRA is not set
+# CONFIG_INPUT_GF2K is not set
+# CONFIG_INPUT_GRIP is not set
+# CONFIG_INPUT_INTERACT is not set
+# CONFIG_INPUT_TMDC is not set
+# CONFIG_INPUT_SIDEWINDER is not set
+# CONFIG_INPUT_IFORCE_USB is not set
+# CONFIG_INPUT_IFORCE_232 is not set
+# CONFIG_INPUT_WARRIOR is not set
+# CONFIG_INPUT_MAGELLAN is not set
+# CONFIG_INPUT_SPACEORB is not set
+# CONFIG_INPUT_SPACEBALL is not set
+# CONFIG_INPUT_STINGER is not set
+# CONFIG_INPUT_DB9 is not set
+# CONFIG_INPUT_GAMECON is not set
+# CONFIG_INPUT_TURBOGRAFX is not set
+# CONFIG_QIC02_TAPE is not set
+# CONFIG_IPMI_HANDLER is not set
+# CONFIG_IPMI_PANIC_EVENT is not set
+# CONFIG_IPMI_DEVICE_INTERFACE is not set
+# CONFIG_IPMI_KCS is not set
+# CONFIG_IPMI_WATCHDOG is not set
+
+#
+# Watchdog Cards
+#
+# CONFIG_WATCHDOG is not set
+# CONFIG_SCx200 is not set
+# CONFIG_SCx200_GPIO is not set
+# CONFIG_AMD_PM768 is not set
+# CONFIG_NVRAM is not set
+# CONFIG_RTC is not set
+# CONFIG_DTLK is not set
+# CONFIG_R3964 is not set
+# CONFIG_APPLICOM is not set
+
+#
+# Ftape, the floppy tape device driver
+#
+# CONFIG_FTAPE is not set
+# CONFIG_AGP is not set
+
+#
+# Direct Rendering Manager (XFree86 DRI support)
+#
+# CONFIG_DRM is not set
+
+#
+# PCMCIA character devices
+#
+# CONFIG_PCMCIA_SERIAL_CS is not set
+# CONFIG_SYNCLINK_CS is not set
+# CONFIG_AU1X00_GPIO is not set
+# CONFIG_TS_AU1X00_ADS7846 is not set
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_AUTOFS_FS=y
+# CONFIG_AUTOFS4_FS is not set
+# CONFIG_REISERFS_FS is not set
+# CONFIG_REISERFS_CHECK is not set
+# CONFIG_REISERFS_PROC_INFO is not set
+# CONFIG_ADFS_FS is not set
+# CONFIG_ADFS_FS_RW is not set
+# CONFIG_AFFS_FS is not set
+# CONFIG_HFS_FS is not set
+# CONFIG_HFSPLUS_FS is not set
+# CONFIG_BEFS_FS is not set
+# CONFIG_BEFS_DEBUG is not set
+# CONFIG_BFS_FS is not set
+CONFIG_EXT3_FS=y
+CONFIG_JBD=y
+# CONFIG_JBD_DEBUG is not set
+CONFIG_FAT_FS=y
+CONFIG_MSDOS_FS=y
+# CONFIG_UMSDOS_FS is not set
+CONFIG_VFAT_FS=y
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+CONFIG_JFFS2_FS=y
+CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS2_FS_NAND is not set
+# CONFIG_CRAMFS is not set
+CONFIG_TMPFS=y
+CONFIG_RAMFS=y
+CONFIG_ISO9660_FS=y
+CONFIG_JOLIET=y
+# CONFIG_ZISOFS is not set
+# CONFIG_JFS_FS is not set
+# CONFIG_JFS_DEBUG is not set
+# CONFIG_JFS_STATISTICS is not set
+# CONFIG_MINIX_FS is not set
+# CONFIG_VXFS_FS is not set
+# CONFIG_NTFS_FS is not set
+# CONFIG_NTFS_RW is not set
+# CONFIG_HPFS_FS is not set
+CONFIG_PROC_FS=y
+# CONFIG_DEVFS_FS is not set
+# CONFIG_DEVFS_MOUNT is not set
+# CONFIG_DEVFS_DEBUG is not set
+CONFIG_DEVPTS_FS=y
+# CONFIG_QNX4FS_FS is not set
+# CONFIG_QNX4FS_RW is not set
+# CONFIG_ROMFS_FS is not set
+CONFIG_EXT2_FS=y
+# CONFIG_SYSV_FS is not set
+# CONFIG_UDF_FS is not set
+# CONFIG_UDF_RW is not set
+# CONFIG_UFS_FS is not set
+# CONFIG_UFS_FS_WRITE is not set
+# CONFIG_XFS_FS is not set
+# CONFIG_XFS_QUOTA is not set
+# CONFIG_XFS_RT is not set
+# CONFIG_XFS_TRACE is not set
+# CONFIG_XFS_DEBUG is not set
+
+#
+# Network File Systems
+#
+# CONFIG_CODA_FS is not set
+# CONFIG_INTERMEZZO_FS is not set
+CONFIG_NFS_FS=y
+CONFIG_NFS_V3=y
+# CONFIG_NFS_DIRECTIO is not set
+CONFIG_ROOT_NFS=y
+# CONFIG_NFSD is not set
+# CONFIG_NFSD_V3 is not set
+# CONFIG_NFSD_TCP is not set
+CONFIG_SUNRPC=y
+CONFIG_LOCKD=y
+CONFIG_LOCKD_V4=y
+# CONFIG_SMB_FS is not set
+# CONFIG_NCP_FS is not set
+# CONFIG_NCPFS_PACKET_SIGNING is not set
+# CONFIG_NCPFS_IOCTL_LOCKING is not set
+# CONFIG_NCPFS_STRONG is not set
+# CONFIG_NCPFS_NFS_NS is not set
+# CONFIG_NCPFS_OS2_NS is not set
+# CONFIG_NCPFS_SMALLDOS is not set
+# CONFIG_NCPFS_NLS is not set
+# CONFIG_NCPFS_EXTRAS is not set
+# CONFIG_ZISOFS_FS is not set
+
+#
+# Partition Types
+#
+# CONFIG_PARTITION_ADVANCED is not set
+CONFIG_MSDOS_PARTITION=y
+# CONFIG_SMB_NLS is not set
+CONFIG_NLS=y
+
+#
+# Native Language Support
+#
+CONFIG_NLS_DEFAULT="iso8859-1"
+# CONFIG_NLS_CODEPAGE_437 is not set
+# CONFIG_NLS_CODEPAGE_737 is not set
+# CONFIG_NLS_CODEPAGE_775 is not set
+# CONFIG_NLS_CODEPAGE_850 is not set
+# CONFIG_NLS_CODEPAGE_852 is not set
+# CONFIG_NLS_CODEPAGE_855 is not set
+# CONFIG_NLS_CODEPAGE_857 is not set
+# CONFIG_NLS_CODEPAGE_860 is not set
+# CONFIG_NLS_CODEPAGE_861 is not set
+# CONFIG_NLS_CODEPAGE_862 is not set
+# CONFIG_NLS_CODEPAGE_863 is not set
+# CONFIG_NLS_CODEPAGE_864 is not set
+# CONFIG_NLS_CODEPAGE_865 is not set
+# CONFIG_NLS_CODEPAGE_866 is not set
+# CONFIG_NLS_CODEPAGE_869 is not set
+# CONFIG_NLS_CODEPAGE_936 is not set
+# CONFIG_NLS_CODEPAGE_950 is not set
+# CONFIG_NLS_CODEPAGE_932 is not set
+# CONFIG_NLS_CODEPAGE_949 is not set
+# CONFIG_NLS_CODEPAGE_874 is not set
+# CONFIG_NLS_ISO8859_8 is not set
+# CONFIG_NLS_CODEPAGE_1250 is not set
+# CONFIG_NLS_CODEPAGE_1251 is not set
+# CONFIG_NLS_ISO8859_1 is not set
+# CONFIG_NLS_ISO8859_2 is not set
+# CONFIG_NLS_ISO8859_3 is not set
+# CONFIG_NLS_ISO8859_4 is not set
+# CONFIG_NLS_ISO8859_5 is not set
+# CONFIG_NLS_ISO8859_6 is not set
+# CONFIG_NLS_ISO8859_7 is not set
+# CONFIG_NLS_ISO8859_9 is not set
+# CONFIG_NLS_ISO8859_13 is not set
+# CONFIG_NLS_ISO8859_14 is not set
+# CONFIG_NLS_ISO8859_15 is not set
+# CONFIG_NLS_KOI8_R is not set
+# CONFIG_NLS_KOI8_U is not set
+# CONFIG_NLS_UTF8 is not set
+
+#
+# Multimedia devices
+#
+# CONFIG_VIDEO_DEV is not set
+
+#
+# Console drivers
+#
+# CONFIG_VGA_CONSOLE is not set
+# CONFIG_MDA_CONSOLE is not set
+
+#
+# Frame-buffer support
+#
+CONFIG_FB=y
+CONFIG_DUMMY_CONSOLE=y
+# CONFIG_FB_RIVA is not set
+# CONFIG_FB_CLGEN is not set
+# CONFIG_FB_PM2 is not set
+# CONFIG_FB_PM3 is not set
+# CONFIG_FB_CYBER2000 is not set
+CONFIG_FB_AU1200=y
+CONFIG_FB_AU1200_DEVS=4
+# CONFIG_FB_MATROX is not set
+# CONFIG_FB_ATY is not set
+# CONFIG_FB_RADEON is not set
+# CONFIG_FB_ATY128 is not set
+# CONFIG_FB_INTEL is not set
+# CONFIG_FB_SIS is not set
+# CONFIG_FB_SMI501 is not set
+# CONFIG_FB_NEOMAGIC is not set
+# CONFIG_FB_3DFX is not set
+# CONFIG_FB_VOODOO1 is not set
+# CONFIG_FB_TRIDENT is not set
+# CONFIG_FB_E1356 is not set
+# CONFIG_FB_IT8181 is not set
+# CONFIG_FB_VIRTUAL is not set
+CONFIG_FBCON_ADVANCED=y
+# CONFIG_FBCON_MFB is not set
+# CONFIG_FBCON_CFB2 is not set
+# CONFIG_FBCON_CFB4 is not set
+# CONFIG_FBCON_CFB8 is not set
+CONFIG_FBCON_CFB16=y
+# CONFIG_FBCON_CFB24 is not set
+CONFIG_FBCON_CFB32=y
+# CONFIG_FBCON_AFB is not set
+# CONFIG_FBCON_ILBM is not set
+# CONFIG_FBCON_IPLAN2P2 is not set
+# CONFIG_FBCON_IPLAN2P4 is not set
+# CONFIG_FBCON_IPLAN2P8 is not set
+# CONFIG_FBCON_MAC is not set
+# CONFIG_FBCON_VGA_PLANES is not set
+# CONFIG_FBCON_VGA is not set
+# CONFIG_FBCON_HGA is not set
+# CONFIG_FBCON_FONTWIDTH8_ONLY is not set
+CONFIG_FBCON_FONTS=y
+CONFIG_FONT_8x8=y
+CONFIG_FONT_8x16=y
+# CONFIG_FONT_SUN8x16 is not set
+# CONFIG_FONT_SUN12x22 is not set
+# CONFIG_FONT_6x11 is not set
+# CONFIG_FONT_PEARL_8x8 is not set
+# CONFIG_FONT_ACORN_8x8 is not set
+
+#
+# Sound
+#
+CONFIG_SOUND=y
+# CONFIG_SOUND_ALI5455 is not set
+# CONFIG_SOUND_BT878 is not set
+# CONFIG_SOUND_CMPCI is not set
+# CONFIG_SOUND_EMU10K1 is not set
+# CONFIG_MIDI_EMU10K1 is not set
+# CONFIG_SOUND_FUSION is not set
+# CONFIG_SOUND_CS4281 is not set
+# CONFIG_SOUND_ES1370 is not set
+# CONFIG_SOUND_ES1371 is not set
+# CONFIG_SOUND_ESSSOLO1 is not set
+# CONFIG_SOUND_MAESTRO is not set
+# CONFIG_SOUND_MAESTRO3 is not set
+# CONFIG_SOUND_FORTE is not set
+# CONFIG_SOUND_ICH is not set
+# CONFIG_SOUND_RME96XX is not set
+# CONFIG_SOUND_SONICVIBES is not set
+CONFIG_SOUND_AU1550_PSC=y
+# CONFIG_SOUND_AU1550_I2S is not set
+# CONFIG_SOUND_TRIDENT is not set
+# CONFIG_SOUND_MSNDCLAS is not set
+# CONFIG_SOUND_MSNDPIN is not set
+# CONFIG_SOUND_VIA82CXXX is not set
+# CONFIG_MIDI_VIA82CXXX is not set
+# CONFIG_SOUND_OSS is not set
+# CONFIG_SOUND_TVMIXER is not set
+# CONFIG_SOUND_AD1980 is not set
+# CONFIG_SOUND_WM97XX is not set
+
+#
+# USB support
+#
+CONFIG_USB=y
+# CONFIG_USB_DEBUG is not set
+CONFIG_USB_DEVICEFS=y
+# CONFIG_USB_BANDWIDTH is not set
+CONFIG_USB_EHCI_HCD=y
+CONFIG_USB_NON_PCI_EHCI=y
+# CONFIG_USB_UHCI is not set
+# CONFIG_USB_UHCI_ALT is not set
+CONFIG_USB_OHCI=y
+CONFIG_USB_NON_PCI_OHCI=y
+# CONFIG_USB_AUDIO is not set
+# CONFIG_USB_EMI26 is not set
+# CONFIG_USB_BLUETOOTH is not set
+# CONFIG_USB_MIDI is not set
+CONFIG_USB_STORAGE=y
+# CONFIG_USB_STORAGE_DEBUG is not set
+# CONFIG_USB_STORAGE_DATAFAB is not set
+# CONFIG_USB_STORAGE_FREECOM is not set
+# CONFIG_USB_STORAGE_ISD200 is not set
+# CONFIG_USB_STORAGE_DPCM is not set
+# CONFIG_USB_STORAGE_HP8200e is not set
+# CONFIG_USB_STORAGE_SDDR09 is not set
+# CONFIG_USB_STORAGE_SDDR55 is not set
+# CONFIG_USB_STORAGE_JUMPSHOT is not set
+# CONFIG_USB_ACM is not set
+# CONFIG_USB_PRINTER is not set
+CONFIG_USB_HID=y
+CONFIG_USB_HIDINPUT=y
+CONFIG_USB_HIDDEV=y
+# CONFIG_USB_AIPTEK is not set
+# CONFIG_USB_WACOM is not set
+# CONFIG_USB_KBTAB is not set
+# CONFIG_USB_POWERMATE is not set
+# CONFIG_USB_DC2XX is not set
+# CONFIG_USB_MDC800 is not set
+# CONFIG_USB_SCANNER is not set
+# CONFIG_USB_MICROTEK is not set
+# CONFIG_USB_HPUSBSCSI is not set
+# CONFIG_USB_PEGASUS is not set
+# CONFIG_USB_RTL8150 is not set
+# CONFIG_USB_KAWETH is not set
+# CONFIG_USB_CATC is not set
+# CONFIG_USB_CDCETHER is not set
+# CONFIG_USB_USBNET is not set
+# CONFIG_USB_USS720 is not set
+
+#
+# USB Serial Converter support
+#
+# CONFIG_USB_SERIAL is not set
+# CONFIG_USB_RIO500 is not set
+# CONFIG_USB_AUERSWALD is not set
+# CONFIG_USB_TIGL is not set
+# CONFIG_USB_BRLVGER is not set
+# CONFIG_USB_LCD is not set
+
+#
+# Support for USB gadgets
+#
+# CONFIG_USB_GADGET is not set
+
+#
+# Bluetooth support
+#
+# CONFIG_BLUEZ is not set
+
+#
+# MMC/SD Card support
+#
+CONFIG_MMC=y
+# CONFIG_MMC_DEBUG is not set
+CONFIG_MMC_AU1100=y
+
+#
+# Kernel hacking
+#
+CONFIG_CROSSCOMPILE=y
+# CONFIG_RUNTIME_DEBUG is not set
+# CONFIG_KGDB is not set
+# CONFIG_GDB_CONSOLE is not set
+# CONFIG_DEBUG_INFO is not set
+# CONFIG_MAGIC_SYSRQ is not set
+# CONFIG_MIPS_UNCACHED is not set
+CONFIG_LOG_BUF_SHIFT=0
+
+#
+# Cryptographic options
+#
+# CONFIG_CRYPTO is not set
+
+#
+# Library routines
+#
+# CONFIG_CRC32 is not set
+CONFIG_ZLIB_INFLATE=y
+CONFIG_ZLIB_DEFLATE=y
+# CONFIG_FW_LOADER is not set
diff -Nur linux-2.4.30/arch/mips/defconfig-pb1500 linux-2.4.30-linux-mips.org/arch/mips/defconfig-pb1500
--- linux-2.4.30/arch/mips/defconfig-pb1500	2005-01-19 15:09:28.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-pb1500	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 #
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
 #
 CONFIG_MIPS=y
 CONFIG_MIPS32=y
@@ -22,16 +22,19 @@
 #
 # CONFIG_ACER_PICA_61 is not set
 # CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_FICMMP is not set
 # CONFIG_MIPS_MIRAGE is not set
 # CONFIG_MIPS_DB1000 is not set
 # CONFIG_MIPS_DB1100 is not set
 # CONFIG_MIPS_DB1500 is not set
 # CONFIG_MIPS_DB1550 is not set
+# CONFIG_MIPS_DB1200 is not set
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 CONFIG_MIPS_PB1500=y
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_PB1200 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -79,6 +82,7 @@
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
 CONFIG_SOC_AU1X00=y
 CONFIG_SOC_AU1500=y
+CONFIG_SOC_AU1X00_32KHZ=y
 CONFIG_NONCOHERENT_IO=y
 CONFIG_PC_KEYB=y
 # CONFIG_MIPS_AU1000 is not set
@@ -176,14 +180,11 @@
 # CONFIG_MTD_CONCAT is not set
 # CONFIG_MTD_REDBOOT_PARTS is not set
 # CONFIG_MTD_CMDLINE_PARTS is not set
-
-#
-# User Modules And Translation Layers
-#
 CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 # CONFIG_FTL is not set
 # CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
 
 #
 # RAM/ROM/Flash chip drivers
@@ -195,6 +196,7 @@
 CONFIG_MTD_CFI_INTELEXT=y
 # CONFIG_MTD_CFI_AMDSTD is not set
 # CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
 # CONFIG_MTD_RAM is not set
 # CONFIG_MTD_ROM is not set
 # CONFIG_MTD_ABSENT is not set
@@ -206,19 +208,10 @@
 #
 # Mapping drivers for chip access
 #
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
 # CONFIG_MTD_PHYSMAP is not set
-# CONFIG_MTD_PB1000 is not set
-CONFIG_MTD_PB1500=y
-# CONFIG_MTD_PB1100 is not set
-# CONFIG_MTD_BOSPORUS is not set
+CONFIG_MTD_ALCHEMY=y
 # CONFIG_MTD_XXS1500 is not set
-# CONFIG_MTD_MTX1 is not set
-CONFIG_MTD_PB1500_BOOT=y
-# CONFIG_MTD_PB1500_USER is not set
-# CONFIG_MTD_DB1X00 is not set
-# CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
-# CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
 # CONFIG_MTD_LASAT is not set
@@ -232,13 +225,9 @@
 # CONFIG_MTD_SLRAM is not set
 # CONFIG_MTD_MTDRAM is not set
 # CONFIG_MTD_BLKMTD is not set
-
-#
-# Disk-On-Chip Device Drivers
-#
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
 # CONFIG_MTD_DOCPROBE is not set
 
 #
@@ -335,17 +324,8 @@
 # CONFIG_IP_SCTP is not set
 # CONFIG_ATM is not set
 # CONFIG_VLAN_8021Q is not set
-
-#
-#  
-#
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -383,10 +363,6 @@
 # IDE, ATA and ATAPI Block devices
 #
 CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
 # CONFIG_BLK_DEV_HD_IDE is not set
 # CONFIG_BLK_DEV_HD is not set
 # CONFIG_BLK_DEV_IDE_SATA is not set
@@ -400,10 +376,6 @@
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_IDE_TASK_IOCTL is not set
-
-#
-# IDE chipset support/bugfixes
-#
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
@@ -445,6 +417,10 @@
 # CONFIG_BLK_DEV_SLC90E66 is not set
 # CONFIG_BLK_DEV_TRM290 is not set
 # CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_BLK_DEV_IDE_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX is not set
 # CONFIG_IDE_CHIPSETS is not set
 CONFIG_IDEDMA_AUTO=y
 # CONFIG_IDEDMA_IVB is not set
@@ -505,6 +481,7 @@
 CONFIG_NET_ETHERNET=y
 CONFIG_MIPS_AU1X00_ENET=y
 # CONFIG_BCM5222_DUAL_PHY is not set
+# CONFIG_AU1XXX_SMC91111 is not set
 # CONFIG_SUNLANCE is not set
 # CONFIG_HAPPYMEAL is not set
 # CONFIG_SUNBMAC is not set
@@ -675,7 +652,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
@@ -701,10 +677,6 @@
 # CONFIG_INPUT_EMU10K1 is not set
 # CONFIG_INPUT_SERIO is not set
 # CONFIG_INPUT_SERPORT is not set
-
-#
-# Joysticks
-#
 # CONFIG_INPUT_ANALOG is not set
 # CONFIG_INPUT_A3D is not set
 # CONFIG_INPUT_ADI is not set
@@ -789,11 +761,8 @@
 # CONFIG_UMSDOS_FS is not set
 # CONFIG_VFAT_FS is not set
 # CONFIG_EFS_FS is not set
-CONFIG_JFFS_FS=m
-CONFIG_JFFS_FS_VERBOSE=0
-CONFIG_JFFS_PROC_FS=y
-CONFIG_JFFS2_FS=m
-CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS_FS is not set
+# CONFIG_JFFS2_FS is not set
 CONFIG_CRAMFS=m
 CONFIG_TMPFS=y
 CONFIG_RAMFS=y
@@ -933,6 +902,7 @@
 # CONFIG_FB_ATY128 is not set
 # CONFIG_FB_INTEL is not set
 # CONFIG_FB_SIS is not set
+CONFIG_FB_SMI501=y
 # CONFIG_FB_NEOMAGIC is not set
 # CONFIG_FB_3DFX is not set
 # CONFIG_FB_VOODOO1 is not set
@@ -985,8 +955,6 @@
 # CONFIG_SOUND_RME96XX is not set
 # CONFIG_SOUND_SONICVIBES is not set
 CONFIG_SOUND_AU1X00=y
-# CONFIG_SOUND_AU1550_PSC is not set
-# CONFIG_SOUND_AU1550_I2S is not set
 # CONFIG_SOUND_TRIDENT is not set
 # CONFIG_SOUND_MSNDCLAS is not set
 # CONFIG_SOUND_MSNDPIN is not set
@@ -1002,32 +970,18 @@
 #
 CONFIG_USB=y
 # CONFIG_USB_DEBUG is not set
-
-#
-# Miscellaneous USB options
-#
 # CONFIG_USB_DEVICEFS is not set
 # CONFIG_USB_BANDWIDTH is not set
-
-#
-# USB Host Controller Drivers
-#
 # CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_NON_PCI_EHCI is not set
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=y
-
-#
-# USB Device Class drivers
-#
+CONFIG_USB_NON_PCI_OHCI=y
 # CONFIG_USB_AUDIO is not set
 # CONFIG_USB_EMI26 is not set
 # CONFIG_USB_BLUETOOTH is not set
 # CONFIG_USB_MIDI is not set
-
-#
-#   SCSI support is needed for USB Storage
-#
 # CONFIG_USB_STORAGE is not set
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DATAFAB is not set
@@ -1040,10 +994,6 @@
 # CONFIG_USB_STORAGE_JUMPSHOT is not set
 # CONFIG_USB_ACM is not set
 # CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
 CONFIG_USB_HID=y
 CONFIG_USB_HIDINPUT=y
 # CONFIG_USB_HIDDEV is not set
@@ -1051,47 +1001,23 @@
 # CONFIG_USB_WACOM is not set
 # CONFIG_USB_KBTAB is not set
 # CONFIG_USB_POWERMATE is not set
-
-#
-# USB Imaging devices
-#
 # CONFIG_USB_DC2XX is not set
 # CONFIG_USB_MDC800 is not set
 # CONFIG_USB_SCANNER is not set
 # CONFIG_USB_MICROTEK is not set
 # CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-
-#
-#   Video4Linux support is needed for USB Multimedia device support
-#
-
-#
-# USB Network adaptors
-#
 # CONFIG_USB_PEGASUS is not set
 # CONFIG_USB_RTL8150 is not set
 # CONFIG_USB_KAWETH is not set
 # CONFIG_USB_CATC is not set
 # CONFIG_USB_CDCETHER is not set
 # CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
 # CONFIG_USB_USS720 is not set
 
 #
 # USB Serial Converter support
 #
 # CONFIG_USB_SERIAL is not set
-
-#
-# USB Miscellaneous drivers
-#
 # CONFIG_USB_RIO500 is not set
 # CONFIG_USB_AUERSWALD is not set
 # CONFIG_USB_TIGL is not set
@@ -1109,6 +1035,12 @@
 # CONFIG_BLUEZ is not set
 
 #
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+# CONFIG_MMC_AU1100 is not set
+
+#
 # Kernel hacking
 #
 CONFIG_CROSSCOMPILE=y
diff -Nur linux-2.4.30/arch/mips/defconfig-pb1550 linux-2.4.30-linux-mips.org/arch/mips/defconfig-pb1550
--- linux-2.4.30/arch/mips/defconfig-pb1550	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-pb1550	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 #
-# Automatically generated make config: don't edit
+# Automatically generated by make menuconfig: don't edit
 #
 CONFIG_MIPS=y
 CONFIG_MIPS32=y
@@ -22,16 +22,19 @@
 #
 # CONFIG_ACER_PICA_61 is not set
 # CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_FICMMP is not set
 # CONFIG_MIPS_MIRAGE is not set
 # CONFIG_MIPS_DB1000 is not set
 # CONFIG_MIPS_DB1100 is not set
 # CONFIG_MIPS_DB1500 is not set
 # CONFIG_MIPS_DB1550 is not set
+# CONFIG_MIPS_DB1200 is not set
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 CONFIG_MIPS_PB1550=y
+# CONFIG_MIPS_PB1200 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -79,6 +82,7 @@
 # CONFIG_RWSEM_XCHGADD_ALGORITHM is not set
 CONFIG_SOC_AU1X00=y
 CONFIG_SOC_AU1550=y
+CONFIG_SOC_AU1X00_32KHZ=y
 # CONFIG_NONCOHERENT_IO is not set
 CONFIG_PC_KEYB=y
 # CONFIG_MIPS_AU1000 is not set
@@ -176,14 +180,11 @@
 # CONFIG_MTD_CONCAT is not set
 # CONFIG_MTD_REDBOOT_PARTS is not set
 # CONFIG_MTD_CMDLINE_PARTS is not set
-
-#
-# User Modules And Translation Layers
-#
 CONFIG_MTD_CHAR=y
 CONFIG_MTD_BLOCK=y
 # CONFIG_FTL is not set
 # CONFIG_NFTL is not set
+# CONFIG_INFTL is not set
 
 #
 # RAM/ROM/Flash chip drivers
@@ -192,9 +193,10 @@
 # CONFIG_MTD_JEDECPROBE is not set
 CONFIG_MTD_GEN_PROBE=y
 # CONFIG_MTD_CFI_ADV_OPTIONS is not set
-CONFIG_MTD_CFI_INTELEXT=y
+# CONFIG_MTD_CFI_INTELEXT is not set
 CONFIG_MTD_CFI_AMDSTD=y
 # CONFIG_MTD_CFI_STAA is not set
+CONFIG_MTD_CFI_UTIL=y
 # CONFIG_MTD_RAM is not set
 # CONFIG_MTD_ROM is not set
 # CONFIG_MTD_ABSENT is not set
@@ -206,19 +208,10 @@
 #
 # Mapping drivers for chip access
 #
+# CONFIG_MTD_COMPLEX_MAPPINGS is not set
 # CONFIG_MTD_PHYSMAP is not set
-# CONFIG_MTD_PB1000 is not set
-# CONFIG_MTD_PB1500 is not set
-# CONFIG_MTD_PB1100 is not set
-# CONFIG_MTD_BOSPORUS is not set
+CONFIG_MTD_ALCHEMY=y
 # CONFIG_MTD_XXS1500 is not set
-# CONFIG_MTD_MTX1 is not set
-# CONFIG_MTD_DB1X00 is not set
-CONFIG_MTD_PB1550=y
-CONFIG_MTD_PB1550_BOOT=y
-CONFIG_MTD_PB1550_USER=y
-# CONFIG_MTD_HYDROGEN3 is not set
-# CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
 # CONFIG_MTD_LASAT is not set
@@ -232,21 +225,16 @@
 # CONFIG_MTD_SLRAM is not set
 # CONFIG_MTD_MTDRAM is not set
 # CONFIG_MTD_BLKMTD is not set
-
-#
-# Disk-On-Chip Device Drivers
-#
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
 # CONFIG_MTD_DOCPROBE is not set
 
 #
 # NAND Flash Device Drivers
 #
-CONFIG_MTD_NAND=m
-# CONFIG_MTD_NAND_VERIFY_WRITE is not set
-CONFIG_MTD_NAND_IDS=m
+# CONFIG_MTD_NAND is not set
+# CONFIG_MTD_NAND_AU1550 is not set
 
 #
 # Parallel port support
@@ -337,17 +325,8 @@
 # CONFIG_IP_SCTP is not set
 # CONFIG_ATM is not set
 # CONFIG_VLAN_8021Q is not set
-
-#
-#  
-#
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -385,10 +364,6 @@
 # IDE, ATA and ATAPI Block devices
 #
 CONFIG_BLK_DEV_IDE=y
-
-#
-# Please see Documentation/ide.txt for help/info on IDE drives
-#
 # CONFIG_BLK_DEV_HD_IDE is not set
 # CONFIG_BLK_DEV_HD is not set
 # CONFIG_BLK_DEV_IDE_SATA is not set
@@ -402,10 +377,6 @@
 # CONFIG_BLK_DEV_IDEFLOPPY is not set
 # CONFIG_BLK_DEV_IDESCSI is not set
 # CONFIG_IDE_TASK_IOCTL is not set
-
-#
-# IDE chipset support/bugfixes
-#
 # CONFIG_BLK_DEV_CMD640 is not set
 # CONFIG_BLK_DEV_CMD640_ENHANCED is not set
 # CONFIG_BLK_DEV_ISAPNP is not set
@@ -448,6 +419,10 @@
 # CONFIG_BLK_DEV_SLC90E66 is not set
 # CONFIG_BLK_DEV_TRM290 is not set
 # CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_BLK_DEV_IDE_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX is not set
+# CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX is not set
 # CONFIG_IDE_CHIPSETS is not set
 # CONFIG_IDEDMA_AUTO is not set
 # CONFIG_IDEDMA_IVB is not set
@@ -509,6 +484,7 @@
 CONFIG_NET_ETHERNET=y
 CONFIG_MIPS_AU1X00_ENET=y
 # CONFIG_BCM5222_DUAL_PHY is not set
+# CONFIG_AU1XXX_SMC91111 is not set
 # CONFIG_SUNLANCE is not set
 # CONFIG_HAPPYMEAL is not set
 # CONFIG_SUNBMAC is not set
@@ -633,7 +609,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
@@ -659,10 +634,6 @@
 # CONFIG_INPUT_EMU10K1 is not set
 # CONFIG_INPUT_SERIO is not set
 # CONFIG_INPUT_SERPORT is not set
-
-#
-# Joysticks
-#
 # CONFIG_INPUT_ANALOG is not set
 # CONFIG_INPUT_A3D is not set
 # CONFIG_INPUT_ADI is not set
@@ -748,8 +719,7 @@
 CONFIG_VFAT_FS=y
 # CONFIG_EFS_FS is not set
 # CONFIG_JFFS_FS is not set
-CONFIG_JFFS2_FS=y
-CONFIG_JFFS2_FS_DEBUG=0
+# CONFIG_JFFS2_FS is not set
 # CONFIG_CRAMFS is not set
 CONFIG_TMPFS=y
 CONFIG_RAMFS=y
@@ -888,6 +858,7 @@
 # CONFIG_FB_ATY128 is not set
 # CONFIG_FB_INTEL is not set
 # CONFIG_FB_SIS is not set
+CONFIG_FB_SMI501=y
 # CONFIG_FB_NEOMAGIC is not set
 # CONFIG_FB_3DFX is not set
 # CONFIG_FB_VOODOO1 is not set
@@ -937,7 +908,6 @@
 # CONFIG_SOUND_ICH is not set
 # CONFIG_SOUND_RME96XX is not set
 # CONFIG_SOUND_SONICVIBES is not set
-# CONFIG_SOUND_AU1X00 is not set
 CONFIG_SOUND_AU1550_PSC=y
 # CONFIG_SOUND_AU1550_I2S is not set
 # CONFIG_SOUND_TRIDENT is not set
@@ -955,32 +925,18 @@
 #
 CONFIG_USB=y
 # CONFIG_USB_DEBUG is not set
-
-#
-# Miscellaneous USB options
-#
 # CONFIG_USB_DEVICEFS is not set
 # CONFIG_USB_BANDWIDTH is not set
-
-#
-# USB Host Controller Drivers
-#
 # CONFIG_USB_EHCI_HCD is not set
+# CONFIG_USB_NON_PCI_EHCI is not set
 # CONFIG_USB_UHCI is not set
 # CONFIG_USB_UHCI_ALT is not set
 CONFIG_USB_OHCI=y
-
-#
-# USB Device Class drivers
-#
+CONFIG_USB_NON_PCI_OHCI=y
 # CONFIG_USB_AUDIO is not set
 # CONFIG_USB_EMI26 is not set
 # CONFIG_USB_BLUETOOTH is not set
 # CONFIG_USB_MIDI is not set
-
-#
-#   SCSI support is needed for USB Storage
-#
 # CONFIG_USB_STORAGE is not set
 # CONFIG_USB_STORAGE_DEBUG is not set
 # CONFIG_USB_STORAGE_DATAFAB is not set
@@ -993,10 +949,6 @@
 # CONFIG_USB_STORAGE_JUMPSHOT is not set
 # CONFIG_USB_ACM is not set
 # CONFIG_USB_PRINTER is not set
-
-#
-# USB Human Interface Devices (HID)
-#
 CONFIG_USB_HID=y
 CONFIG_USB_HIDINPUT=y
 # CONFIG_USB_HIDDEV is not set
@@ -1004,47 +956,23 @@
 # CONFIG_USB_WACOM is not set
 # CONFIG_USB_KBTAB is not set
 # CONFIG_USB_POWERMATE is not set
-
-#
-# USB Imaging devices
-#
 # CONFIG_USB_DC2XX is not set
 # CONFIG_USB_MDC800 is not set
 # CONFIG_USB_SCANNER is not set
 # CONFIG_USB_MICROTEK is not set
 # CONFIG_USB_HPUSBSCSI is not set
-
-#
-# USB Multimedia devices
-#
-
-#
-#   Video4Linux support is needed for USB Multimedia device support
-#
-
-#
-# USB Network adaptors
-#
 # CONFIG_USB_PEGASUS is not set
 # CONFIG_USB_RTL8150 is not set
 # CONFIG_USB_KAWETH is not set
 # CONFIG_USB_CATC is not set
 # CONFIG_USB_CDCETHER is not set
 # CONFIG_USB_USBNET is not set
-
-#
-# USB port drivers
-#
 # CONFIG_USB_USS720 is not set
 
 #
 # USB Serial Converter support
 #
 # CONFIG_USB_SERIAL is not set
-
-#
-# USB Miscellaneous drivers
-#
 # CONFIG_USB_RIO500 is not set
 # CONFIG_USB_AUERSWALD is not set
 # CONFIG_USB_TIGL is not set
@@ -1062,6 +990,12 @@
 # CONFIG_BLUEZ is not set
 
 #
+# MMC/SD Card support
+#
+# CONFIG_MMC is not set
+# CONFIG_MMC_AU1100 is not set
+
+#
 # Kernel hacking
 #
 CONFIG_CROSSCOMPILE=y
@@ -1082,6 +1016,6 @@
 # Library routines
 #
 # CONFIG_CRC32 is not set
-CONFIG_ZLIB_INFLATE=y
-CONFIG_ZLIB_DEFLATE=y
+CONFIG_ZLIB_INFLATE=m
+CONFIG_ZLIB_DEFLATE=m
 # CONFIG_FW_LOADER is not set
diff -Nur linux-2.4.30/arch/mips/defconfig-rbtx4927 linux-2.4.30-linux-mips.org/arch/mips/defconfig-rbtx4927
--- linux-2.4.30/arch/mips/defconfig-rbtx4927	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-rbtx4927	2005-05-12 17:57:05.000000000 +0200
@@ -28,8 +28,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -223,11 +223,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -466,7 +461,6 @@
 CONFIG_SERIAL_TXX9=y
 CONFIG_SERIAL_TXX9_CONSOLE=y
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 # CONFIG_UNIX98_PTYS is not set
 
 #
diff -Nur linux-2.4.30/arch/mips/defconfig-rm200 linux-2.4.30-linux-mips.org/arch/mips/defconfig-rm200
--- linux-2.4.30/arch/mips/defconfig-rm200	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-rm200	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -229,11 +229,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -340,7 +335,6 @@
 # CONFIG_SERIAL is not set
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-sb1250-swarm linux-2.4.30-linux-mips.org/arch/mips/defconfig-sb1250-swarm
--- linux-2.4.30/arch/mips/defconfig-sb1250-swarm	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-sb1250-swarm	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -90,6 +90,7 @@
 # CONFIG_SIBYTE_TBPROF is not set
 CONFIG_SIBYTE_GENBUS_IDE=y
 CONFIG_SMP_CAPABLE=y
+CONFIG_MIPS_RTC=y
 # CONFIG_SNI_RM200_PCI is not set
 # CONFIG_TANBAC_TB0226 is not set
 # CONFIG_TANBAC_TB0229 is not set
@@ -253,11 +254,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -469,7 +465,6 @@
 CONFIG_SIBYTE_SB1250_DUART=y
 CONFIG_SIBYTE_SB1250_DUART_CONSOLE=y
 CONFIG_SERIAL_CONSOLE=y
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-sead linux-2.4.30-linux-mips.org/arch/mips/defconfig-sead
--- linux-2.4.30/arch/mips/defconfig-sead	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-sead	2005-05-12 17:57:05.000000000 +0200
@@ -28,8 +28,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -244,7 +244,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 # CONFIG_UNIX98_PTYS is not set
 
 #
diff -Nur linux-2.4.30/arch/mips/defconfig-stretch linux-2.4.30-linux-mips.org/arch/mips/defconfig-stretch
--- linux-2.4.30/arch/mips/defconfig-stretch	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-stretch	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -240,11 +240,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -324,9 +319,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -516,7 +513,6 @@
 # CONFIG_SERIAL_TXX9 is not set
 # CONFIG_SERIAL_TXX9_CONSOLE is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-tb0226 linux-2.4.30-linux-mips.org/arch/mips/defconfig-tb0226
--- linux-2.4.30/arch/mips/defconfig-tb0226	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-tb0226	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -228,11 +228,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -312,9 +307,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -518,7 +515,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 # CONFIG_VR41XX_KIU is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
diff -Nur linux-2.4.30/arch/mips/defconfig-tb0229 linux-2.4.30-linux-mips.org/arch/mips/defconfig-tb0229
--- linux-2.4.30/arch/mips/defconfig-tb0229	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-tb0229	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -230,11 +230,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -445,7 +440,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 # CONFIG_VR41XX_KIU is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
diff -Nur linux-2.4.30/arch/mips/defconfig-ti1500 linux-2.4.30-linux-mips.org/arch/mips/defconfig-ti1500
--- linux-2.4.30/arch/mips/defconfig-ti1500	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-ti1500	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 CONFIG_MIPS_XXS1500=y
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -213,9 +213,7 @@
 # CONFIG_MTD_BOSPORUS is not set
 CONFIG_MTD_XXS1500=y
 # CONFIG_MTD_MTX1 is not set
-# CONFIG_MTD_DB1X00 is not set
 # CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
 # CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
@@ -234,7 +232,6 @@
 #
 # Disk-On-Chip Device Drivers
 #
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
 # CONFIG_MTD_DOCPROBE is not set
@@ -339,11 +336,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -600,7 +592,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-workpad linux-2.4.30-linux-mips.org/arch/mips/defconfig-workpad
--- linux-2.4.30/arch/mips/defconfig-workpad	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-workpad	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -222,11 +222,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -426,7 +421,6 @@
 # CONFIG_SERIAL_MULTIPORT is not set
 # CONFIG_HUB6 is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 # CONFIG_VR41XX_KIU is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
diff -Nur linux-2.4.30/arch/mips/defconfig-xxs1500 linux-2.4.30-linux-mips.org/arch/mips/defconfig-xxs1500
--- linux-2.4.30/arch/mips/defconfig-xxs1500	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-xxs1500	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 CONFIG_MIPS_XXS1500=y
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -213,9 +213,7 @@
 # CONFIG_MTD_BOSPORUS is not set
 CONFIG_MTD_XXS1500=y
 # CONFIG_MTD_MTX1 is not set
-# CONFIG_MTD_DB1X00 is not set
 # CONFIG_MTD_PB1550 is not set
-# CONFIG_MTD_HYDROGEN3 is not set
 # CONFIG_MTD_MIRAGE is not set
 # CONFIG_MTD_CSTM_MIPS_IXX is not set
 # CONFIG_MTD_OCELOT is not set
@@ -234,7 +232,6 @@
 #
 # Disk-On-Chip Device Drivers
 #
-# CONFIG_MTD_DOC1000 is not set
 # CONFIG_MTD_DOC2000 is not set
 # CONFIG_MTD_DOC2001 is not set
 # CONFIG_MTD_DOCPROBE is not set
@@ -339,11 +336,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -671,7 +663,6 @@
 # CONFIG_AU1X00_USB_TTY is not set
 # CONFIG_AU1X00_USB_RAW is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/defconfig-yosemite linux-2.4.30-linux-mips.org/arch/mips/defconfig-yosemite
--- linux-2.4.30/arch/mips/defconfig-yosemite	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/defconfig-yosemite	2005-05-12 17:57:05.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -227,11 +227,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -310,9 +305,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -477,7 +474,6 @@
 # CONFIG_SERIAL_TXX9 is not set
 # CONFIG_SERIAL_TXX9_CONSOLE is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips/kernel/cpu-probe.c linux-2.4.30-linux-mips.org/arch/mips/kernel/cpu-probe.c
--- linux-2.4.30/arch/mips/kernel/cpu-probe.c	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/kernel/cpu-probe.c	2005-05-12 17:57:06.000000000 +0200
@@ -34,16 +34,12 @@
 		".set\tmips0");
 }
 
-/* The Au1xxx wait is available only if we run CONFIG_PM and
- * the timer setup found we had a 32KHz counter available.
- * There are still problems with functions that may call au1k_wait
- * directly, but that will be discovered pretty quickly.
- */
-extern void (*au1k_wait_ptr)(void);
-void au1k_wait(void)
+/* The Au1xxx wait is available only if using 32khz counter or
+ * external timer source, but specifically not CP0 Counter. */
+int allow_au1k_wait; 
+static void au1k_wait(void)
 {
-#ifdef CONFIG_PM
-	unsigned long addr;
+	unsigned long addr = 0;
 	/* using the wait instruction makes CP0 counter unusable */
 	__asm__("la %0,au1k_wait\n\t"
 		".set mips3\n\t"
@@ -58,10 +54,6 @@
 		"nop\n\t"
 		".set mips0\n\t"
 		: : "r" (addr));
-#else
-	__asm__("nop\n\t"
-		"nop");
-#endif
 }
 
 static inline void check_wait(void)
@@ -100,20 +92,17 @@
 		cpu_wait = r4k_wait;
 		printk(" available.\n");
 		break;
-#ifdef CONFIG_PM
 	case CPU_AU1000:
 	case CPU_AU1100:
 	case CPU_AU1500:
 	case CPU_AU1550:
-		if (au1k_wait_ptr != NULL) {
-			cpu_wait = au1k_wait_ptr;
+	case CPU_AU1200:
+		if (allow_au1k_wait) {
+			cpu_wait = au1k_wait;
 			printk(" available.\n");
-		}
-		else {
+		} else
 			printk(" unavailable.\n");
-		}
 		break;
-#endif
 	default:
 		printk(" unavailable.\n");
 		break;
diff -Nur linux-2.4.30/arch/mips/kernel/head.S linux-2.4.30-linux-mips.org/arch/mips/kernel/head.S
--- linux-2.4.30/arch/mips/kernel/head.S	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/kernel/head.S	2005-05-12 17:57:06.000000000 +0200
@@ -43,9 +43,9 @@
 
 		/* Cache Error */
 		LEAF(except_vec2_generic)
+		.set	push
 		.set	noreorder
 		.set	noat
-		.set    mips0
 		/*
 		 * This is a very bad place to be.  Our cache error
 		 * detection has triggered.  If we have write-back data
@@ -64,10 +64,9 @@
 
 		j	cache_parity_error
 		 nop
+		.set	pop
 		END(except_vec2_generic)
 
-		.set	at
-
 		/*
 		 * Special interrupt vector for embedded MIPS.  This is a
 		 * dedicated interrupt vector which reduces interrupt processing
@@ -76,8 +75,11 @@
 		 * size!
 		 */
 		NESTED(except_vec4, 0, sp)
+		.set	push
+		.set	noreorder
 1:		j	1b			/* Dummy, will be replaced */
 		 nop
+		.set	pop
 		END(except_vec4)
 
 		/*
@@ -87,8 +89,11 @@
 		 * unconditional jump to this vector.
 		 */
 		NESTED(except_vec_ejtag_debug, 0, sp)
+		.set	push
+		.set	noreorder
 		j	ejtag_debug_handler
 		 nop
+		.set	pop
 		END(except_vec_ejtag_debug)
 
 		__FINIT
@@ -97,6 +102,7 @@
 		 * EJTAG debug exception handler.
 		 */
 		NESTED(ejtag_debug_handler, PT_SIZE, sp)
+		.set	push
 		.set	noat
 		.set	noreorder
 		mtc0	k0, CP0_DESAVE
@@ -120,7 +126,7 @@
 		deret
 		.set	mips0
 		 nop
-		.set	at
+		.set	pop
 		END(ejtag_debug_handler)
 
 		__INIT
@@ -132,13 +138,17 @@
 		* unconditional jump to this vector.
 		*/
 		NESTED(except_vec_nmi, 0, sp)
+		.set	push
+		.set	noreorder
 		j       nmi_handler
 		 nop
+		.set	pop
 		END(except_vec_nmi)
 
 		__FINIT
 
 		NESTED(nmi_handler, PT_SIZE, sp)
+		.set	push
 		.set    noat
 		.set    noreorder
 		.set    mips3
@@ -147,8 +157,7 @@
 		 move   a0, sp
 		RESTORE_ALL
 		eret
-		.set    at
-		.set    mips0
+		.set	pop
 		END(nmi_handler)
 
 		__INIT
@@ -157,7 +166,20 @@
 		 * Kernel entry point
 		 */
 		NESTED(kernel_entry, 16, sp)
+		.set	push
+		/*
+		 * For the moment disable interrupts and mark the kernel mode.
+		 * A full initialization of the CPU's status register is done
+		 * later in per_cpu_trap_init().
+		 */
+		mfc0	t0, CP0_STATUS
+		or	t0, ST0_CU0|0x1f
+		xor	t0, 0x1f
+		mtc0	t0, CP0_STATUS
+
 		.set	noreorder
+		sll	zero,3				# ehb
+		.set	reorder
 
 		/*
 		 * The firmware/bootloader passes argc/argp/envp
@@ -170,8 +192,8 @@
 		la	t1, (_end - 4)
 1:
 		addiu	t0, 4
+		sw	zero, (t0)
 		bne	t0, t1, 1b
-		 sw	zero, (t0)
 
 		/*
 		 * Stack for kernel and init, current variable
@@ -182,7 +204,7 @@
 		sw	t0, kernelsp
 
 		jal	init_arch
-		 nop
+		.set	pop
 		END(kernel_entry)
 
 
@@ -193,17 +215,26 @@
  * function after setting up the stack and gp registers.
  */
 		LEAF(smp_bootstrap)
-		.set push
-		.set noreorder
-		mtc0	zero, CP0_WIRED
-		CLI
+		.set	push
+		/*
+		 * For the moment disable interrupts and bootstrap exception
+		 * vectors and mark the kernel mode.  A full initialization of
+		 * the CPU's status register is done later in
+		 * per_cpu_trap_init().
+		 */
 		mfc0	t0, CP0_STATUS
-		li	t1, ~(ST0_CU1|ST0_CU2|ST0_CU3|ST0_KX|ST0_SX|ST0_UX)
-		and	t0, t1
-		or	t0, (ST0_CU0);
+		or	t0, ST0_CU0|ST0_BEV|0x1f
+		xor	t0, ST0_BEV|0x1f
+		mtc0	t0, CP0_STATUS
+
+		.set	noreorder
+		sll	zero,3				# ehb
+		.set	reorder
+
+		mtc0	zero, CP0_WIRED
+
 		jal	start_secondary
-		 mtc0	t0, CP0_STATUS
-		.set pop
+		.set	pop
 		END(smp_bootstrap)
 #endif
 
diff -Nur linux-2.4.30/arch/mips/kernel/process.c linux-2.4.30-linux-mips.org/arch/mips/kernel/process.c
--- linux-2.4.30/arch/mips/kernel/process.c	2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.30-linux-mips.org/arch/mips/kernel/process.c	2005-05-12 17:57:06.000000000 +0200
@@ -128,6 +128,26 @@
 	return 1;
 }
 
+void dump_regs(elf_greg_t *gp, struct pt_regs *regs)
+{
+	int i;
+
+	for (i = 0; i < EF_REG0; i++)
+		gp[i] = 0;
+	gp[EF_REG0] = 0;
+	for (i = 1; i <= 31; i++)
+		gp[EF_REG0 + i] = regs->regs[i];
+	gp[EF_REG26] = 0;
+	gp[EF_REG27] = 0;
+	gp[EF_LO] = regs->lo;
+	gp[EF_HI] = regs->hi;
+	gp[EF_CP0_EPC] = regs->cp0_epc;
+	gp[EF_CP0_BADVADDR] = regs->cp0_badvaddr;
+	gp[EF_CP0_STATUS] = regs->cp0_status;
+	gp[EF_CP0_CAUSE] = regs->cp0_cause;
+	gp[EF_UNUSED0] = 0;
+}
+
 /*
  * Create a kernel thread
  */
diff -Nur linux-2.4.30/arch/mips/kernel/scall_o32.S linux-2.4.30-linux-mips.org/arch/mips/kernel/scall_o32.S
--- linux-2.4.30/arch/mips/kernel/scall_o32.S	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/kernel/scall_o32.S	2005-05-12 17:57:06.000000000 +0200
@@ -121,15 +121,14 @@
 
 trace_a_syscall:
 	SAVE_STATIC
-	sw	t2, PT_R1(sp)
+	move	s0, t2
 	jal	syscall_trace
-	lw	t2, PT_R1(sp)
 
 	lw	a0, PT_R4(sp)		# Restore argument registers
 	lw	a1, PT_R5(sp)
 	lw	a2, PT_R6(sp)
 	lw	a3, PT_R7(sp)
-	jalr	t2
+	jalr	s0
 
 	li	t0, -EMAXERRNO - 1	# error?
 	sltu	t0, t0, v0
diff -Nur linux-2.4.30/arch/mips/kernel/setup.c linux-2.4.30-linux-mips.org/arch/mips/kernel/setup.c
--- linux-2.4.30/arch/mips/kernel/setup.c	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/kernel/setup.c	2005-05-12 17:57:06.000000000 +0200
@@ -5,7 +5,7 @@
  *
  * Copyright (C) 1995  Linus Torvalds
  * Copyright (C) 1995  Waldorf Electronics
- * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 2001  Ralf Baechle
+ * Copyright (C) 1995, 1996, 1997, 1998, 1999, 2000, 01, 05  Ralf Baechle
  * Copyright (C) 1996  Stoned Elipot
  * Copyright (C) 2000, 2001, 2002  Maciej W. Rozycki
  */
@@ -71,6 +71,8 @@
 extern struct rtc_ops no_rtc_ops;
 struct rtc_ops *rtc_ops;
 
+EXPORT_SYMBOL(rtc_ops);
+
 #ifdef CONFIG_PC_KEYB
 struct kbd_ops *kbd_ops;
 #endif
@@ -132,10 +134,6 @@
 	 */
 	load_mmu();
 
-	/* Disable coprocessors and set FPU for 16/32 FPR register model */
-	clear_c0_status(ST0_CU1|ST0_CU2|ST0_CU3|ST0_KX|ST0_SX|ST0_FR);
-	set_c0_status(ST0_CU0);
-
 	start_kernel();
 }
 
diff -Nur linux-2.4.30/arch/mips/kernel/traps.c linux-2.4.30-linux-mips.org/arch/mips/kernel/traps.c
--- linux-2.4.30/arch/mips/kernel/traps.c	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/kernel/traps.c	2005-05-12 17:57:06.000000000 +0200
@@ -452,9 +452,10 @@
 	}
 	ll_task = current;
 
+	compute_return_epc(regs);
+
 	regs->regs[(opcode & RT) >> 16] = value;
 
-	compute_return_epc(regs);
 	return;
 
 sig:
@@ -485,8 +486,8 @@
 		goto sig;
 	}
 	if (ll_bit == 0 || ll_task != current) {
-		regs->regs[reg] = 0;
 		compute_return_epc(regs);
+		regs->regs[reg] = 0;
 		return;
 	}
 
@@ -495,9 +496,9 @@
 		goto sig;
 	}
 
+	compute_return_epc(regs);
 	regs->regs[reg] = 1;
 
-	compute_return_epc(regs);
 	return;
 
 sig:
@@ -887,12 +888,18 @@
 void __init per_cpu_trap_init(void)
 {
 	unsigned int cpu = smp_processor_id();
+	unsigned int status_set = ST0_CU0;
 
-	/* Some firmware leaves the BEV flag set, clear it.  */
-	clear_c0_status(ST0_CU3|ST0_CU2|ST0_CU1|ST0_BEV|ST0_KX|ST0_SX|ST0_UX);
-
+	/*
+	 * Disable coprocessors and 64-bit addressing and set FPU for
+	 * the 16/32 FPR register model.  Reset the BEV flag that some
+	 * firmware may have left set and the TS bit (for IP27).  Set
+	 * XX for ISA IV code to work.
+	 */
 	if (current_cpu_data.isa_level == MIPS_CPU_ISA_IV)
-		set_c0_status(ST0_XX);
+		status_set |= ST0_XX;
+	change_c0_status(ST0_CU|ST0_FR|ST0_BEV|ST0_TS|ST0_KX|ST0_SX|ST0_UX,
+			 status_set);
 
 	/*
 	 * Some MIPS CPUs have a dedicated interrupt vector which reduces the
@@ -902,7 +909,7 @@
 		set_c0_cause(CAUSEF_IV);
 
 	cpu_data[cpu].asid_cache = ASID_FIRST_VERSION;
-	write_c0_context(cpu << 23);
+	TLBMISS_HANDLER_SETUP();
 
 	atomic_inc(&init_mm.mm_count);
 	current->active_mm = &init_mm;
@@ -918,8 +925,6 @@
 	extern char except_vec4;
 	unsigned long i;
 
-	per_cpu_trap_init();
-
 	/* Copy the generic exception handler code to it's final destination. */
 	memcpy((void *)(KSEG0 + 0x80), &except_vec1_generic, 0x80);
 
@@ -1020,10 +1025,5 @@
 
 	flush_icache_range(KSEG0, KSEG0 + 0x400);
 
-	atomic_inc(&init_mm.mm_count);	/* XXX UP?  */
-	current->active_mm = &init_mm;
-
-	/* XXX Must be done for all CPUs  */
-	current_cpu_data.asid_cache = ASID_FIRST_VERSION;
-	TLBMISS_HANDLER_SETUP();
+	per_cpu_trap_init();
 }
diff -Nur linux-2.4.30/arch/mips/lib/rtc-no.c linux-2.4.30-linux-mips.org/arch/mips/lib/rtc-no.c
--- linux-2.4.30/arch/mips/lib/rtc-no.c	2004-02-18 14:36:30.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/lib/rtc-no.c	2005-05-12 17:57:06.000000000 +0200
@@ -6,10 +6,9 @@
  * Stub RTC routines to keep Linux from crashing on machine which don't
  * have a RTC chip.
  *
- * Copyright (C) 1998, 2001 by Ralf Baechle
+ * Copyright (C) 1998, 2001, 2005 by Ralf Baechle
  */
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <linux/mc146818rtc.h>
 
 static unsigned int shouldnt_happen(void)
@@ -29,5 +28,3 @@
     .rtc_write_data = (void *) &shouldnt_happen,
     .rtc_bcd_mode   = (void *) &shouldnt_happen
 };
-
-EXPORT_SYMBOL(rtc_ops);
diff -Nur linux-2.4.30/arch/mips/lib/rtc-std.c linux-2.4.30-linux-mips.org/arch/mips/lib/rtc-std.c
--- linux-2.4.30/arch/mips/lib/rtc-std.c	2004-02-18 14:36:30.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/lib/rtc-std.c	2005-05-12 17:57:06.000000000 +0200
@@ -5,9 +5,8 @@
  *
  * RTC routines for PC style attached Dallas chip.
  *
- * Copyright (C) 1998, 2001 by Ralf Baechle
+ * Copyright (C) 1998, 2001, 05 by Ralf Baechle
  */
-#include <linux/module.h>
 #include <linux/mc146818rtc.h>
 #include <asm/io.h>
 
@@ -33,5 +32,3 @@
 	&std_rtc_write_data,
 	&std_rtc_bcd_mode
 };
-
-EXPORT_SYMBOL(rtc_ops);
diff -Nur linux-2.4.30/arch/mips/Makefile linux-2.4.30-linux-mips.org/arch/mips/Makefile
--- linux-2.4.30/arch/mips/Makefile	2005-01-19 15:09:26.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -17,7 +17,8 @@
 # Select the object file format to substitute into the linker script.
 #
 ifdef CONFIG_CPU_LITTLE_ENDIAN
-tool-prefix	= mipsel-linux-
+#tool-prefix	= mipsel-linux-
+tool-prefix	= mips_nfp_le-
 ld-emul		= elf32ltsmip
 else
 tool-prefix	= mips-linux-
@@ -29,6 +30,8 @@
 endif
 
 MAKEBOOT = $(MAKE) -C arch/$(ARCH)/boot
+MAKEZBOOT = $(MAKE) -C arch/$(ARCH)/zboot
+BOOT_TARGETS = zImage zImage.initrd zImage.flash
 
 check_gcc = $(shell if $(CC) $(1) -S -o /dev/null -xc /dev/null > /dev/null 2>&1; then echo "$(1)"; else echo "$(2)"; fi)
 
@@ -211,7 +214,7 @@
 endif
 
 #
-# Au1000 (Alchemy Semi PB1000) eval board
+# Au1x AMD Alchemy eval boards
 #
 ifdef CONFIG_MIPS_PB1000
 LIBS		+= arch/mips/au1000/pb1000/pb1000.o \
@@ -220,9 +223,6 @@
 LOADADDR	:= 0x80100000
 endif
 
-#
-# Au1100 (Alchemy Semi PB1100) eval board
-#
 ifdef CONFIG_MIPS_PB1100
 LIBS          += arch/mips/au1000/pb1100/pb1100.o \
                  arch/mips/au1000/common/au1000.o
@@ -230,9 +230,6 @@
 LOADADDR      += 0x80100000
 endif
 
-#
-# Au1500 (Alchemy Semi PB1500) eval board
-#
 ifdef CONFIG_MIPS_PB1500
 LIBS		+= arch/mips/au1000/pb1500/pb1500.o \
 		   arch/mips/au1000/common/au1000.o
@@ -240,9 +237,6 @@
 LOADADDR	:= 0x80100000
 endif
 
-#
-# Au1x00 (AMD/Alchemy) eval boards
-#
 ifdef CONFIG_MIPS_DB1000
 LIBS          += arch/mips/au1000/db1x00/db1x00.o \
                  arch/mips/au1000/common/au1000.o
@@ -313,6 +307,27 @@
 LOADADDR      += 0x80100000
 endif
 
+ifdef CONFIG_MIPS_PB1200
+LIBS          += arch/mips/au1000/pb1200/pb1200.o \
+                 arch/mips/au1000/common/au1000.o
+SUBDIRS       += arch/mips/au1000/pb1200 arch/mips/au1000/common
+LOADADDR      += 0x80100000
+endif
+
+ifdef CONFIG_MIPS_DB1200
+LIBS          += arch/mips/au1000/pb1200/pb1200.o \
+                 arch/mips/au1000/common/au1000.o
+SUBDIRS       += arch/mips/au1000/pb1200 arch/mips/au1000/common
+LOADADDR      += 0x80100000
+endif
+
+ifdef CONFIG_MIPS_FICMMP
+LIBS          += arch/mips/au1000/ficmmp/ficmmp.o \
+                 arch/mips/au1000/common/au1000.o
+SUBDIRS       += arch/mips/au1000/ficmmp arch/mips/au1000/common
+LOADADDR      += 0x80100000
+endif
+
 
 #
 # Cogent CSB250
@@ -755,14 +770,23 @@
 mips-boot: vmlinux
 	@$(MAKEBOOT) boot
 
+boot: mips-boot
+
+mips-boot: vmlinux
+	@$(MAKEBOOT) boot
+
 vmlinux.ecoff: vmlinux
 	@$(MAKEBOOT) $@
 
+$(BOOT_TARGETS): vmlinux
+	@$(MAKEZBOOT) $@
+
 vmlinux.srec: vmlinux
 	@$(MAKEBOOT) $@
 
 archclean:
 	@$(MAKEBOOT) clean
+	@$(MAKEZBOOT) clean
 	rm -f arch/$(ARCH)/ld.script
 	$(MAKE) -C arch/$(ARCH)/tools clean
 	$(MAKE) -C arch/mips/baget clean
diff -Nur linux-2.4.30/arch/mips/mm/cerr-sb1.c linux-2.4.30-linux-mips.org/arch/mips/mm/cerr-sb1.c
--- linux-2.4.30/arch/mips/mm/cerr-sb1.c	2004-02-18 14:36:30.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/mm/cerr-sb1.c	2005-05-12 17:57:06.000000000 +0200
@@ -252,14 +252,14 @@
 
 /* Masks to select bits for Hamming parity, mask_72_64[i] for bit[i] */
 static const uint64_t mask_72_64[8] = {
-	0x0738C808099264FFL,
-	0x38C808099264FF07L,
-	0xC808099264FF0738L,
-	0x08099264FF0738C8L,
-	0x099264FF0738C808L,
-	0x9264FF0738C80809L,
-	0x64FF0738C8080992L,
-	0xFF0738C808099264L
+	0x0738C808099264FFULL,
+	0x38C808099264FF07ULL,
+	0xC808099264FF0738ULL,
+	0x08099264FF0738C8ULL,
+	0x099264FF0738C808ULL,
+	0x9264FF0738C80809ULL,
+	0x64FF0738C8080992ULL,
+	0xFF0738C808099264ULL
 };
 
 /* Calculate the parity on a range of bits */
@@ -331,9 +331,9 @@
 				    ((lru >> 4) & 0x3),
 				    ((lru >> 6) & 0x3));
 		}
-		va = (taglo & 0xC0000FFFFFFFE000) | addr;
+		va = (taglo & 0xC0000FFFFFFFE000ULL) | addr;
 		if ((taglo & (1 << 31)) && (((taglo >> 62) & 0x3) == 3))
-			va |= 0x3FFFF00000000000;
+			va |= 0x3FFFF00000000000ULL;
 		valid = ((taghi >> 29) & 1);
 		if (valid) {
 			tlo_tmp = taglo & 0xfff3ff;
@@ -474,7 +474,7 @@
 		: "r" ((way << 13) | addr));
 
 		taglo = ((unsigned long long)taglohi << 32) | taglolo;
-		pa = (taglo & 0xFFFFFFE000) | addr;
+		pa = (taglo & 0xFFFFFFE000ULL) | addr;
 		if (way == 0) {
 			lru = (taghi >> 14) & 0xff;
 			prom_printf("[Bank %d Set 0x%02x]  LRU > %d %d %d %d > MRU\n",
diff -Nur linux-2.4.30/arch/mips/mm/c-r4k.c linux-2.4.30-linux-mips.org/arch/mips/mm/c-r4k.c
--- linux-2.4.30/arch/mips/mm/c-r4k.c	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/mm/c-r4k.c	2005-05-12 17:57:06.000000000 +0200
@@ -867,9 +867,16 @@
 	 * normally they'd suffer from aliases but magic in the hardware deals
 	 * with that for us so we don't need to take care ourselves.
 	 */
-	if (c->cputype != CPU_R10000 && c->cputype != CPU_R12000)
-		if (c->dcache.waysize > PAGE_SIZE)
-		        c->dcache.flags |= MIPS_CACHE_ALIASES;
+	switch (c->cputype) {
+	case CPU_R10000:
+	case CPU_R12000:
+		break;
+	case CPU_24K:
+		if (!(read_c0_config7() & (1 << 16)))
+	default:
+			if (c->dcache.waysize > PAGE_SIZE)
+				c->dcache.flags |= MIPS_CACHE_ALIASES;
+	}
 
 	switch (c->cputype) {
 	case CPU_20KC:
@@ -1069,9 +1076,6 @@
 	probe_pcache();
 	setup_scache();
 
-	if (c->dcache.sets * c->dcache.ways > PAGE_SIZE)
-		c->dcache.flags |= MIPS_CACHE_ALIASES;
-
 	r4k_blast_dcache_page_setup();
 	r4k_blast_dcache_page_indexed_setup();
 	r4k_blast_dcache_setup();
diff -Nur linux-2.4.30/arch/mips/mm/tlbex-mips32.S linux-2.4.30-linux-mips.org/arch/mips/mm/tlbex-mips32.S
--- linux-2.4.30/arch/mips/mm/tlbex-mips32.S	2004-02-18 14:36:30.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/mm/tlbex-mips32.S	2005-05-12 17:57:06.000000000 +0200
@@ -196,7 +196,7 @@
 	.set	noat; \
 	SAVE_ALL; \
 	mfc0	a2, CP0_BADVADDR; \
-	STI; \
+	KMODE; \
 	.set	at; \
 	move	a0, sp; \
 	jal	do_page_fault; \
diff -Nur linux-2.4.30/arch/mips/mm/tlbex-r4k.S linux-2.4.30-linux-mips.org/arch/mips/mm/tlbex-r4k.S
--- linux-2.4.30/arch/mips/mm/tlbex-r4k.S	2004-02-18 14:36:30.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/mm/tlbex-r4k.S	2005-05-12 17:57:06.000000000 +0200
@@ -184,13 +184,10 @@
 	P_MTC0	k0, CP0_ENTRYLO0		# load it
 	PTE_SRL	k1, k1, 6			# convert to entrylo1
 	P_MTC0	k1, CP0_ENTRYLO1		# load it
-	b	1f
-	rm9000_tlb_hazard
+	mtc0_tlbw_hazard
 	tlbwr					# write random tlb entry
-1:
-	nop
-	rm9000_tlb_hazard
-	eret					# return from trap
+	tlbw_eret_hazard
+	eret
 	END(except_vec0_r4000)
 
 	/* TLB refill, EXL == 0, R4600 version */
@@ -468,13 +465,10 @@
 	PTE_PRESENT(k0, k1, nopage_tlbl)
 	PTE_MAKEVALID(k0, k1)
 	PTE_RELOAD(k1, k0)
-	rm9000_tlb_hazard
-	nop
-	b	1f
-	 tlbwi
-1:
+	mtc0_tlbw_hazard
+	tlbwi
 	nop
-	rm9000_tlb_hazard
+	tlbw_eret_hazard
 	.set	mips3
 	eret
 	.set	mips0
@@ -496,13 +490,10 @@
 	PTE_WRITABLE(k0, k1, nopage_tlbs)
 	PTE_MAKEWRITE(k0, k1)
 	PTE_RELOAD(k1, k0)
-	rm9000_tlb_hazard
-	nop
-	b	1f
-	 tlbwi
-1:
+	mtc0_tlbw_hazard
+	tlbwi
 	nop
-	rm9000_tlb_hazard
+	tlbw_eret_hazard
 	.set	mips3
 	eret
 	.set	mips0
@@ -529,13 +520,10 @@
 
 	/* Now reload the entry into the tlb. */
 	PTE_RELOAD(k1, k0)
-	rm9000_tlb_hazard
-	nop
-	b	1f
-	 tlbwi
-1:
-	rm9000_tlb_hazard
+	mtc0_tlbw_hazard
+	tlbwi
 	nop
+	tlbw_eret_hazard
 	.set	mips3
 	eret
 	.set	mips0
diff -Nur linux-2.4.30/arch/mips/mm/tlb-r4k.c linux-2.4.30-linux-mips.org/arch/mips/mm/tlb-r4k.c
--- linux-2.4.30/arch/mips/mm/tlb-r4k.c	2005-01-19 15:09:29.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/mm/tlb-r4k.c	2005-05-12 17:57:06.000000000 +0200
@@ -3,17 +3,12 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * r4xx0.c: R4000 processor variant specific MMU/Cache routines.
- *
  * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
  * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ralf@gnu.org
- *
- * To do:
- *
- *  - this code is a overbloated pig
- *  - many of the bug workarounds are not efficient at all, but at
- *    least they are functional ...
+ * Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 2002 MIPS Technologies, Inc.  All rights reserved.
  */
+#include <linux/config.h>
 #include <linux/init.h>
 #include <linux/sched.h>
 #include <linux/mm.h>
@@ -25,9 +20,6 @@
 #include <asm/pgtable.h>
 #include <asm/system.h>
 
-#undef DEBUG_TLB
-#undef DEBUG_TLBUPDATE
-
 extern char except_vec0_nevada, except_vec0_r4000, except_vec0_r4600;
 
 /* CP0 hazard avoidance. */
@@ -41,33 +33,23 @@
 	unsigned long old_ctx;
 	int entry;
 
-#ifdef DEBUG_TLB
-	printk("[tlball]");
-#endif
-
 	local_irq_save(flags);
 	/* Save old context and create impossible VPN2 value */
 	old_ctx = read_c0_entryhi();
 	write_c0_entrylo0(0);
 	write_c0_entrylo1(0);
-	BARRIER;
 
 	entry = read_c0_wired();
 
 	/* Blast 'em all away. */
 	while (entry < current_cpu_data.tlbsize) {
-		/*
-		 * Make sure all entries differ.  If they're not different
-		 * MIPS32 will take revenge ...
-		 */
 		write_c0_entryhi(KSEG0 + entry*0x2000);
 		write_c0_index(entry);
-		BARRIER;
+		mtc0_tlbw_hazard();
 		tlb_write_indexed();
-		BARRIER;
 		entry++;
 	}
-	BARRIER;
+	tlbw_use_hazard();
 	write_c0_entryhi(old_ctx);
 	local_irq_restore(flags);
 }
@@ -76,12 +58,8 @@
 {
 	int cpu = smp_processor_id();
 
-	if (cpu_context(cpu, mm) != 0) {
-#ifdef DEBUG_TLB
-		printk("[tlbmm<%d>]", cpu_context(cpu, mm));
-#endif
+	if (cpu_context(cpu, mm) != 0)
 		drop_mmu_context(mm,cpu);
-	}
 }
 
 void local_flush_tlb_range(struct mm_struct *mm, unsigned long start,
@@ -93,10 +71,6 @@
 		unsigned long flags;
 		int size;
 
-#ifdef DEBUG_TLB
-		printk("[tlbrange<%02x,%08lx,%08lx>]",
-		       cpu_asid(cpu, mm), start, end);
-#endif
 		local_irq_save(flags);
 		size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
 		size = (size + 1) >> 1;
@@ -112,7 +86,7 @@
 
 				write_c0_entryhi(start | newpid);
 				start += (PAGE_SIZE << 1);
-				BARRIER;
+				mtc0_tlbw_hazard();
 				tlb_probe();
 				BARRIER;
 				idx = read_c0_index();
@@ -122,10 +96,10 @@
 					continue;
 				/* Make sure all entries differ. */
 				write_c0_entryhi(KSEG0 + idx*0x2000);
-				BARRIER;
+				mtc0_tlbw_hazard();
 				tlb_write_indexed();
-				BARRIER;
 			}
+			tlbw_use_hazard();
 			write_c0_entryhi(oldpid);
 		} else {
 			drop_mmu_context(mm, cpu);
@@ -138,34 +112,30 @@
 {
 	int cpu = smp_processor_id();
 
-	if (!vma || cpu_context(cpu, vma->vm_mm) != 0) {
+	if (cpu_context(cpu, vma->vm_mm) != 0) {
 		unsigned long flags;
-		int oldpid, newpid, idx;
+		unsigned long oldpid, newpid, idx;
 
-#ifdef DEBUG_TLB
-		printk("[tlbpage<%d,%08lx>]", cpu_context(cpu, vma->vm_mm),
-		       page);
-#endif
 		newpid = cpu_asid(cpu, vma->vm_mm);
 		page &= (PAGE_MASK << 1);
 		local_irq_save(flags);
 		oldpid = read_c0_entryhi();
 		write_c0_entryhi(page | newpid);
-		BARRIER;
+		mtc0_tlbw_hazard();
 		tlb_probe();
 		BARRIER;
 		idx = read_c0_index();
 		write_c0_entrylo0(0);
 		write_c0_entrylo1(0);
-		if(idx < 0)
+		if (idx < 0)
 			goto finish;
 		/* Make sure all entries differ. */
 		write_c0_entryhi(KSEG0+idx*0x2000);
-		BARRIER;
+		mtc0_tlbw_hazard();
 		tlb_write_indexed();
+		tlbw_use_hazard();
 
 	finish:
-		BARRIER;
 		write_c0_entryhi(oldpid);
 		local_irq_restore(flags);
 	}
@@ -185,7 +155,7 @@
 
 	local_irq_save(flags);
 	write_c0_entryhi(page);
-	BARRIER;
+	mtc0_tlbw_hazard();
 	tlb_probe();
 	BARRIER;
 	idx = read_c0_index();
@@ -194,18 +164,19 @@
 	if (idx >= 0) {
 		/* Make sure all entries differ. */
 		write_c0_entryhi(KSEG0+idx*0x2000);
+		mtc0_tlbw_hazard();
 		tlb_write_indexed();
+		tlbw_use_hazard();
 	}
-	BARRIER;
 	write_c0_entryhi(oldpid);
+
 	local_irq_restore(flags);
 }
 
 EXPORT_SYMBOL(local_flush_tlb_one);
 
-/* We will need multiple versions of update_mmu_cache(), one that just
- * updates the TLB with the new pte(s), and another which also checks
- * for the R4k "end of page" hardware bug and does the needy.
+/*
+ * Updates the TLB with the new pte(s).
  */
 void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte)
 {
@@ -223,25 +194,16 @@
 
 	pid = read_c0_entryhi() & ASID_MASK;
 
-#ifdef DEBUG_TLB
-	if ((pid != cpu_asid(cpu, vma->vm_mm)) ||
-	    (cpu_context(vma->vm_mm) == 0)) {
-		printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%d "
-		       "tlbpid=%d\n", (int) (cpu_asid(cpu, vma->vm_mm)), pid);
-	}
-#endif
-
 	local_irq_save(flags);
 	address &= (PAGE_MASK << 1);
 	write_c0_entryhi(address | pid);
 	pgdp = pgd_offset(vma->vm_mm, address);
-	BARRIER;
+	mtc0_tlbw_hazard();
 	tlb_probe();
 	BARRIER;
 	pmdp = pmd_offset(pgdp, address);
 	idx = read_c0_index();
 	ptep = pte_offset(pmdp, address);
-	BARRIER;
 #if defined(CONFIG_64BIT_PHYS_ADDR) && defined(CONFIG_CPU_MIPS32)
 	write_c0_entrylo0(ptep->pte_high);
 	ptep++;
@@ -251,15 +213,13 @@
 	write_c0_entrylo1(pte_val(*ptep) >> 6);
 #endif
 	write_c0_entryhi(address | pid);
-	BARRIER;
-	if (idx < 0) {
+	mtc0_tlbw_hazard();
+	if (idx < 0)
 		tlb_write_random();
-	} else {
+	else
 		tlb_write_indexed();
-	}
-	BARRIER;
+	tlbw_use_hazard();
 	write_c0_entryhi(pid);
-	BARRIER;
 	local_irq_restore(flags);
 }
 
@@ -279,24 +239,26 @@
 	asid = read_c0_entryhi() & ASID_MASK;
 	write_c0_entryhi(address | asid);
 	pgdp = pgd_offset(vma->vm_mm, address);
+	mtc0_tlbw_hazard();
 	tlb_probe();
+	BARRIER;
 	pmdp = pmd_offset(pgdp, address);
 	idx = read_c0_index();
 	ptep = pte_offset(pmdp, address);
 	write_c0_entrylo0(pte_val(*ptep++) >> 6);
 	write_c0_entrylo1(pte_val(*ptep) >> 6);
-	BARRIER;
+	mtc0_tlbw_hazard();
 	if (idx < 0)
 		tlb_write_random();
 	else
 		tlb_write_indexed();
-	BARRIER;
+	tlbw_use_hazard();
 	local_irq_restore(flags);
 }
 #endif
 
 void __init add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
-		     unsigned long entryhi, unsigned long pagemask)
+			    unsigned long entryhi, unsigned long pagemask)
 {
 	unsigned long flags;
 	unsigned long wired;
@@ -315,9 +277,9 @@
 	write_c0_entryhi(entryhi);
 	write_c0_entrylo0(entrylo0);
 	write_c0_entrylo1(entrylo1);
-	BARRIER;
+	mtc0_tlbw_hazard();
 	tlb_write_indexed();
-	BARRIER;
+	tlbw_use_hazard();
 
 	write_c0_entryhi(old_ctx);
 	BARRIER;
@@ -355,17 +317,15 @@
 	}
 
 	write_c0_index(temp_tlb_entry);
-	BARRIER;
 	write_c0_pagemask(pagemask);
 	write_c0_entryhi(entryhi);
 	write_c0_entrylo0(entrylo0);
 	write_c0_entrylo1(entrylo1);
-	BARRIER;
+	mtc0_tlbw_hazard();
 	tlb_write_indexed();
-	BARRIER;
+	tlbw_use_hazard();
 
 	write_c0_entryhi(old_ctx);
-	BARRIER;
 	write_c0_pagemask(old_pagemask);
 out:
 	local_irq_restore(flags);
@@ -375,7 +335,7 @@
 static void __init probe_tlb(unsigned long config)
 {
 	struct cpuinfo_mips *c = &current_cpu_data;
-	unsigned int reg;
+	unsigned int config1;
 
 	/*
 	 * If this isn't a MIPS32 / MIPS64 compliant CPU.  Config 1 register
@@ -385,16 +345,16 @@
 	if ((c->processor_id  & 0xff0000) == PRID_COMP_LEGACY)
 		return;
 
-	reg = read_c0_config1();
+	config1 = read_c0_config1();
 	if (!((config >> 7) & 3))
 		panic("No TLB present");
 
-	c->tlbsize = ((reg >> 25) & 0x3f) + 1;
+	c->tlbsize = ((config1 >> 25) & 0x3f) + 1;
 }
 
 void __init r4k_tlb_init(void)
 {
-	u32 config = read_c0_config();
+	unsigned int config = read_c0_config();
 
 	/*
 	 * You should never change this register:
diff -Nur linux-2.4.30/arch/mips/zboot/common/au1k_uart.c linux-2.4.30-linux-mips.org/arch/mips/zboot/common/au1k_uart.c
--- linux-2.4.30/arch/mips/zboot/common/au1k_uart.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/common/au1k_uart.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,103 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *	Simple Au1000 uart routines.
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *		ppopov@mvista.com or source@mvista.com
+ *
+ *  This program is free software; you can redistribute	 it and/or modify it
+ *  under  the terms of	 the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the	License, or (at your
+ *  option) any later version.
+ *
+ *  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.
+ */
+#include <linux/config.h>
+#include <asm/io.h>
+#include <asm/au1000.h>
+#include "ns16550.h"
+
+typedef         unsigned char uint8;
+typedef         unsigned int  uint32;
+
+#define         UART16550_BAUD_2400             2400
+#define         UART16550_BAUD_4800             4800
+#define         UART16550_BAUD_9600             9600
+#define         UART16550_BAUD_19200            19200
+#define         UART16550_BAUD_38400            38400
+#define         UART16550_BAUD_57600            57600
+#define         UART16550_BAUD_115200           115200
+
+#define         UART16550_PARITY_NONE           0
+#define         UART16550_PARITY_ODD            0x08
+#define         UART16550_PARITY_EVEN           0x18
+#define         UART16550_PARITY_MARK           0x28
+#define         UART16550_PARITY_SPACE          0x38
+
+#define         UART16550_DATA_5BIT             0x0
+#define         UART16550_DATA_6BIT             0x1
+#define         UART16550_DATA_7BIT             0x2
+#define         UART16550_DATA_8BIT             0x3
+
+#define         UART16550_STOP_1BIT             0x0
+#define         UART16550_STOP_2BIT             0x4
+
+/* It would be nice if we had a better way to do this.
+ * It could be a variable defined in one of the board specific files.
+ */
+#undef UART_BASE
+#ifdef CONFIG_COGENT_CSB250
+#define UART_BASE UART3_ADDR
+#else
+#define UART_BASE UART0_ADDR
+#endif
+
+/* memory-mapped read/write of the port */
+#define UART16550_READ(y)    (readl(UART_BASE + y) & 0xff)
+#define UART16550_WRITE(y,z) (writel(z&0xff, UART_BASE + y))
+
+/*
+ * We use uart 0, which is already initialized by
+ * yamon. 
+ */
+volatile struct NS16550 *
+serial_init(int chan)
+{
+	volatile struct NS16550 *com_port;
+	com_port = (struct NS16550 *) UART_BASE;
+	return (com_port);
+}
+
+void
+serial_putc(volatile struct NS16550 *com_port, unsigned char c)
+{
+	while ((UART16550_READ(UART_LSR)&0x40) == 0);
+	UART16550_WRITE(UART_TX, c);
+}
+
+unsigned char
+serial_getc(volatile struct NS16550 *com_port)
+{
+	while((UART16550_READ(UART_LSR) & 0x1) == 0);
+	return UART16550_READ(UART_RX);
+}
+
+int
+serial_tstc(volatile struct NS16550 *com_port)
+{
+	return((UART16550_READ(UART_LSR) & LSR_DR) != 0);
+}
diff -Nur linux-2.4.30/arch/mips/zboot/common/ctype.c linux-2.4.30-linux-mips.org/arch/mips/zboot/common/ctype.c
--- linux-2.4.30/arch/mips/zboot/common/ctype.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/common/ctype.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,35 @@
+/*
+ *  linux/lib/ctype.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+#include <linux/ctype.h>
+
+unsigned char _ctype[] = {
+_C,_C,_C,_C,_C,_C,_C,_C,			/* 0-7 */
+_C,_C|_S,_C|_S,_C|_S,_C|_S,_C|_S,_C,_C,		/* 8-15 */
+_C,_C,_C,_C,_C,_C,_C,_C,			/* 16-23 */
+_C,_C,_C,_C,_C,_C,_C,_C,			/* 24-31 */
+_S|_SP,_P,_P,_P,_P,_P,_P,_P,			/* 32-39 */
+_P,_P,_P,_P,_P,_P,_P,_P,			/* 40-47 */
+_D,_D,_D,_D,_D,_D,_D,_D,			/* 48-55 */
+_D,_D,_P,_P,_P,_P,_P,_P,			/* 56-63 */
+_P,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U|_X,_U,	/* 64-71 */
+_U,_U,_U,_U,_U,_U,_U,_U,			/* 72-79 */
+_U,_U,_U,_U,_U,_U,_U,_U,			/* 80-87 */
+_U,_U,_U,_P,_P,_P,_P,_P,			/* 88-95 */
+_P,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L|_X,_L,	/* 96-103 */
+_L,_L,_L,_L,_L,_L,_L,_L,			/* 104-111 */
+_L,_L,_L,_L,_L,_L,_L,_L,			/* 112-119 */
+_L,_L,_L,_P,_P,_P,_P,_C,			/* 120-127 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 128-143 */
+0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,		/* 144-159 */
+_S|_SP,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,   /* 160-175 */
+_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,_P,       /* 176-191 */
+_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,_U,       /* 192-207 */
+_U,_U,_U,_U,_U,_U,_U,_P,_U,_U,_U,_U,_U,_U,_U,_L,       /* 208-223 */
+_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,_L,       /* 224-239 */
+_L,_L,_L,_L,_L,_L,_L,_P,_L,_L,_L,_L,_L,_L,_L,_L};      /* 240-255 */
+
+
diff -Nur linux-2.4.30/arch/mips/zboot/common/dummy.c linux-2.4.30-linux-mips.org/arch/mips/zboot/common/dummy.c
--- linux-2.4.30/arch/mips/zboot/common/dummy.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/common/dummy.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,4 @@
+int main(void)
+{
+	return 0;
+}
diff -Nur linux-2.4.30/arch/mips/zboot/common/Makefile linux-2.4.30-linux-mips.org/arch/mips/zboot/common/Makefile
--- linux-2.4.30/arch/mips/zboot/common/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/common/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,27 @@
+#
+# arch/mips/zboot/common/Makefile
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+#
+# Tom Rini	January 2001
+#
+
+.c.s:
+	$(CC) $(CFLAGS) -S -o $*.s $<
+.s.o:
+	$(AS) -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) -c -o $*.o $<
+.S.s:
+	$(CPP) $(AFLAGS) -o $*.o $<
+.S.o:
+	$(CC) $(AFLAGS) -c -o $*.o $<
+
+clean:
+	rm -rf *.o
+
+OBJCOPY_ARGS = -O elf32-tradlittlemips
+
+include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/arch/mips/zboot/common/misc-common.c linux-2.4.30-linux-mips.org/arch/mips/zboot/common/misc-common.c
--- linux-2.4.30/arch/mips/zboot/common/misc-common.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/common/misc-common.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,437 @@
+/*
+ * arch/mips/zboot/common/misc-common.c
+ * 
+ * Misc. bootloader code (almost) all platforms can use
+ *
+ * Author: Johnnie Peters <jpeters@mvista.com>
+ * Editor: Tom Rini <trini@mvista.com>
+ *
+ * Derived from arch/ppc/boot/prep/misc.c
+ *
+ * Ported by Pete Popov <ppopov@mvista.com> to
+ * support mips board(s).  I also got rid of the vga console
+ * code.
+ *
+ * Copyright 2000-2001 MontaVista Software Inc.
+ *
+ * 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.
+ */
+
+#include <linux/config.h>
+#include "zlib.h"
+#include <stdarg.h>
+
+extern char *avail_ram;
+extern char *end_avail;
+extern char _end[];
+
+void puts(const char *);
+void putc(const char c);
+void puthex(unsigned long val);
+void _bcopy(char *src, char *dst, int len);
+void gunzip(void *, int, unsigned char *, int *);
+static int _cvt(unsigned long val, char *buf, long radix, char *digits);
+
+void _vprintk(void(*)(const char), const char *, va_list ap);
+
+struct NS16550 *com_port;
+
+int serial_tstc(volatile struct NS16550 *);
+unsigned char serial_getc(volatile struct NS16550 *);
+void serial_putc(volatile struct NS16550 *, unsigned char);
+
+void pause(void)
+{
+	puts("pause\n");
+}
+
+void exit(void)
+{
+	puts("exit\n");
+	while(1); 
+}
+
+int tstc(void)
+{
+	return (serial_tstc(com_port));
+}
+
+int getc(void)
+{
+	while (1) {
+		if (serial_tstc(com_port))
+			return (serial_getc(com_port));
+	}
+}
+
+void 
+putc(const char c)
+{
+	int x,y;
+
+	serial_putc(com_port, c);
+	if ( c == '\n' )
+		serial_putc(com_port, '\r');
+}
+
+void puts(const char *s)
+{
+	char c;
+	while ( ( c = *s++ ) != '\0' ) {
+	        serial_putc(com_port, c);
+	        if ( c == '\n' ) serial_putc(com_port, '\r');
+	}
+}
+
+void error(char *x)
+{
+	puts("\n\n");
+	puts(x);
+	puts("\n\n -- System halted");
+
+	while(1);	/* Halt */
+}
+
+void *zalloc(void *x, unsigned items, unsigned size)
+{
+	void *p = avail_ram;
+	
+	size *= items;
+	size = (size + 7) & -8;
+	avail_ram += size;
+	if (avail_ram > end_avail) {
+		puts("oops... out of memory\n");
+		pause();
+	}
+	return p;
+}
+
+void zfree(void *x, void *addr, unsigned nb)
+{
+}
+
+#define HEAD_CRC	2
+#define EXTRA_FIELD	4
+#define ORIG_NAME	8
+#define COMMENT		0x10
+#define RESERVED	0xe0
+
+#define DEFLATED	8
+
+void gunzip(void *dst, int dstlen, unsigned char *src, int *lenp)
+{
+	z_stream s;
+	int r, i, flags;
+	
+	/* skip header */
+	i = 10;
+	flags = src[3];
+	if (src[2] != DEFLATED || (flags & RESERVED) != 0) {
+		puts("bad gzipped data\n");
+		exit();
+	}
+	if ((flags & EXTRA_FIELD) != 0)
+		i = 12 + src[10] + (src[11] << 8);
+	if ((flags & ORIG_NAME) != 0)
+		while (src[i++] != 0)
+			;
+	if ((flags & COMMENT) != 0)
+		while (src[i++] != 0)
+			;
+	if ((flags & HEAD_CRC) != 0)
+		i += 2;
+	if (i >= *lenp) {
+		puts("gunzip: ran out of data in header\n");
+		exit();
+	}
+	
+	s.zalloc = zalloc;
+	s.zfree = zfree;
+	r = inflateInit2(&s, -MAX_WBITS);
+	if (r != Z_OK) {
+		puts("inflateInit2 returned %d\n");
+		exit();
+	}
+	s.next_in = src + i;
+	s.avail_in = *lenp - i;
+	s.next_out = dst;
+	s.avail_out = dstlen;
+	r = inflate(&s, Z_FINISH);
+	if (r != Z_OK && r != Z_STREAM_END) {
+		puts("inflate returned %d\n");
+		exit();
+	}
+	*lenp = s.next_out - (unsigned char *) dst;
+	inflateEnd(&s);
+}
+
+void
+puthex(unsigned long val)
+{
+
+	unsigned char buf[10];
+	int i;
+	for (i = 7;  i >= 0;  i--)
+	{
+		buf[i] = "0123456789ABCDEF"[val & 0x0F];
+		val >>= 4;
+	}
+	buf[8] = '\0';
+	puts(buf);
+}
+
+#define FALSE 0
+#define TRUE  1
+
+void
+_printk(char const *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	_vprintk(putc, fmt, ap);
+	va_end(ap);
+	return;
+}
+
+#define is_digit(c) ((c >= '0') && (c <= '9'))
+
+void
+_vprintk(void(*putc)(const char), const char *fmt0, va_list ap)
+{
+	char c, sign, *cp = 0;
+	int left_prec, right_prec, zero_fill, length = 0, pad, pad_on_right;
+	char buf[32];
+	long val;
+	while ((c = *fmt0++))
+	{
+		if (c == '%')
+		{
+			c = *fmt0++;
+			left_prec = right_prec = pad_on_right = 0;
+			if (c == '-')
+			{
+				c = *fmt0++;
+				pad_on_right++;
+			}
+			if (c == '0')
+			{
+				zero_fill = TRUE;
+				c = *fmt0++;
+			} else
+			{
+				zero_fill = FALSE;
+			}
+			while (is_digit(c))
+			{
+				left_prec = (left_prec * 10) + (c - '0');
+				c = *fmt0++;
+			}
+			if (c == '.')
+			{
+				c = *fmt0++;
+				zero_fill++;
+				while (is_digit(c))
+				{
+					right_prec = (right_prec * 10) + (c - '0');
+					c = *fmt0++;
+				}
+			} else
+			{
+				right_prec = left_prec;
+			}
+			sign = '\0';
+			switch (c)
+			{
+			case 'd':
+			case 'x':
+			case 'X':
+				val = va_arg(ap, long);
+				switch (c)
+				{
+				case 'd':
+					if (val < 0)
+					{
+						sign = '-';
+						val = -val;
+					}
+					length = _cvt(val, buf, 10, "0123456789");
+					break;
+				case 'x':
+					length = _cvt(val, buf, 16, "0123456789abcdef");
+					break;
+				case 'X':
+					length = _cvt(val, buf, 16, "0123456789ABCDEF");
+					break;
+				}
+				cp = buf;
+				break;
+			case 's':
+				cp = va_arg(ap, char *);
+				length = strlen(cp);
+				break;
+			case 'c':
+				c = va_arg(ap, long /*char*/);
+				(*putc)(c);
+				continue;
+			default:
+				(*putc)('?');
+			}
+			pad = left_prec - length;
+			if (sign != '\0')
+			{
+				pad--;
+			}
+			if (zero_fill)
+			{
+				c = '0';
+				if (sign != '\0')
+				{
+					(*putc)(sign);
+					sign = '\0';
+				}
+			} else
+			{
+				c = ' ';
+			}
+			if (!pad_on_right)
+			{
+				while (pad-- > 0)
+				{
+					(*putc)(c);
+				}
+			}
+			if (sign != '\0')
+			{
+				(*putc)(sign);
+			}
+			while (length-- > 0)
+			{
+				(*putc)(c = *cp++);
+				if (c == '\n')
+				{
+					(*putc)('\r');
+				}
+			}
+			if (pad_on_right)
+			{
+				while (pad-- > 0)
+				{
+					(*putc)(c);
+				}
+			}
+		} else
+		{
+			(*putc)(c);
+			if (c == '\n')
+			{
+				(*putc)('\r');
+			}
+		}
+	}
+}
+
+int
+_cvt(unsigned long val, char *buf, long radix, char *digits)
+{
+	char temp[80];
+	char *cp = temp;
+	int length = 0;
+	if (val == 0)
+	{ /* Special case */
+		*cp++ = '0';
+	} else
+		while (val)
+		{
+			*cp++ = digits[val % radix];
+			val /= radix;
+		}
+	while (cp != temp)
+	{
+		*buf++ = *--cp;
+		length++;
+	}
+	*buf = '\0';
+	return (length);
+}
+
+void
+_dump_buf_with_offset(unsigned char *p, int s, unsigned char *base)
+{
+	int i, c;
+	if ((unsigned int)s > (unsigned int)p)
+	{
+		s = (unsigned int)s - (unsigned int)p;
+	}
+	while (s > 0)
+	{
+		if (base)
+		{
+			_printk("%06X: ", (int)p - (int)base);
+		} else
+		{
+			_printk("%06X: ", p);
+		}
+		for (i = 0;  i < 16;  i++)
+		{
+			if (i < s)
+			{
+				_printk("%02X", p[i] & 0xFF);
+			} else
+			{
+				_printk("  ");
+			}
+			if ((i % 2) == 1) _printk(" ");
+			if ((i % 8) == 7) _printk(" ");
+		}
+		_printk(" |");
+		for (i = 0;  i < 16;  i++)
+		{
+			if (i < s)
+			{
+				c = p[i] & 0xFF;
+				if ((c < 0x20) || (c >= 0x7F)) c = '.';
+			} else
+			{
+				c = ' ';
+			}
+			_printk("%c", c);
+		}
+		_printk("|\n");
+		s -= 16;
+		p += 16;
+	}
+}
+
+void
+_dump_buf(unsigned char *p, int s)
+{
+	_printk("\n");
+	_dump_buf_with_offset(p, s, 0);
+}
+
+/*
+ * Local variables:
+ *  c-indent-level: 8
+ *  c-basic-offset: 8
+ *  tab-width: 8
+ * End:
+ */
diff -Nur linux-2.4.30/arch/mips/zboot/common/misc-simple.c linux-2.4.30-linux-mips.org/arch/mips/zboot/common/misc-simple.c
--- linux-2.4.30/arch/mips/zboot/common/misc-simple.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/common/misc-simple.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,127 @@
+/*
+ * arch/mips/zboot/common/misc-simple.c
+ *
+ * Misc. bootloader code for many machines.  This assumes you have are using
+ * a 6xx/7xx/74xx CPU in your machine.  This assumes the chunk of memory
+ * below 8MB is free.  Finally, it assumes you have a NS16550-style uart for 
+ * your serial console.  If a machine meets these requirements, it can quite
+ * likely use this code during boot.
+ * 
+ * Author: Matt Porter <mporter@mvista.com>
+ * Derived from arch/ppc/boot/prep/misc.c
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/types.h>
+#include <linux/elf.h>
+#include <linux/config.h>
+
+#include <asm/page.h>
+#include <asm/processor.h>
+#include <asm/mmu.h>
+
+#include "zlib.h"
+
+extern struct NS16550 *com_port;
+
+char *avail_ram;
+char *end_avail;
+extern char _end[];
+char *zimage_start;
+
+#ifdef CONFIG_CMDLINE
+#define CMDLINE CONFIG_CMDLINE
+#else
+#define CMDLINE ""
+#endif
+char cmd_preset[] = CMDLINE;
+char cmd_buf[256];
+char *cmd_line = cmd_buf;
+
+/* The linker tells us where the image is.
+*/
+extern unsigned char __image_begin, __image_end;
+extern unsigned char __ramdisk_begin, __ramdisk_end;
+unsigned long initrd_size;
+
+extern void puts(const char *);
+extern void putc(const char c);
+extern void puthex(unsigned long val);
+extern void *memcpy(void * __dest, __const void * __src,
+			    __kernel_size_t __n);
+extern void gunzip(void *, int, unsigned char *, int *);
+extern void udelay(long delay);
+extern int tstc(void);
+extern int getc(void);
+extern volatile struct NS16550 *serial_init(int chan);
+
+void
+decompress_kernel(unsigned long load_addr, int num_words, 
+		unsigned long cksum, unsigned long *sp)
+{
+	int timer = 0;
+	extern unsigned long start;
+	char *cp, ch;
+	int i;
+	int	zimage_size;
+
+	com_port = (struct NS16550 *)serial_init(0);
+
+	initrd_size = (unsigned long)(&__ramdisk_end) -
+		(unsigned long)(&__ramdisk_begin);
+
+	/*
+	 * Reveal where we were loaded at and where we
+	 * were relocated to.
+	 */
+	puts("loaded at:     "); puthex(load_addr);
+	puts(" "); puthex((unsigned long)(load_addr + (4*num_words))); puts("\n");
+	if ( (unsigned long)load_addr != (unsigned long)&start )
+	{
+		puts("relocated to:  "); puthex((unsigned long)&start);
+		puts(" ");
+		puthex((unsigned long)((unsigned long)&start + (4*num_words)));
+		puts("\n");
+	}
+
+	/*
+	 * We link ourself to an arbitrary low address.  When we run, we
+	 * relocate outself to that address.  __image_being points to
+	 * the part of the image where the zImage is. -- Tom
+	 */
+	zimage_start = (char *)(unsigned long)(&__image_begin);
+	zimage_size = (unsigned long)(&__image_end) -
+			(unsigned long)(&__image_begin);
+
+	/*
+	 * The zImage and initrd will be between start and _end, so they've
+	 * already been moved once.  We're good to go now. -- Tom
+	 */
+	puts("zimage at:     "); puthex((unsigned long)zimage_start);
+	puts(" "); puthex((unsigned long)(zimage_size+zimage_start));
+	puts("\n");
+
+	if ( initrd_size ) {
+		puts("initrd at:     ");
+		puthex((unsigned long)(&__ramdisk_begin));
+		puts(" "); puthex((unsigned long)(&__ramdisk_end));puts("\n");
+	}
+
+	/* assume the chunk below 8M is free */
+	avail_ram = (char *)AVAIL_RAM_START;
+	end_avail = (char *)AVAIL_RAM_END;
+
+	/* Display standard Linux/MIPS boot prompt for kernel args */
+	puts("Uncompressing Linux at load address ");
+	puthex(LOADADDR);
+	puts("\n");
+	/* I don't like this hard coded gunzip size (fixme) */
+	gunzip((void *)LOADADDR, 0x400000, zimage_start, &zimage_size);
+	puts("Now booting the kernel\n");
+}
diff -Nur linux-2.4.30/arch/mips/zboot/common/no_initrd.c linux-2.4.30-linux-mips.org/arch/mips/zboot/common/no_initrd.c
--- linux-2.4.30/arch/mips/zboot/common/no_initrd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/common/no_initrd.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,2 @@
+char initrd_data[1];
+int initrd_len = 0;
diff -Nur linux-2.4.30/arch/mips/zboot/common/ns16550.c linux-2.4.30-linux-mips.org/arch/mips/zboot/common/ns16550.c
--- linux-2.4.30/arch/mips/zboot/common/ns16550.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/common/ns16550.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,57 @@
+/*
+ * NS16550 support
+ */
+
+#include <linux/config.h>
+#include <asm/serial.h>
+#include "ns16550.h"
+
+typedef struct NS16550 *NS16550_t;
+
+const NS16550_t COM_PORTS[] = { (NS16550_t) COM1,
+    (NS16550_t) COM2,
+    (NS16550_t) COM3,
+    (NS16550_t) COM4 };
+
+volatile struct NS16550 *
+serial_init(int chan)
+{
+	volatile struct NS16550 *com_port;
+	com_port = (struct NS16550 *) COM_PORTS[chan];
+	/* See if port is present */
+	com_port->lcr = 0x00;
+	com_port->ier = 0xFF;
+#if 0
+	if (com_port->ier != 0x0F) return ((struct NS16550 *)0);
+#endif
+	com_port->ier = 0x00;
+	com_port->lcr = 0x80;  /* Access baud rate */
+#ifdef CONFIG_SERIAL_CONSOLE_NONSTD
+	com_port->dll = (BASE_BAUD / CONFIG_SERIAL_CONSOLE_BAUD);
+	com_port->dlm = (BASE_BAUD / CONFIG_SERIAL_CONSOLE_BAUD) >> 8;
+#endif
+	com_port->lcr = 0x03;  /* 8 data, 1 stop, no parity */
+	com_port->mcr = 0x03;  /* RTS/DTR */
+	com_port->fcr = 0x07;  /* Clear & enable FIFOs */
+	return (com_port);
+}
+
+void
+serial_putc(volatile struct NS16550 *com_port, unsigned char c)
+{
+	while ((com_port->lsr & LSR_THRE) == 0) ;
+	com_port->thr = c;
+}
+
+unsigned char
+serial_getc(volatile struct NS16550 *com_port)
+{
+	while ((com_port->lsr & LSR_DR) == 0) ;
+	return (com_port->rbr);
+}
+
+int
+serial_tstc(volatile struct NS16550 *com_port)
+{
+	return ((com_port->lsr & LSR_DR) != 0);
+}
diff -Nur linux-2.4.30/arch/mips/zboot/common/string.c linux-2.4.30-linux-mips.org/arch/mips/zboot/common/string.c
--- linux-2.4.30/arch/mips/zboot/common/string.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/common/string.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,497 @@
+/*
+ *  linux/lib/string.c
+ *
+ *  Copyright (C) 1991, 1992  Linus Torvalds
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ *
+ * * Fri Jun 25 1999, Ingo Oeser <ioe@informatik.tu-chemnitz.de>
+ * -  Added strsep() which will replace strtok() soon (because strsep() is
+ *    reentrant and should be faster). Use only strsep() in new code, please.
+ */
+ 
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ctype.h>
+
+/**
+ * strnicmp - Case insensitive, length-limited string comparison
+ * @s1: One string
+ * @s2: The other string
+ * @len: the maximum number of characters to compare
+ */
+int strnicmp(const char *s1, const char *s2, size_t len)
+{
+	/* Yes, Virginia, it had better be unsigned */
+	unsigned char c1, c2;
+
+	c1 = 0;	c2 = 0;
+	if (len) {
+		do {
+			c1 = *s1; c2 = *s2;
+			s1++; s2++;
+			if (!c1)
+				break;
+			if (!c2)
+				break;
+			if (c1 == c2)
+				continue;
+			c1 = tolower(c1);
+			c2 = tolower(c2);
+			if (c1 != c2)
+				break;
+		} while (--len);
+	}
+	return (int)c1 - (int)c2;
+}
+
+char * ___strtok;
+
+#ifndef __HAVE_ARCH_STRCPY
+/**
+ * strcpy - Copy a %NUL terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ */
+char * strcpy(char * dest,const char *src)
+{
+	char *tmp = dest;
+
+	while ((*dest++ = *src++) != '\0')
+		/* nothing */;
+	return tmp;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNCPY
+/**
+ * strncpy - Copy a length-limited, %NUL-terminated string
+ * @dest: Where to copy the string to
+ * @src: Where to copy the string from
+ * @count: The maximum number of bytes to copy
+ *
+ * Note that unlike userspace strncpy, this does not %NUL-pad the buffer.
+ * However, the result is not %NUL-terminated if the source exceeds
+ * @count bytes.
+ */
+char * strncpy(char * dest,const char *src,size_t count)
+{
+	char *tmp = dest;
+
+	while (count-- && (*dest++ = *src++) != '\0')
+		/* nothing */;
+
+	return tmp;
+}
+#endif
+
+/**
+ * strcat - Append one %NUL-terminated string to another
+ * @dest: The string to be appended to
+ * @src: The string to append to it
+ */
+char * strcat(char * dest, const char * src)
+{
+	char *tmp = dest;
+
+	while (*dest)
+		dest++;
+	while ((*dest++ = *src++) != '\0')
+		;
+
+	return tmp;
+}
+
+/**
+ * strncat - Append a length-limited, %NUL-terminated string to another
+ * @dest: The string to be appended to
+ * @src: The string to append to it
+ * @count: The maximum numbers of bytes to copy
+ *
+ * Note that in contrast to strncpy, strncat ensures the result is
+ * terminated.
+ */
+char * strncat(char *dest, const char *src, size_t count)
+{
+	char *tmp = dest;
+
+	if (count) {
+		while (*dest)
+			dest++;
+		while ((*dest++ = *src++)) {
+			if (--count == 0) {
+				*dest = '\0';
+				break;
+			}
+		}
+	}
+
+	return tmp;
+}
+
+#ifndef __HAVE_ARCH_STRCMP
+/**
+ * strcmp - Compare two strings
+ * @cs: One string
+ * @ct: Another string
+ */
+int strcmp(const char * cs,const char * ct)
+{
+	register signed char __res;
+
+	while (1) {
+		if ((__res = *cs - *ct++) != 0 || !*cs++)
+			break;
+	}
+
+	return __res;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRNCMP
+/**
+ * strncmp - Compare two length-limited strings
+ * @cs: One string
+ * @ct: Another string
+ * @count: The maximum number of bytes to compare
+ */
+int strncmp(const char * cs,const char * ct,size_t count)
+{
+	register signed char __res = 0;
+
+	while (count) {
+		if ((__res = *cs - *ct++) != 0 || !*cs++)
+			break;
+		count--;
+	}
+
+	return __res;
+}
+#endif
+
+/**
+ * strchr - Find the first occurrence of a character in a string
+ * @s: The string to be searched
+ * @c: The character to search for
+ */
+char * strchr(const char * s, int c)
+{
+	for(; *s != (char) c; ++s)
+		if (*s == '\0')
+			return NULL;
+	return (char *) s;
+}
+
+/**
+ * strrchr - Find the last occurrence of a character in a string
+ * @s: The string to be searched
+ * @c: The character to search for
+ */
+char * strrchr(const char * s, int c)
+{
+       const char *p = s + strlen(s);
+       do {
+           if (*p == (char)c)
+               return (char *)p;
+       } while (--p >= s);
+       return NULL;
+}
+
+/**
+ * strlen - Find the length of a string
+ * @s: The string to be sized
+ */
+size_t strlen(const char * s)
+{
+	const char *sc;
+
+	for (sc = s; *sc != '\0'; ++sc)
+		/* nothing */;
+	return sc - s;
+}
+
+/**
+ * strnlen - Find the length of a length-limited string
+ * @s: The string to be sized
+ * @count: The maximum number of bytes to search
+ */
+size_t strnlen(const char * s, size_t count)
+{
+	const char *sc;
+
+	for (sc = s; count-- && *sc != '\0'; ++sc)
+		/* nothing */;
+	return sc - s;
+}
+
+/**
+ * strspn - Calculate the length of the initial substring of @s which only
+ * 	contain letters in @accept
+ * @s: The string to be searched
+ * @accept: The string to search for
+ */
+size_t strspn(const char *s, const char *accept)
+{
+	const char *p;
+	const char *a;
+	size_t count = 0;
+
+	for (p = s; *p != '\0'; ++p) {
+		for (a = accept; *a != '\0'; ++a) {
+			if (*p == *a)
+				break;
+		}
+		if (*a == '\0')
+			return count;
+		++count;
+	}
+
+	return count;
+}
+
+/**
+ * strpbrk - Find the first occurrence of a set of characters
+ * @cs: The string to be searched
+ * @ct: The characters to search for
+ */
+char * strpbrk(const char * cs,const char * ct)
+{
+	const char *sc1,*sc2;
+
+	for( sc1 = cs; *sc1 != '\0'; ++sc1) {
+		for( sc2 = ct; *sc2 != '\0'; ++sc2) {
+			if (*sc1 == *sc2)
+				return (char *) sc1;
+		}
+	}
+	return NULL;
+}
+
+/**
+ * strtok - Split a string into tokens
+ * @s: The string to be searched
+ * @ct: The characters to search for
+ *
+ * WARNING: strtok is deprecated, use strsep instead.
+ */
+char * strtok(char * s,const char * ct)
+{
+	char *sbegin, *send;
+
+	sbegin  = s ? s : ___strtok;
+	if (!sbegin) {
+		return NULL;
+	}
+	sbegin += strspn(sbegin,ct);
+	if (*sbegin == '\0') {
+		___strtok = NULL;
+		return( NULL );
+	}
+	send = strpbrk( sbegin, ct);
+	if (send && *send != '\0')
+		*send++ = '\0';
+	___strtok = send;
+	return (sbegin);
+}
+
+/**
+ * strsep - Split a string into tokens
+ * @s: The string to be searched
+ * @ct: The characters to search for
+ *
+ * strsep() updates @s to point after the token, ready for the next call.
+ *
+ * It returns empty tokens, too, behaving exactly like the libc function
+ * of that name. In fact, it was stolen from glibc2 and de-fancy-fied.
+ * Same semantics, slimmer shape. ;)
+ */
+char * strsep(char **s, const char *ct)
+{
+	char *sbegin = *s, *end;
+
+	if (sbegin == NULL)
+		return NULL;
+
+	end = strpbrk(sbegin, ct);
+	if (end)
+		*end++ = '\0';
+	*s = end;
+
+	return sbegin;
+}
+
+/**
+ * memset - Fill a region of memory with the given value
+ * @s: Pointer to the start of the area.
+ * @c: The byte to fill the area with
+ * @count: The size of the area.
+ *
+ * Do not use memset() to access IO space, use memset_io() instead.
+ */
+void * memset(void * s,int c, size_t count)
+{
+	char *xs = (char *) s;
+
+	while (count--)
+		*xs++ = c;
+
+	return s;
+}
+
+/**
+ * bcopy - Copy one area of memory to another
+ * @src: Where to copy from
+ * @dest: Where to copy to
+ * @count: The size of the area.
+ *
+ * Note that this is the same as memcpy(), with the arguments reversed.
+ * memcpy() is the standard, bcopy() is a legacy BSD function.
+ *
+ * You should not use this function to access IO space, use memcpy_toio()
+ * or memcpy_fromio() instead.
+ */
+char * bcopy(const char * src, char * dest, int count)
+{
+	char *tmp = dest;
+
+	while (count--)
+		*tmp++ = *src++;
+
+	return dest;
+}
+
+/**
+ * memcpy - Copy one area of memory to another
+ * @dest: Where to copy to
+ * @src: Where to copy from
+ * @count: The size of the area.
+ *
+ * You should not use this function to access IO space, use memcpy_toio()
+ * or memcpy_fromio() instead.
+ */
+void * memcpy(void * dest,const void *src,size_t count)
+{
+	char *tmp = (char *) dest, *s = (char *) src;
+
+	while (count--)
+		*tmp++ = *s++;
+
+	return dest;
+}
+
+/**
+ * memmove - Copy one area of memory to another
+ * @dest: Where to copy to
+ * @src: Where to copy from
+ * @count: The size of the area.
+ *
+ * Unlike memcpy(), memmove() copes with overlapping areas.
+ */
+void * memmove(void * dest,const void *src,size_t count)
+{
+	char *tmp, *s;
+
+	if (dest <= src) {
+		tmp = (char *) dest;
+		s = (char *) src;
+		while (count--)
+			*tmp++ = *s++;
+		}
+	else {
+		tmp = (char *) dest + count;
+		s = (char *) src + count;
+		while (count--)
+			*--tmp = *--s;
+		}
+
+	return dest;
+}
+
+/**
+ * memcmp - Compare two areas of memory
+ * @cs: One area of memory
+ * @ct: Another area of memory
+ * @count: The size of the area.
+ */
+int memcmp(const void * cs,const void * ct,size_t count)
+{
+	const unsigned char *su1, *su2;
+	signed char res = 0;
+
+	for( su1 = cs, su2 = ct; 0 < count; ++su1, ++su2, count--)
+		if ((res = *su1 - *su2) != 0)
+			break;
+	return res;
+}
+
+#ifndef __HAVE_ARCH_MEMSCAN
+/**
+ * memscan - Find a character in an area of memory.
+ * @addr: The memory area
+ * @c: The byte to search for
+ * @size: The size of the area.
+ *
+ * returns the address of the first occurrence of @c, or 1 byte past
+ * the area if @c is not found
+ */
+void * memscan(void * addr, int c, size_t size)
+{
+	unsigned char * p = (unsigned char *) addr;
+	unsigned char * e = p + size;
+
+	while (p != e) {
+		if (*p == c)
+			return (void *) p;
+		p++;
+	}
+
+	return (void *) p;
+}
+#endif
+
+/**
+ * strstr - Find the first substring in a %NUL terminated string
+ * @s1: The string to be searched
+ * @s2: The string to search for
+ */
+char * strstr(const char * s1,const char * s2)
+{
+	int l1, l2;
+
+	l2 = strlen(s2);
+	if (!l2)
+		return (char *) s1;
+	l1 = strlen(s1);
+	while (l1 >= l2) {
+		l1--;
+		if (!memcmp(s1,s2,l2))
+			return (char *) s1;
+		s1++;
+	}
+	return NULL;
+}
+
+/**
+ * memchr - Find a character in an area of memory.
+ * @s: The memory area
+ * @c: The byte to search for
+ * @n: The size of the area.
+ *
+ * returns the address of the first occurrence of @c, or %NULL
+ * if @c is not found
+ */
+void *memchr(const void *s, int c, size_t n)
+{
+	const unsigned char *p = s;
+	while (n-- != 0) {
+        	if ((unsigned char)c == *p++) {
+			return (void *)(p-1);
+		}
+	}
+	return NULL;
+}
diff -Nur linux-2.4.30/arch/mips/zboot/csb250/head.S linux-2.4.30-linux-mips.org/arch/mips/zboot/csb250/head.S
--- linux-2.4.30/arch/mips/zboot/csb250/head.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/csb250/head.S	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,157 @@
+/*
+ * arch/mips/kernel/head.S
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1994, 1995 Waldorf Electronics
+ * Written by Ralf Baechle and Andreas Busse
+ * Copyright (C) 1995 - 1999 Ralf Baechle
+ * Copyright (C) 1996 Paul M. Antoine
+ * Modified for DECStation and hence R3000 support by Paul M. Antoine
+ * Further modifications by David S. Miller and Harald Koerfgen
+ * Copyright (C) 1999 Silicon Graphics, Inc.
+ *
+ * Head.S contains the MIPS exception handler and startup code.
+ *
+ **************************************************************************
+ *  9 Nov, 2000.
+ *  Added Cache Error exception handler and SBDDP EJTAG debug exception.
+ *
+ *  Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
+ *  Copyright (C) 2000 MIPS Technologies, Inc.  All rights reserved.
+ **************************************************************************
+ */
+#include <linux/config.h>
+#include <linux/threads.h>
+
+#include <asm/asm.h>
+#include <asm/cacheops.h>
+#include <asm/mipsregs.h>
+#include <asm/offset.h>
+#include <asm/cachectl.h>
+#include <asm/regdef.h>
+
+#define IndexInvalidate_I       0x00
+#define IndexWriteBack_D        0x01
+
+	.set noreorder
+	.cprestore
+	LEAF(start)
+start:
+	bal	locate
+	nop
+
+	.globl	asize		/* Someday we'll put the initrd info here. */
+asize:	.word	0
+	.word	0
+	.word	0
+	.word	0
+
+locate:
+	subu	s8, ra, 8	/* Where we were loaded */
+	la	sp, (.stack + 8192)
+
+	move	s0, a0		/* Save boot rom start args */
+	move	s1, a1
+	move	s2, a2
+	move	s3, a3
+
+	la	a0, start	/* Where we were linked to run */
+
+	move	a1, s8
+	la	a2, _edata
+	subu	t1, a2, a0
+	srl	t1, t1, 2
+
+	/* copy text section */
+	li	t0, 0
+1:	lw	v0, 0(a1)
+	nop
+	sw	v0, 0(a0)
+	xor	t0, t0, v0
+	addu	a0, 4
+	bne	a2, a0, 1b
+	addu	a1, 4
+
+	/* Clear BSS */
+	la	a0, _edata
+	la	a2, _end
+2:	sw	zero, 0(a0)
+	bne	a2, a0, 2b
+	addu	a0, 4
+
+	/* push the D-Cache and invalidate I-Cache */
+	li	k0, 0x80000000  # start address
+	li	k1, 0x80004000  # end address (16KB I-Cache)
+	subu	k1, 128
+
+1:
+	.set mips3
+	cache	IndexWriteBack_D, 0(k0)
+	cache	IndexWriteBack_D, 32(k0)
+	cache	IndexWriteBack_D, 64(k0)
+	cache	IndexWriteBack_D, 96(k0)
+	cache	IndexInvalidate_I, 0(k0)
+	cache	IndexInvalidate_I, 32(k0)
+	cache	IndexInvalidate_I, 64(k0)
+	cache	IndexInvalidate_I, 96(k0)
+	.set mips0
+
+	bne	k0, k1, 1b
+	addu	k0, k0, 128
+	/* done */
+
+/*	move	a0, s8		     /* load address */
+	subu	a0, s8, 0x1000		     /* load address */
+	move	a1, t1               /* length in words */
+	move	a2, t0               /* checksum */
+	move	a3, sp
+
+	la	ra, 1f
+	la	k0, decompress_kernel
+	jr	k0
+	nop
+1:
+
+	la	a2, __ramdisk_begin
+	la	a3, initrd_size
+	lw	a0, 0(a2)
+	lw	a1, 0(a3)
+	li	k0, KERNEL_ENTRY
+	jr	k0
+	nop
+3:
+	b 3b
+	END(start)
+
+	LEAF(udelay)
+udelay:
+	END(udelay)
+
+
+	LEAF(FlushCache)
+	li	k0, 0x80000000  # start address
+	li	k1, 0x80004000  # end address (16KB I-Cache)
+	subu	k1, 128
+
+1:
+	.set mips3
+	cache	IndexWriteBack_D, 0(k0)
+	cache	IndexWriteBack_D, 32(k0)
+	cache	IndexWriteBack_D, 64(k0)
+	cache	IndexWriteBack_D, 96(k0)
+	cache	IndexInvalidate_I, 0(k0)
+	cache	IndexInvalidate_I, 32(k0)
+	cache	IndexInvalidate_I, 64(k0)
+	cache	IndexInvalidate_I, 96(k0)
+	.set mips0
+
+	bne	k0, k1, 1b
+	addu	k0, k0, 128
+	jr	ra
+	nop
+	END(FlushCache)
+
+	.comm .stack,4096*2,4
diff -Nur linux-2.4.30/arch/mips/zboot/csb250/Makefile linux-2.4.30-linux-mips.org/arch/mips/zboot/csb250/Makefile
--- linux-2.4.30/arch/mips/zboot/csb250/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/csb250/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,90 @@
+# arch/mips/zboot/pb1xxx/Makefile
+# 
+# Makefile for Cogent CSB250 Au1500 board.
+# All of the boot loader code was derived from the ppc
+# boot code.
+#
+# 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.
+
+.c.s:
+	$(CC) $(CFLAGS) -S -o $*.s $<
+.s.o:
+	$(AS) -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) -D__BOOTER__ -c -o $*.o $<
+.S.s:
+	$(CPP) $(AFLAGS) -o $*.o $<
+.S.o:
+	$(CC) $(AFLAGS) -c -o $*.o $<
+
+#########################################################################
+# START BOARD SPECIFIC VARIABLES
+BNAME=csb250
+
+# These two variables control where the zImage is stored
+# in flash and loaded in memory.  It only controls how the srec
+# file is generated, the code is the same.
+RAM_RUN_ADDR = 0x80a00000
+FLASH_LOAD_ADDR = 0xBFD00000
+
+# These two variables specify the free ram region
+# that can be used for temporary malloc area
+AVAIL_RAM_START=0x80400000
+AVAIL_RAM_END=0x80800000
+
+# This one must match the LOADADDR in arch/mips/Makefile!
+LOADADDR=0x80100000
+# END BOARD SPECIFIC VARIABLES
+#########################################################################
+
+OBJECTS := head.o ../common/misc-common.o ../common/misc-simple.o \
+	../common/au1k_uart.o ../common/string.o ../common/ctype.o
+LIBS := ../lib/zlib.a
+
+ENTRY := ../utils/entry
+OFFSET := ../utils/offset
+SIZE := ../utils/size
+
+LD_ARGS := -T ../ld.script -Ttext $(RAM_RUN_ADDR) -Bstatic
+OBJCOPY_ARGS = -O elf32-tradbigmips
+
+all: zImage
+
+clean:
+	rm -rf *.o vmlinux* zvmlinux.* ../images/*.srec
+
+head.o: head.S $(TOPDIR)/vmlinux
+	$(CC) $(AFLAGS) \
+	-DKERNEL_ENTRY=$(shell sh $(ENTRY) $(NM) $(TOPDIR)/vmlinux ) \
+	-c -o $*.o $<
+
+../common/misc-simple.o:
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 -DZIMAGE_OFFSET=0 \
+		-DAVAIL_RAM_START=$(AVAIL_RAM_START) \
+		-DAVAIL_RAM_END=$(AVAIL_RAM_END) \
+		-DLOADADDR=$(LOADADDR) \
+		-DZIMAGE_SIZE=0 -c -o $@ $*.c
+
+zvmlinux: $(OBJECTS) $(LIBS) ../ld.script ../images/vmlinux.gz ../common/dummy.o
+	$(OBJCOPY) \
+		--add-section=.image=../images/vmlinux.gz \
+		--set-section-flags=.image=contents,alloc,load,readonly,data \
+		../common/dummy.o image.o
+	$(LD) $(LD_ARGS) -o $@ $(OBJECTS) image.o $(LIBS)
+	$(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab -R .stabstr \
+		-R .initrd -R .sysmap
+
+# Here we manipulate the image in order to get it the necessary
+# srecord file we need.
+zImage: zvmlinux
+	mv zvmlinux ../images/zImage.$(BNAME)
+	$(OBJCOPY) -O binary ../images/zImage.$(BNAME) ../images/$(BNAME).bin
+
+zImage.flash: zImage
+	$(OBJCOPY) -O srec --adjust-vma 0x3ed00000 \
+		../images/zImage.$(BNAME) ../images/$(BNAME).flash.srec
+
+include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/arch/mips/zboot/images/Makefile linux-2.4.30-linux-mips.org/arch/mips/zboot/images/Makefile
--- linux-2.4.30/arch/mips/zboot/images/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/images/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,10 @@
+
+include $(TOPDIR)/Rules.make
+
+vmlinux.gz: $(TOPDIR)/vmlinux
+	$(OBJCOPY) -S -O binary $(TOPDIR)/vmlinux vmlinux
+	gzip -vf vmlinux
+
+clean:
+	rm -f vmlinux.*  zImage.*
+
diff -Nur linux-2.4.30/arch/mips/zboot/include/nonstdio.h linux-2.4.30-linux-mips.org/arch/mips/zboot/include/nonstdio.h
--- linux-2.4.30/arch/mips/zboot/include/nonstdio.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/include/nonstdio.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,18 @@
+/*
+ * Copyright (C) Paul Mackerras 1997.
+ *
+ * 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.
+ */
+typedef int	FILE;
+extern FILE *stdin, *stdout;
+#define NULL	((void *)0)
+#define EOF	(-1)
+#define fopen(n, m)	NULL
+#define fflush(f)	0
+#define fclose(f)	0
+extern char *fgets();
+
+#define perror(s)	printf("%s: no files!\n", (s))
diff -Nur linux-2.4.30/arch/mips/zboot/include/ns16550.h linux-2.4.30-linux-mips.org/arch/mips/zboot/include/ns16550.h
--- linux-2.4.30/arch/mips/zboot/include/ns16550.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/include/ns16550.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,49 @@
+/*
+ * NS16550 Serial Port
+ */
+
+/*
+ * Figure out which file will have the definitons of COMx
+ */
+#if !defined(CONFIG_AU1X00_UART)
+#error no serial.h
+#endif
+
+/* Some machines have their uart registers 16 bytes apart.  Most don't.
+ * TODO: Make this work like drivers/char/serial does - Tom */
+#if !defined(UART_REG_PAD)
+#define UART_REG_PAD(x)
+#endif
+
+struct NS16550
+ {
+  unsigned char rbr;  /* 0 */
+  UART_REG_PAD(rbr)
+  unsigned char ier;  /* 1 */
+  UART_REG_PAD(ier)
+  unsigned char fcr;  /* 2 */
+  UART_REG_PAD(fcr)
+  unsigned char lcr;  /* 3 */
+  UART_REG_PAD(lcr)
+  unsigned char mcr;  /* 4 */
+  UART_REG_PAD(mcr)
+  unsigned char lsr;  /* 5 */
+  UART_REG_PAD(lsr)
+  unsigned char msr;  /* 6 */
+  UART_REG_PAD(msr)
+  unsigned char scr;  /* 7 */
+ };
+
+#define thr rbr
+#define iir fcr
+#define dll rbr
+#define dlm ier
+
+#define LSR_DR   0x01  /* Data ready */
+#define LSR_OE   0x02  /* Overrun */
+#define LSR_PE   0x04  /* Parity error */
+#define LSR_FE   0x08  /* Framing error */
+#define LSR_BI   0x10  /* Break */
+#define LSR_THRE 0x20  /* Xmit holding register empty */
+#define LSR_TEMT 0x40  /* Xmitter empty */
+#define LSR_ERR  0x80  /* Error */
diff -Nur linux-2.4.30/arch/mips/zboot/include/pb1000_serial.h linux-2.4.30-linux-mips.org/arch/mips/zboot/include/pb1000_serial.h
--- linux-2.4.30/arch/mips/zboot/include/pb1000_serial.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/include/pb1000_serial.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,20 @@
+/*
+ * arch/ppc/boot/include/sandpoint_serial.h
+ * 
+ * Location of the COM ports on Motorola SPS Sandpoint machines
+ *
+ * Author: Mark A. Greer
+ * 	   mgreer@mvista.com
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ *
+ * 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.
+ */
+
+#define COM1 0xfe0003f8
+#define COM2 0xfe0002f8
+#define COM3 0x00000000		/* No COM3 */
+#define COM4 0x00000000		/* No COM4 */
diff -Nur linux-2.4.30/arch/mips/zboot/include/zlib.h linux-2.4.30-linux-mips.org/arch/mips/zboot/include/zlib.h
--- linux-2.4.30/arch/mips/zboot/include/zlib.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/include/zlib.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,432 @@
+/*	$Id: zlib.h,v 1.2 2002/02/16 16:55:45 ppopov Exp $	*/
+
+/*
+ * This file is derived from zlib.h and zconf.h from the zlib-0.95
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets.
+ */
+
+/*
+ *  ==FILEVERSION 960122==
+ *
+ * This marker is used by the Linux installation script to determine
+ * whether an up-to-date version of this file is already installed.
+ */
+
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+  version 0.95, Aug 16th, 1995.
+
+  Copyright (C) 1995 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  gzip@prep.ai.mit.edu    madler@alumni.caltech.edu
+ */
+
+#ifndef _ZLIB_H
+#define _ZLIB_H
+
+/* #include "zconf.h" */	/* included directly here */
+
+/* zconf.h -- configuration of the zlib compression library
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* From: zconf.h,v 1.12 1995/05/03 17:27:12 jloup Exp */
+
+/*
+     The library does not install any signal handler. It is recommended to
+  add at least a handler for SIGSEGV when decompressing; the library checks
+  the consistency of the input data whenever possible but may go nuts
+  for some forms of corrupted input.
+ */
+
+/*
+ * Compile with -DMAXSEG_64K if the alloc function cannot allocate more
+ * than 64k bytes at a time (needed on systems with 16-bit int).
+ * Compile with -DUNALIGNED_OK if it is OK to access shorts or ints
+ * at addresses which are not a multiple of their size.
+ * Under DOS, -DFAR=far or -DFAR=__far may be needed.
+ */
+
+#ifndef STDC
+#  if defined(MSDOS) || defined(__STDC__) || defined(__cplusplus)
+#    define STDC
+#  endif
+#endif
+
+#ifdef	__MWERKS__ /* Metrowerks CodeWarrior declares fileno() in unix.h */
+#  include <unix.h>
+#endif
+
+/* Maximum value for memLevel in deflateInit2 */
+#ifndef MAX_MEM_LEVEL
+#  ifdef MAXSEG_64K
+#    define MAX_MEM_LEVEL 8
+#  else
+#    define MAX_MEM_LEVEL 9
+#  endif
+#endif
+
+#ifndef FAR
+#  define FAR
+#endif
+
+/* Maximum value for windowBits in deflateInit2 and inflateInit2 */
+#ifndef MAX_WBITS
+#  define MAX_WBITS   15 /* 32K LZ77 window */
+#endif
+
+/* The memory requirements for deflate are (in bytes):
+            1 << (windowBits+2)   +  1 << (memLevel+9)
+ that is: 128K for windowBits=15  +  128K for memLevel = 8  (default values)
+ plus a few kilobytes for small objects. For example, if you want to reduce
+ the default memory requirements from 256K to 128K, compile with
+     make CFLAGS="-O -DMAX_WBITS=14 -DMAX_MEM_LEVEL=7"
+ Of course this will generally degrade compression (there's no free lunch).
+
+   The memory requirements for inflate are (in bytes) 1 << windowBits
+ that is, 32K for windowBits=15 (default value) plus a few kilobytes
+ for small objects.
+*/
+
+                        /* Type declarations */
+
+#ifndef OF /* function prototypes */
+#  ifdef STDC
+#    define OF(args)  args
+#  else
+#    define OF(args)  ()
+#  endif
+#endif
+
+typedef unsigned char  Byte;  /* 8 bits */
+typedef unsigned int   uInt;  /* 16 bits or more */
+typedef unsigned long  uLong; /* 32 bits or more */
+
+typedef Byte FAR Bytef;
+typedef char FAR charf;
+typedef int FAR intf;
+typedef uInt FAR uIntf;
+typedef uLong FAR uLongf;
+
+#ifdef STDC
+   typedef void FAR *voidpf;
+   typedef void     *voidp;
+#else
+   typedef Byte FAR *voidpf;
+   typedef Byte     *voidp;
+#endif
+
+/* end of original zconf.h */
+
+#define ZLIB_VERSION "0.95P"
+
+/* 
+     The 'zlib' compression library provides in-memory compression and
+  decompression functions, including integrity checks of the uncompressed
+  data.  This version of the library supports only one compression method
+  (deflation) but other algorithms may be added later and will have the same
+  stream interface.
+
+     For compression the application must provide the output buffer and
+  may optionally provide the input buffer for optimization. For decompression,
+  the application must provide the input buffer and may optionally provide
+  the output buffer for optimization.
+
+     Compression can be done in a single step if the buffers are large
+  enough (for example if an input file is mmap'ed), or can be done by
+  repeated calls of the compression function.  In the latter case, the
+  application must provide more input and/or consume the output
+  (providing more output space) before each call.
+*/
+
+typedef voidpf (*alloc_func) OF((voidpf opaque, uInt items, uInt size));
+typedef void   (*free_func)  OF((voidpf opaque, voidpf address, uInt nbytes));
+
+struct internal_state;
+
+typedef struct z_stream_s {
+    Bytef    *next_in;  /* next input byte */
+    uInt     avail_in;  /* number of bytes available at next_in */
+    uLong    total_in;  /* total nb of input bytes read so far */
+
+    Bytef    *next_out; /* next output byte should be put there */
+    uInt     avail_out; /* remaining free space at next_out */
+    uLong    total_out; /* total nb of bytes output so far */
+
+    char     *msg;      /* last error message, NULL if no error */
+    struct internal_state FAR *state; /* not visible by applications */
+
+    alloc_func zalloc;  /* used to allocate the internal state */
+    free_func  zfree;   /* used to free the internal state */
+    voidp      opaque;  /* private data object passed to zalloc and zfree */
+
+    Byte     data_type; /* best guess about the data type: ascii or binary */
+
+} z_stream;
+
+/*
+   The application must update next_in and avail_in when avail_in has
+   dropped to zero. It must update next_out and avail_out when avail_out
+   has dropped to zero. The application must initialize zalloc, zfree and
+   opaque before calling the init function. All other fields are set by the
+   compression library and must not be updated by the application.
+
+   The opaque value provided by the application will be passed as the first
+   parameter for calls of zalloc and zfree. This can be useful for custom
+   memory management. The compression library attaches no meaning to the
+   opaque value.
+
+   zalloc must return Z_NULL if there is not enough memory for the object.
+   On 16-bit systems, the functions zalloc and zfree must be able to allocate
+   exactly 65536 bytes, but will not be required to allocate more than this
+   if the symbol MAXSEG_64K is defined (see zconf.h). WARNING: On MSDOS,
+   pointers returned by zalloc for objects of exactly 65536 bytes *must*
+   have their offset normalized to zero. The default allocation function
+   provided by this library ensures this (see zutil.c). To reduce memory
+   requirements and avoid any allocation of 64K objects, at the expense of
+   compression ratio, compile the library with -DMAX_WBITS=14 (see zconf.h).
+
+   The fields total_in and total_out can be used for statistics or
+   progress reports. After compression, total_in holds the total size of
+   the uncompressed data and may be saved for use in the decompressor
+   (particularly if the decompressor wants to decompress everything in
+   a single step).
+*/
+
+                        /* constants */
+
+#define Z_NO_FLUSH      0
+#define Z_PARTIAL_FLUSH 1
+#define Z_FULL_FLUSH    2
+#define Z_SYNC_FLUSH    3 /* experimental: partial_flush + byte align */
+#define Z_FINISH        4
+#define Z_PACKET_FLUSH	5
+/* See deflate() below for the usage of these constants */
+
+#define Z_OK            0
+#define Z_STREAM_END    1
+#define Z_ERRNO        (-1)
+#define Z_STREAM_ERROR (-2)
+#define Z_DATA_ERROR   (-3)
+#define Z_MEM_ERROR    (-4)
+#define Z_BUF_ERROR    (-5)
+/* error codes for the compression/decompression functions */
+
+#define Z_BEST_SPEED             1
+#define Z_BEST_COMPRESSION       9
+#define Z_DEFAULT_COMPRESSION  (-1)
+/* compression levels */
+
+#define Z_FILTERED            1
+#define Z_HUFFMAN_ONLY        2
+#define Z_DEFAULT_STRATEGY    0
+
+#define Z_BINARY   0
+#define Z_ASCII    1
+#define Z_UNKNOWN  2
+/* Used to set the data_type field */
+
+#define Z_NULL  0  /* for initializing zalloc, zfree, opaque */
+
+extern char *zlib_version;
+/* The application can compare zlib_version and ZLIB_VERSION for consistency.
+   If the first character differs, the library code actually used is
+   not compatible with the zlib.h header file used by the application.
+ */
+
+                        /* basic functions */
+
+extern int inflateInit OF((z_stream *strm));
+/* 
+     Initializes the internal stream state for decompression. The fields
+   zalloc and zfree must be initialized before by the caller.  If zalloc and
+   zfree are set to Z_NULL, inflateInit updates them to use default allocation
+   functions.
+
+     inflateInit returns Z_OK if success, Z_MEM_ERROR if there was not
+   enough memory.  msg is set to null if there is no error message.
+   inflateInit does not perform any decompression: this will be done by
+   inflate().
+*/
+
+
+extern int inflate OF((z_stream *strm, int flush));
+/*
+  Performs one or both of the following actions:
+
+  - Decompress more input starting at next_in and update next_in and avail_in
+    accordingly. If not all input can be processed (because there is not
+    enough room in the output buffer), next_in is updated and processing
+    will resume at this point for the next call of inflate().
+
+  - Provide more output starting at next_out and update next_out and avail_out
+    accordingly.  inflate() always provides as much output as possible
+    (until there is no more input data or no more space in the output buffer).
+
+  Before the call of inflate(), the application should ensure that at least
+  one of the actions is possible, by providing more input and/or consuming
+  more output, and updating the next_* and avail_* values accordingly.
+  The application can consume the uncompressed output when it wants, for
+  example when the output buffer is full (avail_out == 0), or after each
+  call of inflate().
+
+    If the parameter flush is set to Z_PARTIAL_FLUSH or Z_PACKET_FLUSH,
+  inflate flushes as much output as possible to the output buffer. The
+  flushing behavior of inflate is not specified for values of the flush
+  parameter other than Z_PARTIAL_FLUSH, Z_PACKET_FLUSH or Z_FINISH, but the
+  current implementation actually flushes as much output as possible
+  anyway.  For Z_PACKET_FLUSH, inflate checks that once all the input data
+  has been consumed, it is expecting to see the length field of a stored
+  block; if not, it returns Z_DATA_ERROR.
+
+    inflate() should normally be called until it returns Z_STREAM_END or an
+  error. However if all decompression is to be performed in a single step
+  (a single call of inflate), the parameter flush should be set to
+  Z_FINISH. In this case all pending input is processed and all pending
+  output is flushed; avail_out must be large enough to hold all the
+  uncompressed data. (The size of the uncompressed data may have been saved
+  by the compressor for this purpose.) The next operation on this stream must
+  be inflateEnd to deallocate the decompression state. The use of Z_FINISH
+  is never required, but can be used to inform inflate that a faster routine
+  may be used for the single inflate() call.
+
+    inflate() returns Z_OK if some progress has been made (more input
+  processed or more output produced), Z_STREAM_END if the end of the
+  compressed data has been reached and all uncompressed output has been
+  produced, Z_DATA_ERROR if the input data was corrupted, Z_STREAM_ERROR if
+  the stream structure was inconsistent (for example if next_in or next_out
+  was NULL), Z_MEM_ERROR if there was not enough memory, Z_BUF_ERROR if no
+  progress is possible or if there was not enough room in the output buffer
+  when Z_FINISH is used. In the Z_DATA_ERROR case, the application may then
+  call inflateSync to look for a good compression block.  */
+
+
+extern int inflateEnd OF((z_stream *strm));
+/*
+     All dynamically allocated data structures for this stream are freed.
+   This function discards any unprocessed input and does not flush any
+   pending output.
+
+     inflateEnd returns Z_OK if success, Z_STREAM_ERROR if the stream state
+   was inconsistent. In the error case, msg may be set but then points to a
+   static string (which must not be deallocated).
+*/
+
+                        /* advanced functions */
+
+extern int inflateInit2 OF((z_stream *strm,
+                            int  windowBits));
+/*   
+     This is another version of inflateInit with more compression options. The
+   fields next_out, zalloc and zfree must be initialized before by the caller.
+
+     The windowBits parameter is the base two logarithm of the maximum window
+   size (the size of the history buffer).  It should be in the range 8..15 for
+   this version of the library (the value 16 will be allowed soon). The
+   default value is 15 if inflateInit is used instead. If a compressed stream
+   with a larger window size is given as input, inflate() will return with
+   the error code Z_DATA_ERROR instead of trying to allocate a larger window.
+
+     If next_out is not null, the library will use this buffer for the history
+   buffer; the buffer must either be large enough to hold the entire output
+   data, or have at least 1<<windowBits bytes.  If next_out is null, the
+   library will allocate its own buffer (and leave next_out null). next_in
+   need not be provided here but must be provided by the application for the
+   next call of inflate().
+
+     If the history buffer is provided by the application, next_out must
+   never be changed by the application since the decompressor maintains
+   history information inside this buffer from call to call; the application
+   can only reset next_out to the beginning of the history buffer when
+   avail_out is zero and all output has been consumed.
+
+      inflateInit2 returns Z_OK if success, Z_MEM_ERROR if there was
+   not enough memory, Z_STREAM_ERROR if a parameter is invalid (such as
+   windowBits < 8). msg is set to null if there is no error message.
+   inflateInit2 does not perform any decompression: this will be done by
+   inflate().
+*/
+
+extern int inflateSync OF((z_stream *strm));
+/* 
+    Skips invalid compressed data until the special marker (see deflate()
+  above) can be found, or until all available input is skipped. No output
+  is provided.
+
+    inflateSync returns Z_OK if the special marker has been found, Z_BUF_ERROR
+  if no more input was provided, Z_DATA_ERROR if no marker has been found,
+  or Z_STREAM_ERROR if the stream structure was inconsistent. In the success
+  case, the application may save the current current value of total_in which
+  indicates where valid compressed data was found. In the error case, the
+  application may repeatedly call inflateSync, providing more input each time,
+  until success or end of the input data.
+*/
+
+extern int inflateReset OF((z_stream *strm));
+/*
+     This function is equivalent to inflateEnd followed by inflateInit,
+   but does not free and reallocate all the internal decompression state.
+   The stream will keep attributes that may have been set by inflateInit2.
+
+      inflateReset returns Z_OK if success, or Z_STREAM_ERROR if the source
+   stream state was inconsistent (such as zalloc or state being NULL).
+*/
+
+extern int inflateIncomp OF((z_stream *strm));
+/*
+     This function adds the data at next_in (avail_in bytes) to the output
+   history without performing any output.  There must be no pending output,
+   and the decompressor must be expecting to see the start of a block.
+   Calling this function is equivalent to decompressing a stored block
+   containing the data at next_in (except that the data is not output).
+*/
+
+                        /* checksum functions */
+
+/*
+     This function is not related to compression but is exported
+   anyway because it might be useful in applications using the
+   compression library.
+*/
+
+extern uLong adler32 OF((uLong adler, Bytef *buf, uInt len));
+
+/*
+     Update a running Adler-32 checksum with the bytes buf[0..len-1] and
+   return the updated checksum. If buf is NULL, this function returns
+   the required initial value for the checksum.
+   An Adler-32 checksum is almost as reliable as a CRC32 but can be computed
+   much faster. Usage example:
+
+     uLong adler = adler32(0L, Z_NULL, 0);
+
+     while (read_buffer(buffer, length) != EOF) {
+       adler = adler32(adler, buffer, length);
+     }
+     if (adler != original_adler) error();
+*/
+
+#ifndef _Z_UTIL_H
+    struct internal_state {int dummy;}; /* hack for buggy compilers */
+#endif
+
+#endif /* _ZLIB_H */
diff -Nur linux-2.4.30/arch/mips/zboot/ld.script linux-2.4.30-linux-mips.org/arch/mips/zboot/ld.script
--- linux-2.4.30/arch/mips/zboot/ld.script	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/ld.script	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,151 @@
+OUTPUT_ARCH(mips)
+ENTRY(start)
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  /* . = 0x81000000; */
+  .init          : { *(.init)		} =0
+  .text      :
+  {
+    _ftext = . ;
+    *(.text)
+    *(.rodata)
+    *(.rodata1)
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+  } =0
+  .kstrtab : { *(.kstrtab) }
+
+  . = ALIGN(16);		/* Exception table */
+  __start___ex_table = .;
+  __ex_table : { *(__ex_table) }
+  __stop___ex_table = .;
+
+  __start___dbe_table = .;	/* Exception table for data bus errors */
+  __dbe_table : { *(__dbe_table) }
+  __stop___dbe_table = .;
+
+  __start___ksymtab = .;	/* Kernel symbol table */
+  __ksymtab : { *(__ksymtab) }
+  __stop___ksymtab = .;
+
+  _etext = .;
+
+  . = ALIGN(8192);
+  .data.init_task : { *(.data.init_task) }
+
+  /* Startup code */
+  . = ALIGN(4096);
+  __init_begin = .;
+  .text.init : { *(.text.init) }
+  .data.init : { *(.data.init) }
+  . = ALIGN(16);
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+  __initcall_start = .;
+  .initcall.init : { *(.initcall.init) }
+  __initcall_end = .;
+  . = ALIGN(4096);	/* Align double page for init_task_union */
+  __init_end = .;
+
+  . = ALIGN(4096);
+  .data.page_aligned : { *(.data.idt) }
+
+  . = ALIGN(32);
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+  .fini      : { *(.fini)    } =0
+  .reginfo : { *(.reginfo) }
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  It would
+     be more correct to do this:
+       . = .;
+     The current expression does not correctly handle the case of a
+     text segment ending precisely at the end of a page; it causes the
+     data segment to skip a page.  The above expression does not have
+     this problem, but it will currently (2/95) cause BFD to allocate
+     a single segment, combining both text and data, for this case.
+     This will prevent the text segment from being shared among
+     multiple executions of the program; I think that is more
+     important than losing a page of the virtual address space (note
+     that no actual memory is lost; the page which is skipped can not
+     be referenced).  */
+  . = .;
+  .data    :
+  {
+    _fdata = . ;
+    *(.data)
+
+   /* Put the compressed image here, so bss is on the end. */
+   __image_begin = .;
+   *(.image)
+   __image_end = .;
+   /* Align the initial ramdisk image (INITRD) on page boundaries. */
+   . = ALIGN(4096);
+   __ramdisk_begin = .;
+   *(.initrd)
+   __ramdisk_end = .;
+   . = ALIGN(4096);
+
+    CONSTRUCTORS
+  }
+  .data1   : { *(.data1) }
+  _gp = . + 0x8000;
+  .lit8 : { *(.lit8) }
+  .lit4 : { *(.lit4) }
+  .ctors         : { *(.ctors)   }
+  .dtors         : { *(.dtors)   }
+  .got           : { *(.got.plt) *(.got) }
+  .dynamic       : { *(.dynamic) }
+  /* We want the small data sections together, so single-instruction offsets
+     can access them all, and initialized data all before uninitialized, so
+     we can shorten the on-disk segment size.  */
+  .sdata     : { *(.sdata) }
+  . = ALIGN(4);
+  _edata  =  .;
+  PROVIDE (edata = .);
+
+  __bss_start = .;
+  _fbss = .;
+  .sbss      : { *(.sbss) *(.scommon) }
+  .bss       :
+  {
+   *(.dynbss)
+   *(.bss)
+   *(COMMON)
+   .  = ALIGN(4);
+  _end = . ;
+  PROVIDE (end = .);
+  }
+
+  /* Sections to be discarded */
+  /DISCARD/ :
+  {
+        *(.text.exit)
+        *(.data.exit)
+        *(.exitcall.exit)
+  }
+
+  /* This is the MIPS specific mdebug section.  */
+  .mdebug : { *(.mdebug) }
+  /* These are needed for ELF backends which have not yet been
+     converted to the new style linker.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  /* DWARF debug sections.
+     Symbols in the .debug DWARF section are relative to the beginning of the
+     section so we begin .debug at 0.  It's not clear yet what needs to happen
+     for the others.   */
+  .debug          0 : { *(.debug) }
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  .line           0 : { *(.line) }
+  /* These must appear regardless of  .  */
+  .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
+  .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
+  .comment : { *(.comment) }
+  .note : { *(.note) }
+}
diff -Nur linux-2.4.30/arch/mips/zboot/lib/Makefile linux-2.4.30-linux-mips.org/arch/mips/zboot/lib/Makefile
--- linux-2.4.30/arch/mips/zboot/lib/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/lib/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,9 @@
+#
+# Makefile for some libs needed by zImage.
+#
+
+L_TARGET := zlib.a
+
+obj-y := zlib.o
+
+include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/arch/mips/zboot/lib/zlib.c linux-2.4.30-linux-mips.org/arch/mips/zboot/lib/zlib.c
--- linux-2.4.30/arch/mips/zboot/lib/zlib.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/lib/zlib.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,2148 @@
+/*
+ * This file is derived from various .h and .c files from the zlib-0.95
+ * distribution by Jean-loup Gailly and Mark Adler, with some additions
+ * by Paul Mackerras to aid in implementing Deflate compression and
+ * decompression for PPP packets.  See zlib.h for conditions of
+ * distribution and use.
+ *
+ * Changes that have been made include:
+ * - changed functions not used outside this file to "local"
+ * - added minCompression parameter to deflateInit2
+ * - added Z_PACKET_FLUSH (see zlib.h for details)
+ * - added inflateIncomp
+ *
+ * $Id: zlib.c,v 1.2 2002/02/16 16:55:45 ppopov Exp $
+ */
+
+/*+++++*/
+/* zutil.h -- internal interface and configuration of the compression library
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* From: zutil.h,v 1.9 1995/05/03 17:27:12 jloup Exp */
+
+#define _Z_UTIL_H
+
+#include "zlib.h"
+
+#ifndef local
+#  define local static
+#endif
+/* compile with -Dlocal if your debugger can't find static symbols */
+
+#define FAR
+
+typedef unsigned char  uch;
+typedef uch FAR uchf;
+typedef unsigned short ush;
+typedef ush FAR ushf;
+typedef unsigned long  ulg;
+
+extern char *z_errmsg[]; /* indexed by 1-zlib_error */
+
+#define ERR_RETURN(strm,err) return (strm->msg=z_errmsg[1-err], err)
+/* To be used only when the state is known to be valid */
+
+#ifndef NULL
+#define NULL	((void *) 0)
+#endif
+
+        /* common constants */
+
+#define DEFLATED   8
+
+#ifndef DEF_WBITS
+#  define DEF_WBITS MAX_WBITS
+#endif
+/* default windowBits for decompression. MAX_WBITS is for compression only */
+
+#if MAX_MEM_LEVEL >= 8
+#  define DEF_MEM_LEVEL 8
+#else
+#  define DEF_MEM_LEVEL  MAX_MEM_LEVEL
+#endif
+/* default memLevel */
+
+#define STORED_BLOCK 0
+#define STATIC_TREES 1
+#define DYN_TREES    2
+/* The three kinds of block type */
+
+#define MIN_MATCH  3
+#define MAX_MATCH  258
+/* The minimum and maximum match lengths */
+
+         /* functions */
+
+#include <linux/string.h>
+#define zmemcpy memcpy
+#define zmemzero(dest, len)	memset(dest, 0, len)
+
+/* Diagnostic functions */
+#ifdef DEBUG_ZLIB
+#  include <stdio.h>
+#  ifndef verbose
+#    define verbose 0
+#  endif
+#  define Assert(cond,msg) {if(!(cond)) z_error(msg);}
+#  define Trace(x) fprintf x
+#  define Tracev(x) {if (verbose) fprintf x ;}
+#  define Tracevv(x) {if (verbose>1) fprintf x ;}
+#  define Tracec(c,x) {if (verbose && (c)) fprintf x ;}
+#  define Tracecv(c,x) {if (verbose>1 && (c)) fprintf x ;}
+#else
+#  define Assert(cond,msg)
+#  define Trace(x)
+#  define Tracev(x)
+#  define Tracevv(x)
+#  define Tracec(c,x)
+#  define Tracecv(c,x)
+#endif
+
+
+typedef uLong (*check_func) OF((uLong check, Bytef *buf, uInt len));
+
+/* voidpf zcalloc OF((voidpf opaque, unsigned items, unsigned size)); */
+/* void   zcfree  OF((voidpf opaque, voidpf ptr)); */
+
+#define ZALLOC(strm, items, size) \
+           (*((strm)->zalloc))((strm)->opaque, (items), (size))
+#define ZFREE(strm, addr, size)	\
+	   (*((strm)->zfree))((strm)->opaque, (voidpf)(addr), (size))
+#define TRY_FREE(s, p, n) {if (p) ZFREE(s, p, n);}
+
+/* deflate.h -- internal compression state
+ * Copyright (C) 1995 Jean-loup Gailly
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/*+++++*/
+/* infblock.h -- header to use infblock.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_blocks_state;
+typedef struct inflate_blocks_state FAR inflate_blocks_statef;
+
+local inflate_blocks_statef * inflate_blocks_new OF((
+    z_stream *z,
+    check_func c,               /* check function */
+    uInt w));                   /* window size */
+
+local int inflate_blocks OF((
+    inflate_blocks_statef *,
+    z_stream *,
+    int));                      /* initial return code */
+
+local void inflate_blocks_reset OF((
+    inflate_blocks_statef *,
+    z_stream *,
+    uLongf *));                  /* check value on output */
+
+local int inflate_blocks_free OF((
+    inflate_blocks_statef *,
+    z_stream *,
+    uLongf *));                  /* check value on output */
+
+local int inflate_addhistory OF((
+    inflate_blocks_statef *,
+    z_stream *));
+
+local int inflate_packet_flush OF((
+    inflate_blocks_statef *));
+
+/*+++++*/
+/* inftrees.h -- header to use inftrees.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* Huffman code lookup table entry--this entry is four bytes for machines
+   that have 16-bit pointers (e.g. PC's in the small or medium model). */
+
+typedef struct inflate_huft_s FAR inflate_huft;
+
+struct inflate_huft_s {
+  union {
+    struct {
+      Byte Exop;        /* number of extra bits or operation */
+      Byte Bits;        /* number of bits in this code or subcode */
+    } what;
+    uInt Nalloc;	/* number of these allocated here */
+    Bytef *pad;         /* pad structure to a power of 2 (4 bytes for */
+  } word;               /*  16-bit, 8 bytes for 32-bit machines) */
+  union {
+    uInt Base;          /* literal, length base, or distance base */
+    inflate_huft *Next; /* pointer to next level of table */
+  } more;
+};
+
+#ifdef DEBUG_ZLIB
+  local uInt inflate_hufts;
+#endif
+
+local int inflate_trees_bits OF((
+    uIntf *,                    /* 19 code lengths */
+    uIntf *,                    /* bits tree desired/actual depth */
+    inflate_huft * FAR *,       /* bits tree result */
+    z_stream *));               /* for zalloc, zfree functions */
+
+local int inflate_trees_dynamic OF((
+    uInt,                       /* number of literal/length codes */
+    uInt,                       /* number of distance codes */
+    uIntf *,                    /* that many (total) code lengths */
+    uIntf *,                    /* literal desired/actual bit depth */
+    uIntf *,                    /* distance desired/actual bit depth */
+    inflate_huft * FAR *,       /* literal/length tree result */
+    inflate_huft * FAR *,       /* distance tree result */
+    z_stream *));               /* for zalloc, zfree functions */
+
+local int inflate_trees_fixed OF((
+    uIntf *,                    /* literal desired/actual bit depth */
+    uIntf *,                    /* distance desired/actual bit depth */
+    inflate_huft * FAR *,       /* literal/length tree result */
+    inflate_huft * FAR *));     /* distance tree result */
+
+local int inflate_trees_free OF((
+    inflate_huft *,             /* tables to free */
+    z_stream *));               /* for zfree function */
+
+
+/*+++++*/
+/* infcodes.h -- header to use infcodes.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+struct inflate_codes_state;
+typedef struct inflate_codes_state FAR inflate_codes_statef;
+
+local inflate_codes_statef *inflate_codes_new OF((
+    uInt, uInt,
+    inflate_huft *, inflate_huft *,
+    z_stream *));
+
+local int inflate_codes OF((
+    inflate_blocks_statef *,
+    z_stream *,
+    int));
+
+local void inflate_codes_free OF((
+    inflate_codes_statef *,
+    z_stream *));
+
+
+/*+++++*/
+/* inflate.c -- zlib interface to inflate modules
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* inflate private state */
+struct internal_state {
+
+  /* mode */
+  enum {
+      METHOD,   /* waiting for method byte */
+      FLAG,     /* waiting for flag byte */
+      BLOCKS,   /* decompressing blocks */
+      CHECK4,   /* four check bytes to go */
+      CHECK3,   /* three check bytes to go */
+      CHECK2,   /* two check bytes to go */
+      CHECK1,   /* one check byte to go */
+      DONE,     /* finished check, done */
+      BAD}      /* got an error--stay here */
+    mode;               /* current inflate mode */
+
+  /* mode dependent information */
+  union {
+    uInt method;        /* if FLAGS, method byte */
+    struct {
+      uLong was;                /* computed check value */
+      uLong need;               /* stream check value */
+    } check;            /* if CHECK, check values to compare */
+    uInt marker;        /* if BAD, inflateSync's marker bytes count */
+  } sub;        /* submode */
+
+  /* mode independent information */
+  int  nowrap;          /* flag for no wrapper */
+  uInt wbits;           /* log2(window size)  (8..15, defaults to 15) */
+  inflate_blocks_statef 
+    *blocks;            /* current inflate_blocks state */
+
+};
+
+
+int inflateReset(z)
+z_stream *z;
+{
+  uLong c;
+
+  if (z == Z_NULL || z->state == Z_NULL)
+    return Z_STREAM_ERROR;
+  z->total_in = z->total_out = 0;
+  z->msg = Z_NULL;
+  z->state->mode = z->state->nowrap ? BLOCKS : METHOD;
+  inflate_blocks_reset(z->state->blocks, z, &c);
+  Trace((stderr, "inflate: reset\n"));
+  return Z_OK;
+}
+
+
+int inflateEnd(z)
+z_stream *z;
+{
+  uLong c;
+
+  if (z == Z_NULL || z->state == Z_NULL || z->zfree == Z_NULL)
+    return Z_STREAM_ERROR;
+  if (z->state->blocks != Z_NULL)
+    inflate_blocks_free(z->state->blocks, z, &c);
+  ZFREE(z, z->state, sizeof(struct internal_state));
+  z->state = Z_NULL;
+  Trace((stderr, "inflate: end\n"));
+  return Z_OK;
+}
+
+
+int inflateInit2(z, w)
+z_stream *z;
+int w;
+{
+  /* initialize state */
+  if (z == Z_NULL)
+    return Z_STREAM_ERROR;
+/*  if (z->zalloc == Z_NULL) z->zalloc = zcalloc; */
+/*  if (z->zfree == Z_NULL) z->zfree = zcfree; */
+  if ((z->state = (struct internal_state FAR *)
+       ZALLOC(z,1,sizeof(struct internal_state))) == Z_NULL)
+    return Z_MEM_ERROR;
+  z->state->blocks = Z_NULL;
+
+  /* handle undocumented nowrap option (no zlib header or check) */
+  z->state->nowrap = 0;
+  if (w < 0)
+  {
+    w = - w;
+    z->state->nowrap = 1;
+  }
+
+  /* set window size */
+  if (w < 8 || w > 15)
+  {
+    inflateEnd(z);
+    return Z_STREAM_ERROR;
+  }
+  z->state->wbits = (uInt)w;
+
+  /* create inflate_blocks state */
+  if ((z->state->blocks =
+       inflate_blocks_new(z, z->state->nowrap ? Z_NULL : adler32, 1 << w))
+      == Z_NULL)
+  {
+    inflateEnd(z);
+    return Z_MEM_ERROR;
+  }
+  Trace((stderr, "inflate: allocated\n"));
+
+  /* reset state */
+  inflateReset(z);
+  return Z_OK;
+}
+
+
+int inflateInit(z)
+z_stream *z;
+{
+  return inflateInit2(z, DEF_WBITS);
+}
+
+
+#define NEEDBYTE {if(z->avail_in==0)goto empty;r=Z_OK;}
+#define NEXTBYTE (z->avail_in--,z->total_in++,*z->next_in++)
+
+int inflate(z, f)
+z_stream *z;
+int f;
+{
+  int r;
+  uInt b;
+
+  if (z == Z_NULL || z->next_in == Z_NULL)
+    return Z_STREAM_ERROR;
+  r = Z_BUF_ERROR;
+  while (1) switch (z->state->mode)
+  {
+    case METHOD:
+      NEEDBYTE
+      if (((z->state->sub.method = NEXTBYTE) & 0xf) != DEFLATED)
+      {
+        z->state->mode = BAD;
+        z->msg = "unknown compression method";
+        z->state->sub.marker = 5;       /* can't try inflateSync */
+        break;
+      }
+      if ((z->state->sub.method >> 4) + 8 > z->state->wbits)
+      {
+        z->state->mode = BAD;
+        z->msg = "invalid window size";
+        z->state->sub.marker = 5;       /* can't try inflateSync */
+        break;
+      }
+      z->state->mode = FLAG;
+    case FLAG:
+      NEEDBYTE
+      if ((b = NEXTBYTE) & 0x20)
+      {
+        z->state->mode = BAD;
+        z->msg = "invalid reserved bit";
+        z->state->sub.marker = 5;       /* can't try inflateSync */
+        break;
+      }
+      if (((z->state->sub.method << 8) + b) % 31)
+      {
+        z->state->mode = BAD;
+        z->msg = "incorrect header check";
+        z->state->sub.marker = 5;       /* can't try inflateSync */
+        break;
+      }
+      Trace((stderr, "inflate: zlib header ok\n"));
+      z->state->mode = BLOCKS;
+    case BLOCKS:
+      r = inflate_blocks(z->state->blocks, z, r);
+      if (f == Z_PACKET_FLUSH && z->avail_in == 0 && z->avail_out != 0)
+	  r = inflate_packet_flush(z->state->blocks);
+      if (r == Z_DATA_ERROR)
+      {
+        z->state->mode = BAD;
+        z->state->sub.marker = 0;       /* can try inflateSync */
+        break;
+      }
+      if (r != Z_STREAM_END)
+        return r;
+      r = Z_OK;
+      inflate_blocks_reset(z->state->blocks, z, &z->state->sub.check.was);
+      if (z->state->nowrap)
+      {
+        z->state->mode = DONE;
+        break;
+      }
+      z->state->mode = CHECK4;
+    case CHECK4:
+      NEEDBYTE
+      z->state->sub.check.need = (uLong)NEXTBYTE << 24;
+      z->state->mode = CHECK3;
+    case CHECK3:
+      NEEDBYTE
+      z->state->sub.check.need += (uLong)NEXTBYTE << 16;
+      z->state->mode = CHECK2;
+    case CHECK2:
+      NEEDBYTE
+      z->state->sub.check.need += (uLong)NEXTBYTE << 8;
+      z->state->mode = CHECK1;
+    case CHECK1:
+      NEEDBYTE
+      z->state->sub.check.need += (uLong)NEXTBYTE;
+
+      if (z->state->sub.check.was != z->state->sub.check.need)
+      {
+        z->state->mode = BAD;
+        z->msg = "incorrect data check";
+        z->state->sub.marker = 5;       /* can't try inflateSync */
+        break;
+      }
+      Trace((stderr, "inflate: zlib check ok\n"));
+      z->state->mode = DONE;
+    case DONE:
+      return Z_STREAM_END;
+    case BAD:
+      return Z_DATA_ERROR;
+    default:
+      return Z_STREAM_ERROR;
+  }
+
+ empty:
+  if (f != Z_PACKET_FLUSH)
+    return r;
+  z->state->mode = BAD;
+  z->state->sub.marker = 0;       /* can try inflateSync */
+  return Z_DATA_ERROR;
+}
+
+/*
+ * This subroutine adds the data at next_in/avail_in to the output history
+ * without performing any output.  The output buffer must be "caught up";
+ * i.e. no pending output (hence s->read equals s->write), and the state must
+ * be BLOCKS (i.e. we should be willing to see the start of a series of
+ * BLOCKS).  On exit, the output will also be caught up, and the checksum
+ * will have been updated if need be.
+ */
+
+int inflateIncomp(z)
+z_stream *z;
+{
+    if (z->state->mode != BLOCKS)
+	return Z_DATA_ERROR;
+    return inflate_addhistory(z->state->blocks, z);
+}
+
+
+int inflateSync(z)
+z_stream *z;
+{
+  uInt n;       /* number of bytes to look at */
+  Bytef *p;     /* pointer to bytes */
+  uInt m;       /* number of marker bytes found in a row */
+  uLong r, w;   /* temporaries to save total_in and total_out */
+
+  /* set up */
+  if (z == Z_NULL || z->state == Z_NULL)
+    return Z_STREAM_ERROR;
+  if (z->state->mode != BAD)
+  {
+    z->state->mode = BAD;
+    z->state->sub.marker = 0;
+  }
+  if ((n = z->avail_in) == 0)
+    return Z_BUF_ERROR;
+  p = z->next_in;
+  m = z->state->sub.marker;
+
+  /* search */
+  while (n && m < 4)
+  {
+    if (*p == (Byte)(m < 2 ? 0 : 0xff))
+      m++;
+    else if (*p)
+      m = 0;
+    else
+      m = 4 - m;
+    p++, n--;
+  }
+
+  /* restore */
+  z->total_in += p - z->next_in;
+  z->next_in = p;
+  z->avail_in = n;
+  z->state->sub.marker = m;
+
+  /* return no joy or set up to restart on a new block */
+  if (m != 4)
+    return Z_DATA_ERROR;
+  r = z->total_in;  w = z->total_out;
+  inflateReset(z);
+  z->total_in = r;  z->total_out = w;
+  z->state->mode = BLOCKS;
+  return Z_OK;
+}
+
+#undef NEEDBYTE
+#undef NEXTBYTE
+
+/*+++++*/
+/* infutil.h -- types and macros common to blocks and codes
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+/* inflate blocks semi-private state */
+struct inflate_blocks_state {
+
+  /* mode */
+  enum {
+      TYPE,     /* get type bits (3, including end bit) */
+      LENS,     /* get lengths for stored */
+      STORED,   /* processing stored block */
+      TABLE,    /* get table lengths */
+      BTREE,    /* get bit lengths tree for a dynamic block */
+      DTREE,    /* get length, distance trees for a dynamic block */
+      CODES,    /* processing fixed or dynamic block */
+      DRY,      /* output remaining window bytes */
+      DONEB,     /* finished last block, done */
+      BADB}      /* got a data error--stuck here */
+    mode;               /* current inflate_block mode */
+
+  /* mode dependent information */
+  union {
+    uInt left;          /* if STORED, bytes left to copy */
+    struct {
+      uInt table;               /* table lengths (14 bits) */
+      uInt index;               /* index into blens (or border) */
+      uIntf *blens;             /* bit lengths of codes */
+      uInt bb;                  /* bit length tree depth */
+      inflate_huft *tb;         /* bit length decoding tree */
+      int nblens;		/* # elements allocated at blens */
+    } trees;            /* if DTREE, decoding info for trees */
+    struct {
+      inflate_huft *tl, *td;    /* trees to free */
+      inflate_codes_statef 
+         *codes;
+    } decode;           /* if CODES, current state */
+  } sub;                /* submode */
+  uInt last;            /* true if this block is the last block */
+
+  /* mode independent information */
+  uInt bitk;            /* bits in bit buffer */
+  uLong bitb;           /* bit buffer */
+  Bytef *window;        /* sliding window */
+  Bytef *end;           /* one byte after sliding window */
+  Bytef *read;          /* window read pointer */
+  Bytef *write;         /* window write pointer */
+  check_func checkfn;   /* check function */
+  uLong check;          /* check on output */
+
+};
+
+
+/* defines for inflate input/output */
+/*   update pointers and return */
+#define UPDBITS {s->bitb=b;s->bitk=k;}
+#define UPDIN {z->avail_in=n;z->total_in+=p-z->next_in;z->next_in=p;}
+#define UPDOUT {s->write=q;}
+#define UPDATE {UPDBITS UPDIN UPDOUT}
+#define LEAVE {UPDATE return inflate_flush(s,z,r);}
+/*   get bytes and bits */
+#define LOADIN {p=z->next_in;n=z->avail_in;b=s->bitb;k=s->bitk;}
+#define NEEDBYTE {if(n)r=Z_OK;else LEAVE}
+#define NEXTBYTE (n--,*p++)
+#define NEEDBITS(j) {while(k<(j)){NEEDBYTE;b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define DUMPBITS(j) {b>>=(j);k-=(j);}
+/*   output bytes */
+#define WAVAIL (q<s->read?s->read-q-1:s->end-q)
+#define LOADOUT {q=s->write;m=WAVAIL;}
+#define WRAP {if(q==s->end&&s->read!=s->window){q=s->window;m=WAVAIL;}}
+#define FLUSH {UPDOUT r=inflate_flush(s,z,r); LOADOUT}
+#define NEEDOUT {if(m==0){WRAP if(m==0){FLUSH WRAP if(m==0) LEAVE}}r=Z_OK;}
+#define OUTBYTE(a) {*q++=(Byte)(a);m--;}
+/*   load local pointers */
+#define LOAD {LOADIN LOADOUT}
+
+/*
+ * The IBM 150 firmware munges the data right after _etext[].  This
+ * protects it. -- Cort
+ */
+local uInt protect_mask[] = {0, 0, 0, 0, 0, 0, 0, 0, 0 ,0 ,0 ,0};
+/* And'ing with mask[n] masks the lower n bits */
+local uInt inflate_mask[] = {
+    0x0000,
+    0x0001, 0x0003, 0x0007, 0x000f, 0x001f, 0x003f, 0x007f, 0x00ff,
+    0x01ff, 0x03ff, 0x07ff, 0x0fff, 0x1fff, 0x3fff, 0x7fff, 0xffff
+};
+
+/* copy as much as possible from the sliding window to the output area */
+local int inflate_flush OF((
+    inflate_blocks_statef *,
+    z_stream *,
+    int));
+
+/*+++++*/
+/* inffast.h -- header to use inffast.c
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* WARNING: this file should *not* be used by applications. It is
+   part of the implementation of the compression library and is
+   subject to change. Applications should only use zlib.h.
+ */
+
+local int inflate_fast OF((
+    uInt,
+    uInt,
+    inflate_huft *,
+    inflate_huft *,
+    inflate_blocks_statef *,
+    z_stream *));
+
+
+/*+++++*/
+/* infblock.c -- interpret and process block types to last block
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* Table for deflate from PKZIP's appnote.txt. */
+local uInt border[] = { /* Order of the bit length code lengths */
+        16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15};
+
+/*
+   Notes beyond the 1.93a appnote.txt:
+
+   1. Distance pointers never point before the beginning of the output
+      stream.
+   2. Distance pointers can point back across blocks, up to 32k away.
+   3. There is an implied maximum of 7 bits for the bit length table and
+      15 bits for the actual data.
+   4. If only one code exists, then it is encoded using one bit.  (Zero
+      would be more efficient, but perhaps a little confusing.)  If two
+      codes exist, they are coded using one bit each (0 and 1).
+   5. There is no way of sending zero distance codes--a dummy must be
+      sent if there are none.  (History: a pre 2.0 version of PKZIP would
+      store blocks with no distance codes, but this was discovered to be
+      too harsh a criterion.)  Valid only for 1.93a.  2.04c does allow
+      zero distance codes, which is sent as one code of zero bits in
+      length.
+   6. There are up to 286 literal/length codes.  Code 256 represents the
+      end-of-block.  Note however that the static length tree defines
+      288 codes just to fill out the Huffman codes.  Codes 286 and 287
+      cannot be used though, since there is no length base or extra bits
+      defined for them.  Similarily, there are up to 30 distance codes.
+      However, static trees define 32 codes (all 5 bits) to fill out the
+      Huffman codes, but the last two had better not show up in the data.
+   7. Unzip can check dynamic Huffman blocks for complete code sets.
+      The exception is that a single code would not be complete (see #4).
+   8. The five bits following the block type is really the number of
+      literal codes sent minus 257.
+   9. Length codes 8,16,16 are interpreted as 13 length codes of 8 bits
+      (1+6+6).  Therefore, to output three times the length, you output
+      three codes (1+1+1), whereas to output four times the same length,
+      you only need two codes (1+3).  Hmm.
+  10. In the tree reconstruction algorithm, Code = Code + Increment
+      only if BitLength(i) is not zero.  (Pretty obvious.)
+  11. Correction: 4 Bits: # of Bit Length codes - 4     (4 - 19)
+  12. Note: length code 284 can represent 227-258, but length code 285
+      really is 258.  The last length deserves its own, short code
+      since it gets used a lot in very redundant files.  The length
+      258 is special since 258 - 3 (the min match length) is 255.
+  13. The literal/length and distance code bit lengths are read as a
+      single stream of lengths.  It is possible (and advantageous) for
+      a repeat code (16, 17, or 18) to go across the boundary between
+      the two sets of lengths.
+ */
+
+
+local void inflate_blocks_reset(s, z, c)
+inflate_blocks_statef *s;
+z_stream *z;
+uLongf *c;
+{
+  if (s->checkfn != Z_NULL)
+    *c = s->check;
+  if (s->mode == BTREE || s->mode == DTREE)
+    ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt));
+  if (s->mode == CODES)
+  {
+    inflate_codes_free(s->sub.decode.codes, z);
+    inflate_trees_free(s->sub.decode.td, z);
+    inflate_trees_free(s->sub.decode.tl, z);
+  }
+  s->mode = TYPE;
+  s->bitk = 0;
+  s->bitb = 0;
+  s->read = s->write = s->window;
+  if (s->checkfn != Z_NULL)
+    s->check = (*s->checkfn)(0L, Z_NULL, 0);
+  Trace((stderr, "inflate:   blocks reset\n"));
+}
+
+
+local inflate_blocks_statef *inflate_blocks_new(z, c, w)
+z_stream *z;
+check_func c;
+uInt w;
+{
+  inflate_blocks_statef *s;
+
+  if ((s = (inflate_blocks_statef *)ZALLOC
+       (z,1,sizeof(struct inflate_blocks_state))) == Z_NULL)
+    return s;
+  if ((s->window = (Bytef *)ZALLOC(z, 1, w)) == Z_NULL)
+  {
+    ZFREE(z, s, sizeof(struct inflate_blocks_state));
+    return Z_NULL;
+  }
+  s->end = s->window + w;
+  s->checkfn = c;
+  s->mode = TYPE;
+  Trace((stderr, "inflate:   blocks allocated\n"));
+  inflate_blocks_reset(s, z, &s->check);
+  return s;
+}
+
+
+local int inflate_blocks(s, z, r)
+inflate_blocks_statef *s;
+z_stream *z;
+int r;
+{
+  uInt t;               /* temporary storage */
+  uLong b;              /* bit buffer */
+  uInt k;               /* bits in bit buffer */
+  Bytef *p;             /* input data pointer */
+  uInt n;               /* bytes available there */
+  Bytef *q;             /* output window write pointer */
+  uInt m;               /* bytes to end of window or read pointer */
+
+  /* copy input/output information to locals (UPDATE macro restores) */
+  LOAD
+
+  /* process input based on current state */
+  while (1) switch (s->mode)
+  {
+    case TYPE:
+      NEEDBITS(3)
+      t = (uInt)b & 7;
+      s->last = t & 1;
+      switch (t >> 1)
+      {
+        case 0:                         /* stored */
+          Trace((stderr, "inflate:     stored block%s\n",
+                 s->last ? " (last)" : ""));
+          DUMPBITS(3)
+          t = k & 7;                    /* go to byte boundary */
+          DUMPBITS(t)
+          s->mode = LENS;               /* get length of stored block */
+          break;
+        case 1:                         /* fixed */
+          Trace((stderr, "inflate:     fixed codes block%s\n",
+                 s->last ? " (last)" : ""));
+          {
+            uInt bl, bd;
+            inflate_huft *tl, *td;
+
+            inflate_trees_fixed(&bl, &bd, &tl, &td);
+            s->sub.decode.codes = inflate_codes_new(bl, bd, tl, td, z);
+            if (s->sub.decode.codes == Z_NULL)
+            {
+              r = Z_MEM_ERROR;
+              LEAVE
+            }
+            s->sub.decode.tl = Z_NULL;  /* don't try to free these */
+            s->sub.decode.td = Z_NULL;
+          }
+          DUMPBITS(3)
+          s->mode = CODES;
+          break;
+        case 2:                         /* dynamic */
+          Trace((stderr, "inflate:     dynamic codes block%s\n",
+                 s->last ? " (last)" : ""));
+          DUMPBITS(3)
+          s->mode = TABLE;
+          break;
+        case 3:                         /* illegal */
+          DUMPBITS(3)
+          s->mode = BADB;
+          z->msg = "invalid block type";
+          r = Z_DATA_ERROR;
+          LEAVE
+      }
+      break;
+    case LENS:
+      NEEDBITS(32)
+      if (((~b) >> 16) != (b & 0xffff))
+      {
+        s->mode = BADB;
+        z->msg = "invalid stored block lengths";
+        r = Z_DATA_ERROR;
+        LEAVE
+      }
+      s->sub.left = (uInt)b & 0xffff;
+      b = k = 0;                      /* dump bits */
+      Tracev((stderr, "inflate:       stored length %u\n", s->sub.left));
+      s->mode = s->sub.left ? STORED : TYPE;
+      break;
+    case STORED:
+      if (n == 0)
+        LEAVE
+      NEEDOUT
+      t = s->sub.left;
+      if (t > n) t = n;
+      if (t > m) t = m;
+      zmemcpy(q, p, t);
+      p += t;  n -= t;
+      q += t;  m -= t;
+      if ((s->sub.left -= t) != 0)
+        break;
+      Tracev((stderr, "inflate:       stored end, %lu total out\n",
+              z->total_out + (q >= s->read ? q - s->read :
+              (s->end - s->read) + (q - s->window))));
+      s->mode = s->last ? DRY : TYPE;
+      break;
+    case TABLE:
+      NEEDBITS(14)
+      s->sub.trees.table = t = (uInt)b & 0x3fff;
+#ifndef PKZIP_BUG_WORKAROUND
+      if ((t & 0x1f) > 29 || ((t >> 5) & 0x1f) > 29)
+      {
+        s->mode = BADB;
+        z->msg = "too many length or distance symbols";
+        r = Z_DATA_ERROR;
+        LEAVE
+      }
+#endif
+      t = 258 + (t & 0x1f) + ((t >> 5) & 0x1f);
+      if (t < 19)
+        t = 19;
+      if ((s->sub.trees.blens = (uIntf*)ZALLOC(z, t, sizeof(uInt))) == Z_NULL)
+      {
+        r = Z_MEM_ERROR;
+        LEAVE
+      }
+      s->sub.trees.nblens = t;
+      DUMPBITS(14)
+      s->sub.trees.index = 0;
+      Tracev((stderr, "inflate:       table sizes ok\n"));
+      s->mode = BTREE;
+    case BTREE:
+      while (s->sub.trees.index < 4 + (s->sub.trees.table >> 10))
+      {
+        NEEDBITS(3)
+        s->sub.trees.blens[border[s->sub.trees.index++]] = (uInt)b & 7;
+        DUMPBITS(3)
+      }
+      while (s->sub.trees.index < 19)
+        s->sub.trees.blens[border[s->sub.trees.index++]] = 0;
+      s->sub.trees.bb = 7;
+      t = inflate_trees_bits(s->sub.trees.blens, &s->sub.trees.bb,
+                             &s->sub.trees.tb, z);
+      if (t != Z_OK)
+      {
+        r = t;
+        if (r == Z_DATA_ERROR)
+          s->mode = BADB;
+        LEAVE
+      }
+      s->sub.trees.index = 0;
+      Tracev((stderr, "inflate:       bits tree ok\n"));
+      s->mode = DTREE;
+    case DTREE:
+      while (t = s->sub.trees.table,
+             s->sub.trees.index < 258 + (t & 0x1f) + ((t >> 5) & 0x1f))
+      {
+        inflate_huft *h;
+        uInt i, j, c;
+
+        t = s->sub.trees.bb;
+        NEEDBITS(t)
+        h = s->sub.trees.tb + ((uInt)b & inflate_mask[t]);
+        t = h->word.what.Bits;
+        c = h->more.Base;
+        if (c < 16)
+        {
+          DUMPBITS(t)
+          s->sub.trees.blens[s->sub.trees.index++] = c;
+        }
+        else /* c == 16..18 */
+        {
+          i = c == 18 ? 7 : c - 14;
+          j = c == 18 ? 11 : 3;
+          NEEDBITS(t + i)
+          DUMPBITS(t)
+          j += (uInt)b & inflate_mask[i];
+          DUMPBITS(i)
+          i = s->sub.trees.index;
+          t = s->sub.trees.table;
+          if (i + j > 258 + (t & 0x1f) + ((t >> 5) & 0x1f) ||
+              (c == 16 && i < 1))
+          {
+            s->mode = BADB;
+            z->msg = "invalid bit length repeat";
+            r = Z_DATA_ERROR;
+            LEAVE
+          }
+          c = c == 16 ? s->sub.trees.blens[i - 1] : 0;
+          do {
+            s->sub.trees.blens[i++] = c;
+          } while (--j);
+          s->sub.trees.index = i;
+        }
+      }
+      inflate_trees_free(s->sub.trees.tb, z);
+      s->sub.trees.tb = Z_NULL;
+      {
+        uInt bl, bd;
+        inflate_huft *tl, *td;
+        inflate_codes_statef *c;
+
+        bl = 9;         /* must be <= 9 for lookahead assumptions */
+        bd = 6;         /* must be <= 9 for lookahead assumptions */
+        t = s->sub.trees.table;
+        t = inflate_trees_dynamic(257 + (t & 0x1f), 1 + ((t >> 5) & 0x1f),
+                                  s->sub.trees.blens, &bl, &bd, &tl, &td, z);
+        if (t != Z_OK)
+        {
+          if (t == (uInt)Z_DATA_ERROR)
+            s->mode = BADB;
+          r = t;
+          LEAVE
+        }
+        Tracev((stderr, "inflate:       trees ok\n"));
+        if ((c = inflate_codes_new(bl, bd, tl, td, z)) == Z_NULL)
+        {
+          inflate_trees_free(td, z);
+          inflate_trees_free(tl, z);
+          r = Z_MEM_ERROR;
+          LEAVE
+        }
+        ZFREE(z, s->sub.trees.blens, s->sub.trees.nblens * sizeof(uInt));
+        s->sub.decode.codes = c;
+        s->sub.decode.tl = tl;
+        s->sub.decode.td = td;
+      }
+      s->mode = CODES;
+    case CODES:
+      UPDATE
+      if ((r = inflate_codes(s, z, r)) != Z_STREAM_END)
+        return inflate_flush(s, z, r);
+      r = Z_OK;
+      inflate_codes_free(s->sub.decode.codes, z);
+      inflate_trees_free(s->sub.decode.td, z);
+      inflate_trees_free(s->sub.decode.tl, z);
+      LOAD
+      Tracev((stderr, "inflate:       codes end, %lu total out\n",
+              z->total_out + (q >= s->read ? q - s->read :
+              (s->end - s->read) + (q - s->window))));
+      if (!s->last)
+      {
+        s->mode = TYPE;
+        break;
+      }
+      if (k > 7)              /* return unused byte, if any */
+      {
+        Assert(k < 16, "inflate_codes grabbed too many bytes")
+        k -= 8;
+        n++;
+        p--;                    /* can always return one */
+      }
+      s->mode = DRY;
+    case DRY:
+      FLUSH
+      if (s->read != s->write)
+        LEAVE
+      s->mode = DONEB;
+    case DONEB:
+      r = Z_STREAM_END;
+      LEAVE
+    case BADB:
+      r = Z_DATA_ERROR;
+      LEAVE
+    default:
+      r = Z_STREAM_ERROR;
+      LEAVE
+  }
+}
+
+
+local int inflate_blocks_free(s, z, c)
+inflate_blocks_statef *s;
+z_stream *z;
+uLongf *c;
+{
+  inflate_blocks_reset(s, z, c);
+  ZFREE(z, s->window, s->end - s->window);
+  ZFREE(z, s, sizeof(struct inflate_blocks_state));
+  Trace((stderr, "inflate:   blocks freed\n"));
+  return Z_OK;
+}
+
+/*
+ * This subroutine adds the data at next_in/avail_in to the output history
+ * without performing any output.  The output buffer must be "caught up";
+ * i.e. no pending output (hence s->read equals s->write), and the state must
+ * be BLOCKS (i.e. we should be willing to see the start of a series of
+ * BLOCKS).  On exit, the output will also be caught up, and the checksum
+ * will have been updated if need be.
+ */
+local int inflate_addhistory(s, z)
+inflate_blocks_statef *s;
+z_stream *z;
+{
+    uLong b;              /* bit buffer */  /* NOT USED HERE */
+    uInt k;               /* bits in bit buffer */ /* NOT USED HERE */
+    uInt t;               /* temporary storage */
+    Bytef *p;             /* input data pointer */
+    uInt n;               /* bytes available there */
+    Bytef *q;             /* output window write pointer */
+    uInt m;               /* bytes to end of window or read pointer */
+
+    if (s->read != s->write)
+	return Z_STREAM_ERROR;
+    if (s->mode != TYPE)
+	return Z_DATA_ERROR;
+
+    /* we're ready to rock */
+    LOAD
+    /* while there is input ready, copy to output buffer, moving
+     * pointers as needed.
+     */
+    while (n) {
+	t = n;  /* how many to do */
+	/* is there room until end of buffer? */
+	if (t > m) t = m;
+	/* update check information */
+	if (s->checkfn != Z_NULL)
+	    s->check = (*s->checkfn)(s->check, q, t);
+	zmemcpy(q, p, t);
+	q += t;
+	p += t;
+	n -= t;
+	z->total_out += t;
+	s->read = q;    /* drag read pointer forward */
+/*      WRAP  */ 	/* expand WRAP macro by hand to handle s->read */
+	if (q == s->end) {
+	    s->read = q = s->window;
+	    m = WAVAIL;
+	}
+    }
+    UPDATE
+    return Z_OK;
+}
+
+
+/*
+ * At the end of a Deflate-compressed PPP packet, we expect to have seen
+ * a `stored' block type value but not the (zero) length bytes.
+ */
+local int inflate_packet_flush(s)
+    inflate_blocks_statef *s;
+{
+    if (s->mode != LENS)
+	return Z_DATA_ERROR;
+    s->mode = TYPE;
+    return Z_OK;
+}
+
+
+/*+++++*/
+/* inftrees.c -- generate Huffman trees for efficient decoding
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+
+local int huft_build OF((
+    uIntf *,            /* code lengths in bits */
+    uInt,               /* number of codes */
+    uInt,               /* number of "simple" codes */
+    uIntf *,            /* list of base values for non-simple codes */
+    uIntf *,            /* list of extra bits for non-simple codes */
+    inflate_huft * FAR*,/* result: starting table */
+    uIntf *,            /* maximum lookup bits (returns actual) */
+    z_stream *));       /* for zalloc function */
+
+local voidpf falloc OF((
+    voidpf,             /* opaque pointer (not used) */
+    uInt,               /* number of items */
+    uInt));             /* size of item */
+
+local void ffree OF((
+    voidpf q,           /* opaque pointer (not used) */
+    voidpf p,           /* what to free (not used) */
+    uInt n));		/* number of bytes (not used) */
+
+/* Tables for deflate from PKZIP's appnote.txt. */
+local uInt cplens[] = { /* Copy lengths for literal codes 257..285 */
+        3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,
+        35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258, 0, 0};
+        /* actually lengths - 2; also see note #13 above about 258 */
+local uInt cplext[] = { /* Extra bits for literal codes 257..285 */
+        0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2,
+        3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0, 192, 192}; /* 192==invalid */
+local uInt cpdist[] = { /* Copy offsets for distance codes 0..29 */
+        1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193,
+        257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145,
+        8193, 12289, 16385, 24577};
+local uInt cpdext[] = { /* Extra bits for distance codes */
+        0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6,
+        7, 7, 8, 8, 9, 9, 10, 10, 11, 11,
+        12, 12, 13, 13};
+
+/*
+   Huffman code decoding is performed using a multi-level table lookup.
+   The fastest way to decode is to simply build a lookup table whose
+   size is determined by the longest code.  However, the time it takes
+   to build this table can also be a factor if the data being decoded
+   is not very long.  The most common codes are necessarily the
+   shortest codes, so those codes dominate the decoding time, and hence
+   the speed.  The idea is you can have a shorter table that decodes the
+   shorter, more probable codes, and then point to subsidiary tables for
+   the longer codes.  The time it costs to decode the longer codes is
+   then traded against the time it takes to make longer tables.
+
+   This results of this trade are in the variables lbits and dbits
+   below.  lbits is the number of bits the first level table for literal/
+   length codes can decode in one step, and dbits is the same thing for
+   the distance codes.  Subsequent tables are also less than or equal to
+   those sizes.  These values may be adjusted either when all of the
+   codes are shorter than that, in which case the longest code length in
+   bits is used, or when the shortest code is *longer* than the requested
+   table size, in which case the length of the shortest code in bits is
+   used.
+
+   There are two different values for the two tables, since they code a
+   different number of possibilities each.  The literal/length table
+   codes 286 possible values, or in a flat code, a little over eight
+   bits.  The distance table codes 30 possible values, or a little less
+   than five bits, flat.  The optimum values for speed end up being
+   about one bit more than those, so lbits is 8+1 and dbits is 5+1.
+   The optimum values may differ though from machine to machine, and
+   possibly even between compilers.  Your mileage may vary.
+ */
+
+
+/* If BMAX needs to be larger than 16, then h and x[] should be uLong. */
+#define BMAX 15         /* maximum bit length of any code */
+#define N_MAX 288       /* maximum number of codes in any set */
+
+#ifdef DEBUG_ZLIB
+  uInt inflate_hufts;
+#endif
+
+local int huft_build(b, n, s, d, e, t, m, zs)
+uIntf *b;               /* code lengths in bits (all assumed <= BMAX) */
+uInt n;                 /* number of codes (assumed <= N_MAX) */
+uInt s;                 /* number of simple-valued codes (0..s-1) */
+uIntf *d;               /* list of base values for non-simple codes */
+uIntf *e;               /* list of extra bits for non-simple codes */  
+inflate_huft * FAR *t;  /* result: starting table */
+uIntf *m;               /* maximum lookup bits, returns actual */
+z_stream *zs;           /* for zalloc function */
+/* Given a list of code lengths and a maximum table size, make a set of
+   tables to decode that set of codes.  Return Z_OK on success, Z_BUF_ERROR
+   if the given code set is incomplete (the tables are still built in this
+   case), Z_DATA_ERROR if the input is invalid (all zero length codes or an
+   over-subscribed set of lengths), or Z_MEM_ERROR if not enough memory. */
+{
+
+  uInt a;                       /* counter for codes of length k */
+  uInt c[BMAX+1];               /* bit length count table */
+  uInt f;                       /* i repeats in table every f entries */
+  int g;                        /* maximum code length */
+  int h;                        /* table level */
+  register uInt i;              /* counter, current code */
+  register uInt j;              /* counter */
+  register int k;               /* number of bits in current code */
+  int l;                        /* bits per table (returned in m) */
+  register uIntf *p;            /* pointer into c[], b[], or v[] */
+  inflate_huft *q;              /* points to current table */
+  struct inflate_huft_s r;      /* table entry for structure assignment */
+  inflate_huft *u[BMAX];        /* table stack */
+  uInt v[N_MAX];                /* values in order of bit length */
+  register int w;               /* bits before this table == (l * h) */
+  uInt x[BMAX+1];               /* bit offsets, then code stack */
+  uIntf *xp;                    /* pointer into x */
+  int y;                        /* number of dummy codes added */
+  uInt z;                       /* number of entries in current table */
+
+
+  /* Generate counts for each bit length */
+  p = c;
+#define C0 *p++ = 0;
+#define C2 C0 C0 C0 C0
+#define C4 C2 C2 C2 C2
+  C4                            /* clear c[]--assume BMAX+1 is 16 */
+  p = b;  i = n;
+  do {
+    c[*p++]++;                  /* assume all entries <= BMAX */
+  } while (--i);
+  if (c[0] == n)                /* null input--all zero length codes */
+  {
+    *t = (inflate_huft *)Z_NULL;
+    *m = 0;
+    return Z_OK;
+  }
+
+
+  /* Find minimum and maximum length, bound *m by those */
+  l = *m;
+  for (j = 1; j <= BMAX; j++)
+    if (c[j])
+      break;
+  k = j;                        /* minimum code length */
+  if ((uInt)l < j)
+    l = j;
+  for (i = BMAX; i; i--)
+    if (c[i])
+      break;
+  g = i;                        /* maximum code length */
+  if ((uInt)l > i)
+    l = i;
+  *m = l;
+
+
+  /* Adjust last length count to fill out codes, if needed */
+  for (y = 1 << j; j < i; j++, y <<= 1)
+    if ((y -= c[j]) < 0)
+      return Z_DATA_ERROR;
+  if ((y -= c[i]) < 0)
+    return Z_DATA_ERROR;
+  c[i] += y;
+
+
+  /* Generate starting offsets into the value table for each length */
+  x[1] = j = 0;
+  p = c + 1;  xp = x + 2;
+  while (--i) {                 /* note that i == g from above */
+    *xp++ = (j += *p++);
+  }
+
+
+  /* Make a table of values in order of bit lengths */
+  p = b;  i = 0;
+  do {
+    if ((j = *p++) != 0)
+      v[x[j]++] = i;
+  } while (++i < n);
+
+
+  /* Generate the Huffman codes and for each, make the table entries */
+  x[0] = i = 0;                 /* first Huffman code is zero */
+  p = v;                        /* grab values in bit order */
+  h = -1;                       /* no tables yet--level -1 */
+  w = -l;                       /* bits decoded == (l * h) */
+  u[0] = (inflate_huft *)Z_NULL;        /* just to keep compilers happy */
+  q = (inflate_huft *)Z_NULL;   /* ditto */
+  z = 0;                        /* ditto */
+
+  /* go through the bit lengths (k already is bits in shortest code) */
+  for (; k <= g; k++)
+  {
+    a = c[k];
+    while (a--)
+    {
+      /* here i is the Huffman code of length k bits for value *p */
+      /* make tables up to required level */
+      while (k > w + l)
+      {
+        h++;
+        w += l;                 /* previous table always l bits */
+
+        /* compute minimum size table less than or equal to l bits */
+        z = (z = g - w) > (uInt)l ? l : z;      /* table size upper limit */
+        if ((f = 1 << (j = k - w)) > a + 1)     /* try a k-w bit table */
+        {                       /* too few codes for k-w bit table */
+          f -= a + 1;           /* deduct codes from patterns left */
+          xp = c + k;
+          if (j < z)
+            while (++j < z)     /* try smaller tables up to z bits */
+            {
+              if ((f <<= 1) <= *++xp)
+                break;          /* enough codes to use up j bits */
+              f -= *xp;         /* else deduct codes from patterns */
+            }
+        }
+        z = 1 << j;             /* table entries for j-bit table */
+
+        /* allocate and link in new table */
+        if ((q = (inflate_huft *)ZALLOC
+             (zs,z + 1,sizeof(inflate_huft))) == Z_NULL)
+        {
+          if (h)
+            inflate_trees_free(u[0], zs);
+          return Z_MEM_ERROR;   /* not enough memory */
+        }
+	q->word.Nalloc = z + 1;
+#ifdef DEBUG_ZLIB
+        inflate_hufts += z + 1;
+#endif
+        *t = q + 1;             /* link to list for huft_free() */
+        *(t = &(q->next)) = Z_NULL;
+        u[h] = ++q;             /* table starts after link */
+
+        /* connect to last table, if there is one */
+        if (h)
+        {
+          x[h] = i;             /* save pattern for backing up */
+          r.bits = (Byte)l;     /* bits to dump before this table */
+          r.exop = (Byte)j;     /* bits in this table */
+          r.next = q;           /* pointer to this table */
+          j = i >> (w - l);     /* (get around Turbo C bug) */
+          u[h-1][j] = r;        /* connect to last table */
+        }
+      }
+
+      /* set up table entry in r */
+      r.bits = (Byte)(k - w);
+      if (p >= v + n)
+        r.exop = 128 + 64;      /* out of values--invalid code */
+      else if (*p < s)
+      {
+        r.exop = (Byte)(*p < 256 ? 0 : 32 + 64);     /* 256 is end-of-block */
+        r.base = *p++;          /* simple code is just the value */
+      }
+      else
+      {
+        r.exop = (Byte)e[*p - s] + 16 + 64; /* non-simple--look up in lists */
+        r.base = d[*p++ - s];
+      }
+
+      /* fill code-like entries with r */
+      f = 1 << (k - w);
+      for (j = i >> w; j < z; j += f)
+        q[j] = r;
+
+      /* backwards increment the k-bit code i */
+      for (j = 1 << (k - 1); i & j; j >>= 1)
+        i ^= j;
+      i ^= j;
+
+      /* backup over finished tables */
+      while ((i & ((1 << w) - 1)) != x[h])
+      {
+        h--;                    /* don't need to update q */
+        w -= l;
+      }
+    }
+  }
+
+
+  /* Return Z_BUF_ERROR if we were given an incomplete table */
+  return y != 0 && g != 1 ? Z_BUF_ERROR : Z_OK;
+}
+
+
+local int inflate_trees_bits(c, bb, tb, z)
+uIntf *c;               /* 19 code lengths */
+uIntf *bb;              /* bits tree desired/actual depth */
+inflate_huft * FAR *tb; /* bits tree result */
+z_stream *z;            /* for zfree function */
+{
+  int r;
+
+  r = huft_build(c, 19, 19, (uIntf*)Z_NULL, (uIntf*)Z_NULL, tb, bb, z);
+  if (r == Z_DATA_ERROR)
+    z->msg = "oversubscribed dynamic bit lengths tree";
+  else if (r == Z_BUF_ERROR)
+  {
+    inflate_trees_free(*tb, z);
+    z->msg = "incomplete dynamic bit lengths tree";
+    r = Z_DATA_ERROR;
+  }
+  return r;
+}
+
+
+local int inflate_trees_dynamic(nl, nd, c, bl, bd, tl, td, z)
+uInt nl;                /* number of literal/length codes */
+uInt nd;                /* number of distance codes */
+uIntf *c;               /* that many (total) code lengths */
+uIntf *bl;              /* literal desired/actual bit depth */
+uIntf *bd;              /* distance desired/actual bit depth */
+inflate_huft * FAR *tl; /* literal/length tree result */
+inflate_huft * FAR *td; /* distance tree result */
+z_stream *z;            /* for zfree function */
+{
+  int r;
+
+  /* build literal/length tree */
+  if ((r = huft_build(c, nl, 257, cplens, cplext, tl, bl, z)) != Z_OK)
+  {
+    if (r == Z_DATA_ERROR)
+      z->msg = "oversubscribed literal/length tree";
+    else if (r == Z_BUF_ERROR)
+    {
+      inflate_trees_free(*tl, z);
+      z->msg = "incomplete literal/length tree";
+      r = Z_DATA_ERROR;
+    }
+    return r;
+  }
+
+  /* build distance tree */
+  if ((r = huft_build(c + nl, nd, 0, cpdist, cpdext, td, bd, z)) != Z_OK)
+  {
+    if (r == Z_DATA_ERROR)
+      z->msg = "oversubscribed literal/length tree";
+    else if (r == Z_BUF_ERROR) {
+#ifdef PKZIP_BUG_WORKAROUND
+      r = Z_OK;
+    }
+#else
+      inflate_trees_free(*td, z);
+      z->msg = "incomplete literal/length tree";
+      r = Z_DATA_ERROR;
+    }
+    inflate_trees_free(*tl, z);
+    return r;
+#endif
+  }
+
+  /* done */
+  return Z_OK;
+}
+
+
+/* build fixed tables only once--keep them here */
+local int fixed_lock = 0;
+local int fixed_built = 0;
+#define FIXEDH 530      /* number of hufts used by fixed tables */
+local uInt fixed_left = FIXEDH;
+local inflate_huft fixed_mem[FIXEDH];
+local uInt fixed_bl;
+local uInt fixed_bd;
+local inflate_huft *fixed_tl;
+local inflate_huft *fixed_td;
+
+
+local voidpf falloc(q, n, s)
+voidpf q;        /* opaque pointer (not used) */
+uInt n;         /* number of items */
+uInt s;         /* size of item */
+{
+  Assert(s == sizeof(inflate_huft) && n <= fixed_left,
+         "inflate_trees falloc overflow");
+  if (q) s++; /* to make some compilers happy */
+  fixed_left -= n;
+  return (voidpf)(fixed_mem + fixed_left);
+}
+
+
+local void ffree(q, p, n)
+voidpf q;
+voidpf p;
+uInt n;
+{
+  Assert(0, "inflate_trees ffree called!");
+  if (q) q = p; /* to make some compilers happy */
+}
+
+
+local int inflate_trees_fixed(bl, bd, tl, td)
+uIntf *bl;               /* literal desired/actual bit depth */
+uIntf *bd;               /* distance desired/actual bit depth */
+inflate_huft * FAR *tl;  /* literal/length tree result */
+inflate_huft * FAR *td;  /* distance tree result */
+{
+  /* build fixed tables if not built already--lock out other instances */
+  while (++fixed_lock > 1)
+    fixed_lock--;
+  if (!fixed_built)
+  {
+    int k;              /* temporary variable */
+    unsigned c[288];    /* length list for huft_build */
+    z_stream z;         /* for falloc function */
+
+    /* set up fake z_stream for memory routines */
+    z.zalloc = falloc;
+    z.zfree = ffree;
+    z.opaque = Z_NULL;
+
+    /* literal table */
+    for (k = 0; k < 144; k++)
+      c[k] = 8;
+    for (; k < 256; k++)
+      c[k] = 9;
+    for (; k < 280; k++)
+      c[k] = 7;
+    for (; k < 288; k++)
+      c[k] = 8;
+    fixed_bl = 7;
+    huft_build(c, 288, 257, cplens, cplext, &fixed_tl, &fixed_bl, &z);
+
+    /* distance table */
+    for (k = 0; k < 30; k++)
+      c[k] = 5;
+    fixed_bd = 5;
+    huft_build(c, 30, 0, cpdist, cpdext, &fixed_td, &fixed_bd, &z);
+
+    /* done */
+    fixed_built = 1;
+  }
+  fixed_lock--;
+  *bl = fixed_bl;
+  *bd = fixed_bd;
+  *tl = fixed_tl;
+  *td = fixed_td;
+  return Z_OK;
+}
+
+
+local int inflate_trees_free(t, z)
+inflate_huft *t;        /* table to free */
+z_stream *z;            /* for zfree function */
+/* Free the malloc'ed tables built by huft_build(), which makes a linked
+   list of the tables it made, with the links in a dummy first entry of
+   each table. */
+{
+  register inflate_huft *p, *q;
+
+  /* Go through linked list, freeing from the malloced (t[-1]) address. */
+  p = t;
+  while (p != Z_NULL)
+  {
+    q = (--p)->next;
+    ZFREE(z, p, p->word.Nalloc * sizeof(inflate_huft));
+    p = q;
+  } 
+  return Z_OK;
+}
+
+/*+++++*/
+/* infcodes.c -- process literals and length/distance pairs
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* inflate codes private state */
+struct inflate_codes_state {
+
+  /* mode */
+  enum {        /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+      START,    /* x: set up for LEN */
+      LEN,      /* i: get length/literal/eob next */
+      LENEXT,   /* i: getting length extra (have base) */
+      DIST,     /* i: get distance next */
+      DISTEXT,  /* i: getting distance extra */
+      COPY,     /* o: copying bytes in window, waiting for space */
+      LIT,      /* o: got literal, waiting for output space */
+      WASH,     /* o: got eob, possibly still output waiting */
+      END,      /* x: got eob and all data flushed */
+      BADCODE}  /* x: got error */
+    mode;               /* current inflate_codes mode */
+
+  /* mode dependent information */
+  uInt len;
+  union {
+    struct {
+      inflate_huft *tree;       /* pointer into tree */
+      uInt need;                /* bits needed */
+    } code;             /* if LEN or DIST, where in tree */
+    uInt lit;           /* if LIT, literal */
+    struct {
+      uInt get;                 /* bits to get for extra */
+      uInt dist;                /* distance back to copy from */
+    } copy;             /* if EXT or COPY, where and how much */
+  } sub;                /* submode */
+
+  /* mode independent information */
+  Byte lbits;           /* ltree bits decoded per branch */
+  Byte dbits;           /* dtree bits decoder per branch */
+  inflate_huft *ltree;          /* literal/length/eob tree */
+  inflate_huft *dtree;          /* distance tree */
+
+};
+
+
+local inflate_codes_statef *inflate_codes_new(bl, bd, tl, td, z)
+uInt bl, bd;
+inflate_huft *tl, *td;
+z_stream *z;
+{
+  inflate_codes_statef *c;
+
+  if ((c = (inflate_codes_statef *)
+       ZALLOC(z,1,sizeof(struct inflate_codes_state))) != Z_NULL)
+  {
+    c->mode = START;
+    c->lbits = (Byte)bl;
+    c->dbits = (Byte)bd;
+    c->ltree = tl;
+    c->dtree = td;
+    Tracev((stderr, "inflate:       codes new\n"));
+  }
+  return c;
+}
+
+
+local int inflate_codes(s, z, r)
+inflate_blocks_statef *s;
+z_stream *z;
+int r;
+{
+  uInt j;               /* temporary storage */
+  inflate_huft *t;      /* temporary pointer */
+  uInt e;               /* extra bits or operation */
+  uLong b;              /* bit buffer */
+  uInt k;               /* bits in bit buffer */
+  Bytef *p;             /* input data pointer */
+  uInt n;               /* bytes available there */
+  Bytef *q;             /* output window write pointer */
+  uInt m;               /* bytes to end of window or read pointer */
+  Bytef *f;             /* pointer to copy strings from */
+  inflate_codes_statef *c = s->sub.decode.codes;  /* codes state */
+
+  /* copy input/output information to locals (UPDATE macro restores) */
+  LOAD
+
+  /* process input and output based on current state */
+  while (1) switch (c->mode)
+  {             /* waiting for "i:"=input, "o:"=output, "x:"=nothing */
+    case START:         /* x: set up for LEN */
+#ifndef SLOW
+      if (m >= 258 && n >= 10)
+      {
+        UPDATE
+        r = inflate_fast(c->lbits, c->dbits, c->ltree, c->dtree, s, z);
+        LOAD
+        if (r != Z_OK)
+        {
+          c->mode = r == Z_STREAM_END ? WASH : BADCODE;
+          break;
+        }
+      }
+#endif /* !SLOW */
+      c->sub.code.need = c->lbits;
+      c->sub.code.tree = c->ltree;
+      c->mode = LEN;
+    case LEN:           /* i: get length/literal/eob next */
+      j = c->sub.code.need;
+      NEEDBITS(j)
+      t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+      DUMPBITS(t->bits)
+      e = (uInt)(t->exop);
+      if (e == 0)               /* literal */
+      {
+        c->sub.lit = t->base;
+        Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+                 "inflate:         literal '%c'\n" :
+                 "inflate:         literal 0x%02x\n", t->base));
+        c->mode = LIT;
+        break;
+      }
+      if (e & 16)               /* length */
+      {
+        c->sub.copy.get = e & 15;
+        c->len = t->base;
+        c->mode = LENEXT;
+        break;
+      }
+      if ((e & 64) == 0)        /* next table */
+      {
+        c->sub.code.need = e;
+        c->sub.code.tree = t->next;
+        break;
+      }
+      if (e & 32)               /* end of block */
+      {
+        Tracevv((stderr, "inflate:         end of block\n"));
+        c->mode = WASH;
+        break;
+      }
+      c->mode = BADCODE;        /* invalid code */
+      z->msg = "invalid literal/length code";
+      r = Z_DATA_ERROR;
+      LEAVE
+    case LENEXT:        /* i: getting length extra (have base) */
+      j = c->sub.copy.get;
+      NEEDBITS(j)
+      c->len += (uInt)b & inflate_mask[j];
+      DUMPBITS(j)
+      c->sub.code.need = c->dbits;
+      c->sub.code.tree = c->dtree;
+      Tracevv((stderr, "inflate:         length %u\n", c->len));
+      c->mode = DIST;
+    case DIST:          /* i: get distance next */
+      j = c->sub.code.need;
+      NEEDBITS(j)
+      t = c->sub.code.tree + ((uInt)b & inflate_mask[j]);
+      DUMPBITS(t->bits)
+      e = (uInt)(t->exop);
+      if (e & 16)               /* distance */
+      {
+        c->sub.copy.get = e & 15;
+        c->sub.copy.dist = t->base;
+        c->mode = DISTEXT;
+        break;
+      }
+      if ((e & 64) == 0)        /* next table */
+      {
+        c->sub.code.need = e;
+        c->sub.code.tree = t->next;
+        break;
+      }
+      c->mode = BADCODE;        /* invalid code */
+      z->msg = "invalid distance code";
+      r = Z_DATA_ERROR;
+      LEAVE
+    case DISTEXT:       /* i: getting distance extra */
+      j = c->sub.copy.get;
+      NEEDBITS(j)
+      c->sub.copy.dist += (uInt)b & inflate_mask[j];
+      DUMPBITS(j)
+      Tracevv((stderr, "inflate:         distance %u\n", c->sub.copy.dist));
+      c->mode = COPY;
+    case COPY:          /* o: copying bytes in window, waiting for space */
+#ifndef __TURBOC__ /* Turbo C bug for following expression */
+      f = (uInt)(q - s->window) < c->sub.copy.dist ?
+          s->end - (c->sub.copy.dist - (q - s->window)) :
+          q - c->sub.copy.dist;
+#else
+      f = q - c->sub.copy.dist;
+      if ((uInt)(q - s->window) < c->sub.copy.dist)
+        f = s->end - (c->sub.copy.dist - (q - s->window));
+#endif
+      while (c->len)
+      {
+        NEEDOUT
+        OUTBYTE(*f++)
+        if (f == s->end)
+          f = s->window;
+        c->len--;
+      }
+      c->mode = START;
+      break;
+    case LIT:           /* o: got literal, waiting for output space */
+      NEEDOUT
+      OUTBYTE(c->sub.lit)
+      c->mode = START;
+      break;
+    case WASH:          /* o: got eob, possibly more output */
+      FLUSH
+      if (s->read != s->write)
+        LEAVE
+      c->mode = END;
+    case END:
+      r = Z_STREAM_END;
+      LEAVE
+    case BADCODE:       /* x: got error */
+      r = Z_DATA_ERROR;
+      LEAVE
+    default:
+      r = Z_STREAM_ERROR;
+      LEAVE
+  }
+}
+
+
+local void inflate_codes_free(c, z)
+inflate_codes_statef *c;
+z_stream *z;
+{
+  ZFREE(z, c, sizeof(struct inflate_codes_state));
+  Tracev((stderr, "inflate:       codes free\n"));
+}
+
+/*+++++*/
+/* inflate_util.c -- data and routines common to blocks and codes
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* copy as much as possible from the sliding window to the output area */
+local int inflate_flush(s, z, r)
+inflate_blocks_statef *s;
+z_stream *z;
+int r;
+{
+  uInt n;
+  Bytef *p, *q;
+
+  /* local copies of source and destination pointers */
+  p = z->next_out;
+  q = s->read;
+
+  /* compute number of bytes to copy as far as end of window */
+  n = (uInt)((q <= s->write ? s->write : s->end) - q);
+  if (n > z->avail_out) n = z->avail_out;
+  if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+  /* update counters */
+  z->avail_out -= n;
+  z->total_out += n;
+
+  /* update check information */
+  if (s->checkfn != Z_NULL)
+    s->check = (*s->checkfn)(s->check, q, n);
+
+  /* copy as far as end of window */
+  zmemcpy(p, q, n);
+  p += n;
+  q += n;
+
+  /* see if more to copy at beginning of window */
+  if (q == s->end)
+  {
+    /* wrap pointers */
+    q = s->window;
+    if (s->write == s->end)
+      s->write = s->window;
+
+    /* compute bytes to copy */
+    n = (uInt)(s->write - q);
+    if (n > z->avail_out) n = z->avail_out;
+    if (n && r == Z_BUF_ERROR) r = Z_OK;
+
+    /* update counters */
+    z->avail_out -= n;
+    z->total_out += n;
+
+    /* update check information */
+    if (s->checkfn != Z_NULL)
+      s->check = (*s->checkfn)(s->check, q, n);
+
+    /* copy */
+    zmemcpy(p, q, n);
+    p += n;
+    q += n;
+  }
+
+  /* update pointers */
+  z->next_out = p;
+  s->read = q;
+
+  /* done */
+  return r;
+}
+
+
+/*+++++*/
+/* inffast.c -- process literals and length/distance pairs fast
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* simplify the use of the inflate_huft type with some defines */
+#define base more.Base
+#define next more.Next
+#define exop word.what.Exop
+#define bits word.what.Bits
+
+/* macros for bit input with no checking and for returning unused bytes */
+#define GRABBITS(j) {while(k<(j)){b|=((uLong)NEXTBYTE)<<k;k+=8;}}
+#define UNGRAB {n+=(c=k>>3);p-=c;k&=7;}
+
+/* Called with number of bytes left to write in window at least 258
+   (the maximum string length) and number of input bytes available
+   at least ten.  The ten bytes are six bytes for the longest length/
+   distance pair plus four bytes for overloading the bit buffer. */
+
+local int inflate_fast(bl, bd, tl, td, s, z)
+uInt bl, bd;
+inflate_huft *tl, *td;
+inflate_blocks_statef *s;
+z_stream *z;
+{
+  inflate_huft *t;      /* temporary pointer */
+  uInt e;               /* extra bits or operation */
+  uLong b;              /* bit buffer */
+  uInt k;               /* bits in bit buffer */
+  Bytef *p;             /* input data pointer */
+  uInt n;               /* bytes available there */
+  Bytef *q;             /* output window write pointer */
+  uInt m;               /* bytes to end of window or read pointer */
+  uInt ml;              /* mask for literal/length tree */
+  uInt md;              /* mask for distance tree */
+  uInt c;               /* bytes to copy */
+  uInt d;               /* distance back to copy from */
+  Bytef *r;             /* copy source pointer */
+
+  /* load input, output, bit values */
+  LOAD
+
+  /* initialize masks */
+  ml = inflate_mask[bl];
+  md = inflate_mask[bd];
+
+  /* do until not enough input or output space for fast loop */
+  do {                          /* assume called with m >= 258 && n >= 10 */
+    /* get literal/length code */
+    GRABBITS(20)                /* max bits for literal/length code */
+    if ((e = (t = tl + ((uInt)b & ml))->exop) == 0)
+    {
+      DUMPBITS(t->bits)
+      Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+                "inflate:         * literal '%c'\n" :
+                "inflate:         * literal 0x%02x\n", t->base));
+      *q++ = (Byte)t->base;
+      m--;
+      continue;
+    }
+    do {
+      DUMPBITS(t->bits)
+      if (e & 16)
+      {
+        /* get extra bits for length */
+        e &= 15;
+        c = t->base + ((uInt)b & inflate_mask[e]);
+        DUMPBITS(e)
+        Tracevv((stderr, "inflate:         * length %u\n", c));
+
+        /* decode distance base of block to copy */
+        GRABBITS(15);           /* max bits for distance code */
+        e = (t = td + ((uInt)b & md))->exop;
+        do {
+          DUMPBITS(t->bits)
+          if (e & 16)
+          {
+            /* get extra bits to add to distance base */
+            e &= 15;
+            GRABBITS(e)         /* get extra bits (up to 13) */
+            d = t->base + ((uInt)b & inflate_mask[e]);
+            DUMPBITS(e)
+            Tracevv((stderr, "inflate:         * distance %u\n", d));
+
+            /* do the copy */
+            m -= c;
+            if ((uInt)(q - s->window) >= d)     /* offset before dest */
+            {                                   /*  just copy */
+              r = q - d;
+              *q++ = *r++;  c--;        /* minimum count is three, */
+              *q++ = *r++;  c--;        /*  so unroll loop a little */
+            }
+            else                        /* else offset after destination */
+            {
+              e = d - (q - s->window);  /* bytes from offset to end */
+              r = s->end - e;           /* pointer to offset */
+              if (c > e)                /* if source crosses, */
+              {
+                c -= e;                 /* copy to end of window */
+                do {
+                  *q++ = *r++;
+                } while (--e);
+                r = s->window;          /* copy rest from start of window */
+              }
+            }
+            do {                        /* copy all or what's left */
+              *q++ = *r++;
+            } while (--c);
+            break;
+          }
+          else if ((e & 64) == 0)
+            e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop;
+          else
+          {
+            z->msg = "invalid distance code";
+            UNGRAB
+            UPDATE
+            return Z_DATA_ERROR;
+          }
+        } while (1);
+        break;
+      }
+      if ((e & 64) == 0)
+      {
+        if ((e = (t = t->next + ((uInt)b & inflate_mask[e]))->exop) == 0)
+        {
+          DUMPBITS(t->bits)
+          Tracevv((stderr, t->base >= 0x20 && t->base < 0x7f ?
+                    "inflate:         * literal '%c'\n" :
+                    "inflate:         * literal 0x%02x\n", t->base));
+          *q++ = (Byte)t->base;
+          m--;
+          break;
+        }
+      }
+      else if (e & 32)
+      {
+        Tracevv((stderr, "inflate:         * end of block\n"));
+        UNGRAB
+        UPDATE
+        return Z_STREAM_END;
+      }
+      else
+      {
+        z->msg = "invalid literal/length code";
+        UNGRAB
+        UPDATE
+        return Z_DATA_ERROR;
+      }
+    } while (1);
+  } while (m >= 258 && n >= 10);
+
+  /* not enough input or output--restore pointers and return */
+  UNGRAB
+  UPDATE
+  return Z_OK;
+}
+
+
+/*+++++*/
+/* zutil.c -- target dependent utility functions for the compression library
+ * Copyright (C) 1995 Jean-loup Gailly.
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* From: zutil.c,v 1.8 1995/05/03 17:27:12 jloup Exp */
+
+char *zlib_version = ZLIB_VERSION;
+
+char *z_errmsg[] = {
+"stream end",          /* Z_STREAM_END    1 */
+"",                    /* Z_OK            0 */
+"file error",          /* Z_ERRNO        (-1) */
+"stream error",        /* Z_STREAM_ERROR (-2) */
+"data error",          /* Z_DATA_ERROR   (-3) */
+"insufficient memory", /* Z_MEM_ERROR    (-4) */
+"buffer error",        /* Z_BUF_ERROR    (-5) */
+""};
+
+
+/*+++++*/
+/* adler32.c -- compute the Adler-32 checksum of a data stream
+ * Copyright (C) 1995 Mark Adler
+ * For conditions of distribution and use, see copyright notice in zlib.h 
+ */
+
+/* From: adler32.c,v 1.6 1995/05/03 17:27:08 jloup Exp */
+
+#define BASE 65521L /* largest prime smaller than 65536 */
+#define NMAX 5552
+/* NMAX is the largest n such that 255n(n+1)/2 + (n+1)(BASE-1) <= 2^32-1 */
+
+#define DO1(buf)  {s1 += *buf++; s2 += s1;}
+#define DO2(buf)  DO1(buf); DO1(buf);
+#define DO4(buf)  DO2(buf); DO2(buf);
+#define DO8(buf)  DO4(buf); DO4(buf);
+#define DO16(buf) DO8(buf); DO8(buf);
+
+/* ========================================================================= */
+uLong adler32(adler, buf, len)
+    uLong adler;
+    Bytef *buf;
+    uInt len;
+{
+    unsigned long s1 = adler & 0xffff;
+    unsigned long s2 = (adler >> 16) & 0xffff;
+    int k;
+
+    if (buf == Z_NULL) return 1L;
+
+    while (len > 0) {
+        k = len < NMAX ? len : NMAX;
+        len -= k;
+        while (k >= 16) {
+            DO16(buf);
+            k -= 16;
+        }
+        if (k != 0) do {
+            DO1(buf);
+        } while (--k);
+        s1 %= BASE;
+        s2 %= BASE;
+    }
+    return (s2 << 16) | s1;
+}
diff -Nur linux-2.4.30/arch/mips/zboot/Makefile linux-2.4.30-linux-mips.org/arch/mips/zboot/Makefile
--- linux-2.4.30/arch/mips/zboot/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,93 @@
+#
+# arch/mips/zboot/Makefile
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+
+# Adapted for MIPS Pete Popov, Dan Malek
+#
+# Copyright (C) 1994 by Linus Torvalds
+# Adapted for PowerPC by Gary Thomas
+# modified by Cort (cort@cs.nmt.edu)
+#
+
+.c.s:
+	$(CC) $(CFLAGS) -S -o $*.s $<
+.s.o:
+	$(AS) -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) -c -o $*.o $<
+.S.s:
+	$(CPP) $(AFLAGS) -o $*.o $<
+.S.o:
+	$(CC) $(AFLAGS) -c -o $*.o $<
+
+GZIP_FLAGS = -v9f
+
+CFLAGS  += -D__BOOTER__ -I$(TOPDIR)/arch/$(ARCH)/zboot/include
+AFLAGS	+= -D__BOOTER__
+
+BOOT_TARGETS = zImage zImage.initrd zImage.flash zImage.initrd.flash
+
+lib/zlib.a:
+	$(MAKE) -C lib
+
+images/vmlinux.gz: $(TOPDIR)/vmlinux
+	$(MAKE) -C images vmlinux.gz
+
+$(BOOT_TARGETS): lib/zlib.a images/vmlinux.gz
+ifdef CONFIG_MIPS_PB1000
+	$(MAKE) -C pb1xxx $@
+endif
+ifdef CONFIG_MIPS_PB1500
+	$(MAKE) -C pb1xxx $@
+endif
+ifdef CONFIG_MIPS_PB1100
+	$(MAKE) -C pb1xxx $@
+endif
+ifdef CONFIG_MIPS_PB1550
+	$(MAKE) -C pb1xxx $@
+endif
+ifdef CONFIG_MIPS_PB1200
+	$(MAKE) -C pb1xxx $@
+endif
+ifdef CONFIG_MIPS_DB1000
+	$(MAKE) -C pb1xxx $@
+endif
+ifdef CONFIG_MIPS_DB1100
+	$(MAKE) -C pb1xxx $@
+endif
+ifdef CONFIG_MIPS_DB1500
+	$(MAKE) -C pb1xxx $@
+endif
+ifdef CONFIG_MIPS_DB1550
+	$(MAKE) -C pb1xxx $@
+endif
+ifdef CONFIG_MIPS_DB1200
+	$(MAKE) -C pb1xxx $@
+endif
+ifdef CONFIG_MIPS_BOSPORUS
+	$(MAKE) -C pb1xxx $@
+endif
+ifdef CONFIG_MIPS_MIRAGE
+	$(MAKE) -C pb1xxx $@
+endif
+ifdef CONFIG_MIPS_MTX1
+	$(MAKE) -C pb1xxx $@
+endif
+ifdef CONFIG_COGENT_CSB250
+	$(MAKE) -C csb250 $@
+endif
+ifdef CONFIG_MIPS_XXS1500
+BOOT_DIR = xxs1500
+endif
+
+# Do the dirs
+clean:
+	$(MAKE) -C common clean
+	$(MAKE) -C images clean
+	$(MAKE) -C pb1xxx clean
+	$(MAKE) -C xxs1500 clean
+
+include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/arch/mips/zboot/pb1xxx/head.S linux-2.4.30-linux-mips.org/arch/mips/zboot/pb1xxx/head.S
--- linux-2.4.30/arch/mips/zboot/pb1xxx/head.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/pb1xxx/head.S	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,149 @@
+/*
+ * arch/mips/kernel/head.S
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1994, 1995 Waldorf Electronics
+ * Written by Ralf Baechle and Andreas Busse
+ * Copyright (C) 1995 - 1999 Ralf Baechle
+ * Copyright (C) 1996 Paul M. Antoine
+ * Modified for DECStation and hence R3000 support by Paul M. Antoine
+ * Further modifications by David S. Miller and Harald Koerfgen
+ * Copyright (C) 1999 Silicon Graphics, Inc.
+ *
+ * Head.S contains the MIPS exception handler and startup code.
+ *
+ **************************************************************************
+ *  9 Nov, 2000.
+ *  Added Cache Error exception handler and SBDDP EJTAG debug exception.
+ *
+ *  Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
+ *  Copyright (C) 2000 MIPS Technologies, Inc.  All rights reserved.
+ **************************************************************************
+ */
+#include <linux/config.h>
+#include <linux/threads.h>
+
+#include <asm/asm.h>
+#include <asm/cacheops.h>
+#include <asm/mipsregs.h>
+#include <asm/offset.h>
+#include <asm/cachectl.h>
+#include <asm/regdef.h>
+
+#define IndexInvalidate_I       0x00
+#define IndexWriteBack_D        0x01
+
+	.set noreorder
+	.cprestore
+	LEAF(start)
+start:
+	bal	locate
+	nop
+locate:
+	subu	s8, ra, 8	/* Where we were loaded */
+	la	sp, (.stack + 8192)
+
+	move	s0, a0		/* Save boot rom start args */
+	move	s1, a1
+	move	s2, a2
+	move	s3, a3
+
+	la	a0, start	/* Where we were linked to run */
+
+	move	a1, s8
+	la	a2, _edata
+	subu	t1, a2, a0
+	srl	t1, t1, 2
+
+	/* copy text section */
+	li	t0, 0
+1:	lw	v0, 0(a1)
+	nop
+	sw	v0, 0(a0)
+	xor	t0, t0, v0
+	addu	a0, 4
+	bne	a2, a0, 1b
+	addu	a1, 4
+
+	/* Clear BSS */
+	la	a0, _edata
+	la	a2, _end
+2:	sw	zero, 0(a0)
+	bne	a2, a0, 2b
+	addu	a0, 4
+
+	/* push the D-Cache and invalidate I-Cache */
+	li	k0, 0x80000000  # start address
+	li	k1, 0x80004000  # end address (16KB I-Cache)
+	subu	k1, 128
+
+1:
+	.set mips3
+	cache	IndexWriteBack_D, 0(k0)
+	cache	IndexWriteBack_D, 32(k0)
+	cache	IndexWriteBack_D, 64(k0)
+	cache	IndexWriteBack_D, 96(k0)
+	cache	IndexInvalidate_I, 0(k0)
+	cache	IndexInvalidate_I, 32(k0)
+	cache	IndexInvalidate_I, 64(k0)
+	cache	IndexInvalidate_I, 96(k0)
+	.set mips0
+
+	bne	k0, k1, 1b
+	addu	k0, k0, 128
+	/* done */
+
+	move	a0, s8		     /* load address */
+	move	a1, t1               /* length in words */
+	move	a2, t0               /* checksum */
+	move	a3, sp
+
+	la	ra, 1f
+	la	k0, decompress_kernel
+	jr	k0
+	nop
+1:
+
+	move	a0, s0
+	move	a1, s1
+	move	a2, s2
+	move	a3, s3
+	li	k0, KERNEL_ENTRY
+	jr	k0
+	nop
+3:
+	b 3b
+	END(start)
+
+	LEAF(udelay)
+udelay:
+	END(udelay)
+
+
+	LEAF(FlushCache)
+	li	k0, 0x80000000  # start address
+	li	k1, 0x80004000  # end address (16KB I-Cache)
+	subu	k1, 128
+
+1:
+	.set mips3
+	cache	IndexWriteBack_D, 0(k0)
+	cache	IndexWriteBack_D, 32(k0)
+	cache	IndexWriteBack_D, 64(k0)
+	cache	IndexWriteBack_D, 96(k0)
+	cache	IndexInvalidate_I, 0(k0)
+	cache	IndexInvalidate_I, 32(k0)
+	cache	IndexInvalidate_I, 64(k0)
+	cache	IndexInvalidate_I, 96(k0)
+	.set mips0
+
+	bne	k0, k1, 1b
+	addu	k0, k0, 128
+	jr	ra
+	nop
+	END(FlushCache)
+
+	.comm .stack,4096*2,4
diff -Nur linux-2.4.30/arch/mips/zboot/pb1xxx/Makefile linux-2.4.30-linux-mips.org/arch/mips/zboot/pb1xxx/Makefile
--- linux-2.4.30/arch/mips/zboot/pb1xxx/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/pb1xxx/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,147 @@
+# arch/mips/zboot/pb1xxx/Makefile
+# 
+# Makefile for Alchemy Semiconductor Pb1[015]00 boards.
+# All of the boot loader code was derived from the ppc
+# boot code.
+#
+# Copyright 2001,2002 MontaVista Software Inc.
+#
+# Author: Mark A. Greer
+#	  mgreer@mvista.com
+# Ported and modified for mips support by 
+# Pete Popov <ppopov@mvista.com>
+#
+# This program is free software; you can redistribute  it and/or modify it
+# under  the terms of  the GNU General  Public License as published by the
+# Free Software Foundation;  either version 2 of the  License, or (at your
+# option) any later version.
+
+.c.s:
+	$(CC) $(CFLAGS) -S -o $*.s $<
+.s.o:
+	$(AS) -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) -D__BOOTER__ -c -o $*.o $<
+.S.s:
+	$(CPP) $(AFLAGS) -o $*.o $<
+.S.o:
+	$(CC) $(AFLAGS) -c -o $*.o $<
+
+#########################################################################
+# START BOARD SPECIFIC VARIABLES
+ifdef CONFIG_MIPS_PB1000
+BNAME=pb1000
+endif
+
+ifdef CONFIG_MIPS_PB1100
+BNAME=pb1100
+endif
+
+ifdef CONFIG_MIPS_PB1500
+BNAME=pb1500
+endif
+
+ifdef CONFIG_MIPS_PB1550
+BNAME=pb1550
+endif
+
+ifdef CONFIG_MIPS_PB1200
+BNAME=pb1200
+endif
+
+ifdef CONFIG_MIPS_DB1000
+BNAME=db1000
+endif
+
+ifdef CONFIG_MIPS_DB1100
+BNAME=db1100
+endif
+
+ifdef CONFIG_MIPS_DB1500
+BNAME=db1500
+endif
+
+ifdef CONFIG_MIPS_DB1550
+BNAME=db1550
+endif
+
+ifdef CONFIG_MIPS_DB1200
+BNAME=db1200
+endif
+
+ifdef CONFIG_MIPS_BOSPORUS
+BNAME=bosporus
+endif
+
+ifdef CONFIG_MIPS_MIRAGE
+BNAME=mirage
+endif
+
+ifdef CONFIG_MIPS_MTX1
+BNAME=mtx-1
+endif
+
+# These two variables control where the zImage is stored
+# in flash and loaded in memory.  It only controls how the srec
+# file is generated, the code is the same.
+RAM_RUN_ADDR = 0x81000000
+FLASH_LOAD_ADDR = 0xBFD00000
+
+# These two variables specify the free ram region
+# that can be used for temporary malloc area
+AVAIL_RAM_START=0x81400000
+AVAIL_RAM_END=0x81800000
+
+# This one must match the LOADADDR in arch/mips/Makefile!
+LOADADDR=0x80100000
+# END BOARD SPECIFIC VARIABLES
+#########################################################################
+
+OBJECTS := head.o ../common/misc-common.o ../common/misc-simple.o \
+	../common/au1k_uart.o ../common/string.o ../common/ctype.o
+LIBS := ../lib/zlib.a
+
+ENTRY := ../utils/entry
+OFFSET := ../utils/offset
+SIZE := ../utils/size
+
+LD_ARGS := -T ../ld.script -Ttext $(RAM_RUN_ADDR) -Bstatic
+OBJCOPY_ARGS = -O elf32-tradlittlemips
+
+all: zImage
+
+clean:
+	rm -rf *.o vmlinux* zvmlinux.* ../images/*.srec
+
+head.o: head.S $(TOPDIR)/vmlinux
+	$(CC) $(AFLAGS) \
+	-DKERNEL_ENTRY=$(shell sh $(ENTRY) $(NM) $(TOPDIR)/vmlinux ) \
+	-c -o $*.o $<
+
+../common/misc-simple.o:
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 -DZIMAGE_OFFSET=0 \
+		-DAVAIL_RAM_START=$(AVAIL_RAM_START) \
+		-DAVAIL_RAM_END=$(AVAIL_RAM_END) \
+		-DLOADADDR=$(LOADADDR) \
+		-DZIMAGE_SIZE=0 -c -o $@ $*.c
+
+zvmlinux: $(OBJECTS) $(LIBS) ../ld.script ../images/vmlinux.gz ../common/dummy.o
+	$(OBJCOPY) \
+		--add-section=.image=../images/vmlinux.gz \
+		--set-section-flags=.image=contents,alloc,load,readonly,data \
+		../common/dummy.o image.o
+	$(LD) $(LD_ARGS) -o $@ $(OBJECTS) image.o $(LIBS)
+	$(OBJCOPY) $(OBJCOPY_ARGS) $@ $@ -R .comment -R .stab -R .stabstr \
+		-R .initrd -R .sysmap
+
+# Here we manipulate the image in order to get it the necessary
+# srecord file we need.
+zImage: zvmlinux
+	mv zvmlinux ../images/zImage.$(BNAME)
+	$(OBJCOPY) -O srec ../images/zImage.$(BNAME) ../images/$(BNAME).srec
+
+zImage.flash: zImage
+	$(OBJCOPY) -O srec --adjust-vma 0x3ed00000 \
+		../images/zImage.$(BNAME) ../images/$(BNAME).flash.srec
+
+include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/arch/mips/zboot/utils/entry linux-2.4.30-linux-mips.org/arch/mips/zboot/utils/entry
--- linux-2.4.30/arch/mips/zboot/utils/entry	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/utils/entry	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+# grab the kernel_entry address from the vmlinux elf image
+entry=`$1 $2  | grep kernel_entry`
+
+fs=`echo $entry | grep ffffffff`  # check toolchain output
+
+if [ -n "$fs" ]; then
+	echo "0x"`$1 $2  | grep kernel_entry | cut -c9- | awk '{print $1}'`
+else
+	echo "0x"`$1 $2  | grep kernel_entry | cut -c1- | awk '{print $1}'`
+fi
diff -Nur linux-2.4.30/arch/mips/zboot/utils/offset linux-2.4.30-linux-mips.org/arch/mips/zboot/utils/offset
--- linux-2.4.30/arch/mips/zboot/utils/offset	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/utils/offset	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,3 @@
+#!/bin/sh
+
+echo "0x"`$1 -h $2  | grep $3 | grep -v zvmlinux| awk '{print $6}'`
diff -Nur linux-2.4.30/arch/mips/zboot/utils/size linux-2.4.30-linux-mips.org/arch/mips/zboot/utils/size
--- linux-2.4.30/arch/mips/zboot/utils/size	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/utils/size	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,4 @@
+#!/bin/sh
+
+OFFSET=`$1 -h $2  | grep $3 | grep -v zvmlinux | awk '{print $3}'`
+echo "0x"$OFFSET
diff -Nur linux-2.4.30/arch/mips/zboot/xxs1500/head.S linux-2.4.30-linux-mips.org/arch/mips/zboot/xxs1500/head.S
--- linux-2.4.30/arch/mips/zboot/xxs1500/head.S	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/xxs1500/head.S	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,137 @@
+/*
+ * arch/mips/kernel/head.S
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1994, 1995 Waldorf Electronics
+ * Written by Ralf Baechle and Andreas Busse
+ * Copyright (C) 1995 - 1999 Ralf Baechle
+ * Copyright (C) 1996 Paul M. Antoine
+ * Modified for DECStation and hence R3000 support by Paul M. Antoine
+ * Further modifications by David S. Miller and Harald Koerfgen
+ * Copyright (C) 1999 Silicon Graphics, Inc.
+ *
+ * Head.S contains the MIPS exception handler and startup code.
+ *
+ **************************************************************************
+ *  9 Nov, 2000.
+ *  Added Cache Error exception handler and SBDDP EJTAG debug exception.
+ *
+ *  Kevin Kissell, kevink@mips.com and Carsten Langgaard, carstenl@mips.com
+ *  Copyright (C) 2000 MIPS Technologies, Inc.  All rights reserved.
+ **************************************************************************
+ */
+#include <linux/config.h>
+#include <linux/threads.h>
+
+#include <asm/asm.h>
+#include <asm/cacheops.h>
+#include <asm/mipsregs.h>
+#include <asm/offset.h>
+#include <asm/cachectl.h>
+#include <asm/regdef.h>
+
+#define IndexInvalidate_I       0x00
+
+	.set noreorder
+	.cprestore
+	LEAF(start)
+start:
+
+locate:
+	la	sp, .stack
+	move	s0, a0
+	move	s1, a1
+	move	s2, a2
+	move	s3, a3
+
+	la	a0, start
+
+	li	a1, FLASH_LOAD_ADDR
+	la	a2, _edata
+	subu	t1, a2, a0
+	srl	t1, t1, 2
+
+	/* copy text section */
+	li	t0, 0
+1:	lw	v0, 0(a1)
+	nop
+	sw	v0, 0(a0)
+	xor	t0, t0, v0
+	addu	a0, 4
+	bne	a2, a0, 1b
+	addu	a1, 4
+
+	/* Clear BSS */
+	la	a0, _edata
+	la	a2, _end
+2:	sw	zero, 0(a0)
+	bne	a2, a0, 2b
+	addu	a0, 4
+
+	/* flush the I-Cache */
+	li	k0, 0x80000000  # start address
+	li	k1, 0x80004000  # end address (16KB I-Cache)
+	subu	k1, 128
+
+1:
+	.set mips3
+	cache	IndexInvalidate_I, 0(k0)
+	cache	IndexInvalidate_I, 32(k0)
+	cache	IndexInvalidate_I, 64(k0)
+	cache	IndexInvalidate_I, 96(k0)
+	.set mips0
+
+	bne	k0, k1, 1b
+	addu	k0, k0, 128
+	/* done */
+
+	li	a0, FLASH_LOAD_ADDR  /* load address */
+	move	a1, t1               /* length in words */
+	move	a2, t0               /* checksum */
+	move	a3, sp
+
+	la	ra, 1f
+	la	k0, decompress_kernel
+	jr	k0
+	nop
+1:
+
+	move	a0, s0
+	move	a1, s1
+	move	a2, s2
+	move	a3, s3
+	li	k0, KERNEL_ENTRY
+	jr	k0
+	nop
+3:
+	b 3b
+	END(start)
+
+	LEAF(udelay)
+udelay:
+	END(udelay)
+
+
+	LEAF(FlushCache)
+	li	k0, 0x80000000  # start address
+	li	k1, 0x80004000  # end address (16KB I-Cache)
+	subu	k1, 128
+
+1:
+	.set mips3
+	cache	IndexInvalidate_I, 0(k0)
+	cache	IndexInvalidate_I, 32(k0)
+	cache	IndexInvalidate_I, 64(k0)
+	cache	IndexInvalidate_I, 96(k0)
+	.set mips0
+
+	bne	k0, k1, 1b
+	addu	k0, k0, 128
+	jr	ra
+	nop
+	END(FlushCache)
+
+	.comm .stack,4096*2,4
diff -Nur linux-2.4.30/arch/mips/zboot/xxs1500/ld.script linux-2.4.30-linux-mips.org/arch/mips/zboot/xxs1500/ld.script
--- linux-2.4.30/arch/mips/zboot/xxs1500/ld.script	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/xxs1500/ld.script	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,147 @@
+OUTPUT_ARCH(mips)
+ENTRY(start)
+SECTIONS
+{
+  /* Read-only sections, merged into text segment: */
+  /* . = 0x81000000; */
+  .init          : { *(.init)		} =0
+  .text      :
+  {
+    _ftext = . ;
+    *(.text)
+    *(.rodata)
+    *(.rodata1)
+    /* .gnu.warning sections are handled specially by elf32.em.  */
+    *(.gnu.warning)
+  } =0
+  .kstrtab : { *(.kstrtab) }
+
+  . = ALIGN(16);		/* Exception table */
+  __start___ex_table = .;
+  __ex_table : { *(__ex_table) }
+  __stop___ex_table = .;
+
+  __start___dbe_table = .;	/* Exception table for data bus errors */
+  __dbe_table : { *(__dbe_table) }
+  __stop___dbe_table = .;
+
+  __start___ksymtab = .;	/* Kernel symbol table */
+  __ksymtab : { *(__ksymtab) }
+  __stop___ksymtab = .;
+
+  _etext = .;
+
+  . = ALIGN(8192);
+  .data.init_task : { *(.data.init_task) }
+
+  /* Startup code */
+  . = ALIGN(4096);
+  __init_begin = .;
+  .text.init : { *(.text.init) }
+  .data.init : { *(.data.init) }
+  . = ALIGN(16);
+  __setup_start = .;
+  .setup.init : { *(.setup.init) }
+  __setup_end = .;
+  __initcall_start = .;
+  .initcall.init : { *(.initcall.init) }
+  __initcall_end = .;
+  . = ALIGN(4096);	/* Align double page for init_task_union */
+  __init_end = .;
+
+  . = ALIGN(4096);
+  .data.page_aligned : { *(.data.idt) }
+
+  . = ALIGN(32);
+  .data.cacheline_aligned : { *(.data.cacheline_aligned) }
+
+  .fini      : { *(.fini)    } =0
+  .reginfo : { *(.reginfo) }
+  /* Adjust the address for the data segment.  We want to adjust up to
+     the same address within the page on the next page up.  It would
+     be more correct to do this:
+       . = .;
+     The current expression does not correctly handle the case of a
+     text segment ending precisely at the end of a page; it causes the
+     data segment to skip a page.  The above expression does not have
+     this problem, but it will currently (2/95) cause BFD to allocate
+     a single segment, combining both text and data, for this case.
+     This will prevent the text segment from being shared among
+     multiple executions of the program; I think that is more
+     important than losing a page of the virtual address space (note
+     that no actual memory is lost; the page which is skipped can not
+     be referenced).  */
+  . = .;
+  .data    :
+  {
+    _fdata = . ;
+    *(.data)
+
+   /* Align the initial ramdisk image (INITRD) on page boundaries. */
+   . = ALIGN(4096);
+   __rd_start = .;
+   *(.initrd)
+   __rd_end = .;
+   . = ALIGN(4096);
+
+    CONSTRUCTORS
+  }
+  .data1   : { *(.data1) }
+  _gp = . + 0x8000;
+  .lit8 : { *(.lit8) }
+  .lit4 : { *(.lit4) }
+  .ctors         : { *(.ctors)   }
+  .dtors         : { *(.dtors)   }
+  .got           : { *(.got.plt) *(.got) }
+  .dynamic       : { *(.dynamic) }
+  /* We want the small data sections together, so single-instruction offsets
+     can access them all, and initialized data all before uninitialized, so
+     we can shorten the on-disk segment size.  */
+  .sdata     : { *(.sdata) }
+  . = ALIGN(4);
+  _edata  =  .;
+  PROVIDE (edata = .);
+
+  __bss_start = .;
+  _fbss = .;
+  .sbss      : { *(.sbss) *(.scommon) }
+  .bss       :
+  {
+   *(.dynbss)
+   *(.bss)
+   *(COMMON)
+   .  = ALIGN(4);
+  _end = . ;
+  PROVIDE (end = .);
+  }
+
+  /* Sections to be discarded */
+  /DISCARD/ :
+  {
+        *(.text.exit)
+        *(.data.exit)
+        *(.exitcall.exit)
+  }
+
+  /* This is the MIPS specific mdebug section.  */
+  .mdebug : { *(.mdebug) }
+  /* These are needed for ELF backends which have not yet been
+     converted to the new style linker.  */
+  .stab 0 : { *(.stab) }
+  .stabstr 0 : { *(.stabstr) }
+  /* DWARF debug sections.
+     Symbols in the .debug DWARF section are relative to the beginning of the
+     section so we begin .debug at 0.  It's not clear yet what needs to happen
+     for the others.   */
+  .debug          0 : { *(.debug) }
+  .debug_srcinfo  0 : { *(.debug_srcinfo) }
+  .debug_aranges  0 : { *(.debug_aranges) }
+  .debug_pubnames 0 : { *(.debug_pubnames) }
+  .debug_sfnames  0 : { *(.debug_sfnames) }
+  .line           0 : { *(.line) }
+  /* These must appear regardless of  .  */
+  .gptab.sdata : { *(.gptab.data) *(.gptab.sdata) }
+  .gptab.sbss : { *(.gptab.bss) *(.gptab.sbss) }
+  .comment : { *(.comment) }
+  .note : { *(.note) }
+}
diff -Nur linux-2.4.30/arch/mips/zboot/xxs1500/Makefile linux-2.4.30-linux-mips.org/arch/mips/zboot/xxs1500/Makefile
--- linux-2.4.30/arch/mips/zboot/xxs1500/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips/zboot/xxs1500/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,123 @@
+# arch/mips/compressed/alchemy/Makefile
+# 
+# Makefile for Alchemy Semiconductor Pb1[015]00 boards.
+# All of the boot loader code was derived from the ppc
+# boot code.
+#
+# Copyright 2001,2002 MontaVista Software Inc.
+#
+# Author: Mark A. Greer
+#	  mgreer@mvista.com
+# Ported and modified for mips support by 
+# Pete Popov <ppopov@mvista.com>
+#
+# This program is free software; you can redistribute  it and/or modify it
+# under  the terms of  the GNU General  Public License as published by the
+# Free Software Foundation;  either version 2 of the  License, or (at your
+# option) any later version.
+
+.c.s:
+	$(CC) $(CFLAGS) -S -o $*.s $<
+.s.o:
+	$(AS) -o $*.o $<
+.c.o:
+	$(CC) $(CFLAGS) -D__BOOTER__ -c -o $*.o $<
+.S.s:
+	$(CPP) $(AFLAGS) -o $*.o $<
+.S.o:
+	$(CC) $(AFLAGS) -c -o $*.o $<
+
+#########################################################################
+# START BOARD SPECIFIC VARIABLES
+BNAME=xxs1500
+
+
+# These two variables control where the zImage is stored
+# in flash and loaded in memory. If you change either one,
+# be sure to make the appropriate change to the zImage
+# rule.
+RAM_LOAD_ADDR = 0x81000000
+FLASH_LOAD_ADDR = 0xBF000000
+
+# These two variables specify the free ram region
+# that can be used for temporary malloc area
+AVAIL_RAM_START=0x80400000
+AVAIL_RAM_END=0x80800000
+
+# This one must match the LOADADDR in arch/mips/Makefile!
+LOADADDR=0x80100000
+# END BOARD SPECIFIC VARIABLES
+#########################################################################
+
+ZLINKFLAGS = -T ld.script -Ttext $(RAM_LOAD_ADDR)
+
+OBJECTS := head.o ../common/misc-common.o ../common/misc-simple.o \
+	../common/au1k_uart.o ../common/string.o ../common/ctype.o
+LIBS := ../lib/zlib.a
+
+ENTRY := ../utils/entry
+OFFSET := ../utils/offset
+SIZE := ../utils/size
+
+all: zImage
+
+clean:
+	rm -rf *.o vmlinux* zvmlinux.*
+
+head.o: head.S $(TOPDIR)/vmlinux
+	$(CC) -DFLASH_LOAD_ADDR=$(FLASH_LOAD_ADDR) $(AFLAGS) \
+	-DKERNEL_ENTRY=$(shell sh $(ENTRY) $(NM) $(TOPDIR)/vmlinux ) \
+	-c -o $*.o $<
+
+../common/misc-simple.o:
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 -DZIMAGE_OFFSET=0 \
+		-DAVAIL_RAM_START=$(AVAIL_RAM_START) \
+		-DAVAIL_RAM_END=$(AVAIL_RAM_END) \
+		-DLOADADDR=$(LOADADDR) \
+		-DZIMAGE_SIZE=0 -c -o $@ $*.c
+
+# This is the first pass at building the boot loader image,
+# without knowing the file offset where the vmlinuz.gz
+# kernel will end up.  We build this image, check the offset,
+# and then rebuild it with the correct offset and size
+# passed to mips-simple.c
+zvmlinux.no: $(OBJECTS) $(LIBS) ../images/vmlinux.gz
+	$(LD) $(ZLINKFLAGS) -o $@.tmp $(OBJECTS) $(LIBS)
+	$(OBJCOPY) -R .comment \
+		--add-section=image=../images/vmlinux.gz \
+		$@.tmp $@
+	# rm -f $@.tmp
+
+
+# This is the final image we build, now that we know what
+# the vmlinuz.gz offset is.
+zvmlinux: $(OBJECTS) $(LIBS) ../images/vmlinux.gz zvmlinux.no
+	$(CC) $(CFLAGS) -DINITRD_OFFSET=0 -DINITRD_SIZE=0 \
+		-DZIMAGE_OFFSET=$(shell sh $(OFFSET) $(OBJDUMP) $@.no image) \
+		-DZIMAGE_SIZE=$(shell sh $(SIZE) $(OBJDUMP) $@.no image) \
+		-D__BOOTER__ \
+		-DAVAIL_RAM_START=$(AVAIL_RAM_START) \
+		-DAVAIL_RAM_END=$(AVAIL_RAM_END) \
+		-DLOADADDR=$(LOADADDR) \
+		-c -o ../common/misc-simple.o ../common/misc-simple.c
+	$(LD) $(ZLINKFLAGS) -o $@.tmp $(OBJECTS) $(LIBS)
+	$(OBJCOPY) -R .comment \
+		--add-section=image=../images/vmlinux.gz \
+		$@.tmp $@
+	$(OBJCOPY) --adjust-section-vma=image+$(RAM_LOAD_ADDR) $@
+	$(OBJCOPY) --adjust-section-vma=image+$(shell sh $(OFFSET) \
+	$(OBJDUMP) $@.no image ) $@
+	# rm -f $@.tmp
+	# rm -f $@.no
+
+
+# Here we manipulate the image in order to get it the necessary
+# srecord file we need.
+zImage: zvmlinux
+	mv zvmlinux ../images/$@.$(BNAME)
+	$(OBJCOPY) --set-section-flags=image=alloc,load,code ../images/$@.$(BNAME)
+	$(OBJCOPY) -O srec --adjust-vma 0x3e000000 \
+	../images/$@.$(BNAME) ../images/$@.$(BNAME).srec
+	# rm ../images/vmlinux.gz
+
+include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/arch/mips64/defconfig linux-2.4.30-linux-mips.org/arch/mips64/defconfig
--- linux-2.4.30/arch/mips64/defconfig	2005-01-19 15:09:30.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/defconfig	2005-05-12 17:57:08.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -470,9 +470,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -658,7 +660,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips64/defconfig-atlas linux-2.4.30-linux-mips.org/arch/mips64/defconfig-atlas
--- linux-2.4.30/arch/mips64/defconfig-atlas	2005-01-19 15:09:30.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/defconfig-atlas	2005-05-12 17:57:08.000000000 +0200
@@ -28,8 +28,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -232,11 +232,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -314,9 +309,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -474,7 +471,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips64/defconfig-decstation linux-2.4.30-linux-mips.org/arch/mips64/defconfig-decstation
--- linux-2.4.30/arch/mips64/defconfig-decstation	2005-01-19 15:09:30.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/defconfig-decstation	2005-05-12 17:57:08.000000000 +0200
@@ -28,8 +28,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -224,11 +224,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -307,9 +302,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -477,7 +474,6 @@
 CONFIG_SERIAL_DEC_CONSOLE=y
 # CONFIG_DZ is not set
 CONFIG_ZS=y
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips64/defconfig-ip22 linux-2.4.30-linux-mips.org/arch/mips64/defconfig-ip22
--- linux-2.4.30/arch/mips64/defconfig-ip22	2005-01-19 15:09:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/defconfig-ip22	2005-05-12 17:57:08.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -235,11 +235,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -319,9 +314,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -488,7 +485,6 @@
 # CONFIG_SERIAL_TXX9_CONSOLE is not set
 # CONFIG_TXX927_SERIAL is not set
 CONFIG_IP22_SERIAL=y
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips64/defconfig-ip27 linux-2.4.30-linux-mips.org/arch/mips64/defconfig-ip27
--- linux-2.4.30/arch/mips64/defconfig-ip27	2005-01-19 15:09:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/defconfig-ip27	2005-05-12 17:57:08.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -470,9 +470,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -658,7 +660,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips64/defconfig-jaguar linux-2.4.30-linux-mips.org/arch/mips64/defconfig-jaguar
--- linux-2.4.30/arch/mips64/defconfig-jaguar	2005-01-19 15:09:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/defconfig-jaguar	2005-05-12 17:57:08.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -227,11 +227,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -403,7 +398,6 @@
 # CONFIG_SERIAL_TXX9 is not set
 # CONFIG_SERIAL_TXX9_CONSOLE is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips64/defconfig-malta linux-2.4.30-linux-mips.org/arch/mips64/defconfig-malta
--- linux-2.4.30/arch/mips64/defconfig-malta	2005-01-19 15:09:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/defconfig-malta	2005-05-12 17:57:08.000000000 +0200
@@ -22,16 +22,19 @@
 #
 # CONFIG_ACER_PICA_61 is not set
 # CONFIG_MIPS_BOSPORUS is not set
+# CONFIG_MIPS_FICMMP is not set
 # CONFIG_MIPS_MIRAGE is not set
 # CONFIG_MIPS_DB1000 is not set
 # CONFIG_MIPS_DB1100 is not set
 # CONFIG_MIPS_DB1500 is not set
 # CONFIG_MIPS_DB1550 is not set
+# CONFIG_MIPS_DB1200 is not set
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_PB1200 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -146,9 +149,9 @@
 CONFIG_BINFMT_ELF=y
 CONFIG_MIPS32_COMPAT=y
 CONFIG_MIPS32_O32=y
-# CONFIG_MIPS32_N32 is not set
+CONFIG_MIPS32_N32=y
 CONFIG_BINFMT_ELF32=y
-# CONFIG_BINFMT_MISC is not set
+CONFIG_BINFMT_MISC=y
 # CONFIG_OOM_KILLER is not set
 # CONFIG_CMDLINE_BOOL is not set
 
@@ -235,11 +238,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -271,8 +269,83 @@
 #
 # ATA/IDE/MFM/RLL support
 #
-# CONFIG_IDE is not set
+CONFIG_IDE=y
+
+#
+# IDE, ATA and ATAPI Block devices
+#
+CONFIG_BLK_DEV_IDE=y
+
+#
+# Please see Documentation/ide.txt for help/info on IDE drives
+#
+# CONFIG_BLK_DEV_HD_IDE is not set
 # CONFIG_BLK_DEV_HD is not set
+# CONFIG_BLK_DEV_IDE_SATA is not set
+CONFIG_BLK_DEV_IDEDISK=y
+# CONFIG_IDEDISK_MULTI_MODE is not set
+# CONFIG_IDEDISK_STROKE is not set
+# CONFIG_BLK_DEV_IDECS is not set
+# CONFIG_BLK_DEV_DELKIN is not set
+CONFIG_BLK_DEV_IDECD=y
+CONFIG_BLK_DEV_IDETAPE=y
+CONFIG_BLK_DEV_IDEFLOPPY=y
+# CONFIG_BLK_DEV_IDESCSI is not set
+# CONFIG_IDE_TASK_IOCTL is not set
+
+#
+# IDE chipset support/bugfixes
+#
+# CONFIG_BLK_DEV_CMD640 is not set
+# CONFIG_BLK_DEV_CMD640_ENHANCED is not set
+# CONFIG_BLK_DEV_ISAPNP is not set
+CONFIG_BLK_DEV_IDEPCI=y
+CONFIG_BLK_DEV_GENERIC=y
+CONFIG_IDEPCI_SHARE_IRQ=y
+CONFIG_BLK_DEV_IDEDMA_PCI=y
+# CONFIG_BLK_DEV_OFFBOARD is not set
+CONFIG_BLK_DEV_IDEDMA_FORCED=y
+CONFIG_IDEDMA_PCI_AUTO=y
+# CONFIG_IDEDMA_ONLYDISK is not set
+CONFIG_BLK_DEV_IDEDMA=y
+# CONFIG_IDEDMA_PCI_WIP is not set
+# CONFIG_BLK_DEV_ADMA100 is not set
+# CONFIG_BLK_DEV_AEC62XX is not set
+# CONFIG_BLK_DEV_ALI15X3 is not set
+# CONFIG_WDC_ALI15X3 is not set
+# CONFIG_BLK_DEV_AMD74XX is not set
+# CONFIG_AMD74XX_OVERRIDE is not set
+# CONFIG_BLK_DEV_ATIIXP is not set
+# CONFIG_BLK_DEV_CMD64X is not set
+# CONFIG_BLK_DEV_TRIFLEX is not set
+# CONFIG_BLK_DEV_CY82C693 is not set
+# CONFIG_BLK_DEV_CS5530 is not set
+# CONFIG_BLK_DEV_HPT34X is not set
+# CONFIG_HPT34X_AUTODMA is not set
+# CONFIG_BLK_DEV_HPT366 is not set
+CONFIG_BLK_DEV_PIIX=y
+# CONFIG_BLK_DEV_NS87415 is not set
+# CONFIG_BLK_DEV_OPTI621 is not set
+# CONFIG_BLK_DEV_PDC202XX_OLD is not set
+# CONFIG_PDC202XX_BURST is not set
+# CONFIG_BLK_DEV_PDC202XX_NEW is not set
+# CONFIG_BLK_DEV_RZ1000 is not set
+# CONFIG_BLK_DEV_SC1200 is not set
+# CONFIG_BLK_DEV_SVWKS is not set
+# CONFIG_BLK_DEV_SIIMAGE is not set
+# CONFIG_BLK_DEV_SIS5513 is not set
+# CONFIG_BLK_DEV_SLC90E66 is not set
+# CONFIG_BLK_DEV_TRM290 is not set
+# CONFIG_BLK_DEV_VIA82CXXX is not set
+# CONFIG_IDE_CHIPSETS is not set
+CONFIG_IDEDMA_AUTO=y
+# CONFIG_IDEDMA_IVB is not set
+# CONFIG_DMA_NONPCI is not set
+# CONFIG_BLK_DEV_ATARAID is not set
+# CONFIG_BLK_DEV_ATARAID_PDC is not set
+# CONFIG_BLK_DEV_ATARAID_HPT is not set
+# CONFIG_BLK_DEV_ATARAID_MEDLEY is not set
+# CONFIG_BLK_DEV_ATARAID_SII is not set
 
 #
 # SCSI support
@@ -317,9 +390,11 @@
 # CONFIG_SCSI_MEGARAID is not set
 # CONFIG_SCSI_MEGARAID2 is not set
 # CONFIG_SCSI_SATA is not set
+# CONFIG_SCSI_SATA_AHCI is not set
 # CONFIG_SCSI_SATA_SVW is not set
 # CONFIG_SCSI_ATA_PIIX is not set
 # CONFIG_SCSI_SATA_NV is not set
+# CONFIG_SCSI_SATA_QSTOR is not set
 # CONFIG_SCSI_SATA_PROMISE is not set
 # CONFIG_SCSI_SATA_SX4 is not set
 # CONFIG_SCSI_SATA_SIL is not set
@@ -477,7 +552,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips64/defconfig-ocelotc linux-2.4.30-linux-mips.org/arch/mips64/defconfig-ocelotc
--- linux-2.4.30/arch/mips64/defconfig-ocelotc	2005-01-19 15:09:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/defconfig-ocelotc	2005-05-12 17:57:08.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -231,11 +231,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -453,7 +448,6 @@
 # CONFIG_SERIAL_TXX9 is not set
 # CONFIG_SERIAL_TXX9_CONSOLE is not set
 # CONFIG_TXX927_SERIAL is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips64/defconfig-sb1250-swarm linux-2.4.30-linux-mips.org/arch/mips64/defconfig-sb1250-swarm
--- linux-2.4.30/arch/mips64/defconfig-sb1250-swarm	2005-01-19 15:09:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/defconfig-sb1250-swarm	2005-05-12 17:57:08.000000000 +0200
@@ -30,8 +30,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -90,6 +90,7 @@
 # CONFIG_SIBYTE_TBPROF is not set
 CONFIG_SIBYTE_GENBUS_IDE=y
 CONFIG_SMP_CAPABLE=y
+CONFIG_MIPS_RTC=y
 # CONFIG_SNI_RM200_PCI is not set
 # CONFIG_TANBAC_TB0226 is not set
 # CONFIG_TANBAC_TB0229 is not set
@@ -253,11 +254,6 @@
 #
 # CONFIG_IPX is not set
 # CONFIG_ATALK is not set
-
-#
-# Appletalk devices
-#
-# CONFIG_DEV_APPLETALK is not set
 # CONFIG_DECNET is not set
 # CONFIG_BRIDGE is not set
 # CONFIG_X25 is not set
@@ -432,7 +428,6 @@
 CONFIG_SIBYTE_SB1250_DUART=y
 CONFIG_SIBYTE_SB1250_DUART_CONSOLE=y
 CONFIG_SERIAL_CONSOLE=y
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 CONFIG_UNIX98_PTYS=y
 CONFIG_UNIX98_PTY_COUNT=256
 
diff -Nur linux-2.4.30/arch/mips64/defconfig-sead linux-2.4.30-linux-mips.org/arch/mips64/defconfig-sead
--- linux-2.4.30/arch/mips64/defconfig-sead	2005-01-19 15:09:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/defconfig-sead	2005-05-12 17:57:08.000000000 +0200
@@ -28,8 +28,8 @@
 # CONFIG_MIPS_PB1000 is not set
 # CONFIG_MIPS_PB1100 is not set
 # CONFIG_MIPS_PB1500 is not set
-# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_PB1550 is not set
+# CONFIG_MIPS_HYDROGEN3 is not set
 # CONFIG_MIPS_XXS1500 is not set
 # CONFIG_MIPS_MTX1 is not set
 # CONFIG_COGENT_CSB250 is not set
@@ -242,7 +242,6 @@
 CONFIG_SERIAL_CONSOLE=y
 # CONFIG_SERIAL_EXTENDED is not set
 # CONFIG_SERIAL_NONSTANDARD is not set
-# CONFIG_MIPS_HYDROGEN3_BUTTONS is not set
 # CONFIG_UNIX98_PTYS is not set
 
 #
diff -Nur linux-2.4.30/arch/mips64/kernel/binfmt_elfn32.c linux-2.4.30-linux-mips.org/arch/mips64/kernel/binfmt_elfn32.c
--- linux-2.4.30/arch/mips64/kernel/binfmt_elfn32.c	2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.30-linux-mips.org/arch/mips64/kernel/binfmt_elfn32.c	2005-05-12 17:57:08.000000000 +0200
@@ -116,4 +116,7 @@
 #undef MODULE_DESCRIPTION
 #undef MODULE_AUTHOR
 
+#undef TASK_SIZE
+#define TASK_SIZE TASK_SIZE32
+
 #include "../../../fs/binfmt_elf.c"
diff -Nur linux-2.4.30/arch/mips64/kernel/binfmt_elfo32.c linux-2.4.30-linux-mips.org/arch/mips64/kernel/binfmt_elfo32.c
--- linux-2.4.30/arch/mips64/kernel/binfmt_elfo32.c	2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.30-linux-mips.org/arch/mips64/kernel/binfmt_elfo32.c	2005-05-12 17:57:08.000000000 +0200
@@ -137,4 +137,7 @@
 #undef MODULE_DESCRIPTION
 #undef MODULE_AUTHOR
 
+#undef TASK_SIZE
+#define TASK_SIZE TASK_SIZE32
+
 #include "../../../fs/binfmt_elf.c"
diff -Nur linux-2.4.30/arch/mips64/kernel/head.S linux-2.4.30-linux-mips.org/arch/mips64/kernel/head.S
--- linux-2.4.30/arch/mips64/kernel/head.S	2004-02-18 14:36:30.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/kernel/head.S	2005-05-12 17:57:08.000000000 +0200
@@ -91,6 +91,21 @@
 	__INIT
 
 NESTED(kernel_entry, 16, sp)			# kernel entry point
+	.set	push
+	/*
+	 * For the moment disable interrupts, mark the kernel mode and
+	 * set ST0_KX so that the CPU does not spit fire when using
+	 * 64-bit addresses.  A full initialization of the CPU's status
+	 * register is done later in per_cpu_trap_init().
+	 */
+	mfc0	t0, CP0_STATUS
+	or	t0, ST0_CU0|ST0_KX|0x1f
+	xor	t0, 0x1f
+	mtc0	t0, CP0_STATUS
+
+	.set	noreorder
+	sll	zero,3				# ehb
+	.set	reorder
 
 	ori	sp, 0xf				# align stack on 16 byte.
 	xori	sp, 0xf
@@ -103,8 +118,6 @@
 
 	ARC64_TWIDDLE_PC
 
-	CLI					# disable interrupts
-
 	/*
 	 * The firmware/bootloader passes argc/argp/envp
 	 * to us as arguments.  But clear bss first because
@@ -125,6 +138,7 @@
 	dsubu	sp, 4*SZREG			# init stack pointer
 
 	j	init_arch
+	.set	pop
 	END(kernel_entry)
 
 #ifdef CONFIG_SMP
@@ -133,6 +147,23 @@
  * function after setting up the stack and gp registers.
  */
 NESTED(smp_bootstrap, 16, sp)
+	.set	push
+	/*
+	 * For the moment disable interrupts and bootstrap exception
+	 * vectors, mark the kernel mode and set ST0_KX so that the CPU
+	 * does not spit fire when using 64-bit addresses.  A full
+	 * initialization of the CPU's status register is done later in
+	 * per_cpu_trap_init().
+	 */
+	mfc0	t0, CP0_STATUS
+	or	t0, ST0_CU0|ST0_BEV|ST0_KX|0x1f
+	xor	t0, ST0_BEV|0x1f
+	mtc0	t0, CP0_STATUS
+
+	.set	noreorder
+	sll	zero,3				# ehb
+	.set	reorder
+
 #ifdef CONFIG_SGI_IP27
 	GET_NASID_ASM	t1
 	dli	t0, KLDIR_OFFSET + (KLI_KERN_VARS * KLDIR_ENT_SIZE) + \
@@ -146,19 +177,8 @@
 	ARC64_TWIDDLE_PC
 #endif /* CONFIG_SGI_IP27 */
 
-	CLI
-
-	/*
-	 * For the moment set ST0_KU so the CPU will not spit fire when
-	 * executing 64-bit instructions.  The full initialization of the
-	 * CPU's status register is done later in per_cpu_trap_init().
-	 */
-        mfc0	t0, CP0_STATUS
-        or	t0, ST0_KX
-	mtc0	t0, CP0_STATUS
-
 	jal	start_secondary			# XXX: IP27: cboot
-
+	.set	pop
 	END(smp_bootstrap)
 #endif /* CONFIG_SMP */
 
diff -Nur linux-2.4.30/arch/mips64/kernel/ioctl32.c linux-2.4.30-linux-mips.org/arch/mips64/kernel/ioctl32.c
--- linux-2.4.30/arch/mips64/kernel/ioctl32.c	2005-01-19 15:09:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/kernel/ioctl32.c	2005-05-12 17:57:08.000000000 +0200
@@ -2352,7 +2352,7 @@
 	IOCTL32_HANDLER(AUTOFS_IOC_SETTIMEOUT32, ioc_settimeout),
 	IOCTL32_DEFAULT(AUTOFS_IOC_EXPIRE),
 	IOCTL32_DEFAULT(AUTOFS_IOC_EXPIRE_MULTI),
-	IOCTL32_DEFAULT(AUTOFS_IOC_PROTSUBVER),
+	IOCTL32_DEFAULT(AUTOFS_IOC_PROTOSUBVER),
 	IOCTL32_DEFAULT(AUTOFS_IOC_ASKREGHOST),
 	IOCTL32_DEFAULT(AUTOFS_IOC_TOGGLEREGHOST),
 	IOCTL32_DEFAULT(AUTOFS_IOC_ASKUMOUNT),
diff -Nur linux-2.4.30/arch/mips64/kernel/linux32.c linux-2.4.30-linux-mips.org/arch/mips64/kernel/linux32.c
--- linux-2.4.30/arch/mips64/kernel/linux32.c	2005-04-04 03:42:19.000000000 +0200
+++ linux-2.4.30-linux-mips.org/arch/mips64/kernel/linux32.c	2005-05-12 17:57:08.000000000 +0200
@@ -1187,72 +1187,19 @@
    lseek back to original location.  They fail just like lseek does on
    non-seekable files.  */
 
-asmlinkage ssize_t sys32_pread(unsigned int fd, char * buf,
-			       size_t count, u32 unused, u64 a4, u64 a5)
+asmlinkage ssize_t sys32_pread(unsigned int fd, char *buf,
+                                 size_t count, u32 unused, u64 a4, u64 a5)
 {
-	ssize_t ret;
-	struct file * file;
-	ssize_t (*read)(struct file *, char *, size_t, loff_t *);
-	loff_t pos;
-
-	ret = -EBADF;
-	file = fget(fd);
-	if (!file)
-		goto bad_file;
-	if (!(file->f_mode & FMODE_READ))
-		goto out;
-	pos = merge_64(a4, a5);
-	ret = locks_verify_area(FLOCK_VERIFY_READ, file->f_dentry->d_inode,
-				file, pos, count);
-	if (ret)
-		goto out;
-	ret = -EINVAL;
-	if (!file->f_op || !(read = file->f_op->read))
-		goto out;
-	if (pos < 0)
-		goto out;
-	ret = read(file, buf, count, &pos);
-	if (ret > 0)
-		dnotify_parent(file->f_dentry, DN_ACCESS);
-out:
-	fput(file);
-bad_file:
-	return ret;
+	return sys_pread(fd, buf, count, merge_64(a4, a5));
 }
 
 asmlinkage ssize_t sys32_pwrite(unsigned int fd, const char * buf,
 			        size_t count, u32 unused, u64 a4, u64 a5)
 {
-	ssize_t ret;
-	struct file * file;
-	ssize_t (*write)(struct file *, const char *, size_t, loff_t *);
-	loff_t pos;
-
-	ret = -EBADF;
-	file = fget(fd);
-	if (!file)
-		goto bad_file;
-	if (!(file->f_mode & FMODE_WRITE))
-		goto out;
-	pos = merge_64(a4, a5);
-	ret = locks_verify_area(FLOCK_VERIFY_WRITE, file->f_dentry->d_inode,
-				file, pos, count);
-	if (ret)
-		goto out;
-	ret = -EINVAL;
-	if (!file->f_op || !(write = file->f_op->write))
-		goto out;
-	if (pos < 0)
-		goto out;
-
-	ret = write(file, buf, count, &pos);
-	if (ret > 0)
-		dnotify_parent(file->f_dentry, DN_MODIFY);
-out:
-	fput(file);
-bad_file:
-	return ret;
+	return sys_pwrite(fd, buf, count, merge_64(a4, a5));
 }
+
+
 /*
  * Ooo, nasty.  We need here to frob 32-bit unsigned longs to
  * 64-bit unsigned longs.
diff -Nur linux-2.4.30/arch/mips64/kernel/process.c linux-2.4.30-linux-mips.org/arch/mips64/kernel/process.c
--- linux-2.4.30/arch/mips64/kernel/process.c	2003-08-25 13:44:40.000000000 +0200
+++ linux-2.4.30-linux-mips.org/arch/mips64/kernel/process.c	2005-05-12 17:57:11.000000000 +0200
@@ -125,6 +125,25 @@
 	return 1;
 }
 
+void dump_regs(elf_greg_t *gp, struct pt_regs *regs)
+{
+	int i;
+
+	for (i = 0; i < EF_REG0; i++)
+		gp[i] = 0;
+	gp[EF_REG0] = 0;
+	for (i = 1; i <= 31; i++)
+		gp[EF_REG0 + i] = regs->regs[i];
+	gp[EF_REG26] = 0;
+	gp[EF_REG27] = 0;
+	gp[EF_LO] = regs->lo;
+	gp[EF_HI] = regs->hi;
+	gp[EF_CP0_EPC] = regs->cp0_epc;
+	gp[EF_CP0_BADVADDR] = regs->cp0_badvaddr;
+	gp[EF_CP0_STATUS] = regs->cp0_status;
+	gp[EF_CP0_CAUSE] = regs->cp0_cause;
+}
+
 /*
  * Create a kernel thread
  */
diff -Nur linux-2.4.30/arch/mips64/kernel/scall_64.S linux-2.4.30-linux-mips.org/arch/mips64/kernel/scall_64.S
--- linux-2.4.30/arch/mips64/kernel/scall_64.S	2005-01-19 15:09:32.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/kernel/scall_64.S	2005-05-12 17:57:11.000000000 +0200
@@ -102,15 +102,14 @@
 
 trace_a_syscall:
 	SAVE_STATIC
-	sd	t2,PT_R1(sp)
+	move	s0, t2
 	jal	syscall_trace
-	ld	t2,PT_R1(sp)
 
 	ld	a0, PT_R4(sp)		# Restore argument registers
 	ld	a1, PT_R5(sp)
 	ld	a2, PT_R6(sp)
 	ld	a3, PT_R7(sp)
-	jalr	t2
+	jalr	s0
 
 	li	t0, -EMAXERRNO - 1	# error?
 	sltu	t0, t0, v0
diff -Nur linux-2.4.30/arch/mips64/kernel/scall_n32.S linux-2.4.30-linux-mips.org/arch/mips64/kernel/scall_n32.S
--- linux-2.4.30/arch/mips64/kernel/scall_n32.S	2005-01-19 15:09:32.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/kernel/scall_n32.S	2005-05-12 17:57:11.000000000 +0200
@@ -106,15 +106,14 @@
 
 trace_a_syscall:
 	SAVE_STATIC
-	sd	t2,PT_R1(sp)
+	move	s0, t2
 	jal	syscall_trace
-	ld	t2,PT_R1(sp)
 
 	ld	a0, PT_R4(sp)		# Restore argument registers
 	ld	a1, PT_R5(sp)
 	ld	a2, PT_R6(sp)
 	ld	a3, PT_R7(sp)
-	jalr	t2
+	jalr	s0
 
 	li	t0, -EMAXERRNO - 1	# error?
 	sltu	t0, t0, v0
diff -Nur linux-2.4.30/arch/mips64/kernel/scall_o32.S linux-2.4.30-linux-mips.org/arch/mips64/kernel/scall_o32.S
--- linux-2.4.30/arch/mips64/kernel/scall_o32.S	2005-01-19 15:09:32.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/kernel/scall_o32.S	2005-05-12 17:57:11.000000000 +0200
@@ -118,9 +118,8 @@
 	sd	a6, PT_R10(sp)
 	sd	a7, PT_R11(sp)
 
-	sd	t2,PT_R1(sp)
+	move	s0, t2
 	jal	syscall_trace
-	ld	t2,PT_R1(sp)
 
 	ld	a0, PT_R4(sp)		# Restore argument registers
 	ld	a1, PT_R5(sp)
@@ -129,7 +128,7 @@
 	ld	a4, PT_R8(sp)
 	ld	a5, PT_R9(sp)
 
-	jalr	t2
+	jalr	s0
 
 	li	t0, -EMAXERRNO - 1	# error?
 	sltu	t0, t0, v0
@@ -576,6 +575,8 @@
 sys_call_table:
 	syscalltable
 
+	.purgem	sys
+
 	.macro	sys function, nargs
 	.byte	\nargs
 	.endm
diff -Nur linux-2.4.30/arch/mips64/kernel/setup.c linux-2.4.30-linux-mips.org/arch/mips64/kernel/setup.c
--- linux-2.4.30/arch/mips64/kernel/setup.c	2005-01-19 15:09:32.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/kernel/setup.c	2005-05-12 17:57:11.000000000 +0200
@@ -129,14 +129,6 @@
 	 */
 	load_mmu();
 
-	/*
-	 * On IP27, I am seeing the TS bit set when the kernel is loaded.
-	 * Maybe because the kernel is in ckseg0 and not xkphys? Clear it
-	 * anyway ...
-	 */
-	clear_c0_status(ST0_BEV|ST0_TS|ST0_CU1|ST0_CU2|ST0_CU3);
-	set_c0_status(ST0_CU0|ST0_KX|ST0_SX|ST0_FR);
-
 	start_kernel();
 }
 
diff -Nur linux-2.4.30/arch/mips64/kernel/signal_n32.c linux-2.4.30-linux-mips.org/arch/mips64/kernel/signal_n32.c
--- linux-2.4.30/arch/mips64/kernel/signal_n32.c	2005-01-19 15:09:33.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/kernel/signal_n32.c	2005-05-12 17:57:11.000000000 +0200
@@ -68,7 +68,7 @@
 };
 
 extern asmlinkage int restore_sigcontext(struct pt_regs *regs, struct sigcontext *sc);
-extern int inline setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc);
+extern int setup_sigcontext(struct pt_regs *regs, struct sigcontext *sc);
 
 asmlinkage void sysn32_rt_sigreturn(abi64_no_regargs, struct pt_regs regs)
 {
diff -Nur linux-2.4.30/arch/mips64/kernel/traps.c linux-2.4.30-linux-mips.org/arch/mips64/kernel/traps.c
--- linux-2.4.30/arch/mips64/kernel/traps.c	2005-01-19 15:09:33.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/kernel/traps.c	2005-05-12 17:57:11.000000000 +0200
@@ -462,9 +462,10 @@
 	}
 	ll_task = current;
 
+	compute_return_epc(regs);
+
 	regs->regs[(opcode & RT) >> 16] = value;
 
-	compute_return_epc(regs);
 	return;
 
 sig:
@@ -495,8 +496,8 @@
 		goto sig;
 	}
 	if (ll_bit == 0 || ll_task != current) {
-		regs->regs[reg] = 0;
 		compute_return_epc(regs);
+		regs->regs[reg] = 0;
 		return;
 	}
 
@@ -505,9 +506,9 @@
 		goto sig;
 	}
 
+	compute_return_epc(regs);
 	regs->regs[reg] = 1;
 
-	compute_return_epc(regs);
 	return;
 
 sig:
@@ -809,13 +810,18 @@
 void __init per_cpu_trap_init(void)
 {
 	unsigned int cpu = smp_processor_id();
+	unsigned int status_set = ST0_CU0|ST0_FR|ST0_KX|ST0_SX|ST0_UX;
 
-	/* Some firmware leaves the BEV flag set, clear it.  */
-	clear_c0_status(ST0_CU1|ST0_CU2|ST0_CU3|ST0_BEV);
-	set_c0_status(ST0_CU0|ST0_FR|ST0_KX|ST0_SX|ST0_UX);
-
+	/*
+	 * Disable coprocessors, enable 64-bit addressing and set FPU
+	 * for the 32/32 FPR register model.  Reset the BEV flag that
+	 * some firmware may have left set and the TS bit (for IP27).
+	 * Set XX for ISA IV code to work.
+	 */
 	if (current_cpu_data.isa_level == MIPS_CPU_ISA_IV)
-		set_c0_status(ST0_XX);
+		status_set |= ST0_XX;
+	change_c0_status(ST0_CU|ST0_FR|ST0_BEV|ST0_TS|ST0_KX|ST0_SX|ST0_UX,
+			 status_set);
 
 	/*
 	 * Some MIPS CPUs have a dedicated interrupt vector which reduces the
@@ -825,13 +831,11 @@
 		set_c0_cause(CAUSEF_IV);
 
 	cpu_data[cpu].asid_cache = ASID_FIRST_VERSION;
-	write_c0_context(((long)(&pgd_current[cpu])) << 23);
-	write_c0_wired(0);
+	TLBMISS_HANDLER_SETUP();
 
 	atomic_inc(&init_mm.mm_count);
 	current->active_mm = &init_mm;
-	if (current->mm)
-		BUG();
+	BUG_ON(current->mm);
 	enter_lazy_tlb(&init_mm, current, cpu);
 }
 
@@ -842,8 +846,6 @@
 	extern char except_vec4;
 	unsigned long i;
 
-	per_cpu_trap_init();
-
 	/* Copy the generic exception handlers to their final destination. */
 	memcpy((void *) KSEG0         , &except_vec0_generic, 0x80);
 	memcpy((void *)(KSEG0 + 0x180), &except_vec3_generic, 0x80);
@@ -933,6 +935,5 @@
 
 	flush_icache_range(KSEG0, KSEG0 + 0x400);
 
-	atomic_inc(&init_mm.mm_count);	/* XXX UP?  */
-	current->active_mm = &init_mm;
+	per_cpu_trap_init();
 }
diff -Nur linux-2.4.30/arch/mips64/mm/cerr-sb1.c linux-2.4.30-linux-mips.org/arch/mips64/mm/cerr-sb1.c
--- linux-2.4.30/arch/mips64/mm/cerr-sb1.c	2004-02-18 14:36:30.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/mm/cerr-sb1.c	2005-05-12 17:57:11.000000000 +0200
@@ -252,14 +252,14 @@
 
 /* Masks to select bits for Hamming parity, mask_72_64[i] for bit[i] */
 static const uint64_t mask_72_64[8] = {
-	0x0738C808099264FFL,
-	0x38C808099264FF07L,
-	0xC808099264FF0738L,
-	0x08099264FF0738C8L,
-	0x099264FF0738C808L,
-	0x9264FF0738C80809L,
-	0x64FF0738C8080992L,
-	0xFF0738C808099264L
+	0x0738C808099264FFULL,
+	0x38C808099264FF07ULL,
+	0xC808099264FF0738ULL,
+	0x08099264FF0738C8ULL,
+	0x099264FF0738C808ULL,
+	0x9264FF0738C80809ULL,
+	0x64FF0738C8080992ULL,
+	0xFF0738C808099264ULL
 };
 
 /* Calculate the parity on a range of bits */
@@ -331,9 +331,9 @@
 				    ((lru >> 4) & 0x3),
 				    ((lru >> 6) & 0x3));
 		}
-		va = (taglo & 0xC0000FFFFFFFE000) | addr;
+		va = (taglo & 0xC0000FFFFFFFE000ULL) | addr;
 		if ((taglo & (1 << 31)) && (((taglo >> 62) & 0x3) == 3))
-			va |= 0x3FFFF00000000000;
+			va |= 0x3FFFF00000000000ULL;
 		valid = ((taghi >> 29) & 1);
 		if (valid) {
 			tlo_tmp = taglo & 0xfff3ff;
@@ -474,7 +474,7 @@
 		: "r" ((way << 13) | addr));
 
 		taglo = ((unsigned long long)taglohi << 32) | taglolo;
-		pa = (taglo & 0xFFFFFFE000) | addr;
+		pa = (taglo & 0xFFFFFFE000ULL) | addr;
 		if (way == 0) {
 			lru = (taghi >> 14) & 0xff;
 			prom_printf("[Bank %d Set 0x%02x]  LRU > %d %d %d %d > MRU\n",
diff -Nur linux-2.4.30/arch/mips64/mm/c-r4k.c linux-2.4.30-linux-mips.org/arch/mips64/mm/c-r4k.c
--- linux-2.4.30/arch/mips64/mm/c-r4k.c	2005-01-19 15:09:33.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/mm/c-r4k.c	2005-05-12 17:57:11.000000000 +0200
@@ -867,9 +867,16 @@
 	 * normally they'd suffer from aliases but magic in the hardware deals
 	 * with that for us so we don't need to take care ourselves.
 	 */
-	if (c->cputype != CPU_R10000 && c->cputype != CPU_R12000)
-		if (c->dcache.waysize > PAGE_SIZE)
-		        c->dcache.flags |= MIPS_CACHE_ALIASES;
+	switch (c->cputype) {
+	case CPU_R10000:
+	case CPU_R12000:
+		break;
+	case CPU_24K:
+		if (!(read_c0_config7() & (1 << 16)))
+	default:
+			if (c->dcache.waysize > PAGE_SIZE)
+				c->dcache.flags |= MIPS_CACHE_ALIASES;
+	}
 
 	switch (c->cputype) {
 	case CPU_20KC:
@@ -1070,9 +1077,6 @@
 	setup_scache();
 	coherency_setup();
 
-	if (c->dcache.sets * c->dcache.ways > PAGE_SIZE)
-		c->dcache.flags |= MIPS_CACHE_ALIASES;
-
 	r4k_blast_dcache_page_setup();
 	r4k_blast_dcache_page_indexed_setup();
 	r4k_blast_dcache_setup();
diff -Nur linux-2.4.30/arch/mips64/mm/tlbex-r4k.S linux-2.4.30-linux-mips.org/arch/mips64/mm/tlbex-r4k.S
--- linux-2.4.30/arch/mips64/mm/tlbex-r4k.S	2004-02-18 14:36:30.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/mm/tlbex-r4k.S	2005-05-12 17:57:11.000000000 +0200
@@ -151,11 +151,9 @@
 	ld	k0, 0(k1)			# get even pte
 	ld	k1, 8(k1)			# get odd pte
 	PTE_RELOAD k0 k1
-	rm9000_tlb_hazard
-	b	1f
-	 tlbwr
-1:	nop
-	rm9000_tlb_hazard
+	mtc0_tlbw_hazard
+	tlbwr
+1:	tlbw_eret_hazard
 	eret
 
 9:						# handle the vmalloc range
@@ -163,11 +161,9 @@
 	ld	k0, 0(k1)			# get even pte
 	ld	k1, 8(k1)			# get odd pte
 	PTE_RELOAD k0 k1
-	rm9000_tlb_hazard
-	b	1f
-	 tlbwr
-1:	nop
-	rm9000_tlb_hazard
+	mtc0_tlbw_hazard
+	tlbwr
+1:	tlbw_eret_hazard
 	eret
 END(handle_vec1_r4k)
 
@@ -195,10 +191,9 @@
 	ld	k0, 0(k1)			# get even pte
 	ld	k1, 8(k1)			# get odd pte
 	PTE_RELOAD k0 k1
-	rm9000_tlb_hazard
-	nop
+	mtc0_tlbw_hazard
 	tlbwr
-	rm9000_tlb_hazard
+	tlbw_eret_hazard
 	eret
 
 9:						# handle the vmalloc range
@@ -206,10 +201,9 @@
 	ld	k0, 0(k1)			# get even pte
 	ld	k1, 8(k1)			# get odd pte
 	PTE_RELOAD k0 k1
-	rm9000_tlb_hazard
-	nop
+	mtc0_tlbw_hazard
 	tlbwr
-	rm9000_tlb_hazard
+	tlbw_eret_hazard
 	eret
 END(handle_vec1_r10k)
 
diff -Nur linux-2.4.30/arch/mips64/mm/tlb-r4k.c linux-2.4.30-linux-mips.org/arch/mips64/mm/tlb-r4k.c
--- linux-2.4.30/arch/mips64/mm/tlb-r4k.c	2005-01-19 15:09:33.000000000 +0100
+++ linux-2.4.30-linux-mips.org/arch/mips64/mm/tlb-r4k.c	2005-05-12 17:57:11.000000000 +0200
@@ -1,24 +1,12 @@
 /*
- * Carsten Langgaard, carstenl@mips.com
- * Copyright (C) 2002 MIPS Technologies, Inc.  All rights reserved.
- *
- * 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
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
  * 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.
- *
- * MIPS64 CPU variant specific MMU routines.
- * These routine are not optimized in any way, they are done in a generic way
- * so they can be used on all MIPS64 compliant CPUs, and also done in an
- * attempt not to break anything for the R4xx0 style CPUs.
+ * Copyright (C) 1996 David S. Miller (dm@engr.sgi.com)
+ * Copyright (C) 1997, 1998, 1999, 2000 Ralf Baechle ralf@gnu.org
+ * Carsten Langgaard, carstenl@mips.com
+ * Copyright (C) 2002 MIPS Technologies, Inc.  All rights reserved.
  */
 #include <linux/init.h>
 #include <linux/sched.h>
@@ -30,9 +18,6 @@
 #include <asm/pgtable.h>
 #include <asm/system.h>
 
-#undef DEBUG_TLB
-#undef DEBUG_TLBUPDATE
-
 extern void except_vec1_r4k(void);
 
 /* CP0 hazard avoidance. */
@@ -46,31 +31,23 @@
 	unsigned long old_ctx;
 	int entry;
 
-#ifdef DEBUG_TLB
-	printk("[tlball]");
-#endif
-
 	local_irq_save(flags);
 	/* Save old context and create impossible VPN2 value */
 	old_ctx = read_c0_entryhi();
-	write_c0_entryhi(XKPHYS);
 	write_c0_entrylo0(0);
 	write_c0_entrylo1(0);
-	BARRIER;
 
 	entry = read_c0_wired();
 
 	/* Blast 'em all away. */
-	while(entry < current_cpu_data.tlbsize) {
-	        /* Make sure all entries differ. */
-	        write_c0_entryhi(XKPHYS+entry*0x2000);
+	while (entry < current_cpu_data.tlbsize) {
+	        write_c0_entryhi(XKPHYS + entry*0x2000);
 		write_c0_index(entry);
-		BARRIER;
+		mtc0_tlbw_hazard();
 		tlb_write_indexed();
-		BARRIER;
 		entry++;
 	}
-	BARRIER;
+	tlbw_use_hazard();
 	write_c0_entryhi(old_ctx);
 	local_irq_restore(flags);
 }
@@ -79,12 +56,8 @@
 {
 	int cpu = smp_processor_id();
 
-	if (cpu_context(cpu, mm) != 0) {
-#ifdef DEBUG_TLB
-		printk("[tlbmm<%d>]", mm->context);
-#endif
+	if (cpu_context(cpu, mm) != 0)
 		drop_mmu_context(mm,cpu);
-	}
 }
 
 void local_flush_tlb_range(struct mm_struct *mm, unsigned long start,
@@ -96,10 +69,6 @@
 		unsigned long flags;
 		int size;
 
-#ifdef DEBUG_TLB
-		printk("[tlbrange<%02x,%08lx,%08lx>]", (mm->context & ASID_MASK),
-		       start, end);
-#endif
 		local_irq_save(flags);
 		size = (end - start + (PAGE_SIZE - 1)) >> PAGE_SHIFT;
 		size = (size + 1) >> 1;
@@ -110,25 +79,25 @@
 			start &= (PAGE_MASK << 1);
 			end += ((PAGE_SIZE << 1) - 1);
 			end &= (PAGE_MASK << 1);
-			while(start < end) {
+			while (start < end) {
 				int idx;
 
 				write_c0_entryhi(start | newpid);
 				start += (PAGE_SIZE << 1);
-				BARRIER;
+				mtc0_tlbw_hazard();
 				tlb_probe();
 				BARRIER;
 				idx = read_c0_index();
 				write_c0_entrylo0(0);
 				write_c0_entrylo1(0);
-				if(idx < 0)
+				if (idx < 0)
 					continue;
 				/* Make sure all entries differ. */
 				write_c0_entryhi(XKPHYS+idx*0x2000);
-				BARRIER;
+				mtc0_tlbw_hazard();
 				tlb_write_indexed();
-				BARRIER;
 			}
+			tlbw_use_hazard();
 			write_c0_entryhi(oldpid);
 		} else {
 			drop_mmu_context(mm, cpu);
@@ -145,28 +114,26 @@
 		unsigned long flags;
 		unsigned long oldpid, newpid, idx;
 
-#ifdef DEBUG_TLB
-		printk("[tlbpage<%d,%08lx>]", vma->vm_mm->context, page);
-#endif
 		newpid = cpu_asid(cpu, vma->vm_mm);
 		page &= (PAGE_MASK << 1);
 		local_irq_save(flags);
 		oldpid = read_c0_entryhi();
 		write_c0_entryhi(page | newpid);
-		BARRIER;
+		mtc0_tlbw_hazard();
 		tlb_probe();
 		BARRIER;
 		idx = read_c0_index();
 		write_c0_entrylo0(0);
 		write_c0_entrylo1(0);
-		if(idx < 0)
+		if (idx < 0)
 			goto finish;
 		/* Make sure all entries differ. */
 		write_c0_entryhi(XKPHYS+idx*0x2000);
-		BARRIER;
+		mtc0_tlbw_hazard();
 		tlb_write_indexed();
+		tlbw_use_hazard();
+
 	finish:
-		BARRIER;
 		write_c0_entryhi(oldpid);
 		local_irq_restore(flags);
 	}
@@ -186,7 +153,7 @@
 
 	local_irq_save(flags);
 	write_c0_entryhi(page);
-	BARRIER;
+	mtc0_tlbw_hazard();
 	tlb_probe();
 	BARRIER;
 	idx = read_c0_index();
@@ -195,10 +162,12 @@
 	if (idx >= 0) {
 		/* Make sure all entries differ. */
 		write_c0_entryhi(KSEG0+idx*0x2000);
+		mtc0_tlbw_hazard();
 		tlb_write_indexed();
+		tlbw_use_hazard();
 	}
-	BARRIER;
 	write_c0_entryhi(oldpid);
+
 	local_irq_restore(flags);
 }
 
@@ -208,7 +177,6 @@
 void __update_tlb(struct vm_area_struct * vma, unsigned long address, pte_t pte)
 {
 	unsigned long flags;
-	unsigned int asid;
 	pgd_t *pgdp;
 	pmd_t *pmdp;
 	pte_t *ptep;
@@ -222,70 +190,58 @@
 
 	pid = read_c0_entryhi() & ASID_MASK;
 
-#ifdef DEBUG_TLB
-	if ((pid != (cpu_asid(smp_processor_id(), vma->vm_mm))) ||
-	   (cpu_context(smp_processor_id(), vma->vm_mm) == 0)) {
-		printk("update_mmu_cache: Wheee, bogus tlbpid mmpid=%d"
-		       "tlbpid=%d\n", (int) (cpu_context(smp_processor_id(),
-		       vma->vm_mm) & ASID_MASK), pid);
-	}
-#endif
-
 	local_irq_save(flags);
 	address &= (PAGE_MASK << 1);
-	write_c0_entryhi(address | (pid));
+	write_c0_entryhi(address | pid);
 	pgdp = pgd_offset(vma->vm_mm, address);
-	BARRIER;
+	mtc0_tlbw_hazard();
 	tlb_probe();
 	BARRIER;
 	pmdp = pmd_offset(pgdp, address);
 	idx = read_c0_index();
 	ptep = pte_offset(pmdp, address);
-	BARRIER;
 	write_c0_entrylo0(pte_val(*ptep++) >> 6);
 	write_c0_entrylo1(pte_val(*ptep) >> 6);
-	write_c0_entryhi(address | (pid));
-	BARRIER;
-	if(idx < 0) {
+	write_c0_entryhi(address | pid);
+	mtc0_tlbw_hazard();
+	if (idx < 0)
 		tlb_write_random();
-	} else {
+	else
 		tlb_write_indexed();
-	}
-	BARRIER;
+	tlbw_use_hazard();
 	write_c0_entryhi(pid);
-	BARRIER;
 	local_irq_restore(flags);
 }
 
-void add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
-				      unsigned long entryhi, unsigned long pagemask)
+void __init add_wired_entry(unsigned long entrylo0, unsigned long entrylo1,
+			    unsigned long entryhi, unsigned long pagemask)
 {
-        unsigned long flags;
-        unsigned long wired;
-        unsigned long old_pagemask;
-        unsigned long old_ctx;
-
-        local_irq_save(flags);
-        /* Save old context and create impossible VPN2 value */
-        old_ctx = (read_c0_entryhi() & ASID_MASK);
-        old_pagemask = read_c0_pagemask();
-        wired = read_c0_wired();
-        write_c0_wired(wired + 1);
-        write_c0_index(wired);
-        BARRIER;
-        write_c0_pagemask(pagemask);
-        write_c0_entryhi(entryhi);
-        write_c0_entrylo0(entrylo0);
-        write_c0_entrylo1(entrylo1);
-        BARRIER;
-        tlb_write_indexed();
-        BARRIER;
-
-        write_c0_entryhi(old_ctx);
-        BARRIER;
-        write_c0_pagemask(old_pagemask);
-        local_flush_tlb_all();
-        local_irq_restore(flags);
+	unsigned long flags;
+	unsigned long wired;
+	unsigned long old_pagemask;
+	unsigned long old_ctx;
+
+	local_irq_save(flags);
+	/* Save old context and create impossible VPN2 value */
+	old_ctx = read_c0_entryhi() & ASID_MASK;
+	old_pagemask = read_c0_pagemask();
+	wired = read_c0_wired();
+	write_c0_wired(wired + 1);
+	write_c0_index(wired);
+	BARRIER;
+	write_c0_pagemask(pagemask);
+	write_c0_entryhi(entryhi);
+	write_c0_entrylo0(entrylo0);
+	write_c0_entrylo1(entrylo1);
+	mtc0_tlbw_hazard();
+	tlb_write_indexed();
+	tlbw_use_hazard();
+
+	write_c0_entryhi(old_ctx);
+	BARRIER;
+	write_c0_pagemask(old_pagemask);
+	local_flush_tlb_all();
+	local_irq_restore(flags);
 }
 
 /*
@@ -317,17 +273,15 @@
 	}
 
 	write_c0_index(temp_tlb_entry);
-	BARRIER;
 	write_c0_pagemask(pagemask);
 	write_c0_entryhi(entryhi);
 	write_c0_entrylo0(entrylo0);
 	write_c0_entrylo1(entrylo1);
-	BARRIER;
+	mtc0_tlbw_hazard();
 	tlb_write_indexed();
-	BARRIER;
+	tlbw_use_hazard();
 
 	write_c0_entryhi(old_ctx);
-	BARRIER;
 	write_c0_pagemask(old_pagemask);
 out:
 	local_irq_restore(flags);
@@ -348,15 +302,23 @@
 		return;
 
 	config1 = read_c0_config1();
-	if (!((config1 >> 7) & 3))
-		panic("No MMU present");
+	if (!((config >> 7) & 3))
+		panic("No TLB present");
 
 	c->tlbsize = ((config1 >> 25) & 0x3f) + 1;
 }
 
 void __init r4k_tlb_init(void)
 {
-	unsigned long config = read_c0_config();
+	unsigned int config = read_c0_config();
+
+	/*
+	 * You should never change this register:
+	 *   - On R4600 1.7 the tlbp never hits for pages smaller than
+	 *     the value in the c0_pagemask register.
+	 *   - The entire mm handling assumes the c0_pagemask register to
+	 *     be set for 4kb pages.
+	 */
 	probe_tlb(config);
 	write_c0_pagemask(PM_DEFAULT_MASK);
 	write_c0_wired(0);
diff -Nur linux-2.4.30/Documentation/Alchemy.README linux-2.4.30-linux-mips.org/Documentation/Alchemy.README
--- linux-2.4.30/Documentation/Alchemy.README	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/Documentation/Alchemy.README	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,105 @@
+README for arch/mips/au1000/common directory and subdirectories
+
+Embedded Edge, Embedded Alley
+
+MOTIVATION
+----------
+
+The Alchemy family of SOCs are very similarly architecturally, which
+lends itself well to sharing a great deal of kernel code, and drivers.
+
+The code in this directory is utilized by all Alchemy-based designs,
+and is intended to be as board-independent as possible, but with the
+appropriate SOC changes.
+
+HOW TO CHOOSE THE KERNEL TIMER
+
+	There can be three sources for the kernel timer tick with the Au1x00:
+	- MIPS CP0 Count/Compare mechanism
+	- Au1XXX RTC/TOY integrated timer
+	- External timer
+	The CP0 Counter operates at the frequency of the Au1 core; however,
+	it does not count during the WAIT/Idle modes.
+
+	The TOY/RTC operates from an optional 32kHz external oscillator; both
+	count during the WAIT/Idle modes, but only the TOY continues to
+	count during the Sleep mode of the Au1x00.
+
+	The external timer could be anything, presumably connected via a GPIO.
+	This might be used in systems where an external battery-backed clock
+	source is required since early Au1x00 silicon drained the battery
+	too quickly in Sleep mode. With an external timer source, the WAIT/Idle
+	mode can be utilized.
+
+	The different abilities of the timing sources lead to several possible
+	configurations for building this time management code:
+
+	1) Use CP0 Count/Compare as the ticker and source for real-time clock;
+	   note this prevents the use of the WAIT instruction and its power savings.
+	2) Use Cp0 Count/Compare as the ticker and RTC/TOY as the source for
+	   the real-time clock (again, can not use the WAIT instruction).
+	3) Use the TOY/RTC as the ticker source and also as the source for
+	   the real-time clock. This mode permits the use of the WAIT
+	   instruction and the Au1 core Idle modes for power savings.
+	4) Use of an external timer as the ticker source, which also permits
+	   the use of WAIT instruction and its power savings.
+
+	The support for these scenarios is modularized as such:
+	CONFIG_SOC_AU1X00_32KHZ is declared if the board populates a 32kHz
+	oscillator. If so, it is assumed that the TOY/RTC will be used for
+	the kernel's real-time clock, and thus will enable the appropriate
+	code.
+
+	CONFIG_SOC_AU1X00_32KHZ_TIMER is declared if the board is to utilize
+	the TOY/RTC as the kernel timer tick. In the absence of this 
+	declaration, the kernel will assume the use of the MIPS CP0 Counter.
+
+	If an external timer source is utilized, then arch/mips/au1000/common/
+	time.c will need to	be examined and changed appropriately.
+
+	When the TOY/RTC is used to source the real-time clock, it is the TOY
+	that is used, not the RTC. While this is definitely possibly confusing,
+	keep in mind that the TOY is able to count during Sleep modes, and so
+	it makes sense to maintain the real-time clock in the TOY so that an
+	alarm based on date/time configured in the TOY match 2 register can
+	be utilized to awake the Au1x00 system from sleep state.
+
+	Since the 32KHz crystal is not [typically] an integer multiple
+	of HZ, there will be some drift associated with utilizing it
+	as a timer tick source. Here's the scoop.
+	1/32768 = 0.00003051s is 30.5microseconds per tick
+	if HZ=100:
+		trim is 32768/100 rounded UP = 328
+		328 * 30.5us = 0.010004us
+	So the periodic rate is extremely close to the desired 0.01,
+	but it is not exact. Thus the drift is calculated as:
+		0.01 / 0.000004 = 2500
+	Which means that every 2500 timer intervals, the timer has
+	drift one HZ. So we must account for that.
+	if HZ=1000:
+		trim is 32768/1000 rounded UP = 33
+		33 * 30.5us = 0.0010065us
+		0.001 / 0.0000065 = 153.8
+
+	Also, in rtcm1_irq(), the code checks to make sure that the counter
+	is not at the value we're about to program into the match; since it
+	takes one counter tick for the update to occur. This can occur since
+	Linux' interrupt response time is highly irregular, and so the RTC
+	may already be near the next RTCM1 value by the time this handler
+	is invoked. To do this, the code estimates how many RTC ticks will
+	elapse by the time the update occurs, and figures this into the
+	check. The check is complicated since the RTC can be ahead of RTCM1,
+	and vice versa, and that can be valid, or not, depending upon whether
+	or not RTC, or RTCM1, has rolled-over.
+
+	See also Documentation/mips/time.README.
+
+HOW TO ADD A BOARD
+------------------
+ 
+TBD
+
+TO-DO LIST
+----------
+
+TBD
diff -Nur linux-2.4.30/Documentation/Configure.help linux-2.4.30-linux-mips.org/Documentation/Configure.help
--- linux-2.4.30/Documentation/Configure.help	2005-04-04 03:42:19.000000000 +0200
+++ linux-2.4.30-linux-mips.org/Documentation/Configure.help	2005-05-12 18:01:29.000000000 +0200
@@ -16579,6 +16579,25 @@
   The module will be called usb-midi.o. If you want to compile it as a
   module, say M here and read <file:Documentation/modules.txt>.
 
+MMC/SD Card support
+CONFIG_MMC
+  Say Y or M here if you want to enable support for Multimedia or
+  Secure Digital Cards.  You will need an MMC/SD socket and drivers
+  for your socket.  At the moment, access to the information required
+  to write a driver for SD or SDIO requires signing and NDA that
+  precludes the release of an open source driver, so only MMC is
+  supported at this time.  If you want to compile it as a module, say
+  M here and read <file:Documentation/modules.txt>.
+
+MMC debugging
+CONFIG_MMC_DEBUG
+  Say Y here if you want to enable debug output from the MMC drivers.
+
+MMC debugging verbosity
+CONFIG_MMC_DEBUG_VERBOSE
+  Enter a number from 0 (quiet) to 3 (noisy) here to control the
+  verbosity of MMC debug output.
+
 Minix fs support
 CONFIG_MINIX_FS
   Minix is a simple operating system used in many classes about OS's.
diff -Nur linux-2.4.30/drivers/char/au1000_gpio.c linux-2.4.30-linux-mips.org/drivers/char/au1000_gpio.c
--- linux-2.4.30/drivers/char/au1000_gpio.c	2003-08-25 13:44:41.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/char/au1000_gpio.c	2005-05-12 17:57:19.000000000 +0200
@@ -246,7 +246,7 @@
 
 static struct miscdevice au1000gpio_miscdev =
 {
-	GPIO_MINOR,
+	MISC_DYNAMIC_MINOR,
 	"au1000_gpio",
 	&au1000gpio_fops
 };
diff -Nur linux-2.4.30/drivers/char/au1550_psc_spi.c linux-2.4.30-linux-mips.org/drivers/char/au1550_psc_spi.c
--- linux-2.4.30/drivers/char/au1550_psc_spi.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/char/au1550_psc_spi.c	2005-05-12 17:57:19.000000000 +0200
@@ -0,0 +1,466 @@
+/*
+ *  Driver for Alchemy Au1550 SPI on the PSC.
+ *
+ * Copyright 2004 Embedded Edge, LLC.
+ *	dan@embeddededge.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  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.
+ */
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/miscdevice.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+#include <asm/io.h>
+#include <asm/au1000.h>
+#include <asm/au1550_spi.h>
+#include <asm/au1xxx_psc.h>
+
+#ifdef CONFIG_MIPS_PB1550
+#include <asm/pb1550.h>
+#endif
+
+#ifdef CONFIG_MIPS_DB1550
+#include <asm/db1x00.h>
+#endif
+
+#ifdef CONFIG_MIPS_PB1200
+#include <asm/pb1200.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
+ * copies the bytes out to the SPI, and whatever is returned is placed
+ * in the same buffer.  Kinda weird, maybe we'll change it, but for now
+ * it works OK.
+ * I didn't implement any DMA yet, and it's a debate about the necessity.
+ * The SPI clocks are usually quite fast, so data is sent/received as
+ * quickly as you can stuff the FIFO.  The overhead of DMA and interrupts
+ * are usually far greater than the data transfer itself.  If, however,
+ * we find applications that move large amounts of data, we may choose
+ * use the overhead of buffering and DMA to do the work.
+ */
+
+/* The maximum clock rate specified in the manual is 2mHz.
+*/
+#define MAX_BAUD_RATE	(2 * 1000000)
+#define PSC_INTCLK_RATE (32 * 1000000)
+
+static	int	inuse;
+
+/* We have to know what the user requested for the data length
+ * so we know how to stuff the fifo.  The FIFO is 32 bits wide,
+ * and we have to load it with the bits to go in a single transfer.
+ */
+static	uint	spi_datalen;
+
+static int
+au1550spi_master_done( int ms )
+{
+	int timeout=ms;
+	volatile psc_spi_t *sp;
+
+	sp = (volatile psc_spi_t *)SPI_PSC_BASE;
+
+	/* Loop until MD is set or timeout has expired */
+	while(!(sp->psc_spievent & PSC_SPIEVNT_MD) &&  timeout--) udelay(1000);
+
+	if ( !timeout )
+		return 0;
+	else
+		sp->psc_spievent |= PSC_SPIEVNT_MD;
+
+	return 1;
+}
+
+static int
+au1550spi_open(struct inode *inode, struct file *file)
+{
+	if (inuse)
+		return -EBUSY;
+
+	inuse = 1;
+
+	MOD_INC_USE_COUNT;
+
+	return 0;
+}
+
+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;
+	volatile psc_spi_t *sp;
+
+	/* Get the number of bytes per transfer.
+	*/
+	bytelen = ((spi_datalen - 1) / 8) + 1;
+
+	/* User needs to send us multiple of this count.
+	*/
+	if ((count % bytelen) != 0)
+		return -EINVAL;
+
+	rp = wp = (unsigned char *)bp;
+	retval = rcount = count;
+
+	/* Reset the FIFO.
+	*/
+	sp = (volatile psc_spi_t *)SPI_PSC_BASE;
+	sp->psc_spipcr = (PSC_SPIPCR_RC | PSC_SPIPCR_TC);
+	au_sync();
+	do {
+		pcr = sp->psc_spipcr;
+		au_sync();
+	} while (pcr != 0);
+
+	/* Prime the transmit FIFO.
+	*/
+	while (count > 0) {
+		fifoword = 0;
+		for (i=0; i<bytelen; i++) {
+			fifoword <<= 8;
+			if (get_user(sb, wp) < 0)
+				return -EFAULT;
+			fifoword |= sb;
+			wp++;
+		}
+		count -= bytelen;
+		if (count <= 0)
+			fifoword |= PSC_SPITXRX_LC;
+		sp->psc_spitxrx = fifoword;
+		au_sync();
+		stat = sp->psc_spistat;
+		au_sync();
+		if (stat & PSC_SPISTAT_TF)
+			break;
+	}
+
+	/* Start the transfer.
+	*/
+	sp->psc_spipcr = PSC_SPIPCR_MS;
+	au_sync();
+
+	/* Now, just keep the transmit fifo full and empty the receive.
+	*/
+	while (count > 0) {
+		stat = sp->psc_spistat;
+		au_sync();
+		while ((stat & PSC_SPISTAT_RE) == 0) {
+			fifoword = sp->psc_spitxrx;
+			au_sync();
+			for (i=0; i<bytelen; i++) {
+				sb = fifoword & 0xff;
+				if (put_user(sb, rp) < 0)
+					return -EFAULT;
+				fifoword >>= 8;
+				rp++;
+			}
+			rcount -= bytelen;
+			stat = sp->psc_spistat;
+			au_sync();
+		}
+		if ((stat & PSC_SPISTAT_TF) == 0) {
+			fifoword = 0;
+			for (i=0; i<bytelen; i++) {
+				fifoword <<= 8;
+				if (get_user(sb, wp) < 0)
+					return -EFAULT;
+				fifoword |= sb;
+				wp++;
+			}
+			count -= bytelen;
+			if (count <= 0)
+				fifoword |= PSC_SPITXRX_LC;
+			sp->psc_spitxrx = fifoword;
+			au_sync();
+		}
+	}
+
+	/* All of the bytes for transmit have been written.  Hang
+	 * out waiting for any residual bytes that are yet to be
+	 * read from the fifo.
+	 */
+	while (rcount > 0) {
+		stat = sp->psc_spistat;
+		au_sync();
+		if ((stat & PSC_SPISTAT_RE) == 0) {
+			fifoword = sp->psc_spitxrx;
+			au_sync();
+			for (i=0; i<bytelen; i++) {
+				sb = fifoword & 0xff;
+				if (put_user(sb, rp) < 0)
+					return -EFAULT;
+				fifoword >>= 8;
+				rp++;
+			}
+			rcount -= bytelen;
+		}
+	}
+
+	/* Wait for MasterDone event. 30ms timeout */
+	if (!au1550spi_master_done(30) ) retval = -EFAULT;
+	return retval;
+}
+
+static int
+au1550spi_release(struct inode *inode, struct file *file)
+{
+	MOD_DEC_USE_COUNT;
+
+	inuse = 0;
+
+	return 0;
+}
+
+/* Set the baud rate closest to the request, then return the actual
+ * value we are using.
+ */
+static uint
+set_baud_rate(uint baud)
+{
+	uint	rate, tmpclk, brg, ctl, stat;
+	volatile psc_spi_t *sp;
+
+	/* For starters, the input clock is divided by two.
+	*/
+	tmpclk = PSC_INTCLK_RATE/2;
+
+	rate = tmpclk / baud;
+
+	/* The dividers work as follows:
+	 *	baud = tmpclk / (2 * (brg + 1))
+	 */
+	 brg = (rate/2) - 1;
+
+	 /* Test BRG to ensure it will fit into the 6 bits allocated.
+	 */
+
+	 /* Make sure the device is disabled while we make the change.
+	 */
+	sp = (volatile psc_spi_t *)SPI_PSC_BASE;
+	ctl = sp->psc_spicfg;
+	au_sync();
+	sp->psc_spicfg = ctl & ~PSC_SPICFG_DE_ENABLE;
+	au_sync();
+	ctl = PSC_SPICFG_CLR_BAUD(ctl);
+	ctl |= PSC_SPICFG_SET_BAUD(brg);
+	sp->psc_spicfg = ctl;
+	au_sync();
+
+	/* If the device was running prior to getting here, wait for
+	 * it to restart.
+	 */
+	if (ctl & PSC_SPICFG_DE_ENABLE) {
+		do {
+			stat = sp->psc_spistat;
+			au_sync();
+		} while ((stat & PSC_SPISTAT_DR) == 0);
+	}
+
+	/* Return the actual value.
+	*/
+	rate = tmpclk / (2 * (brg + 1));
+
+	return(rate);
+}
+
+static uint
+set_word_len(uint len)
+{
+	uint	ctl, stat;
+	volatile psc_spi_t *sp;
+
+	if ((len < 4) || (len > 24))
+		return -EINVAL;
+
+	 /* Make sure the device is disabled while we make the change.
+	 */
+	sp = (volatile psc_spi_t *)SPI_PSC_BASE;
+	ctl = sp->psc_spicfg;
+	au_sync();
+	sp->psc_spicfg = ctl & ~PSC_SPICFG_DE_ENABLE;
+	au_sync();
+	ctl = PSC_SPICFG_CLR_LEN(ctl);
+	ctl |= PSC_SPICFG_SET_LEN(len);
+	sp->psc_spicfg = ctl;
+	au_sync();
+
+	/* If the device was running prior to getting here, wait for
+	 * it to restart.
+	 */
+	if (ctl & PSC_SPICFG_DE_ENABLE) {
+		do {
+			stat = sp->psc_spistat;
+			au_sync();
+		} while ((stat & PSC_SPISTAT_DR) == 0);
+	}
+
+	return 0;
+}
+
+static int
+au1550spi_ioctl(struct inode *inode, struct file *file,
+			    unsigned int cmd, unsigned long arg)
+{
+	int status;
+	u32 val;
+
+	status = 0;
+
+	switch(cmd) {
+	case AU1550SPI_WORD_LEN:
+		status = set_word_len(arg);
+		break;
+
+	case AU1550SPI_SET_BAUD:
+		if (get_user(val, (u32 *)arg)) 
+			return -EFAULT;
+
+		val = set_baud_rate(val);
+		if (put_user(val, (u32 *)arg)) 
+			return -EFAULT;
+		break;
+
+	default:
+		status = -ENOIOCTLCMD;
+
+	}
+
+	return status;
+}
+
+
+static struct file_operations au1550spi_fops =
+{
+	owner:		THIS_MODULE,
+	write:		au1550spi_write,
+	ioctl:		au1550spi_ioctl,
+	open:		au1550spi_open,
+	release:	au1550spi_release,
+};
+
+
+static struct miscdevice au1550spi_miscdev =
+{
+	MISC_DYNAMIC_MINOR,
+	"au1550_spi",
+	&au1550spi_fops
+};
+
+
+int __init
+au1550spi_init(void)
+{
+	uint	clk, rate, stat;
+	volatile psc_spi_t *sp;
+
+	/* Wire up Freq3 as a clock for the SPI.  The PSC does
+	 * factor of 2 divisor, so run a higher rate so we can
+	 * get some granularity to the clock speeds.
+	 * We can't do this in board set up because the frequency
+	 * is computed too late.
+	 */
+	rate = get_au1x00_speed();
+	rate /= PSC_INTCLK_RATE;
+
+	/* The FRDIV in the frequency control is (FRDIV + 1) * 2
+	*/
+	rate /=2;
+	rate--;
+	clk = au_readl(SYS_FREQCTRL1);
+	au_sync();
+	clk &= ~SYS_FC_FRDIV3_MASK;
+	clk |= (rate << SYS_FC_FRDIV3_BIT);
+	clk |= SYS_FC_FE3;
+	au_writel(clk, SYS_FREQCTRL1);
+	au_sync();
+
+	/* Set up the clock source routing to get Freq3 to PSC0_intclk.
+	*/
+	clk = au_readl(SYS_CLKSRC);
+	au_sync();
+	clk &= ~0x03e0;
+	clk |= (5 << 7);
+	au_writel(clk, SYS_CLKSRC);
+	au_sync();
+
+	/* Set up GPIO pin function to drive PSC0_SYNC1, which is
+	 * the SPI Select.
+	 */
+	clk = au_readl(SYS_PINFUNC);
+	au_sync();
+	clk |= 1;
+	au_writel(clk, SYS_PINFUNC);
+	au_sync();
+
+	/* Now, set up the PSC for SPI PIO mode.
+	*/
+	sp = (volatile psc_spi_t *)SPI_PSC_BASE;
+	sp->psc_ctrl = PSC_CTRL_DISABLE;
+	au_sync();
+	sp->psc_sel = PSC_SEL_PS_SPIMODE;
+	sp->psc_spicfg = 0;
+	au_sync();
+	sp->psc_ctrl = PSC_CTRL_ENABLE;
+	au_sync();
+	do {
+		stat = sp->psc_spistat;
+		au_sync();
+	} while ((stat & PSC_SPISTAT_SR) == 0);
+
+	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);
+	spi_datalen = 8;
+	sp->psc_spimsk = PSC_SPIMSK_ALLMASK;
+	au_sync();
+
+	set_baud_rate(1000000);
+
+	sp->psc_spicfg |= PSC_SPICFG_DE_ENABLE;
+	do {
+		stat = sp->psc_spistat;
+		au_sync();
+	} while ((stat & PSC_SPISTAT_DR) == 0);
+
+	misc_register(&au1550spi_miscdev);
+	printk("Au1550 SPI driver\n");
+	return 0;
+}	
+
+
+void __exit
+au1550spi_exit(void)
+{
+	misc_deregister(&au1550spi_miscdev);
+}
+
+
+module_init(au1550spi_init);
+module_exit(au1550spi_exit);
diff -Nur linux-2.4.30/drivers/char/Config.in linux-2.4.30-linux-mips.org/drivers/char/Config.in
--- linux-2.4.30/drivers/char/Config.in	2004-08-08 01:26:04.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/char/Config.in	2005-05-12 17:57:19.000000000 +0200
@@ -313,14 +313,11 @@
 if [ "$CONFIG_OBSOLETE" = "y" -a "$CONFIG_ALPHA_BOOK1" = "y" ]; then
    bool 'Tadpole ANA H8 Support (OBSOLETE)'  CONFIG_H8
 fi
-if [ "$CONFIG_MIPS" = "y" -a "$CONFIG_NEW_TIME_C" = "y" ]; then
-   tristate 'Generic MIPS RTC Support' CONFIG_MIPS_RTC
-fi
 if [ "$CONFIG_SGI_IP22" = "y" ]; then
-   bool 'SGI DS1286 RTC support' CONFIG_SGI_DS1286
+   tristate 'Dallas DS1286 RTC support' CONFIG_DS1286
 fi
 if [ "$CONFIG_SGI_IP27" = "y" ]; then
-   bool 'SGI M48T35 RTC support' CONFIG_SGI_IP27_RTC
+   tristate 'SGI M48T35 RTC support' CONFIG_SGI_IP27_RTC
 fi
 if [ "$CONFIG_TOSHIBA_RBTX4927" = "y" -o "$CONFIG_TOSHIBA_JMR3927" = "y" ]; then
    tristate 'Dallas DS1742 RTC support' CONFIG_DS1742
@@ -383,6 +380,11 @@
       source drivers/char/drm/Config.in
    fi
 fi
+
+if [ "$CONFIG_X86" = "y" ]; then
+   tristate 'ACP Modem (Mwave) support' CONFIG_MWAVE
+fi
+
 endmenu
 
 if [ "$CONFIG_HOTPLUG" = "y" -a "$CONFIG_PCMCIA" != "n" ]; then
@@ -391,6 +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
 fi
 if [ "$CONFIG_MIPS_ITE8172" = "y" ]; then
   tristate ' ITE GPIO' CONFIG_ITE_GPIO
diff -Nur linux-2.4.30/drivers/char/decserial.c linux-2.4.30-linux-mips.org/drivers/char/decserial.c
--- linux-2.4.30/drivers/char/decserial.c	2003-08-25 13:44:41.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/char/decserial.c	2005-05-12 17:57:19.000000000 +0200
@@ -3,95 +3,105 @@
  *      choose the right serial device at boot time
  *
  * triemer 6-SEP-1998
- *      sercons.c is designed to allow the three different kinds 
+ *      sercons.c is designed to allow the three different kinds
  *      of serial devices under the decstation world to co-exist
- *      in the same kernel.  The idea here is to abstract 
+ *      in the same kernel.  The idea here is to abstract
  *      the pieces of the drivers that are common to this file
  *      so that they do not clash at compile time and runtime.
  *
  * HK 16-SEP-1998 v0.002
  *      removed the PROM console as this is not a real serial
  *      device. Added support for PROM console in drivers/char/tty_io.c
- *      instead. Although it may work to enable more than one 
+ *      instead. Although it may work to enable more than one
  *      console device I strongly recommend to use only one.
+ *
+ *	Copyright (C) 2004  Maciej W. Rozycki
  */
 
 #include <linux/config.h>
+#include <linux/errno.h>
 #include <linux/init.h>
+
 #include <asm/dec/machtype.h>
+#include <asm/dec/serial.h>
+
+extern int register_zs_hook(unsigned int channel,
+			    struct dec_serial_hook *hook);
+extern int unregister_zs_hook(unsigned int channel);
+
+extern int register_dz_hook(unsigned int channel,
+			    struct dec_serial_hook *hook);
+extern int unregister_dz_hook(unsigned int channel);
 
+int register_dec_serial_hook(unsigned int channel,
+			     struct dec_serial_hook *hook)
+{
 #ifdef CONFIG_ZS
-extern int zs_init(void);
+	if (IOASIC)
+		return register_zs_hook(channel, hook);
 #endif
-
 #ifdef CONFIG_DZ
-extern int dz_init(void);
+	if (!IOASIC)
+		return register_dz_hook(channel, hook);
 #endif
+	return 0;
+}
 
-#ifdef CONFIG_SERIAL_DEC_CONSOLE
-
+int unregister_dec_serial_hook(unsigned int channel)
+{
 #ifdef CONFIG_ZS
-extern void zs_serial_console_init(void);
+	if (IOASIC)
+		return unregister_zs_hook(channel);
 #endif
-
 #ifdef CONFIG_DZ
-extern void dz_serial_console_init(void);
-#endif
-
+	if (!IOASIC)
+		return unregister_dz_hook(channel);
 #endif
+	return 0;
+}
 
-/* rs_init - starts up the serial interface -
-   handle normal case of starting up the serial interface */
 
-#ifdef CONFIG_SERIAL_DEC
+extern int zs_init(void);
+extern int dz_init(void);
 
+/*
+ * rs_init - starts up the serial interface -
+ * handle normal case of starting up the serial interface
+ */
 int __init rs_init(void)
 {
-
-#if defined(CONFIG_ZS) && defined(CONFIG_DZ)
-    if (IOASIC)
-	return zs_init();
-    else
-	return dz_init();
-#else
-
 #ifdef CONFIG_ZS
-    return zs_init();
+	if (IOASIC)
+		return zs_init();
 #endif
-
 #ifdef CONFIG_DZ
-    return dz_init();
-#endif
-
+	if (!IOASIC)
+		return dz_init();
 #endif
+	return -ENXIO;
 }
 
 __initcall(rs_init);
 
-#endif
 
 #ifdef CONFIG_SERIAL_DEC_CONSOLE
 
-/* dec_serial_console_init handles the special case of starting
- *   up the console on the serial port
+extern void zs_serial_console_init(void);
+extern void dz_serial_console_init(void);
+
+/*
+ * dec_serial_console_init handles the special case of starting
+ * up the console on the serial port
  */
 void __init dec_serial_console_init(void)
 {
-#if defined(CONFIG_ZS) && defined(CONFIG_DZ)
-    if (IOASIC)
-	zs_serial_console_init();
-    else
-	dz_serial_console_init();
-#else
-
 #ifdef CONFIG_ZS
-    zs_serial_console_init();
+	if (IOASIC)
+		zs_serial_console_init();
 #endif
-
 #ifdef CONFIG_DZ
-    dz_serial_console_init();
-#endif
-
+	if (!IOASIC)
+		dz_serial_console_init();
 #endif
 }
 
diff -Nur linux-2.4.30/drivers/char/ds1286.c linux-2.4.30-linux-mips.org/drivers/char/ds1286.c
--- linux-2.4.30/drivers/char/ds1286.c	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/char/ds1286.c	2005-05-12 17:57:21.000000000 +0200
@@ -1,6 +1,10 @@
 /*
  * DS1286 Real Time Clock interface for Linux
  *
+ * Copyright (C) 2003 TimeSys Corp.
+ *                    S. James Hill (James.Hill@timesys.com)
+ *                                  (sjhill@realitydiluted.com)
+ *
  * Copyright (C) 1998, 1999, 2000 Ralf Baechle
  *
  * Based on code written by Paul Gortmaker.
@@ -29,6 +33,7 @@
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/miscdevice.h>
+#include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/ioport.h>
 #include <linux/fcntl.h>
@@ -95,6 +100,12 @@
 	return -EIO;
 }
 
+void rtc_ds1286_wait(void)
+{
+	unsigned char sec = CMOS_READ(RTC_SECONDS);
+	while (sec == CMOS_READ(RTC_SECONDS));
+}
+
 static int ds1286_ioctl(struct inode *inode, struct file *file,
                         unsigned int cmd, unsigned long arg)
 {
@@ -249,23 +260,22 @@
 {
 	spin_lock_irq(&ds1286_lock);
 
-	if (ds1286_status & RTC_IS_OPEN)
-		goto out_busy;
+	if (ds1286_status & RTC_IS_OPEN) {
+		spin_unlock_irq(&ds1286_lock);
+		return -EBUSY;
+	}
 
 	ds1286_status |= RTC_IS_OPEN;
 
-	spin_lock_irq(&ds1286_lock);
+	spin_unlock_irq(&ds1286_lock);
 	return 0;
-
-out_busy:
-	spin_lock_irq(&ds1286_lock);
-	return -EBUSY;
 }
 
 static int ds1286_release(struct inode *inode, struct file *file)
 {
+	spin_lock_irq(&ds1286_lock);
 	ds1286_status &= ~RTC_IS_OPEN;
-
+	spin_unlock_irq(&ds1286_lock);
 	return 0;
 }
 
@@ -276,32 +286,6 @@
 	return 0;
 }
 
-/*
- *	The various file operations we support.
- */
-
-static struct file_operations ds1286_fops = {
-	.llseek		= no_llseek,
-	.read		= ds1286_read,
-	.poll		= ds1286_poll,
-	.ioctl		= ds1286_ioctl,
-	.open		= ds1286_open,
-	.release	= ds1286_release,
-};
-
-static struct miscdevice ds1286_dev=
-{
-	.minor	= RTC_MINOR,
-	.name	= "rtc",
-	.fops	= &ds1286_fops,
-};
-
-int __init ds1286_init(void)
-{
-	printk(KERN_INFO "DS1286 Real Time Clock Driver v%s\n", DS1286_VERSION);
-	return misc_register(&ds1286_dev);
-}
-
 static char *days[] = {
 	"***", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
 };
@@ -528,3 +512,38 @@
 	BCD_TO_BIN(alm_tm->tm_hour);
 	alm_tm->tm_sec = 0;
 }
+
+static struct file_operations ds1286_fops = {
+	.owner		= THIS_MODULE,
+	.llseek		= no_llseek,
+	.read		= ds1286_read,
+	.poll		= ds1286_poll,
+	.ioctl		= ds1286_ioctl,
+	.open		= ds1286_open,
+	.release	= ds1286_release,
+};
+
+static struct miscdevice ds1286_dev =
+{
+	.minor	= RTC_MINOR,
+	.name	= "rtc",
+	.fops	= &ds1286_fops,
+};
+
+static int __init ds1286_init(void)
+{
+	printk(KERN_INFO "DS1286 Real Time Clock Driver v%s\n", DS1286_VERSION);
+	return misc_register(&ds1286_dev);
+}
+
+static void __exit ds1286_exit(void)
+{
+	misc_deregister(&ds1286_dev);
+}
+
+module_init(ds1286_init);
+module_exit(ds1286_exit);
+EXPORT_NO_SYMBOLS;
+
+MODULE_AUTHOR("Ralf Baechle");
+MODULE_LICENSE("GPL");
diff -Nur linux-2.4.30/drivers/char/ds1742.c linux-2.4.30-linux-mips.org/drivers/char/ds1742.c
--- linux-2.4.30/drivers/char/ds1742.c	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/char/ds1742.c	2005-05-12 17:57:21.000000000 +0200
@@ -142,6 +142,7 @@
 	CMOS_WRITE(RTC_WRITE, RTC_CONTROL);
 
 	/* convert */
+	memset(&tm, 0, sizeof(struct rtc_time));
 	to_tm(t, &tm);
 
 	/* check each field one by one */
@@ -216,6 +217,7 @@
 	unsigned long curr_time;
 
 	curr_time = rtc_ds1742_get_time();
+	memset(&tm, 0, sizeof(struct rtc_time));
 	to_tm(curr_time, &tm);
 
 	p = buf;
@@ -251,8 +253,8 @@
 
 void rtc_ds1742_wait(void)
 {
-	while (CMOS_READ(RTC_SECONDS) & 1);
-	while (!(CMOS_READ(RTC_SECONDS) & 1));
+	unsigned char sec = CMOS_READ(RTC_SECONDS);
+	while (sec == CMOS_READ(RTC_SECONDS));
 }
 
 static int ds1742_ioctl(struct inode *inode, struct file *file,
@@ -264,6 +266,7 @@
 	switch (cmd) {
 	case RTC_RD_TIME:	/* Read the time/date from RTC  */
 		curr_time = rtc_ds1742_get_time();
+		memset(&rtc_tm, 0, sizeof(struct rtc_time));
 		to_tm(curr_time, &rtc_tm);
 		rtc_tm.tm_year -= 1900;
 		return copy_to_user((void *) arg, &rtc_tm, sizeof(rtc_tm)) ? 
diff -Nur linux-2.4.30/drivers/char/dummy_keyb.c linux-2.4.30-linux-mips.org/drivers/char/dummy_keyb.c
--- linux-2.4.30/drivers/char/dummy_keyb.c	2003-08-25 13:44:41.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/char/dummy_keyb.c	2005-05-12 17:57:21.000000000 +0200
@@ -140,3 +140,7 @@
 {
 	printk("Dummy keyboard driver installed.\n");
 }
+#ifdef CONFIG_MAGIC_SYSRQ
+unsigned char kbd_sysrq_key;
+unsigned char kbd_sysrq_xlate[128];
+#endif
diff -Nur linux-2.4.30/drivers/char/dz.c linux-2.4.30-linux-mips.org/drivers/char/dz.c
--- linux-2.4.30/drivers/char/dz.c	2005-01-19 15:09:44.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/char/dz.c	2005-05-12 17:57:21.000000000 +0200
@@ -1,11 +1,13 @@
 /*
- * dz.c: Serial port driver for DECStations equiped 
+ * dz.c: Serial port driver for DECstations equipped 
  *       with the DZ chipset.
  *
  * Copyright (C) 1998 Olivier A. D. Lebaillif 
  *             
  * Email: olivier.lebaillif@ifrsys.com
  *
+ * Copyright (C) 2004  Maciej W. Rozycki
+ *
  * [31-AUG-98] triemer
  * Changed IRQ to use Harald's dec internals interrupts.h
  * removed base_addr code - moving address assignment to setup.c
@@ -24,6 +26,7 @@
 #undef DEBUG_DZ
 
 #include <linux/config.h>
+#include <linux/delay.h>
 #include <linux/version.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
@@ -54,33 +57,56 @@
 #include <asm/system.h>
 #include <asm/uaccess.h>
 
-#define CONSOLE_LINE (3)	/* for definition of struct console */
+#ifdef CONFIG_MAGIC_SYSRQ
+#include <linux/sysrq.h>
+#endif
 
 #include "dz.h"
 
-#define DZ_INTR_DEBUG 1
-
 DECLARE_TASK_QUEUE(tq_serial);
 
-static struct dz_serial *lines[4];
-static unsigned char tmp_buffer[256];
+static struct dz_serial multi[DZ_NB_PORT];    /* Four serial lines in the DZ chip */
+static struct tty_driver serial_driver, callout_driver;
+
+static struct tty_struct *serial_table[DZ_NB_PORT];
+static struct termios *serial_termios[DZ_NB_PORT];
+static struct termios *serial_termios_locked[DZ_NB_PORT];
+
+static int serial_refcount;
 
-#ifdef DEBUG_DZ
 /*
- * debugging code to send out chars via prom 
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the copy_from_user blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
  */
-static void debug_console(const char *s, int count)
-{
-	unsigned i;
+static unsigned char *tmp_buf;
+static DECLARE_MUTEX(tmp_buf_sem);
 
-	for (i = 0; i < count; i++) {
-		if (*s == 10)
-			prom_printf("%c", 13);
-		prom_printf("%c", *s++);
-	}
-}
+static char *dz_name __initdata = "DECstation DZ serial driver version ";
+static char *dz_version __initdata = "1.03";
+
+static struct dz_serial *lines[DZ_NB_PORT];
+static unsigned char tmp_buffer[256];
+
+#ifdef CONFIG_SERIAL_DEC_CONSOLE
+static struct console dz_sercons;
+#endif
+#if defined(CONFIG_SERIAL_DEC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && \
+   !defined(MODULE)
+static unsigned long break_pressed; /* break, really ... */
 #endif
 
+static void change_speed (struct dz_serial *);
+
+static int baud_table[] = {
+        0, 50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800,
+        9600, 0
+};
+
 /*
  * ------------------------------------------------------------
  * dz_in () and dz_out ()
@@ -94,15 +120,16 @@
 {
 	volatile unsigned short *addr =
 		(volatile unsigned short *) (info->port + offset);
+
 	return *addr;
 }
 
 static inline void dz_out(struct dz_serial *info, unsigned offset,
                           unsigned short value)
 {
-
 	volatile unsigned short *addr =
 		(volatile unsigned short *) (info->port + offset);
+
 	*addr = value;
 }
 
@@ -143,25 +170,24 @@
 
 	tmp |= mask;		/* set the TX flag */
 	dz_out(info, DZ_TCR, tmp);
-
 }
 
 /*
  * ------------------------------------------------------------
- * Here starts the interrupt handling routines.  All of the 
- * following subroutines are declared as inline and are folded 
- * into dz_interrupt.  They were separated out for readability's 
- * sake. 
  *
- * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * dz_interrupt().  They were separated out for readability's sake.
+ *
+ * Note: dz_interrupt() is a "fast" interrupt, which means that it
  * runs with interrupts turned off.  People who may want to modify
- * rs_interrupt() should try to keep the interrupt handler as fast as
+ * dz_interrupt() should try to keep the interrupt handler as fast as
  * possible.  After you are done making modifications, it is not a bad
  * idea to do:
  * 
  * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer dz.c
  *
- * and look at the resulting assemble code in serial.s.
+ * and look at the resulting assemble code in dz.s.
  *
  * ------------------------------------------------------------
  */
@@ -188,101 +214,97 @@
  * This routine deals with inputs from any lines.
  * ------------------------------------------------------------
  */
-static inline void receive_chars(struct dz_serial *info_in)
+static inline void receive_chars(struct dz_serial *info_in,
+				 struct pt_regs *regs)
 {
-
 	struct dz_serial *info;
-	struct tty_struct *tty = 0;
+	struct tty_struct *tty;
 	struct async_icount *icount;
-	int ignore = 0;
-	unsigned short status, tmp;
-	unsigned char ch;
-
-	/* this code is going to be a problem...
-	   the call to tty_flip_buffer is going to need
-	   to be rethought...
-	 */
-	do {
-		status = dz_in(info_in, DZ_RBUF);
-		info = lines[LINE(status)];
+	int lines_rx[DZ_NB_PORT] = { [0 ... DZ_NB_PORT - 1] = 0 };
+	unsigned short status;
+	unsigned char ch, flag;
+	int i;
 
-		/* punt so we don't get duplicate characters */
-		if (!(status & DZ_DVAL))
-			goto ignore_char;
-
-		ch = UCHAR(status);	/* grab the char */
-
-#if 0
-		if (info->is_console) {
-			if (ch == 0)
-				return;	/* it's a break ... */
-		}
-#endif
+	while ((status = dz_in(info_in, DZ_RBUF)) & DZ_DVAL) {
+		info = lines[LINE(status)];
+		tty = info->tty;		/* point to the proper dev */
 
-		tty = info->tty;	/* now tty points to the proper dev */
-		icount = &info->icount;
+		ch = UCHAR(status);		/* grab the char */
 
-		if (!tty)
-			break;
-		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
-			break;
+		if (!tty && (!info->hook || !info->hook->rx_char))
+			continue;
 
-		*tty->flip.char_buf_ptr = ch;
-		*tty->flip.flag_buf_ptr = 0;
+		icount = &info->icount;
 		icount->rx++;
 
-		/* keep track of the statistics */
-		if (status & (DZ_OERR | DZ_FERR | DZ_PERR)) {
-			if (status & DZ_PERR)	/* parity error */
-				icount->parity++;
-			else if (status & DZ_FERR)	/* frame error */
-				icount->frame++;
-			if (status & DZ_OERR)	/* overrun error */
-				icount->overrun++;
-
-			/*  check to see if we should ignore the character
-			   and mask off conditions that should be ignored
+		flag = 0;
+		if (status & DZ_FERR) {		/* frame error */
+			/*
+			 * There is no separate BREAK status bit, so
+			 * treat framing errors as BREAKs for Magic SysRq
+			 * and SAK; normally, otherwise.
 			 */
-
-			if (status & info->ignore_status_mask) {
-				if (++ignore > 100)
-					break;
-				goto ignore_char;
+#if defined(CONFIG_SERIAL_DEC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && \
+   !defined(MODULE)
+			if (info->line == dz_sercons.index) {
+				if (!break_pressed)
+					break_pressed = jiffies;
+				continue;
 			}
-			/* mask off the error conditions we want to ignore */
-			tmp = status & info->read_status_mask;
-
-			if (tmp & DZ_PERR) {
-				*tty->flip.flag_buf_ptr = TTY_PARITY;
-#ifdef DEBUG_DZ
-				debug_console("PERR\n", 5);
-#endif
-			} else if (tmp & DZ_FERR) {
-				*tty->flip.flag_buf_ptr = TTY_FRAME;
-#ifdef DEBUG_DZ
-				debug_console("FERR\n", 5);
 #endif
+			flag = TTY_BREAK;
+			if (info->flags & DZ_SAK)
+				do_SAK(tty);
+			else
+				flag = TTY_FRAME;
+		} else if (status & DZ_OERR)	/* overrun error */
+			flag = TTY_OVERRUN;
+		else if (status & DZ_PERR)	/* parity error */
+			flag = TTY_PARITY;
+
+#if defined(CONFIG_SERIAL_DEC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && \
+   !defined(MODULE)
+		if (break_pressed && info->line == dz_sercons.index) {
+			if (time_before(jiffies, break_pressed + HZ * 5)) {
+				handle_sysrq(ch, regs, NULL, NULL);
+				break_pressed = 0;
+				continue;
 			}
-			if (tmp & DZ_OERR) {
-#ifdef DEBUG_DZ
-				debug_console("OERR\n", 5);
+			break_pressed = 0;
+		}
 #endif
-				if (tty->flip.count < TTY_FLIPBUF_SIZE) {
-					tty->flip.count++;
-					tty->flip.flag_buf_ptr++;
-					tty->flip.char_buf_ptr++;
-					*tty->flip.flag_buf_ptr = TTY_OVERRUN;
-				}
-			}
+
+		if (info->hook && info->hook->rx_char) {
+			(*info->hook->rx_char)(ch, flag);
+			return;
 		}
-		tty->flip.flag_buf_ptr++;
-		tty->flip.char_buf_ptr++;
-		tty->flip.count++;
-	      ignore_char:
-	} while (status & DZ_DVAL);
 
-	if (tty)
-		tty_flip_buffer_push(tty);
+		/* keep track of the statistics */
+		switch (flag) {
+		case TTY_FRAME:
+			icount->frame++;
+			break;
+		case TTY_PARITY:
+			icount->parity++;
+			break;
+		case TTY_OVERRUN:
+			icount->overrun++;
+			break;
+		case TTY_BREAK:
+			icount->brk++;
+			break;
+		default:
+			break;
+		}
+
+		if ((status & info->ignore_status_mask) == 0) {
+			tty_insert_flip_char(tty, ch, flag);
+			lines_rx[LINE(status)] = 1;
+		}
+	}
+	for (i = 0; i < DZ_NB_PORT; i++)
+		if (lines_rx[i])
+			tty_flip_buffer_push(lines[i]->tty);
 }
 
 /*
@@ -292,20 +314,34 @@
  * This routine deals with outputs to any lines.
  * ------------------------------------------------------------
  */
-static inline void transmit_chars(struct dz_serial *info)
+static inline void transmit_chars(struct dz_serial *info_in)
 {
+	struct dz_serial *info;
+	unsigned short status;
 	unsigned char tmp;
 
+	status = dz_in(info_in, DZ_CSR);
+	info = lines[LINE(status)];
 
+	if (info->hook || !info->tty) {
+		unsigned short mask, tmp;
 
-	if (info->x_char) {	/* XON/XOFF chars */
+		mask = 1 << info->line;
+		tmp = dz_in(info, DZ_TCR);	/* read the TX flag */
+		tmp &= ~mask;			/* clear the TX flag */
+		dz_out(info, DZ_TCR, tmp);
+		return;
+	}
+
+	if (info->x_char) {			/* XON/XOFF chars */
 		dz_out(info, DZ_TDR, info->x_char);
 		info->icount.tx++;
 		info->x_char = 0;
 		return;
 	}
 	/* if nothing to do or stopped or hardware stopped */
-	if ((info->xmit_cnt <= 0) || info->tty->stopped || info->tty->hw_stopped) {
+	if (info->xmit_cnt <= 0 ||
+	    info->tty->stopped || info->tty->hw_stopped) {
 		dz_stop(info->tty);
 		return;
 	}
@@ -359,15 +395,14 @@
  */
 static void dz_interrupt(int irq, void *dev, struct pt_regs *regs)
 {
-	struct dz_serial *info;
+	struct dz_serial *info = (struct dz_serial *)dev;
 	unsigned short status;
 
 	/* get the reason why we just got an irq */
-	status = dz_in((struct dz_serial *) dev, DZ_CSR);
-	info = lines[LINE(status)];	/* re-arrange info the proper port */
+	status = dz_in(info, DZ_CSR);
 
 	if (status & DZ_RDONE)
-		receive_chars(info);	/* the receive function */
+		receive_chars(info, regs);
 
 	if (status & DZ_TRDY)
 		transmit_chars(info);
@@ -514,7 +549,7 @@
 
 
 	info->cflags &= ~DZ_CREAD;	/* turn off receive enable flag */
-	dz_out(info, DZ_LPR, info->cflags);
+	dz_out(info, DZ_LPR, info->cflags | info->line);
 
 	if (info->xmit_buf) {	/* free Tx buffer */
 		free_page((unsigned long) info->xmit_buf);
@@ -545,18 +580,21 @@
 {
 	unsigned long flags;
 	unsigned cflag;
-	int baud;
+	int baud, i;
 
-	if (!info->tty || !info->tty->termios)
-		return;
+	if (!info->hook) {
+		if (!info->tty || !info->tty->termios)
+			return;
+		cflag = info->tty->termios->c_cflag;
+	} else {
+		cflag = info->hook->cflags;
+	}
 
 	save_flags(flags);
 	cli();
 
 	info->cflags = info->line;
 
-	cflag = info->tty->termios->c_cflag;
-
 	switch (cflag & CSIZE) {
 	case CS5:
 		info->cflags |= DZ_CS5;
@@ -579,7 +617,16 @@
 	if (cflag & PARODD)
 		info->cflags |= DZ_PARODD;
 
-	baud = tty_get_baud_rate(info->tty);
+	i = cflag & CBAUD;
+	if (i & CBAUDEX) {
+		i &= ~CBAUDEX;
+		if (!info->hook)
+			info->tty->termios->c_cflag &= ~CBAUDEX;
+		else
+			info->hook->cflags &= ~CBAUDEX;
+	}
+        baud = baud_table[i];
+
 	switch (baud) {
 	case 50:
 		info->cflags |= DZ_B50;
@@ -629,16 +676,16 @@
 	}
 
 	info->cflags |= DZ_RXENAB;
-	dz_out(info, DZ_LPR, info->cflags);
+	dz_out(info, DZ_LPR, info->cflags | info->line);
 
 	/* setup accept flag */
 	info->read_status_mask = DZ_OERR;
-	if (I_INPCK(info->tty))
+	if (info->tty && I_INPCK(info->tty))
 		info->read_status_mask |= (DZ_FERR | DZ_PERR);
 
 	/* characters to ignore */
 	info->ignore_status_mask = 0;
-	if (I_IGNPAR(info->tty))
+	if (info->tty && I_IGNPAR(info->tty))
 		info->ignore_status_mask |= (DZ_FERR | DZ_PERR);
 
 	restore_flags(flags);
@@ -694,7 +741,7 @@
 
 		down(&tmp_buf_sem);
 		while (1) {
-			c = MIN(count, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head));
+			c = min(count, min(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head));
 			if (c <= 0)
 				break;
 
@@ -707,7 +754,7 @@
 			save_flags(flags);
 			cli();
 
-			c = MIN(c, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head));
+			c = min(c, min(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head));
 			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
 			info->xmit_head = ((info->xmit_head + c) & (DZ_XMIT_SIZE - 1));
 			info->xmit_cnt += c;
@@ -727,7 +774,7 @@
 			save_flags(flags);
 			cli();
 
-			c = MIN(count, MIN(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head));
+			c = min(count, min(DZ_XMIT_SIZE - info->xmit_cnt - 1, DZ_XMIT_SIZE - info->xmit_head));
 			if (c <= 0) {
 				restore_flags(flags);
 				break;
@@ -845,7 +892,7 @@
 
 /*
  * ------------------------------------------------------------
- * rs_ioctl () and friends
+ * dz_ioctl () and friends
  * ------------------------------------------------------------
  */
 static int get_serial_info(struct dz_serial *info,
@@ -958,6 +1005,9 @@
 	struct dz_serial *info = (struct dz_serial *) tty->driver_data;
 	int retval;
 
+	if (info->hook)
+		return -ENODEV;
+
 	if ((cmd != TIOCGSERIAL) && (cmd != TIOCSSERIAL) &&
 	    (cmd != TIOCSERCONFIG) && (cmd != TIOCSERGWILD) &&
 	    (cmd != TIOCSERSWILD) && (cmd != TIOCSERGSTRUCT)) {
@@ -1252,19 +1302,14 @@
 	int retval, line;
 
 	line = MINOR(tty->device) - tty->driver.minor_start;
-
-	/* The dz lines for the mouse/keyboard must be
-	 * opened using their respective drivers.
-	 */
 	if ((line < 0) || (line >= DZ_NB_PORT))
 		return -ENODEV;
+	info = lines[line];
 
-	if ((line == DZ_KEYBOARD) || (line == DZ_MOUSE))
+	if (info->hook)
 		return -ENODEV;
 
-	info = lines[line];
 	info->count++;
-
 	tty->driver_data = info;
 	info->tty = tty;
 
@@ -1285,14 +1330,21 @@
 		else
 			*tty->termios = info->callout_termios;
 		change_speed(info);
-
 	}
+#ifdef CONFIG_SERIAL_DEC_CONSOLE
+	if (dz_sercons.cflag && dz_sercons.index == line) {
+		tty->termios->c_cflag = dz_sercons.cflag;
+		dz_sercons.cflag = 0;
+		change_speed(info);
+        }
+#endif
+
 	info->session = current->session;
 	info->pgrp = current->pgrp;
 	return 0;
 }
 
-static void show_serial_version(void)
+static void __init show_serial_version(void)
 {
 	printk("%s%s\n", dz_name, dz_version);
 }
@@ -1300,7 +1352,6 @@
 int __init dz_init(void)
 {
 	int i;
-	long flags;
 	struct dz_serial *info;
 
 	/* Setup base handler, and timer table. */
@@ -1311,9 +1362,9 @@
 	memset(&serial_driver, 0, sizeof(struct tty_driver));
 	serial_driver.magic = TTY_DRIVER_MAGIC;
 #if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS))
-	serial_driver.name = "ttyS";
-#else
 	serial_driver.name = "tts/%d";
+#else
+	serial_driver.name = "ttyS";
 #endif
 	serial_driver.major = TTY_MAJOR;
 	serial_driver.minor_start = 64;
@@ -1352,9 +1403,9 @@
 	 */
 	callout_driver = serial_driver;
 #if (LINUX_VERSION_CODE > 0x2032D && defined(CONFIG_DEVFS_FS))
-	callout_driver.name = "cua";
-#else
 	callout_driver.name = "cua/%d";
+#else
+	callout_driver.name = "cua";
 #endif
 	callout_driver.major = TTYAUX_MAJOR;
 	callout_driver.subtype = SERIAL_TYPE_CALLOUT;
@@ -1363,25 +1414,27 @@
 		panic("Couldn't register serial driver");
 	if (tty_register_driver(&callout_driver))
 		panic("Couldn't register callout driver");
-	save_flags(flags);
-	cli();
 
 	for (i = 0; i < DZ_NB_PORT; i++) {
 		info = &multi[i];
 		lines[i] = info;
-		info->magic = SERIAL_MAGIC;
-
+		info->tty = 0;
+		info->x_char = 0;
 		if (mips_machtype == MACH_DS23100 ||
 		    mips_machtype == MACH_DS5100)
 			info->port = (unsigned long) KN01_DZ11_BASE;
 		else
 			info->port = (unsigned long) KN02_DZ11_BASE;
-
 		info->line = i;
-		info->tty = 0;
+
+		if (info->hook && info->hook->init_info) {
+			(*info->hook->init_info)(info);
+			continue;
+		}
+
+		info->magic = SERIAL_MAGIC;
 		info->close_delay = 50;
 		info->closing_wait = 3000;
-		info->x_char = 0;
 		info->event = 0;
 		info->count = 0;
 		info->blocked_open = 0;
@@ -1393,25 +1446,16 @@
 		info->normal_termios = serial_driver.init_termios;
 		init_waitqueue_head(&info->open_wait);
 		init_waitqueue_head(&info->close_wait);
-
-		/*
-		 * If we are pointing to address zero then punt - not correctly
-		 * set up in setup.c to handle this.
-		 */
-		if (!info->port)
-			return 0;
-
-		printk("ttyS%02d at 0x%08x (irq = %d)\n", info->line,
-		       info->port, dec_interrupt[DEC_IRQ_DZ11]);
-
+		printk("ttyS%02d at 0x%08x (irq = %d) is a DC7085 DZ\n",
+		       info->line, info->port, dec_interrupt[DEC_IRQ_DZ11]);
 		tty_register_devfs(&serial_driver, 0,
-				 serial_driver.minor_start + info->line);
+				   serial_driver.minor_start + info->line);
 		tty_register_devfs(&callout_driver, 0,
-				callout_driver.minor_start + info->line);
+				   callout_driver.minor_start + info->line);
 	}
 
-	/* reset the chip */
 #ifndef CONFIG_SERIAL_DEC_CONSOLE
+	/* reset the chip */
 	dz_out(info, DZ_CSR, DZ_CLR);
 	while (dz_in(info, DZ_CSR) & DZ_CLR);
 	iob();
@@ -1420,43 +1464,104 @@
 	dz_out(info, DZ_CSR, DZ_MSE);
 #endif
 
-	/* order matters here... the trick is that flags
-	   is updated... in request_irq - to immediatedly obliterate
-	   it is unwise. */
-	restore_flags(flags);
-
-
 	if (request_irq(dec_interrupt[DEC_IRQ_DZ11], dz_interrupt,
-			SA_INTERRUPT, "DZ", lines[0]))
+			0, "DZ", lines[0]))
 		panic("Unable to register DZ interrupt");
 
+	for (i = 0; i < DZ_NB_PORT; i++)
+		if (lines[i]->hook) {
+			startup(lines[i]);
+			if (lines[i]->hook->init_channel)
+				(*lines[i]->hook->init_channel)(lines[i]);
+		}
+
 	return 0;
 }
 
-#ifdef CONFIG_SERIAL_DEC_CONSOLE
-static void dz_console_put_char(unsigned char ch)
+/*
+ * polling I/O routines
+ */
+static int dz_poll_tx_char(void *handle, unsigned char ch)
 {
 	unsigned long flags;
-	int loops = 2500;
-	unsigned short tmp = ch;
-	/* this code sends stuff out to serial device - spinning its
-	   wheels and waiting. */
+	struct dz_serial *info = handle;
+	unsigned short csr, tcr, trdy, mask;
+	int loops = 10000;
+	int ret;
 
-	/* force the issue - point it at lines[3] */
-	dz_console = &multi[CONSOLE_LINE];
+	local_irq_save(flags);
+	csr = dz_in(info, DZ_CSR);
+	dz_out(info, DZ_CSR, csr & ~DZ_TIE);
+	tcr = dz_in(info, DZ_TCR);
+	tcr |= 1 << info->line;
+	mask = tcr;
+	dz_out(info, DZ_TCR, mask);
+	iob();
+	local_irq_restore(flags);
 
-	save_flags(flags);
-	cli();
+	while (loops--) {
+		trdy = dz_in(info, DZ_CSR);
+		if (!(trdy & DZ_TRDY))
+			continue;
+		trdy = (trdy & DZ_TLINE) >> 8;
+		if (trdy == info->line)
+			break;
+		mask &= ~(1 << trdy);
+		dz_out(info, DZ_TCR, mask);
+		iob();
+		udelay(2);
+	}
 
+	if (loops) {
+		dz_out(info, DZ_TDR, ch);
+		ret = 0;
+	} else
+		ret = -EAGAIN;
 
-	/* spin our wheels */
-	while (((dz_in(dz_console, DZ_CSR) & DZ_TRDY) != DZ_TRDY) && loops--);
+	dz_out(info, DZ_TCR, tcr);
+	dz_out(info, DZ_CSR, csr);
 
-	/* Actually transmit the character. */
-	dz_out(dz_console, DZ_TDR, tmp);
+	return ret;
+}
 
-	restore_flags(flags);
+static int dz_poll_rx_char(void *handle)
+{
+	return -ENODEV;
+}
+
+int register_dz_hook(unsigned int channel, struct dec_serial_hook *hook)
+{
+	struct dz_serial *info = multi + channel;
+
+	if (info->hook) {
+		printk("%s: line %d has already a hook registered\n",
+		       __FUNCTION__, channel);
+
+		return 0;
+	} else {
+		hook->poll_rx_char = dz_poll_rx_char;
+		hook->poll_tx_char = dz_poll_tx_char;
+		info->hook = hook;
+
+		return 1;
+	}
+}
+
+int unregister_dz_hook(unsigned int channel)
+{
+	struct dz_serial *info = &multi[channel];
+
+	if (info->hook) {
+		info->hook = NULL;
+		return 1;
+	} else {
+		printk("%s: trying to unregister hook on line %d,"
+		       " but none is registered\n", __FUNCTION__, channel);
+		return 0;
+	}
 }
+
+#ifdef CONFIG_SERIAL_DEC_CONSOLE
 /* 
  * -------------------------------------------------------------------
  * dz_console_print ()
@@ -1465,17 +1570,19 @@
  * The console must be locked when we get here.
  * ------------------------------------------------------------------- 
  */
-static void dz_console_print(struct console *cons,
+static void dz_console_print(struct console *co,
 			     const char *str,
 			     unsigned int count)
 {
+	struct dz_serial *info = multi + co->index;
+
 #ifdef DEBUG_DZ
 	prom_printf((char *) str);
 #endif
 	while (count--) {
 		if (*str == '\n')
-			dz_console_put_char('\r');
-		dz_console_put_char(*str++);
+			dz_poll_tx_char(info, '\r');
+		dz_poll_tx_char(info, *str++);
 	}
 }
 
@@ -1486,12 +1593,12 @@
 
 static int __init dz_console_setup(struct console *co, char *options)
 {
+	struct dz_serial *info = multi + co->index;
 	int baud = 9600;
 	int bits = 8;
 	int parity = 'n';
 	int cflag = CREAD | HUPCL | CLOCAL;
 	char *s;
-	unsigned short mask, tmp;
 
 	if (options) {
 		baud = simple_strtoul(options, NULL, 10);
@@ -1542,44 +1649,31 @@
 	}
 	co->cflag = cflag;
 
-	/* TOFIX: force to console line */
-	dz_console = &multi[CONSOLE_LINE];
 	if ((mips_machtype == MACH_DS23100) || (mips_machtype == MACH_DS5100))
-		dz_console->port = KN01_DZ11_BASE;
+		info->port = KN01_DZ11_BASE;
 	else
-		dz_console->port = KN02_DZ11_BASE;
-	dz_console->line = CONSOLE_LINE;
+		info->port = KN02_DZ11_BASE;
+	info->line = co->index;
 
-	dz_out(dz_console, DZ_CSR, DZ_CLR);
-	while ((tmp = dz_in(dz_console, DZ_CSR)) & DZ_CLR);
+	dz_out(info, DZ_CSR, DZ_CLR);
+	while (dz_in(info, DZ_CSR) & DZ_CLR);
 
 	/* enable scanning */
-	dz_out(dz_console, DZ_CSR, DZ_MSE);
+	dz_out(info, DZ_CSR, DZ_MSE);
 
 	/*  Set up flags... */
-	dz_console->cflags = 0;
-	dz_console->cflags |= DZ_B9600;
-	dz_console->cflags |= DZ_CS8;
-	dz_console->cflags |= DZ_PARENB;
-	dz_out(dz_console, DZ_LPR, dz_console->cflags);
-
-	mask = 1 << dz_console->line;
-	tmp = dz_in(dz_console, DZ_TCR);	/* read the TX flag */
-	if (!(tmp & mask)) {
-		tmp |= mask;	/* set the TX flag */
-		dz_out(dz_console, DZ_TCR, tmp);
-	}
+	dz_out(info, DZ_LPR, cflag | info->line);
+
 	return 0;
 }
 
-static struct console dz_sercons =
-{
-    .name	= "ttyS",
-    .write	= dz_console_print,
-    .device	= dz_console_device,
-    .setup	= dz_console_setup,
-    .flags	= CON_CONSDEV | CON_PRINTBUFFER,
-    .index	= CONSOLE_LINE,
+static struct console dz_sercons = {
+	.name	= "ttyS",
+	.write	= dz_console_print,
+	.device	= dz_console_device,
+	.setup	= dz_console_setup,
+	.flags	= CON_PRINTBUFFER,
+	.index	= -1,
 };
 
 void __init dz_serial_console_init(void)
diff -Nur linux-2.4.30/drivers/char/dz.h linux-2.4.30-linux-mips.org/drivers/char/dz.h
--- linux-2.4.30/drivers/char/dz.h	2002-08-03 02:39:43.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/char/dz.h	2005-05-12 17:57:21.000000000 +0200
@@ -10,6 +10,8 @@
 #ifndef DZ_SERIAL_H
 #define DZ_SERIAL_H
 
+#include <asm/dec/serial.h>
+
 #define SERIAL_MAGIC 0x5301
 
 /*
@@ -17,6 +19,7 @@
  */
 #define DZ_TRDY        0x8000                 /* Transmitter empty */
 #define DZ_TIE         0x4000                 /* Transmitter Interrupt Enable */
+#define DZ_TLINE       0x0300                 /* Transmitter Line Number */
 #define DZ_RDONE       0x0080                 /* Receiver data ready */
 #define DZ_RIE         0x0040                 /* Receive Interrupt Enable */
 #define DZ_MSE         0x0020                 /* Master Scan Enable */
@@ -37,19 +40,30 @@
 #define UCHAR(x) (unsigned char)(x & DZ_RBUF_MASK)
 
 /*
- * Definitions for the Transmit Register.
+ * Definitions for the Transmit Control Register.
  */
 #define DZ_LINE_KEYBOARD 0x0001
 #define DZ_LINE_MOUSE    0x0002
 #define DZ_LINE_MODEM    0x0004
 #define DZ_LINE_PRINTER  0x0008
 
+#define DZ_MODEM_RTS     0x0800               /* RTS for the modem line (2) */
 #define DZ_MODEM_DTR     0x0400               /* DTR for the modem line (2) */
+#define DZ_PRINT_RTS     0x0200               /* RTS for the printer line (3) */
+#define DZ_PRINT_DTR     0x0100               /* DTR for the printer line (3) */
+#define DZ_LNENB         0x000f               /* Transmitter Line Enable */
 
 /*
  * Definitions for the Modem Status Register.
  */
+#define DZ_MODEM_RI      0x0800               /* RI for the modem line (2) */
+#define DZ_MODEM_CD      0x0400               /* CD for the modem line (2) */
 #define DZ_MODEM_DSR     0x0200               /* DSR for the modem line (2) */
+#define DZ_MODEM_CTS     0x0100               /* CTS for the modem line (2) */
+#define DZ_PRINT_RI      0x0008               /* RI for the printer line (2) */
+#define DZ_PRINT_CD      0x0004               /* CD for the printer line (2) */
+#define DZ_PRINT_DSR     0x0002               /* DSR for the printer line (2) */
+#define DZ_PRINT_CTS     0x0001               /* CTS for the printer line (2) */
 
 /*
  * Definitions for the Transmit Data Register.
@@ -115,9 +129,6 @@
 
 #define DZ_EVENT_WRITE_WAKEUP   0
 
-#ifndef MIN
-#define MIN(a,b)        ((a) < (b) ? (a) : (b))
-
 #define DZ_INITIALIZED       0x80000000 /* Serial port was initialized */
 #define DZ_CALLOUT_ACTIVE    0x40000000 /* Call out device is active */
 #define DZ_NORMAL_ACTIVE     0x20000000 /* Normal device is active */
@@ -129,6 +140,7 @@
 #define DZ_CLOSING_WAIT_INF  0
 #define DZ_CLOSING_WAIT_NONE 65535
 
+#define DZ_SAK             0x0004 /* Secure Attention Key (Orange book) */
 #define DZ_SPLIT_TERMIOS   0x0008 /* Separate termios for dialin/callout */
 #define DZ_SESSION_LOCKOUT 0x0100 /* Lock out cua opens based on session */
 #define DZ_PGRP_LOCKOUT    0x0200 /* Lock out cua opens based on pgrp */
@@ -166,79 +178,9 @@
   long                    session;             /* Session of opening process */
   long                    pgrp;                /* pgrp of opening process */
 
+  struct dec_serial_hook  *hook;               /* Hook on this channel.  */
   unsigned char           is_console;          /* flag indicating a serial console */
   unsigned char           is_initialized;
 };
 
-static struct dz_serial multi[DZ_NB_PORT];    /* Four serial lines in the DZ chip */
-static struct dz_serial *dz_console;
-static struct tty_driver serial_driver, callout_driver;
-
-static struct tty_struct *serial_table[DZ_NB_PORT];
-static struct termios *serial_termios[DZ_NB_PORT];
-static struct termios *serial_termios_locked[DZ_NB_PORT];
-
-static int serial_refcount;
-
-/*
- * tmp_buf is used as a temporary buffer by serial_write.  We need to
- * lock it in case the copy_from_user blocks while swapping in a page,
- * and some other program tries to do a serial write at the same time.
- * Since the lock will only come under contention when the system is
- * swapping and available memory is low, it makes sense to share one
- * buffer across all the serial ports, since it significantly saves
- * memory if large numbers of serial ports are open.
- */
-static unsigned char *tmp_buf;
-static DECLARE_MUTEX(tmp_buf_sem);
-
-static char *dz_name = "DECstation DZ serial driver version ";
-static char *dz_version = "1.02";
-
-static inline unsigned short dz_in (struct dz_serial *, unsigned);
-static inline void dz_out (struct dz_serial *, unsigned, unsigned short);
-
-static inline void dz_sched_event (struct dz_serial *, int);
-static inline void receive_chars (struct dz_serial *);
-static inline void transmit_chars (struct dz_serial *);
-static inline void check_modem_status (struct dz_serial *);
-
-static void dz_stop (struct tty_struct *);
-static void dz_start (struct tty_struct *);
-static void dz_interrupt (int, void *, struct pt_regs *);
-static void do_serial_bh (void);
-static void do_softint (void *);
-static void do_serial_hangup (void *);
-static void change_speed (struct dz_serial *);
-static void dz_flush_chars (struct tty_struct *);
-static void dz_console_print (struct console *, const char *, unsigned int);
-static void dz_flush_buffer (struct tty_struct *);
-static void dz_throttle (struct tty_struct *);
-static void dz_unthrottle (struct tty_struct *);
-static void dz_send_xchar (struct tty_struct *, char);
-static void shutdown (struct dz_serial *);
-static void send_break (struct dz_serial *, int);
-static void dz_set_termios (struct tty_struct *, struct termios *);
-static void dz_close (struct tty_struct *, struct file *);
-static void dz_hangup (struct tty_struct *);
-static void show_serial_version (void);
-
-static int dz_write (struct tty_struct *, int, const unsigned char *, int);
-static int dz_write_room (struct tty_struct *);
-static int dz_chars_in_buffer (struct tty_struct *);
-static int startup (struct dz_serial *);
-static int get_serial_info (struct dz_serial *, struct serial_struct *);
-static int set_serial_info (struct dz_serial *, struct serial_struct *);
-static int get_lsr_info (struct dz_serial *, unsigned int *);
-static int dz_ioctl (struct tty_struct *, struct file *, unsigned int, unsigned long);
-static int block_til_ready (struct tty_struct *, struct file *, struct dz_serial *);
-static int dz_open (struct tty_struct *, struct file *);
-
-#ifdef MODULE
-int init_module (void)
-void cleanup_module (void)
-#endif
-
-#endif
-
 #endif /* DZ_SERIAL_H */
diff -Nur linux-2.4.30/drivers/char/ibm_workpad_keymap.map linux-2.4.30-linux-mips.org/drivers/char/ibm_workpad_keymap.map
--- linux-2.4.30/drivers/char/ibm_workpad_keymap.map	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/char/ibm_workpad_keymap.map	2005-05-12 17:57:21.000000000 +0200
@@ -0,0 +1,343 @@
+# Keymap for IBM Workpad z50
+# US Mapping
+#
+# by Michael Klar <wyldfier@iname.com>
+#
+# This is a great big mess on account of how the Caps Lock key is handled as
+# LeftShift-RightShift.  Right shift key had to be broken out, so don't use
+# use this map file as a basis for other keyboards that don't do the same
+# thing with Caps Lock.
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+
+keymaps 0-2,4-5,8,12,32-33,36-37
+strings as usual
+
+keycode 0 = F1 F11 Console_13
+	shiftr keycode 0 = F11
+	shift shiftr keycode 0 = F11
+	control keycode 0 = F1
+	alt keycode 0 = Console_1
+	control alt keycode 0 = Console_1
+keycode 1 = F3 F13 Console_15
+	shiftr keycode 1 = F13
+	shift shiftr keycode 1 = F13
+	control keycode 1 = F3
+	alt keycode 1 = Console_3
+	control alt keycode 1 = Console_3
+keycode 2 = F5 F15 Console_17
+	shiftr keycode 2 = F15
+	shift shiftr keycode 2 = F15
+	control keycode 2 = F5
+	alt keycode 2 = Console_5
+	control alt keycode 2 = Console_5
+keycode 3 = F7 F17 Console_19
+	shiftr keycode 3 = F17
+	shift shiftr keycode 3 = F17
+	control keycode 3 = F7
+	alt keycode 3 = Console_7
+	control alt keycode 3 = Console_7
+keycode 4 = F9 F19 Console_21
+	shiftr keycode 4 = F19
+	shift shiftr keycode 4 = F19
+	control keycode 4 = F9
+	alt keycode 4 = Console_9
+	control alt keycode 4 = Console_9
+#keycode 5 is contrast down
+#keycode 6 is contrast up
+keycode 7 = F11 F11 Console_23
+	shiftr keycode 7 = F11
+	shift shiftr keycode 7 = F11
+	control keycode 7 = F11
+	alt keycode 7 = Console_11
+	control alt keycode 7 = Console_11
+keycode 8 = F2 F12 Console_14
+	shiftr keycode 8 = F12
+	shift shiftr keycode 8 = F12
+	control keycode 8 = F2
+	alt keycode 8 = Console_2
+	control alt keycode 8 = Console_2
+keycode 9 = F4 F14 Console_16
+	shiftr keycode 9 = F14
+	shift shiftr keycode 9 = F14
+	control keycode 9 = F4
+	alt keycode 9 = Console_4
+	control alt keycode 9 = Console_4
+keycode 10 = F6 F16 Console_18
+	shiftr keycode 10 = F16
+	shift shiftr keycode 10 = F16
+	control keycode 10 = F6
+	alt keycode 10 = Console_6
+	control alt keycode 10 = Console_6
+keycode 11 = F8 F18 Console_20
+	shiftr keycode 11 = F18
+	shift shiftr keycode 11 = F18
+	control keycode 11 = F8
+	alt keycode 11 = Console_8
+	control alt keycode 11 = Console_8
+keycode 12 = F10 F20 Console_22
+	shiftr keycode 12 = F20
+	shift shiftr keycode 12 = F20
+	control keycode 12 = F10
+	alt keycode 12 = Console_10
+	control alt keycode 12 = Console_10
+#keycode 13 is brightness down
+#keycode 14 is brightness up
+keycode 15 = F12 F12 Console_24
+	shiftr keycode 15 = F12
+	shift shiftr keycode 15 = F12
+	control keycode 15 = F12
+	alt keycode 15 = Console_12
+	control alt keycode 15 = Console_12
+keycode 16 = apostrophe quotedbl
+	shiftr keycode 16 = quotedbl
+	shift shiftr keycode 16 = quotedbl
+	control keycode 16 = Control_g
+	alt keycode 16 = Meta_apostrophe
+keycode 17 = bracketleft braceleft
+	shiftr keycode 17 = braceleft
+	shift shiftr keycode 17 = braceleft
+	control keycode 17 = Escape
+	alt keycode 17 = Meta_bracketleft
+keycode 18 = minus underscore backslash       
+	shiftr keycode 18 = underscore
+	shift shiftr keycode 18 = underscore
+	control keycode 18 = Control_underscore
+	shift control keycode 18 = Control_underscore
+	shiftr control keycode 18 = Control_underscore
+	shift shiftr control keycode 18 = Control_underscore
+	alt keycode 18 = Meta_minus
+keycode 19 = zero parenright braceright
+	shiftr keycode 19 = parenright
+	shift shiftr keycode 19 = parenright
+	alt keycode 19 = Meta_zero
+keycode 20 = p
+	shiftr keycode 20 = +P
+	shift shiftr keycode 20 = +p
+keycode 21 = semicolon colon
+	shiftr keycode 21 = colon
+	shift shiftr keycode 21 = colon
+	alt keycode 21 = Meta_semicolon
+keycode 22 = Up Scroll_Backward
+	shiftr keycode 22 = Scroll_Backward
+	shift shiftr keycode 22 = Scroll_Backward
+	alt keycode 22 = Prior
+keycode 23 = slash question
+	shiftr keycode 23 = question
+	shift shiftr keycode 23 = question
+	control keycode 23 = Delete
+	alt keycode 23 = Meta_slash
+
+keycode 27 = nine parenleft bracketright
+	shiftr keycode 27 = parenleft
+	shift shiftr keycode 27 = parenleft
+	alt keycode 27 = Meta_nine
+keycode 28 = o
+	shiftr keycode 28 = +O
+	shift shiftr keycode 28 = +o
+keycode 29 = l
+	shiftr keycode 29 = +L
+	shift shiftr keycode 29 = +l
+keycode 30 = period greater
+	shiftr keycode 30 = greater
+	shift shiftr keycode 30 = greater
+	control keycode 30 = Compose
+	alt keycode 30 = Meta_period
+
+keycode 32 = Left Decr_Console
+	shiftr keycode 32 = Decr_Console
+	shift shiftr keycode 32 = Decr_Console
+	alt keycode 32 = Home
+keycode 33 = bracketright braceright asciitilde      
+	shiftr keycode 33 = braceright
+	shift shiftr keycode 33 = braceright
+	control keycode 33 = Control_bracketright
+	alt keycode 33 = Meta_bracketright
+keycode 34 = equal plus
+	shiftr keycode 34 = plus
+	shift shiftr keycode 34 = plus
+	alt keycode 34 = Meta_equal
+keycode 35 = eight asterisk bracketleft
+	shiftr keycode 35 = asterisk
+	shift shiftr keycode 35 = asterisk
+	control keycode 35 = Delete
+	alt keycode 35 = Meta_eight
+keycode 36 = i
+	shiftr keycode 36 = +I
+	shift shiftr keycode 36 = +i
+keycode 37 = k
+	shiftr keycode 37 = +K
+	shift shiftr keycode 37 = +k
+keycode 38 = comma less
+	shiftr keycode 38 = less
+	shift shiftr keycode 38 = less
+	alt keycode 38 = Meta_comma
+
+keycode 40 = h
+	shiftr keycode 40 = +H
+	shift shiftr keycode 40 = +h
+keycode 41 = y
+	shiftr keycode 41 = +Y
+	shift shiftr keycode 41 = +y
+keycode 42 = six asciicircum
+	shiftr keycode 42 = asciicircum
+	shift shiftr keycode 42 = asciicircum
+	control keycode 42 = Control_asciicircum
+	alt keycode 42 = Meta_six
+keycode 43 = seven ampersand braceleft
+	shiftr keycode 43 = ampersand
+	shift shiftr keycode 43 = ampersand
+	control keycode 43 = Control_underscore
+	alt keycode 43 = Meta_seven
+keycode 44 = u
+	shiftr keycode 44 = +U
+	shift shiftr keycode 44 = +u
+keycode 45 = j
+	shiftr keycode 45 = +J
+	shift shiftr keycode 45 = +j
+keycode 46 = m
+	shiftr keycode 46 = +M
+	shift shiftr keycode 46 = +m
+keycode 47 = n
+	shiftr keycode 47 = +N
+	shift shiftr keycode 47 = +n
+
+# This is the "Backspace" key:
+keycode 49 = Delete Delete
+	shiftr keycode 49 = Delete
+	shift shiftr keycode 49 = Delete
+	control keycode 49 = BackSpace
+	alt keycode 49 = Meta_Delete
+keycode 50 = Num_Lock
+	shift keycode 50 = Bare_Num_Lock
+	shiftr keycode 50 = Bare_Num_Lock
+	shift shiftr keycode 50 = Bare_Num_Lock
+# This is the "Delete" key:
+keycode 51 = Remove
+	control alt keycode 51 = Boot
+
+keycode 53 = backslash bar
+	shiftr keycode 53 = bar
+	shift shiftr keycode 53 = bar
+	control keycode 53 = Control_backslash
+	alt keycode 53 = Meta_backslash
+keycode 54 = Return
+	alt keycode 54 = Meta_Control_m
+keycode 55 = space space           
+	shiftr keycode 55 = space
+	shift shiftr keycode 55 = space
+	control keycode 55 = nul
+	alt keycode 55 = Meta_space
+keycode 56 = g
+	shiftr keycode 56 = +G
+	shift shiftr keycode 56 = +g
+keycode 57 = t
+	shiftr keycode 57 = +T
+	shift shiftr keycode 57 = +t
+keycode 58 = five percent
+	shiftr keycode 58 = percent
+	shift shiftr keycode 58 = percent
+	control keycode 58 = Control_bracketright
+	alt keycode 58 = Meta_five
+keycode 59 = four dollar dollar
+	shiftr keycode 59 = dollar
+	shift shiftr keycode 59 = dollar
+	control keycode 59 = Control_backslash
+	alt keycode 59 = Meta_four
+keycode 60 = r
+	shiftr keycode 60 = +R
+	shift shiftr keycode 60 = +r
+keycode 61 = f
+	shiftr keycode 61 = +F
+	shift shiftr keycode 61 = +f
+	altgr keycode 61 = Hex_F
+keycode 62 = v
+	shiftr keycode 62 = +V
+	shift shiftr keycode 62 = +v
+keycode 63 = b
+	shiftr keycode 63 = +B
+	shift shiftr keycode 63 = +b
+	altgr keycode 63 = Hex_B
+
+keycode 67 = three numbersign
+	shiftr keycode 67 = numbersign
+	shift shiftr keycode 67 = numbersign
+	control keycode 67 = Escape
+	alt keycode 67 = Meta_three
+keycode 68 = e
+	shiftr keycode 68 = +E
+	shift shiftr keycode 68 = +e
+	altgr keycode 68 = Hex_E
+keycode 69 = d
+	shiftr keycode 69 = +D
+	shift shiftr keycode 69 = +d
+	altgr keycode 69 = Hex_D
+keycode 70 = c
+	shiftr keycode 70 = +C
+	shift shiftr keycode 70 = +c
+	altgr keycode 70 = Hex_C
+keycode 71 = Right Incr_Console
+	shiftr keycode 71 = Incr_Console
+	shift shiftr keycode 71 = Incr_Console
+	alt keycode 71 = End
+
+keycode 75 = two at at
+	shiftr keycode 75 = at
+	shift shiftr keycode 75 = at
+	control keycode 75 = nul
+	shift control keycode 75 = nul
+	shiftr control keycode 75 = nul
+	shift shiftr control keycode 75 = nul
+	alt keycode 75 = Meta_two
+keycode 76 = w
+	shiftr keycode 76 = +W
+	shift shiftr keycode 76 = +w
+keycode 77 = s
+	shiftr keycode 77 = +S
+	shift shiftr keycode 77 = +s
+keycode 78 = x
+	shiftr keycode 78 = +X
+	shift shiftr keycode 78 = +x
+keycode 79 = Down Scroll_Forward
+	shiftr keycode 79 = Scroll_Forward
+	shift shiftr keycode 79 = Scroll_Forward
+	alt keycode 79 = Next
+keycode 80 = Escape Escape
+	shiftr keycode 80 = Escape
+	shift shiftr keycode 80 = Escape
+	alt keycode 80 = Meta_Escape
+keycode 81 = Tab Tab             
+	shiftr keycode 81 = Tab
+	shift shiftr keycode 81 = Tab
+	alt keycode 81 = Meta_Tab
+keycode 82 = grave asciitilde
+	shiftr keycode 82 = asciitilde
+	shift shiftr keycode 82 = asciitilde
+	control keycode 82 = nul
+	alt keycode 82 = Meta_grave
+keycode 83 = one exclam
+	shiftr keycode 83 = exclam
+	shift shiftr keycode 83 = exclam
+	alt keycode 83 = Meta_one
+keycode 84 = q
+	shiftr keycode 84 = +Q
+	shift shiftr keycode 84 = +q
+keycode 85 = a
+	shiftr keycode 85 = +A
+	shift shiftr keycode 85 = +a
+	altgr keycode 85 = Hex_A
+keycode 86 = z
+	shiftr keycode 86 = +Z
+	shift shiftr keycode 86 = +z
+
+# This is the windows key:
+keycode 88 = Decr_Console
+keycode 89 = Shift
+keycode 90 = Control
+keycode 91 = Control
+keycode 92 = Alt
+keycode 93 = AltGr
+keycode 94 = ShiftR
+	shift keycode 94 = Caps_Lock
diff -Nur linux-2.4.30/drivers/char/indydog.c linux-2.4.30-linux-mips.org/drivers/char/indydog.c
--- linux-2.4.30/drivers/char/indydog.c	2003-08-25 13:44:41.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/char/indydog.c	2005-05-12 17:57:21.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- *	IndyDog	0.2	A Hardware Watchdog Device for SGI IP22
+ *	IndyDog	0.3	A Hardware Watchdog Device for SGI IP22
  *
  *	(c) Copyright 2002 Guido Guenther <agx@sigxcpu.org>, All Rights Reserved.
  *
@@ -7,10 +7,10 @@
  *	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.
- *	
+ *
  *	based on softdog.c by Alan Cox <alan@redhat.com>
  */
- 
+
 #include <linux/module.h>
 #include <linux/config.h>
 #include <linux/types.h>
@@ -19,13 +19,12 @@
 #include <linux/mm.h>
 #include <linux/miscdevice.h>
 #include <linux/watchdog.h>
-#include <linux/smp_lock.h>
 #include <linux/init.h>
 #include <asm/uaccess.h>
 #include <asm/sgi/mc.h>
 
-static unsigned long indydog_alive;
-static int expect_close = 0;
+#define PFX "indydog: "
+static int indydog_alive;
 
 #ifdef CONFIG_WATCHDOG_NOWAYOUT
 static int nowayout = 1;
@@ -33,10 +32,30 @@
 static int nowayout = 0;
 #endif
 
+#define WATCHDOG_TIMEOUT 30		/* 30 sec default timeout */
+
 MODULE_PARM(nowayout,"i");
 MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=CONFIG_WATCHDOG_NOWAYOUT)");
 
-static inline void indydog_ping(void)
+static void indydog_start(void)
+{
+	u32 mc_ctrl0 = sgimc->cpuctrl0;
+
+	mc_ctrl0 = sgimc->cpuctrl0 | SGIMC_CCTRL0_WDOG;
+	sgimc->cpuctrl0 = mc_ctrl0;
+}
+
+static void indydog_stop(void)
+{
+	u32 mc_ctrl0 = sgimc->cpuctrl0;
+
+	mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG;
+	sgimc->cpuctrl0 = mc_ctrl0;
+
+	printk(KERN_INFO PFX "Stopped watchdog timer.\n");
+}
+
+static void indydog_ping(void)
 {
 	sgimc->watchdogt = 0;
 }
@@ -46,18 +65,14 @@
  */
 static int indydog_open(struct inode *inode, struct file *file)
 {
-	u32 mc_ctrl0;
-	
-	if (test_and_set_bit(0,&indydog_alive))
+	if (indydog_alive)
 		return -EBUSY;
 
-	if (nowayout) {
+	if (nowayout)
 		MOD_INC_USE_COUNT;
-	}
 
 	/* Activate timer */
-	mc_ctrl0 = sgimc->cpuctrl0 | SGIMC_CCTRL0_WDOG;
-	sgimc->cpuctrl0 = mc_ctrl0;
+	indydog_start();
 	indydog_ping();
 			
 	indydog_alive = 1;
@@ -69,63 +84,48 @@
 static int indydog_release(struct inode *inode, struct file *file)
 {
 	/* Shut off the timer.
-	 * Lock it in if it's a module and we set nowayout. */
-	lock_kernel();
-	if (expect_close) {
-		u32 mc_ctrl0 = sgimc->cpuctrl0;
+	 * Lock it in if it's a module and we defined ...NOWAYOUT */
+	if (!nowayout) {
+		u32 mc_ctrl0 = sgimc->cpuctrl0; 
 		mc_ctrl0 &= ~SGIMC_CCTRL0_WDOG;
 		sgimc->cpuctrl0 = mc_ctrl0;
 		printk(KERN_INFO "Stopped watchdog timer.\n");
-	} else
-		printk(KERN_CRIT "WDT device closed unexpectedly.  WDT will not stop!\n");
-	clear_bit(0, &indydog_alive);
-	unlock_kernel();
+	}
+	indydog_alive = 0;
 
 	return 0;
 }
 
 static ssize_t indydog_write(struct file *file, const char *data, size_t len, loff_t *ppos)
 {
-	/*  Can't seek (pwrite) on this device  */
+	/* Can't seek (pwrite) on this device */
 	if (ppos != &file->f_pos)
 		return -ESPIPE;
 
-	/*
-	 *	Refresh the timer.
-	 */
+	/* Refresh the timer. */
 	if (len) {
-		if (!nowayout) {
-			size_t i;
-
-			/* In case it was set long ago */
-			expect_close = 0;
- 
-			for (i = 0; i != len; i++) {
-				char c;
-				if (get_user(c, data + i))
-					return -EFAULT;
-				if (c == 'V')
-					expect_close = 1;
-			}
-		}
 		indydog_ping();
-		return 1;
 	}
-	return 0;
+	return len;
 }
 
 static int indydog_ioctl(struct inode *inode, struct file *file,
 	unsigned int cmd, unsigned long arg)
 {
+	int options, retval = -EINVAL;
 	static struct watchdog_info ident = {
-		options: WDIOF_MAGICCLOSE,
-		identity: "Hardware Watchdog for SGI IP22",
+		.options		= WDIOF_KEEPALIVEPING |
+					  WDIOF_MAGICCLOSE,
+		.firmware_version	= 0,
+		.identity		= "Hardware Watchdog for SGI IP22",
 	};
+
 	switch (cmd) {
 		default:
 			return -ENOIOCTLCMD;
 		case WDIOC_GETSUPPORT:
-			if(copy_to_user((struct watchdog_info *)arg, &ident, sizeof(ident)))
+			if (copy_to_user((struct watchdog_info *)arg,
+					 &ident, sizeof(ident)))
 				return -EFAULT;
 			return 0;
 		case WDIOC_GETSTATUS:
@@ -134,31 +134,53 @@
 		case WDIOC_KEEPALIVE:
 			indydog_ping();
 			return 0;
+		case WDIOC_GETTIMEOUT:
+			return put_user(WATCHDOG_TIMEOUT,(int *)arg);
+		case WDIOC_SETOPTIONS:
+		{
+			if (get_user(options, (int *)arg))
+				return -EFAULT;
+
+			if (options & WDIOS_DISABLECARD) {
+				indydog_stop();
+				retval = 0;
+			}
+
+			if (options & WDIOS_ENABLECARD) {
+				indydog_start();
+				retval = 0;
+			}
+
+			return retval;
+		}
 	}
 }
 
 static struct file_operations indydog_fops = {
-	owner:		THIS_MODULE,
-	write:		indydog_write,
-	ioctl:		indydog_ioctl,
-	open:		indydog_open,
-	release:	indydog_release,
+	.owner		= THIS_MODULE,
+	.write		= indydog_write,
+	.ioctl		= indydog_ioctl,
+	.open		= indydog_open,
+	.release	= indydog_release,
 };
 
 static struct miscdevice indydog_miscdev = {
-	minor:		WATCHDOG_MINOR,
-	name:		"watchdog",
-	fops:		&indydog_fops,
+	.minor		= WATCHDOG_MINOR,
+	.name		= "watchdog",
+	.fops		= &indydog_fops,
 };
 
-static const char banner[] __initdata = KERN_INFO "Hardware Watchdog Timer for SGI IP22: 0.2\n";
+static char banner[] __initdata =
+	KERN_INFO PFX "Hardware Watchdog Timer for SGI IP22: 0.3\n";
 
 static int __init watchdog_init(void)
 {
 	int ret = misc_register(&indydog_miscdev);
-
-	if (ret)
+	if (ret) {
+		printk(KERN_ERR PFX "cannot register miscdev on minor=%d (err=%d)\n",
+			WATCHDOG_MINOR, ret);
 		return ret;
+	}
 
 	printk(banner);
 
@@ -172,4 +194,7 @@
 
 module_init(watchdog_init);
 module_exit(watchdog_exit);
+
+MODULE_AUTHOR("Guido Guenther <agx@sigxcpu.org>");
+MODULE_DESCRIPTION("Hardware Watchdog Device for SGI IP22");
 MODULE_LICENSE("GPL");
diff -Nur linux-2.4.30/drivers/char/ip27-rtc.c linux-2.4.30-linux-mips.org/drivers/char/ip27-rtc.c
--- linux-2.4.30/drivers/char/ip27-rtc.c	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/char/ip27-rtc.c	2005-05-12 17:57:21.000000000 +0200
@@ -44,6 +44,7 @@
 #include <asm/sn/klconfig.h>
 #include <asm/sn/sn0/ip27.h>
 #include <asm/sn/sn0/hub.h>
+#include <asm/sn/sn_private.h>
 
 static int rtc_ioctl(struct inode *inode, struct file *file,
 		     unsigned int cmd, unsigned long arg);
@@ -209,11 +210,8 @@
 
 static int __init rtc_init(void)
 {
-	nasid_t nid;
-
-	nid = get_nasid();
 	rtc = (struct m48t35_rtc *)
-	    (KL_CONFIG_CH_CONS_INFO(nid)->memory_base + IOC3_BYTEBUS_DEV0);
+	(KL_CONFIG_CH_CONS_INFO(master_nasid)->memory_base + IOC3_BYTEBUS_DEV0);
 
 	printk(KERN_INFO "Real Time Clock Driver v%s\n", RTC_VERSION);
 	if (misc_register(&rtc_dev)) {
@@ -325,3 +323,7 @@
 
 	rtc_tm->tm_mon--;
 }
+
+MODULE_AUTHOR("Ralf Baechle <ralf@linux-mips.org>");
+MODULE_DESCRIPTION("SGI IP27 M48T35 RTC driver");
+MODULE_LICENSE("GPL");
diff -Nur linux-2.4.30/drivers/char/Makefile linux-2.4.30-linux-mips.org/drivers/char/Makefile
--- linux-2.4.30/drivers/char/Makefile	2004-08-08 01:26:04.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/char/Makefile	2005-05-12 17:57:19.000000000 +0200
@@ -48,7 +48,12 @@
     KEYBD    =
   endif
   ifeq ($(CONFIG_VR41XX_KIU),y)
-    KEYMAP   =
+    ifeq ($(CONFIG_IBM_WORKPAD),y)
+      KEYMAP = ibm_workpad_keymap.o
+    endif
+    ifeq ($(CONFIG_VICTOR_MPC30X),y)
+      KEYMAP = victor_mpc30x_keymap.o
+    endif
     KEYBD    = vr41xx_keyb.o
   endif
 endif
@@ -251,7 +256,6 @@
 obj-$(CONFIG_RTC) += rtc.o
 obj-$(CONFIG_GEN_RTC) += genrtc.o
 obj-$(CONFIG_EFI_RTC) += efirtc.o
-obj-$(CONFIG_SGI_DS1286) += ds1286.o
 obj-$(CONFIG_MIPS_RTC) += mips_rtc.o
 obj-$(CONFIG_SGI_IP27_RTC) += ip27-rtc.o
 ifeq ($(CONFIG_PPC),)
@@ -259,6 +263,7 @@
 endif
 obj-$(CONFIG_TOSHIBA) += toshiba.o
 obj-$(CONFIG_I8K) += i8k.o
+obj-$(CONFIG_DS1286) += ds1286.o
 obj-$(CONFIG_DS1620) += ds1620.o
 obj-$(CONFIG_DS1742) += ds1742.o
 obj-$(CONFIG_INTEL_RNG) += i810_rng.o
@@ -269,6 +274,7 @@
 
 obj-$(CONFIG_ITE_GPIO) += ite_gpio.o
 obj-$(CONFIG_AU1X00_GPIO) += au1000_gpio.o
+obj-$(CONFIG_AU1550_PSC_SPI) += au1550_psc_spi.o
 obj-$(CONFIG_AU1X00_USB_TTY) += au1000_usbtty.o
 obj-$(CONFIG_AU1X00_USB_RAW) += au1000_usbraw.o
 obj-$(CONFIG_COBALT_LCD) += lcd.o
@@ -353,3 +359,9 @@
 
 qtronixmap.c: qtronixmap.map
 	set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@
+
+ibm_workpad_keymap.c: ibm_workpad_keymap.map
+	set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@
+
+victor_mpc30x_keymap.c: victor_mpc30x_keymap.map
+	set -e ; loadkeys --mktable $< | sed -e 's/^static *//' > $@
diff -Nur linux-2.4.30/drivers/char/mips_rtc.c linux-2.4.30-linux-mips.org/drivers/char/mips_rtc.c
--- linux-2.4.30/drivers/char/mips_rtc.c	2004-01-05 14:53:56.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/char/mips_rtc.c	2005-05-12 17:57:21.000000000 +0200
@@ -53,14 +53,6 @@
 #include <asm/io.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
-
-/*
- * Check machine
- */
-#if !defined(CONFIG_MIPS) || !defined(CONFIG_NEW_TIME_C)
-#error "This driver is for MIPS machines with CONFIG_NEW_TIME_C defined"
-#endif
-
 #include <asm/time.h>
 
 static unsigned long rtc_status = 0;	/* bitmapped status byte.       */
diff -Nur linux-2.4.30/drivers/char/sb1250_duart.c linux-2.4.30-linux-mips.org/drivers/char/sb1250_duart.c
--- linux-2.4.30/drivers/char/sb1250_duart.c	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/char/sb1250_duart.c	2005-05-12 17:57:21.000000000 +0200
@@ -328,10 +328,11 @@
 		if (c <= 0) break;
 
 		if (from_user) {
+			spin_unlock_irqrestore(&us->outp_lock, flags);
 			if (copy_from_user(us->outp_buf + us->outp_tail, buf, c)) {
-				spin_unlock_irqrestore(&us->outp_lock, flags);
 				return -EFAULT;
 			}
+			spin_lock_irqsave(&us->outp_lock, flags);
 		} else {
 			memcpy(us->outp_buf + us->outp_tail, buf, c);
 		}
@@ -498,9 +499,31 @@
 	duart_set_cflag(us->line, tty->termios->c_cflag);
 }
 
+static int get_serial_info(uart_state_t *us, struct serial_struct * retinfo) {
+
+	struct serial_struct tmp;
+
+	memset(&tmp, 0, sizeof(tmp));
+
+	tmp.type=PORT_SB1250;
+	tmp.line=us->line;
+	tmp.port=A_DUART_CHANREG(tmp.line,0);
+	tmp.irq=K_INT_UART_0 + tmp.line;
+	tmp.xmit_fifo_size=16; /* fixed by hw */
+	tmp.baud_base=5000000;
+	tmp.io_type=SERIAL_IO_MEM;
+
+	if (copy_to_user(retinfo,&tmp,sizeof(*retinfo)))
+		return -EFAULT;
+
+	return 0;
+}
+
 static int duart_ioctl(struct tty_struct *tty, struct file * file,
 		       unsigned int cmd, unsigned long arg)
 {
+	uart_state_t *us = (uart_state_t *) tty->driver_data;
+
 /*	if (serial_paranoia_check(info, tty->device, "rs_ioctl"))
 	return -ENODEV;*/
 	switch (cmd) {
@@ -517,7 +540,7 @@
 		printk("Ignoring TIOCMSET\n");
 		break;
 	case TIOCGSERIAL:
-		printk("Ignoring TIOCGSERIAL\n");
+		return get_serial_info(us,(struct serial_struct *) arg);
 		break;
 	case TIOCSSERIAL:
 		printk("Ignoring TIOCSSERIAL\n");
diff -Nur linux-2.4.30/drivers/char/serial.c linux-2.4.30-linux-mips.org/drivers/char/serial.c
--- linux-2.4.30/drivers/char/serial.c	2005-01-19 15:09:50.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/char/serial.c	2005-05-12 17:57:21.000000000 +0200
@@ -62,6 +62,12 @@
  *        Robert Schwebel <robert@schwebel.de>,
  *        Juergen Beisert <jbeisert@eurodsn.de>,
  *        Theodore Ts'o <tytso@mit.edu>
+ *
+ * 10/00: Added suport for MIPS Atlas board.
+ * 11/00: Hooks for serial kernel debug port support added.
+ *        Kevin D. Kissell, kevink@mips.com and Carsten Langgaard,
+ *        carstenl@mips.com
+ *        Copyright (C) 2000 MIPS Technologies, Inc.  All rights reserved.
  */
 
 static char *serial_version = "5.05c";
@@ -413,6 +419,22 @@
 	return 0;
 }
 
+#if defined(CONFIG_MIPS_ATLAS) || defined(CONFIG_MIPS_SEAD)
+
+#include <asm/mips-boards/atlas.h>
+
+static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset)
+{
+        return (*(volatile unsigned int *)(mips_io_port_base + ATLAS_UART_REGS_BASE + offset*8) & 0xff);
+}
+
+static _INLINE_ void serial_out(struct async_struct *info, int offset, int value)
+{
+        *(volatile unsigned int *)(mips_io_port_base + ATLAS_UART_REGS_BASE + offset*8) = value;
+}
+
+#else
+
 static _INLINE_ unsigned int serial_in(struct async_struct *info, int offset)
 {
 	switch (info->io_type) {
@@ -447,6 +469,8 @@
 		outb(value, info->port+offset);
 	}
 }
+#endif
+
 
 /*
  * We used to support using pause I/O for certain machines.  We
diff -Nur linux-2.4.30/drivers/char/victor_mpc30x_keymap.map linux-2.4.30-linux-mips.org/drivers/char/victor_mpc30x_keymap.map
--- linux-2.4.30/drivers/char/victor_mpc30x_keymap.map	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/char/victor_mpc30x_keymap.map	2005-05-12 17:57:22.000000000 +0200
@@ -0,0 +1,102 @@
+# Victor Interlink MP-C303/304 keyboard keymap
+#
+# Copyright (C) 2003  Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
+#
+# This file is subject to the terms and conditions of the GNU General Public
+# License.  See the file "COPYING" in the main directory of this archive
+# for more details.
+keymaps 0-1,4-5,8-9,12
+alt_is_meta
+strings as usual
+compose as usual for "iso-8859-1"
+
+# First line
+keycode 89 = Escape
+keycode  9 = Delete
+
+# 2nd line
+keycode 73 = one              exclam
+keycode 18 = two              quotedbl
+keycode 92 = three            numbersign
+	control	keycode 92 = Escape
+keycode 53 = four             dollar
+	control	keycode 53 = Control_backslash
+keycode 21 = five             percent
+	control	keycode 21 = Control_bracketright
+keycode 50 = six              ampersand
+	control	keycode 50 = Control_underscore
+keycode 48 = seven            apostrophe
+keycode 51 = eight            parenleft
+keycode 16 = nine             parenright
+keycode 80 = zero             asciitilde
+	control	keycode 80 = nul
+keycode 49 = minus            equal
+keycode 30 = asciicircum      asciitilde
+	control	keycode 30 = Control_asciicircum
+keycode  5 = backslash        bar
+	control	keycode  5 = Control_backslash
+keycode 13 = BackSpace
+# 3rd line
+keycode 57 = Tab
+keycode 74 = q
+keycode 26 = w
+keycode 81 = e
+keycode 29 = r
+keycode 37 = t
+keycode 45 = y
+keycode 72 = u
+keycode 24 = i
+keycode 32 = o
+keycode 41 = p
+keycode  1 = at               grave
+	control	keycode  1 = nul
+keycode 54 = bracketleft      braceleft
+keycode 63 = Return
+	alt	keycode 63 = Meta_Control_m
+# 4th line
+keycode 23 = Caps_Lock
+keycode 34 = a
+keycode 66 = s
+keycode 52 = d
+keycode 20 = f
+keycode 84 = g
+keycode 67 = h
+keycode 64 = j
+keycode 17 = k
+keycode 83 = l
+keycode 22 = semicolon        plus
+keycode 61 = colon            asterisk
+	control keycode 61 = Control_g
+keycode 65 = bracketright     braceright
+	control	keycode 65 = Control_bracketright
+# 5th line
+keycode 91 = Shift
+keycode 76 = z
+keycode 68 = x
+keycode 28 = c
+keycode 36 = v
+keycode 44 = b
+keycode 19 = n
+keycode 27 = m
+keycode 35 = comma            less
+keycode  3 = period           greater
+	control	keycode  3 = Compose
+keycode 38 = slash            question
+	control	keycode 38 = Delete
+	shift	control	keycode 38 = Delete
+keycode  6 = backslash        underscore
+	control	keycode  6 = Control_backslash
+keycode 55 = Up
+	alt keycode 55 = PageUp
+keycode 14 = Shift
+# 6th line
+keycode 56 = Control
+keycode 42 = Alt
+keycode 33 = space
+	control	keycode 33 = nul
+keycode  7 = Left
+	alt keycode  7 = Home
+keycode 31 = Down
+	alt keycode 31 = PageDown
+keycode 47 = Right
+	alt keycode 47 = End
diff -Nur linux-2.4.30/drivers/char/vr41xx_keyb.c linux-2.4.30-linux-mips.org/drivers/char/vr41xx_keyb.c
--- linux-2.4.30/drivers/char/vr41xx_keyb.c	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/char/vr41xx_keyb.c	2005-05-12 17:57:22.000000000 +0200
@@ -308,7 +308,7 @@
 			if (found != 0) {
 				kiu_base = VRC4173_KIU_OFFSET;
 				mkiuintreg = VRC4173_MKIUINTREG_OFFSET;
-				vrc4173_clock_supply(VRC4173_KIU_CLOCK);
+				vrc4173_supply_clock(VRC4173_KIU_CLOCK);
 			}
 		}
 #endif
@@ -325,7 +325,7 @@
 
 	if (current_cpu_data.cputype == CPU_VR4111 ||
 	    current_cpu_data.cputype == CPU_VR4121)
-		vr41xx_clock_supply(KIU_CLOCK);
+		vr41xx_supply_clock(KIU_CLOCK);
 
 	kiu_writew(KIURST_KIURST, KIURST);
 
diff -Nur linux-2.4.30/drivers/i2c/Config.in linux-2.4.30-linux-mips.org/drivers/i2c/Config.in
--- linux-2.4.30/drivers/i2c/Config.in	2004-04-14 15:05:29.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/i2c/Config.in	2005-05-12 17:57:22.000000000 +0200
@@ -57,6 +57,10 @@
    if [ "$CONFIG_SGI_IP22" = "y" ]; then
       dep_tristate 'I2C SGI interfaces' CONFIG_I2C_ALGO_SGI $CONFIG_I2C
    fi
+
+   if [ "$CONFIG_SOC_AU1550" = "y" -o "$CONFIG_SOC_AU1200" ]; then
+      dep_tristate 'Au1550/Au1200 SMBus interface' CONFIG_I2C_ALGO_AU1550 $CONFIG_I2C
+   fi
  
 # This is needed for automatic patch generation: sensors code starts here
 # This is needed for automatic patch generation: sensors code ends here
diff -Nur linux-2.4.30/drivers/i2c/i2c-algo-au1550.c linux-2.4.30-linux-mips.org/drivers/i2c/i2c-algo-au1550.c
--- linux-2.4.30/drivers/i2c/i2c-algo-au1550.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/i2c/i2c-algo-au1550.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,347 @@
+/*
+ * i2c-algo-au1550.c: SMBus (i2c) driver algorithms for Alchemy PSC interface
+ * Copyright (C) 2004 Embedded Edge, LLC <dan@embeddededge.com>
+ *
+ * The documentation describes this as an SMBus controller, but it doesn't
+ * understand any of the SMBus protocol in hardware.  It's really an I2C
+ * controller that could emulate most of the SMBus in software.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+
+#include <asm/au1000.h>
+#include <asm/au1xxx_psc.h>
+
+#include <linux/i2c.h>
+#include <linux/i2c-algo-au1550.h>
+
+static int
+wait_xfer_done(struct i2c_algo_au1550_data *adap)
+{
+	u32	stat;
+	int	i;
+	volatile psc_smb_t	*sp;
+
+	sp = (volatile psc_smb_t *)(adap->psc_base);
+
+	/* Wait for Tx FIFO Underflow.
+	*/
+	for (i = 0; i < adap->xfer_timeout; i++) {
+		stat = sp->psc_smbevnt;
+		au_sync();
+		if ((stat & PSC_SMBEVNT_TU) != 0) {
+			/* Clear it.  */
+			sp->psc_smbevnt = PSC_SMBEVNT_TU;
+			au_sync();
+			return 0;
+		}
+		udelay(1);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int
+wait_ack(struct i2c_algo_au1550_data *adap)
+{
+	u32	stat;
+	volatile psc_smb_t	*sp;
+
+	if (wait_xfer_done(adap))
+		return -ETIMEDOUT;
+
+	sp = (volatile psc_smb_t *)(adap->psc_base);
+
+	stat = sp->psc_smbevnt;
+	au_sync();
+
+	if ((stat & (PSC_SMBEVNT_DN | PSC_SMBEVNT_AN | PSC_SMBEVNT_AL)) != 0)
+		return -ETIMEDOUT;
+
+	return 0;
+}
+
+static int
+wait_master_done(struct i2c_algo_au1550_data *adap)
+{
+	u32	stat;
+	int	i;
+	volatile psc_smb_t	*sp;
+
+	sp = (volatile psc_smb_t *)(adap->psc_base);
+
+	/* Wait for Master Done.
+	*/
+	for (i = 0; i < adap->xfer_timeout; i++) {
+		stat = sp->psc_smbevnt;
+		au_sync();
+		if ((stat & PSC_SMBEVNT_MD) != 0)
+			return 0;
+		udelay(1);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static int
+do_address(struct i2c_algo_au1550_data *adap, unsigned int addr, int rd)
+{
+	volatile psc_smb_t	*sp;
+	u32			stat;
+
+	sp = (volatile psc_smb_t *)(adap->psc_base);
+
+	/* Reset the FIFOs, clear events.
+	*/
+	stat = sp->psc_smbstat;
+	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();
+	} while ((stat & PSC_SMBPCR_DC) != 0);
+		udelay(50);
+	}
+	/* Write out the i2c chip address and specify operation
+	*/
+	addr <<= 1;
+	if (rd)
+		addr |= 1;
+
+	/* Put byte into fifo, start up master.
+	*/
+	sp->psc_smbtxrx = addr;
+	au_sync();
+	sp->psc_smbpcr = PSC_SMBPCR_MS;
+	au_sync();
+	if (wait_ack(adap))
+		return -EIO;
+	return 0;
+}
+
+static u32
+wait_for_rx_byte(struct i2c_algo_au1550_data *adap, u32 *ret_data)
+{
+	int	j;
+	u32	data, stat;
+	volatile psc_smb_t	*sp;
+
+	if (wait_xfer_done(adap))
+		return -EIO;
+
+	sp = (volatile psc_smb_t *)(adap->psc_base);
+
+	j =  adap->xfer_timeout * 100;
+	do {
+		j--;
+		if (j <= 0)
+			return -EIO;
+
+		stat = sp->psc_smbstat;
+		au_sync();
+		if ((stat & PSC_SMBSTAT_RE) == 0)
+			j = 0;
+		else
+			udelay(1);
+	} while (j > 0);
+	data = sp->psc_smbtxrx;
+	au_sync();
+	*ret_data = data;
+
+	return 0;
+}
+
+static int
+i2c_read(struct i2c_algo_au1550_data *adap, unsigned char *buf,
+		    unsigned int len)
+{
+	int	i;
+	u32	data;
+	volatile psc_smb_t	*sp;
+
+	if (len == 0)
+		return 0;
+
+	/* A read is performed by stuffing the transmit fifo with
+	 * zero bytes for timing, waiting for bytes to appear in the
+	 * receive fifo, then reading the bytes.
+	 */
+
+	sp = (volatile psc_smb_t *)(adap->psc_base);
+
+	i = 0;
+	while (i < (len-1)) {
+		sp->psc_smbtxrx = 0;
+		au_sync();
+		if (wait_for_rx_byte(adap, &data))
+			return -EIO;
+
+		buf[i] = data;
+		i++;
+	}
+
+	/* The last byte has to indicate transfer done.
+	*/
+	sp->psc_smbtxrx = PSC_SMBTXRX_STP;
+	au_sync();
+	if (wait_master_done(adap))
+		return -EIO;
+
+	data = sp->psc_smbtxrx;
+	au_sync();
+	buf[i] = data;
+	return 0;
+}
+
+static int
+i2c_write(struct i2c_algo_au1550_data *adap, unsigned char *buf,
+		     unsigned int len)
+{
+	int	i;
+	u32	data;
+	volatile psc_smb_t	*sp;
+
+	if (len == 0)
+		return 0;
+
+	sp = (volatile psc_smb_t *)(adap->psc_base);
+
+	i = 0;
+	while (i < (len-1)) {
+		data = buf[i];
+		sp->psc_smbtxrx = data;
+		au_sync();
+		if (wait_ack(adap))
+			return -EIO;
+		i++;
+	}
+
+	/* The last byte has to indicate transfer done.
+	*/
+	data = buf[i];
+	data |= PSC_SMBTXRX_STP;
+	sp->psc_smbtxrx = data;
+	au_sync();
+	if (wait_master_done(adap))
+		return -EIO;
+	return 0;
+}
+
+static int
+au1550_xfer(struct i2c_adapter *i2c_adap, struct i2c_msg msgs[], int num)
+{
+	struct i2c_algo_au1550_data *adap = i2c_adap->algo_data;
+	struct i2c_msg *p;
+	int i, err = 0;
+
+	for (i = 0; !err && i < num; i++) {
+		p = &msgs[i];
+		err = do_address(adap, p->addr, p->flags & I2C_M_RD);
+		if (err || !p->len)
+			continue;
+		if (p->flags & I2C_M_RD)
+			err = i2c_read(adap, p->buf, p->len);
+		else
+			err = i2c_write(adap, p->buf, p->len);
+	}
+
+	/* Return the number of messages processed, or the error code.
+	*/
+	if (err == 0)
+		err = num;
+	return err;
+}
+
+static u32
+au1550_func(struct i2c_adapter *adap)
+{
+	return I2C_FUNC_I2C;
+}
+
+static struct i2c_algorithm au1550_algo = {
+	.name		= "Au1550 algorithm",
+	.id		= I2C_ALGO_AU1550,
+	.master_xfer	= au1550_xfer,
+	.functionality	= au1550_func,
+};
+
+/* 
+ * registering functions to load algorithms at runtime 
+ * Prior to calling us, the 50MHz clock frequency and routing
+ * must have been set up for the PSC indicated by the adapter.
+ */
+int
+i2c_au1550_add_bus(struct i2c_adapter *i2c_adap)
+{
+	struct i2c_algo_au1550_data *adap = i2c_adap->algo_data;
+	volatile psc_smb_t	*sp;
+	u32	stat;
+
+	i2c_adap->algo = &au1550_algo;
+
+	/* Now, set up the PSC for SMBus PIO mode.
+	*/
+	sp = (volatile psc_smb_t *)(adap->psc_base);
+	sp->psc_ctrl = PSC_CTRL_DISABLE;
+	au_sync();
+	sp->psc_sel = PSC_SEL_PS_SMBUSMODE;
+	sp->psc_smbcfg = 0;
+	au_sync();
+	sp->psc_ctrl = PSC_CTRL_ENABLE;
+	au_sync();
+	do {
+		stat = sp->psc_smbstat;
+		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);
+
+	/* 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_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);
+	au_sync();
+
+	sp->psc_smbcfg |= PSC_SMBCFG_DE_ENABLE;
+	do {
+		stat = sp->psc_smbstat;
+		au_sync();
+	} while ((stat & PSC_SMBSTAT_DR) == 0);
+
+	return i2c_add_adapter(i2c_adap);
+}
+
+
+int
+i2c_au1550_del_bus(struct i2c_adapter *adap)
+{
+	return i2c_del_adapter(adap);
+}
+
+EXPORT_SYMBOL(i2c_au1550_add_bus);
+EXPORT_SYMBOL(i2c_au1550_del_bus);
+
+MODULE_AUTHOR("Dan Malek <dan@embeddededge.com>");
+MODULE_DESCRIPTION("SMBus Au1550 algorithm");
+MODULE_LICENSE("GPL");
diff -Nur linux-2.4.30/drivers/i2c/i2c-au1550.c linux-2.4.30-linux-mips.org/drivers/i2c/i2c-au1550.c
--- linux-2.4.30/drivers/i2c/i2c-au1550.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/i2c/i2c-au1550.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,157 @@
+/*
+ * i2c-au1550.c: SMBus (i2c) adapter for Alchemy PSC interface
+ * Copyright (C) 2004 Embedded Edge, LLC <dan@embeddededge.com>
+ *
+ * This is just a skeleton adapter to use with the Au1550 PSC
+ * algorithm.  It was developed for the Pb1550, but will work with
+ * any Au1550 board that has a similar PSC configuration.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ * 
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+
+#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 <linux/i2c.h>
+#include <linux/i2c-algo-au1550.h>
+
+
+
+static int
+pb1550_reg(struct i2c_client *client)
+{
+	return 0;
+}
+
+static int
+pb1550_unreg(struct i2c_client *client)
+{
+	return 0;
+}
+
+static void
+pb1550_inc_use(struct i2c_adapter *adap)
+{
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+}
+
+static void
+pb1550_dec_use(struct i2c_adapter *adap)
+{
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+}
+
+static struct i2c_algo_au1550_data pb1550_i2c_info = {
+	SMBUS_PSC_BASE, 200, 200
+};
+
+static struct i2c_adapter pb1550_board_adapter = {
+	name:              "pb1550 adapter",
+	id:                I2C_HW_AU1550_PSC,
+	algo:              NULL,
+	algo_data:         &pb1550_i2c_info,
+	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
+	 * configured by Yamon as a 48MHz clock, close enough
+	 * for our work.
+	 */
+        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);
+}
+
+/* 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);
+}
+
+EXPORT_SYMBOL(pb1550_wm_codec_write);
+EXPORT_SYMBOL(pb1550_i2c_xfer);
+
+MODULE_AUTHOR("Dan Malek, Embedded Edge, LLC.");
+MODULE_DESCRIPTION("SMBus adapter Alchemy pb1550");
+MODULE_LICENSE("GPL");
+
+int
+init_module(void)
+{
+	return i2c_pb1550_init();
+}
+
+void
+cleanup_module(void)
+{
+	i2c_au1550_del_bus(&pb1550_board_adapter);
+}
diff -Nur linux-2.4.30/drivers/i2c/i2c-core.c linux-2.4.30-linux-mips.org/drivers/i2c/i2c-core.c
--- linux-2.4.30/drivers/i2c/i2c-core.c	2005-01-19 15:09:54.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/i2c/i2c-core.c	2005-05-12 17:57:22.000000000 +0200
@@ -1280,6 +1280,9 @@
 #ifdef CONFIG_I2C_MAX1617
 	extern int i2c_max1617_init(void);
 #endif
+#ifdef CONFIG_I2C_ALGO_AU1550
+	extern int i2c_pb1550_init(void);
+#endif
 
 #ifdef CONFIG_I2C_PROC
 	extern int sensors_init(void);
@@ -1335,6 +1338,10 @@
 	i2c_max1617_init();
 #endif
 
+#ifdef CONFIG_I2C_ALGO_AU1550
+	i2c_pb1550_init();
+#endif
+
 	/* -------------- proc interface ---- */
 #ifdef CONFIG_I2C_PROC
 	sensors_init();
diff -Nur linux-2.4.30/drivers/i2c/Makefile linux-2.4.30-linux-mips.org/drivers/i2c/Makefile
--- linux-2.4.30/drivers/i2c/Makefile	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/i2c/Makefile	2005-05-12 17:57:22.000000000 +0200
@@ -6,7 +6,7 @@
 
 export-objs	:= i2c-core.o i2c-algo-bit.o i2c-algo-pcf.o \
 		   i2c-algo-ite.o i2c-algo-sibyte.o i2c-algo-sgi.o \
-		   i2c-proc.o
+		   i2c-algo-au1550.o i2c-proc.o i2c-au1550.o
 
 obj-$(CONFIG_I2C)		+= i2c-core.o
 obj-$(CONFIG_I2C_CHARDEV)	+= i2c-dev.o
@@ -25,6 +25,7 @@
 obj-$(CONFIG_I2C_ALGO_SIBYTE)	+= i2c-algo-sibyte.o i2c-sibyte.o
 obj-$(CONFIG_I2C_MAX1617)	+= i2c-max1617.o
 obj-$(CONFIG_I2C_ALGO_SGI)	+= i2c-algo-sgi.o
+obj-$(CONFIG_I2C_ALGO_AU1550)	+= i2c-algo-au1550.o i2c-au1550.o
 
 # This is needed for automatic patch generation: sensors code starts here
 # This is needed for automatic patch generation: sensors code ends here
diff -Nur linux-2.4.30/drivers/ide/au1xxx/au1xxx-ide.c linux-2.4.30-linux-mips.org/drivers/ide/au1xxx/au1xxx-ide.c
--- linux-2.4.30/drivers/ide/au1xxx/au1xxx-ide.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/ide/au1xxx/au1xxx-ide.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,853 @@
+/*
+ * linux/drivers/ide/au1xxx/au1xxx-ide.c	version 0.9	Jan. 28 2005
+ *
+ * BRIEF MODULE DESCRIPTION
+ *	AMD Alchemy Au1xxx IDE interface routines over the Static Bus
+ *
+ * Copyright (c) 2003-2005 AMD, Personal Connectivity Solutions
+ *
+ *  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.
+ *
+ *  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>
+
+/*
+ * Platform dependent header files
+ */
+#if defined(CONFIG_MIPS_EP1000)
+#include <asm/ep1000.h>
+
+#elif defined(CONFIG_MIPS_PB1550)
+#include <asm/pb1550.h>
+
+#elif defined(CONFIG_MIPS_DB1550)
+#include <asm/db1x00.h>
+
+#elif defined(CONFIG_MIPS_PB1200)
+#include <asm/pb1200.h>
+
+#elif defined(CONFIG_MIPS_DB1200)
+#include <asm/db1200.h>
+
+#elif defined(CONFIG_MIPS_FICMMP)
+#include <asm/ficmmp.h>
+#endif
+
+#include <linux/config.h>
+
+#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		2
+#endif
+
+typedef struct
+{
+	u32			tx_dev_id, rx_dev_id;
+	u32			tx_chan, rx_chan;
+	void			*tx_desc_head, *rx_desc_head;
+	ide_hwif_t 		*hwif;
+#if defined(CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX)
+	ide_drive_t 		*drive;
+	struct dbdma_cmd	*dma_table_cpu;
+	dma_addr_t		dma_table_dma;
+	struct scatterlist	*sg_table;
+	int			sg_nents;
+	int			sg_dma_direction;
+
+	struct pci_dev 		pcidev;
+	struct pci_bus 		pcibus;
+#endif
+} _auide_hwif;
+
+struct drive_list_entry {
+	char * id_model;
+	char * id_firmware;
+};
+static _auide_hwif auide_hwif;
+
+static struct drive_list_entry dma_black_list[] = {
+	{ "WDC WD100EB-00CGH0"	,	"ALL"		},
+	{ "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
+static int auide_base=KSEG1ADDR(AU1XXX_ATA_PHYS_ADDR);
+static int auide_len=AU1XXX_ATA_PHYS_LEN;
+static int auide_irq=AU1XXX_ATA_INT;
+
+
+/*
+ * local I/O functions
+ */
+u8 auide_inb(unsigned long port)
+{
+	return (au_readb(port));
+}
+
+u16 auide_inw(unsigned long port)
+{
+	return (au_readw(port));
+}
+
+u32 auide_inl(unsigned long port)
+{
+	return (au_readl(port));
+}
+
+void auide_insw(unsigned long port, void *addr, u32 count)
+{
+#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_dest_flags(ahwif->rx_chan, (void*)addr, count << 1, DDMA_FLAGS_NOIE)) {
+		printk(KERN_ERR "%s failed %d\n", __FUNCTION__, __LINE__);
+		return;
+	}
+        ctp = *((chan_tab_t **)ahwif->rx_chan);
+	dp = ctp->cur_ptr;
+	while (dp->dscr_cmd0 & DSCR_CMD0_V)
+		;
+        ctp->cur_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+#else
+	while (count--)
+	{
+		*(u16 *)addr = au_readw(port);
+		addr +=2 ;
+	}
+#endif
+}
+
+void auide_insl(unsigned long port, void *addr, u32 count)
+{
+	while (count--)
+	{
+		*(u32 *)addr = au_readl(port);
+		/* NOTE: For IDE interfaces over PCMCIA, 32-bit access does not work */
+		addr += 4;
+	}
+}
+
+void auide_outb(u8 addr, unsigned long port)
+{
+	return (au_writeb(addr, port));
+}
+
+void auide_outbsync(ide_drive_t *drive, u8 addr, unsigned long port)
+{
+	return (au_writeb(addr, port));
+}
+
+void auide_outw(u16 addr, unsigned long port)
+{
+	return (au_writew(addr, port));
+}
+
+void auide_outl(u32 addr, unsigned long port)
+{
+	return (au_writel(addr, port));
+}
+
+void auide_outsw(unsigned long port, void *addr, u32 count)
+{
+#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;
+	}
+	ctp = *((chan_tab_t **)ahwif->tx_chan);
+	dp = ctp->cur_ptr;
+	while (dp->dscr_cmd0 & DSCR_CMD0_V)
+		;
+        ctp->cur_ptr = phys_to_virt(DSCR_GET_NXTPTR(dp->dscr_nxtptr));
+#else
+	while (count--)
+	{
+		au_writew(*(u16 *)addr, port);
+		addr += 2;
+	}
+#endif
+}
+
+void auide_outsl(unsigned long port, void *addr, u32 count)
+{
+	while (count--)
+	{
+		au_writel(*(u32 *)addr, port);
+		/* NOTE: For IDE interfaces over PCMCIA, 32-bit access does not work */
+		addr += 4;
+	}
+}
+
+/*
+ * 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;
+	struct buffer_head *bh;
+	struct scatterlist *sg = ahwif->sg_table;
+	unsigned long lastdataend = ~0UL;
+	int nents = 0;
+
+	ahwif->sg_dma_direction = data_dir;
+
+	if (hwif->sg_dma_active)
+		BUG();
+
+	bh = rq->bh;
+	do {
+		int contig = 0;
+
+		if (bh->b_page) {
+			if (bh_phys(bh) == lastdataend)
+				contig = 1;
+		} else {
+			if ((unsigned long) bh->b_data == lastdataend)
+				contig = 1;
+		}
+
+		if (contig) {
+			sg[nents - 1].length += bh->b_size;
+			lastdataend += bh->b_size;
+			continue;
+		}
+
+		if (nents >= NUM_DESCRIPTORS)
+		{
+			printk(KERN_ERR "%s nents >= NUM_DESCRIPTORS\n", __FUNCTION__);
+			return 0;
+		}
+
+		memset(&sg[nents], 0, sizeof(*sg));
+
+		if (bh->b_page) {
+			sg[nents].page = bh->b_page;
+			sg[nents].offset = bh_offset(bh);
+			lastdataend = bh_phys(bh) + bh->b_size;
+		} else {
+			if ((unsigned long) bh->b_data < PAGE_SIZE)
+				BUG();
+
+			sg[nents].address = bh->b_data;
+			lastdataend = (unsigned long) bh->b_data + bh->b_size;
+		}
+
+		sg[nents].length = bh->b_size;
+		nents++;
+	} while ((bh = bh->b_reqnext) != NULL);
+
+	if(nents == 0)
+		BUG();
+	return pci_map_sg(hwif->pci_dev, sg, nents, data_dir);
+}
+
+static int  auide_raw_build_sglist(ide_hwif_t *hwif, struct request *rq)
+{
+	_auide_hwif *ahwif = (_auide_hwif*)hwif->hwif_data;
+	struct scatterlist *sg = hwif->sg_table;
+	int nents = 0;
+	ide_task_t *args = rq->special;
+	unsigned char *virt_addr = rq->buffer;
+	int sector_count = rq->nr_sectors;
+
+	if (args->command_type == IDE_DRIVE_TASK_RAW_WRITE)
+		ahwif->sg_dma_direction = PCI_DMA_TODEVICE;
+	else
+		ahwif->sg_dma_direction = PCI_DMA_FROMDEVICE;
+
+	if (sector_count > 128) {
+		memset(&sg[nents], 0, sizeof(*sg));
+		sg[nents].address = virt_addr;
+		sg[nents].length = 128  * SECTOR_SIZE;
+		nents++;
+		virt_addr = virt_addr + (128 * SECTOR_SIZE);
+		sector_count -= 128;
+	}
+	memset(&sg[nents], 0, sizeof(*sg));
+	sg[nents].address = virt_addr;
+	sg[nents].length =  sector_count  * SECTOR_SIZE;
+	nents++;
+
+	return pci_map_sg(hwif->pci_dev, sg, nents, ahwif->sg_dma_direction);
+}
+
+static void auide_destroy_dmatable (ide_drive_t *drive)
+{
+	struct pci_dev *dev = HWIF(drive)->pci_dev;
+	ide_hwif_t *hwif = HWIF(drive);
+	_auide_hwif *ahwif = (_auide_hwif*)hwif->hwif_data;
+
+	if (ahwif->sg_nents) {
+		pci_unmap_sg(dev, ahwif->sg_table, ahwif->sg_nents, ahwif->sg_dma_direction);
+		ahwif->sg_nents = 0;
+		HWIF(drive)->sg_dma_active = 0;
+	}
+}
+
+int auide_build_dmatable(ide_drive_t *drive, struct request *rq, int ddir)
+{
+	int i, iswrite, count = 0;
+	ide_hwif_t *hwif = HWIF(drive);
+	_auide_hwif *ahwif = (_auide_hwif*)hwif->hwif_data;
+	struct scatterlist *sg;
+
+	iswrite = (ddir == PCI_DMA_TODEVICE);
+	/* Save for interrupt context */
+	ahwif->drive = drive;
+
+	/* Build sglist */
+	if (rq->cmd == IDE_DRIVE_TASKFILE)
+		ahwif->sg_nents = i = auide_raw_build_sglist(hwif, rq);
+	else
+		ahwif->sg_nents = i = auide_build_sglist(hwif, rq, ddir);
+	if (!i)
+		return 0;
+
+	/* fill the descriptors */
+	sg = ahwif->sg_table;
+	while (i && sg_dma_len(sg)) {
+		u32 cur_addr;
+		u32 cur_len;
+
+		cur_addr = sg_dma_address(sg);
+		cur_len = sg_dma_len(sg);
+
+		while (cur_len) {
+			u32 flags = DDMA_FLAGS_NOIE;
+			unsigned int tc = (cur_len < 0xfe00)? cur_len: 0xfe00;
+
+			if (++count >= PRD_ENTRIES) {
+				printk(KERN_WARNING "%s: DMA table too small\n",
+				       drive->name);
+				goto use_pio_instead;
+			}
+
+			/* Lets enable intr for the last descriptor only */
+			if (1==i)
+				flags = DDMA_FLAGS_IE;
+
+			if (iswrite) {
+				if(!au1xxx_dbdma_put_source_flags(ahwif->tx_chan, (void*)phys_to_virt(cur_addr), tc, flags))
+					printk(KERN_ERR "%s failed %d\n", __FUNCTION__, __LINE__);
+			}
+			else {
+				if(!au1xxx_dbdma_put_dest_flags(ahwif->rx_chan, (void*)phys_to_virt(cur_addr), tc, flags))
+					printk(KERN_ERR "%s failed %d\n", __FUNCTION__, __LINE__);
+			}
+
+			cur_addr += tc;
+			cur_len -= tc;
+		}
+		sg++;
+		i--;
+	}
+	if (count)
+		return 1;
+use_pio_instead:
+	pci_unmap_sg(hwif->pci_dev,
+		     ahwif->sg_table,
+		     ahwif->sg_nents,
+		     ahwif->sg_dma_direction);
+	hwif->sg_dma_active = 0;
+	return 0; /* revert to PIO for this request */
+}
+
+static int auide_dma_end(ide_drive_t *drive)
+{
+	auide_destroy_dmatable(drive);
+	return 0;
+}
+
+int auide_dma_begin(ide_drive_t *drive )
+{
+	return 0;
+}
+
+int auide_dma_read(ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	u8 lba48 = (drive->addressing == 1) ? 1 : 0;
+	task_ioreg_t command = WIN_NOP;
+
+	if (!auide_build_dmatable(drive, rq, PCI_DMA_FROMDEVICE)) {
+		/* try PIO instead of DMA */
+		return 1;
+	}
+	drive->waiting_for_dma = 1;
+	if (drive->media != ide_disk)
+		return 0;
+
+	/*
+	 * FIX ME to use only ACB ide_task_t args Struct
+	 */
+	command = (lba48) ? WIN_READDMA_EXT : WIN_READDMA;
+	if (rq->cmd == IDE_DRIVE_TASKFILE) {
+		ide_task_t *args = rq->special;
+		command = args->tfRegister[IDE_COMMAND_OFFSET];
+	}
+
+	/* issue cmd to drive */
+	ide_execute_command(drive, command, &ide_dma_intr, (2*WAIT_CMD), NULL);
+	return auide_dma_begin(drive);
+}
+
+int auide_dma_write(ide_drive_t *drive)
+{
+	struct request *rq = HWGROUP(drive)->rq;
+	u8 lba48 = (drive->addressing == 1) ? 1 : 0;
+	task_ioreg_t command = WIN_NOP;
+
+	if (!auide_build_dmatable(drive, rq, PCI_DMA_TODEVICE))
+		/* try PIO instead of DMA */
+		return 1;
+
+	drive->waiting_for_dma = 1;
+	if (drive->media != ide_disk)
+		return 0;
+	/*
+	 * FIX ME to use only ACB ide_task_t args Struct
+	 */
+	command = (lba48) ? WIN_WRITEDMA_EXT : WIN_WRITEDMA;
+	if (rq->cmd == IDE_DRIVE_TASKFILE) {
+		ide_task_t *args = rq->special;
+		command = args->tfRegister[IDE_COMMAND_OFFSET];
+	}
+
+	/* issue cmd to drive */
+	ide_execute_command(drive, command, &ide_dma_intr, (2*WAIT_CMD), NULL);
+	return auide_dma_begin(drive);
+}
+
+static int auide_dma_count(ide_drive_t *drive)
+{
+	return 0;
+}
+
+static int auide_dma_test_irq(ide_drive_t *drive)
+{
+	if (!drive->waiting_for_dma)
+		printk(KERN_WARNING "%s: ide_dma_test_irq \
+			called while not waiting\n", drive->name);
+
+	/* If dbdma didn't execute the STOP command yet, the
+	 * active bit is still set */
+	drive->waiting_for_dma++;
+	if (drive->waiting_for_dma >= DMA_WAIT_TIMEOUT) {
+		printk(KERN_WARNING "%s: timeout waiting for ddma to complete\n", drive->name);
+		return 1;
+	}
+	udelay(10);
+	return 0;
+}
+
+static int auide_dma_host_off(ide_drive_t *drive)
+{
+	return 0;
+}
+
+static int auide_dma_host_on(ide_drive_t *drive)
+{
+	return 0;
+}
+
+static int auide_dma_lostirq(ide_drive_t *drive)
+{
+	return 0;
+}
+
+static int auide_dma_on(ide_drive_t *drive)
+{
+	/*
+		Is the drive in our DMA black list?
+	 */
+	if ( in_drive_list(drive->id, dma_black_list ) ) {
+		drive->using_dma = 0;
+		printk("%s found in dma_blacklist[]! Disabling DMA.\n", drive->id->model);
+	}
+	else 
+		drive->using_dma = 1;
+
+	return HWIF(drive)->ide_dma_host_on(drive);
+}
+
+static void auide_ddma_tx_callback(int irq, void *param, struct pt_regs *regs)
+{
+	_auide_hwif *ahwif = (_auide_hwif*)param;
+	ahwif->drive->waiting_for_dma = 0;
+	return;
+}
+
+static void auide_ddma_rx_callback(int irq, void *param, struct pt_regs *regs)
+{
+	_auide_hwif *ahwif = (_auide_hwif*)param;
+	ahwif->drive->waiting_for_dma = 0;
+	return;
+}
+#endif /* end CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX */
+
+#if !defined(CONFIG_BLK_DEV_IDE_PIO_AU1XXX)
+static int auide_ddma_init( _auide_hwif *auide )
+{
+	dbdev_tab_t dev;
+#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 */
+#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;
+
+	dev.dev_tsize = 8;
+	dev.dev_devwidth = 32;
+
+	dev.dev_physaddr = (u32)AU1XXX_ATA_PHYS_ADDR;
+	dev.dev_intlevel = 0;
+	dev.dev_intpolarity = 0;
+	auide->tx_dev_id = au1xxx_ddma_add_device( &dev );
+
+	dev.dev_flags = DEV_FLAGS_IN|DEV_FLAGS_SYNC|DEV_FLAGS_BURSTABLE;
+	auide->rx_dev_id = au1xxx_ddma_add_device( &dev );
+
+#if  defined(CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX)
+	/* Get a channel for TX */
+	auide->tx_chan = au1xxx_dbdma_chan_alloc( DSCR_CMD0_ALWAYS, auide->tx_dev_id, auide_ddma_tx_callback, (void*)auide );
+	/* Get a channel for RX */
+	auide->rx_chan = au1xxx_dbdma_chan_alloc( auide->rx_dev_id, DSCR_CMD0_ALWAYS, auide_ddma_rx_callback, (void*)auide );
+#else	/* CONFIG_BLK_DEV_IDE_PIO_DBDMA_AU1XXX */
+	/*
+	 * Note: if call back is not enabled, update ctp->cur_ptr manually 
+	 */
+	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
+	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 );
+
+#if defined(CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX)
+	hwif->dmatable_cpu = pci_alloc_consistent(hwif->pci_dev,
+						  PRD_ENTRIES * PRD_BYTES,	/* 1 Page */
+						  &hwif->dmatable_dma);
+
+	auide->sg_table = kmalloc(sizeof(struct scatterlist) * PRD_ENTRIES,
+				 GFP_KERNEL|GFP_DMA);
+	if (auide->sg_table == NULL) {
+		return -ENOMEM;
+	}
+
+	hwif->ide_dma_off 		= &__ide_dma_off;
+	hwif->ide_dma_off_quietly	= &__ide_dma_off_quietly;
+	hwif->ide_dma_good_drive 	= &__ide_dma_good_drive;
+	hwif->ide_dma_bad_drive 	= &__ide_dma_bad_drive;
+	hwif->ide_dma_verbose 		= &__ide_dma_verbose;
+	hwif->ide_dma_timeout 		= &__ide_dma_timeout;
+	hwif->ide_dma_retune 		= &__ide_dma_retune;
+
+	hwif->ide_dma_check			= &__ide_dma_check;
+	hwif->ide_dma_read		= &auide_dma_read;
+	hwif->ide_dma_write		= &auide_dma_write;
+	hwif->ide_dma_count		= &auide_dma_count;
+	hwif->ide_dma_begin		= &auide_dma_begin;
+	hwif->ide_dma_end		= &auide_dma_end;
+	hwif->ide_dma_test_irq		= &auide_dma_test_irq;
+	hwif->ide_dma_host_off		= &auide_dma_host_off;
+	hwif->ide_dma_host_on		= &auide_dma_host_on;
+	hwif->ide_dma_lostirq 		= &auide_dma_lostirq;
+	hwif->ide_dma_on		= &auide_dma_on;
+#endif
+	au1xxx_dbdma_start( auide->tx_chan );
+	au1xxx_dbdma_start( auide->rx_chan );
+	return 0;
+}
+#endif /* end DMA + Dbdma || PIO + Dbdma*/
+
+static void auide_setup_ports(hw_regs_t *hw)
+{
+	int i;
+	ide_ioreg_t *ata_regs = hw->io_ports;
+
+	for (i = 0; i < IDE_CONTROL_OFFSET; i++)
+		*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);
+}
+
+/*
+ * The following routines are all that is needed to utilize the
+ * IDE subsystems' generic PIO driver with an IDE interface on
+ * the Au1xxx static bus. The only items required are a *valid*
+ * base address and IRQ. Note that when PCI is enabled, the
+ * mips_io_offset utilized in the INB/OUTB and likes changes
+ * and can cause this to no longer work.
+ */
+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 0x170: return 15;
+		case 0x1e8: return 11;
+		case 0x168: return 10;
+		case 0x1e0: return 8;
+//		case 0x160: return 12;
+		default:
+			return 0;
+	}
+}
+
+static ide_ioreg_t auide_default_io_base(int index)
+{
+	switch (index) {
+		case 0:	return ((ide_ioreg_t)auide_base);
+		case 1:	return 0x1f0;
+		case 2:	return 0x170;
+		case 3: return 0x1e8;
+		case 4: return 0x168;
+		case 5: return 0x1e0;
+//		case 5: return 0x160;
+		default:
+			return 0;
+	}
+}
+
+static void auide_init_hwif_ports (hw_regs_t *hw, ide_ioreg_t data_port,
+			    ide_ioreg_t ctrl_port, int *irq)
+{
+	ide_ioreg_t reg = data_port;
+	int i;
+
+	/* 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);
+			return;
+		}
+		auide_setup_ports(hw);
+
+		if (irq != NULL)
+			*irq = (int)auide_irq;
+	}
+	else {
+		for (i = IDE_DATA_OFFSET; i <= IDE_STATUS_OFFSET; i++) {
+			hw->io_ports[i] = reg;
+			reg += 1;
+		}
+		if (ctrl_port) {
+			hw->io_ports[IDE_CONTROL_OFFSET] = ctrl_port;
+		} else {
+			hw->io_ports[IDE_CONTROL_OFFSET] = hw->io_ports[IDE_DATA_OFFSET] + 0x206;
+		}
+		if (irq != NULL)
+			*irq = 0;
+		hw->io_ports[IDE_IRQ_OFFSET] = 0;
+	}
+}
+
+struct ide_ops auide_ops = {
+	auide_default_irq,
+	auide_default_io_base,
+	auide_init_hwif_ports,
+};
+
+int __init auide_probe(void)
+{
+	ide_hwif_t *hwif;;
+	hw_regs_t hw;
+	int i=0;
+
+	if (!auide_base || !auide_len || (auide_irq <= 0))
+		return -ENODEV;
+
+	if (!request_mem_region (auide_base, auide_len, "Au1xxx IDE")) {
+		printk (KERN_ERR "controller already in use");
+		return -EBUSY;
+	}
+
+#if defined(CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX)
+	memset(&auide_hwif, 0, sizeof(_auide_hwif));
+	/* PCI is used to for API simplicity -- no real device present */
+	strcpy(auide_hwif.pcidev.name, "non-PCI Au1xxx IDE");
+	strcpy(auide_hwif.pcidev.slot_name, "builtin");
+	auide_hwif.pcibus.parent = NULL;
+	auide_hwif.pcibus.parent = NULL;
+	auide_hwif.pcidev.bus = &auide_hwif.pcibus;
+	auide_hwif.pcidev.irq = auide_irq;
+#endif
+	memset(&hw, 0, sizeof(hw));
+	auide_setup_ports(&hw);
+
+	/* register hwif */
+	if ((i = ide_register_hw(&hw, NULL)) < 0)
+	{
+		printk(KERN_DEBUG "%s could not register ide i/f\n", __FUNCTION__);
+		return -ENODEV;
+	}
+	hwif = &ide_hwifs[i];
+
+	hwif->chipset = ide_au1xxx;
+
+#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;
+#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  */
+	hwif->swdma_mask = 0x07;
+#else
+	hwif->mwdma_mask = 0x0;
+	hwif->swdma_mask = 0x0;
+#endif
+	hwif->noprobe = !hwif->io_ports[IDE_DATA_OFFSET];
+	hwif->drives[0].unmask = 1;
+	hwif->drives[1].unmask = 1;
+
+	/* hold should be on in all cases */
+	hwif->hold = 1;
+	hwif->mmio = 1;
+
+	/* set up local I/O function entry points */
+	hwif->INB = auide_inb;
+	hwif->INW = auide_inw;
+	hwif->INL = auide_inl;
+	hwif->INSW = auide_insw;
+	hwif->INSL = auide_insl;
+	hwif->OUTB = auide_outb;
+	hwif->OUTBSYNC = auide_outbsync;
+	hwif->OUTW = auide_outw;
+	hwif->OUTL = auide_outl;
+	hwif->OUTSW = auide_outsw;
+	hwif->OUTSL = auide_outsl;
+
+#if defined(CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX)
+	hwif->autodma = 1;
+	hwif->drives[0].autodma = 1;
+	hwif->drives[1].autodma = 1;
+	hwif->atapi_dma = 1;
+	hwif->drives[0].using_dma = 1;
+	hwif->drives[1].using_dma = 1;
+#else /* !CONFIG_BLK_DEV_IDE_DMA_DBDMA_AU1XXX */
+	hwif->autodma = 0;
+	hwif->channel = 0;
+	hwif->hold = 1;
+	hwif->select_data = 0;	/* no chipset-specific code */
+	hwif->config_data = 0;	/* no chipset-specific code */
+
+	hwif->drives[0].autodma = 0;
+	hwif->drives[0].drive_data = 0;	/* no drive data */
+	hwif->drives[0].using_dma = 0;
+	hwif->drives[0].waiting_for_dma = 0;
+	hwif->drives[0].autotune = 2;
+	/* secondary hdd not supported */
+	hwif->drives[1].autodma = 0;
+
+	hwif->drives[1].drive_data = 0;
+	hwif->drives[1].using_dma = 0;
+	hwif->drives[1].waiting_for_dma = 0;
+	hwif->drives[1].autotune = 2;
+#endif
+	hwif->drives[0].io_32bit = 0;	/* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
+	hwif->drives[1].io_32bit = 0;	/* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
+
+#if !defined(CONFIG_BLK_DEV_IDE_PIO_AU1XXX)
+	auide_hwif.hwif = hwif;
+	hwif->hwif_data = &auide_hwif;
+	auide_ddma_init(&auide_hwif);
+#endif
+	return 0;
+}
+
+static int __init auide_setup (char* options)
+{
+	char* this_opt;
+
+	if (!options || !*options)
+		return 0;
+
+	for(this_opt=strtok(options,",");this_opt;this_opt=strtok(NULL,",")) {
+		if (!strncmp(this_opt, "base:", 5)) {
+			auide_base = simple_strtoul(this_opt+5, NULL, 0);
+		} else if (!strncmp(this_opt, "len:", 4)) {
+			auide_len = simple_strtoul(this_opt+4, NULL, 0);
+		} else if (!strncmp(this_opt, "irq:", 4)) {
+			auide_irq = simple_strtoul(this_opt+4, NULL, 0);
+		}
+	}
+
+	return 0;
+}
+__setup("auide=", auide_setup);
+
+EXPORT_NO_SYMBOLS;
diff -Nur linux-2.4.30/drivers/ide/au1xxx/Makefile linux-2.4.30-linux-mips.org/drivers/ide/au1xxx/Makefile
--- linux-2.4.30/drivers/ide/au1xxx/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/ide/au1xxx/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,10 @@
+O_TARGET := au1xxx-idedriver.o
+
+obj-y		:=
+obj-m		:=
+
+obj-$(CONFIG_BLK_DEV_IDE_AU1XXX)	+= au1xxx-ide.o
+
+EXTRA_CFLAGS	:= -I../
+
+include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/drivers/ide/Config.in linux-2.4.30-linux-mips.org/drivers/ide/Config.in
--- linux-2.4.30/drivers/ide/Config.in	2004-11-17 12:54:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/ide/Config.in	2005-05-12 18:01:29.000000000 +0200
@@ -9,6 +9,7 @@
 dep_tristate 'Enhanced IDE/MFM/RLL disk/cdrom/tape/floppy support' CONFIG_BLK_DEV_IDE $CONFIG_IDE
 comment 'Please see Documentation/ide.txt for help/info on IDE drives'
 if [ "$CONFIG_BLK_DEV_IDE" != "n" ]; then
+
    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
 
@@ -87,6 +88,15 @@
 	    fi
          fi
       fi
+      if [ "$CONFIG_SOC_AU1X00" = "y" ]; then
+         dep_bool '    Builtin AMD Alchemy Au1xxx IDE support' CONFIG_BLK_DEV_IDE_AU1XXX $CONFIG_BLK_DEV_IDEPCI
+         dep_bool '      PIO mode' CONFIG_BLK_DEV_IDE_PIO_AU1XXX $CONFIG_BLK_DEV_IDE_AU1XXX
+         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
+         fi
+      fi
       if [ "$CONFIG_ALL_PPC" = "y" ]; then
 	 bool '    Builtin PowerMac IDE support' CONFIG_BLK_DEV_IDE_PMAC
 	 dep_bool '      Probe internal Kauai ATA/100 first' CONFIG_BLK_DEV_IDE_PMAC_ATA100FIRST $CONFIG_BLK_DEV_IDE_PMAC
diff -Nur linux-2.4.30/drivers/ide/ide-probe.c linux-2.4.30-linux-mips.org/drivers/ide/ide-probe.c
--- linux-2.4.30/drivers/ide/ide-probe.c	2004-04-14 15:05:29.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/ide/ide-probe.c	2005-05-12 18:01:29.000000000 +0200
@@ -833,6 +833,9 @@
 #if CONFIG_BLK_DEV_PDC4030
 	    (hwif->chipset != ide_pdc4030 || hwif->channel == 0) &&
 #endif /* CONFIG_BLK_DEV_PDC4030 */
+#if CONFIG_BLK_DEV_IDE_AU1XXX
+	    (hwif->chipset != ide_au1xxx) &&
+#endif /* CONFIG_BLK_DEV_AU1XXX */
 	    (hwif_check_regions(hwif))) {
 		u16 msgout = 0;
 		for (unit = 0; unit < MAX_DRIVES; ++unit) {
diff -Nur linux-2.4.30/drivers/ide/Makefile linux-2.4.30-linux-mips.org/drivers/ide/Makefile
--- linux-2.4.30/drivers/ide/Makefile	2004-04-14 15:05:29.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/ide/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -11,14 +11,14 @@
 
 export-objs := ide-iops.o ide-taskfile.o ide-proc.o ide.o ide-probe.o ide-probe-mini.o ide-dma.o ide-lib.o setup-pci.o ide-io.o ide-disk.o
 
-all-subdirs	:= arm legacy pci ppc raid
+all-subdirs	:= arm legacy pci ppc raid au1xxx
 mod-subdirs	:= arm legacy pci ppc raid
 
 obj-y		:=
 obj-m		:=
 ide-obj-y	:=
 
-subdir-$(CONFIG_BLK_DEV_IDE) += legacy ppc arm raid pci
+subdir-$(CONFIG_BLK_DEV_IDE) += legacy ppc arm raid pci au1xxx
 
 # First come modules that register themselves with the core
 
@@ -55,6 +55,7 @@
   obj-y		+= legacy/idedriver-legacy.o
   obj-y		+= ppc/idedriver-ppc.o
   obj-y		+= arm/idedriver-arm.o
+  obj-y		+= au1xxx/au1xxx-idedriver.o
 else
   ifeq ($(CONFIG_BLK_DEV_HD_ONLY),y)
 	subdir-$(CONFIG_BLK_DEV_HD_ONLY) += legacy
diff -Nur linux-2.4.30/drivers/ide/pci/hpt366.c linux-2.4.30-linux-mips.org/drivers/ide/pci/hpt366.c
--- linux-2.4.30/drivers/ide/pci/hpt366.c	2004-08-08 01:26:04.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/ide/pci/hpt366.c	2005-05-12 18:01:30.000000000 +0200
@@ -916,8 +916,9 @@
 		did = inb(dmabase + 0x22);
 		rid = inb(dmabase + 0x28);
 	
-		if((did == 4 && rid == 6) || (did == 5 && rid > 1))
-			is_372n = 1;
+		if((did == 4 && rid == 6) || (did == 5 && rid > 1) ||
+				(did == 7 && rid == 2)) 
+			is_372n = 1; /* force 372n timing for 371n */
 	}
 		
 	/*
diff -Nur linux-2.4.30/drivers/Makefile linux-2.4.30-linux-mips.org/drivers/Makefile
--- linux-2.4.30/drivers/Makefile	2003-11-28 19:26:19.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -48,5 +48,6 @@
 subdir-$(CONFIG_ACPI_BOOT)	+= acpi
 
 subdir-$(CONFIG_BLUEZ)		+= bluetooth
+subdir-$(CONFIG_MMC)		+= mmc
 
 include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/drivers/media/video/indycam.c linux-2.4.30-linux-mips.org/drivers/media/video/indycam.c
--- linux-2.4.30/drivers/media/video/indycam.c	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/media/video/indycam.c	2005-05-12 17:57:24.000000000 +0200
@@ -50,13 +50,14 @@
 		0x80,			/* INDYCAM_GAMMA */
 	};
 
-	int err = 0;
 	struct indycam *camera;
 	struct i2c_client *client;
+	int err = 0;
 
 	client = kmalloc(sizeof(*client), GFP_KERNEL);
-	if (!client) 
+	if (!client)
 		return -ENOMEM;
+
 	camera = kmalloc(sizeof(*camera), GFP_KERNEL);
 	if (!camera) {
 		err = -ENOMEM;
@@ -67,7 +68,7 @@
 	client->adapter = adap;
 	client->addr = addr;
 	client->driver = &i2c_driver_indycam;
-	strcpy(client->name, "IndyCam client");			
+	strcpy(client->name, "IndyCam client");
 	camera->client = client;
 
 	err = i2c_attach_client(client);
@@ -75,18 +76,18 @@
 		goto out_free_camera;
 
 	camera->version = i2c_smbus_read_byte_data(client, INDYCAM_VERSION);
-	if (camera->version != CAMERA_VERSION_INDY &&
-	    camera->version != CAMERA_VERSION_MOOSE) {
+	if ((camera->version != CAMERA_VERSION_INDY) &&
+	    (camera->version != CAMERA_VERSION_MOOSE)) {
 		err = -ENODEV;
 		goto out_detach_client;
 	}
-	printk(KERN_INFO "Indycam v%d.%d detected.\n",
+	printk(KERN_INFO "IndyCam v%d.%d detected.\n",
 	       INDYCAM_VERSION_MAJOR(camera->version),
 	       INDYCAM_VERSION_MINOR(camera->version));
 
 	err = i2c_master_send(client, initseq, sizeof(initseq));
 	if (err)
-		printk(KERN_INFO "IndyCam initalization failed\n");
+		printk(KERN_ERR "IndyCam initalization failed.\n");
 
 	MOD_INC_USE_COUNT;
 	return 0;
diff -Nur linux-2.4.30/drivers/media/video/vino.c linux-2.4.30-linux-mips.org/drivers/media/video/vino.c
--- linux-2.4.30/drivers/media/video/vino.c	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/media/video/vino.c	2005-05-12 17:57:25.000000000 +0200
@@ -5,6 +5,8 @@
  * License version 2 as published by the Free Software Foundation.
  *
  * Copyright (C) 2003 Ladislav Michl <ladis@linux-mips.org>
+ * Copyright (C) 2004 Mikael Nousiainen <tmnousia@cc.hut.fi>
+ * 
  */
 
 #include <linux/module.h>
@@ -37,13 +39,23 @@
 #define DEBUG(x...)
 #endif
 
+/* Channels (who could have guessed) */
+#define VINO_CHAN_NONE		0
+#define VINO_CHAN_A		1
+#define VINO_CHAN_B		2
+
 /* VINO video size */
 #define VINO_PAL_WIDTH		768
 #define VINO_PAL_HEIGHT		576
 #define VINO_NTSC_WIDTH		646
 #define VINO_NTSC_HEIGHT	486
 
-/* set this to some sensible values. note: VINO_MIN_WIDTH has to be 8*x */
+/* Minimum value for Y-clipping (for smaller values the images
+ * will be corrupted) */
+#define VINO_MIN_Y_CLIPPING	2
+ 
+/* Set these to some sensible values.
+ * Note: the picture width has to be divisible by 8 */
 #define VINO_MIN_WIDTH		32
 #define VINO_MIN_HEIGHT		32
 
@@ -64,9 +76,7 @@
 
 struct vino_device {
 	struct video_device vdev;
-#define VINO_CHAN_A	1
-#define VINO_CHAN_B	2
-	int chan;
+	int chan;	/* VINO_CHAN_NONE, VINO_CHAN_A or VINO_CHAN_B */
 	int alpha;
 	/* clipping... */
 	unsigned int left, right, top, bottom;
@@ -106,7 +116,7 @@
 
 struct vino_client {
 	struct i2c_client *driver;
-	int owner;
+	int owner;	/* VINO_CHAN_NONE, VINO_CHAN_A or VINO_CHAN_B */
 };
 
 struct vino_video {
@@ -362,6 +372,7 @@
 static int dma_setup(struct vino_device *v)
 {
 	u32 ctrl, intr;
+	int ofs;
 	struct sgi_vino_channel *ch;
 
 	ch = (v->chan == VINO_CHAN_A) ? &vino->a : &vino->b;
@@ -377,14 +388,24 @@
 	ch->line_size = v->line_size - 8;
 	/* set the alpha register */
 	ch->alpha = v->alpha;
-	/* set cliping registers */
-	ch->clip_start = VINO_CLIP_ODD(v->top) | VINO_CLIP_EVEN(v->top+1) |
+	/* Set the clipping registers, this is the constant source of fun :)
+	 * Y clipping start has to be >= 2 and end has to be start + height/2
+	 * The values of top and bottom are even so dividing is not a problem
+	 *
+	 * The docs say that clipping values for the even field should be
+	 * odd_end + something_to_skip_vertical_blanking + some_lines and
+	 * even_start + height/2, though the image is good this way also
+	 *
+	 * TODO: for analog sources (SAA7191), the clipping values are a bit
+	 * different and that case isn't yet handled
+	 */
+	ofs = VINO_MIN_Y_CLIPPING;	/* Should depend on input source */
+	ch->clip_start = VINO_CLIP_ODD(ofs + v->top / 2) |
+			 VINO_CLIP_EVEN(ofs + v->top / 2 + 1) |
 			 VINO_CLIP_X(v->left);
-	ch->clip_end = VINO_CLIP_ODD(v->bottom) | VINO_CLIP_EVEN(v->bottom+1) |
+	ch->clip_end = VINO_CLIP_ODD(ofs + v->bottom / 2 - 1) |
+		       VINO_CLIP_EVEN(ofs + v->bottom / 2) |
 		       VINO_CLIP_X(v->right);
-	/* FIXME: end-of-field bug workaround
-		       VINO_CLIP_X(VINO_PAL_WIDTH);
-	 */
 	/* init the frame rate and norm (full frame rate only for now...) */
 	ch->frame_rate = VINO_FRAMERT_RT(0x1fff) |
 			 (get_capture_norm(v) == VIDEO_MODE_PAL ?
@@ -510,6 +531,7 @@
 static void vino_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
 	u32 intr, ctrl;
+	int a_eof, b_eof;
 
 	spin_lock(&Vino->vino_lock);
 	ctrl = vino->control;
@@ -525,12 +547,14 @@
 		vino->control = ctrl;
 		clear_eod(&Vino->chB);
 	}
+	a_eof = intr & VINO_INTSTAT_A_EOF;
+	b_eof = intr & VINO_INTSTAT_B_EOF;
 	vino->intr_status = ~intr;
 	spin_unlock(&Vino->vino_lock);
-	/* FIXME: For now we are assuming that interrupt means that frame is
-	 * done. That's not true, but we can live with such brokeness for
-	 * a while ;-) */
-	field_done(&Vino->chA);
+	if (a_eof)
+		field_done(&Vino->chA);
+	if (b_eof)
+		field_done(&Vino->chB);
 }
 
 static int vino_grab(struct vino_device *v, int frame)
diff -Nur linux-2.4.30/drivers/mmc/Config.in linux-2.4.30-linux-mips.org/drivers/mmc/Config.in
--- linux-2.4.30/drivers/mmc/Config.in	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mmc/Config.in	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,19 @@
+#
+# MMC subsystem configuration
+#
+mainmenu_option next_comment
+comment 'MMC/SD Card support'
+
+tristate 'MMC support' CONFIG_MMC
+if [ "$CONFIG_MMC" = "y" -o "$CONFIG_MMC" = "m" ]; then
+  bool '  MMC debugging' CONFIG_MMC_DEBUG
+  if [ "$CONFIG_MMC_DEBUG" = "y" ]; then
+    int '  MMC debugging verbosity (0=quiet, 3=noisy)' CONFIG_MMC_DEBUG_VERBOSE 0
+  fi
+fi
+
+  if [ "$CONFIG_SOC_AU1100" = "y" -o "$CONFIG_SOC_AU1200" = "y" ]; then
+    dep_tristate 'Au1100/Au1200 MMC/SD Card support' CONFIG_MMC_AU1100 $CONFIG_MMC
+  fi
+
+endmenu
diff -Nur linux-2.4.30/drivers/mmc/Makefile linux-2.4.30-linux-mips.org/drivers/mmc/Makefile
--- linux-2.4.30/drivers/mmc/Makefile	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mmc/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,21 @@
+#
+# Makefile for the kernel mmc device drivers.
+#
+
+O_TARGET := mmc.o
+
+export-objs := mmc_core.o mmc_media.o
+
+obj-$(CONFIG_MMC) += mmc_base.o
+obj-$(CONFIG_MMC_AU1100) += mmc_au1x.o
+
+# Declare multi-part drivers.
+list-multi := mmc_base.o
+mmc_base-objs := mmc_protocol.o mmc_core.o mmc_media.o
+
+include $(TOPDIR)/Rules.make
+
+# Link rules for multi-part drivers.
+
+mmc_base.o: $(mmc_base-objs)
+	$(LD) -r -o $@ $(mmc_base-objs)
diff -Nur linux-2.4.30/drivers/mmc/mmc_au1x.c linux-2.4.30-linux-mips.org/drivers/mmc/mmc_au1x.c
--- linux-2.4.30/drivers/mmc/mmc_au1x.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mmc/mmc_au1x.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,1730 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *	MMC framework driver for MMC/SD controllers on the
+ *	Alchemy Au1100 mips processor.  Although the Au1100
+ *	controllers are "Secure Digital" controllers, this
+ *	module only provides a (non-secure) MultiMediaCard
+ *	interface conforming to the MMC framework as developed
+ *	by Andrew Christian.
+ *
+ *	The original MMC framework patch is located at
+ *	//handhelds.org/download/linux/mmc/patch-2.4.19-mmc
+ *
+ *	Thanks to early users of the card whose input helped
+ *	clarify things, such as status bit interpretations in
+ *	pio mode.
+ *
+ *	Portions below, including but not limited to support of
+ *	dynamic media insertion/removal, were derived from a
+ *	Metrowerks driver, which itself was based on code for
+ *	the iPAQ H3800.  Required copyright notices have been
+ *	included below from those sources.
+ *
+ *
+ * Copyright (c) 2003-2004 Embedded Edge, LLC.
+ * Author: Embedded Edge, LLC.
+ * Contact: dan@embeddededge.com
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ *  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.
+ *
+ */
+
+/*
+ * Low-level MMC functions for the AMD db1100 EVB.
+ *
+ *  Portions Copyright (C) 2002 Embedix, Inc
+ *  Originally based on code for the iPAQ H3800, see below.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307  USA
+ */
+
+/*
+ * Low-level MMC functions for the iPAQ H3800
+ *
+ * Copyright 2002 Hewlett-Packard Company
+ *
+ * Use consistent with the GNU GPL is permitted,
+ * provided that this copyright notice is
+ * preserved in its entirety in all copies and derived works.
+ *
+ * HEWLETT-PACKARD COMPANY MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+ * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+ * FITNESS FOR ANY PARTICULAR PURPOSE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Author:  Andrew Christian
+ *          6 May 2002
+ */
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/mmc/mmc_ll.h>
+#include <asm/irq.h>
+#include <asm/au1000.h>
+#include <asm/au1xxx_dbdma.h> 
+#include <asm/au1100_mmc.h>
+
+#if defined(CONFIG_SOC_AU1100)
+#include <asm/au1000_dma.h>
+#define AU1X_MMC_INT AU1100_SD_INT
+
+#elif defined(CONFIG_SOC_AU1200)
+#include <asm/au1xxx_dbdma.h> 
+#define NUM_DBDMA_DESCRIPTORS         2    /*  Number of Descriptor for DDMA */
+#define AU1X_MMC_INT AU1200_SD_INT
+
+#else
+#error "mmc_au1x.c: error unsupported SOC"
+#endif
+
+/*
+ *  Boot options:
+ */
+int au1x_mmc_use_pio =0 ;	/* Force PIO - default is use DMA channels */
+int au1x_mmc_dbl_buf = 0;	/* For DMA, double buffer - use for unaligned requests */
+int au1x_mmc_ctlr = 0;		/* Which SD controller(s) to enable -- SD0 is default */
+
+/*
+ *  Tunable quantum of PIO transfer - (11 bits available).
+ */
+#define AU1X_MMC_CHUNK_SIZE 128
+
+/*
+ *  Timeouts.
+ */
+#define MMC_IRQ_TIMEOUT  (3 * HZ)
+#define MMC_DETECT_TIMEOUT  (HZ/2)
+
+/*
+ *  Controller state flags.
+ */
+#define AU_MMC_STATE_MODULE_INITTED	(1<<0)	/* module-wide init - used in ctlr #0 flags only */
+#define AU_MMC_STATE_CTLR_ACTIVE	(1<<1)
+//#define AU_MMC_STATE_x		(1<<2)
+//#define AU_MMC_STATE_x		(1<<3)
+#define AU_MMC_STATE_USE_DMA		(1<<4)
+#define AU_MMC_STATE_DOUBLE_BUFFER_DMA	(1<<5)
+#define AU_MMC_STATE_RECV_DMA_IN_USE	(1<<6) 
+#define AU_MMC_STATE_XMIT_ACTIVE	(1<<7)
+
+ 
+   
+
+
+/*
+ *  Per card/controller description.
+ */
+static struct au1x_mmc_card
+{
+	struct timer_list	 m_detect_timer;
+	struct timer_list	 m_irq_timer;	/* Panic timer - make sure we get
+	                                         * a response */
+	int			 m_ctlr;	/* controller number */
+	u32			 m_base;	/* base of register block */
+	u32			 m_clock;	/* card clock rate */
+	int			 m_flags;	/* see above flag defs */
+	int			 m_isempty;	/* to avoid needing lock for flags */
+	struct mmc_request	*m_req;		/* upper level request */
+	int			 m_cnt;		/* bytes processed so far */
+	int			 m_tx_dma_devid;/* DMA channel identifiers */
+	int			 m_rx_dma_devid;
+	const char		*m_tx_dma_str;
+	const char		*m_rx_dma_str;
+	int			 m_xmit_dma;	/* transmit dma channel # */
+	int			 m_recv_dma;	/* receive dma channel # */
+	void			*m_tx_rawbuf;	/* transmit buffer */
+	void			*m_rx_rawbuf;	/* receive buffer */
+	dma_addr_t		m_tx_dmaaddr;	/*  virt_to_phys(buffer) */
+	dma_addr_t		m_rx_dmaaddr;
+ 	int			 m_dma_memsize;	/* byte size of each rawbuf */
+#ifdef CONFIG_SOC_AU1200
+	chan_tab_t	**ctp_tx;  /*Channel Tab Pointer to Transmit*/
+	chan_tab_t	**ctp_rx;  /*Channel Tab Pointer to recieve*/
+#endif
+} au1x_mmc_card_table[] =
+{
+	{
+		m_ctlr: 0,
+		m_base:	SD0_BASE,
+		m_clock: MMC_CLOCK_SLOW, /* for starters - changed later */
+		m_flags: (AU_MMC_STATE_USE_DMA
+			/* | AU_MMC_STATE_DOUBLE_BUFFER_DMA */),
+		m_isempty: 1,
+#ifdef CONFIG_SOC_AU1100
+		m_tx_dma_devid: DMA_NUM_DEV + DMA_ID_SD0_TX, // bank #2
+		m_rx_dma_devid: DMA_NUM_DEV + DMA_ID_SD0_RX, // bank #2
+#endif
+#ifdef CONFIG_SOC_AU1200
+		m_tx_dma_devid: DSCR_CMD0_SDMS_TX0, /* defined in dbdma.h*/
+		m_rx_dma_devid: DSCR_CMD0_SDMS_RX0, /* defined in dbdma.h */
+#endif
+                m_tx_dma_str: "MMC/SD #0 XMIT",
+		m_rx_dma_str: "MMC/SD #0 RECV",
+		m_xmit_dma: -1,
+		m_recv_dma: -1,
+	},
+
+#if (NUM_MMC_CONTROLLERS > 1)
+	{
+		m_ctlr: 1,
+		m_base:	SD1_BASE,
+		m_clock: MMC_CLOCK_SLOW, /* for starters - changed later */
+		m_flags: (AU_MMC_STATE_USE_DMA
+			/* | AU_MMC_STATE_DOUBLE_BUFFER_DMA */),
+		m_isempty: 1,
+
+#ifdef CONFIG_SOC_AU1100
+		m_tx_dma_devid: DMA_NUM_DEV + DMA_ID_SD1_TX, // bank #2
+		m_rx_dma_devid: DMA_NUM_DEV + DMA_ID_SD1_RX, // bank #2
+#endif
+#ifdef CONFIG_SOC_AU1200
+		m_tx_dma_devid:  DSCR_CMD0_SDMS_TX1, /* defined in dbdma.h*/
+		m_rx_dma_devid:  DSCR_CMD0_SDMS_RX1, /* defined in dbdma.h*/ 
+#endif
+	        m_tx_dma_str: "MMC/SD #1 XMIT",
+		m_rx_dma_str: "MMC/SD #1 RECV",
+		m_xmit_dma: -1,
+		m_recv_dma: -1,
+	},
+#endif
+};
+
+/* Ptrs for descriptor access */
+static struct au1x_mmc_card *au1x_mmc_cards[NUM_MMC_CONTROLLERS];
+
+/*
+ * Macro to extract which controller to service incoming request.
+ * Currently, outer framework doesn't handle multiple controllers,
+ * so this macro really is for future expansion.
+ */
+static int mmc_active_ctlr = -1;
+#define CTLR_SELECT(_i_)	(mmc_active_ctlr)
+#define SLOT_SELECT(_i_)	(0)
+
+
+/*
+ *  Access macros.
+ */
+#define CARD_STATUS(c)	((c)->m_base + SD_STATUS)
+#define CARD_CONFIG(c)	((c)->m_base + SD_CONFIG)
+#define CARD_ENABLE(c)	((c)->m_base + SD_ENABLE)
+#define CARD_TXPORT(c)	((c)->m_base + SD_TXPORT)
+#define CARD_RXPORT(c)	((c)->m_base + SD_RXPORT)
+#define CARD_CMDARG(c)	((c)->m_base + SD_CMDARG)
+#define CARD_BLKSIZE(c)	((c)->m_base + SD_BLKSIZE)
+#define CARD_CMD(c)	((c)->m_base + SD_CMD)
+#define CARD_CONFIG2(c)	((c)->m_base + SD_CONFIG2)
+#define CARD_TIMEOUT(c)	((c)->m_base + SD_TIMEOUT)
+#define CARD_DEBUG(c)	((c)->m_base + SD_DEBUG)
+
+
+
+/*
+ *  OCR value to use - thanks, Metrowerks
+ */
+#define MMC_VDD_RANGES	( \
+		MMC_VDD_27_28	| \
+		MMC_VDD_28_29	| \
+		MMC_VDD_29_30	| \
+		MMC_VDD_30_31	| \
+		MMC_VDD_31_32	| \
+		MMC_VDD_32_33	| \
+		MMC_VDD_33_34	| \
+		MMC_VDD_34_35	| \
+		MMC_VDD_35_36)
+
+/*
+ *  Internal forward declarations.
+ */
+static	int	mmc_reset_controller(struct au1x_mmc_card *card, u32 rate);
+static	int	au1x_mmc_check_insert(int mmc);
+
+/*
+ *  Forward declarations for MMC framework interface.
+ */
+static	int	au1x_mmc_init(void);
+static	void	au1x_mmc_cleanup(void);
+static	int	au1x_mmc_is_empty(int slot);
+static	int	au1x_mmc_set_clock(int slot, u32 rate);
+static	void	au1x_mmc_send_cmd(struct mmc_request *);
+
+static struct mmc_slot_driver au1x_mmc_driver =
+{
+	owner:		THIS_MODULE,
+	name:		"Au1x MMC Controller",
+	ocr:		MMC_VDD_RANGES,
+	flags:		MMC_SDFLAG_MMC_MODE,
+
+	init:		au1x_mmc_init,
+	cleanup:	au1x_mmc_cleanup,
+	is_empty:	au1x_mmc_is_empty,
+	set_clock:	au1x_mmc_set_clock,
+	send_cmd:	au1x_mmc_send_cmd
+};
+
+
+
+static void au1x_mmc_send_data_pio(struct au1x_mmc_card *card)
+{
+	struct mmc_request *req = card->m_req;
+	int		    cnt = card->m_cnt;
+	int		    i = AU1X_MMC_CHUNK_SIZE;
+
+	while (au_readl(CARD_STATUS(card)) & (SD_STATUS_TA | SD_STATUS_TH)) {
+
+		if (cnt == req->block_len) {
+			au_writel(au_readl(CARD_CONFIG(card)) & ~SD_CONFIG_TH,
+				CARD_CONFIG(card));
+			au_sync();
+			break;
+		}
+		au_writel(req->buffer[cnt++], CARD_TXPORT(card));
+		au_sync();
+		if (! i--) {
+			break;
+		}
+	}
+
+	DEBUG(3, ": Sent %d bytes, total so far %d\n",
+		cnt - card->m_cnt, cnt);
+
+	card->m_cnt = cnt;
+}
+
+
+static void au1x_mmc_receive_data_pio(struct au1x_mmc_card *card)
+{
+	struct mmc_request *req = card->m_req;
+	int		    cnt = card->m_cnt;
+	int		    i = AU1X_MMC_CHUNK_SIZE;
+
+	while (au_readl(CARD_STATUS(card)) & SD_STATUS_NE) {
+		if (cnt == req->block_len) {
+			break;
+		}
+
+		req->buffer[cnt++] = au_readl(CARD_RXPORT(card));
+		if (! i--) {
+			break;
+		}
+	}
+
+	DEBUG(3, ": Rcvd %d bytes, total so far %d, total requested %d\n",
+		cnt - card->m_cnt, cnt, req->block_len);
+
+	card->m_cnt = cnt;
+}
+
+
+static void au1x_mmc_send_data_dma(struct au1x_mmc_card *card)
+{ 
+#ifdef CONFIG_SOC_AU1200
+	struct mmc_request *req = card->m_req;
+	chan_tab_t *chanpt;
+	volatile au1x_dma_chan_t *cp;  
+
+	chanpt= *((chan_tab_t**)(card->ctp_tx)); 
+	cp =chanpt->chan_ptr;        
+
+	if (card->m_tx_rawbuf) {
+		memcpy(card->m_tx_rawbuf, req->buffer, req->block_len);
+	} else {
+		card->m_tx_dmaaddr = virt_to_phys(req->buffer);
+	}
+
+	/*Start the DBDMA Channel*/           
+	au1xxx_dbdma_start((u32)(card->ctp_tx));
+
+       if( ! au1xxx_dbdma_put_source((u32)(card->ctp_tx),card->m_tx_dmaaddr,req->block_len ))
+				      
+                                      {				 
+					 printk("DBDMA Error..Putting Descriptor on Buffer Ring Channel A in Single Channel \n");
+					 
+				       }
+
+	card->m_cnt = req->block_len;
+	au_sync();
+
+	/* to check if channel is halted*/
+	if  (cp->ddma_stat & DDMA_STAT_H )
+		au1xxx_dbdma_start((u32)(card->ctp_tx));
+#endif
+#ifdef CONFIG_SOC_AU1100
+	struct mmc_request *req = card->m_req;
+	int		    chan = card->m_xmit_dma;
+
+	if (card->m_tx_rawbuf) {
+		memcpy(card->m_tx_rawbuf, req->buffer, req->block_len);
+	} else {
+		card->m_tx_dmaaddr = virt_to_phys(req->buffer);
+	}
+
+	if (get_dma_active_buffer(chan) == 1) {
+		clear_dma_done1(chan);
+		set_dma_count1(chan, req->block_len);
+		set_dma_count0(chan, 0);
+		set_dma_addr1(chan, card->m_tx_dmaaddr);
+		enable_dma_buffer1(chan);
+	} else {
+		clear_dma_done0(chan);
+		set_dma_count0(chan, req->block_len);
+		set_dma_count1(chan, 0);
+		set_dma_addr0(chan, card->m_tx_dmaaddr);
+		enable_dma_buffer0(chan);
+	}
+
+	card->m_cnt = req->block_len;
+	au_sync();
+
+	if (dma_halted(chan))
+		start_dma(chan);
+#endif
+}
+
+
+static void au1x_mmc_receive_data_dma(struct au1x_mmc_card *card)
+{
+#ifdef CONFIG_SOC_AU1200
+        struct mmc_request *req = card->m_req;
+        chan_tab_t        *chanpt;
+        volatile au1x_dma_chan_t *cp;  
+        
+         chanpt= *((chan_tab_t**)(card->ctp_tx)); 
+          cp =chanpt->chan_ptr;            
+
+
+     	if (card->m_flags & AU_MMC_STATE_RECV_DMA_IN_USE) {
+		/* spurious */
+		return;
+	}
+
+	card->m_flags |= AU_MMC_STATE_RECV_DMA_IN_USE;
+
+	/*
+	 *  Data is ready for reading.  Disable this interrupt
+	 *  as the DMA engine now takes over.  The transfer done
+	 *  interrupt will serve to let us know when data has
+	 *  been moved.
+	 */
+
+
+	au_writel(au_readl(CARD_CONFIG(card)) & ~SD_CONFIG_NE,
+		CARD_CONFIG(card));
+	au_sync();
+
+	if ( ! (card->m_rx_rawbuf) ) {
+		card->m_rx_dmaaddr = virt_to_phys(req->buffer);
+	 }
+
+            au1xxx_dbdma_start((u32)(card->ctp_rx));
+
+	
+   
+	    if( ! au1xxx_dbdma_put_dest((u32)(card->ctp_rx),card->m_rx_dmaaddr,req->block_len))
+				      
+                                      {				 
+					 printk("DBDMA PUT DESC Error..Putting Descriptor on Buffer Ring Channel A in Single Channel \n");
+	                              }
+				     
+           
+             card->m_cnt = req->block_len;
+             au_sync();
+
+
+	if  (cp->ddma_stat & DDMA_STAT_H )
+	  au1xxx_dbdma_start((u32)(card->ctp_rx));
+#endif
+#ifdef CONFIG_SOC_AU1100
+	struct mmc_request *req = card->m_req;
+	int		    chan = card->m_recv_dma;
+
+	if (card->m_flags & AU_MMC_STATE_RECV_DMA_IN_USE) {
+		/* spurious */
+		return;
+	}
+
+	card->m_flags |= AU_MMC_STATE_RECV_DMA_IN_USE;
+
+	/*
+	 *  Data is ready for reading.  Disable this interrupt
+	 *  as the DMA engine now takes over.  The transfer done
+	 *  interrupt will serve to let us know when data has
+	 *  been moved.
+	 */
+	au_writel(au_readl(CARD_CONFIG(card)) & ~SD_CONFIG_NE,
+		CARD_CONFIG(card));
+	au_sync();
+
+	if ( ! (card->m_rx_rawbuf) ) {
+		card->m_rx_dmaaddr = virt_to_phys(req->buffer);
+	}
+
+	if (get_dma_active_buffer(chan) == 1) {
+		clear_dma_done1(chan);
+		set_dma_count1(chan, req->block_len);
+		set_dma_count0(chan, 0);
+		set_dma_addr1(chan, card->m_rx_dmaaddr);
+		enable_dma_buffer1(chan);
+	} else {
+		clear_dma_done0(chan);
+		set_dma_count0(chan, req->block_len);
+		set_dma_count1(chan, 0);
+		set_dma_addr0(chan, card->m_rx_dmaaddr);
+		enable_dma_buffer0(chan);
+	}
+
+	card->m_cnt = req->block_len;
+	au_sync();
+
+	if (dma_halted(chan))
+		start_dma(chan);
+#endif
+}
+
+
+static void au1x_mmc_dma_complete(struct au1x_mmc_card *card)
+{
+#ifdef CONFIG_SOC_AU1200
+	int chan = 0; //To get rid of complilation error
+	volatile au1x_dma_chan_t *cp; 
+	chan_tab_t *chanpt;
+
+	if (card->m_flags & AU_MMC_STATE_XMIT_ACTIVE)
+		{
+	
+             	  chan = card->m_xmit_dma;
+		 chanpt= *((chan_tab_t**)(card->ctp_tx));
+                 cp = chanpt->chan_ptr;
+		}   
+            else
+	      {
+		chan= card->m_recv_dma;
+		chanpt= *((chan_tab_t**)(card->ctp_rx));
+	        cp= chanpt->chan_ptr;
+	      }
+
+	/*
+	 *  On receive, MMC may signal xfer is done before dma
+	 *  engine has finished reading the fifo and actually
+	 *  transfering data to memory.  A small window perhaps,
+	 *  but we wait until the engine says it's through too.
+	 */
+	while((cp->ddma_stat & DDMA_STAT_V)==1){
+	
+	  DEBUG(1, ": waiting for MMC DMA completion.\n");
+	  au_sync();
+	}
+            
+	if (card->m_flags & AU_MMC_STATE_RECV_DMA_IN_USE) {
+		card->m_flags &= ~AU_MMC_STATE_RECV_DMA_IN_USE;
+
+		if (card->m_rx_rawbuf) {
+			memcpy( card->m_req->buffer,
+				card->m_rx_rawbuf,
+				card->m_cnt);
+		}
+	}
+#endif
+#ifdef CONFIG_SOC_AU1100
+	unsigned int buf_done;
+	int chan;
+
+	if (card->m_flags & AU_MMC_STATE_XMIT_ACTIVE)
+		chan = card->m_xmit_dma;
+	else
+		chan = card->m_recv_dma;
+
+	/*
+	 *  On receive, MMC may signal xfer is done before dma
+	 *  engine has finished reading the fifo and actually
+	 *  transfering data to memory.  A small window perhaps,
+	 *  but we wait until the engine says it's through too.
+	 */
+	while ((buf_done = get_dma_buffer_done(chan)) == 0) {
+		DEBUG(1, ": waiting for MMC DMA completion.\n");
+
+		au_sync();
+	}
+
+	if (buf_done == (DMA_D0 | DMA_D1)) {
+		DEBUG(0, ": MMC DMA error - multiple outstanding buffers\n");
+
+		card->m_cnt = 0;	// signal error
+
+		init_dma(chan);		// reset dma channel
+		au_sync();
+	} else if (buf_done == DMA_D1) {
+		clear_dma_done1(chan);
+	} else {
+		clear_dma_done0(chan);
+	}
+
+	if (card->m_flags & AU_MMC_STATE_RECV_DMA_IN_USE) {
+		card->m_flags &= ~AU_MMC_STATE_RECV_DMA_IN_USE;
+
+		if (card->m_rx_rawbuf) {
+			memcpy( card->m_req->buffer,
+				card->m_rx_rawbuf,
+				card->m_cnt);
+		}
+	}
+#endif
+}
+
+
+static void au1x_mmc_cmd_ack(struct au1x_mmc_card *card)
+{
+	struct mmc_request *req = card->m_req;
+	int base = card->m_base;
+
+	switch (req->rtype) {
+
+	case RESPONSE_NONE:
+		break;
+
+	case RESPONSE_R3:
+	case RESPONSE_R1:
+	case RESPONSE_R1B:
+	  {
+		u8 header = au_readl(base + SD_RESP1);
+		u32 ocr = au_readl(base + SD_RESP0);
+
+		req->response[0] = header;
+		req->response[1] = (ocr >> 24) & 0xff;
+		req->response[2] = (ocr >> 16) & 0xff;
+		req->response[3] = (ocr >>  8) & 0xff;
+		req->response[4] = (ocr) & 0xff;
+		break;
+	  }
+
+	case RESPONSE_R2_CID:
+	case RESPONSE_R2_CSD:
+	  {
+		u32 reg[4];
+		int i;
+
+		reg[0] = au_readl(base + SD_RESP3);
+		reg[1] = au_readl(base + SD_RESP2);
+		reg[2] = au_readl(base + SD_RESP1);
+		reg[3] = au_readl(base + SD_RESP0);
+
+		for (i = 0; i < 4; i++) {
+			if (i) {
+				req->response[i*4] = (reg[i] >> 24) & 0xff;
+			}
+			req->response[i*4 + 1] = (reg[i] >> 16) & 0xff;
+			req->response[i*4 + 2] = (reg[i] >> 8) & 0xff;
+			req->response[i*4 + 3] = (reg[i]) & 0xff;
+		}
+		req->response[0] = 0x3f;
+		break;
+	  }
+
+	case RESPONSE_R4:
+	case RESPONSE_R5:
+	default:
+		DEBUG(0, ": Unsupported MMC response type %d\n", req->rtype);
+		req->result = MMC_ERROR_DRIVER_FAILURE;
+		break;
+	}
+
+	req->result = MMC_NO_ERROR;
+	if (!req->nob) {
+		del_timer_sync(&card->m_irq_timer);
+		mmc_cmd_complete(req);
+	} else {
+		/*
+		 *  Reset timer for data phase of command.
+		 */
+		mod_timer(&card->m_irq_timer, jiffies + MMC_IRQ_TIMEOUT);
+	}
+}
+
+
+#define SD_STATUS_ERRORS ( \
+		SD_STATUS_RAT	| \
+		SD_STATUS_DT	| \
+		SD_STATUS_SC	| \
+		SD_STATUS_RC	| \
+		SD_STATUS_WC	| \
+		SD_STATUS_TU	| \
+		SD_STATUS_RU	| \
+		SD_STATUS_TO	| \
+		SD_STATUS_RO )
+
+static void au1x_mmc_irq(int irq, void *dev_id, struct pt_regs *regs)
+{
+	int mmc;
+
+       
+
+	disable_irq(AU1X_MMC_INT);
+
+	/*
+	 *  Traverse all irq sources.
+	 */
+	for (mmc = 0; mmc < NUM_MMC_CONTROLLERS; ++mmc) {
+
+		u32 status, debug;
+		int tmo;
+		struct au1x_mmc_card *card = au1x_mmc_cards[mmc];
+
+		int tx = card->m_flags & AU_MMC_STATE_XMIT_ACTIVE;
+		int dma = card->m_flags & AU_MMC_STATE_USE_DMA;
+
+                
+                
+
+		/*
+		 *  Punt if this controller has been designated inactive.
+		 */
+		if ( ! (card->m_flags & AU_MMC_STATE_CTLR_ACTIVE) )
+			continue;
+
+		status = au_readl(CARD_STATUS(card));
+		debug = au_readl(CARD_DEBUG(card));
+
+		/* matching dev_id means timeout */
+		tmo = (dev_id == ((void *) card));
+
+		
+
+		if ( (!(status & SD_STATUS_I)) && !tmo ){
+		   continue;
+		}
+
+		DEBUG((tmo?0:2), ": %s" "card %d status 0x%08X\n", (tmo?"timeout ":""), mmc, status);
+
+		/*
+		 *  Check errors.
+		 */
+		if (status & (SD_STATUS_ERRORS)) {
+			if (status & (SD_STATUS_RAT
+				      | SD_STATUS_DT)) {
+				card->m_req->result = MMC_ERROR_TIMEOUT;
+				goto terminate_int;
+			}
+
+			if (status & (SD_STATUS_SC
+				      | SD_STATUS_RC
+				      | SD_STATUS_WC)) {
+				card->m_req->result = MMC_ERROR_CRC;
+				goto terminate_int;
+			}
+
+			if (status & (SD_STATUS_TU
+				      | SD_STATUS_RU)) {
+				card->m_req->result = MMC_ERROR_UNDERRUN;
+				goto terminate_int;
+			}
+
+			if (status & (SD_STATUS_TO
+				      | SD_STATUS_RO)) {
+				card->m_req->result = MMC_ERROR_OVERRUN;
+				goto terminate_int;
+			}
+		} else if (tmo) {
+			/*
+			 *  No discernible error, but we have a timeout.
+			 */
+			card->m_req->result = MMC_ERROR_GENERAL;
+			goto terminate_int;
+		}
+
+		/*
+		 *  Command response.
+		 */
+		if (status & SD_STATUS_CR) {
+			au1x_mmc_cmd_ack(card);
+
+			if (tx) {
+				if (dma) {
+					au1x_mmc_send_data_dma(card);
+				} else {
+					au_writel(au_readl(CARD_CONFIG(card))
+							| SD_CONFIG_TH,
+						CARD_CONFIG(card));
+					au_sync();
+					if (status & ( SD_STATUS_TH
+						     | SD_STATUS_TA
+						     | SD_STATUS_TE)) {
+						au1x_mmc_send_data_pio(card);
+					}
+				}
+			} else {
+				/*
+				 *  Optimization.  Re-read status to possibly
+				 *  catch RX Data-Ready instead of incurring
+				 *  additional interrupt to tell us so.
+				 */
+				u32 status2;
+				au_sync();
+				status2 = au_readl(CARD_STATUS(card));
+				if (status2 != status) {
+					DEBUG(2, ": card %d status2 0x%08X\n",
+						mmc, status2);
+
+					status |= status2;
+				}
+
+				if (dma && (status & SD_STATUS_DB)) {
+					/*
+					 *  Bits are moving into the FIFO.
+					 *  Fire up the bit mover.
+					 */
+				  
+					au1x_mmc_receive_data_dma(card);
+	
+				}
+			}
+		} else if (status & (SD_STATUS_TA | SD_STATUS_TE)) {
+			if (tx && !dma) {
+				au1x_mmc_send_data_pio(card);
+			}
+		}
+
+		/*
+		 *  Data done.
+		 */
+                
+
+		
+		if (status & SD_STATUS_DD) {
+			if (dma) {
+				au1x_mmc_dma_complete(card);
+			} else if (status & SD_STATUS_NE) {
+				/*
+				 *  Make sure we got it all.
+				 */
+				au1x_mmc_receive_data_pio(card);
+			}
+
+			if (card->m_req->block_len != card->m_cnt) {
+				card->m_req->result = MMC_ERROR_CRC;
+				DEBUG(0, ": Incomplete transfer %s - 0x%08x\n",
+					tx ? "writing" : "reading",
+					status);
+			}
+			del_timer_sync(&card->m_irq_timer);
+			mmc_cmd_complete(card->m_req);
+			card->m_req = 0;
+			card->m_cnt = 0;
+			if (tx) {
+				card->m_flags &= ~AU_MMC_STATE_XMIT_ACTIVE;
+			}
+
+			if (! dma ) {
+				au_writel(au_readl(CARD_CONFIG(card))
+					   & ~(SD_CONFIG_TH | SD_CONFIG_RH),
+					CARD_CONFIG(card));
+				au_sync();
+			}
+		}
+
+		/*
+		 *  Depending on timing, x_NE and x_DD may
+		 *  both be asserted momentarily in the status
+		 *  word, thus we test to see if we still have
+		 *  an outstanding request.
+		 */
+		if ((card->m_req) && (status & SD_STATUS_NE)) {
+
+                        if (dma) {
+				au1x_mmc_receive_data_dma(card);
+			} else {
+				au1x_mmc_receive_data_pio(card);
+			}
+		}
+
+		
+		/* clear the bits - errors and all */
+		au_writel(status, CARD_STATUS(card));
+		au_sync();
+		continue;
+
+terminate_int:
+		/* clear the bits - errors and all */
+		au_writel(status, CARD_STATUS(card));
+		au_sync();
+
+		/* clean up after ourselves */
+		del_timer_sync(&card->m_irq_timer);
+		mmc_cmd_complete(card->m_req);
+		card->m_req = 0;
+		card->m_cnt = 0;
+
+		/*
+		 *  This sequence signals state changes to the upper layer that
+		 *  allow it to gracefully clean up and reset us from above.
+		 *  This will result in controller and dma channel resetting.
+		 */
+		mmc_eject(0);
+		mmc_insert(0);
+	}
+
+	enable_irq(AU1X_MMC_INT);
+}
+
+
+static void mmc_irq_timeout(unsigned long id)
+{
+	au1x_mmc_irq(0, (void *)id, 0);
+}
+
+
+static void au1x_mmc_send_cmd(struct mmc_request *req)
+{
+	int mmc = CTLR_SELECT(req->index);
+	struct au1x_mmc_card *card = au1x_mmc_cards[mmc];
+	u32 mmc_cmd = 0;
+	u32 config = 0;
+
+	DEBUG(2, ": card (%d) command = %d\n", mmc, req->cmd);
+
+	card->m_flags &= ~AU_MMC_STATE_XMIT_ACTIVE;
+
+	/*
+	 *  Establish command type.
+	 */
+	switch (req->cmd) {
+
+	case MMC_CIM_RESET:
+		del_timer_sync(&card->m_irq_timer);
+		del_timer_sync(&card->m_detect_timer);
+		card->m_flags &= ~(AU_MMC_STATE_XMIT_ACTIVE | AU_MMC_STATE_RECV_DMA_IN_USE);
+		card->m_req = 0;
+		card->m_cnt = 0;
+
+		if (card->m_xmit_dma >= 0) {
+#ifdef CONFIG_SOC_AU1100
+			disable_dma(card->m_xmit_dma);
+#endif
+#ifdef CONFIG_SOC_AU1200
+		  au1xxx_dbdma_stop((u32)(card->ctp_tx));
+#endif
+			au_sync();
+		}
+		if (card->m_recv_dma >= 0) {
+#ifdef CONFIG_SOC_AU1100
+			disable_dma(card->m_recv_dma);
+#endif
+#ifdef CONFIG_SOC_AU1200
+		  au1xxx_dbdma_stop((u32)(card->ctp_rx));
+#endif
+			au_sync();
+		}
+
+		mmc_power_off(mmc);
+		req->result = mmc_reset_controller(card, MMC_CLOCK_SLOW);
+
+
+         	if ( au1x_mmc_check_insert(mmc) ) {
+			mmc_power_on(mmc);
+			DEBUG(1, ": re-powering on MMC/SD card #%d\n", mmc);
+		}
+		mod_timer(&card->m_detect_timer, jiffies + MMC_DETECT_TIMEOUT);
+
+		if (card->m_xmit_dma >= 0) {
+#ifdef CONFIG_SOC_AU1100
+			init_dma(card->m_xmit_dma);
+#endif
+#ifdef CONFIG_SOC_AU1200
+		  au1xxx_dbdma_reset((u32)(card->ctp_tx));
+#endif
+			au_sync();
+		}
+		if (card->m_recv_dma >= 0) {
+#ifdef CONFIG_SOC_AU1100
+			init_dma(card->m_recv_dma);
+#endif
+#ifdef CONFIG_SOC_AU1200
+			au1xxx_dbdma_reset((u32)(card->ctp_rx));
+#endif
+			au_sync();
+		}
+
+		mmc_cmd_complete(req);
+		return;
+
+	case MMC_WRITE_BLOCK:
+		card->m_flags |= AU_MMC_STATE_XMIT_ACTIVE;
+
+		mmc_cmd |= SD_CMD_CT_1;
+		break;
+
+	case MMC_READ_SINGLE_BLOCK:
+		if ( card->m_flags & AU_MMC_STATE_USE_DMA ) {
+			config |= SD_CONFIG_NE;
+		} else {
+			/* enable recv ready for PIO mode */
+			config |= SD_CONFIG_RH;
+		}
+
+		mmc_cmd |= SD_CMD_CT_2;
+		break;
+
+	case MMC_WRITE_MULTIPLE_BLOCK:	/* SD_CMD_CT_3 */
+	case MMC_READ_MULTIPLE_BLOCK:	/* SD_CMD_CT_4 */
+	case MMC_STOP_TRANSMISSION:	/* SD_CMD_CT_7 */
+	  {
+		u32 ctype=0;
+		switch (req->cmd) {
+		case MMC_WRITE_MULTIPLE_BLOCK:	ctype = SD_CMD_CT_3; break;
+		case MMC_READ_MULTIPLE_BLOCK:	ctype = SD_CMD_CT_4; break;
+		case MMC_STOP_TRANSMISSION:	ctype = SD_CMD_CT_7; break;
+		}
+		DEBUG(0, ": Unsupported MMC command type: %d\n", ctype);
+
+		req->result = MMC_ERROR_ILLEGAL_COMMAND;
+		mmc_cmd_complete(req);
+		return;
+	  }
+
+	default:
+		if (req->nob) {
+			DEBUG(0, ": Data blocks (%d) for non-I/O command %d\n",
+				req->nob, req->cmd);
+
+			req->result = MMC_ERROR_DRIVER_FAILURE;
+			mmc_cmd_complete(req);
+			return;
+		}
+		break;
+	}
+
+	/*
+	 *  Insert upper level command.
+	 */
+	mmc_cmd |= req->cmd << SD_CMD_CI_SHIFT;
+
+	/*
+	 *  Deal with only one data block at a time.
+	 */
+	if (req->nob && req->nob != 1) {
+		DEBUG(0, ": Too many data blocks (%d) for command %d\n",
+			req->nob, req->cmd);
+
+		req->result = MMC_ERROR_DRIVER_FAILURE;
+		mmc_cmd_complete(req);
+		return;
+	}
+
+	/*
+	 *  Establish response type.
+	 */
+	switch (req->rtype) {
+
+	case RESPONSE_NONE:
+		mmc_cmd |= SD_CMD_RT_0;
+		break;
+
+	case RESPONSE_R1:
+		mmc_cmd |= SD_CMD_RT_1;
+		break;
+
+	case RESPONSE_R1B:
+		mmc_cmd |= SD_CMD_RT_1B;
+		break;
+
+	case RESPONSE_R2_CID:
+	case RESPONSE_R2_CSD:
+		mmc_cmd |= SD_CMD_RT_2;
+		break;
+
+	case RESPONSE_R3:
+		mmc_cmd |= SD_CMD_RT_3;
+		break;
+
+	case RESPONSE_R4:
+	case RESPONSE_R5:
+	default:
+		DEBUG(0, ": Unsupported MMC response type %d\n", req->rtype);
+
+		req->result = MMC_ERROR_ILLEGAL_COMMAND;
+		mmc_cmd_complete(req);
+		return;
+	}
+
+	card->m_req = req;
+	card->m_cnt = 0;
+
+	/*
+	 *  Prime timeout before we do anything that might trigger
+	 *  an interrupt that could possibly disarm the timer.
+	 */
+	mod_timer(&card->m_irq_timer, jiffies + MMC_IRQ_TIMEOUT);
+
+	/*
+	 *  Set any command-specific IRQ bits.
+	 */
+	if (config) {
+
+		au_writel(au_readl(CARD_CONFIG(card)) | config,
+			CARD_CONFIG(card));
+		au_sync();
+	}
+
+	/*
+	 *  Command argument must be written first.
+	 */
+	au_writel(req->arg, CARD_CMDARG(card));
+
+	/*
+	 *  Block count and size next.
+	 */
+	if (req->nob) {
+		/*
+		 *  Value programmed is one less than the actual
+		 *  number of bytes in the block.
+		 */
+		au_sync();	/* ordered writes */
+		au_writel(((req->block_len-1) << SD_BLKSIZE_BS_SHIFT)
+				  | (req->nob << SD_BLKSIZE_BC_SHIFT),
+			CARD_BLKSIZE(card));
+	}
+
+	/*
+	 *  Make sure state is updated before starting command.
+	 */
+	au_sync();	/* ordered writes */
+	au_writel((mmc_cmd | SD_CMD_GO), CARD_CMD(card));
+}
+
+
+static int au1x_mmc_check_insert(int mmc)
+{
+	int ins=0;
+
+
+	mmc_card_inserted(mmc, &ins);
+
+        
+	/*
+	 *  Update our own sense of presence.
+	 */
+	if (ins)
+		au1x_mmc_cards[mmc]->m_isempty = 0;
+	else
+		au1x_mmc_cards[mmc]->m_isempty = 1;
+
+	return ( ins );
+	
+               
+}
+
+
+static int au1x_mmc_is_empty(int slot)
+{
+	return ( au1x_mmc_cards[CTLR_SELECT(slot)]->m_isempty );
+}
+
+
+static void mmc_detect_timeout(unsigned long id)
+{
+	struct au1x_mmc_card *card = (struct au1x_mmc_card *) id;
+	int mmc = card->m_ctlr;
+	int old, new;
+
+	old = ! card->m_isempty;
+	new = au1x_mmc_check_insert(mmc);
+
+	if(old != new) {
+
+		DEBUG(1, ": isempty changed %d\n", new);
+		if (new) {
+			DEBUG(1, ": insert\n");
+			mmc_power_on(mmc);
+			DEBUG(1, ": powering on MMC/SD card #%d\n", mmc);
+			mmc_insert(0);
+		} else {
+			DEBUG(1, ": eject\n");
+			mmc_power_off(mmc);
+			DEBUG(1, ": powering off MMC/SD card #%d\n", mmc);
+			mmc_eject(0);
+		}
+	}
+
+	mod_timer(&card->m_detect_timer, jiffies + MMC_DETECT_TIMEOUT);
+}
+
+
+static int do_set_clock(struct au1x_mmc_card *card, u32 rate)
+{
+	unsigned int pbus = get_au1x00_speed();
+	unsigned int divisor;
+	u32 sd_config;
+
+	DEBUG(1, ": MMC clk rate=%d(0x%x) for ctlr #%d, cpu clk:%d(0x%x)\n", rate, rate, card->m_ctlr, pbus, pbus);
+
+	/*
+	 * Calculate appropriate DIVisor:
+	 *
+	 * Databook says 0x000 -> divide by 2
+	 *               0x1ff -> divide by 1024 (0x400)
+	 * peripheral clock runs at (cpuclock / sbus_divisor) / 2
+	 * so
+	 *    divisor = ((((cpuclock / sbus_divisor) / 2) / mmcclock) / 2) - 1
+	 */
+	pbus /= ((au_readl(SYS_POWERCTRL) & 0x3) + 2);
+	pbus /= 2;
+
+	divisor = ((pbus / rate) / 2) - 1;
+	DEBUG(1, ": divisor is 0x%x\n", divisor);
+
+	sd_config = au_readl(CARD_CONFIG(card)) & ~(SD_CONFIG_DIV);
+	sd_config |= ((divisor & SD_CONFIG_DIV) | SD_CONFIG_DE);
+	au_writel(sd_config, CARD_CONFIG(card));
+	au_sync();
+
+	card->m_clock = rate;	/* save rate away for use in possible reset */
+
+	return MMC_NO_ERROR;
+}
+
+
+static int mmc_reset_controller(struct au1x_mmc_card *card, u32 rate)
+{
+	int rv = MMC_NO_ERROR;
+
+	/*
+	 *  Enable controller via reset sequence.
+	 *  Data book says first enable clock,
+	 *  then take peripheral out of reset.
+	 */
+	au_writel(SD_ENABLE_CE, CARD_ENABLE(card));
+	au_sync_delay(1);
+	au_writel((SD_ENABLE_R | SD_ENABLE_CE), CARD_ENABLE(card));
+	au_sync_delay(1);
+
+	/*
+	 *  Clear out any bogus events.
+	 */
+	au_writel(~0, CARD_STATUS(card));
+	au_sync();	/* ordered writes */
+
+	/*
+	 *  Set max timeout - 21 bits - rest are reserved.
+	 */
+	au_writel(0, CARD_BLKSIZE(card));
+	au_writel(0x001fffff, CARD_TIMEOUT(card));
+	au_sync();	/* ordered writes */
+
+	/*
+	 *  Enable serial interface state machine and FIFO master.
+	 */
+	au_writel(SD_CONFIG2_EN, CARD_CONFIG2(card));	/* enable */
+	au_sync();	/* ordered writes */
+	au_writel(SD_CONFIG2_EN | SD_CONFIG2_FF, CARD_CONFIG2(card));	/* flush/reset FIFO */
+	au_sync();	/* ordered writes */
+	au_writel(SD_CONFIG2_EN, CARD_CONFIG2(card));	/* clear sticky flush bit */
+	au_sync();	/* ordered writes */
+
+	/*
+	 *  Enable interrupts.
+	 */
+	au_writel((SD_CONFIG_I |	/* master enable */
+		   SD_CONFIG_CR |	/* command response */
+		   SD_CONFIG_DD),	/* xfer done */
+		CARD_CONFIG(card));
+	au_sync();	/* ordered writes */
+
+	rv = do_set_clock(card, rate);
+
+	return rv;
+}
+
+
+static int au1x_mmc_set_clock(int slot, u32 rate)
+{
+	int mmc = CTLR_SELECT(slot);
+	struct au1x_mmc_card *card = au1x_mmc_cards[mmc];
+
+	return (do_set_clock(card, rate));
+}
+
+
+static int au1x_mmc_init(void)
+{
+	int mmc, ret;
+#ifdef CONFIG_SOC_AU1200
+	chan_tab_t *chanpt;
+#endif
+
+	/*
+	 *  Get irq.  Last arg used to distinguish timeouts.
+	 *  Service routine will scan all controllers.
+	 */
+	ret = request_irq(AU1X_MMC_INT, au1x_mmc_irq,
+				SA_SHIRQ | SA_INTERRUPT,
+				"MMC", au1x_mmc_cards/* must be non-NULL */);
+
+       
+	if(ret) {
+		DEBUG(0, ": request_irq() failed %d(0x%x).\n", -ret, -ret);
+		return ret;
+	}
+	disable_irq(AU1X_MMC_INT);
+
+	for (mmc = 0; mmc < NUM_MMC_CONTROLLERS; ++mmc) {
+
+		struct au1x_mmc_card *card = au1x_mmc_cards[mmc];
+
+		/*
+		 *  Process boot option(s)
+		 */
+		if ( au1x_mmc_use_pio ){
+			card->m_flags &= ~AU_MMC_STATE_USE_DMA; 
+                  
+                        
+		}else{
+			card->m_flags |= AU_MMC_STATE_USE_DMA;
+		}
+		if ( !au1x_mmc_dbl_buf && !au1x_mmc_use_pio ){
+			card->m_flags |= AU_MMC_STATE_DOUBLE_BUFFER_DMA;
+			
+		}else{
+			card->m_flags &= ~AU_MMC_STATE_DOUBLE_BUFFER_DMA;
+			
+                 }
+		if ( (au1x_mmc_ctlr == mmc) || ((unsigned)au1x_mmc_ctlr >= NUM_MMC_CONTROLLERS) )
+			card->m_flags |= AU_MMC_STATE_CTLR_ACTIVE;
+		else
+			card->m_flags &= ~AU_MMC_STATE_CTLR_ACTIVE;
+
+		/*
+		 *  Punt if this controller has been designated inactive.
+		 */
+		if ( ! (card->m_flags & AU_MMC_STATE_CTLR_ACTIVE) )
+			continue;
+
+		/*
+		 *  Assign this active controller to handle incoming requests.
+		 */
+		if (mmc_active_ctlr < 0)
+			mmc_active_ctlr = mmc;
+
+		/*
+		 *  Possibly redundant, as protocol sequence does a reset
+		 *  once a card is inserted.
+		 */
+		mmc_reset_controller(card, card->m_clock);
+
+		/*
+		 *  Detect card.
+		 */
+
+		if ( au1x_mmc_check_insert(mmc) ) {
+		  
+			mmc_power_on(mmc);
+                  
+			DEBUG(1, ": powering on MMC/SD card #%d\n", mmc);
+		}
+
+		/*
+		 *  Attach DMA resources.
+		 */
+		ret = -1;
+		if ( card->m_flags & AU_MMC_STATE_USE_DMA ) {
+
+			DEBUG(1, ": allocating TX DMA chan for MMC #%d\n", mmc);
+#ifdef CONFIG_SOC_AU1100
+			ret = card->m_xmit_dma =
+				request_au1000_dma(card->m_tx_dma_devid,
+								card->m_tx_dma_str,
+								NULL, 0, NULL);
+#endif
+#ifdef CONFIG_SOC_AU1200
+			card->ctp_tx=(chan_tab_t **)au1xxx_dbdma_chan_alloc(DSCR_CMD0_ALWAYS, 
+					(u32)(card->m_tx_dma_devid),
+					(void(*)())NULL, 
+					(void (*)())NULL);
+				chanpt = *((chan_tab_t**)(card->ctp_tx));
+				ret = card->m_xmit_dma=chanpt->chan_index;
+#endif
+
+			if (ret < 0) {
+				DEBUG(0, ": Request %s DMA failed %d(0x%x).\n",
+					card->m_tx_dma_str,
+					-ret, -ret);
+#ifdef CONFIG_SOC_AU1200
+			}else{
+			 
+			  /* Descriptor Ring Buffer Allocation  and Device Width 
+			   * 
+			   */
+                               	
+			      	au1xxx_dbdma_set_devwidth((u32)(card->ctp_tx), 8);
+			
+			        
+			         if (au1xxx_dbdma_ring_alloc((u32)(card->ctp_tx), NUM_DBDMA_DESCRIPTORS) == 0){
+				
+				  	DEBUG(0, ": Request %s DDMA Descriptor Ring Buffer Allocation failed %d(0x%x).\n",
+					      card->m_tx_dma_str);
+					 
+					au1xxx_dbdma_chan_free((u32)(card->ctp_tx));
+					
+					card->m_xmit_dma = -1;
+					ret = -1;
+				}
+#endif
+			
+			} 
+		}
+
+		if (ret >= 0) {
+
+			DEBUG(1, ": allocating RX DMA chan for MMC #%d\n", mmc);
+
+#ifdef CONFIG_SOC_AU1100
+			ret = card->m_recv_dma =
+				request_au1000_dma(card->m_rx_dma_devid,
+								card->m_rx_dma_str,
+								NULL, 0, NULL);
+#endif
+#ifdef CONFIG_SOC_AU1200
+			card->ctp_rx=(chan_tab_t **)au1xxx_dbdma_chan_alloc((u32)(card->m_rx_dma_devid),
+					DSCR_CMD0_ALWAYS,
+					(void(*)())NULL, 
+					(void (*)())NULL); 
+			ret = card->m_recv_dma=(*(card->ctp_rx))->chan_index;
+#endif
+
+			if (ret < 0) {
+				DEBUG(0, ": Request %s DMA failed %d(0x%x).\n",
+					card->m_rx_dma_str,
+					-ret, -ret);
+
+				/* undo xmit allocation */
+				if (card->m_xmit_dma >= 0) {
+#ifdef CONFIG_SOC_AU1100
+					free_au1000_dma(card->m_xmit_dma);
+					card->m_xmit_dma = -1;
+#endif
+#ifdef CONFIG_SOC_AU1200
+					au1xxx_dbdma_chan_free((u32)(card->ctp_tx));
+					card->m_xmit_dma = -1;
+				}
+			} else {
+				au1xxx_dbdma_set_devwidth((u32)(card->ctp_rx), 8);
+				if (au1xxx_dbdma_ring_alloc((u32)(card->ctp_rx), NUM_DBDMA_DESCRIPTORS) == 0) {
+					DEBUG(0, ": Request %s DDMA Descriptor Ring Buffer Allocation failed %d(0x%x).\n",
+						card->m_tx_dma_str);
+
+					au1xxx_dbdma_chan_free((u32)(card->ctp_tx));
+					card->m_xmit_dma = -1;
+					au1xxx_dbdma_chan_free((u32)(card->ctp_rx));
+					card->m_recv_dma = -1;
+					ret = -1;
+#endif
+				}
+			}
+		}
+
+		/* Get memory for any DMA double-buffering. */
+		if ((ret >= 0) && (card->m_flags & AU_MMC_STATE_DOUBLE_BUFFER_DMA)) {
+			void *mem1, *mem2;
+			int memsize;
+
+			/* maximum size (bytes) of MMC transaction */
+			memsize = (SD_BLKSIZE_BS >> SD_BLKSIZE_BS_SHIFT) + 1;
+
+			/* round maximum bytes up to next page boundary */
+			memsize = (memsize + PAGE_SIZE - 1) & PAGE_MASK;
+
+			mem1 = (void *)__get_free_pages(GFP_ATOMIC | GFP_DMA,
+						     get_order(memsize));
+			mem2 = (void *)__get_free_pages(GFP_ATOMIC | GFP_DMA,
+						     get_order(memsize));
+
+			if (mem1 != NULL && mem2 != NULL) {
+				memset(mem1, 0, memsize);
+				memset(mem2, 0, memsize);
+				card->m_tx_rawbuf = mem1;
+				card->m_rx_rawbuf = mem2;
+				card->m_tx_dmaaddr = (dma_addr_t)mem1; // was virt_to_phys(mem1);
+				card->m_rx_dmaaddr = (dma_addr_t)mem2; // was virt_to_phys(mem2);
+
+				card->m_dma_memsize = memsize;
+
+			} else {
+
+				ret = -1;
+				/*
+				 *  Release resources and fall back to PIO.
+				 */
+				DEBUG(0, ": DMA buffer allocation(s) failed"
+					" (0x%x 0x%x).\n",
+					(unsigned int)mem1,
+					(unsigned int)mem2);
+
+				if (mem1 != NULL) {
+					free_pages((unsigned long)mem1,
+						get_order(memsize));
+				}
+				if (mem2 != NULL) {
+					free_pages((unsigned long)mem2,
+						get_order(memsize));
+				}
+#ifdef CONFIG_SOC_AU1100
+				free_au1000_dma(card->m_xmit_dma);
+				card->m_xmit_dma = -1;
+				free_au1000_dma(card->m_recv_dma);
+				card->m_recv_dma = -1;
+#endif
+#ifdef CONFIG_SOC_AU1200
+				au1xxx_dbdma_chan_free((u32)(card->ctp_tx));
+				au1xxx_dbdma_chan_free((u32)(card->ctp_rx));
+#endif
+			}
+		}
+
+		/*
+		 *  Fall back to PIO if DMA setup fails.
+		 */
+		if ((ret < 0) && (card->m_flags & AU_MMC_STATE_USE_DMA)) {
+			card->m_flags &= ~(AU_MMC_STATE_USE_DMA | AU_MMC_STATE_DOUBLE_BUFFER_DMA);
+			DEBUG(0,": MMC #%d DMA setup failed - using PIO\n",mmc);
+		}
+
+		/*
+		 *  Prime the detection timer.
+		 */
+		mod_timer(&card->m_detect_timer, jiffies + MMC_DETECT_TIMEOUT);
+	}
+	au1x_mmc_cards[0]->m_flags |= AU_MMC_STATE_MODULE_INITTED;
+
+	enable_irq(AU1X_MMC_INT);
+
+	return MMC_NO_ERROR;
+}
+
+
+static void au1x_mmc_cleanup(void)
+{
+	int mmc, flags;
+	struct au1x_mmc_card *card;
+
+	/*
+	 *  Punt if we've been here before.
+	 */
+	flags = au1x_mmc_cards[0]->m_flags;
+	au1x_mmc_cards[0]->m_flags &= ~AU_MMC_STATE_MODULE_INITTED;
+	if ( ! (flags & AU_MMC_STATE_MODULE_INITTED) )
+		return;
+
+	disable_irq(AU1X_MMC_INT);
+
+	/*
+	 *  Free resources.
+	 */
+	au_sync();	/* ordered writes */
+	for (mmc = 0; mmc < NUM_MMC_CONTROLLERS; ++mmc) {
+		card = au1x_mmc_cards[mmc];
+
+		/*
+		 *  Punt timers.
+		 */
+		del_timer_sync(&card->m_detect_timer);
+		del_timer_sync(&card->m_irq_timer);
+
+		/*
+		 *  Unattach DMA.
+		 */
+		if (card->m_tx_rawbuf != NULL) {
+			free_pages((unsigned long)card->m_tx_rawbuf,
+				get_order(card->m_dma_memsize));
+			card->m_tx_rawbuf = NULL;
+		}
+		if (card->m_rx_rawbuf != NULL) {
+			free_pages((unsigned long)card->m_rx_rawbuf,
+				get_order(card->m_dma_memsize));
+			card->m_rx_rawbuf = NULL;
+		}
+		if (card->m_xmit_dma >= 0) {
+#ifdef CONFIG_SOC_AU1100
+			free_au1000_dma(card->m_xmit_dma);
+			card->m_xmit_dma = -1;
+#endif
+#ifdef CONFIG_SOC_AU1200
+		  au1xxx_dbdma_chan_free((u32)card->ctp_tx); 
+#endif
+		}
+		if (card->m_recv_dma >= 0) {
+#ifdef CONFIG_SOC_AU1100
+			free_au1000_dma(card->m_recv_dma);
+			card->m_recv_dma = -1;
+#endif
+#ifdef CONFIG_SOC_AU1200
+		  au1xxx_dbdma_chan_free((u32)card->ctp_rx);
+#endif
+		}
+
+		/*
+		 *  Leave cards powered off.
+		 */
+		mmc_power_off(mmc);
+
+		/*
+		 *  Disable controllers.
+		 */
+		au_writel(0, CARD_ENABLE(card));
+		au_sync();	/* ordered writes */
+	}
+
+	/* 2nd arg must match dev_id in request_irq */
+	free_irq(AU1X_MMC_INT, au1x_mmc_cards);
+}
+
+
+static int __init au1x_mmc_module_init(void)
+{
+	int mmc;
+	struct au1x_mmc_card *card;
+
+	for (mmc = 0; mmc < NUM_MMC_CONTROLLERS; ++mmc) {
+
+		/*
+		 *  Complete basic init of controller descriptor (timers, etc).
+		 */
+		card = au1x_mmc_cards[mmc] = &au1x_mmc_card_table[mmc];
+
+		card->m_irq_timer.function = mmc_irq_timeout;
+		card->m_irq_timer.data = (unsigned long) card;
+		init_timer(&card->m_irq_timer);
+
+		card->m_detect_timer.function = mmc_detect_timeout;
+		card->m_detect_timer.data = (unsigned long) card;
+		init_timer(&card->m_detect_timer);
+
+		/*
+		 *  Leave cards powered off until insertion detected.
+		 */
+		mmc_power_off(mmc);
+
+		/*
+		 *  Leave peripheral clock disabled to conserve power.
+		 */
+		au_writel(0, CARD_ENABLE(card));
+		au_sync();	/* ordered writes */
+	}
+
+	/*
+	 *  Insert self into MMC framework, indicating number of slots
+	 *  the controller is servicing.
+	 *
+	 *  The framework supports a single controller but with possibly
+	 *  multiple slots.  A specific slot can be encoded into the
+	 *  command argument.  The Au1x supports a dual-controller, each
+	 *  with a single slot.  We report to the upper layer that we
+	 *  handle only one slot; the active controller is selected at
+	 *  boot-time/module-load-time.
+	 */
+	return mmc_register_slot_driver(&au1x_mmc_driver, 1);
+}
+
+
+static void __exit au1x_mmc_module_exit(void)
+{
+#if defined(CONFIG_MMC_MODULE_UNIFIED)
+	/*
+	 *  If the outer framework and this driver are bound together
+	 *  as a single module, care must be taken on module unload.
+	 *  Ideally we should unregister the slot driver.  However, at
+	 *  module_exit time the upper layers may have already cleaned
+	 *  up and exited; and in fact it turns out the unregistration
+	 *  primitive would restart/reschedule a tasklet that otherwise
+	 *  has been terminated.  Presumably a memory management fault
+	 *  would occur when the tasklet runs after module removal.
+	 *
+	 *  So, if a user takes care to unmount and eject media before
+	 *  removing this module, much of the necessary cleanup will
+	 *  happen as a result of that.  About all we need to do is
+	 *  release local resources and quiesce the hardware.
+	 */
+	au1x_mmc_cleanup();
+#else
+	/*
+	 *  Remove reference counts on the upper framework.
+	 */
+	mmc_unregister_slot_driver(&au1x_mmc_driver);
+	au1x_mmc_cleanup();	/* possibly redundant */
+#endif
+}
+
+
+module_init(au1x_mmc_module_init);
+module_exit(au1x_mmc_module_exit);
+
+
+/* boot/load options - non-module */
+#ifndef MODULE
+static int __init usepio_setup(char *str)
+{
+	au1x_mmc_use_pio = simple_strtol(str, NULL, 0);
+	return 1;
+}
+static int __init dblbuf_setup(char *str)
+{
+	au1x_mmc_dbl_buf = simple_strtol(str, NULL, 0);
+	return 1;
+}
+static int __init ctlr_setup(char *str)
+{
+	au1x_mmc_ctlr = simple_strtol(str, NULL, 0);
+	return 1;
+}
+
+__setup("au1x_mmc_use_pio=", usepio_setup);
+__setup("au1x_mmc_dbl_buf=", dblbuf_setup);
+__setup("au1x_mmc_ctlr=", ctlr_setup);
+#endif
+
+/* boot/load options - module */
+MODULE_PARM(au1x_mmc_use_pio, "i");
+MODULE_PARM_DESC(au1x_mmc_use_pio, "Force PIO - default is use DMA channels.");
+
+MODULE_PARM(au1x_mmc_dbl_buf, "i");
+MODULE_PARM_DESC(au1x_mmc_dbl_buf, "For DMA, double buffer - use for unaligned requests.");
+
+MODULE_PARM(au1x_mmc_ctlr, "i");
+MODULE_PARM_DESC(au1x_mmc_ctlr, "Which SD controller to enable (0, 1, or 2(both)) -- SD0 is default.");
+
+MODULE_AUTHOR("Embedded Edge, LLC.");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au1x00 MultiMediaCard controller support");
+
+
diff -Nur linux-2.4.30/drivers/mmc/mmc_core.c linux-2.4.30-linux-mips.org/drivers/mmc/mmc_core.c
--- linux-2.4.30/drivers/mmc/mmc_core.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mmc/mmc_core.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,839 @@
+/*
+ * Core MMC driver functions
+ *
+ * Copyright (c) 2002 Hewlett-Packard Company
+ *   
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *  
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *  
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Author:  Andrew Christian
+ *          6 May 2002 */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/version.h>
+#include <linux/proc_fs.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/list.h>
+#include <linux/sysctl.h>
+#include <linux/pm.h>
+
+#include "mmc_core.h"
+
+#define STATE_CMD_ACTIVE   (1<<0)
+#define STATE_CMD_DONE     (1<<1)
+#define STATE_INSERT       (1<<2)
+#define STATE_EJECT        (1<<3)
+
+static struct mmc_dev          g_mmc_dev;
+static struct proc_dir_entry  *proc_mmc_dir;
+
+int g_mmc_debug = CONFIG_MMC_DEBUG_VERBOSE;
+EXPORT_SYMBOL(g_mmc_debug);
+
+/**************************************************************************
+ * Debugging functions
+ **************************************************************************/
+
+static char * mmc_result_strings[] = {
+	"NO_RESPONSE",
+	"NO_ERROR",
+	"ERROR_OUT_OF_RANGE",
+	"ERROR_ADDRESS",
+	"ERROR_BLOCK_LEN",
+	"ERROR_ERASE_SEQ",
+	"ERROR_ERASE_PARAM",
+	"ERROR_WP_VIOLATION",
+	"ERROR_CARD_IS_LOCKED",
+	"ERROR_LOCK_UNLOCK_FAILED",
+	"ERROR_COM_CRC",
+	"ERROR_ILLEGAL_COMMAND",
+	"ERROR_CARD_ECC_FAILED",
+	"ERROR_CC",
+	"ERROR_GENERAL",
+	"ERROR_UNDERRUN",
+	"ERROR_OVERRUN",
+	"ERROR_CID_CSD_OVERWRITE",
+	"ERROR_STATE_MISMATCH",
+	"ERROR_HEADER_MISMATCH",
+	"ERROR_TIMEOUT",
+	"ERROR_CRC",
+	"ERROR_DRIVER_FAILURE",
+};
+
+char * mmc_result_to_string( int i )
+{
+	return mmc_result_strings[i+1];
+}
+
+static char * card_state_strings[] = {
+	"empty",
+	"idle",
+	"ready",
+	"ident",
+	"stby",
+	"tran",
+	"data",
+	"rcv",
+	"prg",
+	"dis",
+};
+
+static inline char * card_state_to_string( int i )
+{
+	return card_state_strings[i+1];
+}
+
+/**************************************************************************
+ * Utility functions
+ **************************************************************************/
+
+#define PARSE_U32(_buf,_index) \
+	(((u32)_buf[_index]) << 24) | (((u32)_buf[_index+1]) << 16) | \
+        (((u32)_buf[_index+2]) << 8) | ((u32)_buf[_index+3]);
+
+#define PARSE_U16(_buf,_index) \
+	(((u16)_buf[_index]) << 8) | ((u16)_buf[_index+1]);
+
+int mmc_unpack_csd( struct mmc_request *request, struct mmc_csd *csd )
+{
+	u8 *buf = request->response;
+	
+	if ( request->result ) return request->result;
+
+	csd->csd_structure      = (buf[1] & 0xc0) >> 6;
+	csd->spec_vers          = (buf[1] & 0x3c) >> 2;
+	csd->taac               = buf[2];
+	csd->nsac               = buf[3];
+	csd->tran_speed         = buf[4];
+	csd->ccc                = (((u16)buf[5]) << 4) | ((buf[6] & 0xf0) >> 4);
+	csd->read_bl_len        = buf[6] & 0x0f;
+	csd->read_bl_partial    = (buf[7] & 0x80) ? 1 : 0;
+	csd->write_blk_misalign = (buf[7] & 0x40) ? 1 : 0;
+	csd->read_blk_misalign  = (buf[7] & 0x20) ? 1 : 0;
+	csd->dsr_imp            = (buf[7] & 0x10) ? 1 : 0;
+	csd->c_size             = ((((u16)buf[7]) & 0x03) << 10) | (((u16)buf[8]) << 2) | (((u16)buf[9]) & 0xc0) >> 6;
+	csd->vdd_r_curr_min     = (buf[9] & 0x38) >> 3;
+	csd->vdd_r_curr_max     = buf[9] & 0x07;
+	csd->vdd_w_curr_min     = (buf[10] & 0xe0) >> 5;
+	csd->vdd_w_curr_max     = (buf[10] & 0x1c) >> 2;
+	csd->c_size_mult        = ((buf[10] & 0x03) << 1) | ((buf[11] & 0x80) >> 7);
+	switch ( csd->csd_structure ) {
+	case CSD_STRUCT_VER_1_0:
+	case CSD_STRUCT_VER_1_1:
+		csd->erase.v22.sector_size    = (buf[11] & 0x7c) >> 2;
+		csd->erase.v22.erase_grp_size = ((buf[11] & 0x03) << 3) | ((buf[12] & 0xe0) >> 5);
+		break;
+	case CSD_STRUCT_VER_1_2:
+	default:
+		csd->erase.v31.erase_grp_size = (buf[11] & 0x7c) >> 2;
+		csd->erase.v31.erase_grp_mult = ((buf[11] & 0x03) << 3) | ((buf[12] & 0xe0) >> 5);
+		break;
+	}
+	csd->wp_grp_size        = buf[12] & 0x1f;
+	csd->wp_grp_enable      = (buf[13] & 0x80) ? 1 : 0;
+	csd->default_ecc        = (buf[13] & 0x60) >> 5;
+	csd->r2w_factor         = (buf[13] & 0x1c) >> 2;
+	csd->write_bl_len       = ((buf[13] & 0x03) << 2) | ((buf[14] & 0xc0) >> 6);
+	csd->write_bl_partial   = (buf[14] & 0x20) ? 1 : 0;
+	csd->file_format_grp    = (buf[15] & 0x80) ? 1 : 0;
+	csd->copy               = (buf[15] & 0x40) ? 1 : 0;
+	csd->perm_write_protect = (buf[15] & 0x20) ? 1 : 0;
+	csd->tmp_write_protect  = (buf[15] & 0x10) ? 1 : 0;
+	csd->file_format        = (buf[15] & 0x0c) >> 2;
+	csd->ecc                = buf[15] & 0x03;
+
+	DEBUG(2,"  csd_structure=%d  spec_vers=%d  taac=%02x  nsac=%02x  tran_speed=%02x\n"
+	      "  ccc=%04x  read_bl_len=%d  read_bl_partial=%d  write_blk_misalign=%d\n"
+	      "  read_blk_misalign=%d  dsr_imp=%d  c_size=%d  vdd_r_curr_min=%d\n"
+	      "  vdd_r_curr_max=%d  vdd_w_curr_min=%d  vdd_w_curr_max=%d  c_size_mult=%d\n"
+	      "  wp_grp_size=%d  wp_grp_enable=%d  default_ecc=%d  r2w_factor=%d\n"
+	      "  write_bl_len=%d  write_bl_partial=%d  file_format_grp=%d  copy=%d\n"
+	      "  perm_write_protect=%d  tmp_write_protect=%d  file_format=%d  ecc=%d\n",
+	      csd->csd_structure, csd->spec_vers, 
+	      csd->taac, csd->nsac, csd->tran_speed,
+	      csd->ccc, csd->read_bl_len, 
+	      csd->read_bl_partial, csd->write_blk_misalign,
+	      csd->read_blk_misalign, csd->dsr_imp, 
+	      csd->c_size, csd->vdd_r_curr_min,
+	      csd->vdd_r_curr_max, csd->vdd_w_curr_min, 
+	      csd->vdd_w_curr_max, csd->c_size_mult,
+	      csd->wp_grp_size, csd->wp_grp_enable,
+	      csd->default_ecc, csd->r2w_factor, 
+	      csd->write_bl_len, csd->write_bl_partial,
+	      csd->file_format_grp, csd->copy, 
+	      csd->perm_write_protect, csd->tmp_write_protect,
+	      csd->file_format, csd->ecc);
+	switch (csd->csd_structure) {
+	case CSD_STRUCT_VER_1_0:
+	case CSD_STRUCT_VER_1_1:
+		DEBUG(2," V22 sector_size=%d erase_grp_size=%d\n", 
+		      csd->erase.v22.sector_size, 
+		      csd->erase.v22.erase_grp_size);
+		break;
+	case CSD_STRUCT_VER_1_2:
+	default:
+		DEBUG(2," V31 erase_grp_size=%d erase_grp_mult=%d\n", 
+		      csd->erase.v31.erase_grp_size,
+		      csd->erase.v31.erase_grp_mult);
+		break;
+		
+	}
+
+	if ( buf[0] != 0x3f )  return MMC_ERROR_HEADER_MISMATCH;
+
+	return 0;
+}
+
+int mmc_unpack_r1( struct mmc_request *request, struct mmc_response_r1 *r1, enum card_state state )
+{
+	u8 *buf = request->response;
+
+	if ( request->result )        return request->result;
+
+	r1->cmd    = buf[0];
+	r1->status = PARSE_U32(buf,1);
+
+	DEBUG(2," cmd=%d status=%08x\n", r1->cmd, r1->status);
+
+	if (R1_STATUS(r1->status)) {
+		if ( r1->status & R1_OUT_OF_RANGE )       return MMC_ERROR_OUT_OF_RANGE;
+		if ( r1->status & R1_ADDRESS_ERROR )      return MMC_ERROR_ADDRESS;
+		if ( r1->status & R1_BLOCK_LEN_ERROR )    return MMC_ERROR_BLOCK_LEN;
+		if ( r1->status & R1_ERASE_SEQ_ERROR )    return MMC_ERROR_ERASE_SEQ;
+		if ( r1->status & R1_ERASE_PARAM )        return MMC_ERROR_ERASE_PARAM;
+		if ( r1->status & R1_WP_VIOLATION )       return MMC_ERROR_WP_VIOLATION;
+		if ( r1->status & R1_CARD_IS_LOCKED )     return MMC_ERROR_CARD_IS_LOCKED;
+		if ( r1->status & R1_LOCK_UNLOCK_FAILED ) return MMC_ERROR_LOCK_UNLOCK_FAILED;
+		if ( r1->status & R1_COM_CRC_ERROR )      return MMC_ERROR_COM_CRC;
+		if ( r1->status & R1_ILLEGAL_COMMAND )    return MMC_ERROR_ILLEGAL_COMMAND;
+		if ( r1->status & R1_CARD_ECC_FAILED )    return MMC_ERROR_CARD_ECC_FAILED;
+		if ( r1->status & R1_CC_ERROR )           return MMC_ERROR_CC;
+		if ( r1->status & R1_ERROR )              return MMC_ERROR_GENERAL;
+		if ( r1->status & R1_UNDERRUN )           return MMC_ERROR_UNDERRUN;
+		if ( r1->status & R1_OVERRUN )            return MMC_ERROR_OVERRUN;
+		if ( r1->status & R1_CID_CSD_OVERWRITE )  return MMC_ERROR_CID_CSD_OVERWRITE;
+	}
+
+	if ( buf[0] != request->cmd ) return MMC_ERROR_HEADER_MISMATCH;
+
+	/* This should be last - it's the least dangerous error */
+	if ( R1_CURRENT_STATE(r1->status) != state ) return MMC_ERROR_STATE_MISMATCH;
+
+	return 0;
+}
+
+int mmc_unpack_cid( struct mmc_request *request, struct mmc_cid *cid )
+{
+	u8 *buf = request->response;
+	int i;
+
+	if ( request->result ) return request->result;
+
+	cid->mid = buf[1];
+	cid->oid = PARSE_U16(buf,2);
+	for ( i = 0 ; i < 6 ; i++ )
+		cid->pnm[i] = buf[4+i];
+	cid->pnm[6] = 0;
+	cid->prv = buf[10];
+	cid->psn = PARSE_U32(buf,11);
+	cid->mdt = buf[15];
+	
+	DEBUG(2," mid=%d oid=%d pnm=%s prv=%d.%d psn=%08x mdt=%d/%d\n",
+	      cid->mid, cid->oid, cid->pnm, 
+	      (cid->prv>>4), (cid->prv&0xf), 
+	      cid->psn, (cid->mdt>>4), (cid->mdt&0xf)+1997);
+
+	if ( buf[0] != 0x3f )  return MMC_ERROR_HEADER_MISMATCH;
+      	return 0;
+}
+
+int mmc_unpack_r3( struct mmc_request *request, struct mmc_response_r3 *r3 )
+{
+	u8 *buf = request->response;
+
+	if ( request->result ) return request->result;
+
+	r3->ocr = PARSE_U32(buf,1);
+	DEBUG(2," ocr=%08x\n", r3->ocr);
+
+	if ( buf[0] != 0x3f )  return MMC_ERROR_HEADER_MISMATCH;
+	return 0;
+}
+
+/**************************************************************************/
+
+#define KBPS 1
+#define MBPS 1000
+
+static u32 ts_exp[] = { 100*KBPS, 1*MBPS, 10*MBPS, 100*MBPS, 0, 0, 0, 0 };
+static u32 ts_mul[] = { 0,    1000, 1200, 1300, 1500, 2000, 2500, 3000, 
+			3500, 4000, 4500, 5000, 5500, 6000, 7000, 8000 };
+
+u32 mmc_tran_speed( u8 ts )
+{
+	u32 rate = ts_exp[(ts & 0x7)] * ts_mul[(ts & 0x78) >> 3];
+
+	if ( rate <= 0 ) {
+		DEBUG(0, ": error - unrecognized speed 0x%02x\n", ts);
+		return 1;
+	}
+
+	return rate;
+}
+
+/**************************************************************************/
+
+void mmc_send_cmd( struct mmc_dev *dev, int cmd, u32 arg, 
+		   u16 nob, u16 block_len, enum mmc_rsp_t rtype )
+{
+	dev->request.cmd       = cmd;
+	dev->request.arg       = arg;
+	dev->request.rtype     = rtype;
+	dev->request.nob       = nob;
+	dev->request.block_len = block_len;
+	dev->request.buffer    = NULL;
+	if ( nob && dev->io_request )
+		dev->request.buffer = dev->io_request->buffer;
+
+	dev->state  |= STATE_CMD_ACTIVE;
+	dev->sdrive->send_cmd(&dev->request);
+}
+
+void mmc_finish_io_request( struct mmc_dev *dev, int result )
+{
+	struct mmc_io_request *t = dev->io_request;
+	struct mmc_slot *slot = dev->slot + t->id;
+
+	dev->io_request = NULL;     // Remove the old request (the media driver may requeue)
+	if ( slot->media_driver )
+		slot->media_driver->io_request_done( t, result );
+}
+
+
+/* Only call this when there is no pending request - it unloads the media driver */
+int mmc_check_eject( struct mmc_dev *dev )
+{
+	unsigned long   flags;
+	int             state;
+	int             i;
+
+	DEBUG(2," dev state=%x\n", dev->state);
+
+	local_irq_save(flags);
+	state = dev->state;
+	dev->state = state & ~STATE_EJECT;
+	local_irq_restore(flags);
+
+	if ( !(state & STATE_EJECT) )
+		return 0;
+
+	for ( i = 0 ; i < dev->num_slots ; i++ ) {
+		struct mmc_slot *slot = dev->slot + i;
+
+		if ( slot->flags & MMC_SLOT_FLAG_EJECT ) {
+			slot->state = CARD_STATE_EMPTY;
+			if ( slot->media_driver ) {
+				slot->media_driver->unload( slot );
+				slot->media_driver = NULL;
+			}
+			slot->flags &= ~MMC_SLOT_FLAG_EJECT;
+			run_sbin_mmc_hotplug(dev,i,0);
+		}
+	}
+	return 1;
+}
+
+int mmc_check_insert( struct mmc_dev *dev )
+{
+	unsigned long   flags;
+	int             state;
+	int             i;
+	int             card_count = 0;
+
+	DEBUG(2," dev state=%x\n", dev->state);
+
+	local_irq_save(flags);
+	state = dev->state;
+	dev->state = state & ~STATE_INSERT;
+	local_irq_restore(flags);
+
+	if ( !(state & STATE_INSERT) ) 
+		return 0;
+
+	for ( i = 0 ; i < dev->num_slots ; i++ ) {
+		struct mmc_slot *slot = dev->slot + i;
+
+		if ( slot->flags & MMC_SLOT_FLAG_INSERT ) {
+			if  (!dev->sdrive->is_empty(i)) {
+				slot->state = CARD_STATE_IDLE;
+				card_count++;
+			}
+			slot->flags &= ~MMC_SLOT_FLAG_INSERT;
+		}
+	}
+	return card_count;
+}
+
+/******************************************************************
+ *
+ * Hotplug callback card insertion/removal
+ *
+ ******************************************************************/
+
+#ifdef CONFIG_HOTPLUG
+
+extern char hotplug_path[];
+extern int call_usermodehelper(char *path, char **argv, char **envp);
+
+static void run_sbin_hotplug(struct mmc_dev *dev, int id, int insert)
+{
+	int i;
+	char *argv[3], *envp[8];
+	char media[64], slotnum[16];
+
+	if (!hotplug_path[0])
+		return;
+
+	DEBUG(0,": hotplug_path=%s id=%d insert=%d\n", hotplug_path, id, insert);
+
+	i = 0;
+	argv[i++] = hotplug_path;
+	argv[i++] = "mmc";
+	argv[i] = 0;
+
+	/* minimal command environment */
+	i = 0;
+	envp[i++] = "HOME=/";
+	envp[i++] = "PATH=/sbin:/bin:/usr/sbin:/usr/bin";
+	
+	/* other stuff we want to pass to /sbin/hotplug */
+	sprintf(slotnum, "SLOT=%d", id );
+	if ( dev->slot[id].media_driver && dev->slot[id].media_driver->name )
+		sprintf(media, "MEDIA=%s", dev->slot[id].media_driver->name );
+	else
+		sprintf(media, "MEDIA=unknown");
+
+	envp[i++] = slotnum;
+	envp[i++] = media;
+
+	if (insert)
+		envp[i++] = "ACTION=add";
+	else
+		envp[i++] = "ACTION=remove";
+	envp[i] = 0;
+
+	call_usermodehelper (argv [0], argv, envp);
+}
+
+static void mmc_hotplug_task_handler( void *nr )
+{
+	int insert = ((int) nr) & 0x01;
+	int id     = ((int) nr) >> 1;
+	DEBUG(2," id=%d insert=%d\n", id, insert );
+	run_sbin_hotplug(&g_mmc_dev, id, insert );
+}
+
+static struct tq_struct mmc_hotplug_task = {
+	routine:  mmc_hotplug_task_handler
+};
+
+void run_sbin_mmc_hotplug(struct mmc_dev *dev, int id, int insert )
+{
+	mmc_hotplug_task.data = (void *) ((id << 1) | insert);
+	schedule_task( &mmc_hotplug_task );
+}
+
+#else
+void run_sbin_mmc_hotplug(struct sleeve_dev *sdev, int insert) { }
+#endif /* CONFIG_HOTPLUG */
+
+
+/******************************************************************
+ * Common processing tasklet
+ * Everything gets serialized through this
+ ******************************************************************/
+
+static void mmc_tasklet_action(unsigned long data)
+{
+	struct mmc_dev *dev = (struct mmc_dev *)data;
+	unsigned long   flags;
+	int             state;
+
+	DEBUG(2,": dev=%p flags=%02x\n", dev, dev->state);
+
+	/* Grab the current working state */
+	local_irq_save(flags);
+	state = dev->state;
+	if ( state & STATE_CMD_DONE )
+		dev->state = state & ~(STATE_CMD_DONE | STATE_CMD_ACTIVE);
+	local_irq_restore(flags);
+
+	/* If there is an active command, don't do anything */
+	if ( (state & STATE_CMD_ACTIVE) && !(state & STATE_CMD_DONE) )
+		return;
+
+	if ( dev->protocol )
+		dev->protocol(dev,state);
+}
+
+/******************************************************************
+ * Callbacks from low-level driver
+ * These run at interrupt time
+ ******************************************************************/
+
+void mmc_cmd_complete(struct mmc_request *request)
+{
+	DEBUG(2,": request=%p retval=%d\n", request, request->result);
+	g_mmc_dev.state |= STATE_CMD_DONE;
+	if ( !g_mmc_dev.suspended )
+		tasklet_schedule(&g_mmc_dev.task);
+}
+
+void mmc_insert(int slot)
+{
+	DEBUG(2,": %d\n", slot);
+	g_mmc_dev.state |= STATE_INSERT;
+	g_mmc_dev.slot[slot].flags |= MMC_SLOT_FLAG_INSERT;
+	if ( !g_mmc_dev.suspended )
+		tasklet_schedule(&g_mmc_dev.task);
+}
+
+void mmc_eject(int slot)
+{
+	DEBUG(2,": %d\n", slot);
+	g_mmc_dev.state |= STATE_EJECT;
+	g_mmc_dev.slot[slot].flags |= MMC_SLOT_FLAG_EJECT;
+	if ( !g_mmc_dev.suspended )
+		tasklet_schedule(&g_mmc_dev.task);
+}
+
+EXPORT_SYMBOL(mmc_cmd_complete);
+EXPORT_SYMBOL(mmc_insert);
+EXPORT_SYMBOL(mmc_eject);
+
+/******************************************************************
+ * Called from the media handler
+ ******************************************************************/
+
+void mmc_handle_io_request( struct mmc_io_request *t )
+{
+	DEBUG(2," id=%d cmd=%d sector=%ld nr_sectors=%ld block_len=%ld buf=%p\n",
+	      t->id, t->cmd, t->sector, t->nr_sectors, t->block_len, t->buffer);
+	
+	if ( g_mmc_dev.io_request ) {
+		DEBUG(0,": error! io_request in progress\n");
+		return;
+	}
+	
+	g_mmc_dev.io_request = t;
+	tasklet_schedule(&g_mmc_dev.task);
+}
+
+EXPORT_SYMBOL(mmc_handle_io_request);
+
+/******************************************************************
+ *  Media handlers
+ *  Allow different drivers to register a media handler
+ ******************************************************************/
+
+static LIST_HEAD(mmc_media_drivers);
+
+int mmc_match_media_driver( struct mmc_slot *slot )
+{
+	struct list_head *item;
+
+	DEBUG(2,": slot=%p\n", slot);
+
+	for ( item = mmc_media_drivers.next ; item != &mmc_media_drivers ; item = item->next ) {
+		struct mmc_media_driver *drv = list_entry(item, struct mmc_media_driver, node );
+		if ( drv->probe(slot) ) {
+			slot->media_driver = drv;
+			drv->load(slot);
+			return 1;
+		}
+	}
+	return 0;
+}
+
+int mmc_register_media_driver( struct mmc_media_driver *drv )
+{
+	list_add_tail( &drv->node, &mmc_media_drivers );
+	return 0;
+}
+
+void mmc_unregister_media_driver( struct mmc_media_driver *drv )
+{
+	list_del(&drv->node);
+}
+
+EXPORT_SYMBOL(mmc_register_media_driver);
+EXPORT_SYMBOL(mmc_unregister_media_driver);
+
+/******************************************************************/
+
+int mmc_register_slot_driver( struct mmc_slot_driver *sdrive, int num_slots )
+{
+	int i;
+	int retval;
+
+	DEBUG(2," max=%d ocr=0x%08x\n", num_slots, sdrive->ocr);
+
+	if ( num_slots > MMC_MAX_SLOTS ) {
+		printk("%s%s: illegal num of slots %d\n", KERN_CRIT, __FUNCTION__, num_slots );
+		return -ENOMEM;
+	}
+
+	if ( g_mmc_dev.sdrive ) {
+		printk("%s%s: slot in use\n", KERN_ALERT, __FUNCTION__);
+		return -EBUSY;
+	}
+
+	g_mmc_dev.sdrive    = sdrive;
+	g_mmc_dev.num_slots = num_slots;
+
+	for ( i = 0 ; i < num_slots ; i++ ) {
+		struct mmc_slot *slot = &g_mmc_dev.slot[i];
+		memset(slot,0,sizeof(struct mmc_slot));
+		slot->id = i;
+		slot->state = CARD_STATE_EMPTY;
+	}
+	retval = sdrive->init();
+	if ( retval )
+		return retval;
+
+	/* Generate insert events for cards */
+	for ( i = 0 ; i < num_slots ; i++ )
+		if ( !sdrive->is_empty(i) )
+			mmc_insert(i);
+	return 0;
+}
+
+void mmc_unregister_slot_driver( struct mmc_slot_driver *sdrive )
+{
+	int i;
+	DEBUG(2,"\n");
+
+	for ( i = 0 ; i < g_mmc_dev.num_slots ; i++ )
+		mmc_eject(i);
+
+	if ( sdrive == g_mmc_dev.sdrive ) {
+		g_mmc_dev.sdrive->cleanup();
+		g_mmc_dev.sdrive = NULL;
+	}
+}
+
+EXPORT_SYMBOL(mmc_register_slot_driver);
+EXPORT_SYMBOL(mmc_unregister_slot_driver);
+
+/******************************************************************/
+
+static struct pm_dev *mmc_pm_dev;
+
+static int mmc_pm_callback(struct pm_dev *pm_dev, pm_request_t req, void *data)
+{
+	int i;
+	DEBUG(0,": pm callback %d\n", req );
+
+	switch (req) {
+	case PM_SUSPEND: /* Enter D1-D3 */
+		g_mmc_dev.suspended = 1;
+                break;
+	case PM_RESUME:  /* Enter D0 */
+		if ( g_mmc_dev.suspended ) {
+			g_mmc_dev.suspended = 0;
+			g_mmc_dev.state = 0;     // Clear the old state
+			for ( i = 0 ; i < g_mmc_dev.num_slots ; i++ ) {
+				mmc_eject(i);
+				if ( !g_mmc_dev.sdrive->is_empty(i) )
+					mmc_insert(i);
+			}
+		}
+		break;
+        }
+        return 0;
+}
+
+/******************************************************************
+ * TODO: These would be better handled by driverfs
+ * For the moment, we'll just eject and insert everything
+ ******************************************************************/
+
+int mmc_do_eject(ctl_table *ctl, int write, struct file * filp, void *buffer, size_t *lenp)
+{
+	mmc_eject(0);
+	return 0;
+}
+
+int mmc_do_insert(ctl_table *ctl, int write, struct file * filp, void *buffer, size_t *lenp)
+{
+	mmc_insert(0);
+	return 0;
+}
+
+
+static struct ctl_table mmc_sysctl_table[] =
+{
+	{ 1, "debug", &g_mmc_debug, sizeof(int), 0666, NULL, &proc_dointvec },
+	{ 2, "eject", NULL, 0, 0600, NULL, &mmc_do_eject },
+	{ 3, "insert", NULL, 0, 0600, NULL, &mmc_do_insert },
+	{0}
+};
+
+static struct ctl_table mmc_dir_table[] =
+{
+	{CTL_BUS_MMC, "mmc", NULL, 0, 0555, mmc_sysctl_table},
+	{0}
+};
+
+static struct ctl_table bus_dir_table[] = 
+{
+	{CTL_BUS, "bus", NULL, 0, 0555, mmc_dir_table},
+        {0}
+};
+
+static struct ctl_table_header *mmc_sysctl_header;
+
+static int mmc_proc_read_device(char *page, char **start, off_t off, int count, int *eof, void *data)
+{
+	struct mmc_dev *dev = (struct mmc_dev *)data;
+        char *p = page;
+        int len = 0;
+	int i;
+
+	if (!dev || !dev->sdrive)
+		return 0;
+
+	for ( i = 0 ; i < dev->num_slots ; i++ ) {
+		struct mmc_slot *slot = &dev->slot[i];
+
+		p += sprintf(p, "Slot #%d\n", i);
+		p += sprintf(p, "  State %s (%d)\n", card_state_to_string(slot->state), slot->state);
+
+		if ( slot->state != CARD_STATE_EMPTY ) {
+			p += sprintf(p, "  Media %s\n", (slot->media_driver ? slot->media_driver->name : "unknown"));
+			p += sprintf(p, "  CID mid=%d\n", slot->cid.mid);
+			p += sprintf(p, "      oid=%d\n", slot->cid.oid);
+			p += sprintf(p, "      pnm=%s\n", slot->cid.pnm);
+			p += sprintf(p, "      prv=%d.%d\n", slot->cid.prv>>4, slot->cid.prv&0xf);
+			p += sprintf(p, "      psn=0x%08x\n", slot->cid.psn);
+			p += sprintf(p, "      mdt=%d/%d\n", slot->cid.mdt>>4, (slot->cid.mdt&0xf)+1997);
+
+			p += sprintf(p, "  CSD csd_structure=%d\n", slot->csd.csd_structure);
+			p += sprintf(p, "      spec_vers=%d\n", slot->csd.spec_vers);
+			p += sprintf(p, "      taac=0x%02x\n", slot->csd.taac);
+			p += sprintf(p, "      nsac=0x%02x\n", slot->csd.nsac);
+			p += sprintf(p, "      tran_speed=0x%02x\n", slot->csd.tran_speed);
+			p += sprintf(p, "      ccc=0x%04x\n", slot->csd.ccc);
+			p += sprintf(p, "      read_bl_len=%d\n", slot->csd.read_bl_len);
+			p += sprintf(p, "      read_bl_partial=%d\n", slot->csd.read_bl_partial);
+			p += sprintf(p, "      write_blk_misalign=%d\n", slot->csd.write_blk_misalign);
+			p += sprintf(p, "      read_blk_misalign=%d\n", slot->csd.read_blk_misalign);
+			p += sprintf(p, "      dsr_imp=%d\n", slot->csd.dsr_imp);
+			p += sprintf(p, "      c_size=%d\n", slot->csd.c_size);
+			p += sprintf(p, "      vdd_r_curr_min=%d\n", slot->csd.vdd_r_curr_min);
+			p += sprintf(p, "      vdd_r_curr_max=%d\n", slot->csd.vdd_r_curr_max);
+			p += sprintf(p, "      vdd_w_curr_min=%d\n", slot->csd.vdd_w_curr_min);
+			p += sprintf(p, "      vdd_w_curr_max=%d\n", slot->csd.vdd_w_curr_max);
+			p += sprintf(p, "      c_size_mult=%d\n", slot->csd.c_size_mult);
+			p += sprintf(p, "      wp_grp_size=%d\n", slot->csd.wp_grp_size);
+			p += sprintf(p, "      wp_grp_enable=%d\n", slot->csd.wp_grp_enable);
+			p += sprintf(p, "      default_ecc=%d\n", slot->csd.default_ecc);
+			p += sprintf(p, "      r2w_factor=%d\n", slot->csd.r2w_factor);
+			p += sprintf(p, "      write_bl_len=%d\n", slot->csd.write_bl_len);
+			p += sprintf(p, "      write_bl_partial=%d\n", slot->csd.write_bl_partial);
+			p += sprintf(p, "      file_format_grp=%d\n", slot->csd.file_format_grp);
+			p += sprintf(p, "      copy=%d\n", slot->csd.copy);
+			p += sprintf(p, "      perm_write_protect=%d\n", slot->csd.perm_write_protect);
+			p += sprintf(p, "      tmp_write_protect=%d\n", slot->csd.tmp_write_protect);
+			p += sprintf(p, "      file_format=%d\n", slot->csd.file_format);
+			p += sprintf(p, "      ecc=%d\n", slot->csd.ecc);
+			switch (slot->csd.csd_structure) {
+			case CSD_STRUCT_VER_1_0:
+			case CSD_STRUCT_VER_1_1:
+				p += sprintf(p, "      sector_size=%d\n", slot->csd.erase.v22.sector_size);
+				p += sprintf(p, "      erase_grp_size=%d\n", slot->csd.erase.v22.erase_grp_size);
+				break;
+			case CSD_STRUCT_VER_1_2:
+			default:
+				p += sprintf(p, "      erase_grp_size=%d\n", slot->csd.erase.v31.erase_grp_size);
+				p += sprintf(p, "      erase_grp_mult=%d\n", slot->csd.erase.v31.erase_grp_mult);
+				break;
+			}
+		}
+	}
+
+        len = (p - page) - off;
+	*start = page + off;
+        return len;
+}
+
+/******************************************************************/
+
+void mmc_protocol_single_card( struct mmc_dev *dev, int state_flags );
+extern struct mmc_media_module media_module;
+
+static int __init mmc_init(void) 
+{
+	DEBUG(1,"\n");
+	
+	mmc_sysctl_header = register_sysctl_table(bus_dir_table, 0 );
+
+	tasklet_init(&g_mmc_dev.task,mmc_tasklet_action,(unsigned long)&g_mmc_dev);
+	g_mmc_dev.protocol = mmc_protocol_single_card;
+
+	proc_mmc_dir = proc_mkdir("mmc", proc_bus);
+	if ( proc_mmc_dir )
+		create_proc_read_entry("device", 0, proc_mmc_dir, mmc_proc_read_device, &g_mmc_dev);
+
+	mmc_pm_dev = pm_register(PM_UNKNOWN_DEV, PM_SYS_UNKNOWN, mmc_pm_callback);
+
+	media_module.init();
+	return 0;
+}
+
+static void __exit mmc_exit(void)
+{
+	DEBUG(1,"\n");
+
+	media_module.cleanup();
+
+	unregister_sysctl_table(mmc_sysctl_header);
+
+	tasklet_kill(&g_mmc_dev.task);
+
+	if ( proc_mmc_dir ) {
+		remove_proc_entry("device", proc_mmc_dir);
+		remove_proc_entry("mmc", proc_bus);
+	}
+
+	pm_unregister(mmc_pm_dev);
+}
+
+module_init(mmc_init);
+module_exit(mmc_exit);
+
+MODULE_AUTHOR("Andy Christian");
+MODULE_LICENSE("MIT");
+MODULE_DESCRIPTION("Core support for MultiMedia Cards");
diff -Nur linux-2.4.30/drivers/mmc/mmc_core.h linux-2.4.30-linux-mips.org/drivers/mmc/mmc_core.h
--- linux-2.4.30/drivers/mmc/mmc_core.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mmc/mmc_core.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,79 @@
+/*
+ * Header for MultiMediaCard (MMC)
+ *
+ * Copyright (c) 2002 Hewlett-Packard Company
+ *   
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *  
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *  
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Based strongly on code by:
+ *
+ * Author: Yong-iL Joh <tolkien@mizi.com>
+ * Date  : $Date: 2002/06/18 19:06:06 $ 
+ *
+ * Author:  Andrew Christian
+ *          15 May 2002
+ */
+
+#ifndef MMC_MMC_CORE_H
+#define MMC_MMC_CORE_H
+
+#include <linux/mmc/mmc_ll.h>
+#include "mmc_media.h"
+
+#define ID_TO_RCA(x) ((x)+1)
+
+struct mmc_dev {
+	struct mmc_slot_driver   *sdrive;
+	struct mmc_slot           slot[MMC_MAX_SLOTS];
+	struct mmc_request        request;               // Active request to the low-level driver
+	struct mmc_io_request    *io_request;            // Active transfer request from the high-level media io
+	struct tasklet_struct     task;
+	int    num_slots;                 // Copied from the slot driver; used when slot driver shuts down
+
+	/* State maintenance */
+	int    state;  
+	int    suspended;
+	void (*protocol)(struct mmc_dev *, int);
+};
+
+char * mmc_result_to_string( int );
+int    mmc_unpack_csd( struct mmc_request *request, struct mmc_csd *csd );
+int    mmc_unpack_r1( struct mmc_request *request, struct mmc_response_r1 *r1, enum card_state state );
+int    mmc_unpack_cid( struct mmc_request *request, struct mmc_cid *cid );
+int    mmc_unpack_r3( struct mmc_request *request, struct mmc_response_r3 *r3 );
+
+void   mmc_send_cmd( struct mmc_dev *dev, int cmd, u32 arg, 
+		     u16 nob, u16 block_len, enum mmc_rsp_t rtype );
+void   mmc_finish_io_request( struct mmc_dev *dev, int result );
+int    mmc_check_eject( struct mmc_dev *dev );
+int    mmc_check_insert( struct mmc_dev *dev );
+u32    mmc_tran_speed( u8 ts );
+int    mmc_match_media_driver( struct mmc_slot *slot );
+void   run_sbin_mmc_hotplug(struct mmc_dev *dev, int id, int insert);
+
+static inline void mmc_simple_cmd( struct mmc_dev *dev, int cmd, u32 arg, enum mmc_rsp_t rtype )
+{
+	mmc_send_cmd( dev, cmd, arg, 0, 0, rtype );
+}
+
+#endif  /* MMC_MMC_CORE_H */
+
diff -Nur linux-2.4.30/drivers/mmc/mmc_media.c linux-2.4.30-linux-mips.org/drivers/mmc/mmc_media.c
--- linux-2.4.30/drivers/mmc/mmc_media.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mmc/mmc_media.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,530 @@
+/*
+ * Block driver for media (i.e., flash cards)
+ *
+ * Copyright 2002 Hewlett-Packard Company
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *  
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *  
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Author:  Andrew Christian
+ *          28 May 2002
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/sched.h>
+#include <linux/kernel.h> /* printk() */
+#include <linux/fs.h>     /* everything... */
+#include <linux/errno.h>  /* error codes */
+#include <linux/types.h>  /* size_t */
+#include <linux/fcntl.h>  /* O_ACCMODE */
+#include <linux/hdreg.h>  /* HDIO_GETGEO */
+#include <linux/init.h>
+#include <linux/devfs_fs_kernel.h>
+
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include "mmc_media.h"
+
+#define MAJOR_NR mmc_major /* force definitions on in blk.h */
+static int mmc_major; /* must be declared before including blk.h */
+
+#define MMC_SHIFT           3             /* max 8 partitions per card */
+
+#define DEVICE_NR(device)   (MINOR(device)>>MMC_SHIFT)
+#define DEVICE_NAME         "mmc"         /* name for messaging */
+#define DEVICE_INTR         mmc_intrptr   /* pointer to the bottom half */
+#define DEVICE_NO_RANDOM                  /* no entropy to contribute */
+#define DEVICE_REQUEST      mmc_media_request
+#define DEVICE_OFF(d) /* do-nothing */
+
+#include <linux/blk.h>
+#include <linux/blkpg.h>
+
+static int rahead     = 8;
+static int maxsectors = 4;
+
+MODULE_PARM(maxsectors,"i");
+MODULE_PARM_DESC(maxsectors,"Maximum number of sectors for a single request");
+MODULE_PARM(rahead,"i");
+MODULE_PARM_DESC(rahead,"Default sector read ahead");
+
+#define MMC_NDISK	(MMC_MAX_SLOTS << MMC_SHIFT)
+
+/* 
+   Don't bother messing with blksize_size....it gets changed by various filesystems.
+   You're better off dealing with arbitrary blksize's
+*/ 
+static int              mmc_blk[MMC_NDISK];  /* Used for hardsect_size - should be 512 bytes */
+static int              mmc_max[MMC_NDISK];  /* Used for max_sectors[] - limit size of individual request */
+
+static int              mmc_sizes[MMC_NDISK];        /* Used in gendisk - gives whole size of partition */
+static struct hd_struct mmc_partitions[MMC_NDISK];   /* Used in gendisk - gives particular partition information */
+
+static char             mmc_gendisk_flags;
+#ifdef CONFIG_DEVFS_FS
+static devfs_handle_t   mmc_devfs_handle;
+#endif
+
+// There is one mmc_media_dev per inserted card
+struct mmc_media_dev {
+	int              usage;
+	struct mmc_slot *slot;
+	spinlock_t       lock;
+	int              changed;
+	long             nr_sects;   // In total number of sectors
+
+	int              read_block_len;      // Valid read block length
+	int              write_block_len;     // Valid write block length
+};
+
+static struct mmc_media_dev   g_media_dev[MMC_MAX_SLOTS];
+static struct mmc_io_request  g_io_request;
+static int                    g_busy;
+
+static struct gendisk mmc_gendisk = {
+        major:	        0,               /* major number dynamically assigned */
+	major_name:	DEVICE_NAME,
+	minor_shift:	MMC_SHIFT,	 /* shift to get device number */
+	max_p:	        1 << MMC_SHIFT,	 /* Number of partiions */
+	/* The remainder will be filled in dynamically */
+};
+
+/*************************************************************************/
+/* TODO: O_EXCL, O_NDELAY, invalidate_buffers */
+static int mmc_media_open( struct inode *inode, struct file *filp )
+{
+	struct mmc_media_dev *dev;
+	int num = DEVICE_NR(inode->i_rdev);
+
+	DEBUG(1,": num=%d\n", num);
+
+	if ( num >= MMC_MAX_SLOTS) 
+		return -ENODEV;
+
+	dev = &g_media_dev[num];
+	if ( !dev->slot ) 
+		return -ENODEV;
+
+	spin_lock(&dev->lock);
+	if (!dev->usage)
+		check_disk_change(inode->i_rdev);
+	dev->usage++;
+	MOD_INC_USE_COUNT;
+	spin_unlock(&dev->lock);
+	return 0;
+}
+
+static int mmc_media_release( struct inode *inode, struct file *filep )
+{
+	struct mmc_media_dev *dev = &g_media_dev[DEVICE_NR(inode->i_rdev)];
+
+	DEBUG(1,": num=%d\n", DEVICE_NR(inode->i_rdev));
+
+	spin_lock(&dev->lock);
+	dev->usage--;
+	/* Is this worth doing? */
+	if (!dev->usage) {	
+		fsync_dev(inode->i_rdev);
+		invalidate_buffers(inode->i_rdev);
+	}
+	MOD_DEC_USE_COUNT;
+	spin_unlock(&dev->lock);
+	return 0;
+}
+
+static int mmc_media_revalidate(kdev_t i_rdev)
+{
+	int index, max_p, start, i;
+	struct mmc_media_dev *dev;
+
+	index = DEVICE_NR(i_rdev);
+	DEBUG(2,": index=%d\n", index);
+
+	max_p = mmc_gendisk.max_p;
+	start = index << MMC_SHIFT;
+	dev   = &g_media_dev[index];
+
+	for ( i = max_p - 1 ; i >= 0 ; i-- ) {
+		int item = start + i;
+		invalidate_device(MKDEV(mmc_major,item),1);
+		mmc_gendisk.part[item].start_sect = 0;
+		mmc_gendisk.part[item].nr_sects   = 0;
+		/* TODO: Fix the blocksize? */
+	}
+
+	register_disk(&mmc_gendisk, i_rdev, 1 << MMC_SHIFT, mmc_gendisk.fops, dev->nr_sects);
+	return 0;
+}
+
+static int mmc_media_ioctl (struct inode *inode, struct file *filp,
+			    unsigned int cmd, unsigned long arg)
+{
+	int num = DEVICE_NR(inode->i_rdev);
+	int size;
+	struct hd_geometry geo;
+
+	DEBUG(1," ioctl 0x%x 0x%lx\n", cmd, arg);
+
+	switch(cmd) {
+	case BLKGETSIZE:
+		/* Return the device size, expressed in sectors */
+		/* Not really necessary, but this is faster than walking the gendisk list */
+		if (!access_ok(VERIFY_WRITE, arg, sizeof(long)))
+			return -EFAULT;
+		return put_user(mmc_partitions[MINOR(inode->i_rdev)].nr_sects, (long *)arg);
+
+	case BLKRRPART: /* re-read partition table */
+		if (!capable(CAP_SYS_ADMIN)) 
+			return -EACCES;
+		return mmc_media_revalidate(inode->i_rdev);
+
+	case HDIO_GETGEO:
+		if (!access_ok(VERIFY_WRITE, arg, sizeof(geo)))
+			return -EFAULT;
+		/* Grab the size from the 0 partition for this minor */
+		/* TODO: is this the right thing?  Or should we do it by partition??? */
+		size = mmc_sizes[num << MMC_SHIFT] / mmc_blk[num << MMC_SHIFT];
+		geo.cylinders = (size & ~0x3f) >> 6;
+		geo.heads     = 4;
+		geo.sectors   = 16;
+		geo.start     = mmc_partitions[MINOR(inode->i_rdev)].start_sect;
+		if (copy_to_user((void *) arg, &geo, sizeof(geo)))
+			return -EFAULT;
+		return 0;
+
+	default:
+		return blk_ioctl(inode->i_rdev, cmd, arg);
+	}
+
+	return -ENOTTY; /* should never get here */
+}
+
+static int mmc_media_check_change(kdev_t i_rdev) 
+{
+	int                   index, retval;
+	struct mmc_media_dev *dev;
+	unsigned long         flags;
+
+	index = DEVICE_NR(i_rdev);
+	DEBUG(2," device=%d\n", index);
+	if (index >= MMC_MAX_SLOTS) 
+		return 0;
+
+	dev = &g_media_dev[index];
+
+	spin_lock_irqsave(&dev->lock, flags);
+	retval = (dev->changed ? 1 : 0);
+	dev->changed = 0;
+	spin_unlock_irqrestore(&dev->lock, flags);
+
+	return retval;
+}
+
+static struct mmc_media_dev * mmc_media_locate_device(const struct request *req)
+{
+	int num = DEVICE_NR(req->rq_dev);
+	if ( num >= MMC_MAX_SLOTS) {
+		static int count = 0;
+		if (count++ < 5) /* print the message at most five times */
+			printk(KERN_WARNING "mmc: request for unknown device\n");
+		return NULL;
+	}
+	return &g_media_dev[num];
+}
+
+static int mmc_media_transfer( struct mmc_media_dev *dev, const struct request *req )
+{
+	int minor = MINOR(req->rq_dev);
+	unsigned long flags;
+
+	DEBUG(2,": minor=%d\n", minor);
+
+	if ( req->sector + req->current_nr_sectors > mmc_partitions[minor].nr_sects ) {
+		static int count = 0;
+		if (count++ < 5)
+			printk("%s%s: request past end of partition\n", KERN_WARNING, __FUNCTION__);
+		return 0;
+	}
+	
+	spin_lock_irqsave(&dev->lock, flags);
+
+	g_io_request.id         = DEVICE_NR(req->rq_dev);
+	g_io_request.cmd        = req->cmd;
+	g_io_request.sector     = mmc_partitions[minor].start_sect + req->sector;
+	g_io_request.nr_sectors = req->current_nr_sectors;
+	g_io_request.block_len  = mmc_blk[minor];
+	g_io_request.buffer     = req->buffer;
+
+	DEBUG(2,": id=%d cmd=%d sector=%ld nr_sectors=%ld block_len=%ld buf=%p\n",
+	      g_io_request.id, g_io_request.cmd, g_io_request.sector, g_io_request.nr_sectors,
+	      g_io_request.block_len, g_io_request.buffer );
+
+	mmc_handle_io_request(&g_io_request);
+	spin_unlock_irqrestore(&dev->lock, flags);
+	return 1;
+}
+
+static void mmc_media_request( request_queue_t *q )
+{
+	struct mmc_media_dev *dev;
+
+	if ( g_busy )
+		return;
+
+	while(1) {
+		INIT_REQUEST;  /* returns when queue is empty */
+		dev = mmc_media_locate_device(CURRENT);
+		if ( !dev ) {
+			end_request(0);
+			continue;
+		}
+
+		DEBUG(2," (%p): cmd %i sec %li (nr. %li)\n", CURRENT,
+		      CURRENT->cmd, CURRENT->sector, CURRENT->current_nr_sectors);
+
+		if ( mmc_media_transfer(dev,CURRENT) ) {
+			g_busy = 1;
+			return;
+		}
+		end_request(0);  /* There was a problem with the request */
+	}
+}
+
+static void mmc_media_transfer_done( struct mmc_io_request *trans, int result )
+{
+	unsigned long flags;
+	DEBUG(3,": result=%d\n", result);
+	spin_lock_irqsave(&io_request_lock, flags);
+	end_request(result);
+	g_busy = 0;
+	if (!QUEUE_EMPTY)
+		mmc_media_request(NULL);  // Start the next transfer
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+
+static struct block_device_operations mmc_bdops = {
+	open:               mmc_media_open,
+	release:            mmc_media_release,
+	ioctl:              mmc_media_ioctl,
+	check_media_change: mmc_media_check_change,
+	revalidate:         mmc_media_revalidate
+};
+
+/******************************************************************/
+/* TODO:
+   We have a race condition if two slots need to be revalidated at the same
+   time.  Perhaps we should walk the list of devices and look for change
+   flags?
+*/
+
+static void mmc_media_load_task_handler( void *nr )
+{
+	int slot_id = (int) nr;
+	DEBUG(2," slot_id=%d\n", slot_id );
+	mmc_media_revalidate(MKDEV(mmc_major,(slot_id<<MMC_SHIFT)));
+}
+
+static struct tq_struct mmc_media_load_task = {
+	routine:  mmc_media_load_task_handler
+};
+
+static void mmc_media_load( struct mmc_slot *slot )
+{
+	unsigned long flags;
+	struct mmc_media_dev *dev  = &g_media_dev[slot->id];
+	int i;
+
+	long nr_sects;
+	int  write_block_len;
+	int  read_block_len;
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+	nr_sects        = (1 + slot->csd.c_size) * (1 << (slot->csd.c_size_mult + 2));
+	write_block_len = 1 << slot->csd.write_bl_len;
+	read_block_len  = 1 << slot->csd.read_bl_len;
+
+	MOD_INC_USE_COUNT;
+	DEBUG(1, " slot=%p nr_sect=%ld write_block_length=%d read_block_len=%d\n", 
+	      slot, nr_sects, write_block_len, read_block_len );
+
+	dev->slot            = slot;
+	dev->nr_sects        = nr_sects;
+	dev->read_block_len  = read_block_len;
+	dev->write_block_len = write_block_len;
+	dev->changed         = 1;
+	mmc_gendisk.nr_real++;
+
+	/* Fix up the block size to match read_block_len */
+	/* TODO: can we really do this?  Right now we're affecting blksize_size and hardsect_size */
+	for ( i = 0 ; i < (1 << MMC_SHIFT) ; i++ )
+		mmc_blk[(slot->id << MMC_SHIFT) + i] = read_block_len;
+
+	mmc_media_load_task.data = (void *) slot->id;
+	schedule_task( &mmc_media_load_task );
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/* TODO: This is a problem area.  We've lost our card, so we'd like
+   to flush all outstanding buffers and requests, remove the partitions from
+   the file system, and generally shut everything down.
+*/
+
+static void mmc_media_unload( struct mmc_slot *slot )
+{
+	unsigned long flags;
+	struct mmc_media_dev *dev = &g_media_dev[slot->id];
+
+	spin_lock_irqsave(&dev->lock, flags);
+
+//	for ( i = 0 ; i < MMC_SHIFT ; i++ )
+//		fsync_dev(MKDEV(mmc_major,slot->id,i));
+
+	MOD_DEC_USE_COUNT;
+	DEBUG(1," slot=%p id=%d\n", slot, slot->id);
+
+	dev->slot            = NULL;
+	dev->nr_sects        = 0;
+	dev->changed         = 1;
+	mmc_gendisk.nr_real--;
+
+	mmc_media_load_task.data = (void *) slot->id;
+	schedule_task( &mmc_media_load_task );
+
+	spin_unlock_irqrestore(&dev->lock, flags);
+}
+
+/* 
+   Called once the device has a valid CSD structure
+   In the future this should determine what type of card we have
+   For the moment, everything is a memory card 
+*/
+
+static int mmc_media_probe( struct mmc_slot *slot )
+{
+	return 1;
+}
+
+static struct mmc_media_driver mmc_driver = {
+	name:            "flash",
+	load:            mmc_media_load,
+	unload:          mmc_media_unload,
+	probe:           mmc_media_probe,
+	io_request_done: mmc_media_transfer_done,
+};
+
+/******************************************************************/
+
+static int __init mmc_media_init( void )
+{
+	int i, result;
+	DEBUG(0,"\n");
+
+#ifdef CONFIG_DEVFS_FS
+	/* Get handle - possible NULL return if devfs not configured */
+	mmc_devfs_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL);
+	if (!mmc_devfs_handle) {
+		DEBUG(0,": devfs_mk_dir failed\n");
+		return -EBUSY;
+	}
+
+	result = devfs_register_blkdev(mmc_major, DEVICE_NAME, &mmc_bdops);
+#else
+	result = register_blkdev(mmc_major, DEVICE_NAME, &mmc_bdops);
+#endif
+	if (result < 0) {
+		printk(KERN_WARNING "Unable to get major %d for MMC media.\n", mmc_major);
+		return result;
+	}
+
+	if ( !mmc_major ) mmc_major = result;
+	DEBUG(0,": mmc_major=%d\n", mmc_major);
+
+	/* Set up global block arrays */
+	read_ahead[mmc_major]    = rahead;
+	for(i=0 ; i < MMC_NDISK; i++)
+		mmc_blk[i] = 512;
+	hardsect_size[mmc_major] = mmc_blk;
+	for(i=0; i < MMC_NDISK; i++)
+		mmc_max[i] = maxsectors;
+	max_sectors[mmc_major]   = mmc_max;
+
+	/* Start with zero-sized partitions : we'll fix this later */
+	memset(mmc_sizes, 0, sizeof(int) * MMC_NDISK);
+	blk_size[mmc_major] = mmc_sizes;
+
+	/* Fix up the gendisk structure */
+	mmc_gendisk.part    = mmc_partitions;
+	mmc_gendisk.sizes   = mmc_sizes;
+	mmc_gendisk.nr_real = 0;
+#ifdef CONFIG_DEVFS_FS
+	mmc_gendisk.de_arr  = &mmc_devfs_handle;
+#else
+	mmc_gendisk.de_arr  = NULL;
+#endif
+	mmc_gendisk.flags   = &mmc_gendisk_flags;
+	mmc_gendisk.fops    = &mmc_bdops;
+
+	/* Add ourselves to the global list */
+	mmc_gendisk.major = mmc_major;
+	add_gendisk(&mmc_gendisk);
+	
+	blk_init_queue(BLK_DEFAULT_QUEUE(mmc_major), DEVICE_REQUEST);
+	return mmc_register_media_driver(&mmc_driver);
+}
+
+static void mmc_media_cleanup( void )
+{
+	int i;
+	DEBUG(0,"\n");
+
+	flush_scheduled_tasks();
+	unregister_blkdev(mmc_major, DEVICE_NAME);
+
+	for ( i = 0 ; i < MMC_NDISK; i++ )
+		fsync_dev(MKDEV(mmc_major,i));
+
+	mmc_unregister_media_driver(&mmc_driver);
+
+	blk_cleanup_queue(BLK_DEFAULT_QUEUE(mmc_major));
+
+	blk_size[mmc_major]      = NULL;
+	hardsect_size[mmc_major] = NULL;
+	max_sectors[mmc_major]   = NULL;
+
+	del_gendisk(&mmc_gendisk);
+
+#ifdef CONFIG_DEVFS_FS
+	devfs_unregister(mmc_devfs_handle);
+#else
+	unregister_blkdev( mmc_major, DEVICE_NAME );
+#endif
+}
+
+struct mmc_media_module media_module = {
+	init:    mmc_media_init,
+	cleanup: mmc_media_cleanup
+};
diff -Nur linux-2.4.30/drivers/mmc/mmc_media.h linux-2.4.30-linux-mips.org/drivers/mmc/mmc_media.h
--- linux-2.4.30/drivers/mmc/mmc_media.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mmc/mmc_media.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,96 @@
+/*
+ * Header for MultiMediaCard (MMC)
+ *
+ * Copyright (c) 2002 Hewlett-Packard Company
+ *   
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *  
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *  
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Based strongly on code by:
+ *
+ * Author: Yong-iL Joh <tolkien@mizi.com>
+ * Date  : $Date: 2002/06/18 12:38:40 $ 
+ *
+ * Author:  Andrew Christian
+ *          15 May 2002
+ */
+
+#ifndef MMC_MMC_MEDIA_H
+#define MMC_MMC_MEDIA_H
+
+#include <linux/interrupt.h>
+#include <linux/list.h>
+
+#include <linux/mmc/mmc_protocol.h>
+
+/* Set an upper bound for how many cards we'll support */
+/* This is used only for static array initialization */
+#define MMC_MAX_SLOTS   2
+
+#define MMC_SLOT_FLAG_INSERT  (1<<0)
+#define MMC_SLOT_FLAG_EJECT   (1<<1)
+
+struct mmc_media_driver;
+
+struct mmc_slot {
+	int             id;     /* Card index */
+	/* Card specific information */
+	struct mmc_cid  cid;
+	struct mmc_csd  csd;
+
+	enum card_state state;  /* empty, ident, ready, whatever */
+	int             flags;  /* Ejected, inserted */
+
+	/* Assigned media driver */
+	struct mmc_media_driver *media_driver;
+};
+
+struct mmc_io_request {
+	int            id;         /* Card index     */
+	int            cmd;        /* READ or WRITE  */
+	unsigned long  sector;     /* Start address  */
+	unsigned long  nr_sectors; /* Length of read */
+	unsigned long  block_len;  /* Size of sector (sanity check) */
+	char          *buffer;     /* Data buffer    */
+};
+
+/* Media driver (e.g., Flash card, I/O card...) */
+struct mmc_media_driver {
+	struct list_head   node;
+	char              *name;
+	void (*load)(struct mmc_slot *);
+	void (*unload)(struct mmc_slot *);
+	int  (*probe)(struct mmc_slot *);
+	void (*io_request_done)(struct mmc_io_request *, int result);
+};
+
+struct mmc_media_module {
+	int (*init)(void);
+	void (*cleanup)(void);
+};
+
+/* Calls made by the media driver */
+extern int  mmc_register_media_driver( struct mmc_media_driver * );
+extern void mmc_unregister_media_driver( struct mmc_media_driver * );
+extern void mmc_handle_io_request( struct mmc_io_request * );
+
+#endif  /* MMC_MMC_MEDIA_H */
+
diff -Nur linux-2.4.30/drivers/mmc/mmc_protocol.c linux-2.4.30-linux-mips.org/drivers/mmc/mmc_protocol.c
--- linux-2.4.30/drivers/mmc/mmc_protocol.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mmc/mmc_protocol.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,440 @@
+/*
+ * MMC State machine functions
+ *
+ * Copyright (c) 2002 Hewlett-Packard Company
+ *   
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *  
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *  
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * This part of the code is separated from mmc_core.o so we can
+ * plug in different state machines (e.g., SPI, SD)
+ *
+ * This code assumes that you have exactly one card slot, no more.
+ *
+ * Author:  Andrew Christian
+ *          6 May 2002
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+
+#include <linux/version.h>
+#include <linux/proc_fs.h>
+
+#include "mmc_core.h"
+
+static void * mmc_cim_default_state( struct mmc_dev *dev, int first );
+
+/******************************************************************
+ *
+ * Useful utility functions
+ *
+ ******************************************************************/
+
+static int mmc_has_valid_request( struct mmc_dev *dev )
+{
+	struct mmc_io_request *request = dev->io_request;
+	struct mmc_slot *slot;
+
+	DEBUG(2," (%p)\n", request);
+
+	if ( !request ) return 0;
+
+	slot = dev->slot + request->id;
+
+	if ( !slot->media_driver ) {
+		DEBUG(0,": card doesn't have media driver\n");
+		return 0;
+	}
+
+	return 1;
+}
+
+static void mmc_configure_card( struct mmc_dev *dev, int slot )
+{
+	u32 rate;
+	DEBUG(2,": slot=%d\n", slot);
+
+	/* Fix the clock rate */
+	rate = mmc_tran_speed(dev->slot[slot].csd.tran_speed);
+	if ( rate < MMC_CLOCK_SLOW )
+		rate = MMC_CLOCK_SLOW;
+	if ( rate > MMC_CLOCK_FAST )
+		rate = MMC_CLOCK_FAST;
+
+	dev->sdrive->set_clock(slot, rate);
+	
+	/* Match the drive media */
+	mmc_match_media_driver(&dev->slot[slot]);
+	run_sbin_mmc_hotplug(dev, slot, 1);
+}
+
+/* The blocks requested by the kernel may or may not
+   match what we can do.  Unfortunately, filesystems play
+   fast and loose with block sizes, so we're stuck with this */
+
+static void mmc_fix_request_block_size( struct mmc_dev *dev )
+{
+	struct mmc_io_request *t = dev->io_request;
+	struct mmc_slot *slot = dev->slot + t->id;
+	u16 block_len;
+
+	DEBUG(1, ": io_request id=%d cmd=%d sector=%ld nr_sectors=%ld block_len=%ld buf=%p\n",
+	      t->id, t->cmd, t->sector, t->nr_sectors, t->block_len, t->buffer);
+
+	switch( t->cmd ) {
+	case READ:
+		block_len = 1 << slot->csd.read_bl_len;
+		break;
+	case WRITE:
+		block_len = 1 << slot->csd.write_bl_len;
+		break;
+	default:
+		DEBUG(0,": unrecognized command %d\n", t->cmd);
+		return;
+	}
+
+	if ( block_len < t->block_len ) {
+		int scale = t->block_len / block_len;
+		DEBUG(1,": scaling by %d from block_len=%d to %ld\n", 
+		      scale, block_len, t->block_len);
+		t->block_len   = block_len;
+		t->sector     *= scale;
+		t->nr_sectors *= scale;
+	}
+}
+
+
+/******************************************************************
+ * State machine routines to read and write data
+ *
+ *  SET_BLOCKLEN only needs to be done once for each card.
+ *  SET_BLOCK_COUNT is only valid in MMC 3.1; most cards don't support this,
+ *  so we don't use it.
+ * 
+ *  In the 2.x cards we have a choice between STREAMING mode and
+ *  SINGLE mode.  There's an obvious performance possibility in 
+ *  using streaming mode, but at this time we're just using the SINGLE
+ *  mode.
+ ******************************************************************/
+
+static void * mmc_cim_read_write_block( struct mmc_dev *dev, int first )
+{
+	struct mmc_io_request *t = dev->io_request;
+	struct mmc_response_r1 r1;
+	struct mmc_slot *slot = dev->slot + t->id;
+	int    retval = 0;
+	int    i;
+
+	DEBUG(2," first=%d\n",first);
+
+	if ( first ) {
+		mmc_fix_request_block_size( dev );
+
+		switch ( slot->state ) {
+		case CARD_STATE_STBY:
+			mmc_simple_cmd(dev, MMC_SELECT_CARD, ID_TO_RCA(slot->id) << 16, RESPONSE_R1B );
+			break;
+		case CARD_STATE_TRAN:
+			mmc_simple_cmd(dev, MMC_SET_BLOCKLEN, t->block_len, RESPONSE_R1 );
+			break;
+		default:
+			DEBUG(0,": invalid card state %d\n", slot->state);
+			goto read_block_error;
+			break;
+		}
+		return NULL;
+	}
+
+	switch (dev->request.cmd) {
+	case MMC_SELECT_CARD:
+		if ( (retval = mmc_unpack_r1( &dev->request, &r1, slot->state )) )
+			goto read_block_error;
+
+		for ( i = 0 ; i < dev->num_slots ; i++ )
+			dev->slot[i].state = ( i == t->id ? CARD_STATE_TRAN : CARD_STATE_STBY );
+
+		mmc_simple_cmd(dev, MMC_SET_BLOCKLEN, t->block_len, RESPONSE_R1 );
+		break;
+
+	case MMC_SET_BLOCKLEN:
+		if ( (retval = mmc_unpack_r1( &dev->request, &r1, slot->state )) )
+			goto read_block_error;
+
+		mmc_send_cmd(dev, (t->cmd == READ ? MMC_READ_SINGLE_BLOCK : MMC_WRITE_BLOCK), 
+			     t->sector * t->block_len, 1, t->block_len, RESPONSE_R1 );
+		break;
+
+	case MMC_READ_SINGLE_BLOCK:
+	case MMC_WRITE_BLOCK:
+		if ( (retval = mmc_unpack_r1( &dev->request, &r1, slot->state )) )
+			goto read_block_error;
+
+		t->nr_sectors--;
+		t->sector++;
+		t->buffer += t->block_len;
+
+		if ( t->nr_sectors ) {
+			mmc_send_cmd(dev, (t->cmd == READ ? MMC_READ_SINGLE_BLOCK : MMC_WRITE_BLOCK), 
+				     t->sector * t->block_len, 1, t->block_len, RESPONSE_R1 );
+		}
+		else {
+			mmc_finish_io_request( dev, 1 );
+			if ( mmc_has_valid_request(dev) )
+				return mmc_cim_read_write_block;
+			return mmc_cim_default_state;
+		}
+		break;
+
+	default:
+		goto read_block_error;
+		break;
+	}
+	return NULL;
+
+read_block_error:
+	DEBUG(0,": failure during cmd %d, error %d (%s)\n", 
+	      dev->request.cmd, retval, mmc_result_to_string(retval));
+	mmc_finish_io_request( dev, 0 );   // Failure
+	return mmc_cim_default_state;
+}
+
+/* Update the card's status information in preparation to running a read/write cycle */
+
+static void * mmc_cim_get_status( struct mmc_dev *dev, int first )
+{
+	struct mmc_slot *slot = dev->slot + dev->io_request->id;
+	struct mmc_response_r1 r1;
+	int retval = MMC_NO_ERROR;
+
+	DEBUG(2," first=%d\n",first);
+
+	if ( first ) {
+		mmc_simple_cmd(dev, MMC_SEND_STATUS, ID_TO_RCA(slot->id) << 16, RESPONSE_R1 );
+		return NULL;
+	}
+
+	switch (dev->request.cmd) {
+	case MMC_SEND_STATUS:
+		retval = mmc_unpack_r1(&dev->request,&r1,slot->state);
+		if ( !retval || retval == MMC_ERROR_STATE_MISMATCH ) {
+			slot->state = R1_CURRENT_STATE(r1.status);
+			return mmc_cim_read_write_block;
+		}
+		break;
+
+	default:
+		break;
+	}
+
+	DEBUG(0, ": failure during cmd %d, error=%d (%s)\n", dev->request.cmd,
+	      retval, mmc_result_to_string(retval));
+	mmc_finish_io_request(dev,0);
+	return mmc_cim_default_state;
+}
+
+static void * mmc_cim_handle_request( struct mmc_dev *dev, int first )
+{
+	DEBUG(2," first=%d\n",first);
+
+	if ( !first && !mmc_has_valid_request(dev)) {
+		DEBUG(0, ": invalid request\n");
+		mmc_finish_io_request(dev,0);
+		return mmc_cim_default_state;
+	}
+
+	if ( first )
+		return mmc_cim_get_status;
+
+	return mmc_cim_read_write_block;
+}
+
+/******************************************************************
+ *
+ * State machine routines to initialize card(s)
+ *
+ ******************************************************************/
+
+/*
+  CIM_SINGLE_CARD_ACQ  (frequency at 400 kHz)
+  --- Must enter from GO_IDLE_STATE ---
+
+  1. SEND_OP_COND (Full Range) [CMD1]   {optional}
+  2. SEND_OP_COND (Set Range ) [CMD1]
+     If busy, delay and repeat step 2
+  3. ALL_SEND_CID              [CMD2]
+     If timeout, set an error (no cards found)
+  4. SET_RELATIVE_ADDR         [CMD3]
+  5. SEND_CSD                  [CMD9]
+  6. SET_DSR                   [CMD4]    Only call this if (csd.dsr_imp).
+  7. Set clock frequency (check available in csd.tran_speed)
+ */
+
+static void * mmc_cim_single_card_acq( struct mmc_dev *dev, int first )
+{
+	struct mmc_response_r3 r3;
+	struct mmc_response_r1 r1;
+	struct mmc_slot *slot = dev->slot;     /* Must be slot 0 */
+	int retval;
+
+	DEBUG(2,"\n");
+
+	if ( first ) {
+		mmc_simple_cmd(dev, MMC_GO_IDLE_STATE, 0, RESPONSE_NONE);
+		return NULL;
+	}
+
+	switch (dev->request.cmd) {
+	case MMC_GO_IDLE_STATE: /* No response to parse */
+		if ( (dev->sdrive->flags & MMC_SDFLAG_VOLTAGE ))
+			DEBUG(0,": error - current driver doesn't do OCR\n");
+		mmc_simple_cmd(dev, MMC_SEND_OP_COND, dev->sdrive->ocr, RESPONSE_R3);
+		break;
+
+	case MMC_SEND_OP_COND:
+		retval = mmc_unpack_r3(&dev->request, &r3);
+		if ( retval ) {
+			DEBUG(0,": failed SEND_OP_COND error=%d (%s) - could be SD card\n", 
+			      retval, mmc_result_to_string(retval));
+			return mmc_cim_default_state;
+		}
+
+		DEBUG(2,": read ocr value = 0x%08x\n", r3.ocr);
+		if (!(r3.ocr & MMC_CARD_BUSY)) {
+			mmc_simple_cmd(dev, MMC_SEND_OP_COND, dev->sdrive->ocr, RESPONSE_R3);
+		}
+		else {
+			slot->state = CARD_STATE_READY;
+			mmc_simple_cmd(dev, MMC_ALL_SEND_CID, 0, RESPONSE_R2_CID);
+		}
+		break;
+		
+	case MMC_ALL_SEND_CID: 
+		retval = mmc_unpack_cid( &dev->request, &slot->cid );
+		if ( retval ) {
+			DEBUG(0,": unable to ALL_SEND_CID error=%d (%s)\n", 
+			      retval, mmc_result_to_string(retval));
+			return mmc_cim_default_state;
+		}
+		slot->state = CARD_STATE_IDENT;
+		mmc_simple_cmd(dev, MMC_SET_RELATIVE_ADDR, ID_TO_RCA(slot->id) << 16, RESPONSE_R1);
+		break;
+
+        case MMC_SET_RELATIVE_ADDR:
+		retval = mmc_unpack_r1(&dev->request,&r1,slot->state);
+		if ( retval ) {
+			DEBUG(0, ": unable to SET_RELATIVE_ADDR error=%d (%s)\n", 
+			      retval, mmc_result_to_string(retval));
+			return mmc_cim_default_state;
+		}
+		slot->state = CARD_STATE_STBY;
+		mmc_simple_cmd(dev, MMC_SEND_CSD, ID_TO_RCA(slot->id) << 16, RESPONSE_R2_CSD);
+		break;
+
+	case MMC_SEND_CSD:
+		retval = mmc_unpack_csd(&dev->request, &slot->csd);
+		if ( retval ) {
+			DEBUG(0, ": unable to SEND_CSD error=%d (%s)\n", 
+			      retval, mmc_result_to_string(retval));
+			return mmc_cim_default_state;
+		}
+		if ( slot->csd.dsr_imp ) {
+			DEBUG(0, ": driver doesn't support setting DSR\n");
+				// mmc_simple_cmd(dev, MMC_SET_DSR, 0, RESPONSE_NONE);
+		}
+		mmc_configure_card( dev, 0 );
+		return mmc_cim_default_state;
+
+	default:
+		DEBUG(0, ": error!  Illegal last cmd %d\n", dev->request.cmd);
+		return mmc_cim_default_state;
+	}
+	return NULL;
+}
+
+/*
+  CIM_INIT_STACK       (frequency at 400 kHz)
+
+  1. GO_IDLE_STATE (CMD0)
+  2. Do CIM_SINGLE_CARD_ACQ
+*/
+
+static void * mmc_cim_init_stack( struct mmc_dev *dev, int first )
+{
+	DEBUG(2,"\n");
+
+	if ( first ) {
+		mmc_simple_cmd(dev, MMC_CIM_RESET, 0, RESPONSE_NONE);
+		return NULL;
+	}
+
+	switch (dev->request.cmd) {
+	case MMC_CIM_RESET:
+		if ( dev->slot[0].state == CARD_STATE_EMPTY )
+			return mmc_cim_default_state;
+
+		dev->slot[0].state = CARD_STATE_IDLE;
+		return mmc_cim_single_card_acq;
+
+	default:
+		DEBUG(0,": invalid state %d\n", dev->request.cmd);
+		break;
+	}
+
+	return NULL;
+}
+
+/******************************************************************
+ *  Default state - start here
+ ******************************************************************/
+
+static void * mmc_cim_default_state( struct mmc_dev *dev, int first )
+{
+	DEBUG(2,"\n");
+
+	mmc_check_eject(dev);
+
+	if (mmc_check_insert(dev))
+		return mmc_cim_init_stack;
+	else if (mmc_has_valid_request(dev))
+		return mmc_cim_handle_request;
+
+	return NULL;
+}
+
+
+/******************************************************************
+ *  State function handler
+ ******************************************************************/
+
+typedef void *(*state_func_t)(struct mmc_dev *, int);
+static state_func_t g_single_card = &mmc_cim_default_state;
+
+void mmc_protocol_single_card( struct mmc_dev *dev, int state_flags )
+{
+	state_func_t    sf;
+
+	sf = g_single_card(dev,0);
+	while ( sf ) {
+		g_single_card = sf;
+		sf = g_single_card(dev,1);
+	}
+}
diff -Nur linux-2.4.30/drivers/mtd/afs.c linux-2.4.30-linux-mips.org/drivers/mtd/afs.c
--- linux-2.4.30/drivers/mtd/afs.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/afs.c	2005-05-12 18:01:29.000000000 +0200
@@ -21,7 +21,7 @@
    This is access code for flashes using ARM's flash partitioning 
    standards.
 
-   $Id: afs.c,v 1.8 2002/05/04 08:49:09 rmk Exp $
+   $Id: afs.c,v 1.13 2004/02/27 22:09:59 rmk Exp $
 
 ======================================================================*/
 
@@ -57,6 +57,17 @@
 	u32 checksum;		/* Image checksum (inc. this struct)     */
 };
 
+static u32 word_sum(void *words, int num)
+{
+	u32 *p = words;
+	u32 sum = 0;
+
+	while (num--)
+		sum += *p++;
+
+	return sum;
+}
+
 static int
 afs_read_footer(struct mtd_info *mtd, u_int *img_start, u_int *iis_start,
 		u_int off, u_int mask)
@@ -76,17 +87,25 @@
 		return ret;
 	}
 
+	ret = 1;
+
 	/*
 	 * Does it contain the magic number?
 	 */
 	if (fs.signature != 0xa0ffff9f)
-		ret = 1;
+		ret = 0;
+
+	/*
+	 * Check the checksum.
+	 */
+	if (word_sum(&fs, sizeof(fs) / sizeof(u32)) != 0xffffffff)
+		ret = 0;
 
 	/*
 	 * Don't touch the SIB.
 	 */
 	if (fs.type == 2)
-		ret = 1;
+		ret = 0;
 
 	*iis_start = fs.image_info_base & mask;
 	*img_start = fs.image_start & mask;
@@ -96,14 +115,14 @@
 	 * be located after the footer structure.
 	 */
 	if (*iis_start >= ptr)
-		ret = 1;
+		ret = 0;
 
 	/*
 	 * Check the start of this image.  The image
 	 * data can not be located after this block.
 	 */
 	if (*img_start > off)
-		ret = 1;
+		ret = 0;
 
 	return ret;
 }
@@ -112,20 +131,41 @@
 afs_read_iis(struct mtd_info *mtd, struct image_info_struct *iis, u_int ptr)
 {
 	size_t sz;
-	int ret;
+	int ret, i;
 
 	memset(iis, 0, sizeof(*iis));
 	ret = mtd->read(mtd, ptr, sizeof(*iis), &sz, (u_char *) iis);
-	if (ret >= 0 && sz != sizeof(*iis))
-		ret = -EINVAL;
 	if (ret < 0)
-		printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
-			ptr, ret);
+		goto failed;
+
+	if (sz != sizeof(*iis)) {
+		ret = -EINVAL;
+		goto failed;
+	}
+
+	ret = 0;
+
+	/*
+	 * Validate the name - it must be NUL terminated.
+	 */
+	for (i = 0; i < sizeof(iis->name); i++)
+		if (iis->name[i] == '\0')
+			break;
+
+	if (i < sizeof(iis->name))
+		ret = 1;
 
 	return ret;
+
+ failed:
+	printk(KERN_ERR "AFS: mtd read failed at 0x%x: %d\n",
+		ptr, ret);
+	return ret;
 }
 
-int parse_afs_partitions(struct mtd_info *mtd, struct mtd_partition **pparts)
+static int parse_afs_partitions(struct mtd_info *mtd, 
+                         struct mtd_partition **pparts,
+                         unsigned long origin)
 {
 	struct mtd_partition *parts;
 	u_int mask, off, idx, sz;
@@ -150,12 +190,14 @@
 		ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask);
 		if (ret < 0)
 			break;
-		if (ret == 1)
+		if (ret == 0)
 			continue;
 
 		ret = afs_read_iis(mtd, &iis, iis_ptr);
 		if (ret < 0)
 			break;
+		if (ret == 0)
+			continue;
 
 		sz += sizeof(struct mtd_partition);
 		sz += strlen(iis.name) + 1;
@@ -183,13 +225,15 @@
 		ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask);
 		if (ret < 0)
 			break;
-		if (ret == 1)
+		if (ret == 0)
 			continue;
 
 		/* Read the image info block */
 		ret = afs_read_iis(mtd, &iis, iis_ptr);
 		if (ret < 0)
 			break;
+		if (ret == 0)
+			continue;
 
 		strcpy(str, iis.name);
 		size = mtd->erasesize + off - img_ptr;
@@ -227,7 +271,25 @@
 	return idx ? idx : ret;
 }
 
-EXPORT_SYMBOL(parse_afs_partitions);
+static struct mtd_part_parser afs_parser = {
+	.owner = THIS_MODULE,
+	.parse_fn = parse_afs_partitions,
+	.name = "afs",
+};
+
+static int __init afs_parser_init(void)
+{
+	return register_mtd_parser(&afs_parser);
+}
+
+static void __exit afs_parser_exit(void)
+{
+	deregister_mtd_parser(&afs_parser);
+}
+
+module_init(afs_parser_init);
+module_exit(afs_parser_exit);
+
 
 MODULE_AUTHOR("ARM Ltd");
 MODULE_DESCRIPTION("ARM Firmware Suite partition parser");
diff -Nur linux-2.4.30/drivers/mtd/chips/amd_flash.c linux-2.4.30-linux-mips.org/drivers/mtd/chips/amd_flash.c
--- linux-2.4.30/drivers/mtd/chips/amd_flash.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/amd_flash.c	2005-05-12 18:01:29.000000000 +0200
@@ -3,7 +3,7 @@
  *
  * Author: Jonas Holmberg <jonas.holmberg@axis.com>
  *
- * $Id: amd_flash.c,v 1.19 2003/01/24 13:30:11 dwmw2 Exp $
+ * $Id: amd_flash.c,v 1.23 2003/06/12 09:24:13 dwmw2 Exp $
  *
  * Copyright (c) 2001 Axis Communications AB
  *
@@ -19,6 +19,7 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
+#include <linux/init.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/flashchip.h>
@@ -125,10 +126,10 @@
 
 
 static struct mtd_chip_driver amd_flash_chipdrv = {
-	probe: amd_flash_probe,
-	destroy: amd_flash_destroy,
-	name: "amd_flash",
-	module: THIS_MODULE
+	.probe = amd_flash_probe,
+	.destroy = amd_flash_destroy,
+	.name = "amd_flash",
+	.module = THIS_MODULE
 };
 
 
@@ -140,11 +141,11 @@
 static inline __u32 wide_read(struct map_info *map, __u32 addr)
 {
 	if (map->buswidth == 1) {
-		return map->read8(map, addr);
+		return map_read8(map, addr);
 	} else if (map->buswidth == 2) {
-		return map->read16(map, addr);
+		return map_read16(map, addr);
 	} else if (map->buswidth == 4) {
-		return map->read32(map, addr);
+		return map_read32(map, addr);
         }
 
 	return 0;
@@ -153,11 +154,11 @@
 static inline void wide_write(struct map_info *map, __u32 val, __u32 addr)
 {
 	if (map->buswidth == 1) {
-		map->write8(map, val, addr);
+		map_write8(map, val, addr);
 	} else if (map->buswidth == 2) {
-		map->write16(map, val, addr);
+		map_write16(map, val, addr);
 	} else if (map->buswidth == 4) {
-		map->write32(map, val, addr);
+		map_write32(map, val, addr);
 	}
 }
 
@@ -424,231 +425,228 @@
 
 static struct mtd_info *amd_flash_probe(struct map_info *map)
 {
-	/* Keep this table on the stack so that it gets deallocated after the
-	 * probe is done.
-	 */
-	const struct amd_flash_info table[] = {
+	static const struct amd_flash_info table[] = {
 	{
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29LV160DT,
-		name: "AMD AM29LV160DT",
-		size: 0x00200000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
-			{ offset: 0x1F0000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x1F8000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x1FC000, erasesize: 0x04000, numblocks:  1 }
+		.mfr_id = MANUFACTURER_AMD,
+		.dev_id = AM29LV160DT,
+		.name = "AMD AM29LV160DT",
+		.size = 0x00200000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
+			{ .offset = 0x1F0000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x1F8000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x1FC000, .erasesize = 0x04000, .numblocks =  1 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29LV160DB,
-		name: "AMD AM29LV160DB",
-		size: 0x00200000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x04000, numblocks:  1 },
-			{ offset: 0x004000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x008000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
+		.mfr_id = MANUFACTURER_AMD,
+		.dev_id = AM29LV160DB,
+		.name = "AMD AM29LV160DB",
+		.size = 0x00200000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x04000, .numblocks =  1 },
+			{ .offset = 0x004000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x008000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_TOSHIBA,
-		dev_id: TC58FVT160,
-		name: "Toshiba TC58FVT160",
-		size: 0x00200000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
-			{ offset: 0x1F0000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x1F8000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x1FC000, erasesize: 0x04000, numblocks:  1 }
+		.mfr_id = MANUFACTURER_TOSHIBA,
+		.dev_id = TC58FVT160,
+		.name = "Toshiba TC58FVT160",
+		.size = 0x00200000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
+			{ .offset = 0x1F0000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x1F8000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x1FC000, .erasesize = 0x04000, .numblocks =  1 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_FUJITSU,
-		dev_id: MBM29LV160TE,
-		name: "Fujitsu MBM29LV160TE",
-		size: 0x00200000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
-			{ offset: 0x1F0000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x1F8000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x1FC000, erasesize: 0x04000, numblocks:  1 }
+		.mfr_id = MANUFACTURER_FUJITSU,
+		.dev_id = MBM29LV160TE,
+		.name = "Fujitsu MBM29LV160TE",
+		.size = 0x00200000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
+			{ .offset = 0x1F0000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x1F8000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x1FC000, .erasesize = 0x04000, .numblocks =  1 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_TOSHIBA,
-		dev_id: TC58FVB160,
-		name: "Toshiba TC58FVB160",
-		size: 0x00200000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x04000, numblocks:  1 },
-			{ offset: 0x004000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x008000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
+		.mfr_id = MANUFACTURER_TOSHIBA,
+		.dev_id = TC58FVB160,
+		.name = "Toshiba TC58FVB160",
+		.size = 0x00200000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x04000, .numblocks =  1 },
+			{ .offset = 0x004000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x008000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_FUJITSU,
-		dev_id: MBM29LV160BE,
-		name: "Fujitsu MBM29LV160BE",
-		size: 0x00200000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x04000, numblocks:  1 },
-			{ offset: 0x004000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x008000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
+		.mfr_id = MANUFACTURER_FUJITSU,
+		.dev_id = MBM29LV160BE,
+		.name = "Fujitsu MBM29LV160BE",
+		.size = 0x00200000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x04000, .numblocks =  1 },
+			{ .offset = 0x004000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x008000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29LV800BB,
-		name: "AMD AM29LV800BB",
-		size: 0x00100000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x04000, numblocks:  1 },
-			{ offset: 0x004000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x008000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x010000, erasesize: 0x10000, numblocks: 15 }
+		.mfr_id = MANUFACTURER_AMD,
+		.dev_id = AM29LV800BB,
+		.name = "AMD AM29LV800BB",
+		.size = 0x00100000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x04000, .numblocks =  1 },
+			{ .offset = 0x004000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x008000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29F800BB,
-		name: "AMD AM29F800BB",
-		size: 0x00100000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x04000, numblocks:  1 },
-			{ offset: 0x004000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x008000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x010000, erasesize: 0x10000, numblocks: 15 }
+		.mfr_id = MANUFACTURER_AMD,
+		.dev_id = AM29F800BB,
+		.name = "AMD AM29F800BB",
+		.size = 0x00100000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x04000, .numblocks =  1 },
+			{ .offset = 0x004000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x008000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29LV800BT,
-		name: "AMD AM29LV800BT",
-		size: 0x00100000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
-			{ offset: 0x0F0000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x0F8000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x0FC000, erasesize: 0x04000, numblocks:  1 }
+		.mfr_id = MANUFACTURER_AMD,
+		.dev_id = AM29LV800BT,
+		.name = "AMD AM29LV800BT",
+		.size = 0x00100000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
+			{ .offset = 0x0F0000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x0F8000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x0FC000, .erasesize = 0x04000, .numblocks =  1 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29F800BT,
-		name: "AMD AM29F800BT",
-		size: 0x00100000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
-			{ offset: 0x0F0000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x0F8000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x0FC000, erasesize: 0x04000, numblocks:  1 }
+		.mfr_id = MANUFACTURER_AMD,
+		.dev_id = AM29F800BT,
+		.name = "AMD AM29F800BT",
+		.size = 0x00100000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
+			{ .offset = 0x0F0000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x0F8000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x0FC000, .erasesize = 0x04000, .numblocks =  1 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29LV800BB,
-		name: "AMD AM29LV800BB",
-		size: 0x00100000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
-			{ offset: 0x0F0000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x0F8000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x0FC000, erasesize: 0x04000, numblocks:  1 }
+		.mfr_id = MANUFACTURER_AMD,
+		.dev_id = AM29LV800BB,
+		.name = "AMD AM29LV800BB",
+		.size = 0x00100000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
+			{ .offset = 0x0F0000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x0F8000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x0FC000, .erasesize = 0x04000, .numblocks =  1 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_FUJITSU,
-		dev_id: MBM29LV800BB,
-		name: "Fujitsu MBM29LV800BB",
-		size: 0x00100000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x04000, numblocks:  1 },
-			{ offset: 0x004000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x008000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x010000, erasesize: 0x10000, numblocks: 15 }
+		.mfr_id = MANUFACTURER_FUJITSU,
+		.dev_id = MBM29LV800BB,
+		.name = "Fujitsu MBM29LV800BB",
+		.size = 0x00100000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x04000, .numblocks =  1 },
+			{ .offset = 0x004000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x008000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_ST,
-		dev_id: M29W800T,
-		name: "ST M29W800T",
-		size: 0x00100000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x10000, numblocks: 15 },
-			{ offset: 0x0F0000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x0F8000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x0FC000, erasesize: 0x04000, numblocks:  1 }
+		.mfr_id = MANUFACTURER_ST,
+		.dev_id = M29W800T,
+		.name = "ST M29W800T",
+		.size = 0x00100000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 },
+			{ .offset = 0x0F0000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x0F8000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x0FC000, .erasesize = 0x04000, .numblocks =  1 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_ST,
-		dev_id: M29W160DT,
-		name: "ST M29W160DT",
-		size: 0x00200000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
-			{ offset: 0x1F0000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x1F8000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x1FC000, erasesize: 0x04000, numblocks:  1 }
+		.mfr_id = MANUFACTURER_ST,
+		.dev_id = M29W160DT,
+		.name = "ST M29W160DT",
+		.size = 0x00200000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
+			{ .offset = 0x1F0000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x1F8000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x1FC000, .erasesize = 0x04000, .numblocks =  1 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_ST,
-		dev_id: M29W160DB,
-		name: "ST M29W160DB",
-		size: 0x00200000,
-		numeraseregions: 4,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x04000, numblocks:  1 },
-			{ offset: 0x004000, erasesize: 0x02000, numblocks:  2 },
-			{ offset: 0x008000, erasesize: 0x08000, numblocks:  1 },
-			{ offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
+		.mfr_id = MANUFACTURER_ST,
+		.dev_id = M29W160DB,
+		.name = "ST M29W160DB",
+		.size = 0x00200000,
+		.numeraseregions = 4,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x04000, .numblocks =  1 },
+			{ .offset = 0x004000, .erasesize = 0x02000, .numblocks =  2 },
+			{ .offset = 0x008000, .erasesize = 0x08000, .numblocks =  1 },
+			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29BDS323D,
-		name: "AMD AM29BDS323D",
-		size: 0x00400000,
-		numeraseregions: 3,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x10000, numblocks: 48 },
-			{ offset: 0x300000, erasesize: 0x10000, numblocks: 15 },
-			{ offset: 0x3f0000, erasesize: 0x02000, numblocks:  8 },
+		.mfr_id = MANUFACTURER_AMD,
+		.dev_id = AM29BDS323D,
+		.name = "AMD AM29BDS323D",
+		.size = 0x00400000,
+		.numeraseregions = 3,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 48 },
+			{ .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 },
+			{ .offset = 0x3f0000, .erasesize = 0x02000, .numblocks =  8 },
 		}
 	}, {
-		mfr_id: MANUFACTURER_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_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",
-		size: 0x00200000,
-		numeraseregions: 2,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x02000, numblocks:  8 },
-			{ offset: 0x010000, erasesize: 0x10000, numblocks: 31 }
+		.mfr_id = MANUFACTURER_ATMEL,
+		.dev_id = AT49xV16x,
+		.name = "Atmel AT49xV16x",
+		.size = 0x00200000,
+		.numeraseregions = 2,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x02000, .numblocks =  8 },
+			{ .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 }
 		}
 	}, {
-		mfr_id: MANUFACTURER_ATMEL,
-		dev_id: AT49xV16xT,
-		name: "Atmel AT49xV16xT",
-		size: 0x00200000,
-		numeraseregions: 2,
-		regions: {
-			{ offset: 0x000000, erasesize: 0x10000, numblocks: 31 },
-			{ offset: 0x1F0000, erasesize: 0x02000, numblocks:  8 }
+		.mfr_id = MANUFACTURER_ATMEL,
+		.dev_id = AT49xV16xT,
+		.name = "Atmel AT49xV16xT",
+		.size = 0x00200000,
+		.numeraseregions = 2,
+		.regions = {
+			{ .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 },
+			{ .offset = 0x1F0000, .erasesize = 0x02000, .numblocks =  8 }
 		}
 	} 
 	};
@@ -822,7 +820,7 @@
 
 	chip->state = FL_READY;
 
-	map->copy_from(map, buf, adr, len);
+	map_copy_from(map, buf, adr, len);
 
 	wake_up(&chip->wq);
 	spin_unlock_bh(chip->mutex);
@@ -984,7 +982,7 @@
 		u_char tmp_buf[4];
 		__u32 datum;
 
-		map->copy_from(map, tmp_buf,
+		map_copy_from(map, tmp_buf,
 			       bus_ofs + private->chips[chipnum].start,
 			       map->buswidth);
 		while (len && i < map->buswidth)
@@ -1057,7 +1055,7 @@
 		u_char tmp_buf[2];
 		__u32 datum;
 
-		map->copy_from(map, tmp_buf,
+		map_copy_from(map, tmp_buf,
 			       ofs + private->chips[chipnum].start,
 			       map->buswidth);
 		while (len--) {
@@ -1178,7 +1176,7 @@
 		__u8 verify;
 
 		for (address = adr; address < (adr + size); address++) {
-			if ((verify = map->read8(map, address)) != 0xFF) {
+			if ((verify = map_read8(map, address)) != 0xFF) {
 				error = 1;
 				break;
 			}
diff -Nur linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0001.c linux-2.4.30-linux-mips.org/drivers/mtd/chips/cfi_cmdset_0001.c
--- linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0001.c	2004-11-17 12:54:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/cfi_cmdset_0001.c	2005-05-12 18:01:29.000000000 +0200
@@ -4,7 +4,7 @@
  *
  * (C) 2000 Red Hat. GPL'd
  *
- * $Id: cfi_cmdset_0001.c,v 1.114 2003/03/18 12:28:40 dwmw2 Exp $
+ * $Id: cfi_cmdset_0001.c,v 1.135 2004/03/25 13:24:40 dwmw2 Exp $
  *
  * 
  * 10/10/2000	Nicolas Pitre <nico@cam.org>
@@ -21,6 +21,7 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <asm/byteorder.h>
 
@@ -29,10 +30,14 @@
 #include <linux/delay.h>
 #include <linux/interrupt.h>
 #include <linux/mtd/map.h>
-#include <linux/mtd/cfi.h>
+#include <linux/mtd/mtd.h>
 #include <linux/mtd/compatmac.h>
+#include <linux/mtd/cfi.h>
+
+/* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */
 
-// debugging, turns off buffer write mode #define FORCE_WORD_WRITE
+// debugging, turns off buffer write mode if set to 1
+#define FORCE_WORD_WRITE 0
 
 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 *);
@@ -52,16 +57,21 @@
 
 static struct mtd_info *cfi_intelext_setup (struct map_info *);
 
-static int do_point (struct mtd_info *mtd, loff_t from, size_t len,
+static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len,
 		     size_t *retlen, u_char **mtdbuf);
-static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from,
+static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from,
 			size_t len);
 
+
+/*
+ *  *********** SETUP AND PROBE BITS  ***********
+ */
+
 static struct mtd_chip_driver cfi_intelext_chipdrv = {
-	probe: NULL, /* Not usable directly */
-	destroy: cfi_intelext_destroy,
-	name: "cfi_cmdset_0001",
-	module: THIS_MODULE
+	.probe		= NULL, /* Not usable directly */
+	.destroy	= cfi_intelext_destroy,
+	.name		= "cfi_cmdset_0001",
+	.module		= THIS_MODULE
 };
 
 /* #define DEBUG_LOCK_BITS */
@@ -102,13 +112,63 @@
 	}
 	
 	printk("  Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n", 
-	       extp->VccOptimal >> 8, extp->VccOptimal & 0xf);
+	       extp->VccOptimal >> 4, extp->VccOptimal & 0xf);
 	if (extp->VppOptimal)
 		printk("  Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", 
-		       extp->VppOptimal >> 8, extp->VppOptimal & 0xf);
+		       extp->VppOptimal >> 4, extp->VppOptimal & 0xf);
+}
+#endif
+
+#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)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
+
+	printk(KERN_WARNING "cfi_cmdset_0001: Suspend "
+	                    "erase on write disabled.\n");
+	extp->SuspendCmdSupport &= ~1;
 }
 #endif
 
+static void fixup_st_m28w320ct(struct map_info *map, void* param)
+{
+	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)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+	
+	/* Note this is done after the region info is endian swapped */
+	cfi->cfiq->EraseRegionInfo[1] =
+		(cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e;
+};
+
+static struct cfi_fixup fixup_table[] = {
+#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
+	{
+		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
+	}
+};
+
 /* This routine is made available to other mtd code via
  * inter_module_register.  It must only be accessed through
  * inter_module_get which will bump the use count of this module.  The
@@ -120,7 +180,6 @@
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	int i;
-	__u32 base = cfi->chips[0].start;
 
 	if (cfi->cfi_mode == CFI_MODE_CFI) {
 		/* 
@@ -130,40 +189,20 @@
 		 */
 		__u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
 		struct cfi_pri_intelext *extp;
-		int ofs_factor = cfi->interleave * cfi->device_type;
 
-		//printk(" Intel/Sharp Extended Query Table at 0x%4.4X\n", adr);
-		if (!adr)
+		extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "Intel/Sharp");
+		if (!extp)
 			return NULL;
-
-		/* Switch it into Query Mode */
-		cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
-
-		extp = kmalloc(sizeof(*extp), GFP_KERNEL);
-		if (!extp) {
-			printk(KERN_ERR "Failed to allocate memory\n");
-			return NULL;
-		}
-		
-		/* Read in the Extended Query Table */
-		for (i=0; i<sizeof(*extp); 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 IntelExt 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);
+
+		/* Install our own private info structure */
+		cfi->cmdset_priv = extp;	
+
+		cfi_fixup(map, fixup_table);
 			
 #ifdef DEBUG_CFI_FEATURES
 		/* Tell the user about it in lots of lovely detail */
@@ -171,18 +210,8 @@
 #endif	
 
 		if(extp->SuspendCmdSupport & 1) {
-//#define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
-#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
-/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ 
-			printk(KERN_WARNING "cfi_cmdset_0001: Suspend "
-			       "erase on write disabled.\n");
-			extp->SuspendCmdSupport &= ~1;
-#else
 			printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n");
-#endif
 		}
-		/* Install our own private info structure */
-		cfi->cmdset_priv = extp;	
 	}
 
 	for (i=0; i< cfi->numchips; i++) {
@@ -194,8 +223,6 @@
 
 	map->fldrv = &cfi_intelext_chipdrv;
 	
-	/* Make sure it's in read mode */
-	cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL);
 	return cfi_intelext_setup(map);
 }
 
@@ -261,20 +288,16 @@
 	mtd->erase = cfi_intelext_erase_varsize;
 	mtd->read = cfi_intelext_read;
 
-	if(map->point && map->unpoint){
-		mtd->point = do_point;
-		mtd->unpoint = do_unpoint;
+	if (map_is_linear(map)) {
+		mtd->point = cfi_intelext_point;
+		mtd->unpoint = cfi_intelext_unpoint;
 	}
 
-#ifndef FORCE_WORD_WRITE
-	if ( cfi->cfiq->BufWriteTimeoutTyp ) {
-		printk("Using buffer write method\n" );
+	if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) {
+		printk(KERN_INFO "Using buffer write method\n" );
 		mtd->write = cfi_intelext_write_buffers;
 	} else {
-#else
-	{
-#endif
-		printk("Using word write method\n" );
+		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;
@@ -286,8 +309,8 @@
 	mtd->resume = cfi_intelext_resume;
 	mtd->flags = MTD_CAP_NORFLASH;
 	map->fldrv = &cfi_intelext_chipdrv;
-	MOD_INC_USE_COUNT;
 	mtd->name = map->name;
+	__module_get(THIS_MODULE);
 	return mtd;
 
  setup_err:
@@ -301,78 +324,171 @@
 	return NULL;
 }
 
-static int do_point_onechip (struct map_info *map,  struct flchip *chip, loff_t adr, size_t len)
+/*
+ *  *********** CHIP ACCESS FUNCTIONS ***********
+ */
+
+static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
 {
-	cfi_word status, status_OK;
-	unsigned long timeo;
 	DECLARE_WAITQUEUE(wait, current);
-	unsigned long cmd_addr;
 	struct cfi_private *cfi = map->fldrv_priv;
+	cfi_word status, status_OK = CMD(0x80);
+	unsigned long timeo;
+	struct cfi_pri_intelext *cfip = (struct cfi_pri_intelext *)cfi->cmdset_priv;
 
-	adr += chip->start;
-
-	/* Ensure cmd read/writes are aligned. */ 
-	cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); 
-
-	/* Let's determine this according to the interleave only once */
-	status_OK = CMD(0x80);
-
+ resettime:
 	timeo = jiffies + HZ;
  retry:
-	spin_lock(chip->mutex);
-
-	/* Check that the chip's ready to talk to us.
-	 * If it's in FL_ERASING state, suspend it and make it talk now.
-	 */
 	switch (chip->state) {
 
-	case FL_READY:
-	case FL_POINT:
-		break;
+	case FL_STATUS:
+		for (;;) {
+			status = cfi_read(map, adr);
+			if ((status & status_OK) == status_OK)
+				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);
+				return -EIO;
+			}
+			spin_unlock(chip->mutex);
+			cfi_udelay(1);
+			spin_lock(chip->mutex);
+			/* Someone else might have been playing with it. */
+			goto retry;
+		}
+				
+	case FL_READY:
 	case FL_CFI_QUERY:
 	case FL_JEDEC_QUERY:
-		cfi_write(map, CMD(0x70), cmd_addr);
-		chip->state = FL_STATUS;
+		return 0;
+
+	case FL_ERASING:
+		if (!(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);
+
+		/* 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);
+		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)
+			        break;
+
+			if (time_after(jiffies, timeo)) {
+				/* Urgh. Resume and pretend we weren't here.  */
+				cfi_write(map, CMD(0xd0), adr);
+				/* Make sure we're in 'read status' mode if it had finished */
+				cfi_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);
+				return -EIO;
+			}
 
-	case FL_STATUS:
-		status = cfi_read(map, cmd_addr);
-		if ((status & status_OK) == status_OK) {
-			cfi_write(map, CMD(0xff), cmd_addr);
-			chip->state = FL_READY;
-			break;
-		}
-		
-		/* Urgh. Chip not yet ready to talk to us. */
-		if (time_after(jiffies, timeo)) {
 			spin_unlock(chip->mutex);
-			printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status);
-			return -EIO;
+			cfi_udelay(1);
+			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_STATUS;
+		return 0;
 
-		/* Latency issues. Drop the lock, wait a while and retry */
-		spin_unlock(chip->mutex);
-		cfi_udelay(1);
-		goto retry;
+	case FL_POINT:
+		/* Only if there's no operation suspended... */
+		if (mode == FL_READY && chip->oldstate == FL_READY)
+			return 0;
 
 	default:
-		/* Stick ourselves on a wait queue to be woken when
-		   someone changes the status */
+	sleep:
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		add_wait_queue(&chip->wq, &wait);
 		spin_unlock(chip->mutex);
 		schedule();
 		remove_wait_queue(&chip->wq, &wait);
-		timeo = jiffies + HZ;
-		goto retry;
+		spin_lock(chip->mutex);
+		goto resettime;
+	}
+}
+
+static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+
+	switch(chip->oldstate) {
+	case FL_ERASING:
+		chip->state = chip->oldstate;
+		/* What if one interleaved chip has finished and the 
+		   other hasn't? The old code would leave the finished
+		   one in READY mode. That's bad, and caused -EROFS 
+		   errors to be returned from do_erase_oneblock because
+		   that's the only bit it checked for at the time.
+		   As the state machine appears to explicitly allow 
+		   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);
+		chip->oldstate = FL_READY;
+		chip->state = FL_ERASING;
+		break;
+
+	case FL_READY:
+	case FL_STATUS:
+	case FL_JEDEC_QUERY:
+		/* We should really make set_vpp() count, rather than doing this */
+		DISABLE_VPP(map);
+		break;
+	default:
+		printk(KERN_ERR "put_chip() called with oldstate %d!!\n", chip->oldstate);
 	}
+	wake_up(&chip->wq);
+}
+
+static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len)
+{
+	unsigned long cmd_addr;
+	struct cfi_private *cfi = map->fldrv_priv;
+	int ret = 0;
+
+	adr += chip->start;
+
+	/* Ensure cmd read/writes are aligned. */ 
+	cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); 
 
-	chip->state = FL_POINT;
-	chip->ref_point_counter++;
+	spin_lock(chip->mutex);
+
+	ret = get_chip(map, chip, cmd_addr, FL_POINT);
+
+	if (!ret) {
+		if (chip->state != FL_POINT && chip->state != FL_READY)
+			cfi_write(map, CMD(0xff), cmd_addr);
+
+		chip->state = FL_POINT;
+		chip->ref_point_counter++;
+	}
 	spin_unlock(chip->mutex);
-	return 0;
+
+	return ret;
 }
-static int do_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf)
+
+static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf)
 {
 	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
@@ -380,19 +496,17 @@
 	int chipnum;
 	int ret = 0;
 
-	if (from + len > mtd->size)
+	if (!map->virt || (from + len > mtd->size))
 		return -EINVAL;
 	
-	*mtdbuf = map->point(map, from, len);
-	if(*mtdbuf == NULL)
-		return -EINVAL; /* can not point this region */
+	*mtdbuf = (void *)map->virt + from;
 	*retlen = 0;
 
 	/* Now lock the chip(s) to POINT state */
 
 	/* ofs: offset within the first chip that the first read should start */
 	chipnum = (from >> cfi->chipshift);
-	ofs = from - (chipnum <<  cfi->chipshift);
+	ofs = from - (chipnum << cfi->chipshift);
 
 	while (len) {
 		unsigned long thislen;
@@ -418,14 +532,13 @@
 	return 0;
 }
 
-static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
+static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
 {
 	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
 	unsigned long ofs;
 	int chipnum;
 
-	map->unpoint(map, addr, from, len);
 	/* Now unlock the chip(s) POINT state */
 
 	/* ofs: offset within the first chip that the first read should start */
@@ -446,13 +559,14 @@
 			thislen = len;
 
 		spin_lock(chip->mutex);
-		if(chip->state == FL_POINT){
+		if (chip->state == FL_POINT) {
 			chip->ref_point_counter--;
 			if(chip->ref_point_counter == 0)
 				chip->state = FL_READY;
 		} else
-			printk("Warning: unpoint called on non pointed region\n"); /* Should this give an error? */
-		wake_up(&chip->wq);
+			printk(KERN_ERR "Warning: unpoint called on non pointed region\n"); /* Should this give an error? */
+
+		put_chip(map, chip, chip->start);
 		spin_unlock(chip->mutex);
 
 		len -= thislen;
@@ -463,136 +577,32 @@
 
 static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
 {
-	cfi_word status, status_OK;
-	unsigned long timeo;
-	DECLARE_WAITQUEUE(wait, current);
-	int suspended = 0;
 	unsigned long cmd_addr;
 	struct cfi_private *cfi = map->fldrv_priv;
+	int ret;
 
 	adr += chip->start;
 
 	/* Ensure cmd read/writes are aligned. */ 
 	cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); 
 
-	/* Let's determine this according to the interleave only once */
-	status_OK = CMD(0x80);
-
-	timeo = jiffies + HZ;
- retry:
 	spin_lock(chip->mutex);
+	ret = get_chip(map, chip, cmd_addr, FL_READY);
+	if (ret) {
+		spin_unlock(chip->mutex);
+		return ret;
+	}
 
-	/* Check that the chip's ready to talk to us.
-	 * If it's in FL_ERASING state, suspend it and make it talk now.
-	 */
-	switch (chip->state) {
-	case FL_ERASING:
-		if (!cfi->cmdset_priv ||
-		    !(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2))
-			goto sleep; /* We don't support erase suspend */
-		
-		cfi_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);
-		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)
-				break;
-			
-			if (time_after(jiffies, timeo)) {
-				/* Urgh */
-				cfi_write(map, CMD(0xd0), cmd_addr);
-				/* make sure we're in 'read status' mode */
-				cfi_write(map, CMD(0x70), cmd_addr);
-				chip->state = FL_ERASING;
-				spin_unlock(chip->mutex);
-				printk(KERN_ERR "Chip not ready after erase "
-				       "suspended: status = 0x%llx\n", (__u64)status);
-				return -EIO;
-			}
-			
-			spin_unlock(chip->mutex);
-			cfi_udelay(1);
-			spin_lock(chip->mutex);
-		}
-		
-		suspended = 1;
+	if (chip->state != FL_POINT && chip->state != FL_READY) {
 		cfi_write(map, CMD(0xff), cmd_addr);
-		chip->state = FL_READY;
-		break;
-	
-#if 0
-	case FL_WRITING:
-		/* Not quite yet */
-#endif
-
-	case FL_READY:
-	case FL_POINT:
-		break;
-
-	case FL_CFI_QUERY:
-	case FL_JEDEC_QUERY:
-		cfi_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);
-			chip->state = FL_READY;
-			break;
-		}
-		
-		/* Urgh. Chip not yet ready to talk to us. */
-		if (time_after(jiffies, timeo)) {
-			spin_unlock(chip->mutex);
-			printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status);
-			return -EIO;
-		}
-
-		/* Latency issues. Drop the lock, wait a while and retry */
-		spin_unlock(chip->mutex);
-		cfi_udelay(1);
-		goto retry;
-
-	default:
-	sleep:
-		/* Stick ourselves on a wait queue to be woken when
-		   someone changes the status */
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
-		spin_unlock(chip->mutex);
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-		timeo = jiffies + HZ;
-		goto retry;
+		chip->state = FL_READY;
 	}
 
-	map->copy_from(map, buf, adr, len);
+	map_copy_from(map, buf, adr, len);
 
-	if (suspended) {
-		chip->state = chip->oldstate;
-		/* What if one interleaved chip has finished and the 
-		   other hasn't? The old code would leave the finished
-		   one in READY mode. That's bad, and caused -EROFS 
-		   errors to be returned from do_erase_oneblock because
-		   that's the only bit it checked for at the time.
-		   As the state machine appears to explicitly allow 
-		   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);		
-	}
+	put_chip(map, chip, cmd_addr);
 
-	wake_up(&chip->wq);
 	spin_unlock(chip->mutex);
 	return 0;
 }
@@ -640,70 +650,52 @@
 {
 	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	struct cfi_pri_intelext *extp=cfi->cmdset_priv;
-	int ofs_factor = cfi->interleave * cfi->device_type;
-	int   count=len;
+	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
 	struct flchip *chip;
-	int chip_num,offst;
-	unsigned long timeo;
-	DECLARE_WAITQUEUE(wait, current);
+	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;
 
-	chip=0;
+	while (count) {
 	/* Calculate which chip & protection register offset we need */
-	chip_num=((unsigned int)from/reg_sz);
-	offst=from-(reg_sz*chip_num)+base_offst;
 
-	while(count){
-		
-		if(chip_num>=cfi->numchips)
+		if (chip_num >= cfi->numchips)
 			goto out;
 
-		/* Make sure that the chip is in the right state */
-
-		timeo = jiffies + HZ;
-		chip=&cfi->chips[chip_num];
-	retry:		
-		spin_lock(chip->mutex);
-	
-		switch (chip->state) {
-		case FL_READY:
-		case FL_STATUS:
-		case FL_CFI_QUERY:
-		case FL_JEDEC_QUERY:
-			break;
+		chip = &cfi->chips[chip_num];
 		
-		default:
-				/* Stick ourselves on a wait queue to be woken when
-				   someone changes the status */
-			set_current_state(TASK_UNINTERRUPTIBLE);
-			add_wait_queue(&chip->wq, &wait);
+		spin_lock(chip->mutex);
+		ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY);
+		if (ret) {
 			spin_unlock(chip->mutex);
-			schedule();
-			remove_wait_queue(&chip->wq, &wait);
-			timeo = jiffies + HZ;
-			goto retry;
+			return (len-count)?:ret;
 		}
-			
-		/* Now read the data required from this flash */
-       
-		cfi_send_gen_cmd(0x90, 0x55,chip->start, map, cfi, cfi->device_type, NULL);
-		while(count && ((offst-base_offst)<reg_sz)){
-			*buf=map->read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst));
+
+		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--;
 		}
-	       
-		chip->state=FL_CFI_QUERY;
+
+		put_chip(map, chip, chip->start);
 		spin_unlock(chip->mutex);
+
 		/* Move on to the next chip */
 		chip_num++;
-		offst=base_offst;
-	
+		offst = base_offst;
 	}
 	
  out:	
-	wake_up(&chip->wq);
 	return len-count;
 }
 	
@@ -749,103 +741,20 @@
 static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
 	cfi_word status, status_OK;
 	unsigned long timeo;
-	DECLARE_WAITQUEUE(wait, current);
-	int z, suspended=0, ret=0;
+	int z, ret=0;
 
 	adr += chip->start;
 
-	/* Let's determine this according to the interleave only once */
-	status_OK = CMD(0x80);
-
-	timeo = jiffies + HZ;
- retry:
-	spin_lock(chip->mutex);
-
-	/* Check that the chip's ready to talk to us.
-	 * Later, we can actually think about interrupting it
-	 * if it's in FL_ERASING state.
-	 * Not just yet, though.
-	 */
-	switch (chip->state) {
-	case FL_READY:
-		break;
-		
-	case FL_CFI_QUERY:
-	case FL_JEDEC_QUERY:
-		cfi_write(map, CMD(0x70), adr);
-		chip->state = FL_STATUS;
-
-	case FL_STATUS:
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
-			break;
-		
-		/* Urgh. Chip not yet ready to talk to us. */
-		if (time_after(jiffies, timeo)) {
-			spin_unlock(chip->mutex);
-			printk(KERN_ERR "waiting for chip to be ready timed out in read\n");
-			return -EIO;
-		}
-
-		/* Latency issues. Drop the lock, wait a while and retry */
-		spin_unlock(chip->mutex);
-		cfi_udelay(1);
-		goto retry;
-
-	case FL_ERASING:
-		if (!extp || 
-		    !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1)))
-			goto sleep; /* We don't support erase suspend */
-		
-		cfi_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);
-		chip->oldstate = FL_ERASING;
-		chip->state = FL_ERASE_SUSPENDING;
-		for (;;) {
-			status = cfi_read(map, adr);
-			if ((status & status_OK) == status_OK)
-				break;
-			
-			if (time_after(jiffies, timeo)) {
-				/* Urgh */
-				cfi_write(map, CMD(0xd0), adr);
-				/* make sure we're in 'read status' mode */
-				cfi_write(map, CMD(0x70), adr);
-				chip->state = FL_ERASING;
-				spin_unlock(chip->mutex);
-				printk(KERN_ERR "Chip not ready after erase "
-				       "suspended: status = 0x%x\n", status);
-				return -EIO;
-			}
-			
-			spin_unlock(chip->mutex);
-			cfi_udelay(1);
-			spin_lock(chip->mutex);
-		}
-		suspended = 1;
-		chip->state = FL_STATUS;
-		break;
+	/* Let's determine this according to the interleave only once */
+	status_OK = CMD(0x80);
 
-	default:
-	sleep:
-		/* Stick ourselves on a wait queue to be woken when
-		   someone changes the status */
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
+	spin_lock(chip->mutex);
+	ret = get_chip(map, chip, adr, FL_WRITING);
+	if (ret) {
 		spin_unlock(chip->mutex);
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-		timeo = jiffies + HZ;
-		goto retry;
+		return ret;
 	}
 
 	ENABLE_VPP(map);
@@ -862,6 +771,8 @@
 	for (;;) {
 		if (chip->state != FL_WRITING) {
 			/* Someone's suspended the write. Sleep */
+			DECLARE_WAITQUEUE(wait, current);
+
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			add_wait_queue(&chip->wq, &wait);
 			spin_unlock(chip->mutex);
@@ -879,7 +790,6 @@
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
 			chip->state = FL_STATUS;
-			DISABLE_VPP(map);
 			printk(KERN_ERR "waiting for chip to be ready timed out in word write\n");
 			ret = -EIO;
 			goto out;
@@ -908,27 +818,11 @@
 		/* put back into read status register mode */
 		cfi_write(map, CMD(0x70), adr);
 		ret = -EROFS;
-		goto out;
 	}
  out:
-	if (suspended) {
-		chip->state = chip->oldstate;
-		/* What if one interleaved chip has finished and the 
-		   other hasn't? The old code would leave the finished
-		   one in READY mode. That's bad, and caused -EROFS 
-		   errors to be returned from do_erase_oneblock because
-		   that's the only bit it checked for at the time.
-		   As the state machine appears to explicitly allow 
-		   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);		
-	} else
-		DISABLE_VPP(map); /* must not clear the VPP if there is a suspended erase to be resumed */
-
-	wake_up(&chip->wq);
+	put_chip(map, chip, adr);
 	spin_unlock(chip->mutex);
+
 	return ret;
 }
 
@@ -1059,11 +953,9 @@
 				  unsigned long adr, const u_char *buf, int len)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
 	cfi_word status, status_OK;
 	unsigned long cmd_adr, timeo;
-	DECLARE_WAITQUEUE(wait, current);
-	int wbufsize, z, suspended=0, ret=0;
+	int wbufsize, z, ret=0, bytes, words;
 
 	wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
 	adr += chip->start;
@@ -1072,91 +964,18 @@
 	/* Let's determine this according to the interleave only once */
 	status_OK = CMD(0x80);
 
-	timeo = jiffies + HZ;
- retry:
 	spin_lock(chip->mutex);
-
-	/* Check that the chip's ready to talk to us.
-	 * Later, we can actually think about interrupting it
-	 * if it's in FL_ERASING state.
-	 * Not just yet, though.
-	 */
-	switch (chip->state) {
-	case FL_READY:
-	case FL_CFI_QUERY:
-	case FL_JEDEC_QUERY:
-		cfi_write(map, CMD(0x70), cmd_adr);
-		chip->state = FL_STATUS;
-
-	case FL_STATUS:
-		status = cfi_read(map, cmd_adr);
-		if ((status & status_OK) == status_OK)
-			break;
-		/* Urgh. Chip not yet ready to talk to us. */
-		if (time_after(jiffies, timeo)) {
-			spin_unlock(chip->mutex);
-			printk(KERN_ERR "waiting for chip to be ready timed out in buffer write\n");
-			return -EIO;
-		}
-
-		/* Latency issues. Drop the lock, wait a while and retry */
+	ret = get_chip(map, chip, cmd_adr, FL_WRITING);
+	if (ret) {
 		spin_unlock(chip->mutex);
-		cfi_udelay(1);
-		goto retry;
+		return ret;
+	}
 
-	case FL_ERASING:
-		if (!extp || 
-		    !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1)))
-			goto sleep; /* We don't support erase suspend */
-		
-		cfi_write (map, CMD(0xb0), adr);
+	if (chip->state != FL_STATUS)
+		cfi_write(map, CMD(0x70), cmd_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);
-		chip->oldstate = FL_ERASING;
-		chip->state = FL_ERASE_SUSPENDING;
-		for (;;) {
-			status = cfi_read(map, adr);
-			if ((status & status_OK) == status_OK)
-				break;
-			
-			if (time_after(jiffies, timeo)) {
-				/* Urgh */
-				cfi_write(map, CMD(0xd0), adr);
-				/* make sure we're in 'read status' mode */
-				cfi_write(map, CMD(0x70), adr);
-				chip->state = FL_ERASING;
-				spin_unlock(chip->mutex);
-				printk(KERN_ERR "Chip not ready after erase "
-				       "suspended: status = 0x%x\n", status);
-				return -EIO;
-			}
-			
-			spin_unlock(chip->mutex);
-			cfi_udelay(1);
-			spin_lock(chip->mutex);
-		}
-		suspended = 1;
-		chip->state = FL_STATUS;
-		break;
+	status = cfi_read(map, cmd_adr);
 
-	default:
-	sleep:
-		/* Stick ourselves on a wait queue to be woken when
-		   someone changes the status */
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
-		spin_unlock(chip->mutex);
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-		timeo = jiffies + HZ;
-		goto retry;
-	}
-	/* We know we're now in FL_STATUS mode, and 'status' is current */
 	/* §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
@@ -1185,7 +1004,6 @@
 			/* Argh. Not ready for write to buffer */
 			cfi_write(map, CMD(0x70), cmd_adr);
 			chip->state = FL_STATUS;
-			DISABLE_VPP(map);
 			printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %llx, status = %llx\n", (__u64)status, (__u64)cfi_read(map, cmd_adr));
 			/* Odd. Clear status bits */
 			cfi_write(map, CMD(0x50), cmd_adr);
@@ -1196,24 +1014,42 @@
 	}
 
 	/* Write length of data to come */
-	cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr );
+	bytes = len & (CFIDEV_BUSWIDTH-1);
+	words = len / CFIDEV_BUSWIDTH;
+	cfi_write(map, CMD(words - !bytes), cmd_adr );
 
 	/* Write data */
-	for (z = 0; z < len; z += CFIDEV_BUSWIDTH) {
+	z = 0;
+	while(z < words * CFIDEV_BUSWIDTH) {
 		if (cfi_buswidth_is_1()) {
-			map->write8 (map, *(__u8*)buf, adr+z);
-			buf += sizeof(__u8);
+			map_write8 (map, *((__u8*)buf)++, adr+z);
 		} else if (cfi_buswidth_is_2()) {
-			map->write16 (map, *(__u16*)buf, adr+z);
-			buf += sizeof(__u16);
+			map_write16 (map, *((__u16*)buf)++, adr+z);
 		} else if (cfi_buswidth_is_4()) {
-			map->write32 (map, *(__u32*)buf, adr+z);
-			buf += sizeof(__u32);
+			map_write32 (map, *((__u32*)buf)++, adr+z);
 		} else if (cfi_buswidth_is_8()) {
-			map->write64 (map, *(__u64*)buf, adr+z);
-			buf += sizeof(__u64);
+			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 {
-			DISABLE_VPP(map);
 			ret = -EINVAL;
 			goto out;
 		}
@@ -1231,6 +1067,7 @@
 	for (;;) {
 		if (chip->state != FL_WRITING) {
 			/* Someone's suspended the write. Sleep */
+			DECLARE_WAITQUEUE(wait, current);
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			add_wait_queue(&chip->wq, &wait);
 			spin_unlock(chip->mutex);
@@ -1248,7 +1085,6 @@
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
 			chip->state = FL_STATUS;
-			DISABLE_VPP(map);
 			printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n");
 			ret = -EIO;
 			goto out;
@@ -1269,7 +1105,8 @@
 		chip->buffer_write_time++;
 
 	/* Done and happy. */
-	chip->state = FL_STATUS;
+ 	chip->state = FL_STATUS;
+
 	/* check for lock bit */
 	if (status & CMD(0x02)) {
 		/* clear status */
@@ -1277,26 +1114,10 @@
 		/* put back into read status register mode */
 		cfi_write(map, CMD(0x70), adr);
 		ret = -EROFS;
-		goto out;
 	}
- out:
-	if (suspended) {
-		chip->state = chip->oldstate;
-		/* What if one interleaved chip has finished and the 
-		   other hasn't? The old code would leave the finished
-		   one in READY mode. That's bad, and caused -EROFS 
-		   errors to be returned from do_erase_oneblock because
-		   that's the only bit it checked for at the time.
-		   As the state machine appears to explicitly allow 
-		   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);		
-	} else
-		DISABLE_VPP(map); /* must not clear the VPP if there is a suspended erase to be resumed */
 
-	wake_up(&chip->wq);
+ out:
+	put_chip(map, chip, cmd_adr);
 	spin_unlock(chip->mutex);
 	return ret;
 }
@@ -1340,12 +1161,12 @@
 	}
 
 	/* Write buffer is worth it only if more than one word to write... */
-	while(len > CFIDEV_BUSWIDTH) {
+	while(len) {
 		/* We must not cross write block boundaries */
 		int size = wbufsize - (ofs & (wbufsize-1));
 
 		if (size > len)
-			size = len & ~(CFIDEV_BUSWIDTH-1);
+			size = len;
 		ret = do_write_buffer(map, &cfi->chips[chipnum], 
 				      ofs, buf, size);
 		if (ret)
@@ -1363,17 +1184,6 @@
 				return 0;
 		}
 	}
-
-	/* ... and write the remaining bytes */
-	if (len > 0) {
-		size_t local_retlen;
-		ret = cfi_intelext_write_words(mtd, ofs + (chipnum << cfi->chipshift),
-					       len, &local_retlen, buf);
-		if (ret)
-			return ret;
-		(*retlen) += local_retlen;
-	}
-
 	return 0;
 }
 
@@ -1483,45 +1293,12 @@
 	/* Let's determine this according to the interleave only once */
 	status_OK = CMD(0x80);
 
-	timeo = jiffies + HZ;
-retry:
+ retry:
 	spin_lock(chip->mutex);
-
-	/* Check that the chip's ready to talk to us. */
-	switch (chip->state) {
-	case FL_CFI_QUERY:
-	case FL_JEDEC_QUERY:
-	case FL_READY:
-		cfi_write(map, CMD(0x70), adr);
-		chip->state = FL_STATUS;
-
-	case FL_STATUS:
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
-			break;
-		
-		/* Urgh. Chip not yet ready to talk to us. */
-		if (time_after(jiffies, timeo)) {
-			spin_unlock(chip->mutex);
-			printk(KERN_ERR "waiting for chip to be ready timed out in erase\n");
-			return -EIO;
-		}
-
-		/* Latency issues. Drop the lock, wait a while and retry */
-		spin_unlock(chip->mutex);
-		cfi_udelay(1);
-		goto retry;
-
-	default:
-		/* Stick ourselves on a wait queue to be woken when
-		   someone changes the status */
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
+	ret = get_chip(map, chip, adr, FL_ERASING);
+	if (ret) {
 		spin_unlock(chip->mutex);
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-		timeo = jiffies + HZ;
-		goto retry;
+		return ret;
 	}
 
 	ENABLE_VPP(map);
@@ -1532,7 +1309,7 @@
 	cfi_write(map, CMD(0x20), adr);
 	cfi_write(map, CMD(0xD0), adr);
 	chip->state = FL_ERASING;
-	chip->oldstate = 0;
+	chip->erase_suspended = 0;
 
 	spin_unlock(chip->mutex);
 	set_current_state(TASK_UNINTERRUPTIBLE);
@@ -1554,11 +1331,11 @@
 			spin_lock(chip->mutex);
 			continue;
 		}
-		if (chip->oldstate) {
+		if (chip->erase_suspended) {
 			/* This erase was suspended and resumed.
 			   Adjust the timeout */
 			timeo = jiffies + (HZ*20); /* FIXME */
-			chip->oldstate = 0;
+			chip->erase_suspended = 0;
 		}
 
 		status = cfi_read(map, adr);
@@ -1662,39 +1439,22 @@
 	int i;
 	struct flchip *chip;
 	int ret = 0;
-	DECLARE_WAITQUEUE(wait, current);
 
 	for (i=0; !ret && i<cfi->numchips; i++) {
 		chip = &cfi->chips[i];
 
-	retry:
 		spin_lock(chip->mutex);
+		ret = get_chip(map, chip, chip->start, FL_SYNCING);
 
-		switch(chip->state) {
-		case FL_READY:
-		case FL_STATUS:
-		case FL_CFI_QUERY:
-		case FL_JEDEC_QUERY:
+		if (!ret) {
 			chip->oldstate = chip->state;
 			chip->state = FL_SYNCING;
 			/* No need to wake_up() on this state change - 
 			 * as the whole point is that nobody can do anything
 			 * with the chip now anyway.
 			 */
-		case FL_SYNCING:
-			spin_unlock(chip->mutex);
-			break;
-
-		default:
-			/* Not an idle state */
-			add_wait_queue(&chip->wq, &wait);
-			
-			spin_unlock(chip->mutex);
-			schedule();
-		        remove_wait_queue(&chip->wq, &wait);
-			
-			goto retry;
 		}
+		spin_unlock(chip->mutex);
 	}
 
 	/* Unlock the chips again */
@@ -1721,8 +1481,7 @@
 	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);
-	
+	chip->state = FL_JEDEC_QUERY;
 	return 0;
 }
 #endif
@@ -1735,52 +1494,18 @@
 	struct cfi_private *cfi = map->fldrv_priv;
 	cfi_word status, status_OK;
 	unsigned long timeo = jiffies + HZ;
-	DECLARE_WAITQUEUE(wait, current);
+	int ret;
 
 	adr += chip->start;
 
 	/* Let's determine this according to the interleave only once */
 	status_OK = CMD(0x80);
 
-	timeo = jiffies + HZ;
-retry:
 	spin_lock(chip->mutex);
-
-	/* Check that the chip's ready to talk to us. */
-	switch (chip->state) {
-	case FL_CFI_QUERY:
-	case FL_JEDEC_QUERY:
-	case FL_READY:
-		cfi_write(map, CMD(0x70), adr);
-		chip->state = FL_STATUS;
-
-	case FL_STATUS:
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
-			break;
-		
-		/* Urgh. Chip not yet ready to talk to us. */
-		if (time_after(jiffies, timeo)) {
-			spin_unlock(chip->mutex);
-			printk(KERN_ERR "%s: waiting for chip to be ready timed out\n", __FUNCTION__);
-			return -EIO;
-		}
-
-		/* Latency issues. Drop the lock, wait a while and retry */
-		spin_unlock(chip->mutex);
-		cfi_udelay(1);
-		goto retry;
-
-	default:
-		/* Stick ourselves on a wait queue to be woken when
-		   someone changes the status */
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
+	ret = get_chip(map, chip, adr, FL_LOCKING);
+	if (ret) {
 		spin_unlock(chip->mutex);
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-		timeo = jiffies + HZ;
-		goto retry;
+		return ret;
 	}
 
 	ENABLE_VPP(map);
@@ -1827,8 +1552,7 @@
 	
 	/* Done and happy. */
 	chip->state = FL_STATUS;
-	DISABLE_VPP(map);
-	wake_up(&chip->wq);
+	put_chip(map, chip, adr);
 	spin_unlock(chip->mutex);
 	return 0;
 }
@@ -1893,22 +1617,23 @@
 
 		spin_lock(chip->mutex);
 
-		switch(chip->state) {
+		switch (chip->state) {
 		case FL_READY:
 		case FL_STATUS:
 		case FL_CFI_QUERY:
 		case FL_JEDEC_QUERY:
-			chip->oldstate = chip->state;
-			chip->state = FL_PM_SUSPENDED;
-			/* No need to wake_up() on this state change - 
-			 * as the whole point is that nobody can do anything
-			 * with the chip now anyway.
-			 */
-		case FL_PM_SUSPENDED:
+			if (chip->oldstate == FL_READY) {
+				chip->oldstate = chip->state;
+				chip->state = FL_PM_SUSPENDED;
+				/* No need to wake_up() on this state change - 
+				 * as the whole point is that nobody can do anything
+				 * with the chip now anyway.
+				 */
+			}
 			break;
-
 		default:
 			ret = -EAGAIN;
+		case FL_PM_SUSPENDED:
 			break;
 		}
 		spin_unlock(chip->mutex);
@@ -1951,7 +1676,7 @@
 		
 		/* Go to known state. Chip may have been power cycled */
 		if (chip->state == FL_PM_SUSPENDED) {
-			cfi_write(map, CMD(0xFF), 0);
+			cfi_write(map, CMD(0xFF), cfi->chips[i].start);
 			chip->state = FL_READY;
 			wake_up(&chip->wq);
 		}
diff -Nur linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0002.c linux-2.4.30-linux-mips.org/drivers/mtd/chips/cfi_cmdset_0002.c
--- linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0002.c	2004-11-17 12:54:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/cfi_cmdset_0002.c	2005-05-12 18:01:29.000000000 +0200
@@ -3,19 +3,26 @@
  *   AMD & Fujitsu Standard Vendor Command Set (ID 0x0002)
  *
  * Copyright (C) 2000 Crossnet Co. <info@crossnet.co.jp>
+ * Copyright (C) 2004 Arcom Control Systems Ltd <linux@arcom.com>
  *
  * 2_by_8 routines added by Simon Munton
  *
+ * 4_by_16 work by Carolyn J. Smith
+ *
+ * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
+ *
  * This code is GPL
  *
- * $Id: cfi_cmdset_0002.c,v 1.62 2003/01/24 23:30:13 dwmw2 Exp $
+ * $Id: cfi_cmdset_0002.c,v 1.98 2004/04/14 11:15:36 dvrabel Exp $
  *
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <asm/byteorder.h>
 
@@ -23,16 +30,22 @@
 #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/mtd.h>
 #include <linux/mtd/cfi.h>
 
 #define AMD_BOOTLOC_BUG
+#define FORCE_WORD_WRITE 0
+
 
 static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
-static int cfi_amdstd_write(struct mtd_info *, loff_t, size_t, size_t *, const 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_onesize(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 *);
@@ -45,55 +58,136 @@
 
 
 static struct mtd_chip_driver cfi_amdstd_chipdrv = {
-	probe: NULL, /* Not usable directly */
-	destroy: cfi_amdstd_destroy,
-	name: "cfi_cmdset_0002",
-	module: THIS_MODULE
+	.probe		= NULL, /* Not usable directly */
+	.destroy	= cfi_amdstd_destroy,
+	.name		= "cfi_cmdset_0002",
+	.module		= THIS_MODULE
+};
+
+
+/* #define DEBUG_LOCK_BITS */
+/* #define DEBUG_CFI_FEATURES */
+
+
+#ifdef DEBUG_CFI_FEATURES
+static void cfi_tell_features(struct cfi_pri_amdstd *extp)
+{
+	const char* erase_suspend[3] = {
+		"Not supported", "Read only", "Read/write"
+	};
+	const char* top_bottom[6] = {
+		"No WP", "8x8KiB sectors at top & bottom, no WP",
+		"Bottom boot", "Top boot",
+		"Uniform, Bottom WP", "Uniform, Top WP"
+	};
+
+	printk("  Silicon revision: %d\n", extp->SiliconRevision >> 1);
+	printk("  Address sensitive unlock: %s\n", 
+	       (extp->SiliconRevision & 1) ? "Not required" : "Required");
+
+	if (extp->EraseSuspend < ARRAY_SIZE(erase_suspend))
+		printk("  Erase Suspend: %s\n", erase_suspend[extp->EraseSuspend]);
+	else
+		printk("  Erase Suspend: Unknown value %d\n", extp->EraseSuspend);
+
+	if (extp->BlkProt == 0)
+		printk("  Block protection: Not supported\n");
+	else
+		printk("  Block protection: %d sectors per group\n", extp->BlkProt);
+
+
+	printk("  Temporary block unprotect: %s\n",
+	       extp->TmpBlkUnprotect ? "Supported" : "Not supported");
+	printk("  Block protect/unprotect scheme: %d\n", extp->BlkProtUnprot);
+	printk("  Number of simultaneous operations: %d\n", extp->SimultaneousOps);
+	printk("  Burst mode: %s\n",
+	       extp->BurstMode ? "Supported" : "Not supported");
+	if (extp->PageMode == 0)
+		printk("  Page mode: Not supported\n");
+	else
+		printk("  Page mode: %d word page\n", extp->PageMode << 2);
+
+	printk("  Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n", 
+	       extp->VppMin >> 4, extp->VppMin & 0xf);
+	printk("  Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n", 
+	       extp->VppMax >> 4, extp->VppMax & 0xf);
+
+	if (extp->TopBottom < ARRAY_SIZE(top_bottom))
+		printk("  Top/Bottom Boot Block: %s\n", top_bottom[extp->TopBottom]);
+	else
+		printk("  Top/Bottom Boot Block: Unknown value %d\n", extp->TopBottom);
+}
+#endif
+
+#ifdef AMD_BOOTLOC_BUG
+/* Wheee. Bring me the head of someone at AMD. */
+static void fixup_amd_bootblock(struct map_info *map, void* param)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
+	__u8 major = extp->MajorVersion;
+	__u8 minor = extp->MinorVersion;
+
+	if (((major << 8) | minor) < 0x3131) {
+		/* CFI version 1.0 => don't trust bootloc */
+		if (cfi->id & 0x80) {
+			printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id);
+			extp->TopBottom = 3;	/* top boot */
+		} else {
+			extp->TopBottom = 2;	/* bottom boot */
+		}
+	}
+}
+#endif
+
+static struct cfi_fixup fixup_table[] = {
+#ifdef AMD_BOOTLOC_BUG
+	{
+		0x0001,		/* AMD */
+		CFI_ID_ANY,
+		fixup_amd_bootblock, NULL
+	},
+#endif
+	{ 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;
-	int ofs_factor = cfi->interleave * cfi->device_type;
 	int i;
-	__u8 major, minor;
-	__u32 base = cfi->chips[0].start;
 
 	if (cfi->cfi_mode==CFI_MODE_CFI){
+		/* 
+		 * It's a real CFI chip, not one for which the probe
+		 * routine faked a CFI structure. So we read the feature
+		 * table from it.
+		 */
 		__u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
+		struct cfi_pri_amdstd *extp;
 
-		cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
-		
-		major = cfi_read_query(map, base + (adr+3)*ofs_factor);
-		minor = cfi_read_query(map, base + (adr+4)*ofs_factor);
-		
-		printk(KERN_NOTICE " Amd/Fujitsu Extended Query Table v%c.%c at 0x%4.4X\n",
-		       major, minor, adr);
-				cfi_send_gen_cmd(0xf0, 0x55, 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);    
+		extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu");
+		if (!extp)
+			return NULL;
+
+		/* Install our own private info structure */
+		cfi->cmdset_priv = extp;	
+
+		cfi_fixup(map, fixup_table);
+
+#ifdef DEBUG_CFI_FEATURES
+		/* Tell the user about it in lots of lovely detail */
+		cfi_tell_features(extp);
+#endif	
+
+		bootloc = extp->TopBottom;
+		if ((bootloc != 2) && (bootloc != 3)) {
+			printk(KERN_WARNING "%s: CFI does not contain boot "
+			       "bank location. Assuming top.\n", map->name);
+			bootloc = 2;
+		}
 
-		/* Wheee. Bring me the head of someone at AMD. */
-#ifdef AMD_BOOTLOC_BUG
-		if (((major << 8) | minor) < 0x3131) {
-			/* CFI version 1.0 => don't trust bootloc */
-			if (cfi->id & 0x80) {
-				printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id);
-				bootloc = 3;	/* top boot */
-			} else {
-				bootloc = 2;	/* bottom boot */
-			}
-		} else
-#endif
-			{
-				cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
-				bootloc = cfi_read_query(map, base + (adr+15)*ofs_factor);
-			}
 		if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) {
 			printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name);
 			
@@ -106,28 +200,37 @@
 				cfi->cfiq->EraseRegionInfo[j] = swap;
 			}
 		}
-		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;
+		/*
+		 * 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;
 			}
-			break;
-		case CFI_DEVICETYPE_X32:
-			cfi->addr_unlock1 = 0x1555; 
-			cfi->addr_unlock2 = 0xaaa; 
-			break;
-		default:
-			printk(KERN_NOTICE "Eep. Unknown cfi_cmdset_0002 device type %d\n", cfi->device_type);
-			return NULL;
 		}
+
 	} /* CFI mode */
 
 	for (i=0; i< cfi->numchips; i++) {
@@ -138,23 +241,25 @@
 	
 	map->fldrv = &cfi_amdstd_chipdrv;
 
-	cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL);
 	return cfi_amdstd_setup(map);
 }
 
+
 static struct mtd_info *cfi_amdstd_setup(struct map_info *map)
 {
 	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);
+	       (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;
+		printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
+		goto setup_err;
 	}
 
 	memset(mtd, 0, sizeof(*mtd));
@@ -162,86 +267,85 @@
 	mtd->type = MTD_NORFLASH;
 	/* Also select the correct geometry setup too */ 
 	mtd->size = devsize * cfi->numchips;
-	
-	if (cfi->cfiq->NumEraseRegions == 1) {
-		/* No need to muck about with multiple erase sizes */
-		mtd->erasesize = ((cfi->cfiq->EraseRegionInfo[0] >> 8) & ~0xff) * cfi->interleave;
-	} else {
-		unsigned long offset = 0;
-		int i,j;
 
-		mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
-		mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL);
-		if (!mtd->eraseregions) { 
-			printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n");
-			goto setup_err;
-		}
+	mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
+	mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info)
+				    * mtd->numeraseregions, GFP_KERNEL);
+	if (!mtd->eraseregions) { 
+		printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n");
+		goto setup_err;
+	}
 			
-		for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
-			unsigned long ernum, ersize;
-			ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave;
-			ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1;
+	for (i=0; i<cfi->cfiq->NumEraseRegions; i++) {
+		unsigned long ernum, ersize;
+		ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave;
+		ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1;
 			
-			if (mtd->erasesize < ersize) {
-				mtd->erasesize = ersize;
-			}
-			for (j=0; j<cfi->numchips; j++) {
-				mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset;
-				mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize;
-				mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
-			}
-			offset += (ersize * ernum);
-		}
-		if (offset != devsize) {
-			/* Argh */
-			printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
-			goto setup_err;
+		if (mtd->erasesize < ersize) {
+			mtd->erasesize = ersize;
 		}
+		for (j=0; j<cfi->numchips; j++) {
+			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset;
+			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize;
+			mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum;
+		}
+		offset += (ersize * ernum);
+	}
+	if (offset != devsize) {
+		/* Argh */
+		printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
+		goto setup_err;
+	}
 #if 0
-		// debug
-		for (i=0; i<mtd->numeraseregions;i++){
-			printk("%d: offset=0x%x,size=0x%x,blocks=%d\n",
-			       i,mtd->eraseregions[i].offset,
-			       mtd->eraseregions[i].erasesize,
-			       mtd->eraseregions[i].numblocks);
-		}
-#endif
+	// debug
+	for (i=0; i<mtd->numeraseregions;i++){
+		printk("%d: offset=0x%x,size=0x%x,blocks=%d\n",
+		       i,mtd->eraseregions[i].offset,
+		       mtd->eraseregions[i].erasesize,
+		       mtd->eraseregions[i].numblocks);
 	}
+#endif
 
 	switch (CFIDEV_BUSWIDTH)
 	{
 	case 1:
 	case 2:
 	case 4:
-#if 1
-		if (mtd->numeraseregions > 1)
-			mtd->erase = cfi_amdstd_erase_varsize;
-		else
+#ifdef CFI_WORD_64
+	case 8:
 #endif
-		if (((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1)
+		if (mtd->numeraseregions == 1
+		    && ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) {
 			mtd->erase = cfi_amdstd_erase_chip;
-		else
-			mtd->erase = cfi_amdstd_erase_onesize;
+		} 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;
-		mtd->write = cfi_amdstd_write;
 		break;
 
 	default:
-	        printk(KERN_WARNING "Unsupported buswidth\n");
+		printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
+		       __func__, CFIDEV_BUSWIDTH);
 		goto setup_err;
 		break;
 	}
-	if (cfi->fast_prog) {
-		/* In cfi_amdstd_write() we frob the protection stuff
-		   without paying any attention to the state machine.
-		   This upsets in-progress erases. So we turn this flag
-		   off for now till the code gets fixed. */
-		printk(KERN_NOTICE "cfi_cmdset_0002: Disabling fast programming due to code brokenness.\n");
-		cfi->fast_prog = 0;
-	}
+	/* 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? */
+	/* does this chip have a secsi area? */
 	if(cfi->mfr==1){
 		
 		switch(cfi->id){
@@ -266,7 +370,7 @@
 	mtd->flags = MTD_CAP_NORFLASH;
 	map->fldrv = &cfi_amdstd_chipdrv;
 	mtd->name = map->name;
-	MOD_INC_USE_COUNT;
+	__module_get(THIS_MODULE);
 	return mtd;
 
  setup_err:
@@ -280,46 +384,181 @@
 	return NULL;
 }
 
-static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
+/*
+ * Return true if the chip is ready.
+ *
+ * Ready is one of: read mode, query mode, erase-suspend-read mode (in any
+ * non-suspended sector) and is indicated by no toggle 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 chip_ready(struct map_info *map, unsigned long addr)
+{
+    cfi_word d, t;
+
+    d = cfi_read(map, addr);
+    t = d ^ cfi_read(map, addr);
+
+    return t == 0;
+}
+
+static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
 {
 	DECLARE_WAITQUEUE(wait, current);
-	unsigned long timeo = jiffies + HZ;
+	struct cfi_private *cfi = map->fldrv_priv;
+	unsigned long timeo;
+	struct cfi_pri_amdstd *cfip = (struct cfi_pri_amdstd *)cfi->cmdset_priv;
 
+ resettime:
+	timeo = jiffies + HZ;
  retry:
-	cfi_spin_lock(chip->mutex);
+	switch (chip->state) {
 
-	if (chip->state != FL_READY){
-#if 0
-	        printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);
-#endif
+	case FL_STATUS:
+		for (;;) {
+			if (chip_ready(map, adr))
+				break;
+
+			if (time_after(jiffies, timeo)) {
+				printk(KERN_ERR "Waiting for chip to be ready timed out.\n");
+				cfi_spin_unlock(chip->mutex);
+				return -EIO;
+			}
+			cfi_spin_unlock(chip->mutex);
+			cfi_udelay(1);
+			cfi_spin_lock(chip->mutex);
+			/* Someone else might have been playing with it. */
+			goto retry;
+		}
+				
+	case FL_READY:
+	case FL_CFI_QUERY:
+	case FL_JEDEC_QUERY:
+		return 0;
+
+	case FL_ERASING:
+		if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */
+			goto sleep;
+
+		if (!(mode == FL_READY || mode == FL_POINT
+		      || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2))
+		      || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1))))
+			goto sleep;
+
+		/* We could check to see if we're trying to access the sector
+		 * that is currently being erased. However, no user will try
+		 * anything like that so we just wait for the timeout. */
+
+		/* 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);
+		chip->oldstate = FL_ERASING;
+		chip->state = FL_ERASE_SUSPENDING;
+		chip->erase_suspended = 1;
+		for (;;) {
+			if (chip_ready(map, adr))
+				break;
+
+			if (time_after(jiffies, timeo)) {
+				/* Should have suspended the erase by now.
+				 * Send an Erase-Resume command as either
+				 * 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);
+				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);
+			cfi_udelay(1);
+			cfi_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_POINT:
+		/* Only if there's no operation suspended... */
+		if (mode == FL_READY && chip->oldstate == FL_READY)
+			return 0;
+
+	default:
+	sleep:
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		add_wait_queue(&chip->wq, &wait);
-                
 		cfi_spin_unlock(chip->mutex);
-
 		schedule();
 		remove_wait_queue(&chip->wq, &wait);
-#if 0
-		if(signal_pending(current))
-			return -EINTR;
-#endif
-		timeo = jiffies + HZ;
+		cfi_spin_lock(chip->mutex);
+		goto resettime;
+	}
+}
 
-		goto retry;
-	}	
+
+static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+
+	switch(chip->oldstate) {
+	case FL_ERASING:
+		chip->state = chip->oldstate;
+		cfi_write(map, CMD(0x30), chip->in_progress_block_addr);
+		chip->oldstate = FL_READY;
+		chip->state = FL_ERASING;
+		break;
+
+	case FL_READY:
+	case FL_STATUS:
+		/* We should really make set_vpp() count, rather than doing this */
+		DISABLE_VPP(map);
+		break;
+	default:
+		printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate);
+	}
+	wake_up(&chip->wq);
+}
+
+
+static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
+{
+	unsigned long cmd_addr;
+	struct cfi_private *cfi = map->fldrv_priv;
+	int ret;
 
 	adr += chip->start;
 
-	chip->state = FL_READY;
+	/* Ensure cmd read/writes are aligned. */ 
+	cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); 
+
+	cfi_spin_lock(chip->mutex);
+	ret = get_chip(map, chip, cmd_addr, FL_READY);
+	if (ret) {
+		cfi_spin_unlock(chip->mutex);
+		return ret;
+	}
 
-	map->copy_from(map, buf, adr, len);
+	if (chip->state != FL_POINT && chip->state != FL_READY) {
+		cfi_write(map, CMD(0xf0), cmd_addr);
+		chip->state = FL_READY;
+	}
 
-	wake_up(&chip->wq);
-	cfi_spin_unlock(chip->mutex);
+	map_copy_from(map, buf, adr, len);
 
+	put_chip(map, chip, cmd_addr);
+
+	cfi_spin_unlock(chip->mutex);
 	return 0;
 }
 
+
 static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
 {
 	struct map_info *map = mtd->priv;
@@ -361,6 +600,7 @@
 	return ret;
 }
 
+
 static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
 {
 	DECLARE_WAITQUEUE(wait, current);
@@ -372,11 +612,11 @@
 
 	if (chip->state != FL_READY){
 #if 0
-	        printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);
+		printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state);
 #endif
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		add_wait_queue(&chip->wq, &wait);
-                
+		
 		cfi_spin_unlock(chip->mutex);
 
 		schedule();
@@ -393,13 +633,15 @@
 	adr += chip->start;
 
 	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);
+	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);
@@ -454,124 +696,122 @@
 	return ret;
 }
 
-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum, int fast)
+
+static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum)
 {
-	unsigned long timeo = jiffies + HZ;
-	unsigned int oldstatus, status;
-	unsigned int dq6, dq5;	
 	struct cfi_private *cfi = map->fldrv_priv;
-	DECLARE_WAITQUEUE(wait, current);
+	unsigned long timeo = jiffies + HZ;
+	/*
+	 * We use a 1ms + 1 jiffies generic timeout for writes (most devices
+	 * have a max write time of a few hundreds usec). However, we should
+	 * use the maximum timeout value given by the chip at probe time
+	 * instead.  Unfortunately, struct flchip does have a field for
+	 * maximum timeout, only for typical which can be far too short
+	 * depending of the conditions.	 The ' + 1' is to avoid having a
+	 * timeout of 0 jiffies if HZ is smaller than 1000.
+	 */
+	unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
+	int ret = 0;
 
- retry:
-	cfi_spin_lock(chip->mutex);
+	adr += chip->start;
 
-	if (chip->state != FL_READY) {
-#if 0
-	        printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", chip->state);
-#endif
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
-                
+	cfi_spin_lock(chip->mutex);
+	ret = get_chip(map, chip, adr, FL_WRITING);
+	if (ret) {
 		cfi_spin_unlock(chip->mutex);
+		return ret;
+	}
 
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-#if 0
-		printk(KERN_DEBUG "Wake up to write:\n");
-		if(signal_pending(current))
-			return -EINTR;
-#endif
-		timeo = jiffies + HZ;
-
-		goto retry;
-	}	
-
-	chip->state = FL_WRITING;
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n",
+	       __func__, adr, datum );
 
-	adr += chip->start;
-	ENABLE_VPP(map);
-	if (fast) { /* Unlock bypass */
-		cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL);
-	}
-	else {
-	        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);
+	/*
+	 * Check for a NOP for the case when the datum to write is already
+	 * present - it saves time and works around buggy chips that corrupt
+	 * data at other locations when 0xff is written to a location that
+	 * already contains 0xff.
+	 */
+	if (cfi_read(map, adr) == datum) {
+		DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): NOP\n",
+		       __func__);
+		goto op_done;
 	}
 
+	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);
+	chip->state = FL_WRITING;
 
 	cfi_spin_unlock(chip->mutex);
 	cfi_udelay(chip->word_write_time);
 	cfi_spin_lock(chip->mutex);
 
-	/* Polling toggle bits instead of reading back many times
-	   This ensures that write operation is really completed,
-	   or tells us why it failed. */        
-	dq6 = CMD(1<<6);
-	dq5 = CMD(1<<5);
-	timeo = jiffies + (HZ/1000); /* setting timeout to 1ms for now */
-		
-	oldstatus = cfi_read(map, adr);
-	status = cfi_read(map, adr);
-
-	while( (status & dq6) != (oldstatus & dq6) && 
-	       (status & dq5) != dq5 &&
-	       !time_after(jiffies, timeo) ) {
+	/* See comment above for timeout value. */
+	timeo = jiffies + uWriteTimeout; 
+	for (;;) {
+		if (chip->state != FL_WRITING) {
+			/* Someone's suspended the write. Sleep */
+			DECLARE_WAITQUEUE(wait, current);
 
-		if (need_resched()) {
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			add_wait_queue(&chip->wq, &wait);
 			cfi_spin_unlock(chip->mutex);
-			yield();
+			schedule();
+			remove_wait_queue(&chip->wq, &wait);
+			timeo = jiffies + (HZ / 2); /* FIXME */
 			cfi_spin_lock(chip->mutex);
-		} else 
-			udelay(1);
-
-		oldstatus = cfi_read( map, adr );
-		status = cfi_read( map, adr );
-	}
-	
-	if( (status & dq6) != (oldstatus & dq6) ) {
-		/* The erasing didn't stop?? */
-		if( (status & dq5) == dq5 ) {
-			/* When DQ5 raises, we must check once again
-			   if DQ6 is toggling.  If not, the erase has been
-			   completed OK.  If not, reset chip. */
-			oldstatus = cfi_read(map, adr);
-			status = cfi_read(map, adr);
-		    
-			if ( (oldstatus & 0x00FF) == (status & 0x00FF) ) {
-				printk(KERN_WARNING "Warning: DQ5 raised while program operation was in progress, however operation completed OK\n" );
-			} else { 
-				/* DQ5 is active so we can do a reset and stop the erase */
-				cfi_write(map, CMD(0xF0), chip->start);
-				printk(KERN_WARNING "Internal flash device timeout occurred or write operation was performed while flash was programming.\n" );
-			}
-		} else {
-			printk(KERN_WARNING "Waiting for write to complete timed out in do_write_oneword.");        
-			
-			chip->state = FL_READY;
-			wake_up(&chip->wq);
-			cfi_spin_unlock(chip->mutex);
-			DISABLE_VPP(map);
-			return -EIO;
+			continue;
 		}
+
+		if (chip_ready(map, 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);
 	}
 
-	DISABLE_VPP(map);
+	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;
-	wake_up(&chip->wq);
+	put_chip(map, chip, adr);
 	cfi_spin_unlock(chip->mutex);
 
-	return 0;
+	return ret;
 }
 
-static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf)
+
+static int cfi_amdstd_write_words(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;
 	int ret = 0;
 	int chipnum;
 	unsigned long ofs, chipstart;
+	DECLARE_WAITQUEUE(wait, current);
 
 	*retlen = 0;
 	if (!len)
@@ -586,23 +826,56 @@
 		unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1);
 		int i = ofs - bus_ofs;
 		int n = 0;
-		u_char tmp_buf[4];
-		__u32 datum;
+		u_char tmp_buf[8];
+		cfi_word datum;
+
+ retry:
+		cfi_spin_lock(cfi->chips[chipnum].mutex);
+
+		if (cfi->chips[chipnum].state != FL_READY) {
+#if 0
+			printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
+#endif
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			add_wait_queue(&cfi->chips[chipnum].wq, &wait);
+
+			cfi_spin_unlock(cfi->chips[chipnum].mutex);
+
+			schedule();
+			remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
+#if 0
+			if(signal_pending(current))
+				return -EINTR;
+#endif
+			goto retry;
+		}
+
+		map_copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
 
-		map->copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
-		while (len && i < CFIDEV_BUSWIDTH)
-			tmp_buf[i++] = buf[n++], len--;
+		cfi_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 {
-			return -EINVAL;  /* should never happen, but be safe */
+			printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
+			       __func__, CFIDEV_BUSWIDTH);
+			return -EINVAL;
 		}
 
 		ret = do_write_oneword(map, &cfi->chips[chipnum], 
-				bus_ofs, datum, 0);
+				       bus_ofs, datum);
 		if (ret) 
 			return ret;
 		
@@ -618,16 +891,9 @@
 		}
 	}
 	
-	if (cfi->fast_prog) {
-		/* Go into unlock bypass mode */
-		cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL);
-		cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL);
-		cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	}
-
 	/* We are now aligned, write as much as possible */
 	while(len >= CFIDEV_BUSWIDTH) {
-		__u32 datum;
+		cfi_word datum;
 
 		if (cfi_buswidth_is_1()) {
 			datum = *(__u8*)buf;
@@ -635,19 +901,19 @@
 			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, cfi->fast_prog);
-		if (ret) {
-			if (cfi->fast_prog){
-				/* Get out of unlock bypass mode */
-				cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL);
-				cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL);
-			}
+				       ofs, datum);
+		if (ret)
 			return ret;
-		}
 
 		ofs += CFIDEV_BUSWIDTH;
 		buf += CFIDEV_BUSWIDTH;
@@ -655,52 +921,64 @@
 		len -= CFIDEV_BUSWIDTH;
 
 		if (ofs >> cfi->chipshift) {
-			if (cfi->fast_prog){
-				/* Get out of unlock bypass mode */
-				cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL);
-				cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL);
-			}
-
 			chipnum ++; 
 			ofs = 0;
 			if (chipnum == cfi->numchips)
 				return 0;
 			chipstart = cfi->chips[chipnum].start;
-			if (cfi->fast_prog){
-				/* Go into unlock bypass mode for next set of chips */
-				cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL);
-				cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL);
-				cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL);
-			}
 		}
 	}
 
-	if (cfi->fast_prog){
-		/* Get out of unlock bypass mode */
-		cfi_send_gen_cmd(0x90, 0, chipstart, map, cfi, cfi->device_type, NULL);
-		cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL);
-	}
-
 	/* Write the trailing bytes if any */
 	if (len & (CFIDEV_BUSWIDTH-1)) {
 		int i = 0, n = 0;
-		u_char tmp_buf[4];
-		__u32 datum;
+		u_char tmp_buf[8];
+		cfi_word datum;
 
-		map->copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
-		while (len--)
-			tmp_buf[i++] = buf[n++];
+ retry1:
+		cfi_spin_lock(cfi->chips[chipnum].mutex);
 
-		if (cfi_buswidth_is_2()) {
-			datum = *(__u16*)tmp_buf;
-		} else if (cfi_buswidth_is_4()) {
-			datum = *(__u32*)tmp_buf;
+		if (cfi->chips[chipnum].state != FL_READY) {
+#if 0
+			printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
+#endif
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			add_wait_queue(&cfi->chips[chipnum].wq, &wait);
+
+			cfi_spin_unlock(cfi->chips[chipnum].mutex);
+
+			schedule();
+			remove_wait_queue(&cfi->chips[chipnum].wq, &wait);
+#if 0
+			if(signal_pending(current))
+				return -EINTR;
+#endif
+			goto retry1;
+		}
+
+		map_copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
+
+		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 {
-			return -EINVAL;  /* should never happen, but be safe */
+			printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
+			       __func__, CFIDEV_BUSWIDTH);
+			return -EINVAL;
 		}
 
 		ret = do_write_oneword(map, &cfi->chips[chipnum], 
-				ofs, datum, 0);
+				ofs, datum);
 		if (ret) 
 			return ret;
 		
@@ -710,289 +988,353 @@
 	return 0;
 }
 
-static inline int do_erase_chip(struct map_info *map, struct flchip *chip)
+
+/*
+ * 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)
 {
-	unsigned int oldstatus, status;
-	unsigned int dq6, dq5;
-	unsigned long timeo = jiffies + HZ;
-	unsigned int adr;
 	struct cfi_private *cfi = map->fldrv_priv;
-	DECLARE_WAITQUEUE(wait, current);
+	unsigned long timeo = jiffies + HZ;
+	/* see comments in do_write_oneword() regarding uWriteTimeo. */
+	static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
+	int ret = -EIO;
+	unsigned long cmd_adr;
+	int z, bytes, words;
+	cfi_word datum;
 
- retry:
-	cfi_spin_lock(chip->mutex);
+	adr += chip->start;
+	cmd_adr = adr;
 
-	if (chip->state != FL_READY){
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
-                
+	cfi_spin_lock(chip->mutex);
+	ret = get_chip(map, chip, adr, FL_WRITING);
+	if (ret) {
 		cfi_spin_unlock(chip->mutex);
+		return ret;
+	}
 
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-#if 0
-		if(signal_pending(current))
-			return -EINTR;
+	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
-		timeo = jiffies + HZ;
+	} else {
+		printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n",
+		       __func__, CFIDEV_BUSWIDTH);
+		return -EINVAL;
+	}
 
-		goto retry;
-	}	
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n",
+	       __func__, adr, datum );
 
-	chip->state = FL_ERASING;
-	
-	/* Handle devices with one erase region, that only implement
-	 * the chip erase command.
-	 */
 	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);
-	timeo = jiffies + (HZ*20);
-	adr = cfi->addr_unlock1;
+	//cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
 
-	/* Wait for the end of programing/erasure by using the toggle method.
-	 * As long as there is a programming procedure going on, bit 6 of the last
-	 * written byte is toggling it's state with each consectuve read.
-	 * The toggling stops as soon as the procedure is completed.
-	 *
-	 * If the process has gone on for too long on the chip bit 5 gets.
-	 * After bit5 is set you can kill the operation by sending a reset
-	 * command to the chip.
-	 */
-	dq6 = CMD(1<<6);
-	dq5 = CMD(1<<5);
+	/* Write Buffer Load */
+	cfi_write(map, CMD(0x25), cmd_adr);
 
-	oldstatus = cfi_read(map, adr);
-	status = cfi_read(map, adr);
-	while( ((status & dq6) != (oldstatus & dq6)) && 
-		((status & dq5) != dq5) &&
-		!time_after(jiffies, timeo)) {
-		int wait_reps;
+	chip->state = FL_WRITING_TO_BUFFER;
 
-		/* an initial short sleep */
-		cfi_spin_unlock(chip->mutex);
-		schedule_timeout(HZ/100);
-		cfi_spin_lock(chip->mutex);
+	/* Write length of data to come */
+	bytes = len & (CFIDEV_BUSWIDTH-1);
+	words = len / CFIDEV_BUSWIDTH;
+	cfi_write(map, CMD(words - !bytes), 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;
+	}
+
+	adr += z;
+
+	/* Write Buffer Program Confirm: GO GO GO */
+	cfi_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);
+
+	timeo = jiffies + uWriteTimeout; 
 		
-		if (chip->state != FL_ERASING) {
-			/* Someone's suspended the erase. Sleep */
+	for (;;) {
+		if (chip->state != FL_WRITING) {
+			/* Someone's suspended the write. Sleep */
+			DECLARE_WAITQUEUE(wait, current);
+
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			add_wait_queue(&chip->wq, &wait);
-			
 			cfi_spin_unlock(chip->mutex);
-			printk("erase suspended. Sleeping\n");
-			
 			schedule();
 			remove_wait_queue(&chip->wq, &wait);
-#if 0			
-			if (signal_pending(current))
-				return -EINTR;
-#endif			
-			timeo = jiffies + (HZ*2); /* FIXME */
+			timeo = jiffies + (HZ / 2); /* FIXME */
 			cfi_spin_lock(chip->mutex);
 			continue;
 		}
 
-		/* Busy wait for 1/10 of a milisecond */
-		for(wait_reps = 0;
-		    	(wait_reps < 100) &&
-			((status & dq6) != (oldstatus & dq6)) && 
-			((status & dq5) != dq5);
-			wait_reps++) {
-			
-			/* Latency issues. Drop the lock, wait a while and retry */
-			cfi_spin_unlock(chip->mutex);
-			
-			cfi_udelay(1);
-		
-			cfi_spin_lock(chip->mutex);
-			oldstatus = cfi_read(map, adr);
-			status = cfi_read(map, adr);
-		}
-		oldstatus = cfi_read(map, adr);
-		status = cfi_read(map, adr);
-	}
-	if ((status & dq6) != (oldstatus & dq6)) {
-		/* The erasing didn't stop?? */
-		if ((status & dq5) == dq5) {
-			/* dq5 is active so we can do a reset and stop the erase */
-			cfi_write(map, CMD(0xF0), chip->start);
-		}
-		chip->state = FL_READY;
-		wake_up(&chip->wq);
+		if (chip_ready(map, 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);
-		printk("waiting for erase to complete timed out.");
-		DISABLE_VPP(map);
-		return -EIO;
+		cfi_udelay(1);
+		cfi_spin_lock(chip->mutex);
 	}
-	DISABLE_VPP(map);
+
+	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;
-	wake_up(&chip->wq);
+	put_chip(map, chip, adr);
 	cfi_spin_unlock(chip->mutex);
 
+	return ret;
+}
+
+
+static int cfi_amdstd_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;
+	int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
+	int ret = 0;
+	int chipnum;
+	unsigned long ofs;
+
+	*retlen = 0;
+	if (!len)
+		return 0;
+
+	chipnum = to >> cfi->chipshift;
+	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 (local_len > len)
+			local_len = len;
+		ret = cfi_amdstd_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) {
+		/* 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);
+		if (ret)
+			return ret;
+
+		ofs += size;
+		buf += size;
+		(*retlen) += size;
+		len -= size;
+
+		if (ofs >> cfi->chipshift) {
+			chipnum ++; 
+			ofs = 0;
+			if (chipnum == cfi->numchips)
+				return 0;
+		}
+	}
+
 	return 0;
 }
 
-static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr)
+
+/*
+ * 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)
 {
-	unsigned int oldstatus, status;
-	unsigned int dq6, dq5;
-	unsigned long timeo = jiffies + HZ;
 	struct cfi_private *cfi = map->fldrv_priv;
+	unsigned long timeo = jiffies + HZ;
+	unsigned long int adr;
 	DECLARE_WAITQUEUE(wait, current);
+	int ret = 0;
 
- retry:
-	cfi_spin_lock(chip->mutex);
+	adr = cfi->addr_unlock1;
 
-	if (chip->state != FL_READY){
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
-                
+	cfi_spin_lock(chip->mutex);
+	ret = get_chip(map, chip, adr, FL_WRITING);
+	if (ret) {
 		cfi_spin_unlock(chip->mutex);
+		return ret;
+	}
 
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-#if 0
-		if(signal_pending(current))
-			return -EINTR;
-#endif
-		timeo = jiffies + HZ;
-
-		goto retry;
-	}	
-
-	chip->state = FL_ERASING;
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
+	       __func__, chip->start );
 
-	adr += chip->start;
 	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);
-	
-	timeo = jiffies + (HZ*20);
+	cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
 
-	/* Wait for the end of programing/erasure by using the toggle method.
-	 * As long as there is a programming procedure going on, bit 6 of the last
-	 * written byte is toggling it's state with each consectuve read.
-	 * The toggling stops as soon as the procedure is completed.
-	 *
-	 * If the process has gone on for too long on the chip bit 5 gets.
-	 * After bit5 is set you can kill the operation by sending a reset
-	 * command to the chip.
-	 */
-	dq6 = CMD(1<<6);
-	dq5 = CMD(1<<5);
+	chip->state = FL_ERASING;
+	chip->erase_suspended = 0;
+	chip->in_progress_block_addr = adr;
 
-	oldstatus = cfi_read(map, adr);
-	status = cfi_read(map, adr);
-	while( ((status & dq6) != (oldstatus & dq6)) && 
-		((status & dq5) != dq5) &&
-		!time_after(jiffies, timeo)) {
-		int wait_reps;
+	cfi_spin_unlock(chip->mutex);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout((chip->erase_time*HZ)/(2*1000));
+	cfi_spin_lock(chip->mutex);
 
-		/* an initial short sleep */
-		cfi_spin_unlock(chip->mutex);
-		schedule_timeout(HZ/100);
-		cfi_spin_lock(chip->mutex);
-		
+	timeo = jiffies + (HZ*20);
+
+	for (;;) {
 		if (chip->state != FL_ERASING) {
 			/* Someone's suspended the erase. Sleep */
 			set_current_state(TASK_UNINTERRUPTIBLE);
 			add_wait_queue(&chip->wq, &wait);
-			
 			cfi_spin_unlock(chip->mutex);
-			printk(KERN_DEBUG "erase suspended. Sleeping\n");
-			
 			schedule();
 			remove_wait_queue(&chip->wq, &wait);
-#if 0			
-			if (signal_pending(current))
-				return -EINTR;
-#endif			
-			timeo = jiffies + (HZ*2); /* FIXME */
 			cfi_spin_lock(chip->mutex);
 			continue;
 		}
-
-		/* Busy wait for 1/10 of a milisecond */
-		for(wait_reps = 0;
-		    	(wait_reps < 100) &&
-			((status & dq6) != (oldstatus & dq6)) && 
-			((status & dq5) != dq5);
-			wait_reps++) {
-			
-			/* Latency issues. Drop the lock, wait a while and retry */
-			cfi_spin_unlock(chip->mutex);
-			
-			cfi_udelay(1);
-		
-			cfi_spin_lock(chip->mutex);
-			oldstatus = cfi_read(map, adr);
-			status = cfi_read(map, adr);
-		}
-		oldstatus = cfi_read(map, adr);
-		status = cfi_read(map, adr);
-	}
-	if( (status & dq6) != (oldstatus & dq6) ) 
-	{                                       
-		/* The erasing didn't stop?? */
-		if( ( status & dq5 ) == dq5 ) 
-		{   			
-			/* When DQ5 raises, we must check once again if DQ6 is toggling.
-               If not, the erase has been completed OK.  If not, reset chip. */
-		    oldstatus   = cfi_read( map, adr );
-		    status      = cfi_read( map, adr );
-		    
-		    if( ( oldstatus & 0x00FF ) == ( status & 0x00FF ) )
-		    {
-                printk( "Warning: DQ5 raised while erase operation was in progress, but erase completed OK\n" ); 		    
-		    } 			
-			else
-            {
-			    /* DQ5 is active so we can do a reset and stop the erase */
-				cfi_write(map, CMD(0xF0), chip->start);
-                printk( KERN_WARNING "Internal flash device timeout occured or write operation was performed while flash was erasing\n" );
-			}
+		if (chip->erase_suspended) {
+			/* This erase was suspended and resumed.
+			   Adjust the timeout */
+			timeo = jiffies + (HZ*20); /* FIXME */
+			chip->erase_suspended = 0;
 		}
-        else
-        {
-		    printk( "Waiting for erase to complete timed out in do_erase_oneblock.");        
-		    
-		chip->state = FL_READY;
-		wake_up(&chip->wq);
+
+		if (chip_ready(map, 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);
-		DISABLE_VPP(map);
-		return -EIO;
-	}
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+		cfi_spin_lock(chip->mutex);
 	}
 
-	DISABLE_VPP(map);
+	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;
-	wake_up(&chip->wq);
+	put_chip(map, chip, adr);
 	cfi_spin_unlock(chip->mutex);
-	return 0;
+
+	return ret;
 }
 
-static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
+
+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, len;
+	unsigned long adr;
 	int chipnum, ret = 0;
 	int i, first;
 	struct mtd_erase_region_info *regions = mtd->eraseregions;
 
-	if (instr->addr > mtd->size)
+	if (ofs > mtd->size)
 		return -EINVAL;
 
-	if ((instr->len + instr->addr) > mtd->size)
+	if ((len + ofs) > mtd->size)
 		return -EINVAL;
 
 	/* Check that both start and end of the requested erase are
@@ -1007,7 +1349,7 @@
 	   start of the requested erase, and then go back one.
 	*/
 	
-	while (i < mtd->numeraseregions && instr->addr >= regions[i].offset)
+	while (i < mtd->numeraseregions && ofs >= regions[i].offset)
 	       i++;
 	i--;
 
@@ -1017,7 +1359,7 @@
 	   effect here.
 	*/
 
-	if (instr->addr & (regions[i].erasesize-1))
+	if (ofs & (regions[i].erasesize-1))
 		return -EINVAL;
 
 	/* Remember the erase region we start on */
@@ -1027,7 +1369,7 @@
 	 * with the erase region at that address.
 	 */
 
-	while (i<mtd->numeraseregions && (instr->addr + instr->len) >= regions[i].offset)
+	while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
 		i++;
 
 	/* As before, drop back one to point at the region in which
@@ -1035,18 +1377,17 @@
 	*/
 	i--;
 	
-	if ((instr->addr + instr->len) & (regions[i].erasesize-1))
+	if ((ofs + len) & (regions[i].erasesize-1))
 		return -EINVAL;
-	
-	chipnum = instr->addr >> cfi->chipshift;
-	adr = instr->addr - (chipnum << cfi->chipshift);
-	len = instr->len;
 
-	i=first;
+	chipnum = ofs >> cfi->chipshift;
+	adr = ofs - (chipnum << cfi->chipshift);
 
-	while(len) {
-		ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr);
+	i=first;
 
+	while (len) {
+		ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk);
+		
 		if (ret)
 			return ret;
 
@@ -1065,51 +1406,108 @@
 		}
 	}
 
-	instr->state = MTD_ERASE_DONE;
-	if (instr->callback)
-		instr->callback(instr);
-	
 	return 0;
 }
 
-static int cfi_amdstd_erase_onesize(struct mtd_info *mtd, struct erase_info *instr)
+
+static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
 {
-	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	unsigned long adr, len;
-	int chipnum, ret = 0;
+	unsigned long timeo = jiffies + HZ;
+	DECLARE_WAITQUEUE(wait, current);
+	int ret = 0;
 
-	if (instr->addr & (mtd->erasesize - 1))
-		return -EINVAL;
+	adr += chip->start;
 
-	if (instr->len & (mtd->erasesize -1))
-		return -EINVAL;
+	cfi_spin_lock(chip->mutex);
+	ret = get_chip(map, chip, adr, FL_ERASING);
+	if (ret) {
+		cfi_spin_unlock(chip->mutex);
+		return ret;
+	}
 
-	if ((instr->len + instr->addr) > mtd->size)
-		return -EINVAL;
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
+	       __func__, adr );
 
-	chipnum = instr->addr >> cfi->chipshift;
-	adr = instr->addr - (chipnum << cfi->chipshift);
-	len = instr->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);
 
-	while(len) {
-		ret = do_erase_oneblock(map, &cfi->chips[chipnum], 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);
 
-		if (ret)
-			return ret;
+	timeo = jiffies + (HZ*20);
 
-		adr += mtd->erasesize;
-		len -= mtd->erasesize;
+	for (;;) {
+		if (chip->state != FL_ERASING) {
+			/* Someone's suspended the erase. Sleep */
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			add_wait_queue(&chip->wq, &wait);
+			cfi_spin_unlock(chip->mutex);
+			schedule();
+			remove_wait_queue(&chip->wq, &wait);
+			cfi_spin_lock(chip->mutex);
+			continue;
+		}
+		if (chip->erase_suspended) {
+			/* This erase was suspended and resumed.
+			   Adjust the timeout */
+			timeo = jiffies + (HZ*20); /* FIXME */
+			chip->erase_suspended = 0;
+		}
 
-		if (adr >> cfi->chipshift) {
-			adr = 0;
-			chipnum++;
-			
-			if (chipnum >= cfi->numchips)
+		if (chip_ready(map, 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);
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(1);
+		cfi_spin_lock(chip->mutex);
 	}
-		
+	
+	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);
+	return ret;
+}
+
+
+int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
+{
+	unsigned long ofs, len;
+	int ret;
+
+	ofs = instr->addr;
+	len = instr->len;
+
+	ret = cfi_amdstd_varsize_frob(mtd, do_erase_oneblock, ofs, len, 0);
+	if (ret)
+		return ret;
+
 	instr->state = MTD_ERASE_DONE;
 	if (instr->callback)
 		instr->callback(instr);
@@ -1117,6 +1515,7 @@
 	return 0;
 }
 
+
 static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr)
 {
 	struct map_info *map = mtd->priv;
@@ -1140,6 +1539,7 @@
 	return 0;
 }
 
+
 static void cfi_amdstd_sync (struct mtd_info *mtd)
 {
 	struct map_info *map = mtd->priv;
@@ -1178,7 +1578,7 @@
 
 			schedule();
 
-		        remove_wait_queue(&chip->wq, &wait);
+			remove_wait_queue(&chip->wq, &wait);
 			
 			goto retry;
 		}
@@ -1237,7 +1637,7 @@
 	/* Unlock the chips again */
 
 	if (ret) {
-    		for (i--; i >=0; i--) {
+		for (i--; i >=0; i--) {
 			chip = &cfi->chips[i];
 
 			cfi_spin_lock(chip->mutex);
@@ -1253,6 +1653,7 @@
 	return ret;
 }
 
+
 static void cfi_amdstd_resume(struct mtd_info *mtd)
 {
 	struct map_info *map = mtd->priv;
@@ -1278,6 +1679,137 @@
 	}
 }
 
+
+#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;
+	}
+
+	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;
@@ -1290,21 +1822,23 @@
 
 static char im_name[]="cfi_cmdset_0002";
 
+
 int __init cfi_amdstd_init(void)
 {
 	inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002);
 	return 0;
 }
 
+
 static void __exit cfi_amdstd_exit(void)
 {
 	inter_module_unregister(im_name);
 }
 
+
 module_init(cfi_amdstd_init);
 module_exit(cfi_amdstd_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp> et al.");
 MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips");
-
diff -Nur linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0020.c linux-2.4.30-linux-mips.org/drivers/mtd/chips/cfi_cmdset_0020.c
--- linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0020.c	2004-11-17 12:54:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/cfi_cmdset_0020.c	2005-05-12 18:01:29.000000000 +0200
@@ -21,16 +21,19 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/sched.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #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/compatmac.h>
+#include <linux/mtd/mtd.h>
 
 
 static int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *);
@@ -51,10 +54,10 @@
 static struct mtd_info *cfi_staa_setup (struct map_info *);
 
 static struct mtd_chip_driver cfi_staa_chipdrv = {
-	probe: NULL, /* Not usable directly */
-	destroy: cfi_staa_destroy,
-	name: "cfi_cmdset_0020",
-	module: THIS_MODULE
+	.probe		= NULL, /* Not usable directly */
+	.destroy	= cfi_staa_destroy,
+	.name		= "cfi_cmdset_0020",
+	.module		= THIS_MODULE
 };
 
 /* #define DEBUG_LOCK_BITS */
@@ -113,7 +116,6 @@
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	int i;
-	__u32 base = cfi->chips[0].start;
 
 	if (cfi->cfi_mode) {
 		/* 
@@ -123,36 +125,11 @@
 		 */
 		__u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
 		struct cfi_pri_intelext *extp;
-		int ofs_factor = cfi->interleave * cfi->device_type;
 
-                printk(" ST Microelectronics Extended Query Table at 0x%4.4X\n", adr);
-		if (!adr)
+		extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "ST Microelectronics");
+		if (!extp)
 			return NULL;
 
-		/* Switch it into Query Mode */
-		cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
-
-		extp = kmalloc(sizeof(*extp), GFP_KERNEL);
-		if (!extp) {
-			printk(KERN_ERR "Failed to allocate memory\n");
-			return NULL;
-		}
-		
-		/* Read in the Extended Query Table */
-		for (i=0; i<sizeof(*extp); i++) {
-			((unsigned char *)extp)[i] = 
-				cfi_read_query(map, (base+((adr+i)*ofs_factor)));
-		}
-		
-		if (extp->MajorVersion != '1' || 
-                    (extp->MinorVersion < '0' || extp->MinorVersion > '2')) {
-                    printk(KERN_WARNING "  Unknown staa 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);
@@ -172,11 +149,6 @@
 		cfi->chips[i].erase_time = 1024;
 	}		
 
-	map->fldrv = &cfi_staa_chipdrv;
-	MOD_INC_USE_COUNT;
-	
-	/* Make sure it's in read mode */
-	cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL);
 	return cfi_staa_setup(map);
 }
 
@@ -208,6 +180,7 @@
 	if (!mtd->eraseregions) { 
 		printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n");
 		kfree(cfi->cmdset_priv);
+		kfree(mtd);
 		return NULL;
 	}
 	
@@ -232,6 +205,7 @@
 			printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize);
 			kfree(mtd->eraseregions);
 			kfree(cfi->cmdset_priv);
+			kfree(mtd);
 			return NULL;
 		}
 
@@ -256,7 +230,7 @@
 	mtd->flags |= MTD_ECC; /* FIXME: Not all STMicro flashes have this */
 	mtd->eccsize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */
 	map->fldrv = &cfi_staa_chipdrv;
-	MOD_INC_USE_COUNT;
+	__module_get(THIS_MODULE);
 	mtd->name = map->name;
 	return mtd;
 }
@@ -288,7 +262,7 @@
 	 */
 	switch (chip->state) {
 	case FL_ERASING:
-		if (!((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)
+		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);
@@ -374,7 +348,7 @@
 		goto retry;
 	}
 
-	map->copy_from(map, buf, adr, len);
+	map_copy_from(map, buf, adr, len);
 
 	if (suspended) {
 		chip->state = chip->oldstate;
@@ -540,14 +514,11 @@
 	/* Write data */
 	for (z = 0; z < len; z += CFIDEV_BUSWIDTH) {
 		if (cfi_buswidth_is_1()) {
-			map->write8 (map, *(__u8*)buf, adr+z);
-			buf += sizeof(__u8);
+			map_write8 (map, *((__u8*)buf)++, adr+z);
 		} else if (cfi_buswidth_is_2()) {
-			map->write16 (map, *(__u16*)buf, adr+z);
-			buf += sizeof(__u16);
+			map_write16 (map, *((__u16*)buf)++, adr+z);
 		} else if (cfi_buswidth_is_4()) {
-			map->write32 (map, *(__u32*)buf, adr+z);
-			buf += sizeof(__u32);
+			map_write32 (map, *((__u32*)buf)++, adr+z);
 		} else {
 			DISABLE_VPP(map);
 			return -EINVAL;
@@ -1439,13 +1410,13 @@
 
 static char im_name[]="cfi_cmdset_0020";
 
-mod_init_t cfi_staa_init(void)
+int __init cfi_staa_init(void)
 {
 	inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0020);
 	return 0;
 }
 
-mod_exit_t cfi_staa_exit(void)
+static void __exit cfi_staa_exit(void)
 {
 	inter_module_unregister(im_name);
 }
diff -Nur linux-2.4.30/drivers/mtd/chips/cfi_probe.c linux-2.4.30-linux-mips.org/drivers/mtd/chips/cfi_probe.c
--- linux-2.4.30/drivers/mtd/chips/cfi_probe.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/cfi_probe.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,13 +1,14 @@
 /* 
    Common Flash Interface probe code.
    (C) 2000 Red Hat. GPL'd.
-   $Id: cfi_probe.c,v 1.69 2002/05/11 22:13:03 dwmw2 Exp $
+   $Id: cfi_probe.c,v 1.74 2004/02/17 16:43:41 dvrabel Exp $
 */
 
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <asm/byteorder.h>
 #include <linux/errno.h>
@@ -25,7 +26,7 @@
 #endif
 
 static int cfi_probe_chip(struct map_info *map, __u32 base,
-			  struct flchip *chips, struct cfi_private *cfi);
+			  unsigned long *chip_map, struct cfi_private *cfi);
 static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi);
 
 struct mtd_info *cfi_probe(struct map_info *map);
@@ -48,7 +49,7 @@
 }
 
 static int cfi_probe_chip(struct map_info *map, __u32 base,
-			  struct flchip *chips, struct cfi_private *cfi)
+			  unsigned long *chip_map, struct cfi_private *cfi)
 {
 	int i;
 	
@@ -77,18 +78,24 @@
 	}
 
 	/* Check each previous chip to see if it's an alias */
-	for (i=0; i<cfi->numchips; i++) {
+ 	for (i=0; i < (base >> cfi->chipshift); i++) {
+ 		unsigned long start;
+ 		if(!test_bit(i, chip_map)) {
+			/* Skip location; no valid chip at this address */
+ 			continue; 
+ 		}
+ 		start = i << cfi->chipshift;
 		/* This chip should be in read mode if it's one
 		   we've already touched. */
-		if (qry_present(map,chips[i].start,cfi)) {
+		if (qry_present(map, start, cfi)) {
 			/* Eep. This chip also had the QRY marker. 
 			 * Is it an alias for the new one? */
-			cfi_send_gen_cmd(0xF0, 0, chips[i].start, map, cfi, cfi->device_type, NULL);
+			cfi_send_gen_cmd(0xF0, 0, start, map, cfi, cfi->device_type, NULL);
 
 			/* If the QRY marker goes away, it's an alias */
-			if (!qry_present(map, chips[i].start, cfi)) {
+			if (!qry_present(map, start, cfi)) {
 				printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
-				       map->name, base, chips[i].start);
+				       map->name, base, start);
 				return 0;
 			}
 			/* Yes, it's actually got QRY for data. Most 
@@ -99,7 +106,7 @@
 			
 			if (qry_present(map, base, cfi)) {
 				printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
-				       map->name, base, chips[i].start);
+				       map->name, base, start);
 				return 0;
 			}
 		}
@@ -107,13 +114,7 @@
 	
 	/* OK, if we got to here, then none of the previous chips appear to
 	   be aliases for the current one. */
-	if (cfi->numchips == MAX_CFI_CHIPS) {
-		printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS);
-		/* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */
-		return -1;
-	}
-	chips[cfi->numchips].start = base;
-	chips[cfi->numchips].state = FL_READY;
+	set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */
 	cfi->numchips++;
 	
 	/* Put it back into Read Mode */
@@ -149,7 +150,6 @@
 	memset(cfi->cfiq,0,sizeof(struct cfi_ident));	
 	
 	cfi->cfi_mode = CFI_MODE_CFI;
-	cfi->fast_prog=1;		/* CFI supports fast programming */
 	
 	/* Read the CFI info structure */
 	for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) {
@@ -179,9 +179,28 @@
 		       (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1);
 #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",
+	       map->name, cfi->interleave, cfi->device_type*8, base,
+	       map->buswidth*8);
+
 	return 1;
 }
 
@@ -240,11 +259,11 @@
 		printk("No Alternate Algorithm Table\n");
 		
 		
-	printk("Vcc Minimum: %x.%x V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf);
-	printk("Vcc Maximum: %x.%x V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf);
+	printk("Vcc Minimum: %2d.%d V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf);
+	printk("Vcc Maximum: %2d.%d V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf);
 	if (cfip->VppMin) {
-		printk("Vpp Minimum: %x.%x V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf);
-		printk("Vpp Maximum: %x.%x V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf);
+		printk("Vpp Minimum: %2d.%d V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf);
+		printk("Vpp Maximum: %2d.%d V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf);
 	}
 	else
 		printk("No Vpp line\n");
@@ -303,8 +322,8 @@
 #endif /* DEBUG_CFI */
 
 static struct chip_probe cfi_chip_probe = {
-	name: "CFI",
-	probe_chip: cfi_probe_chip
+	.name		= "CFI",
+	.probe_chip	= cfi_probe_chip
 };
 
 struct mtd_info *cfi_probe(struct map_info *map)
@@ -317,9 +336,9 @@
 }
 
 static struct mtd_chip_driver cfi_chipdrv = {
-	probe: cfi_probe,
-	name: "cfi_probe",
-	module: THIS_MODULE
+	.probe		= cfi_probe,
+	.name		= "cfi_probe",
+	.module		= THIS_MODULE
 };
 
 int __init cfi_probe_init(void)
diff -Nur linux-2.4.30/drivers/mtd/chips/cfi_util.c linux-2.4.30-linux-mips.org/drivers/mtd/chips/cfi_util.c
--- linux-2.4.30/drivers/mtd/chips/cfi_util.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/cfi_util.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,91 @@
+/*
+ * Common Flash Interface support:
+ *   Generic utility functions not dependant on command set
+ *
+ * Copyright (C) 2002 Red Hat
+ * Copyright (C) 2003 STMicroelectronics Limited
+ *
+ * This code is covered by the GPL.
+ *
+ * $Id: cfi_util.c,v 1.3 2003/11/14 19:50:03 thayne Exp $
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/interrupt.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)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+	__u32 base = 0; // cfi->chips[0].start;
+	int ofs_factor = cfi->interleave * cfi->device_type;
+	int i;
+	struct cfi_extquery *extp = NULL;
+
+	printk(" %s Extended Query Table at 0x%4.4X\n", name, adr);
+	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;
+	}
+		
+	/* 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 */
+	cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL);
+
+	return extp;
+}
+
+EXPORT_SYMBOL(cfi_read_pri);
+
+void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups)
+{
+	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);
+		}
+	}
+}
+
+EXPORT_SYMBOL(cfi_fixup);
+
+MODULE_LICENSE("GPL");
diff -Nur linux-2.4.30/drivers/mtd/chips/chipreg.c linux-2.4.30-linux-mips.org/drivers/mtd/chips/chipreg.c
--- linux-2.4.30/drivers/mtd/chips/chipreg.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/chipreg.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: chipreg.c,v 1.13 2002/02/21 08:26:58 dwmw2 Exp $
+ * $Id: chipreg.c,v 1.16 2003/05/29 09:36:15 dwmw2 Exp $
  *
  * Registration for chip drivers
  *
@@ -7,10 +7,13 @@
 
 #include <linux/kernel.h>
 #include <linux/config.h>
+#include <linux/module.h>
 #include <linux/kmod.h>
 #include <linux/spinlock.h>
-#include <linux/mtd/compatmac.h>
+#include <linux/slab.h>
 #include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/compatmac.h>
 
 spinlock_t chip_drvs_lock = SPIN_LOCK_UNLOCKED;
 static LIST_HEAD(chip_drvs_list);
@@ -44,10 +47,8 @@
 			break;
 		}
 	}
-	if (ret && !try_inc_mod_count(ret->module)) {
-		/* Eep. Failed. */
+	if (ret && !try_module_get(ret->module))
 		ret = NULL;
-	}
 
 	spin_unlock(&chip_drvs_lock);
 
@@ -64,32 +65,46 @@
 
 	drv = get_mtd_chip_driver(name);
 
-	if (!drv && !request_module(name))
+	if (!drv && !request_module("%s", name))
 		drv = get_mtd_chip_driver(name);
 
 	if (!drv)
 		return NULL;
 
 	ret = drv->probe(map);
-#ifdef CONFIG_MODULES
+
 	/* We decrease the use count here. It may have been a 
 	   probe-only module, which is no longer required from this
 	   point, having given us a handle on (and increased the use
 	   count of) the actual driver code.
 	*/
-	if(drv->module)
-		__MOD_DEC_USE_COUNT(drv->module);
-#endif
+	module_put(drv->module);
 
 	if (ret)
 		return ret;
 	
 	return NULL;
 }
+/*
+ * Destroy an MTD device which was created for a map device.
+ * Make sure the MTD device is already unregistered before calling this
+ */
+void map_destroy(struct mtd_info *mtd)
+{
+	struct map_info *map = mtd->priv;
+
+	if (map->fldrv->destroy)
+		map->fldrv->destroy(mtd);
+
+	module_put(map->fldrv->module);
+
+	kfree(mtd);
+}
 
 EXPORT_SYMBOL(register_mtd_chip_driver);
 EXPORT_SYMBOL(unregister_mtd_chip_driver);
 EXPORT_SYMBOL(do_map_probe);
+EXPORT_SYMBOL(map_destroy);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
diff -Nur linux-2.4.30/drivers/mtd/chips/Config.in linux-2.4.30-linux-mips.org/drivers/mtd/chips/Config.in
--- linux-2.4.30/drivers/mtd/chips/Config.in	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/Config.in	2005-05-12 18:01:29.000000000 +0200
@@ -1,6 +1,6 @@
 # drivers/mtd/chips/Config.in
 
-# $Id: Config.in,v 1.16 2002/09/03 13:30:43 joern Exp $
+# $Id: Config.in,v 1.19 2004/04/15 14:24:09 thayne Exp $
 
 mainmenu_option next_comment
 
@@ -18,6 +18,7 @@
       define_bool CONFIG_MTD_GEN_PROBE n
    fi
 fi
+
 if [ "$CONFIG_MTD_GEN_PROBE" = "y" -o "$CONFIG_MTD_GEN_PROBE" = "m" ]; then
    bool '  Flash chip driver advanced configuration options' CONFIG_MTD_CFI_ADV_OPTIONS
    if [ "$CONFIG_MTD_CFI_ADV_OPTIONS" = "y" ]; then
@@ -46,6 +47,20 @@
 dep_tristate '  Support for AMD/Fujitsu flash chips' CONFIG_MTD_CFI_AMDSTD $CONFIG_MTD_GEN_PROBE
 dep_tristate '  Support for ST (Advanced Architecture) flash chips' CONFIG_MTD_CFI_STAA $CONFIG_MTD_GEN_PROBE
 
+if [ "$CONFIG_MTD_CFI_INTELEXT" = "y" \
+	-o "$CONFIG_MTD_CFI_AMDSTD" = "y" \
+	-o "$CONFIG_MTD_CFI_STAA" = "y" ]; then
+   define_bool CONFIG_MTD_CFI_UTIL y
+else
+   if [ "$CONFIG_MTD_CFI_INTELEXT" = "m" \
+        -o "$CONFIG_MTD_CFI_AMDSTD" = "m" \
+        -o "$CONFIG_MTD_CFI_STAA" = "m" ]; then
+      define_bool CONFIG_MTD_CFI_UTIL m
+   else
+      define_bool CONFIG_MTD_CFI_UTIL n
+   fi
+fi
+
 dep_tristate '  Support for RAM chips in bus mapping' CONFIG_MTD_RAM $CONFIG_MTD
 dep_tristate '  Support for ROM chips in bus mapping' CONFIG_MTD_ROM $CONFIG_MTD
 dep_tristate '  Support for absent chips in bus mapping' CONFIG_MTD_ABSENT $CONFIG_MTD
diff -Nur linux-2.4.30/drivers/mtd/chips/gen_probe.c linux-2.4.30-linux-mips.org/drivers/mtd/chips/gen_probe.c
--- linux-2.4.30/drivers/mtd/chips/gen_probe.c	2003-08-25 13:44:42.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/gen_probe.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,14 +1,17 @@
 /*
  * Routines common to all CFI-type probes.
- * (C) 2001, 2001 Red Hat, Inc.
+ * (C) 2001-2003 Red Hat, Inc.
  * GPL'd
- * $Id: gen_probe.c,v 1.9 2002/09/05 05:15:32 acurtis Exp $
+ * $Id: gen_probe.c,v 1.14 2003/11/08 00:51:21 dsaxena Exp $
  */
 
 #include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
 #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);
@@ -50,11 +53,11 @@
 
 struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp)
 {
-	unsigned long base=0;
 	struct cfi_private cfi;
 	struct cfi_private *retcfi;
-	struct flchip chip[MAX_CFI_CHIPS];
-	int i;
+	unsigned long *chip_map;
+	int i, j;
+	int max_chips;
 
 	memset(&cfi, 0, sizeof(cfi));
 
@@ -77,8 +80,6 @@
 		return NULL;
 	}
 #endif
-	chip[0].start = 0;
-	chip[0].state = FL_READY;
 	cfi.chipshift = cfi.cfiq->DevSize;
 
 	switch(cfi.interleave) {
@@ -102,21 +103,29 @@
 		
 	cfi.numchips = 1;
 
+	/* 
+	 * Allocate memory for bitmap of valid chips. 
+	 * 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);
+	if (!chip_map) {
+		printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
+		kfree(cfi.cfiq);
+		return NULL;
+	}
+
+	set_bit(0, chip_map); /* Mark first chip valid */
+
 	/*
 	 * Now probe for other chips, checking sensibly for aliases while
 	 * we're at it. The new_chip probe above should have let the first
 	 * chip in read mode.
-	 *
-	 * NOTE: Here, we're checking if there is room for another chip
-	 *       the same size within the mapping. Therefore, 
-	 *       base + chipsize <= map->size is the correct thing to do, 
-	 *       because, base + chipsize would be the  _first_ byte of the
-	 *       next chip, not the one we're currently pondering.
 	 */
 
-	for (base = (1<<cfi.chipshift); base + (1<<cfi.chipshift) <= map->size;
-	     base += (1<<cfi.chipshift))
-		cp->probe_chip(map, base, &chip[0], &cfi);
+	for (i = 1; i < max_chips; i++) {
+		cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi);
+	}
 
 	/*
 	 * Now allocate the space for the structures we need to return to 
@@ -128,19 +137,26 @@
 	if (!retcfi) {
 		printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name);
 		kfree(cfi.cfiq);
+		kfree(chip_map);
 		return NULL;
 	}
 
 	memcpy(retcfi, &cfi, sizeof(cfi));
-	memcpy(&retcfi->chips[0], chip, sizeof(struct flchip) * cfi.numchips);
+	memset(&retcfi->chips[0], 0, sizeof(struct flchip) * cfi.numchips);
 
-	/* Fix up the stuff that breaks when you move it */
-	for (i=0; i< retcfi->numchips; i++) {
-		init_waitqueue_head(&retcfi->chips[i].wq);
-		spin_lock_init(&retcfi->chips[i]._spinlock);
-		retcfi->chips[i].mutex = &retcfi->chips[i]._spinlock;
+	for (i = 0, j = 0; (j < cfi.numchips) && (i < max_chips); i++) {
+		if(test_bit(i, chip_map)) {
+			struct flchip *pchip = &retcfi->chips[j++];
+
+			pchip->start = (i << cfi.chipshift);
+			pchip->state = FL_READY;
+			init_waitqueue_head(&pchip->wq);
+			spin_lock_init(&pchip->_spinlock);
+			pchip->mutex = &pchip->_spinlock;
+		}
 	}
 
+	kfree(chip_map);
 	return retcfi;
 }
 
diff -Nur linux-2.4.30/drivers/mtd/chips/jedec.c linux-2.4.30-linux-mips.org/drivers/mtd/chips/jedec.c
--- linux-2.4.30/drivers/mtd/chips/jedec.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/jedec.c	2005-05-12 18:01:29.000000000 +0200
@@ -11,10 +11,16 @@
  * not going to guess how to send commands to them, plus I expect they will
  * all speak CFI..
  *
- * $Id: jedec.c,v 1.14 2002/06/27 02:19:12 dwmw2 Exp $
+ * $Id: jedec.c,v 1.19 2003/05/29 09:25:23 dwmw2 Exp $
  */
 
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
 #include <linux/mtd/jedec.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/compatmac.h>
 
 static struct mtd_info *jedec_probe(struct map_info *);
 static int jedec_probe8(struct map_info *map,unsigned long base,
@@ -33,14 +39,51 @@
 
 /* Listing of parts and sizes. We need this table to learn the sector
    size of the chip and the total length */
-static const struct JEDECTable JEDEC_table[] = 
-  {{0x013D,"AMD Am29F017D",2*1024*1024,64*1024,MTD_CAP_NORFLASH},
-   {0x01AD,"AMD Am29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH},
-   {0x01D5,"AMD Am29F080",1*1024*1024,64*1024,MTD_CAP_NORFLASH},
-   {0x01A4,"AMD Am29F040",512*1024,64*1024,MTD_CAP_NORFLASH},
-   {0x20E3,"AMD Am29W040B",512*1024,64*1024,MTD_CAP_NORFLASH},
-   {0xC2AD,"Macronix MX29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH},
-   {}};
+static const struct JEDECTable JEDEC_table[] = {
+	{
+		.jedec		= 0x013D,
+		.name		= "AMD Am29F017D",
+		.size		= 2*1024*1024,
+		.sectorsize	= 64*1024,
+		.capabilities	= MTD_CAP_NORFLASH
+	},
+	{
+		.jedec		= 0x01AD,
+		.name		= "AMD Am29F016",
+		.size		= 2*1024*1024,
+		.sectorsize	= 64*1024,
+		.capabilities	= MTD_CAP_NORFLASH
+	},
+	{
+		.jedec		= 0x01D5,
+		.name		= "AMD Am29F080",
+		.size		= 1*1024*1024,
+		.sectorsize	= 64*1024,
+		.capabilities	= MTD_CAP_NORFLASH
+	},
+	{
+		.jedec		= 0x01A4,
+		.name		= "AMD Am29F040",
+		.size		= 512*1024,
+		.sectorsize	= 64*1024,
+		.capabilities	= MTD_CAP_NORFLASH
+	},
+	{
+		.jedec		= 0x20E3,
+		.name		= "AMD Am29W040B",
+		.size		= 512*1024,
+		.sectorsize	= 64*1024,
+		.capabilities	= MTD_CAP_NORFLASH
+	},
+	{
+		.jedec		= 0xC2AD,
+		.name		= "Macronix MX29F016",
+		.size		= 2*1024*1024,
+		.sectorsize	= 64*1024,
+		.capabilities	= MTD_CAP_NORFLASH
+	},
+	{ .jedec = 0x0 }
+};
 
 static const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id);
 static void jedec_sync(struct mtd_info *mtd) {};
@@ -54,9 +97,9 @@
 
 
 static struct mtd_chip_driver jedec_chipdrv = {
-	probe: jedec_probe,
-	name: "jedec",
-	module: THIS_MODULE
+	.probe	= jedec_probe,
+	.name	= "jedec",
+	.module	= THIS_MODULE
 };
 
 /* Probe entry point */
@@ -131,8 +174,7 @@
    /* Generate a part name that includes the number of different chips and
       other configuration information */
    count = 1;
-   strncpy(Part,map->name,sizeof(Part)-10);
-   Part[sizeof(Part)-11] = 0;
+   strlcpy(Part,map->name,sizeof(Part)-10);
    strcat(Part," ");
    Uniq = 0;
    for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++)
@@ -209,8 +251,7 @@
    //   printk("Part: '%s'\n",Part);
    
    memset(MTD,0,sizeof(*MTD));
-  // strncpy(MTD->name,Part,sizeof(MTD->name));
-  // MTD->name[sizeof(MTD->name)-1] = 0;
+  // strlcpy(MTD->name,Part,sizeof(MTD->name));
    MTD->name = map->name;
    MTD->type = MTD_NORFLASH;
    MTD->flags = MTD_CAP_NORFLASH;
@@ -229,7 +270,7 @@
    MTD->priv = map;
    map->fldrv_priv = priv;
    map->fldrv = &jedec_chipdrv;
-   MOD_INC_USE_COUNT;
+   __module_get(THIS_MODULE);
    return MTD;
 }
 
@@ -351,8 +392,8 @@
 static int jedec_probe8(struct map_info *map,unsigned long base,
 		  struct jedec_private *priv)
 { 
-   #define flread(x) map->read8(map,base+x)
-   #define flwrite(v,x) map->write8(map,v,base+x)
+   #define flread(x) map_read8(map,base+x)
+   #define flwrite(v,x) map_write8(map,v,base+x)
 
    const unsigned long AutoSel1 = 0xAA;
    const unsigned long AutoSel2 = 0x55;
@@ -411,8 +452,8 @@
 static int jedec_probe32(struct map_info *map,unsigned long base,
 		  struct jedec_private *priv)
 {
-   #define flread(x) map->read32(map,base+((x)<<2))
-   #define flwrite(v,x) map->write32(map,v,base+((x)<<2))
+   #define flread(x) map_read32(map,base+((x)<<2))
+   #define flwrite(v,x) map_write32(map,v,base+((x)<<2))
 
    const unsigned long AutoSel1 = 0xAAAAAAAA;
    const unsigned long AutoSel2 = 0x55555555;
@@ -490,7 +531,7 @@
 {
    struct map_info *map = (struct map_info *)mtd->priv;
    
-   map->copy_from(map, buf, from, len);
+   map_copy_from(map, buf, from, len);
    *retlen = len;
    return 0;   
 }
@@ -514,7 +555,7 @@
 	 get = priv->bank_fill[0] - offset;
 
       bank /= priv->bank_fill[0];      
-      map->copy_from(map,buf + *retlen,bank*my_bank_size + offset,get);
+      map_copy_from(map,buf + *retlen,bank*my_bank_size + offset,get);
       
       len -= get;
       *retlen += get;
@@ -545,8 +586,8 @@
 static int flash_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
    // Does IO to the currently selected chip
-   #define flread(x) map->read8(map,chip->base+((x)<<chip->addrshift))
-   #define flwrite(v,x) map->write8(map,v,chip->base+((x)<<chip->addrshift))
+   #define flread(x) map_read8(map,chip->base+((x)<<chip->addrshift))
+   #define flwrite(v,x) map_write8(map,v,chip->base+((x)<<chip->addrshift))
    
    unsigned long Time = 0;
    unsigned long NoTime = 0;
@@ -608,7 +649,7 @@
 
    /* Poll the flash for erasure completion, specs say this can take as long
       as 480 seconds to do all the sectors (for a 2 meg flash). 
-      Erasure time is dependant on chip age, temp and wear.. */
+      Erasure time is dependent on chip age, temp and wear.. */
    
    /* This being a generic routine assumes a 32 bit bus. It does read32s
       and bundles interleved chips into the same grouping. This will work 
@@ -651,19 +692,19 @@
   	    or this is not really flash ;> */
 	 switch (map->buswidth) {
 	 case 1:
-	    Last[0] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    Last[1] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    Last[2] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off);
+	    Last[0] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
+	    Last[1] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
+	    Last[2] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
 	    break;
 	 case 2:
-	    Last[0] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    Last[1] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    Last[2] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off);
+	    Last[0] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
+	    Last[1] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
+	    Last[2] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
 	    break;
 	 case 3:
-	    Last[0] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    Last[1] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off);
-	    Last[2] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off);
+	    Last[0] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
+	    Last[1] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
+	    Last[2] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
 	    break;
 	 }
 	 Count = 3;
@@ -699,13 +740,13 @@
 
 	    switch (map->buswidth) {
 	    case 1:
-	       Last[Count % 4] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off);
+	       Last[Count % 4] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off);
 	      break;
 	    case 2:
-	       Last[Count % 4] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off);
+	       Last[Count % 4] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off);
 	      break;
 	    case 4:
-	       Last[Count % 4] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off);
+	       Last[Count % 4] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off);
 	      break;
 	    }
 	    Count++;
@@ -755,10 +796,10 @@
 		       size_t *retlen, const u_char *buf)
 {
    /* Does IO to the currently selected chip. It takes the bank addressing
-      base (which is divisable by the chip size) adds the necesary lower bits
-      of addrshift (interleve index) and then adds the control register index. */
-   #define flread(x) map->read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
-   #define flwrite(v,x) map->write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
+      base (which is divisible by the chip size) adds the necessary lower bits
+      of addrshift (interleave index) and then adds the control register index. */
+   #define flread(x) map_read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
+   #define flwrite(v,x) map_write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift))
    
    struct map_info *map = (struct map_info *)mtd->priv;
    struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv;
@@ -794,7 +835,7 @@
       // Loop over this page
       for (; off != (chip->size << chip->addrshift) && len != 0; start++, len--, off++,buf++)
       {
-	 unsigned char oldbyte = map->read8(map,base+off);
+	 unsigned char oldbyte = map_read8(map,base+off);
 	 unsigned char Last[4];
 	 unsigned long Count = 0;
 
@@ -809,10 +850,10 @@
 	 flwrite(0xAA,0x555);
 	 flwrite(0x55,0x2AA);
 	 flwrite(0xA0,0x555);
-	 map->write8(map,*buf,base + off);
-	 Last[0] = map->read8(map,base + off);
-	 Last[1] = map->read8(map,base + off);
-	 Last[2] = map->read8(map,base + off);
+	 map_write8(map,*buf,base + off);
+	 Last[0] = map_read8(map,base + off);
+	 Last[1] = map_read8(map,base + off);
+	 Last[2] = map_read8(map,base + off);
 	 
 	 /* Wait for the flash to finish the operation. We store the last 4
 	    status bytes that have been retrieved so we can determine why
@@ -820,7 +861,7 @@
 	    failure */
 	 for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] &&
 	      Count < 10000; Count++)
-	    Last[Count % 4] = map->read8(map,base + off);
+	    Last[Count % 4] = map_read8(map,base + off);
 	 if (Last[(Count - 1) % 4] != *buf)
 	 {
 	    jedec_flash_failed(Last[(Count - 3) % 4]);
diff -Nur linux-2.4.30/drivers/mtd/chips/jedec_probe.c linux-2.4.30-linux-mips.org/drivers/mtd/chips/jedec_probe.c
--- linux-2.4.30/drivers/mtd/chips/jedec_probe.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/jedec_probe.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,9 +1,11 @@
 /* 
    Common Flash Interface probe code.
    (C) 2000 Red Hat. GPL'd.
-   $Id: jedec_probe.c,v 1.19 2002/11/12 13:12:10 dwmw2 Exp $
+   $Id: jedec_probe.c,v 1.47 2004/04/15 12:32:49 gleixner Exp $
    See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5)
    for the standard this probe goes back to.
+
+   Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com
 */
 
 #include <linux/config.h>
@@ -15,7 +17,9 @@
 #include <linux/errno.h>
 #include <linux/slab.h>
 #include <linux/interrupt.h>
+#include <linux/init.h>
 
+#include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/cfi.h>
 #include <linux/mtd/gen_probe.h>
@@ -26,20 +30,27 @@
 #define MANUFACTURER_FUJITSU	0x0004
 #define MANUFACTURER_INTEL	0x0089
 #define MANUFACTURER_MACRONIX	0x00C2
-#define MANUFACTURER_ST		0x0020
+#define MANUFACTURER_PMC	0x009D
 #define MANUFACTURER_SST	0x00BF
+#define MANUFACTURER_ST		0x0020
 #define MANUFACTURER_TOSHIBA	0x0098
+#define MANUFACTURER_WINBOND	0x00da
 
 
 /* AMD */
+#define AM29DL800BB	0x22C8
+#define AM29DL800BT	0x224A
+
 #define AM29F800BB	0x2258
 #define AM29F800BT	0x22D6
+#define AM29LV400BB	0x22BA
+#define AM29LV400BT	0x22B9
 #define AM29LV800BB	0x225B
 #define AM29LV800BT	0x22DA
 #define AM29LV160DT	0x22C4
 #define AM29LV160DB	0x2249
 #define AM29F017D	0x003D
-#define AM29F016	0x00AD
+#define AM29F016D	0x00AD
 #define AM29F080	0x00D5
 #define AM29F040	0x00A4
 #define AM29LV040B	0x004F
@@ -54,6 +65,7 @@
 #define AT49BV32XT	0x00C9
 
 /* Fujitsu */
+#define MBM29F040C	0x00A4
 #define MBM29LV650UE	0x22D7
 #define MBM29LV320TE	0x22F6
 #define MBM29LV320BE	0x22F9
@@ -61,6 +73,9 @@
 #define MBM29LV160BE	0x2249
 #define MBM29LV800BA	0x225B
 #define MBM29LV800TA	0x22DA
+#define MBM29LV400TC	0x22B9
+#define MBM29LV400BC	0x22BA
+
 
 /* Intel */
 #define I28F004B3T	0x00d4
@@ -93,13 +108,21 @@
 #define MX29F004T	0x0045
 #define MX29F004B	0x0046
 
+/* PMC */
+#define PM49FL002	0x006D
+#define PM49FL004	0x006E
+#define PM49FL008	0x006A
+
 /* ST - www.st.com */
-#define M29W800T	0x00D7
+#define M29W800DT	0x00D7
+#define M29W800DB	0x005B
 #define M29W160DT	0x22C4
 #define M29W160DB	0x2249
 #define M29W040B	0x00E3
 
 /* SST */
+#define SST29EE020	0x0010
+#define SST29LE020	0x0012
 #define SST29EE512	0x005d
 #define SST29LE512	0x003d
 #define SST39LF800	0x2781
@@ -110,6 +133,7 @@
 #define SST39LF040	0x00D7
 #define SST39SF010A	0x00B5
 #define SST39SF020A	0x00B6
+#define SST49LF004B	0x0060
 #define SST49LF030A	0x001C
 #define SST49LF040A	0x0051
 #define SST49LF080A	0x005B
@@ -122,16 +146,88 @@
 #define TC58FVT641	0x0093
 #define TC58FVB641	0x0095
 
+/* Winbond */
+#define W49V002A	0x00b0
+
+
+/*
+ * Unlock address sets for AMD command sets.
+ * Intel command sets use the MTD_UADDR_UNNECESSARY.
+ * Each identifier, except MTD_UADDR_UNNECESSARY, and
+ * MTD_UADDR_NO_SUPPORT must be defined below in unlock_addrs[].
+ * MTD_UADDR_NOT_SUPPORTED must be 0 so that structure
+ * initialization need not require initializing all of the
+ * unlock addresses for all bit widths.
+ */
+enum uaddr {
+	MTD_UADDR_NOT_SUPPORTED = 0,	/* data width not supported */
+	MTD_UADDR_0x0555_0x02AA,
+	MTD_UADDR_0x0555_0x0AAA,
+	MTD_UADDR_0x5555_0x2AAA,
+	MTD_UADDR_0x0AAA_0x0555,
+	MTD_UADDR_DONT_CARE,		/* Requires an arbitrary address */
+	MTD_UADDR_UNNECESSARY,		/* Does not require any address */
+};
+
+
+struct unlock_addr {
+	int addr1;
+	int addr2;
+};
+
+
+/*
+ * I don't like the fact that the first entry in unlock_addrs[]
+ * exists, but is for MTD_UADDR_NOT_SUPPORTED - and, therefore,
+ * should not be used.  The  problem is that structures with
+ * initializers have extra fields initialized to 0.  It is _very_
+ * desireable to have the unlock address entries for unsupported
+ * data widths automatically initialized - that means that
+ * MTD_UADDR_NOT_SUPPORTED must be 0 and the first entry here
+ * must go unused.
+ */
+static const struct unlock_addr  unlock_addrs[] = {
+	[MTD_UADDR_NOT_SUPPORTED] = {
+		.addr1 = 0xffff,
+		.addr2 = 0xffff
+	},
+
+	[MTD_UADDR_0x0555_0x02AA] = {
+		.addr1 = 0x0555,
+		.addr2 = 0x02aa
+	},
+
+	[MTD_UADDR_0x0555_0x0AAA] = {
+		.addr1 = 0x0555,
+		.addr2 = 0x0aaa
+	},
+
+	[MTD_UADDR_0x5555_0x2AAA] = {
+		.addr1 = 0x5555,
+		.addr2 = 0x2aaa
+	},
+
+	[MTD_UADDR_0x0AAA_0x0555] = {
+		.addr1 = 0x0AAA,
+		.addr2 = 0x0555
+	},
+
+	[MTD_UADDR_DONT_CARE] = {
+		.addr1 = 0x0000,      /* Doesn't matter which address */
+		.addr2 = 0x0000       /* is used - must be last entry */
+	}
+};
+
 
 struct amd_flash_info {
 	const __u16 mfr_id;
 	const __u16 dev_id;
 	const char *name;
 	const int DevSize;
-	const int InterfaceDesc;
 	const int NumEraseRegions;
 	const int CmdSet;
-	const ulong regions[4];
+	const __u8 uaddr[4];		/* unlock addrs for 8, 16, 32, 64 */
+	const ulong regions[6];
 };
 
 #define ERASEINFO(size,blocks) (size<<8)|(blocks-1)
@@ -145,760 +241,1277 @@
 #define SIZE_4MiB   22
 #define SIZE_8MiB   23
 
+
+/*
+ * Please keep this list ordered by manufacturer!
+ * Fortunately, the list isn't searched often and so a
+ * slow, linear search isn't so bad.
+ */
 static const struct amd_flash_info jedec_table[] = {
 	{
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29F032B,
-		name: "AMD AM29F032B",
-		DevSize: SIZE_4MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,64)
-		}
-	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29LV160DT,
-		name: "AMD AM29LV160DT",
-		DevSize: SIZE_2MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,31),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x04000,1)
-		}
-	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29LV160DB,
-		name: "AMD AM29LV160DB",
-		DevSize: SIZE_2MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x04000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x10000,31)
-		}
-	}, {
-		mfr_id: MANUFACTURER_TOSHIBA,
-		dev_id: TC58FVT160,
-		name: "Toshiba TC58FVT160",
-		DevSize: SIZE_2MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,31),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x04000,1)
-		}
-	}, {
-		mfr_id: MANUFACTURER_TOSHIBA,
-		dev_id: TC58FVB160,
-		name: "Toshiba TC58FVB160",
-		DevSize: SIZE_2MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x04000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x10000,31)
-		}
-	}, {
-		mfr_id: MANUFACTURER_TOSHIBA,
-		dev_id: TC58FVB321,
-		name: "Toshiba TC58FVB321",
-		DevSize: SIZE_4MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 2,
-		regions: {ERASEINFO(0x02000,8),
-			  ERASEINFO(0x10000,63)
-		}
-	}, {
-		mfr_id: MANUFACTURER_TOSHIBA,
-		dev_id: TC58FVT321,
-		name: "Toshiba TC58FVT321",
-		DevSize: SIZE_4MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 2,
-		regions: {ERASEINFO(0x10000,63),
-			  ERASEINFO(0x02000,8)
-		}
-	}, {
-		mfr_id: MANUFACTURER_TOSHIBA,
-		dev_id: TC58FVB641,
-		name: "Toshiba TC58FVB641",
-		DevSize: SIZE_8MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 2,
-		regions: {ERASEINFO(0x02000,8),
-			  ERASEINFO(0x10000,127)
-		}
-	}, {
-		mfr_id: MANUFACTURER_TOSHIBA,
-		dev_id: TC58FVT641,
-		name: "Toshiba TC58FVT641",
-		DevSize: SIZE_8MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 2,
-		regions: {ERASEINFO(0x10000,127),
-			  ERASEINFO(0x02000,8)
-		}
-	}, {
-		mfr_id: MANUFACTURER_FUJITSU,
-		dev_id: MBM29LV650UE,
-		name: "Fujitsu MBM29LV650UE",
-		DevSize: SIZE_8MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,128)
-		}
-	}, {
-		mfr_id: MANUFACTURER_FUJITSU,
-		dev_id: MBM29LV320TE,
-		name: "Fujitsu MBM29LV320TE",
-		DevSize: SIZE_4MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 2,
-		regions: {ERASEINFO(0x10000,63),
-			  ERASEINFO(0x02000,8)
-		}
-	}, {
-		mfr_id: MANUFACTURER_FUJITSU,
-		dev_id: MBM29LV320BE,
-		name: "Fujitsu MBM29LV320BE",
-		DevSize: SIZE_4MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 2,
-		regions: {ERASEINFO(0x02000,8),
-			  ERASEINFO(0x10000,63)
-		}
-	}, {
-		mfr_id: MANUFACTURER_FUJITSU,
-		dev_id: MBM29LV160TE,
-		name: "Fujitsu MBM29LV160TE",
-		DevSize: SIZE_2MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,31),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x04000,1)
-		}
-	}, {
-		mfr_id: MANUFACTURER_FUJITSU,
-		dev_id: MBM29LV160BE,
-		name: "Fujitsu MBM29LV160BE",
-		DevSize: SIZE_2MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x04000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x10000,31)
-		}
-	}, {
-		mfr_id: MANUFACTURER_FUJITSU,
-		dev_id: MBM29LV800BA,
-		name: "Fujitsu MBM29LV800BA",
-		DevSize: SIZE_1MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x04000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x10000,15)
-		}
-	}, {
-		mfr_id: MANUFACTURER_FUJITSU,
-		dev_id: MBM29LV800TA,
-		name: "Fujitsu MBM29LV800TA",
-		DevSize: SIZE_1MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,15),
- 			  ERASEINFO(0x08000,1),
- 			  ERASEINFO(0x02000,2),
- 			  ERASEINFO(0x04000,1)
-		}
-	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29LV800BB,
-		name: "AMD AM29LV800BB",
-		DevSize: SIZE_1MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x04000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x10000,15),
-		}
-	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29F800BB,
-		name: "AMD AM29F800BB",
-		DevSize: SIZE_1MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x04000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x10000,15),
-		}
-	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29LV800BT,
-		name: "AMD AM29LV800BT",
-		DevSize: SIZE_1MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,15),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x04000,1)
-		}
-	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29F800BT,
-		name: "AMD AM29F800BT",
-		DevSize: SIZE_1MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,15),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x04000,1)
-		}
-	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29LV800BB,
-		name: "AMD AM29LV800BB",
-		DevSize: SIZE_1MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,15),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x04000,1)
-		}
-	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F004B3B,
-		name:			"Intel 28F004B3B",
-		DevSize:		SIZE_512KiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29F032B,
+		.name		= "AMD AM29F032B",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
+		},
+		.DevSize	= SIZE_4MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,64)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29LV160DT,
+		.name		= "AMD AM29LV160DT",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA   /* x16 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x10000,31),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x04000,1)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29LV160DB,
+		.name		= "AMD AM29LV160DB",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA   /* x16 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x10000,31)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29LV400BB,
+		.name		= "AMD AM29LV400BB",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x10000,7)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29LV400BT,
+		.name		= "AMD AM29LV400BT",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x10000,7),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x04000,1)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29LV800BB,
+		.name		= "AMD AM29LV800BB",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x10000,15),
+		}
+	}, {
+/* add DL */
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29DL800BB,
+		.name		= "AMD AM29DL800BB",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 6,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x02000,4),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x10000,14)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29DL800BT,
+		.name		= "AMD AM29DL800BT",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 6,
+		.regions	= {
+			ERASEINFO(0x10000,14),
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x02000,4),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x04000,1)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29F800BB,
+		.name		= "AMD AM29F800BB",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x10000,15),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29LV800BT,
+		.name		= "AMD AM29LV800BT",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x10000,15),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x04000,1)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29F800BT,
+		.name		= "AMD AM29F800BT",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x10000,15),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x04000,1)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29F017D,
+		.name		= "AMD AM29F017D",
+		.uaddr		= {
+			[0] = MTD_UADDR_DONT_CARE     /* x8 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,32),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29F016D,
+		.name		= "AMD AM29F016D",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,32),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29F080,
+		.name		= "AMD AM29F080",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,16),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29F040,
+		.name		= "AMD AM29F040",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,8),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_AMD,
+		.dev_id		= AM29LV040B,
+		.name		= "AMD AM29LV040B",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,8),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_ATMEL,
+		.dev_id		= AT49BV512,
+		.name		= "Atmel AT49BV512",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_64KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,1)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_ATMEL,
+		.dev_id		= AT29LV512,
+		.name		= "Atmel AT29LV512",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_64KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x80,256),
+			ERASEINFO(0x80,256)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_ATMEL,
+		.dev_id		= AT49BV16X,
+		.name		= "Atmel AT49BV16X",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x0AAA,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x0AAA   /* x16 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
+			ERASEINFO(0x02000,8),
+			ERASEINFO(0x10000,31)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_ATMEL,
+		.dev_id		= AT49BV16XT,
+		.name		= "Atmel AT49BV16XT",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x0AAA,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x0AAA   /* x16 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
+			ERASEINFO(0x10000,31),
+			ERASEINFO(0x02000,8)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_ATMEL,
+		.dev_id		= AT49BV32X,
+		.name		= "Atmel AT49BV32X",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x0AAA,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x0AAA   /* x16 */
+		},
+		.DevSize	= SIZE_4MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
+			ERASEINFO(0x02000,8),
+			ERASEINFO(0x10000,63)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_ATMEL,
+		.dev_id		= AT49BV32XT,
+		.name		= "Atmel AT49BV32XT",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x0AAA,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x0AAA   /* x16 */
+		},
+		.DevSize	= SIZE_4MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
+			ERASEINFO(0x10000,63),
+			ERASEINFO(0x02000,8)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_FUJITSU,
+		.dev_id		= MBM29F040C,
+		.name		= "Fujitsu MBM29F040C",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,8)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_FUJITSU,
+		.dev_id		= MBM29LV650UE,
+		.name		= "Fujitsu MBM29LV650UE",
+		.uaddr		= {
+			[0] = MTD_UADDR_DONT_CARE     /* x16 */
+		},
+		.DevSize	= SIZE_8MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,128)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_FUJITSU,
+		.dev_id		= MBM29LV320TE,
+		.name		= "Fujitsu MBM29LV320TE",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_4MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
+			ERASEINFO(0x10000,63),
+			ERASEINFO(0x02000,8)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_FUJITSU,
+		.dev_id		= MBM29LV320BE,
+		.name		= "Fujitsu MBM29LV320BE",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_4MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
+			ERASEINFO(0x02000,8),
+			ERASEINFO(0x10000,63)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_FUJITSU,
+		.dev_id		= MBM29LV160TE,
+		.name		= "Fujitsu MBM29LV160TE",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x10000,31),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x04000,1)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_FUJITSU,
+		.dev_id		= MBM29LV160BE,
+		.name		= "Fujitsu MBM29LV160BE",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x10000,31)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_FUJITSU,
+		.dev_id		= MBM29LV800BA,
+		.name		= "Fujitsu MBM29LV800BA",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x10000,15)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_FUJITSU,
+		.dev_id		= MBM29LV800TA,
+		.name		= "Fujitsu MBM29LV800TA",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x10000,15),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x04000,1)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_FUJITSU,
+		.dev_id		= MBM29LV400BC,
+		.name		= "Fujitsu MBM29LV400BC",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x10000,7)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_FUJITSU,
+		.dev_id		= MBM29LV400TC,
+		.name		= "Fujitsu MBM29LV400TC",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x10000,7),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x04000,1)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F004B3B,
+		.name		= "Intel 28F004B3B",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x02000, 8),
 			ERASEINFO(0x10000, 7),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F004B3T,
-		name:			"Intel 28F004B3T",
-		DevSize:		SIZE_512KiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F004B3T,
+		.name		= "Intel 28F004B3T",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x10000, 7),
 			ERASEINFO(0x02000, 8),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F400B3B,
-		name:			"Intel 28F400B3B",
-		DevSize:		SIZE_512KiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F400B3B,
+		.name		= "Intel 28F400B3B",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+			[1] = MTD_UADDR_UNNECESSARY,    /* x16 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x02000, 8),
 			ERASEINFO(0x10000, 7),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F400B3T,
-		name:			"Intel 28F400B3T",
-		DevSize:		SIZE_512KiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F400B3T,
+		.name		= "Intel 28F400B3T",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+			[1] = MTD_UADDR_UNNECESSARY,    /* x16 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x10000, 7),
 			ERASEINFO(0x02000, 8),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F008B3B,
-		name:			"Intel 28F008B3B",
-		DevSize:		SIZE_1MiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F008B3B,
+		.name		= "Intel 28F008B3B",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x02000, 8),
 			ERASEINFO(0x10000, 15),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F008B3T,
-		name:			"Intel 28F008B3T",
-		DevSize:		SIZE_1MiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F008B3T,
+		.name		= "Intel 28F008B3T",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x10000, 15),
 			ERASEINFO(0x02000, 8),
 		}
 	}, {
-		mfr_id: MANUFACTURER_INTEL,
-		dev_id: I28F008S5,
-		name: "Intel 28F008S5",
-		DevSize: SIZE_1MiB,
-		CmdSet: P_ID_INTEL_EXT,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,16),
-		}
-	}, {
-		mfr_id: MANUFACTURER_INTEL,
-		dev_id: I28F016S5,
-		name: "Intel 28F016S5",
-		DevSize: SIZE_2MiB,
-		CmdSet: P_ID_INTEL_EXT,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,32),
-		}
-	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F008SA,
-		name:			"Intel 28F008SA",
-		DevSize:		SIZE_1MiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	1,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F008S5,
+		.name		= "Intel 28F008S5",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_INTEL_EXT,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,16),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F016S5,
+		.name		= "Intel 28F016S5",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_INTEL_EXT,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,32),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F008SA,
+		.name		= "Intel 28F008SA",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
 			ERASEINFO(0x10000, 16),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F800B3B,
-		name:			"Intel 28F800B3B",
-		DevSize:		SIZE_1MiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F800B3B,
+		.name		= "Intel 28F800B3B",
+		.uaddr		= {
+			[1] = MTD_UADDR_UNNECESSARY,    /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x02000, 8),
 			ERASEINFO(0x10000, 15),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F800B3T,
-		name:			"Intel 28F800B3T",
-		DevSize:		SIZE_1MiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F800B3T,
+		.name		= "Intel 28F800B3T",
+		.uaddr		= {
+			[1] = MTD_UADDR_UNNECESSARY,    /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x10000, 15),
 			ERASEINFO(0x02000, 8),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F016B3B,
-		name:			"Intel 28F016B3B",
-		DevSize:		SIZE_2MiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F016B3B,
+		.name		= "Intel 28F016B3B",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x02000, 8),
 			ERASEINFO(0x10000, 31),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F016S3,
-		name:			"Intel I28F016S3",
-		DevSize:		SIZE_2MiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	1,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F016S3,
+		.name		= "Intel I28F016S3",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
 			ERASEINFO(0x10000, 32),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F016B3T,
-		name:			"Intel 28F016B3T",
-		DevSize:		SIZE_2MiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F016B3T,
+		.name		= "Intel 28F016B3T",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x10000, 31),
 			ERASEINFO(0x02000, 8),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F160B3B,
-		name:			"Intel 28F160B3B",
-		DevSize:		SIZE_2MiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F160B3B,
+		.name		= "Intel 28F160B3B",
+		.uaddr		= {
+			[1] = MTD_UADDR_UNNECESSARY,    /* x16 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x02000, 8),
 			ERASEINFO(0x10000, 31),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F160B3T,
-		name:			"Intel 28F160B3T",
-		DevSize:		SIZE_2MiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F160B3T,
+		.name		= "Intel 28F160B3T",
+		.uaddr		= {
+			[1] = MTD_UADDR_UNNECESSARY,    /* x16 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x10000, 31),
 			ERASEINFO(0x02000, 8),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F320B3B,
-		name:			"Intel 28F320B3B",
-		DevSize:		SIZE_4MiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F320B3B,
+		.name		= "Intel 28F320B3B",
+		.uaddr		= {
+			[1] = MTD_UADDR_UNNECESSARY,    /* x16 */
+		},
+		.DevSize	= SIZE_4MiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x02000, 8),
 			ERASEINFO(0x10000, 63),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F320B3T,
-		name:			"Intel 28F320B3T",
-		DevSize:		SIZE_4MiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F320B3T,
+		.name		= "Intel 28F320B3T",
+		.uaddr		= {
+			[1] = MTD_UADDR_UNNECESSARY,    /* x16 */
+		},
+		.DevSize	= SIZE_4MiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x10000, 63),
 			ERASEINFO(0x02000, 8),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F640B3B,
-		name:			"Intel 28F640B3B",
-		DevSize:		SIZE_8MiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F640B3B,
+		.name		= "Intel 28F640B3B",
+		.uaddr		= {
+			[1] = MTD_UADDR_UNNECESSARY,    /* x16 */
+		},
+		.DevSize	= SIZE_8MiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x02000, 8),
 			ERASEINFO(0x10000, 127),
 		}
 	}, {
-		mfr_id:			MANUFACTURER_INTEL,
-		dev_id:			I28F640B3T,
-		name:			"Intel 28F640B3T",
-		DevSize:		SIZE_8MiB,
-		CmdSet:			P_ID_INTEL_STD,
-		NumEraseRegions:	2,
-		regions: {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I28F640B3T,
+		.name		= "Intel 28F640B3T",
+		.uaddr		= {
+			[1] = MTD_UADDR_UNNECESSARY,    /* x16 */
+		},
+		.DevSize	= SIZE_8MiB,
+		.CmdSet		= P_ID_INTEL_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
 			ERASEINFO(0x10000, 127),
 			ERASEINFO(0x02000, 8),
 		}
 	}, {
-		mfr_id: MANUFACTURER_INTEL,
-		dev_id: I82802AB,
-		name: "Intel 82802AB",
-		DevSize: SIZE_512KiB,
-		CmdSet: P_ID_INTEL_EXT,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,8),
-		}
-	}, {
-		mfr_id: MANUFACTURER_INTEL,
-		dev_id: I82802AC,
-		name: "Intel 82802AC",
-		DevSize: SIZE_1MiB,
-		CmdSet: P_ID_INTEL_EXT,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,16),
-		}
-	}, {
-		mfr_id: MANUFACTURER_ST,
-		dev_id: M29W800T,
-		name: "ST M29W800T",
-		DevSize: SIZE_1MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,15),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x04000,1)
-		}
-	}, {
-		mfr_id: MANUFACTURER_ST,
-		dev_id: M29W160DT,
-		name: "ST M29W160DT",
-		DevSize: SIZE_2MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,31),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x04000,1)
-		}
-	}, {
-		mfr_id: MANUFACTURER_ST,
-		dev_id: M29W160DB,
-		name: "ST M29W160DB",
-		DevSize: SIZE_2MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x04000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x10000,31)
-		}
-	}, {
-		mfr_id: MANUFACTURER_ATMEL,
-		dev_id: AT49BV512,
-		name: "Atmel AT49BV512",
-		DevSize: SIZE_64KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,1)
-		}
-	}, {
-		mfr_id: MANUFACTURER_ATMEL,
-		dev_id: AT29LV512,
-		name: "Atmel AT29LV512",
-		DevSize: SIZE_64KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {
-			ERASEINFO(0x80,256),
-			ERASEINFO(0x80,256)
-		}
-	}, {
-		mfr_id: MANUFACTURER_ATMEL,
-		dev_id: AT49BV16X,
-		name: "Atmel AT49BV16X",
-		DevSize: SIZE_2MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 2,
-		regions: {ERASEINFO(0x02000,8),
-			  ERASEINFO(0x10000,31)
-		}
-	}, {
-		mfr_id: MANUFACTURER_ATMEL,
-		dev_id: AT49BV16XT,
-		name: "Atmel AT49BV16XT",
-		DevSize: SIZE_2MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 2,
-		regions: {ERASEINFO(0x10000,31),
-			  ERASEINFO(0x02000,8)
-		}
-	}, {
-		mfr_id: MANUFACTURER_ATMEL,
-		dev_id: AT49BV32X,
-		name: "Atmel AT49BV32X",
-		DevSize: SIZE_4MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 2,
-		regions: {ERASEINFO(0x02000,8),
-			  ERASEINFO(0x10000,63)
-		}
-	}, {
-		mfr_id: MANUFACTURER_ATMEL,
-		dev_id: AT49BV32XT,
-		name: "Atmel AT49BV32XT",
-		DevSize: SIZE_4MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 2,
-		regions: {ERASEINFO(0x10000,63),
-			  ERASEINFO(0x02000,8)
-		}
-	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29F017D,
-		name: "AMD AM29F017D",
-		DevSize: SIZE_2MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,32),
-		}
-	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29F016,
-		name: "AMD AM29F016",
-		DevSize: SIZE_2MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,32),
-		}
-	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29F080,
-		name: "AMD AM29F080",
-		DevSize: SIZE_1MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,16),
-		}
-	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29F040,
-		name: "AMD AM29F040",
-		DevSize: SIZE_512KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,8),
-		}
-	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29LV040B,
-		name: "AMD AM29LV040B",
-		DevSize: SIZE_512KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,8),
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I82802AB,
+		.name		= "Intel 82802AB",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_INTEL_EXT,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,8),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_INTEL,
+		.dev_id		= I82802AC,
+		.name		= "Intel 82802AC",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_INTEL_EXT,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,16),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_MACRONIX,
+		.dev_id		= MX29LV160T,
+		.name		= "MXIC MX29LV160T",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x10000,31),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x04000,1)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_MACRONIX,
+		.dev_id		= MX29LV160B,
+		.name		= "MXIC MX29LV160B",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x10000,31)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_MACRONIX,
+		.dev_id		= MX29F016,
+		.name		= "Macronix MX29F016",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,32),
 		}
         }, {
-		mfr_id: MANUFACTURER_ST,
-		dev_id: M29W040B,
-		name: "ST M29W040B",
-		DevSize: SIZE_512KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,8),
-		}
-	}, {
-		mfr_id: MANUFACTURER_MACRONIX,
-		dev_id: MX29LV160T,
-		name: "MXIC MX29LV160T",
-		DevSize: SIZE_2MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,31),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x04000,1)
-		}
-	}, {
-		mfr_id: MANUFACTURER_MACRONIX,
-		dev_id: MX29LV160B,
-		name: "MXIC MX29LV160B",
-		DevSize: SIZE_2MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x04000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x10000,31)
-		}
-	}, {
-		mfr_id: MANUFACTURER_MACRONIX,
-		dev_id: MX29F016,
-		name: "Macronix MX29F016",
-		DevSize: SIZE_2MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,32),
+		.mfr_id		= MANUFACTURER_MACRONIX,
+		.dev_id		= MX29F004T,
+		.name		= "Macronix MX29F004T",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x10000,7),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x04000,1),
 		}
         }, {
-		mfr_id: MANUFACTURER_MACRONIX,
-		dev_id: MX29F004T,
-		name: "Macronix MX29F004T",
-		DevSize: SIZE_512KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,7),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x04000,1),
+		.mfr_id		= MANUFACTURER_MACRONIX,
+		.dev_id		= MX29F004B,
+		.name		= "Macronix MX29F004B",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x10000,7),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_PMC,
+		.dev_id		= PM49FL002,
+		.name		= "PMC Pm49FL002",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_256KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO( 0x01000, 64 )
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_PMC,
+		.dev_id		= PM49FL004,
+		.name		= "PMC Pm49FL004",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO( 0x01000, 128 )
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_PMC,
+		.dev_id		= PM49FL008,
+		.name		= "PMC Pm49FL008",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO( 0x01000, 256 )
 		}
         }, {
-		mfr_id: MANUFACTURER_MACRONIX,
-		dev_id: MX29F004B,
-		name: "Macronix MX29F004B",
-		DevSize: SIZE_512KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x04000,1),
-			  ERASEINFO(0x02000,2),
-			  ERASEINFO(0x08000,1),
-			  ERASEINFO(0x10000,7),
+		.mfr_id		= MANUFACTURER_SST,
+		.dev_id		= SST39LF512,
+		.name		= "SST 39LF512",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_64KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x01000,16),
 		}
         }, {
-		mfr_id: MANUFACTURER_SST,
-		dev_id: SST39LF512,
-		name: "SST 39LF512",
-		DevSize: SIZE_64KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x01000,16),
+		.mfr_id		= MANUFACTURER_SST,
+		.dev_id		= SST39LF010,
+		.name		= "SST 39LF010",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_128KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x01000,32),
 		}
         }, {
-		mfr_id: MANUFACTURER_SST,
-		dev_id: SST39LF010,
-		name: "SST 39LF010",
-		DevSize: SIZE_128KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x01000,32),
+		.mfr_id		= MANUFACTURER_SST,
+ 		.dev_id 	= SST29EE020,
+		.name		= "SST 29EE020",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+ 		.DevSize	= SIZE_256KiB,
+ 		.CmdSet		= P_ID_SST_PAGE,
+ 		.NumEraseRegions= 1,
+ 		regions: {ERASEINFO(0x01000,64),
+ 		}
+         }, {
+ 		.mfr_id		= MANUFACTURER_SST,
+		.dev_id		= SST29LE020,
+ 		.name		= "SST 29LE020",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+ 		.DevSize	= SIZE_256KiB,
+ 		.CmdSet		= P_ID_SST_PAGE,
+ 		.NumEraseRegions= 1,
+ 		regions: {ERASEINFO(0x01000,64),
+ 		}
+	}, {
+		.mfr_id		= MANUFACTURER_SST,
+		.dev_id		= SST39LF020,
+		.name		= "SST 39LF020",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_256KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x01000,64),
 		}
         }, {
-		mfr_id: MANUFACTURER_SST,
-		dev_id: SST39LF020,
-		name: "SST 39LF020",
-		DevSize: SIZE_256KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x01000,64),
+		.mfr_id		= MANUFACTURER_SST,
+		.dev_id		= SST39LF040,
+		.name		= "SST 39LF040",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x01000,128),
 		}
         }, {
-		mfr_id: MANUFACTURER_SST,
-		dev_id: SST39LF040,
-		name: "SST 39LF040",
-		DevSize: SIZE_512KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x01000,128),
+		.mfr_id		= MANUFACTURER_SST,
+		.dev_id		= SST39SF010A,
+		.name		= "SST 39SF010A",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_128KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x01000,32),
 		}
         }, {
-		mfr_id: MANUFACTURER_SST,
-		dev_id: SST39SF010A,
-		name: "SST 39SF010A",
-		DevSize: SIZE_128KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x01000,32),
+		.mfr_id		= MANUFACTURER_SST,
+		.dev_id		= SST39SF020A,
+		.name		= "SST 39SF020A",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_256KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x01000,64),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_SST,
+		.dev_id		= SST49LF004B,
+		.name		= "SST 49LF004B",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x01000,128),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_SST,
+		.dev_id		= SST49LF030A,
+		.name		= "SST 49LF030A",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x01000,96),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_SST,
+		.dev_id		= SST49LF040A,
+		.name		= "SST 49LF040A",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x01000,128),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_SST,
+		.dev_id		= SST49LF080A,
+		.name		= "SST 49LF080A",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x01000,256),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_ST,	/* FIXME - CFI device? */
+		.dev_id		= M29W800DT,
+		.name		= "ST M29W800DT",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA,  /* x8 */
+			[1] = MTD_UADDR_0x5555_0x2AAA   /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x10000,15),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x04000,1)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_ST,	/* FIXME - CFI device? */
+		.dev_id		= M29W800DB,
+		.name		= "ST M29W800DB",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA,  /* x8 */
+			[1] = MTD_UADDR_0x5555_0x2AAA   /* x16 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x10000,15)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_ST,	/* FIXME - CFI device? */
+		.dev_id		= M29W160DT,
+		.name		= "ST M29W160DT",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x02AA,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x10000,31),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x04000,1)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_ST,	/* FIXME - CFI device? */
+		.dev_id		= M29W160DB,
+		.name		= "ST M29W160DB",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x02AA,  /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA,  /* x16 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x10000,31)
 		}
         }, {
-		mfr_id: MANUFACTURER_SST,
-		dev_id: SST39SF020A,
-		name: "SST 39SF020A",
-		DevSize: SIZE_256KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x01000,64),
-		}
-	}, {
-		mfr_id: MANUFACTURER_SST,
-		dev_id: SST49LF030A,
-		name: "SST 49LF030A",
-		DevSize: SIZE_512KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x01000,96),
-		}
-	}, {
-		mfr_id: MANUFACTURER_SST,
-		dev_id: SST49LF040A,
-		name: "SST 49LF040A",
-		DevSize: SIZE_512KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x01000,128),
-		}
-	}, {
-		mfr_id: MANUFACTURER_SST,
-		dev_id: SST49LF080A,
-		name: "SST 49LF080A",
-		DevSize: SIZE_1MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x01000,256),
+		.mfr_id		= MANUFACTURER_ST,
+		.dev_id		= M29W040B,
+		.name		= "ST M29W040B",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x02AA /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,8),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_TOSHIBA,
+		.dev_id		= TC58FVT160,
+		.name		= "Toshiba TC58FVT160",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA  /* x16 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x10000,31),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x04000,1)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_TOSHIBA,
+		.dev_id		= TC58FVB160,
+		.name		= "Toshiba TC58FVB160",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA  /* x16 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x04000,1),
+			ERASEINFO(0x02000,2),
+			ERASEINFO(0x08000,1),
+			ERASEINFO(0x10000,31)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_TOSHIBA,
+		.dev_id		= TC58FVB321,
+		.name		= "Toshiba TC58FVB321",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA  /* x16 */
+		},
+		.DevSize	= SIZE_4MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
+			ERASEINFO(0x02000,8),
+			ERASEINFO(0x10000,63)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_TOSHIBA,
+		.dev_id		= TC58FVT321,
+		.name		= "Toshiba TC58FVT321",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA  /* x16 */
+		},
+		.DevSize	= SIZE_4MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
+			ERASEINFO(0x10000,63),
+			ERASEINFO(0x02000,8)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_TOSHIBA,
+		.dev_id		= TC58FVB641,
+		.name		= "Toshiba TC58FVB641",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
+		},
+		.DevSize	= SIZE_8MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
+			ERASEINFO(0x02000,8),
+			ERASEINFO(0x10000,127)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_TOSHIBA,
+		.dev_id		= TC58FVT641,
+		.name		= "Toshiba TC58FVT641",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */
+			[1] = MTD_UADDR_0x0555_0x02AA, /* x16 */
+		},
+		.DevSize	= SIZE_8MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 2,
+		.regions	= {
+			ERASEINFO(0x10000,127),
+			ERASEINFO(0x02000,8)
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_WINBOND,
+		.dev_id		= W49V002A,
+		.name		= "Winbond W49V002A",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_256KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 4,
+		.regions	= {
+			ERASEINFO(0x10000, 3),
+			ERASEINFO(0x08000, 1),
+			ERASEINFO(0x02000, 2),
+			ERASEINFO(0x04000, 1),
 		}
 	} 
 };
@@ -907,7 +1520,7 @@
 static int cfi_jedec_setup(struct cfi_private *p_cfi, int index);
 
 static int jedec_probe_chip(struct map_info *map, __u32 base,
-			    struct flchip *chips, struct cfi_private *cfi);
+			    unsigned long *chip_map, struct cfi_private *cfi);
 
 struct mtd_info *jedec_probe(struct map_info *map);
 
@@ -937,18 +1550,63 @@
 	struct cfi_private *cfi)
 {
 	/* Reset */
-	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
+
+	/* after checking the datasheets for SST, MACRONIX and ATMEL
+	 * (oh and incidentaly the jedec spec - 3.5.3.3) the reset
+	 * sequence is *supposed* to be 0xaa at 0x5555, 0x55 at
+	 * 0x2aaa, 0xF0 at 0x5555 this will not affect the AMD chips
+	 * as they will ignore the writes and dont care what address
+	 * the F0 is written to */
+	if(cfi->addr_unlock1) {
+		/*printk("reset unlock called %x %x \n",cfi->addr_unlock1,cfi->addr_unlock2);*/
+		cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
+		cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
+	}
+
+	cfi_send_gen_cmd(0xF0, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
 	/* Some misdesigned intel chips do not respond for 0xF0 for a reset,
 	 * so ensure we're in read mode.  Send both the Intel and the AMD command
 	 * for this.  Intel uses 0xff for this, AMD uses 0xff for NOP, so
 	 * this should be safe.
 	 */ 
 	cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
+	/* FIXME - should have reset delay before continuing */
+}
+
 
+static inline __u8 finfo_uaddr(const struct amd_flash_info *finfo, int device_type)
+{
+	int uaddr_idx;
+	__u8 uaddr = MTD_UADDR_NOT_SUPPORTED;
+
+	switch ( device_type ) {
+	case CFI_DEVICETYPE_X8:  uaddr_idx = 0; break;
+	case CFI_DEVICETYPE_X16: uaddr_idx = 1; break;
+	case CFI_DEVICETYPE_X32: uaddr_idx = 2; break;
+	default:
+		printk(KERN_NOTICE "MTD: %s(): unknown device_type %d\n",
+		       __func__, device_type);
+		goto uaddr_done;
+	}
+
+	uaddr = finfo->uaddr[uaddr_idx];
+
+	if (uaddr != MTD_UADDR_NOT_SUPPORTED ) {
+		/* ASSERT("The unlock addresses for non-8-bit mode
+		   are bollocks. We don't really need an array."); */
+		uaddr = finfo->uaddr[0];
+	}
+
+ uaddr_done:
+	return uaddr;
 }
+
+
 static int cfi_jedec_setup(struct cfi_private *p_cfi, int index)
 {
 	int i,num_erase_regions;
+	unsigned long mask;
+	__u8 uaddr;
 
 	printk("Found: %s\n",jedec_table[index].name);
 
@@ -971,41 +1629,170 @@
 		p_cfi->cfiq->EraseRegionInfo[i] = jedec_table[index].regions[i];
 	}
 	p_cfi->cmdset_priv = 0;
+
+	/* This may be redundant for some cases, but it doesn't hurt */
+	p_cfi->mfr = jedec_table[index].mfr_id;
+	p_cfi->id = jedec_table[index].dev_id;
+
+	uaddr = finfo_uaddr(&jedec_table[index], p_cfi->device_type);
+	if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) {
+		kfree( p_cfi->cfiq );
+		return 0;
+	}
+
+	/* Mask out address bits which are smaller than the device type */
+	mask = ~(p_cfi->device_type-1);
+	p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 & mask;
+	p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 & mask;
+
 	return 1; 	/* ok */
 }
 
+
+/*
+ * There is a BIG problem properly ID'ing the JEDEC devic and guaranteeing
+ * the mapped address, unlock addresses, and proper chip ID.  This function
+ * attempts to minimize errors.  It is doubtfull that this probe will ever
+ * be perfect - consequently there should be some module parameters that
+ * could be manually specified to force the chip info.
+ */
+static inline int jedec_match( __u32 base,
+			       struct map_info *map,
+			       struct cfi_private *cfi,
+			       const struct amd_flash_info *finfo )
+{
+	int rc = 0;           /* failure until all tests pass */
+	u32 mfr, id;
+	__u8 uaddr;
+	unsigned long mask;
+
+	/*
+	 * The IDs must match.  For X16 and X32 devices operating in
+	 * a lower width ( X8 or X16 ), the device ID's are usually just
+	 * the lower byte(s) of the larger device ID for wider mode.  If
+	 * a part is found that doesn't fit this assumption (device id for
+	 * smaller width mode is completely unrealated to full-width mode)
+	 * then the jedec_table[] will have to be augmented with the IDs
+	 * for different widths.
+	 */
+	switch (cfi->device_type) {
+	case CFI_DEVICETYPE_X8:
+		mfr = (__u8)finfo->mfr_id;
+		id = (__u8)finfo->dev_id;
+		break;
+	case CFI_DEVICETYPE_X16:
+		mfr = (__u16)finfo->mfr_id;
+		id = (__u16)finfo->dev_id;
+		break;
+	case CFI_DEVICETYPE_X32:
+		mfr = (__u16)finfo->mfr_id;
+		id = (__u32)finfo->dev_id;
+		break;
+	default:
+		printk(KERN_WARNING
+		       "MTD %s(): Unsupported device type %d\n",
+		       __func__, cfi->device_type);
+		goto match_done;
+	}
+	if ( cfi->mfr != mfr || cfi->id != id ) {
+		goto match_done;
+	}
+
+	/* the part size must fit in the memory window */
+	DEBUG( MTD_DEBUG_LEVEL3,
+	       "MTD %s(): Check fit 0x%.8x + 0x%.8x = 0x%.8x\n",
+	       __func__, base, 1 << finfo->DevSize, base + (1 << finfo->DevSize) );
+	if ( base + cfi->interleave * ( 1 << finfo->DevSize ) > map->size ) {
+		DEBUG( MTD_DEBUG_LEVEL3,
+		       "MTD %s(): 0x%.4x 0x%.4x %dKiB doesn't fit\n",
+		       __func__, finfo->mfr_id, finfo->dev_id,
+		       1 << finfo->DevSize );
+		goto match_done;
+	}
+
+	uaddr = finfo_uaddr(finfo, cfi->device_type);
+	if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) {
+		goto match_done;
+	}
+
+	mask = ~(cfi->device_type-1);
+
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n",
+	       __func__, cfi->addr_unlock1, cfi->addr_unlock2 );
+	if ( MTD_UADDR_UNNECESSARY != uaddr && MTD_UADDR_DONT_CARE != uaddr
+	     && ( (unlock_addrs[uaddr].addr1 & mask) != cfi->addr_unlock1 ||
+		  (unlock_addrs[uaddr].addr2 & mask) != cfi->addr_unlock2 ) ) {
+		DEBUG( MTD_DEBUG_LEVEL3,
+		       "MTD %s(): 0x%.4x 0x%.4x did not match\n",
+		       __func__,
+		       unlock_addrs[uaddr].addr1 & mask,
+		       unlock_addrs[uaddr].addr2 & mask);
+		goto match_done;
+	}
+
+	/*
+	 * Make sure the ID's dissappear when the device is taken out of
+	 * ID mode.  The only time this should fail when it should succeed
+	 * is when the ID's are written as data to the same
+	 * addresses.  For this rare and unfortunate case the chip
+	 * cannot be probed correctly.
+	 * FIXME - write a driver that takes all of the chip info as
+	 * module parameters, doesn't probe but forces a load.
+	 */
+	DEBUG( MTD_DEBUG_LEVEL3,
+	       "MTD %s(): check ID's disappear when not in ID mode\n",
+	       __func__ );
+	jedec_reset( base, map, cfi );
+	mfr = jedec_read_mfr( map, base, cfi );
+	id = jedec_read_id( map, base, cfi );
+	if ( mfr == cfi->mfr && id == cfi->id ) {
+		DEBUG( MTD_DEBUG_LEVEL3,
+		       "MTD %s(): ID 0x%.2x:0x%.2x did not change after reset:\n"
+		       "You might need to manually specify JEDEC parameters.\n",
+			__func__, cfi->mfr, cfi->id );
+		goto match_done;
+	}
+
+	/* all tests passed - mark  as success */
+	rc = 1;
+
+	/*
+	 * Put the device back in ID mode - only need to do this if we
+	 * were truly frobbing a real device.
+	 */
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): return to ID mode\n", __func__ );
+	if(cfi->addr_unlock1) {
+		cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
+		cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
+	}
+	cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
+	/* FIXME - should have a delay before continuing */
+
+ match_done:	
+	return rc;
+}
+
+
 static int jedec_probe_chip(struct map_info *map, __u32 base,
-			      struct flchip *chips, struct cfi_private *cfi)
+			    unsigned long *chip_map, struct cfi_private *cfi)
 {
 	int i;
-	int unlockpass = 0;
+	enum uaddr uaddr_idx = MTD_UADDR_NOT_SUPPORTED;
 
+ retry:
 	if (!cfi->numchips) {
-		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_NOTICE "Eep. Unknown jedec_probe device type %d\n", cfi->device_type);
-		return 0;
-		}
+		unsigned long mask = ~(cfi->device_type-1);
+
+		uaddr_idx++;
+
+		if (MTD_UADDR_UNNECESSARY == uaddr_idx)
+			return 0;
+
+		/* Mask out address bits which are smaller than the device type */
+		cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1 & mask;
+		cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2 & mask;
 	}
 
- retry:
 	/* Make certain we aren't probing past the end of map */
 	if (base >= map->size) {
 		printk(KERN_NOTICE
@@ -1038,6 +1825,7 @@
 		cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
 	}
 	cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
+	/* FIXME - should have a delay before continuing */
 
 	if (!cfi->numchips) {
 		/* This is the first time we're called. Set up the CFI 
@@ -1045,26 +1833,21 @@
 		
 		cfi->mfr = jedec_read_mfr(map, base, cfi);
 		cfi->id = jedec_read_id(map, base, cfi);
-		printk(KERN_INFO "Search for id:(%02x %02x) interleave(%d) type(%d)\n", 
+		DEBUG(MTD_DEBUG_LEVEL3,
+		      "Search for id:(%02x %02x) interleave(%d) type(%d)\n", 
 			cfi->mfr, cfi->id, cfi->interleave, cfi->device_type);
 		for (i=0; i<sizeof(jedec_table)/sizeof(jedec_table[0]); i++) {
-			if (cfi->mfr == jedec_table[i].mfr_id &&
-			    cfi->id == jedec_table[i].dev_id) {
+			if ( jedec_match( base, map, cfi, &jedec_table[i] ) ) {
+				DEBUG( MTD_DEBUG_LEVEL3,
+				       "MTD %s(): matched device 0x%x,0x%x unlock_addrs: 0x%.4x 0x%.4x\n",
+				       __func__, cfi->mfr, cfi->id,
+				       cfi->addr_unlock1, cfi->addr_unlock2 );
 				if (!cfi_jedec_setup(cfi, i))
 					return 0;
 				goto ok_out;
 			}
 		}
-		switch(unlockpass++) {
-		case 0:
-			cfi->addr_unlock1 |= cfi->addr_unlock1 << 4;
-			cfi->addr_unlock2 |= cfi->addr_unlock2 << 4;
-			goto retry;
-		case 1:
-			cfi->addr_unlock1 = cfi->addr_unlock2 = 0;
-			goto retry;
-		}
-		return 0;
+		goto retry;
 	} else {
 		__u16 mfr;
 		__u16 id;
@@ -1081,21 +1864,24 @@
 		}
 	}
 	
-	/* Check each previous chip to see if it's an alias */
-	for (i=0; i<cfi->numchips; i++) {
-		/* This chip should be in read mode if it's one
-		   we've already touched. */
-		if (jedec_read_mfr(map, chips[i].start, cfi) == cfi->mfr &&
-		    jedec_read_id(map, chips[i].start, cfi) == cfi->id) {
+	/* Check each previous chip locations to see if it's an alias */
+	for (i=0; i < (base >> cfi->chipshift); i++) {
+		unsigned long start;
+		if(!test_bit(i, chip_map)) {
+			continue; /* Skip location; no valid chip at this address */
+		}
+		start = i << cfi->chipshift;
+		if (jedec_read_mfr(map, start, cfi) == cfi->mfr &&
+		    jedec_read_id(map, start, cfi) == cfi->id) {
 			/* Eep. This chip also looks like it's in autoselect mode.
 			   Is it an alias for the new one? */
-			jedec_reset(chips[i].start, map, cfi);
+			jedec_reset(start, map, cfi);
 
 			/* If the device IDs go away, it's an alias */
 			if (jedec_read_mfr(map, base, cfi) != cfi->mfr ||
 			    jedec_read_id(map, base, cfi) != cfi->id) {
 				printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
-				       map->name, base, chips[i].start);
+				       map->name, base, start);
 				return 0;
 			}
 			
@@ -1107,7 +1893,7 @@
 			if (jedec_read_mfr(map, base, cfi) == cfi->mfr &&
 			    jedec_read_id(map, base, cfi) == cfi->id) {
 				printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n",
-				       map->name, base, chips[i].start);
+				       map->name, base, start);
 				return 0;
 			}
 		}
@@ -1115,13 +1901,7 @@
 		
 	/* OK, if we got to here, then none of the previous chips appear to
 	   be aliases for the current one. */
-	if (cfi->numchips == MAX_CFI_CHIPS) {
-		printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS);
-		/* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */
-		return -1;
-	}
-	chips[cfi->numchips].start = base;
-	chips[cfi->numchips].state = FL_READY;
+	set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */
 	cfi->numchips++;
 		
 ok_out:
@@ -1136,8 +1916,8 @@
 }
 
 static struct chip_probe jedec_chip_probe = {
-	name: "JEDEC",
-	probe_chip: jedec_probe_chip
+	.name = "JEDEC",
+	.probe_chip = jedec_probe_chip
 };
 
 struct mtd_info *jedec_probe(struct map_info *map)
@@ -1150,9 +1930,9 @@
 }
 
 static struct mtd_chip_driver jedec_chipdrv = {
-	probe: jedec_probe,
-	name: "jedec_probe",
-	module: THIS_MODULE
+	.probe	= jedec_probe,
+	.name	= "jedec_probe",
+	.module	= THIS_MODULE
 };
 
 int __init jedec_probe_init(void)
diff -Nur linux-2.4.30/drivers/mtd/chips/Makefile linux-2.4.30-linux-mips.org/drivers/mtd/chips/Makefile
--- linux-2.4.30/drivers/mtd/chips/Makefile	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -1,11 +1,12 @@
 #
 # linux/drivers/chips/Makefile
 #
-# $Id: Makefile,v 1.8 2002/01/10 20:27:40 eric Exp $
+# $Id: Makefile.common,v 1.3 2003/09/25 14:40:34 thayne Exp $
 
+ifeq ($(PATCHLEVEL),4)
 O_TARGET	:= chipslink.o
-
-export-objs	:= chipreg.o gen_probe.o
+export-objs	:= chipreg.o gen_probe.o cfi_util.o
+endif
 
 #                       *** BIG UGLY NOTE ***
 #
@@ -17,6 +18,7 @@
 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
@@ -28,4 +30,4 @@
 obj-$(CONFIG_MTD_SHARP)		+= sharp.o
 obj-$(CONFIG_MTD_ABSENT)	+= map_absent.o
 
-include $(TOPDIR)/Rules.make
+-include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/drivers/mtd/chips/map_absent.c linux-2.4.30-linux-mips.org/drivers/mtd/chips/map_absent.c
--- linux-2.4.30/drivers/mtd/chips/map_absent.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/map_absent.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,7 +1,7 @@
 /*
  * Common code to handle absent "placeholder" devices
  * Copyright 2001 Resilience Corporation <ebrower@resilience.com>
- * $Id: map_absent.c,v 1.2 2001/10/02 15:05:12 dwmw2 Exp $
+ * $Id: map_absent.c,v 1.4 2003/05/28 12:51:49 dwmw2 Exp $
  *
  * This map driver is used to allocate "placeholder" MTD
  * devices on systems that have socketed/removable media. 
@@ -23,9 +23,10 @@
 #include <linux/kernel.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
-
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
-
+#include <linux/mtd/compatmac.h>
 
 static int map_absent_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
 static int map_absent_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
@@ -36,10 +37,10 @@
 
 
 static struct mtd_chip_driver map_absent_chipdrv = {
-	probe: 		map_absent_probe,
-	destroy:	map_absent_destroy,
-	name: 		"map_absent",
-	module: 	THIS_MODULE
+	.probe		= map_absent_probe,
+	.destroy	= map_absent_destroy,
+	.name		= "map_absent",
+	.module		= THIS_MODULE
 };
 
 static struct mtd_info *map_absent_probe(struct map_info *map)
@@ -65,7 +66,7 @@
 	mtd->flags 	= 0;
 	mtd->erasesize = PAGE_SIZE;
 
-	MOD_INC_USE_COUNT;
+	__module_get(THIS_MODULE);
 	return mtd;
 }
 
diff -Nur linux-2.4.30/drivers/mtd/chips/map_ram.c linux-2.4.30-linux-mips.org/drivers/mtd/chips/map_ram.c
--- linux-2.4.30/drivers/mtd/chips/map_ram.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/map_ram.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,7 +1,7 @@
 /*
  * Common code to handle map devices which are simple RAM
  * (C) 2000 Red Hat. GPL'd.
- * $Id: map_ram.c,v 1.14 2001/10/02 15:05:12 dwmw2 Exp $
+ * $Id: map_ram.c,v 1.17 2003/05/28 12:51:49 dwmw2 Exp $
  */
 
 #include <linux/module.h>
@@ -11,8 +11,10 @@
 #include <asm/byteorder.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
-
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
+#include <linux/mtd/compatmac.h>
 
 
 static int mapram_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
@@ -23,9 +25,9 @@
 
 
 static struct mtd_chip_driver mapram_chipdrv = {
-	probe: map_ram_probe,
-	name: "map_ram",
-	module: THIS_MODULE
+	.probe	= map_ram_probe,
+	.name	= "map_ram",
+	.module	= THIS_MODULE
 };
 
 static struct mtd_info *map_ram_probe(struct map_info *map)
@@ -34,21 +36,21 @@
 
 	/* Check the first byte is RAM */
 #if 0
-	map->write8(map, 0x55, 0);
-	if (map->read8(map, 0) != 0x55)
+	map_write8(map, 0x55, 0);
+	if (map_read8(map, 0) != 0x55)
 		return NULL;
 
-	map->write8(map, 0xAA, 0);
-	if (map->read8(map, 0) != 0xAA)
+	map_write8(map, 0xAA, 0);
+	if (map_read8(map, 0) != 0xAA)
 		return NULL;
 
 	/* Check the last byte is RAM */
-	map->write8(map, 0x55, map->size-1);
-	if (map->read8(map, map->size-1) != 0x55)
+	map_write8(map, 0x55, map->size-1);
+	if (map_read8(map, map->size-1) != 0x55)
 		return NULL;
 
-	map->write8(map, 0xAA, map->size-1);
-	if (map->read8(map, map->size-1) != 0xAA)
+	map_write8(map, 0xAA, map->size-1);
+	if (map_read8(map, map->size-1) != 0xAA)
 		return NULL;
 #endif
 	/* OK. It seems to be RAM. */
@@ -74,7 +76,7 @@
  	while(mtd->size & (mtd->erasesize - 1))
 		mtd->erasesize >>= 1;
 
-	MOD_INC_USE_COUNT;
+	__module_get(THIS_MODULE);
 	return mtd;
 }
 
@@ -83,7 +85,7 @@
 {
 	struct map_info *map = (struct map_info *)mtd->priv;
 
-	map->copy_from(map, buf, from, len);
+	map_copy_from(map, buf, from, len);
 	*retlen = len;
 	return 0;
 }
@@ -92,7 +94,7 @@
 {
 	struct map_info *map = (struct map_info *)mtd->priv;
 
-	map->copy_to(map, to, buf, len);
+	map_copy_to(map, to, buf, len);
 	*retlen = len;
 	return 0;
 }
@@ -105,7 +107,7 @@
 	unsigned long i;
 
 	for (i=0; i<instr->len; i++)
-		map->write8(map, 0xFF, instr->addr + i);
+		map_write8(map, 0xFF, instr->addr + i);
 
 	if (instr->callback)
 		instr->callback(instr);
diff -Nur linux-2.4.30/drivers/mtd/chips/map_rom.c linux-2.4.30-linux-mips.org/drivers/mtd/chips/map_rom.c
--- linux-2.4.30/drivers/mtd/chips/map_rom.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/map_rom.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,7 +1,7 @@
 /*
  * Common code to handle map devices which are simple ROM
  * (C) 2000 Red Hat. GPL'd.
- * $Id: map_rom.c,v 1.17 2001/10/02 15:05:12 dwmw2 Exp $
+ * $Id: map_rom.c,v 1.20 2003/05/28 12:51:49 dwmw2 Exp $
  */
 
 #include <linux/version.h>
@@ -12,8 +12,10 @@
 #include <asm/byteorder.h>
 #include <linux/errno.h>
 #include <linux/slab.h>
-
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
+#include <linux/mtd/compatmac.h>
 
 static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
 static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
@@ -21,9 +23,9 @@
 struct mtd_info *map_rom_probe(struct map_info *map);
 
 static struct mtd_chip_driver maprom_chipdrv = {
-	probe: map_rom_probe,
-	name: "map_rom",
-	module: THIS_MODULE
+	.probe	= map_rom_probe,
+	.name	= "map_rom",
+	.module	= THIS_MODULE
 };
 
 struct mtd_info *map_rom_probe(struct map_info *map)
@@ -49,7 +51,7 @@
  	while(mtd->size & (mtd->erasesize - 1))
 		mtd->erasesize >>= 1;
 
-	MOD_INC_USE_COUNT;
+	__module_get(THIS_MODULE);
 	return mtd;
 }
 
@@ -58,7 +60,7 @@
 {
 	struct map_info *map = (struct map_info *)mtd->priv;
 
-	map->copy_from(map, buf, from, len);
+	map_copy_from(map, buf, from, len);
 	*retlen = len;
 	return 0;
 }
diff -Nur linux-2.4.30/drivers/mtd/chips/sharp.c linux-2.4.30-linux-mips.org/drivers/mtd/chips/sharp.c
--- linux-2.4.30/drivers/mtd/chips/sharp.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/chips/sharp.c	2005-05-12 18:01:29.000000000 +0200
@@ -4,7 +4,7 @@
  * Copyright 2000,2001 David A. Schleef <ds@schleef.org>
  *           2000,2001 Lineo, Inc.
  *
- * $Id: sharp.c,v 1.8 2002/05/17 08:59:19 dwmw2 Exp $
+ * $Id: sharp.c,v 1.12 2003/05/28 15:39:52 dwmw2 Exp $
  *
  * Devices supported:
  *   LH28F016SCT Symmetrical block flash memory, 2Mx8
@@ -28,6 +28,7 @@
 #include <linux/errno.h>
 #include <linux/interrupt.h>
 #include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
 #include <linux/mtd/cfi.h>
 #include <linux/delay.h>
 
@@ -98,10 +99,10 @@
 static void sharp_destroy(struct mtd_info *mtd);
 
 static struct mtd_chip_driver sharp_chipdrv = {
-	probe: sharp_probe,
-	destroy: sharp_destroy,
-	name: "sharp",
-	module: THIS_MODULE
+	.probe		= sharp_probe,
+	.destroy	= sharp_destroy,
+	.name		= "sharp",
+	.module		= THIS_MODULE
 };
 
 
@@ -116,8 +117,10 @@
 		return NULL;
 
 	sharp = kmalloc(sizeof(*sharp), GFP_KERNEL);
-	if(!sharp)
+	if(!sharp) {
+		kfree(mtd);
 		return NULL;
+	}
 
 	memset(mtd, 0, sizeof(*mtd));
 
@@ -163,12 +166,12 @@
 	u32 read0, read4;
 	int width = 4;
 
-	tmp = map->read32(map, base+0);
+	tmp = map_read32(map, base+0);
 
-	map->write32(map, CMD_READ_ID, base+0);
+	map_write32(map, CMD_READ_ID, base+0);
 
-	read0=map->read32(map, base+0);
-	read4=map->read32(map, base+4);
+	read0=map_read32(map, base+0);
+	read4=map_read32(map, base+4);
 	if(read0 == 0x89898989){
 		printk("Looks like sharp flash\n");
 		switch(read4){
@@ -196,10 +199,10 @@
 			printk("Sort-of looks like sharp flash, 0x%08x 0x%08x\n",
 				read0,read4);
 		}
-	}else if((map->read32(map, base+0) == CMD_READ_ID)){
+	}else if((map_read32(map, base+0) == CMD_READ_ID)){
 		/* RAM, probably */
 		printk("Looks like RAM\n");
-		map->write32(map, tmp, base+0);
+		map_write32(map, tmp, base+0);
 	}else{
 		printk("Doesn't look like sharp flash, 0x%08x 0x%08x\n",
 			read0,read4);
@@ -221,10 +224,10 @@
 
 	switch(chip->state){
 	case FL_READY:
-		map->write32(map,CMD_READ_STATUS,adr);
+		map_write32(map,CMD_READ_STATUS,adr);
 		chip->state = FL_STATUS;
 	case FL_STATUS:
-		status = map->read32(map,adr);
+		status = map_read32(map,adr);
 //printk("status=%08x\n",status);
 
 		udelay(100);
@@ -252,7 +255,7 @@
 		goto retry;
 	}
 
-	map->write32(map,CMD_RESET, adr);
+	map_write32(map,CMD_RESET, adr);
 
 	chip->state = FL_READY;
 
@@ -293,7 +296,7 @@
 		if(ret<0)
 			break;
 
-		map->copy_from(map,buf,ofs,thislen);
+		map_copy_from(map,buf,ofs,thislen);
 
 		sharp_release(&sharp->chips[chipnum]);
 
@@ -354,17 +357,17 @@
 	ret = sharp_wait(map,chip);
 
 	for(try=0;try<10;try++){
-		map->write32(map,CMD_BYTE_WRITE,adr);
+		map_write32(map,CMD_BYTE_WRITE,adr);
 		/* cpu_to_le32 -> hack to fix the writel be->le conversion */
-		map->write32(map,cpu_to_le32(datum),adr);
+		map_write32(map,cpu_to_le32(datum),adr);
 
 		chip->state = FL_WRITING;
 
 		timeo = jiffies + (HZ/2);
 
-		map->write32(map,CMD_READ_STATUS,adr);
+		map_write32(map,CMD_READ_STATUS,adr);
 		for(i=0;i<100;i++){
-			status = map->read32(map,adr);
+			status = map_read32(map,adr);
 			if((status & SR_READY)==SR_READY)
 				break;
 		}
@@ -377,9 +380,9 @@
 
 		printk("sharp: error writing byte at addr=%08lx status=%08x\n",adr,status);
 
-		map->write32(map,CMD_CLEAR_STATUS,adr);
+		map_write32(map,CMD_CLEAR_STATUS,adr);
 	}
-	map->write32(map,CMD_RESET,adr);
+	map_write32(map,CMD_RESET,adr);
 	chip->state = FL_READY;
 
 	wake_up(&chip->wq);
@@ -436,14 +439,14 @@
 	int status;
 	DECLARE_WAITQUEUE(wait, current);
 
-	map->write32(map,CMD_READ_STATUS,adr);
-	status = map->read32(map,adr);
+	map_write32(map,CMD_READ_STATUS,adr);
+	status = map_read32(map,adr);
 
 	timeo = jiffies + HZ;
 
 	while(time_before(jiffies, timeo)){
-		map->write32(map,CMD_READ_STATUS,adr);
-		status = map->read32(map,adr);
+		map_write32(map,CMD_READ_STATUS,adr);
+		status = map_read32(map,adr);
 		if((status & SR_READY)==SR_READY){
 			ret = 0;
 			goto out;
@@ -485,26 +488,26 @@
 	sharp_unlock_oneblock(map,chip,adr);
 #endif
 
-	map->write32(map,CMD_BLOCK_ERASE_1,adr);
-	map->write32(map,CMD_BLOCK_ERASE_2,adr);
+	map_write32(map,CMD_BLOCK_ERASE_1,adr);
+	map_write32(map,CMD_BLOCK_ERASE_2,adr);
 
 	chip->state = FL_ERASING;
 
 	ret = sharp_do_wait_for_ready(map,chip,adr);
 	if(ret<0)return ret;
 
-	map->write32(map,CMD_READ_STATUS,adr);
-	status = map->read32(map,adr);
+	map_write32(map,CMD_READ_STATUS,adr);
+	status = map_read32(map,adr);
 
 	if(!(status&SR_ERRORS)){
-		map->write32(map,CMD_RESET,adr);
+		map_write32(map,CMD_RESET,adr);
 		chip->state = FL_READY;
 		//spin_unlock_bh(chip->mutex);
 		return 0;
 	}
 
 	printk("sharp: error erasing block at addr=%08lx status=%08x\n",adr,status);
-	map->write32(map,CMD_CLEAR_STATUS,adr);
+	map_write32(map,CMD_CLEAR_STATUS,adr);
 
 	//spin_unlock_bh(chip->mutex);
 
@@ -518,17 +521,17 @@
 	int i;
 	int status;
 
-	map->write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr);
-	map->write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr);
+	map_write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr);
+	map_write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr);
 
 	udelay(100);
 
-	status = map->read32(map,adr);
+	status = map_read32(map,adr);
 	printk("status=%08x\n",status);
 
 	for(i=0;i<1000;i++){
-		//map->write32(map,CMD_READ_STATUS,adr);
-		status = map->read32(map,adr);
+		//map_write32(map,CMD_READ_STATUS,adr);
+		status = map_read32(map,adr);
 		if((status & SR_READY)==SR_READY)
 			break;
 		udelay(100);
@@ -538,13 +541,13 @@
 	}
 
 	if(!(status&SR_ERRORS)){
-		map->write32(map,CMD_RESET,adr);
+		map_write32(map,CMD_RESET,adr);
 		chip->state = FL_READY;
 		return;
 	}
 
 	printk("sharp: error unlocking block at addr=%08lx status=%08x\n",adr,status);
-	map->write32(map,CMD_CLEAR_STATUS,adr);
+	map_write32(map,CMD_CLEAR_STATUS,adr);
 }
 #endif
 
diff -Nur linux-2.4.30/drivers/mtd/cmdlinepart.c linux-2.4.30-linux-mips.org/drivers/mtd/cmdlinepart.c
--- linux-2.4.30/drivers/mtd/cmdlinepart.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/cmdlinepart.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: cmdlinepart.c,v 1.6 2002/11/16 01:37:39 dneuer Exp $
+ * $Id: cmdlinepart.c,v 1.12 2004/03/18 08:43:14 dwmw2 Exp $
  *
  * Read flash partition table from command line
  *
@@ -10,7 +10,7 @@
  * mtdparts=<mtddef>[;<mtddef]
  * <mtddef>  := <mtd-id>:<partdef>[,<partdef>]
  * <partdef> := <size>[@offset][<name>][ro]
- * <mtd-id>  := unique id used in mapping driver/device
+ * <mtd-id>  := unique name used in mapping driver/device (mtd->name)
  * <size>    := standard linux memsize OR "-" to denote all remaining space
  * <name>    := '(' NAME ')'
  * 
@@ -28,7 +28,7 @@
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
-#include <asm/setup.h>
+#include <linux/mtd/compatmac.h>
 #include <linux/bootmem.h>
 
 /* error message prefix */
@@ -178,8 +178,7 @@
 	parts[this_part].mask_flags = mask_flags;
 	if (name)
 	{
-		strncpy(extra_mem, name, name_len);
-		extra_mem[name_len] = 0;
+		strlcpy(extra_mem, name, name_len + 1);
 	}
 	else
 	{
@@ -258,8 +257,7 @@
 		this_mtd->parts = parts;
 		this_mtd->num_parts = num_parts;
 		this_mtd->mtd_id = (char*)(this_mtd + 1);
-		strncpy(this_mtd->mtd_id, mtd_id, mtd_id_len);
-		this_mtd->mtd_id[mtd_id_len] = 0;
+		strlcpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1);
 
 		/* link into chain */
 		this_mtd->next = partitions;	    	
@@ -291,13 +289,14 @@
  * information. It returns partitions for the requested mtd device, or
  * the first one in the chain if a NULL mtd_id is passed in.
  */
-int parse_cmdline_partitions(struct mtd_info *master, 
+static int parse_cmdline_partitions(struct mtd_info *master, 
                              struct mtd_partition **pparts,
-                             const char *mtd_id)
+                             unsigned long origin)
 {
 	unsigned long offset;
 	int i;
 	struct cmdline_mtd_partition *part;
+	char *mtd_id = master->name;
 
 	if(!cmdline)
 		return -EINVAL;
@@ -349,7 +348,25 @@
 
 __setup("mtdparts=", mtdpart_setup);
 
-EXPORT_SYMBOL(parse_cmdline_partitions);
+static struct mtd_part_parser cmdline_parser = {
+	.owner = THIS_MODULE,
+	.parse_fn = parse_cmdline_partitions,
+	.name = "cmdlinepart",
+};
+
+static int __init cmdline_parser_init(void)
+{
+	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 -Nur linux-2.4.30/drivers/mtd/Config.in linux-2.4.30-linux-mips.org/drivers/mtd/Config.in
--- linux-2.4.30/drivers/mtd/Config.in	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/Config.in	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 
-# $Id: Config.in,v 1.74 2002/04/23 13:52:14 mag Exp $
+# $Id: Config.in,v 1.76 2004/04/01 10:17:40 gthomas Exp $
 
 mainmenu_option next_comment
 comment 'Memory Technology Devices (MTD)'
@@ -14,6 +14,10 @@
    dep_tristate '  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_ARM" = "y" ]; then
       dep_tristate '  ARM Firmware Suite partition parsing' CONFIG_MTD_AFS_PARTS $CONFIG_MTD_PARTITIONS
@@ -30,6 +34,7 @@
    if [ "$CONFIG_NFTL" = "y" -o "$CONFIG_NFTL" = "m" ]; then
       bool '    Write support for NFTL (BETA)' CONFIG_NFTL_RW
    fi
+   dep_tristate '  INFTL (Inverse NAND Flash Translation Layer) support' CONFIG_INFTL $CONFIG_MTD
 
    source drivers/mtd/chips/Config.in
 
diff -Nur linux-2.4.30/drivers/mtd/devices/blkmtd-25.c linux-2.4.30-linux-mips.org/drivers/mtd/devices/blkmtd-25.c
--- linux-2.4.30/drivers/mtd/devices/blkmtd-25.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/blkmtd-25.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,827 @@
+/*
+ * $Id: blkmtd-25.c,v 1.5 2003/07/16 06:48:27 spse Exp $
+ *
+ * blkmtd.c - use a block device as a fake MTD
+ *
+ * Author: Simon Evans <spse@secret.org.uk>
+ *
+ * Copyright (C) 2001,2002 Simon Evans
+ *
+ * Licence: GPL
+ *
+ * How it works:
+ *	The driver uses raw/io to read/write the device and the page
+ *	cache to cache access. Writes update the page cache with the
+ *	new data and mark it dirty and add the page into a BIO which
+ *	is then written out.
+ *
+ *	It can be loaded Read-Only to prevent erases and writes to the
+ *	medium.
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+#include <linux/bio.h>
+#include <linux/pagemap.h>
+#include <linux/list.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+
+
+#define err(format, arg...) printk(KERN_ERR "blkmtd: " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO "blkmtd: " format "\n" , ## arg)
+#define warn(format, arg...) printk(KERN_WARNING "blkmtd: " format "\n" , ## arg)
+#define crit(format, arg...) printk(KERN_CRIT "blkmtd: " format "\n" , ## arg)
+
+
+/* Default erase size in K, always make it a multiple of PAGE_SIZE */
+#define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10)	/* 128KiB */
+#define VERSION "$Revision: 1.5 $"
+
+/* Info for the block device */
+struct blkmtd_dev {
+	struct list_head list;
+	struct block_device *blkdev;
+	struct mtd_info mtd_info;
+	struct semaphore wrbuf_mutex;
+};
+
+
+/* Static info about the MTD, used in cleanup_module */
+static LIST_HEAD(blkmtd_device_list);
+
+
+static void blkmtd_sync(struct mtd_info *mtd);
+
+#define MAX_DEVICES 4
+
+/* Module parameters passed by insmod/modprobe */
+char *device[MAX_DEVICES];    /* the block device to use */
+int erasesz[MAX_DEVICES];     /* optional default erase size */
+int ro[MAX_DEVICES];          /* optional read only flag */
+int sync;
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Simon Evans <spse@secret.org.uk>");
+MODULE_DESCRIPTION("Emulate an MTD using a block device");
+MODULE_PARM(device, "1-4s");
+MODULE_PARM_DESC(device, "block device to use");
+MODULE_PARM(erasesz, "1-4i");
+MODULE_PARM_DESC(erasesz, "optional erase size to use in KiB. eg 4=4KiB.");
+MODULE_PARM(ro, "1-4i");
+MODULE_PARM_DESC(ro, "1=Read only, writes and erases cause errors");
+MODULE_PARM(sync, "i");
+MODULE_PARM_DESC(sync, "1=Synchronous writes");
+
+
+/* completion handler for BIO reads */
+static int bi_read_complete(struct bio *bio, unsigned int bytes_done, int error)
+{
+	if (bio->bi_size)
+		return 1;
+
+	complete((struct completion*)bio->bi_private);
+	return 0;
+}
+
+
+/* completion handler for BIO writes */
+static int bi_write_complete(struct bio *bio, unsigned int bytes_done, int error)
+{
+	const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags);
+	struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1;
+
+	if (bio->bi_size)
+		return 1;
+
+	if(!uptodate) 
+		err("bi_write_complete: not uptodate\n");
+
+	do {
+		struct page *page = bvec->bv_page;
+		DEBUG(3, "Cleaning up page %ld\n", page->index);
+		if (--bvec >= bio->bi_io_vec)
+			prefetchw(&bvec->bv_page->flags);
+
+		if (uptodate) {
+			SetPageUptodate(page);
+		} else {
+			ClearPageUptodate(page);
+			SetPageError(page);
+		}
+		ClearPageDirty(page);
+		unlock_page(page);
+		page_cache_release(page);
+	} while (bvec >= bio->bi_io_vec);
+	
+	complete((struct completion*)bio->bi_private);
+	return 0;
+}
+
+
+/* read one page from the block device */                 
+static int blkmtd_readpage(struct blkmtd_dev *dev, struct page *page)
+{
+	struct bio *bio;
+	struct completion event;
+	int err = -ENOMEM;
+
+	if(PageUptodate(page)) {
+		DEBUG(2, "blkmtd: readpage page %ld is already upto date\n", page->index);
+		unlock_page(page);
+		return 0;
+	}
+	
+	ClearPageUptodate(page);
+	ClearPageError(page);
+
+	bio = bio_alloc(GFP_KERNEL, 1);
+	if(bio) {
+		init_completion(&event);
+		bio->bi_bdev = dev->blkdev;
+		bio->bi_sector = page->index << (PAGE_SHIFT-9);
+		bio->bi_private = &event;
+		bio->bi_end_io = bi_read_complete;
+		if(bio_add_page(bio, page, PAGE_SIZE, 0) == PAGE_SIZE) {
+			submit_bio(READ, bio);
+			blk_run_queues();
+			wait_for_completion(&event);
+			err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO;
+			bio_put(bio);
+		}
+	}
+
+	if(err)
+		SetPageError(page);
+	else
+		SetPageUptodate(page);
+	flush_dcache_page(page);
+	unlock_page(page);
+	return err;
+}
+
+
+/* write out the current BIO and wait for it to finish */
+static int blkmtd_write_out(struct bio *bio)
+{
+	struct completion event;
+	int err;
+
+	if(!bio->bi_vcnt) {
+		bio_put(bio);
+		return 0;
+	}
+
+	init_completion(&event);
+	bio->bi_private = &event;
+	bio->bi_end_io = bi_write_complete;
+	submit_bio(WRITE, bio);
+	blk_run_queues();
+	wait_for_completion(&event);
+	DEBUG(3, "submit_bio completed, bi_vcnt = %d\n", bio->bi_vcnt);
+	err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO;
+	bio_put(bio);
+	return err; 
+}
+
+
+/**
+ * blkmtd_add_page - add a page to the current BIO
+ * @bio: bio to add to (NULL to alloc initial bio)
+ * @blkdev: block device
+ * @page: page to add
+ * @pagecnt: pages left to add
+ *
+ * Adds a page to the current bio, allocating it if necessary. If it cannot be
+ * added, the current bio is written out and a new one is allocated. Returns
+ * the new bio to add or NULL on error
+ */
+static struct bio *blkmtd_add_page(struct bio *bio, struct block_device *blkdev,
+				   struct page *page, int pagecnt)
+{
+
+ retry:
+	if(!bio) {
+		bio = bio_alloc(GFP_KERNEL, pagecnt);
+		if(!bio)
+			return NULL;
+		bio->bi_sector = page->index << (PAGE_SHIFT-9);
+		bio->bi_bdev = blkdev;
+	}
+
+	if(bio_add_page(bio, page, PAGE_SIZE, 0) != PAGE_SIZE) {
+		blkmtd_write_out(bio);
+		bio = NULL;
+		goto retry;
+	}
+	return bio;
+}
+
+
+/**
+ * write_pages - write block of data to device via the page cache
+ * @dev: device to write to
+ * @buf: data source or NULL if erase (output is set to 0xff)
+ * @to: offset into output device
+ * @len: amount to data to write
+ * @retlen: amount of data written
+ *
+ * Grab pages from the page cache and fill them with the source data.
+ * Non page aligned start and end result in a readin of the page and
+ * part of the page being modified. Pages are added to the bio and then written
+ * out.
+ */
+static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to,
+		    size_t len, size_t *retlen)
+{
+	int pagenr, offset;
+	size_t start_len = 0, end_len;
+	int pagecnt = 0;
+	int err = 0;
+	struct bio *bio = NULL;
+	size_t thislen = 0;
+
+	pagenr = to >> PAGE_SHIFT;
+	offset = to & ~PAGE_MASK;
+
+	DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %d pagenr = %d offset = %d\n",
+	      buf, (long)to, len, pagenr, offset);
+
+	/* see if we have to do a partial write at the start */
+	if(offset) {
+		start_len = ((offset + len) > PAGE_SIZE) ? PAGE_SIZE - offset : len;
+		len -= start_len;
+	}
+
+	/* calculate the length of the other two regions */
+	end_len = len & ~PAGE_MASK;
+	len -= end_len;
+
+	if(start_len)
+		pagecnt++;
+
+	if(len)
+		pagecnt += len >> PAGE_SHIFT;
+
+	if(end_len)
+		pagecnt++;
+
+	down(&dev->wrbuf_mutex);
+
+	DEBUG(3, "blkmtd: write: start_len = %d len = %d end_len = %d pagecnt = %d\n",
+	      start_len, len, end_len, pagecnt);
+
+	if(start_len) {
+		/* do partial start region */
+		struct page *page;
+
+		DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %d offset = %d\n",
+		      pagenr, start_len, offset);
+
+		BUG_ON(!buf);
+		page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev);
+		lock_page(page);
+		if(PageDirty(page)) {
+			err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d\n",
+			    to, start_len, len, end_len, pagenr);
+			BUG();
+		}
+		memcpy(page_address(page)+offset, buf, start_len);
+		SetPageDirty(page);
+		SetPageUptodate(page);
+		buf += start_len;
+		thislen = start_len;
+		bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt);
+		if(!bio) {
+			err = -ENOMEM;
+			err("bio_add_page failed\n");
+			goto write_err;
+		}
+		pagecnt--;
+		pagenr++;
+	}
+
+	/* Now do the main loop to a page aligned, n page sized output */
+	if(len) {
+		int pagesc = len >> PAGE_SHIFT;
+		DEBUG(3, "blkmtd: write: whole pages start = %d, count = %d\n",
+		      pagenr, pagesc);
+		while(pagesc) {
+			struct page *page;
+
+			/* see if page is in the page cache */
+			DEBUG(3, "blkmtd: write: grabbing page %d from page cache\n", pagenr);
+			page = grab_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr);
+			if(PageDirty(page)) {
+				BUG();
+			}
+			if(!page) {
+				warn("write: cannot grab cache page %d", pagenr);
+				err = -ENOMEM;
+				goto write_err;
+			}
+			if(!buf) {
+				memset(page_address(page), 0xff, PAGE_SIZE);
+			} else {
+				memcpy(page_address(page), buf, PAGE_SIZE);
+				buf += PAGE_SIZE;
+			}
+			bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt);
+			if(!bio) {
+				err = -ENOMEM;
+				err("bio_add_page failed\n");
+				goto write_err;
+			}
+			pagenr++;
+			pagecnt--;
+			SetPageDirty(page);
+			SetPageUptodate(page);
+			pagesc--;
+			thislen += PAGE_SIZE;
+		}
+	}
+
+	if(end_len) {
+		/* do the third region */
+		struct page *page;
+		DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %d\n",
+		      pagenr, end_len);
+		BUG_ON(!buf);
+		page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev);
+		lock_page(page);
+		if(PageDirty(page)) {
+			err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d\n",
+			    to, start_len, len, end_len, pagenr);
+			BUG();
+		}
+		memcpy(page_address(page), buf, end_len);
+		SetPageDirty(page);
+		SetPageUptodate(page);
+		DEBUG(3, "blkmtd: write: writing out partial end\n");
+		thislen += end_len;
+		bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt);
+		if(!bio) {
+			err = -ENOMEM;
+			err("bio_add_page failed\n");
+			goto write_err;
+		}
+		pagenr++;
+	}
+
+	DEBUG(3, "blkmtd: write: got %d vectors to write\n", bio->bi_vcnt);
+ write_err:
+	if(bio)
+		blkmtd_write_out(bio);
+
+	DEBUG(2, "blkmtd: write: end, retlen = %d, err = %d\n", *retlen, err);
+	up(&dev->wrbuf_mutex);
+
+	if(retlen)
+		*retlen = thislen;
+	return err;
+}
+
+
+/* erase a specified part of the device */
+static int blkmtd_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	struct blkmtd_dev *dev = mtd->priv;
+	struct mtd_erase_region_info *einfo = mtd->eraseregions;
+	int numregions = mtd->numeraseregions;
+	size_t from;
+	u_long len;
+	int err = -EIO;
+	int retlen;
+
+	instr->state = MTD_ERASING;
+	from = instr->addr;
+	len = instr->len;
+
+	/* check erase region has valid start and length */
+	DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%x len = 0x%lx\n",
+	      mtd->name+9, from, len);
+	while(numregions) {
+		DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n",
+		      einfo->offset, einfo->erasesize, einfo->numblocks);
+		if(from >= einfo->offset
+		   && from < einfo->offset + (einfo->erasesize * einfo->numblocks)) {
+			if(len == einfo->erasesize
+			   && ( (from - einfo->offset) % einfo->erasesize == 0))
+				break;
+		}
+		numregions--;
+		einfo++;
+	}
+
+	if(!numregions) {
+		/* Not a valid erase block */
+		err("erase: invalid erase request 0x%lX @ 0x%08X", len, from);
+		instr->state = MTD_ERASE_FAILED;
+		err = -EIO;
+	}
+
+	if(instr->state != MTD_ERASE_FAILED) {
+		/* do the erase */
+		DEBUG(3, "Doing erase from = %d len = %ld\n", from, len);
+		err = write_pages(dev, NULL, from, len, &retlen);
+		if(err || retlen != len) {
+			err("erase failed err = %d", err);
+			instr->state = MTD_ERASE_FAILED;
+		} else {
+			instr->state = MTD_ERASE_DONE;
+		}
+	}
+
+	DEBUG(3, "blkmtd: erase: checking callback\n");
+	if (instr->callback) {
+		(*(instr->callback))(instr);
+	}
+	DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err);
+	return err;
+}
+
+
+/* read a range of the data via the page cache */
+static int blkmtd_read(struct mtd_info *mtd, loff_t from, size_t len,
+		       size_t *retlen, u_char *buf)
+{
+	struct blkmtd_dev *dev = mtd->priv;
+	int err = 0;
+	int offset;
+	int pagenr, pages;
+	size_t thislen = 0;
+
+	DEBUG(2, "blkmtd: read: dev = `%s' from = %ld len = %d buf = %p\n",
+	      mtd->name+9, (long int)from, len, buf);
+
+	if(from > mtd->size)
+		return -EINVAL;
+	if(from + len > mtd->size)
+		len = mtd->size - from;
+
+	pagenr = from >> PAGE_SHIFT;
+	offset = from - (pagenr << PAGE_SHIFT);
+
+	pages = (offset+len+PAGE_SIZE-1) >> PAGE_SHIFT;
+	DEBUG(3, "blkmtd: read: pagenr = %d offset = %d, pages = %d\n",
+	      pagenr, offset, pages);
+
+	while(pages) {
+		struct page *page;
+		int cpylen;
+
+		DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr);
+		page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev);
+		if(IS_ERR(page)) {
+			err = -EIO;
+			goto readerr;
+		}
+
+		cpylen = (PAGE_SIZE > len) ? len : PAGE_SIZE;
+		if(offset+cpylen > PAGE_SIZE)
+			cpylen = PAGE_SIZE-offset;
+
+		memcpy(buf + thislen, page_address(page) + offset, cpylen);
+		offset = 0;
+		len -= cpylen;
+		thislen += cpylen;
+		pagenr++;
+		pages--;
+		if(!PageDirty(page))
+			page_cache_release(page);
+	}
+
+ readerr:
+	if(retlen)
+		*retlen = thislen;
+	DEBUG(2, "blkmtd: end read: retlen = %d, err = %d\n", thislen, err);
+	return err;
+}
+
+
+/* write data to the underlying device */
+static int blkmtd_write(struct mtd_info *mtd, loff_t to, size_t len,
+			size_t *retlen, const u_char *buf)
+{
+	struct blkmtd_dev *dev = mtd->priv;
+	int err;
+
+	if(!len)
+		return 0;
+
+	DEBUG(2, "blkmtd: write: dev = `%s' to = %ld len = %d buf = %p\n",
+	      mtd->name+9, (long int)to, len, buf);
+
+	if(to >= mtd->size) {
+		return -ENOSPC;
+	}
+
+	if(to + len > mtd->size) {
+		len = mtd->size - to;
+	}
+
+	err = write_pages(dev, buf, to, len, retlen);
+	if(err > 0)
+		err = 0;
+	DEBUG(2, "blkmtd: write: end, err = %d\n", err);
+	return err;
+}
+
+
+/* sync the device - wait until the write queue is empty */
+static void blkmtd_sync(struct mtd_info *mtd)
+{
+	/* Currently all writes are synchronous */
+}
+
+
+static void free_device(struct blkmtd_dev *dev)
+{
+	DEBUG(2, "blkmtd: free_device() dev = %p\n", dev);
+	if(dev) {
+		if(dev->mtd_info.eraseregions)
+			kfree(dev->mtd_info.eraseregions);
+		if(dev->mtd_info.name)
+			kfree(dev->mtd_info.name);
+
+		if(dev->blkdev) {
+			invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping);
+			close_bdev_excl(dev->blkdev, BDEV_RAW);
+		}
+		kfree(dev);
+	}
+}
+
+
+/* For a given size and initial erase size, calculate the number
+ * and size of each erase region. Goes round the loop twice,
+ * once to find out how many regions, then allocates space,
+ * then round the loop again to fill it in.
+ */
+static struct mtd_erase_region_info *calc_erase_regions(
+	size_t erase_size, size_t total_size, int *regions)
+{
+	struct mtd_erase_region_info *info = NULL;
+
+	DEBUG(2, "calc_erase_regions, es = %d size = %d regions = %d\n",
+	      erase_size, total_size, *regions);
+	/* Make any user specified erasesize be a power of 2
+	   and at least PAGE_SIZE */
+	if(erase_size) {
+		int es = erase_size;
+		erase_size = 1;
+		while(es != 1) {
+			es >>= 1;
+			erase_size <<= 1;
+		}
+		if(erase_size < PAGE_SIZE)
+			erase_size = PAGE_SIZE;
+	} else {
+		erase_size = CONFIG_MTD_BLKDEV_ERASESIZE;
+	}
+
+	*regions = 0;
+
+	do {
+		int tot_size = total_size;
+		int er_size = erase_size;
+		int count = 0, offset = 0, regcnt = 0;
+
+		while(tot_size) {
+			count = tot_size / er_size;
+			if(count) {
+				tot_size = tot_size % er_size;
+				if(info) {
+					DEBUG(2, "adding to erase info off=%d er=%d cnt=%d\n",
+					      offset, er_size, count);
+					(info+regcnt)->offset = offset;
+					(info+regcnt)->erasesize = er_size;
+					(info+regcnt)->numblocks = count;
+					(*regions)++;
+				}
+				regcnt++;
+				offset += (count * er_size);
+			}
+			while(er_size > tot_size)
+				er_size >>= 1;
+		}
+		if(info == NULL) {
+			info = kmalloc(regcnt * sizeof(struct mtd_erase_region_info), GFP_KERNEL);
+			if(!info)
+				break;
+		}
+	} while(!(*regions));
+	DEBUG(2, "calc_erase_regions done, es = %d size = %d regions = %d\n",
+	      erase_size, total_size, *regions);
+	return info;
+}
+
+
+extern dev_t __init name_to_dev_t(const char *line);
+
+static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size)
+{
+	struct block_device *bdev;
+	int mode;
+	struct blkmtd_dev *dev;
+
+	if(!devname)
+		return NULL;
+
+	/* Get a handle on the device */
+
+
+#ifdef MODULE
+	mode = (readonly) ? O_RDONLY : O_RDWR;
+	bdev = open_bdev_excl(devname, mode, BDEV_RAW, NULL);
+#else
+	mode = (readonly) ? FMODE_READ : FMODE_WRITE;
+	bdev = open_by_devnum(name_to_dev_t(devname), mode, BDEV_RAW);
+#endif
+	if(IS_ERR(bdev)) {
+		err("error: cannot open device %s", devname);
+		DEBUG(2, "blkmtd: opening bdev returned %ld\n", PTR_ERR(bdev));
+		return NULL;
+	}
+
+	DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n",
+	      MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev));
+
+	if(MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
+		err("attempting to use an MTD device as a block device");
+		blkdev_put(bdev, BDEV_RAW);
+		return NULL;
+	}
+
+	dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL);
+	if(dev == NULL) {
+		blkdev_put(bdev, BDEV_RAW);
+		return NULL;
+	}
+
+	memset(dev, 0, sizeof(struct blkmtd_dev));
+	if(!readonly) {
+		init_MUTEX(&dev->wrbuf_mutex);
+	}
+
+	dev->blkdev = bdev;
+	dev->mtd_info.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
+
+	/* Setup the MTD structure */
+	/* make the name contain the block device in */
+	dev->mtd_info.name = kmalloc(sizeof("blkmtd: ") + strlen(devname), GFP_KERNEL);
+	if(dev->mtd_info.name == NULL)
+		goto devinit_err;
+
+	sprintf(dev->mtd_info.name, "blkmtd: %s", devname);
+	dev->mtd_info.eraseregions = calc_erase_regions(erase_size, dev->mtd_info.size,
+							&dev->mtd_info.numeraseregions);
+	if(dev->mtd_info.eraseregions == NULL)
+		goto devinit_err;
+
+	dev->mtd_info.erasesize = dev->mtd_info.eraseregions->erasesize;
+	DEBUG(1, "blkmtd: init: found %d erase regions\n",
+	      dev->mtd_info.numeraseregions);
+
+	if(readonly) {
+		dev->mtd_info.type = MTD_ROM;
+		dev->mtd_info.flags = MTD_CAP_ROM;
+	} else {
+		dev->mtd_info.type = MTD_RAM;
+		dev->mtd_info.flags = MTD_CAP_RAM;
+		dev->mtd_info.erase = blkmtd_erase;
+		dev->mtd_info.write = blkmtd_write;
+		dev->mtd_info.writev = default_mtd_writev;
+		dev->mtd_info.sync = blkmtd_sync;
+	}
+	dev->mtd_info.read = blkmtd_read;
+	dev->mtd_info.readv = default_mtd_readv;
+	dev->mtd_info.priv = dev;
+	dev->mtd_info.owner = THIS_MODULE;
+
+	list_add(&dev->list, &blkmtd_device_list);
+	if (add_mtd_device(&dev->mtd_info)) {
+		/* Device didnt get added, so free the entry */
+		list_del(&dev->list);
+		goto devinit_err;
+	} else {
+		info("mtd%d: [%s] erase_size = %dKiB %s",
+		     dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "),
+		     dev->mtd_info.erasesize >> 10,
+		     readonly ? "(read-only)" : "");
+	}
+	
+	return dev;
+
+ devinit_err:
+	free_device(dev);
+	return NULL;
+}
+
+
+/* Cleanup and exit - sync the device and kill of the kernel thread */
+static void __devexit cleanup_blkmtd(void)
+{
+	struct list_head *temp1, *temp2;
+
+	/* Remove the MTD devices */
+	list_for_each_safe(temp1, temp2, &blkmtd_device_list) {
+		struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev,
+						    list);
+		blkmtd_sync(&dev->mtd_info);
+		del_mtd_device(&dev->mtd_info);
+		info("mtd%d: [%s] removed", dev->mtd_info.index,
+		     dev->mtd_info.name + strlen("blkmtd: "));
+		list_del(&dev->list);
+		free_device(dev);
+	}
+}
+
+#ifndef MODULE
+
+/* Handle kernel boot params */
+
+
+static int __init param_blkmtd_device(char *str)
+{
+	int i;
+
+	for(i = 0; i < MAX_DEVICES; i++) {
+		device[i] = str;
+		DEBUG(2, "blkmtd: device setup: %d = %s\n", i, device[i]);
+		strsep(&str, ",");
+	}
+	return 1;
+}
+
+
+static int __init param_blkmtd_erasesz(char *str)
+{
+	int i;
+	for(i = 0; i < MAX_DEVICES; i++) {
+		char *val = strsep(&str, ",");
+		if(val)
+			erasesz[i] = simple_strtoul(val, NULL, 0);
+		DEBUG(2, "blkmtd: erasesz setup: %d = %d\n", i, erasesz[i]);
+	}
+
+	return 1;
+}
+
+
+static int __init param_blkmtd_ro(char *str)
+{
+	int i;
+	for(i = 0; i < MAX_DEVICES; i++) {
+		char *val = strsep(&str, ",");
+		if(val)
+			ro[i] = simple_strtoul(val, NULL, 0);
+		DEBUG(2, "blkmtd: ro setup: %d = %d\n", i, ro[i]);
+	}
+
+	return 1;
+}
+
+
+static int __init param_blkmtd_sync(char *str)
+{
+	if(str[0] == '1')
+		sync = 1;
+	return 1;
+}
+
+__setup("blkmtd_device=", param_blkmtd_device);
+__setup("blkmtd_erasesz=", param_blkmtd_erasesz);
+__setup("blkmtd_ro=", param_blkmtd_ro);
+__setup("blkmtd_sync=", param_blkmtd_sync);
+
+#endif
+
+
+/* Startup */
+static int __init init_blkmtd(void)
+{
+	int i;
+
+	info("version " VERSION);
+	/* Check args - device[0] is the bare minimum*/
+	if(!device[0]) {
+		err("error: missing `device' name\n");
+		return -EINVAL;
+	}
+
+	for(i = 0; i < MAX_DEVICES; i++)
+		add_device(device[i], ro[i], erasesz[i] << 10);
+
+	if(list_empty(&blkmtd_device_list))
+		return -EINVAL;
+
+	return 0;
+}
+
+module_init(init_blkmtd);
+module_exit(cleanup_blkmtd);
diff -Nur linux-2.4.30/drivers/mtd/devices/blkmtd.c linux-2.4.30-linux-mips.org/drivers/mtd/devices/blkmtd.c
--- linux-2.4.30/drivers/mtd/devices/blkmtd.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/blkmtd.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: blkmtd.c,v 1.17 2003/01/24 13:00:24 dwmw2 Exp $
+ * $Id: blkmtd.c,v 1.20 2003/06/27 15:10:35 dwmw2 Exp $
  *
  * blkmtd.c - use a block device as a fake MTD
  *
@@ -143,7 +143,7 @@
 	for(cnt = 0; cnt < pages; cnt++) {
 		page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenrs[cnt]);
 		pagelst[cnt] = page;
-		if(!PageUptodate(page)) {
+		if(!Page_Uptodate(page)) {
 				iobuf->blocks[iobuf->nr_pages] = pagenrs[cnt];
 				iobuf->maplist[iobuf->nr_pages++] = page;
 		}
@@ -912,7 +912,7 @@
 	dev->mtd_info.point = 0;
 	dev->mtd_info.unpoint = 0;
 	dev->mtd_info.priv = dev;
-	dev->mtd_info.module = THIS_MODULE;
+	dev->mtd_info.owner = THIS_MODULE;
 
 	list_add(&dev->list, &blkmtd_device_list);
 	if (add_mtd_device(&dev->mtd_info)) {
diff -Nur linux-2.4.30/drivers/mtd/devices/Config.in linux-2.4.30-linux-mips.org/drivers/mtd/devices/Config.in
--- linux-2.4.30/drivers/mtd/devices/Config.in	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/Config.in	2005-05-12 18:01:29.000000000 +0200
@@ -1,6 +1,6 @@
-# drivers/mtd/maps/Config.in
+# drivers/mtd/devices/Config.in
 
-# $Id: Config.in,v 1.8 2003/01/24 23:25:14 dwmw2 Exp $
+# $Id: Config.in,v 1.11 2003/05/28 10:54:23 dwmw2 Exp $
 
 mainmenu_option next_comment
 
@@ -28,13 +28,13 @@
 dep_tristate '  MTD emulation using block device' CONFIG_MTD_BLKMTD $CONFIG_MTD
 
 comment 'Disk-On-Chip Device Drivers'
-   dep_tristate '  M-Systems Disk-On-Chip 1000' CONFIG_MTD_DOC1000 $CONFIG_MTD
    dep_tristate '  M-Systems Disk-On-Chip 2000 and Millennium' CONFIG_MTD_DOC2000 $CONFIG_MTD
    dep_tristate '  M-Systems Disk-On-Chip Millennium-only alternative driver (see help)' CONFIG_MTD_DOC2001 $CONFIG_MTD
-   if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then
+   dep_tristate '  M-Systems Disk-On-Chip Millennium Plus driver (see help)' CONFIG_MTD_DOC2001PLUS $CONFIG_MTD
+   if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then
       define_bool CONFIG_MTD_DOCPROBE y
    else
-      if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then
+      if [ "$CONFIG_MTD_DOC2001PLUS" = "m" -o "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then
 	 define_bool CONFIG_MTD_DOCPROBE m
       else
 	 define_bool CONFIG_MTD_DOCPROBE n
diff -Nur linux-2.4.30/drivers/mtd/devices/doc2000.c linux-2.4.30-linux-mips.org/drivers/mtd/devices/doc2000.c
--- linux-2.4.30/drivers/mtd/devices/doc2000.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/doc2000.c	2005-05-12 18:01:29.000000000 +0200
@@ -4,7 +4,7 @@
  * (c) 1999 Machine Vision Holdings, Inc.
  * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
  *
- * $Id: doc2000.c,v 1.50 2002/12/10 15:05:42 gleixner Exp $
+ * $Id: doc2000.c,v 1.60 2004/04/07 08:30:04 gleixner Exp $
  */
 
 #include <linux/kernel.h>
@@ -19,12 +19,14 @@
 #include <linux/sched.h>
 #include <linux/init.h>
 #include <linux/types.h>
+#include <linux/bitops.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/doc2000.h>
 
 #define DOC_SUPPORT_2000
+#define DOC_SUPPORT_2000TSOP
 #define DOC_SUPPORT_MILLENNIUM
 
 #ifdef DOC_SUPPORT_2000
@@ -33,7 +35,7 @@
 #define DoC_is_2000(doc) (0)
 #endif
 
-#ifdef DOC_SUPPORT_MILLENNIUM
+#if defined(DOC_SUPPORT_2000TSOP) || defined(DOC_SUPPORT_MILLENNIUM)
 #define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil)
 #else
 #define DoC_is_Millennium(doc) (0)
@@ -53,9 +55,12 @@
 static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
 		     size_t *retlen, const u_char *buf);
 static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
-			size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel);
+			size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
 static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
-			 size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel);
+			 size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel);
+static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *vecs, 
+			  unsigned long count, loff_t to, size_t *retlen,
+			  u_char *eccbuf, struct nand_oobinfo *oobsel);
 static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
 			size_t *retlen, u_char *buf);
 static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
@@ -92,6 +97,10 @@
 
 	/* Out-of-line routine to wait for chip response */
 	while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
+		/* issue 2 read from NOP register after reading from CDSNControl register
+	   	see Software Requirement 11.4 item 2. */
+		DoC_Delay(doc, 2);
+
 		if (time_after(jiffies, timeo)) {
 			DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
 			return -EIO;
@@ -145,6 +154,8 @@
 
 	/* Send the command */
 	WriteDOC_(command, docptr, doc->ioreg);
+	if (DoC_is_Millennium(doc))
+		WriteDOC(command, docptr, WritePipeTerm);
 
 	/* Lower the CLE line */
 	WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl);
@@ -206,6 +217,9 @@
 		}
 	}
 
+	if (DoC_is_Millennium(doc))
+		WriteDOC(ofs & 0xff, docptr, WritePipeTerm);
+
 	DoC_Delay(doc, 2);	/* Needed for some slow flash chips. mf. */
 	
 	/* FIXME: The SlowIO's for millennium could be replaced by 
@@ -344,15 +358,25 @@
 
 	/* Read the manufacturer and device id codes from the device */
 
-	/* CDSN Slow IO register see Software Requirement 11.4 item 5. */
-	dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
-	DoC_Delay(doc, 2);
-	mfr = ReadDOC_(doc->virtadr, doc->ioreg);
-
-	/* CDSN Slow IO register see Software Requirement 11.4 item 5. */
-	dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
-	DoC_Delay(doc, 2);
-	id = ReadDOC_(doc->virtadr, doc->ioreg);
+	if (DoC_is_Millennium(doc)) {
+		DoC_Delay(doc, 2);
+		dummy = ReadDOC(doc->virtadr, ReadPipeInit);
+		mfr = ReadDOC(doc->virtadr, LastDataRead);
+
+		DoC_Delay(doc, 2);
+		dummy = ReadDOC(doc->virtadr, ReadPipeInit);
+		id = ReadDOC(doc->virtadr, LastDataRead);
+	} else {
+		/* CDSN Slow IO register see Software Req 11.4 item 5. */
+		dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
+		DoC_Delay(doc, 2);
+		mfr = ReadDOC_(doc->virtadr, doc->ioreg);
+
+		/* CDSN Slow IO register see Software Req 11.4 item 5. */
+		dummy = ReadDOC(doc->virtadr, CDSNSlowIO);
+		DoC_Delay(doc, 2);
+		id = ReadDOC_(doc->virtadr, doc->ioreg);
+	}
 
 	/* No response - return failure */
 	if (mfr == 0xff || mfr == 0)
@@ -386,11 +410,10 @@
 			if (!doc->mfr) {
 				doc->mfr = mfr;
 				doc->id = id;
-				doc->chipshift =
-				    nand_flash_ids[i].chipshift;
-				doc->page256 = nand_flash_ids[i].page256;
-				doc->pageadrlen =
-				    nand_flash_ids[i].chipshift > 25 ? 3 : 2;
+				doc->chipshift = 
+					ffs((nand_flash_ids[i].chipsize << 20)) - 1;
+				doc->page256 = (nand_flash_ids[i].pagesize == 256) ? 1 : 0;
+				doc->pageadrlen = doc->chipshift > 25 ? 3 : 2;
 				doc->erasesize =
 				    nand_flash_ids[i].erasesize;
 				return 1;
@@ -410,20 +433,16 @@
 
 /* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
 
-static void DoC_ScanChips(struct DiskOnChip *this)
+static void DoC_ScanChips(struct DiskOnChip *this, int maxchips)
 {
 	int floor, chip;
 	int numchips[MAX_FLOORS];
-	int maxchips = MAX_CHIPS;
 	int ret = 1;
 
 	this->numchips = 0;
 	this->mfr = 0;
 	this->id = 0;
 
-	if (DoC_is_Millennium(this))
-		maxchips = MAX_CHIPS_MIL;
-
 	/* For each floor, find the number of valid chips it contains */
 	for (floor = 0; floor < MAX_FLOORS; floor++) {
 		ret = 1;
@@ -515,6 +534,7 @@
 {
 	struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
 	struct DiskOnChip *old = NULL;
+	int maxchips;
 
 	/* We must avoid being called twice for the same device. */
 
@@ -538,14 +558,28 @@
 
 
 	switch (this->ChipID) {
+	case DOC_ChipID_Doc2kTSOP:
+		mtd->name = "DiskOnChip 2000 TSOP";
+		this->ioreg = DoC_Mil_CDSN_IO;
+		/* Pretend it's a Millennium */
+		this->ChipID = DOC_ChipID_DocMil;
+		maxchips = MAX_CHIPS;
+		break;
 	case DOC_ChipID_Doc2k:
 		mtd->name = "DiskOnChip 2000";
 		this->ioreg = DoC_2k_CDSN_IO;
+		maxchips = MAX_CHIPS;
 		break;
 	case DOC_ChipID_DocMil:
 		mtd->name = "DiskOnChip Millennium";
 		this->ioreg = DoC_Mil_CDSN_IO;
+		maxchips = MAX_CHIPS_MIL;
 		break;
+	default:
+		printk("Unknown ChipID 0x%02x\n", this->ChipID);
+		kfree(mtd);
+		iounmap((void *) this->virtadr);
+		return;
 	}
 
 	printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name,
@@ -553,11 +587,12 @@
 
 	mtd->type = MTD_NANDFLASH;
 	mtd->flags = MTD_CAP_NANDFLASH;
+	mtd->ecctype = MTD_ECC_RS_DiskOnChip;
 	mtd->size = 0;
 	mtd->erasesize = 0;
 	mtd->oobblock = 512;
 	mtd->oobsize = 16;
-	mtd->module = THIS_MODULE;
+	mtd->owner = THIS_MODULE;
 	mtd->erase = doc_erase;
 	mtd->point = NULL;
 	mtd->unpoint = NULL;
@@ -565,6 +600,7 @@
 	mtd->write = doc_write;
 	mtd->read_ecc = doc_read_ecc;
 	mtd->write_ecc = doc_write_ecc;
+	mtd->writev_ecc = doc_writev_ecc;
 	mtd->read_oob = doc_read_oob;
 	mtd->write_oob = doc_write_oob;
 	mtd->sync = NULL;
@@ -577,7 +613,7 @@
 	init_MUTEX(&this->lock);
 
 	/* Ident all the chips present. */
-	DoC_ScanChips(this);
+	DoC_ScanChips(this, maxchips);
 
 	if (!this->totlen) {
 		kfree(mtd);
@@ -600,7 +636,7 @@
 }
 
 static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len,
-			size_t * retlen, u_char * buf, u_char * eccbuf, int oobsel)
+			size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
 {
 	struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
 	unsigned long docptr;
@@ -608,6 +644,7 @@
 	unsigned char syndrome[6];
 	volatile char dummy;
 	int i, len256 = 0, ret=0;
+	size_t left = len;
 
 	docptr = this->virtadr;
 
@@ -617,122 +654,131 @@
 
 	down(&this->lock);
 
-	/* Don't allow a single read to cross a 512-byte block boundary */
-	if (from + len > ((from | 0x1ff) + 1))
-		len = ((from | 0x1ff) + 1) - from;
-
-	/* The ECC will not be calculated correctly if less than 512 is read */
-	if (len != 0x200 && eccbuf)
-		printk(KERN_WARNING
-		       "ECC needs a full sector read (adr: %lx size %lx)\n",
-		       (long) from, (long) len);
+	*retlen = 0;
+	while (left) {
+		len = left;
+
+		/* Don't allow a single read to cross a 512-byte block boundary */
+		if (from + len > ((from | 0x1ff) + 1))
+			len = ((from | 0x1ff) + 1) - from;
 
-	/* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */
+		/* The ECC will not be calculated correctly if less than 512 is read */
+		if (len != 0x200 && eccbuf)
+			printk(KERN_WARNING
+			       "ECC needs a full sector read (adr: %lx size %lx)\n",
+			       (long) from, (long) len);
 
+		/* printk("DoC_Read (adr: %lx size %lx)\n", (long) from, (long) len); */
 
-	/* Find the chip which is to be used and select it */
-	mychip = &this->chips[from >> (this->chipshift)];
 
-	if (this->curfloor != mychip->floor) {
-		DoC_SelectFloor(this, mychip->floor);
-		DoC_SelectChip(this, mychip->chip);
-	} else if (this->curchip != mychip->chip) {
-		DoC_SelectChip(this, mychip->chip);
-	}
+		/* Find the chip which is to be used and select it */
+		mychip = &this->chips[from >> (this->chipshift)];
 
-	this->curfloor = mychip->floor;
-	this->curchip = mychip->chip;
+		if (this->curfloor != mychip->floor) {
+			DoC_SelectFloor(this, mychip->floor);
+			DoC_SelectChip(this, mychip->chip);
+		} else if (this->curchip != mychip->chip) {
+			DoC_SelectChip(this, mychip->chip);
+		}
 
-	DoC_Command(this,
-		    (!this->page256
-		     && (from & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
-		    CDSN_CTRL_WP);
-	DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP,
-		    CDSN_CTRL_ECC_IO);
-
-	if (eccbuf) {
-		/* Prime the ECC engine */
-		WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
-		WriteDOC(DOC_ECC_EN, docptr, ECCConf);
-	} else {
-		/* disable the ECC engine */
-		WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
-		WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
-	}
+		this->curfloor = mychip->floor;
+		this->curchip = mychip->chip;
 
-	/* treat crossing 256-byte sector for 2M x 8bits devices */
-	if (this->page256 && from + len > (from | 0xff) + 1) {
-		len256 = (from | 0xff) + 1 - from;
-		DoC_ReadBuf(this, buf, len256);
+		DoC_Command(this,
+			    (!this->page256
+			     && (from & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
+			    CDSN_CTRL_WP);
+		DoC_Address(this, ADDR_COLUMN_PAGE, from, CDSN_CTRL_WP,
+			    CDSN_CTRL_ECC_IO);
 
-		DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP);
-		DoC_Address(this, ADDR_COLUMN_PAGE, from + len256,
-			    CDSN_CTRL_WP, CDSN_CTRL_ECC_IO);
-	}
+		if (eccbuf) {
+			/* Prime the ECC engine */
+			WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+			WriteDOC(DOC_ECC_EN, docptr, ECCConf);
+		} else {
+			/* disable the ECC engine */
+			WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+			WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+		}
 
-	DoC_ReadBuf(this, &buf[len256], len - len256);
+		/* treat crossing 256-byte sector for 2M x 8bits devices */
+		if (this->page256 && from + len > (from | 0xff) + 1) {
+			len256 = (from | 0xff) + 1 - from;
+			DoC_ReadBuf(this, buf, len256);
+
+			DoC_Command(this, NAND_CMD_READ0, CDSN_CTRL_WP);
+			DoC_Address(this, ADDR_COLUMN_PAGE, from + len256,
+				    CDSN_CTRL_WP, CDSN_CTRL_ECC_IO);
+		}
 
-	/* Let the caller know we completed it */
-	*retlen = len;
+		DoC_ReadBuf(this, &buf[len256], len - len256);
 
-	if (eccbuf) {
-		/* Read the ECC data through the DiskOnChip ECC logic */
-		/* Note: this will work even with 2M x 8bit devices as   */
-		/*       they have 8 bytes of OOB per 256 page. mf.      */
-		DoC_ReadBuf(this, eccbuf, 6);
+		/* Let the caller know we completed it */
+		*retlen += len;
 
-		/* Flush the pipeline */
-		if (DoC_is_Millennium(this)) {
-			dummy = ReadDOC(docptr, ECCConf);
-			dummy = ReadDOC(docptr, ECCConf);
-			i = ReadDOC(docptr, ECCConf);
-		} else {
-			dummy = ReadDOC(docptr, 2k_ECCStatus);
-			dummy = ReadDOC(docptr, 2k_ECCStatus);
-			i = ReadDOC(docptr, 2k_ECCStatus);
-		}
+		if (eccbuf) {
+			/* Read the ECC data through the DiskOnChip ECC logic */
+			/* Note: this will work even with 2M x 8bit devices as   */
+			/*       they have 8 bytes of OOB per 256 page. mf.      */
+			DoC_ReadBuf(this, eccbuf, 6);
+
+			/* Flush the pipeline */
+			if (DoC_is_Millennium(this)) {
+				dummy = ReadDOC(docptr, ECCConf);
+				dummy = ReadDOC(docptr, ECCConf);
+				i = ReadDOC(docptr, ECCConf);
+			} else {
+				dummy = ReadDOC(docptr, 2k_ECCStatus);
+				dummy = ReadDOC(docptr, 2k_ECCStatus);
+				i = ReadDOC(docptr, 2k_ECCStatus);
+			}
 
-		/* Check the ECC Status */
-		if (i & 0x80) {
-			int nb_errors;
-			/* There was an ECC error */
+			/* Check the ECC Status */
+			if (i & 0x80) {
+				int nb_errors;
+				/* There was an ECC error */
 #ifdef ECC_DEBUG
-			printk(KERN_ERR "DiskOnChip ECC Error: Read at %lx\n", (long)from);
+				printk(KERN_ERR "DiskOnChip ECC Error: Read at %lx\n", (long)from);
 #endif
-			/* Read the ECC syndrom through the DiskOnChip ECC logic.
-			   These syndrome will be all ZERO when there is no error */
-			for (i = 0; i < 6; i++) {
-				syndrome[i] =
-				    ReadDOC(docptr, ECCSyndrome0 + i);
-			}
-                        nb_errors = doc_decode_ecc(buf, syndrome);
+				/* Read the ECC syndrom through the DiskOnChip ECC logic.
+				   These syndrome will be all ZERO when there is no error */
+				for (i = 0; i < 6; i++) {
+					syndrome[i] =
+					    ReadDOC(docptr, ECCSyndrome0 + i);
+				}
+	                        nb_errors = doc_decode_ecc(buf, syndrome);
 
 #ifdef ECC_DEBUG
-			printk(KERN_ERR "Errors corrected: %x\n", nb_errors);
+				printk(KERN_ERR "Errors corrected: %x\n", nb_errors);
 #endif
-                        if (nb_errors < 0) {
-				/* We return error, but have actually done the read. Not that
-				   this can be told to user-space, via sys_read(), but at least
-				   MTD-aware stuff can know about it by checking *retlen */
-				ret = -EIO;
-                        }
-		}
+	                        if (nb_errors < 0) {
+					/* We return error, but have actually done the read. Not that
+					   this can be told to user-space, via sys_read(), but at least
+					   MTD-aware stuff can know about it by checking *retlen */
+					ret = -EIO;
+	                        }
+			}
 
 #ifdef PSYCHO_DEBUG
-		printk(KERN_DEBUG "ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
-			     (long)from, eccbuf[0], eccbuf[1], eccbuf[2],
-			     eccbuf[3], eccbuf[4], eccbuf[5]);
+			printk(KERN_DEBUG "ECC DATA at %lxB: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+				     (long)from, eccbuf[0], eccbuf[1], eccbuf[2],
+				     eccbuf[3], eccbuf[4], eccbuf[5]);
 #endif
 		
-		/* disable the ECC engine */
-		WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
-	}
+			/* disable the ECC engine */
+			WriteDOC(DOC_ECC_DIS, docptr , ECCConf);
+		}
 
-	/* according to 11.4.1, we need to wait for the busy line 
-         * drop if we read to the end of the page.  */
-	if(0 == ((from + *retlen) & 0x1ff))
-	{
-	    DoC_WaitReady(this);
+		/* according to 11.4.1, we need to wait for the busy line 
+	         * drop if we read to the end of the page.  */
+		if(0 == ((from + len) & 0x1ff))
+		{
+		    DoC_WaitReady(this);
+		}
+
+		from += len;
+		left -= len;
+		buf += len;
 	}
 
 	up(&this->lock);
@@ -749,7 +795,7 @@
 
 static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len,
 			 size_t * retlen, const u_char * buf,
-			 u_char * eccbuf, int oobsel)
+			 u_char * eccbuf, struct nand_oobinfo *oobsel)
 {
 	struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
 	int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */
@@ -757,6 +803,8 @@
 	volatile char dummy;
 	int len256 = 0;
 	struct Nand *mychip;
+	size_t left = len;
+	int status;
 
 	docptr = this->virtadr;
 
@@ -766,65 +814,133 @@
 
 	down(&this->lock);
 
-	/* Don't allow a single write to cross a 512-byte block boundary */
-	if (to + len > ((to | 0x1ff) + 1))
-		len = ((to | 0x1ff) + 1) - to;
-
-	/* The ECC will not be calculated correctly if less than 512 is written */
-	if (len != 0x200 && eccbuf)
-		printk(KERN_WARNING
-		       "ECC needs a full sector write (adr: %lx size %lx)\n",
-		       (long) to, (long) len);
+	*retlen = 0;
+	while (left) {
+		len = left;
+
+		/* Don't allow a single write to cross a 512-byte block boundary */
+		if (to + len > ((to | 0x1ff) + 1))
+			len = ((to | 0x1ff) + 1) - to;
+
+		/* The ECC will not be calculated correctly if less than 512 is written */
+/* DBB-
+		if (len != 0x200 && eccbuf)
+			printk(KERN_WARNING
+			       "ECC needs a full sector write (adr: %lx size %lx)\n",
+			       (long) to, (long) len);
+   -DBB */
 
-	/* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */
+		/* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */
 
-	/* Find the chip which is to be used and select it */
-	mychip = &this->chips[to >> (this->chipshift)];
+		/* Find the chip which is to be used and select it */
+		mychip = &this->chips[to >> (this->chipshift)];
 
-	if (this->curfloor != mychip->floor) {
-		DoC_SelectFloor(this, mychip->floor);
-		DoC_SelectChip(this, mychip->chip);
-	} else if (this->curchip != mychip->chip) {
-		DoC_SelectChip(this, mychip->chip);
-	}
+		if (this->curfloor != mychip->floor) {
+			DoC_SelectFloor(this, mychip->floor);
+			DoC_SelectChip(this, mychip->chip);
+		} else if (this->curchip != mychip->chip) {
+			DoC_SelectChip(this, mychip->chip);
+		}
 
-	this->curfloor = mychip->floor;
-	this->curchip = mychip->chip;
+		this->curfloor = mychip->floor;
+		this->curchip = mychip->chip;
 
-	/* Set device to main plane of flash */
-	DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP);
-	DoC_Command(this,
-		    (!this->page256
-		     && (to & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
-		    CDSN_CTRL_WP);
+		/* Set device to main plane of flash */
+		DoC_Command(this, NAND_CMD_RESET, CDSN_CTRL_WP);
+		DoC_Command(this,
+			    (!this->page256
+			     && (to & 0x100)) ? NAND_CMD_READ1 : NAND_CMD_READ0,
+			    CDSN_CTRL_WP);
 
-	DoC_Command(this, NAND_CMD_SEQIN, 0);
-	DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO);
+		DoC_Command(this, NAND_CMD_SEQIN, 0);
+		DoC_Address(this, ADDR_COLUMN_PAGE, to, 0, CDSN_CTRL_ECC_IO);
 
-	if (eccbuf) {
-		/* Prime the ECC engine */
-		WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
-		WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
-	} else {
-		/* disable the ECC engine */
-		WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
-		WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
-	}
+		if (eccbuf) {
+			/* Prime the ECC engine */
+			WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+			WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
+		} else {
+			/* disable the ECC engine */
+			WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+			WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+		}
 
-	/* treat crossing 256-byte sector for 2M x 8bits devices */
-	if (this->page256 && to + len > (to | 0xff) + 1) {
-		len256 = (to | 0xff) + 1 - to;
-		DoC_WriteBuf(this, buf, len256);
+		/* treat crossing 256-byte sector for 2M x 8bits devices */
+		if (this->page256 && to + len > (to | 0xff) + 1) {
+			len256 = (to | 0xff) + 1 - to;
+			DoC_WriteBuf(this, buf, len256);
+
+			DoC_Command(this, NAND_CMD_PAGEPROG, 0);
+
+			DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
+			/* There's an implicit DoC_WaitReady() in DoC_Command */
+
+			dummy = ReadDOC(docptr, CDSNSlowIO);
+			DoC_Delay(this, 2);
+
+			if (ReadDOC_(docptr, this->ioreg) & 1) {
+				printk(KERN_ERR "Error programming flash\n");
+				/* Error in programming */
+				*retlen = 0;
+				up(&this->lock);
+				return -EIO;
+			}
+
+			DoC_Command(this, NAND_CMD_SEQIN, 0);
+			DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0,
+				    CDSN_CTRL_ECC_IO);
+		}
+
+		DoC_WriteBuf(this, &buf[len256], len - len256);
+
+		if (eccbuf) {
+			WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr,
+				 CDSNControl);
+
+			if (DoC_is_Millennium(this)) {
+				WriteDOC(0, docptr, NOP);
+				WriteDOC(0, docptr, NOP);
+				WriteDOC(0, docptr, NOP);
+			} else {
+				WriteDOC_(0, docptr, this->ioreg);
+				WriteDOC_(0, docptr, this->ioreg);
+				WriteDOC_(0, docptr, this->ioreg);
+			}
+
+			WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_FLASH_IO | CDSN_CTRL_CE, docptr,
+				 CDSNControl);
+
+			/* Read the ECC data through the DiskOnChip ECC logic */
+			for (di = 0; di < 6; di++) {
+				eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di);
+			}
+
+			/* Reset the ECC engine */
+			WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+
+#ifdef PSYCHO_DEBUG
+			printk
+			    ("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+			     (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
+			     eccbuf[4], eccbuf[5]);
+#endif
+		}
 
 		DoC_Command(this, NAND_CMD_PAGEPROG, 0);
 
 		DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
 		/* There's an implicit DoC_WaitReady() in DoC_Command */
 
-		dummy = ReadDOC(docptr, CDSNSlowIO);
-		DoC_Delay(this, 2);
+		if (DoC_is_Millennium(this)) {
+			ReadDOC(docptr, ReadPipeInit);
+			status = ReadDOC(docptr, LastDataRead);
+		} else {
+			dummy = ReadDOC(docptr, CDSNSlowIO);
+			DoC_Delay(this, 2);
+			status = ReadDOC_(docptr, this->ioreg);
+		}
 
-		if (ReadDOC_(docptr, this->ioreg) & 1) {
+		if (status & 1) {
 			printk(KERN_ERR "Error programming flash\n");
 			/* Error in programming */
 			*retlen = 0;
@@ -832,82 +948,97 @@
 			return -EIO;
 		}
 
-		DoC_Command(this, NAND_CMD_SEQIN, 0);
-		DoC_Address(this, ADDR_COLUMN_PAGE, to + len256, 0,
-			    CDSN_CTRL_ECC_IO);
+		/* Let the caller know we completed it */
+		*retlen += len;
+		
+		if (eccbuf) {
+			unsigned char x[8];
+			size_t dummy;
+			int ret;
+
+			/* Write the ECC data to flash */
+			for (di=0; di<6; di++)
+				x[di] = eccbuf[di];
+		
+			x[6]=0x55;
+			x[7]=0x55;
+		
+			ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x);
+			if (ret) {
+				up(&this->lock);
+				return ret;
+			}
+		}
+
+		to += len;
+		left -= len;
+		buf += len;
 	}
 
-	DoC_WriteBuf(this, &buf[len256], len - len256);
+	up(&this->lock);
+	return 0;
+}
 
-	if (eccbuf) {
-		WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_CE, docptr,
-			 CDSNControl);
+static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *vecs, 
+			  unsigned long count, loff_t to, size_t *retlen,
+			  u_char *eccbuf, struct nand_oobinfo *oobsel)
+{
+	static char static_buf[512];
+	static DECLARE_MUTEX(writev_buf_sem);
 
-		if (DoC_is_Millennium(this)) {
-			WriteDOC(0, docptr, NOP);
-			WriteDOC(0, docptr, NOP);
-			WriteDOC(0, docptr, NOP);
-		} else {
-			WriteDOC_(0, docptr, this->ioreg);
-			WriteDOC_(0, docptr, this->ioreg);
-			WriteDOC_(0, docptr, this->ioreg);
-		}
+	size_t totretlen = 0;
+	size_t thisvecofs = 0;
+	int ret= 0;
 
-		/* Read the ECC data through the DiskOnChip ECC logic */
-		for (di = 0; di < 6; di++) {
-			eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di);
-		}
+	down(&writev_buf_sem);
 
-		/* Reset the ECC engine */
-		WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+	while(count) {
+		size_t thislen, thisretlen;
+		unsigned char *buf;
 
-#ifdef PSYCHO_DEBUG
-		printk
-		    ("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
-		     (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
-		     eccbuf[4], eccbuf[5]);
-#endif
-	}
+		buf = vecs->iov_base + thisvecofs;
+		thislen = vecs->iov_len - thisvecofs;
 
-	DoC_Command(this, NAND_CMD_PAGEPROG, 0);
 
-	DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
-	/* There's an implicit DoC_WaitReady() in DoC_Command */
+		if (thislen >= 512) {
+			thislen = thislen & ~(512-1);
+			thisvecofs += thislen;
+		} else {
+			/* Not enough to fill a page. Copy into buf */
+			memcpy(static_buf, buf, thislen);
+			buf = &static_buf[thislen];
+
+			while(count && thislen < 512) {
+				vecs++;
+				count--;
+				thisvecofs = min((512-thislen), vecs->iov_len);
+				memcpy(buf, vecs->iov_base, thisvecofs);
+				thislen += thisvecofs;
+				buf += thisvecofs;
+			}
+			buf = static_buf;
+		}
+		if (count && thisvecofs == vecs->iov_len) {
+			thisvecofs = 0;
+			vecs++;
+			count--;
+		}
+		ret = doc_write_ecc(mtd, to, thislen, &thisretlen, buf, eccbuf, oobsel);
 
-	dummy = ReadDOC(docptr, CDSNSlowIO);
-	DoC_Delay(this, 2);
+		totretlen += thisretlen;
 
-	if (ReadDOC_(docptr, this->ioreg) & 1) {
-		printk(KERN_ERR "Error programming flash\n");
-		/* Error in programming */
-		*retlen = 0;
-		up(&this->lock);
-		return -EIO;
-	}
+		if (ret || thisretlen != thislen)
+			break;
 
-	/* Let the caller know we completed it */
-	*retlen = len;
-		
-	if (eccbuf) {
-		unsigned char x[8];
-		size_t dummy;
-		int ret;
-
-		/* Write the ECC data to flash */
-		for (di=0; di<6; di++)
-			x[di] = eccbuf[di];
-		
-		x[6]=0x55;
-		x[7]=0x55;
-		
-		ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x);
-		up(&this->lock);
-		return ret;
-	}
-	up(&this->lock);
-	return 0;
+		to += thislen;
+	}		
+
+	up(&writev_buf_sem);
+	*retlen = totretlen;
+	return ret;
 }
 
+
 static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
 			size_t * retlen, u_char * buf)
 {
@@ -977,6 +1108,7 @@
 	unsigned long docptr = this->virtadr;
 	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
 	volatile int dummy;
+	int status;
 
 	//      printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len,
 	//   buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]);
@@ -1025,10 +1157,16 @@
 		DoC_Command(this, NAND_CMD_STATUS, 0);
 		/* DoC_WaitReady() is implicit in DoC_Command */
 
-		dummy = ReadDOC(docptr, CDSNSlowIO);
-		DoC_Delay(this, 2);
+		if (DoC_is_Millennium(this)) {
+			ReadDOC(docptr, ReadPipeInit);
+			status = ReadDOC(docptr, LastDataRead);
+		} else {
+			dummy = ReadDOC(docptr, CDSNSlowIO);
+			DoC_Delay(this, 2);
+			status = ReadDOC_(docptr, this->ioreg);
+		}
 
-		if (ReadDOC_(docptr, this->ioreg) & 1) {
+		if (status & 1) {
 			printk(KERN_ERR "Error programming oob data\n");
 			/* There was an error */
 			*retlen = 0;
@@ -1044,10 +1182,16 @@
 	DoC_Command(this, NAND_CMD_STATUS, 0);
 	/* DoC_WaitReady() is implicit in DoC_Command */
 
-	dummy = ReadDOC(docptr, CDSNSlowIO);
-	DoC_Delay(this, 2);
+	if (DoC_is_Millennium(this)) {
+		ReadDOC(docptr, ReadPipeInit);
+		status = ReadDOC(docptr, LastDataRead);
+	} else {
+		dummy = ReadDOC(docptr, CDSNSlowIO);
+		DoC_Delay(this, 2);
+		status = ReadDOC_(docptr, this->ioreg);
+	}
 
-	if (ReadDOC_(docptr, this->ioreg) & 1) {
+	if (status & 1) {
 		printk(KERN_ERR "Error programming oob data\n");
 		/* There was an error */
 		*retlen = 0;
@@ -1080,6 +1224,7 @@
 	volatile int dummy;
 	unsigned long docptr;
 	struct Nand *mychip;
+	int status;
 
  	down(&this->lock);
 
@@ -1111,10 +1256,16 @@
 
 		DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
 
-		dummy = ReadDOC(docptr, CDSNSlowIO);
-		DoC_Delay(this, 2);
-		
-		if (ReadDOC_(docptr, this->ioreg) & 1) {
+		if (DoC_is_Millennium(this)) {
+			ReadDOC(docptr, ReadPipeInit);
+			status = ReadDOC(docptr, LastDataRead);
+		} else {
+			dummy = ReadDOC(docptr, CDSNSlowIO);
+			DoC_Delay(this, 2);
+			status = ReadDOC_(docptr, this->ioreg);
+		}
+
+		if (status & 1) {
 			printk(KERN_ERR "Error erasing at 0x%x\n", ofs);
 			/* There was an error */
 			instr->state = MTD_ERASE_FAILED;
diff -Nur linux-2.4.30/drivers/mtd/devices/doc2001.c linux-2.4.30-linux-mips.org/drivers/mtd/devices/doc2001.c
--- linux-2.4.30/drivers/mtd/devices/doc2001.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/doc2001.c	2005-05-12 18:01:29.000000000 +0200
@@ -4,7 +4,7 @@
  * (c) 1999 Machine Vision Holdings, Inc.
  * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
  *
- * $Id: doc2001.c,v 1.38 2002/12/10 15:05:42 gleixner Exp $
+ * $Id: doc2001.c,v 1.42 2004/04/04 12:36:45 gleixner Exp $
  */
 
 #include <linux/kernel.h>
@@ -19,6 +19,7 @@
 #include <linux/sched.h>
 #include <linux/init.h>
 #include <linux/types.h>
+#include <linux/bitops.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
@@ -226,7 +227,7 @@
 			       mfr, id, nand_manuf_ids[j].name, nand_flash_ids[i].name);
 			doc->mfr = mfr;
 			doc->id = id;
-			doc->chipshift = nand_flash_ids[i].chipshift;
+			doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1;
 			break;
 		}
 	}
@@ -359,14 +360,15 @@
 
 	mtd->type = MTD_NANDFLASH;
 	mtd->flags = MTD_CAP_NANDFLASH;
+	mtd->ecctype = MTD_ECC_RS_DiskOnChip;
 	mtd->size = 0;
 
-	/* FIXME: erase size is not always 8kB */
+	/* FIXME: erase size is not always 8KiB */
 	mtd->erasesize = 0x2000;
 
 	mtd->oobblock = 512;
 	mtd->oobsize = 16;
-	mtd->module = THIS_MODULE;
+	mtd->owner = THIS_MODULE;
 	mtd->erase = doc_erase;
 	mtd->point = NULL;
 	mtd->unpoint = NULL;
diff -Nur linux-2.4.30/drivers/mtd/devices/doc2001plus.c linux-2.4.30-linux-mips.org/drivers/mtd/devices/doc2001plus.c
--- linux-2.4.30/drivers/mtd/devices/doc2001plus.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/doc2001plus.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,1155 @@
+/*
+ * Linux driver for Disk-On-Chip Millennium Plus
+ *
+ * (c) 2002-2003 Greg Ungerer <gerg@snapgear.com>
+ * (c) 2002-2003 SnapGear Inc
+ * (c) 1999 Machine Vision Holdings, Inc.
+ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org>
+ *
+ * $Id: doc2001plus.c,v 1.8 2004/04/04 12:36:45 gleixner Exp $
+ *
+ * Released under GPL
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/doc2000.h>
+
+/* #define ECC_DEBUG */
+
+/* I have no idea why some DoC chips can not use memcop_form|to_io().
+ * This may be due to the different revisions of the ASIC controller built-in or
+ * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment
+ * this:*/
+#undef USE_MEMCPY
+
+static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
+		size_t *retlen, u_char *buf);
+static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
+		size_t *retlen, const u_char *buf);
+static int doc_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 doc_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 doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+		size_t *retlen, u_char *buf);
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+		size_t *retlen, const u_char *buf);
+static int doc_erase (struct mtd_info *mtd, struct erase_info *instr);
+
+static struct mtd_info *docmilpluslist = NULL;
+
+
+/* Perform the required delay cycles by writing to the NOP register */
+static void DoC_Delay(unsigned long docptr, int cycles)
+{
+	int i;
+
+	for (i = 0; (i < cycles); i++)
+		WriteDOC(0, docptr, Mplus_NOP);
+}
+
+#define	CDSN_CTRL_FR_B_MASK	(CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1)
+
+/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
+static int _DoC_WaitReady(unsigned long docptr)
+{
+	unsigned int c = 0xffff;
+
+	DEBUG(MTD_DEBUG_LEVEL3,
+	      "_DoC_WaitReady called for out-of-line wait\n");
+
+	/* Out-of-line routine to wait for chip response */
+	while (((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) && --c)
+		;
+
+	if (c == 0)
+		DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n");
+
+	return (c == 0);
+}
+
+static inline int DoC_WaitReady(unsigned long docptr)
+{
+	/* This is inline, to optimise the common case, where it's ready instantly */
+	int ret = 0;
+
+	/* read form NOP register should be issued prior to the read from CDSNControl
+	   see Software Requirement 11.4 item 2. */
+	DoC_Delay(docptr, 4);
+
+	if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK)
+		/* Call the out-of-line routine to wait */
+		ret = _DoC_WaitReady(docptr);
+
+	return ret;
+}
+
+/* For some reason the Millennium Plus seems to occassionally put itself
+ * into reset mode. For me this happens randomly, with no pattern that I
+ * can detect. M-systems suggest always check this on any block level
+ * operation and setting to normal mode if in reset mode.
+ */
+static inline void DoC_CheckASIC(unsigned long docptr)
+{
+	/* Make sure the DoC is in normal mode */
+	if ((ReadDOC(docptr, Mplus_DOCControl) & DOC_MODE_NORMAL) == 0) {
+		WriteDOC((DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_DOCControl);
+		WriteDOC(~(DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_CtrlConfirm);
+	}
+}
+
+/* DoC_Command: Send a flash command to the flash chip through the Flash
+ * command register. Need 2 Write Pipeline Terminates to complete send.
+ */
+static inline void DoC_Command(unsigned long docptr, unsigned char command,
+			       unsigned char xtraflags)
+{
+	WriteDOC(command, docptr, Mplus_FlashCmd);
+	WriteDOC(command, docptr, Mplus_WritePipeTerm);
+	WriteDOC(command, docptr, Mplus_WritePipeTerm);
+}
+
+/* DoC_Address: Set the current address for the flash chip through the Flash
+ * Address register. Need 2 Write Pipeline Terminates to complete send.
+ */
+static inline void DoC_Address(struct DiskOnChip *doc, int numbytes,
+			       unsigned long ofs, unsigned char xtraflags1,
+			       unsigned char xtraflags2)
+{
+	unsigned long docptr = doc->virtadr;
+
+	/* Allow for possible Mill Plus internal flash interleaving */
+	ofs >>= doc->interleave;
+
+	switch (numbytes) {
+	case 1:
+		/* Send single byte, bits 0-7. */
+		WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress);
+		break;
+	case 2:
+		/* Send bits 9-16 followed by 17-23 */
+		WriteDOC((ofs >> 9)  & 0xff, docptr, Mplus_FlashAddress);
+		WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress);
+		break;
+	case 3:
+		/* Send 0-7, 9-16, then 17-23 */
+		WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress);
+		WriteDOC((ofs >> 9)  & 0xff, docptr, Mplus_FlashAddress);
+		WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress);
+		break;
+	default:
+		return;
+	}
+
+	WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
+	WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
+}
+
+/* DoC_SelectChip: Select a given flash chip within the current floor */
+static int DoC_SelectChip(unsigned long docptr, int chip)
+{
+	/* No choice for flash chip on Millennium Plus */
+	return 0;
+}
+
+/* DoC_SelectFloor: Select a given floor (bank of flash chips) */
+static int DoC_SelectFloor(unsigned long docptr, int floor)
+{
+	WriteDOC((floor & 0x3), docptr, Mplus_DeviceSelect);
+	return 0;
+}
+
+/*
+ * Translate the given offset into the appropriate command and offset.
+ * This does the mapping using the 16bit interleave layout defined by
+ * M-Systems, and looks like this for a sector pair:
+ *  +-----------+-------+-------+-------+--------------+---------+-----------+
+ *  | 0 --- 511 |512-517|518-519|520-521| 522 --- 1033 |1034-1039|1040 - 1055|
+ *  +-----------+-------+-------+-------+--------------+---------+-----------+
+ *  | Data 0    | ECC 0 |Flags0 |Flags1 | Data 1       |ECC 1    | OOB 1 + 2 |
+ *  +-----------+-------+-------+-------+--------------+---------+-----------+
+ */
+/* FIXME: This lives in INFTL not here. Other users of flash devices
+   may not want it */
+static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from)
+{
+	struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+
+	if (this->interleave) {
+		unsigned int ofs = *from & 0x3ff;
+		unsigned int cmd;
+
+		if (ofs < 512) {
+			cmd = NAND_CMD_READ0;
+			ofs &= 0x1ff;
+		} else if (ofs < 1014) {
+			cmd = NAND_CMD_READ1;
+			ofs = (ofs & 0x1ff) + 10;
+		} else {
+			cmd = NAND_CMD_READOOB;
+			ofs = ofs - 1014;
+		}
+
+		*from = (*from & ~0x3ff) | ofs;
+		return cmd;
+	} else {
+		/* No interleave */
+		if ((*from) & 0x100)
+			return NAND_CMD_READ1;
+		return NAND_CMD_READ0;
+	}
+}
+
+static unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from)
+{
+	unsigned int ofs, cmd;
+
+	if (*from & 0x200) {
+		cmd = NAND_CMD_READOOB;
+		ofs = 10 + (*from & 0xf);
+	} else {
+		cmd = NAND_CMD_READ1;
+		ofs = (*from & 0xf);
+	}
+
+	*from = (*from & ~0x3ff) | ofs;
+	return cmd;
+}
+
+static unsigned int DoC_GetFlagsOffset(struct mtd_info *mtd, loff_t *from)
+{
+	unsigned int ofs, cmd;
+
+	cmd = NAND_CMD_READ1;
+	ofs = (*from & 0x200) ? 8 : 6;
+	*from = (*from & ~0x3ff) | ofs;
+	return cmd;
+}
+
+static unsigned int DoC_GetHdrOffset(struct mtd_info *mtd, loff_t *from)
+{
+	unsigned int ofs, cmd;
+
+	cmd = NAND_CMD_READOOB;
+	ofs = (*from & 0x200) ? 24 : 16;
+	*from = (*from & ~0x3ff) | ofs;
+	return cmd;
+}
+
+static inline void MemReadDOC(unsigned long docptr, unsigned char *buf, int len)
+{
+#ifndef USE_MEMCPY
+	int i;
+	for (i = 0; i < len; i++)
+		buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i);
+#else
+	memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len);
+#endif
+}
+
+static inline void MemWriteDOC(unsigned long docptr, unsigned char *buf, int len)
+{
+#ifndef USE_MEMCPY
+	int i;
+	for (i = 0; i < len; i++)
+		WriteDOC(buf[i], docptr, Mil_CDSN_IO + i);
+#else
+	memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len);
+#endif
+}
+
+/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */
+static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip)
+{
+	int mfr, id, i, j;
+	volatile char dummy;
+	unsigned long docptr = doc->virtadr;
+
+	/* Page in the required floor/chip */
+	DoC_SelectFloor(docptr, floor);
+	DoC_SelectChip(docptr, chip);
+
+	/* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
+	WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect);
+
+	/* Reset the chip, see Software Requirement 11.4 item 1. */
+	DoC_Command(docptr, NAND_CMD_RESET, 0);
+	DoC_WaitReady(docptr);
+
+	/* Read the NAND chip ID: 1. Send ReadID command */ 
+	DoC_Command(docptr, NAND_CMD_READID, 0);
+
+	/* Read the NAND chip ID: 2. Send address byte zero */ 
+	DoC_Address(doc, 1, 0x00, 0, 0x00);
+
+	WriteDOC(0, docptr, Mplus_FlashControl);
+	DoC_WaitReady(docptr);
+
+	/* Read the manufacturer and device id codes of the flash device through
+	   CDSN IO register see Software Requirement 11.4 item 5.*/
+	dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
+	dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
+
+	mfr = ReadDOC(docptr, Mil_CDSN_IO);
+	if (doc->interleave)
+		dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */
+
+	id  = ReadDOC(docptr, Mil_CDSN_IO);
+	if (doc->interleave)
+		dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */
+
+	dummy = ReadDOC(docptr, Mplus_LastDataRead);
+	dummy = ReadDOC(docptr, Mplus_LastDataRead);
+
+	/* Disable flash internally */
+	WriteDOC(0, docptr, Mplus_FlashSelect);
+
+	/* No response - return failure */
+	if (mfr == 0xff || mfr == 0)
+		return 0;
+
+	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+		if (id == nand_flash_ids[i].id) {
+			/* Try to identify manufacturer */
+			for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
+				if (nand_manuf_ids[j].id == mfr)
+					break;
+			}
+			printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, "
+			       "Chip ID: %2.2X (%s:%s)\n", mfr, id,
+			       nand_manuf_ids[j].name, nand_flash_ids[i].name);
+			doc->mfr = mfr;
+			doc->id = id;
+			doc->chipshift = ffs((nand_flash_ids[i].chipsize << 20)) - 1;
+			doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave;
+			break;
+		}
+	}
+
+	if (nand_flash_ids[i].name == NULL)
+		return 0;
+	return 1;
+}
+
+/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */
+static void DoC_ScanChips(struct DiskOnChip *this)
+{
+	int floor, chip;
+	int numchips[MAX_FLOORS_MPLUS];
+	int ret;
+
+	this->numchips = 0;
+	this->mfr = 0;
+	this->id = 0;
+
+	/* Work out the intended interleave setting */
+	this->interleave = 0;
+	if (this->ChipID == DOC_ChipID_DocMilPlus32)
+		this->interleave = 1;
+
+	/* Check the ASIC agrees */
+	if ( (this->interleave << 2) != 
+	     (ReadDOC(this->virtadr, Mplus_Configuration) & 4)) {
+		u_char conf = ReadDOC(this->virtadr, Mplus_Configuration);
+		printk(KERN_NOTICE "Setting DiskOnChip Millennium Plus interleave to %s\n",
+		       this->interleave?"on (16-bit)":"off (8-bit)");
+		conf ^= 4;
+		WriteDOC(this->virtadr, conf, Mplus_Configuration);
+	}
+
+	/* For each floor, find the number of valid chips it contains */
+	for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) {
+		numchips[floor] = 0;
+		for (chip = 0; chip < MAX_CHIPS_MPLUS && ret != 0; chip++) {
+			ret = DoC_IdentChip(this, floor, chip);
+			if (ret) {
+				numchips[floor]++;
+				this->numchips++;
+			}
+		}
+	}
+	/* If there are none at all that we recognise, bail */
+	if (!this->numchips) {
+		printk("No flash chips recognised.\n");
+		return;
+	}
+
+	/* Allocate an array to hold the information for each chip */
+	this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL);
+	if (!this->chips){
+		printk("MTD: No memory for allocating chip info structures\n");
+		return;
+	}
+
+	/* Fill out the chip array with {floor, chipno} for each 
+	 * detected chip in the device. */
+	for (floor = 0, ret = 0; floor < MAX_FLOORS_MPLUS; floor++) {
+		for (chip = 0 ; chip < numchips[floor] ; chip++) {
+			this->chips[ret].floor = floor;
+			this->chips[ret].chip = chip;
+			this->chips[ret].curadr = 0;
+			this->chips[ret].curmode = 0x50;
+			ret++;
+		}
+	}
+
+	/* Calculate and print the total size of the device */
+	this->totlen = this->numchips * (1 << this->chipshift);
+	printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n",
+	       this->numchips ,this->totlen >> 20);
+}
+
+static int DoCMilPlus_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2)
+{
+	int tmp1, tmp2, retval;
+
+	if (doc1->physadr == doc2->physadr)
+		return 1;
+
+	/* Use the alias resolution register which was set aside for this
+	 * purpose. If it's value is the same on both chips, they might
+	 * be the same chip, and we write to one and check for a change in
+	 * the other. It's unclear if this register is usuable in the
+	 * DoC 2000 (it's in the Millennium docs), but it seems to work. */
+	tmp1 = ReadDOC(doc1->virtadr, Mplus_AliasResolution);
+	tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution);
+	if (tmp1 != tmp2)
+		return 0;
+	
+	WriteDOC((tmp1+1) % 0xff, doc1->virtadr, Mplus_AliasResolution);
+	tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution);
+	if (tmp2 == (tmp1+1) % 0xff)
+		retval = 1;
+	else
+		retval = 0;
+
+	/* Restore register contents.  May not be necessary, but do it just to
+	 * be safe. */
+	WriteDOC(tmp1, doc1->virtadr, Mplus_AliasResolution);
+
+	return retval;
+}
+
+static const char im_name[] = "DoCMilPlus_init";
+
+/* This routine is made available to other mtd code via
+ * inter_module_register.  It must only be accessed through
+ * inter_module_get which will bump the use count of this module.  The
+ * addresses passed back in mtd are valid as long as the use count of
+ * this module is non-zero, i.e. between inter_module_get and
+ * inter_module_put.  Keith Owens <kaos@ocs.com.au> 29 Oct 2000.
+ */
+static void DoCMilPlus_init(struct mtd_info *mtd)
+{
+	struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+	struct DiskOnChip *old = NULL;
+
+	/* We must avoid being called twice for the same device. */
+	if (docmilpluslist)
+		old = (struct DiskOnChip *)docmilpluslist->priv;
+
+	while (old) {
+		if (DoCMilPlus_is_alias(this, old)) {
+			printk(KERN_NOTICE "Ignoring DiskOnChip Millennium "
+				"Plus at 0x%lX - already configured\n",
+				this->physadr);
+			iounmap((void *)this->virtadr);
+			kfree(mtd);
+			return;
+		}
+		if (old->nextdoc)
+			old = (struct DiskOnChip *)old->nextdoc->priv;
+		else
+			old = NULL;
+	}
+
+	mtd->name = "DiskOnChip Millennium Plus";
+	printk(KERN_NOTICE "DiskOnChip Millennium Plus found at "
+		"address 0x%lX\n", this->physadr);
+
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH;
+	mtd->ecctype = MTD_ECC_RS_DiskOnChip;
+	mtd->size = 0;
+
+	mtd->erasesize = 0;
+	mtd->oobblock = 512;
+	mtd->oobsize = 16;
+	mtd->owner = THIS_MODULE;
+	mtd->erase = doc_erase;
+	mtd->point = NULL;
+	mtd->unpoint = NULL;
+	mtd->read = doc_read;
+	mtd->write = doc_write;
+	mtd->read_ecc = doc_read_ecc;
+	mtd->write_ecc = doc_write_ecc;
+	mtd->read_oob = doc_read_oob;
+	mtd->write_oob = doc_write_oob;
+	mtd->sync = NULL;
+
+	this->totlen = 0;
+	this->numchips = 0;
+	this->curfloor = -1;
+	this->curchip = -1;
+
+	/* Ident all the chips present. */
+	DoC_ScanChips(this);
+
+	if (!this->totlen) {
+		kfree(mtd);
+		iounmap((void *)this->virtadr);
+	} else {
+		this->nextdoc = docmilpluslist;
+		docmilpluslist = mtd;
+		mtd->size  = this->totlen;
+		mtd->erasesize = this->erasesize;
+		add_mtd_device(mtd);
+		return;
+	}
+}
+
+#if 0
+static int doc_dumpblk(struct mtd_info *mtd, loff_t from)
+{
+	int i;
+	loff_t fofs;
+	struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+	unsigned long docptr = this->virtadr;
+	struct Nand *mychip = &this->chips[from >> (this->chipshift)];
+	unsigned char *bp, buf[1056];
+	char c[32];
+
+	from &= ~0x3ff;
+
+	/* Don't allow read past end of device */
+	if (from >= this->totlen)
+		return -EINVAL;
+
+	DoC_CheckASIC(docptr);
+
+	/* Find the chip which is to be used and select it */
+	if (this->curfloor != mychip->floor) {
+		DoC_SelectFloor(docptr, mychip->floor);
+		DoC_SelectChip(docptr, mychip->chip);
+	} else if (this->curchip != mychip->chip) {
+		DoC_SelectChip(docptr, mychip->chip);
+	}
+	this->curfloor = mychip->floor;
+	this->curchip = mychip->chip;
+
+	/* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
+	WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect);
+
+	/* Reset the chip, see Software Requirement 11.4 item 1. */
+	DoC_Command(docptr, NAND_CMD_RESET, 0);
+	DoC_WaitReady(docptr);
+
+	fofs = from;
+	DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0);
+	DoC_Address(this, 3, fofs, 0, 0x00);
+	WriteDOC(0, docptr, Mplus_FlashControl);
+	DoC_WaitReady(docptr);
+
+	/* disable the ECC engine */
+	WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
+
+	ReadDOC(docptr, Mplus_ReadPipeInit);
+	ReadDOC(docptr, Mplus_ReadPipeInit);
+
+	/* Read the data via the internal pipeline through CDSN IO
+	   register, see Pipelined Read Operations 11.3 */
+	MemReadDOC(docptr, buf, 1054);
+	buf[1054] = ReadDOC(docptr, Mplus_LastDataRead);
+	buf[1055] = ReadDOC(docptr, Mplus_LastDataRead);
+
+	memset(&c[0], 0, sizeof(c));
+	printk("DUMP OFFSET=%x:\n", (int)from);
+
+        for (i = 0, bp = &buf[0]; (i < 1056); i++) {
+                if ((i % 16) == 0)
+                        printk("%08x: ", i);
+                printk(" %02x", *bp);
+                c[(i & 0xf)] = ((*bp >= 0x20) && (*bp <= 0x7f)) ? *bp : '.';
+                bp++;
+                if (((i + 1) % 16) == 0)
+                        printk("    %s\n", c);
+        }
+	printk("\n");
+
+	/* Disable flash internally */
+	WriteDOC(0, docptr, Mplus_FlashSelect);
+
+	return 0;
+}
+#endif
+
+static int doc_read(struct mtd_info *mtd, loff_t from, size_t len,
+		    size_t *retlen, u_char *buf)
+{
+	/* Just a special case of doc_read_ecc */
+	return doc_read_ecc(mtd, from, len, retlen, buf, NULL, NULL);
+}
+
+static int doc_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)
+{
+	int ret, i;
+	volatile char dummy;
+	loff_t fofs;
+	unsigned char syndrome[6];
+	struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+	unsigned long docptr = this->virtadr;
+	struct Nand *mychip = &this->chips[from >> (this->chipshift)];
+
+	/* Don't allow read past end of device */
+	if (from >= this->totlen)
+		return -EINVAL;
+
+	/* Don't allow a single read to cross a 512-byte block boundary */
+	if (from + len > ((from | 0x1ff) + 1)) 
+		len = ((from | 0x1ff) + 1) - from;
+
+	DoC_CheckASIC(docptr);
+
+	/* Find the chip which is to be used and select it */
+	if (this->curfloor != mychip->floor) {
+		DoC_SelectFloor(docptr, mychip->floor);
+		DoC_SelectChip(docptr, mychip->chip);
+	} else if (this->curchip != mychip->chip) {
+		DoC_SelectChip(docptr, mychip->chip);
+	}
+	this->curfloor = mychip->floor;
+	this->curchip = mychip->chip;
+
+	/* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
+	WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect);
+
+	/* Reset the chip, see Software Requirement 11.4 item 1. */
+	DoC_Command(docptr, NAND_CMD_RESET, 0);
+	DoC_WaitReady(docptr);
+
+	fofs = from;
+	DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0);
+	DoC_Address(this, 3, fofs, 0, 0x00);
+	WriteDOC(0, docptr, Mplus_FlashControl);
+	DoC_WaitReady(docptr);
+
+	if (eccbuf) {
+		/* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
+		WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
+		WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf);
+	} else {
+		/* disable the ECC engine */
+		WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
+	}
+
+	/* Let the caller know we completed it */
+	*retlen = len;
+        ret = 0;
+
+	ReadDOC(docptr, Mplus_ReadPipeInit);
+	ReadDOC(docptr, Mplus_ReadPipeInit);
+
+	if (eccbuf) {
+		/* Read the data via the internal pipeline through CDSN IO
+		   register, see Pipelined Read Operations 11.3 */
+		MemReadDOC(docptr, buf, len);
+
+		/* Read the ECC data following raw data */
+		MemReadDOC(docptr, eccbuf, 4);
+		eccbuf[4] = ReadDOC(docptr, Mplus_LastDataRead);
+		eccbuf[5] = ReadDOC(docptr, Mplus_LastDataRead);
+
+		/* Flush the pipeline */
+		dummy = ReadDOC(docptr, Mplus_ECCConf);
+		dummy = ReadDOC(docptr, Mplus_ECCConf);
+
+		/* Check the ECC Status */
+		if (ReadDOC(docptr, Mplus_ECCConf) & 0x80) {
+                        int nb_errors;
+			/* There was an ECC error */
+#ifdef ECC_DEBUG
+			printk("DiskOnChip ECC Error: Read at %lx\n", (long)from);
+#endif
+			/* Read the ECC syndrom through the DiskOnChip ECC logic.
+			   These syndrome will be all ZERO when there is no error */
+			for (i = 0; i < 6; i++)
+				syndrome[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i);
+
+                        nb_errors = doc_decode_ecc(buf, syndrome);
+#ifdef ECC_DEBUG
+			printk("ECC Errors corrected: %x\n", nb_errors);
+#endif
+                        if (nb_errors < 0) {
+				/* We return error, but have actually done the read. Not that
+				   this can be told to user-space, via sys_read(), but at least
+				   MTD-aware stuff can know about it by checking *retlen */
+#ifdef ECC_DEBUG
+			printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n",
+				__FILE__, __LINE__, (int)from);
+			printk("        syndrome= %02x:%02x:%02x:%02x:%02x:"
+				"%02x\n",
+				syndrome[0], syndrome[1], syndrome[2],
+				syndrome[3], syndrome[4], syndrome[5]);
+			printk("          eccbuf= %02x:%02x:%02x:%02x:%02x:"
+				"%02x\n",
+				eccbuf[0], eccbuf[1], eccbuf[2],
+				eccbuf[3], eccbuf[4], eccbuf[5]);
+#endif
+				ret = -EIO;
+                        }
+		}
+
+#ifdef PSYCHO_DEBUG
+		printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+		       (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
+		       eccbuf[4], eccbuf[5]);
+#endif
+
+		/* disable the ECC engine */
+		WriteDOC(DOC_ECC_DIS, docptr , Mplus_ECCConf);
+	} else {
+		/* Read the data via the internal pipeline through CDSN IO
+		   register, see Pipelined Read Operations 11.3 */
+		MemReadDOC(docptr, buf, len-2);
+		buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead);
+		buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead);
+	}
+
+	/* Disable flash internally */
+	WriteDOC(0, docptr, Mplus_FlashSelect);
+
+	return ret;
+}
+
+static int doc_write(struct mtd_info *mtd, loff_t to, size_t len,
+		     size_t *retlen, const u_char *buf)
+{
+	char eccbuf[6];
+	return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL);
+}
+
+static int doc_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 i, before, ret = 0;
+	loff_t fto;
+	volatile char dummy;
+	struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+	unsigned long docptr = this->virtadr;
+	struct Nand *mychip = &this->chips[to >> (this->chipshift)];
+
+	/* Don't allow write past end of device */
+	if (to >= this->totlen)
+		return -EINVAL;
+
+	/* Don't allow writes which aren't exactly one block (512 bytes) */
+	if ((to & 0x1ff) || (len != 0x200))
+		return -EINVAL;
+
+	/* Determine position of OOB flags, before or after data */
+	before = (this->interleave && (to & 0x200));
+
+	DoC_CheckASIC(docptr);
+
+	/* Find the chip which is to be used and select it */
+	if (this->curfloor != mychip->floor) {
+		DoC_SelectFloor(docptr, mychip->floor);
+		DoC_SelectChip(docptr, mychip->chip);
+	} else if (this->curchip != mychip->chip) {
+		DoC_SelectChip(docptr, mychip->chip);
+	}
+	this->curfloor = mychip->floor;
+	this->curchip = mychip->chip;
+
+	/* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
+	WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect);
+
+	/* Reset the chip, see Software Requirement 11.4 item 1. */
+	DoC_Command(docptr, NAND_CMD_RESET, 0);
+	DoC_WaitReady(docptr);
+
+	/* Set device to appropriate plane of flash */
+	fto = to;
+	WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd);
+
+	/* On interleaved devices the flags for 2nd half 512 are before data */
+	if (eccbuf && before)
+		fto -= 2;
+
+	/* issue the Serial Data In command to initial the Page Program process */
+	DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
+	DoC_Address(this, 3, fto, 0x00, 0x00);
+
+	/* Disable the ECC engine */
+	WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
+
+	if (eccbuf) {
+		if (before) {
+			/* Write the block status BLOCK_USED (0x5555) */
+			WriteDOC(0x55, docptr, Mil_CDSN_IO);
+			WriteDOC(0x55, docptr, Mil_CDSN_IO);
+		}
+
+		/* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/
+		WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf);
+	}
+
+	MemWriteDOC(docptr, (unsigned char *) buf, len);
+
+	if (eccbuf) {
+		/* Write ECC data to flash, the ECC info is generated by
+		   the DiskOnChip ECC logic see Reed-Solomon EDC/ECC 11.1 */
+		DoC_Delay(docptr, 3);
+
+		/* Read the ECC data through the DiskOnChip ECC logic */
+		for (i = 0; i < 6; i++)
+			eccbuf[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i);
+
+		/* disable the ECC engine */
+		WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
+
+		/* Write the ECC data to flash */
+		MemWriteDOC(docptr, eccbuf, 6);
+
+		if (!before) {
+			/* Write the block status BLOCK_USED (0x5555) */
+			WriteDOC(0x55, docptr, Mil_CDSN_IO+6);
+			WriteDOC(0x55, docptr, Mil_CDSN_IO+7);
+		}
+
+#ifdef PSYCHO_DEBUG
+		printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n",
+		       (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3],
+		       eccbuf[4], eccbuf[5]);
+#endif
+	}
+
+	WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
+	WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
+
+	/* Commit the Page Program command and wait for ready
+	   see Software Requirement 11.4 item 1.*/
+	DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00);
+	DoC_WaitReady(docptr);
+
+	/* Read the status of the flash device through CDSN IO register
+	   see Software Requirement 11.4 item 5.*/
+	DoC_Command(docptr, NAND_CMD_STATUS, 0);
+	dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
+	dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
+	DoC_Delay(docptr, 2);
+	if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) {
+		printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to);
+		/* Error in programming
+		   FIXME: implement Bad Block Replacement (in nftl.c ??) */
+		*retlen = 0;
+		ret = -EIO;
+	}
+	dummy = ReadDOC(docptr, Mplus_LastDataRead);
+
+	/* Disable flash internally */
+	WriteDOC(0, docptr, Mplus_FlashSelect);
+
+	/* Let the caller know we completed it */
+	*retlen = len;
+
+	return ret;
+}
+
+static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+			size_t *retlen, u_char *buf)
+{
+	loff_t fofs, base;
+	struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+	unsigned long docptr = this->virtadr;
+	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
+	size_t i, size, got, want;
+
+	DoC_CheckASIC(docptr);
+
+	/* Find the chip which is to be used and select it */
+	if (this->curfloor != mychip->floor) {
+		DoC_SelectFloor(docptr, mychip->floor);
+		DoC_SelectChip(docptr, mychip->chip);
+	} else if (this->curchip != mychip->chip) {
+		DoC_SelectChip(docptr, mychip->chip);
+	}
+	this->curfloor = mychip->floor;
+	this->curchip = mychip->chip;
+
+	/* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
+	WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect);
+
+	/* disable the ECC engine */
+	WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
+	DoC_WaitReady(docptr);
+
+	/* Maximum of 16 bytes in the OOB region, so limit read to that */
+	if (len > 16)
+		len = 16;
+	got = 0;
+	want = len;
+
+	for (i = 0; ((i < 3) && (want > 0)); i++) {
+		/* Figure out which region we are accessing... */
+		fofs = ofs;
+		base = ofs & 0xf;
+		if (!this->interleave) {
+			DoC_Command(docptr, NAND_CMD_READOOB, 0);
+			size = 16 - base;
+		} else if (base < 6) {
+			DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0);
+			size = 6 - base;
+		} else if (base < 8) {
+			DoC_Command(docptr, DoC_GetFlagsOffset(mtd, &fofs), 0);
+			size = 8 - base;
+		} else {
+			DoC_Command(docptr, DoC_GetHdrOffset(mtd, &fofs), 0);
+			size = 16 - base;
+		}
+		if (size > want)
+			size = want;
+
+		/* Issue read command */
+		DoC_Address(this, 3, fofs, 0, 0x00);
+		WriteDOC(0, docptr, Mplus_FlashControl);
+		DoC_WaitReady(docptr);
+
+		ReadDOC(docptr, Mplus_ReadPipeInit);
+		ReadDOC(docptr, Mplus_ReadPipeInit);
+		MemReadDOC(docptr, &buf[got], size - 2);
+		buf[got + size - 2] = ReadDOC(docptr, Mplus_LastDataRead);
+		buf[got + size - 1] = ReadDOC(docptr, Mplus_LastDataRead);
+
+		ofs += size;
+		got += size;
+		want -= size;
+	}
+
+	/* Disable flash internally */
+	WriteDOC(0, docptr, Mplus_FlashSelect);
+
+	*retlen = len;
+	return 0;
+}
+
+static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len,
+			 size_t *retlen, const u_char *buf)
+{
+	volatile char dummy;
+	loff_t fofs, base;
+	struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+	unsigned long docptr = this->virtadr;
+	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
+	size_t i, size, got, want;
+	int ret = 0;
+
+	DoC_CheckASIC(docptr);
+
+	/* Find the chip which is to be used and select it */
+	if (this->curfloor != mychip->floor) {
+		DoC_SelectFloor(docptr, mychip->floor);
+		DoC_SelectChip(docptr, mychip->chip);
+	} else if (this->curchip != mychip->chip) {
+		DoC_SelectChip(docptr, mychip->chip);
+	}
+	this->curfloor = mychip->floor;
+	this->curchip = mychip->chip;
+
+	/* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
+	WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect);
+
+
+	/* Maximum of 16 bytes in the OOB region, so limit write to that */
+	if (len > 16)
+		len = 16;
+	got = 0;
+	want = len;
+
+	for (i = 0; ((i < 3) && (want > 0)); i++) {
+		/* Reset the chip, see Software Requirement 11.4 item 1. */
+		DoC_Command(docptr, NAND_CMD_RESET, 0);
+		DoC_WaitReady(docptr);
+
+		/* Figure out which region we are accessing... */
+		fofs = ofs;
+		base = ofs & 0x0f;
+		if (!this->interleave) {
+			WriteDOC(NAND_CMD_READOOB, docptr, Mplus_FlashCmd);
+			size = 16 - base;
+		} else if (base < 6) {
+			WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd);
+			size = 6 - base;
+		} else if (base < 8) {
+			WriteDOC(DoC_GetFlagsOffset(mtd, &fofs), docptr, Mplus_FlashCmd);
+			size = 8 - base;
+		} else {
+			WriteDOC(DoC_GetHdrOffset(mtd, &fofs), docptr, Mplus_FlashCmd);
+			size = 16 - base;
+		}
+		if (size > want)
+			size = want;
+
+		/* Issue the Serial Data In command to initial the Page Program process */
+		DoC_Command(docptr, NAND_CMD_SEQIN, 0x00);
+		DoC_Address(this, 3, fofs, 0, 0x00);
+
+		/* Disable the ECC engine */
+		WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
+
+		/* Write the data via the internal pipeline through CDSN IO
+		   register, see Pipelined Write Operations 11.2 */
+		MemWriteDOC(docptr, (unsigned char *) &buf[got], size);
+		WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
+		WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
+
+		/* Commit the Page Program command and wait for ready
+	 	   see Software Requirement 11.4 item 1.*/
+		DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00);
+		DoC_WaitReady(docptr);
+
+		/* Read the status of the flash device through CDSN IO register
+		   see Software Requirement 11.4 item 5.*/
+		DoC_Command(docptr, NAND_CMD_STATUS, 0x00);
+		dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
+		dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
+		DoC_Delay(docptr, 2);
+		if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) {
+			printk("MTD: Error 0x%x programming oob at 0x%x\n",
+				dummy, (int)ofs);
+			/* FIXME: implement Bad Block Replacement */
+			*retlen = 0;
+			ret = -EIO;
+		}
+		dummy = ReadDOC(docptr, Mplus_LastDataRead);
+
+		ofs += size;
+		got += size;
+		want -= size;
+	}
+
+	/* Disable flash internally */
+	WriteDOC(0, docptr, Mplus_FlashSelect);
+
+	*retlen = len;
+	return ret;
+}
+
+int doc_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	volatile char dummy;
+	struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
+	__u32 ofs = instr->addr;
+	__u32 len = instr->len;
+	unsigned long docptr = this->virtadr;
+	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
+
+	DoC_CheckASIC(docptr);
+
+	if (len != mtd->erasesize) 
+		printk(KERN_WARNING "MTD: Erase not right size (%x != %x)n",
+		       len, mtd->erasesize);
+
+	/* Find the chip which is to be used and select it */
+	if (this->curfloor != mychip->floor) {
+		DoC_SelectFloor(docptr, mychip->floor);
+		DoC_SelectChip(docptr, mychip->chip);
+	} else if (this->curchip != mychip->chip) {
+		DoC_SelectChip(docptr, mychip->chip);
+	}
+	this->curfloor = mychip->floor;
+	this->curchip = mychip->chip;
+
+	instr->state = MTD_ERASE_PENDING;
+
+	/* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */
+	WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect);
+
+	DoC_Command(docptr, NAND_CMD_RESET, 0x00);
+	DoC_WaitReady(docptr);
+
+	DoC_Command(docptr, NAND_CMD_ERASE1, 0);
+	DoC_Address(this, 2, ofs, 0, 0x00);
+	DoC_Command(docptr, NAND_CMD_ERASE2, 0);
+	DoC_WaitReady(docptr);
+	instr->state = MTD_ERASING;
+
+	/* Read the status of the flash device through CDSN IO register
+	   see Software Requirement 11.4 item 5. */
+	DoC_Command(docptr, NAND_CMD_STATUS, 0);
+	dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
+	dummy = ReadDOC(docptr, Mplus_ReadPipeInit);
+	if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) {
+		printk("MTD: Error 0x%x erasing at 0x%x\n", dummy, ofs);
+		/* FIXME: implement Bad Block Replacement (in nftl.c ??) */
+		instr->state = MTD_ERASE_FAILED;
+	} else {
+		instr->state = MTD_ERASE_DONE;
+	}
+	dummy = ReadDOC(docptr, Mplus_LastDataRead);
+
+	/* Disable flash internally */
+	WriteDOC(0, docptr, Mplus_FlashSelect);
+
+	if (instr->callback) 
+		instr->callback(instr);
+
+	return 0;
+}
+
+/****************************************************************************
+ *
+ * Module stuff
+ *
+ ****************************************************************************/
+
+int __init init_doc2001plus(void)
+{
+	inter_module_register(im_name, THIS_MODULE, &DoCMilPlus_init);
+	return 0;
+}
+
+static void __exit cleanup_doc2001plus(void)
+{
+	struct mtd_info *mtd;
+	struct DiskOnChip *this;
+
+	while ((mtd=docmilpluslist)) {
+		this = (struct DiskOnChip *)mtd->priv;
+		docmilpluslist = this->nextdoc;
+			
+		del_mtd_device(mtd);
+			
+		iounmap((void *)this->virtadr);
+		kfree(this->chips);
+		kfree(mtd);
+	}
+	inter_module_unregister(im_name);
+}
+
+module_exit(cleanup_doc2001plus);
+module_init(init_doc2001plus);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com> et al.");
+MODULE_DESCRIPTION("Driver for DiskOnChip Millennium Plus");
diff -Nur linux-2.4.30/drivers/mtd/devices/docecc.c linux-2.4.30-linux-mips.org/drivers/mtd/devices/docecc.c
--- linux-2.4.30/drivers/mtd/devices/docecc.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/docecc.c	2005-05-12 18:01:29.000000000 +0200
@@ -7,7 +7,7 @@
  * Author: Fabrice Bellard (fabrice.bellard@netgem.com) 
  * Copyright (C) 2000 Netgem S.A.
  *
- * $Id: docecc.c,v 1.4 2001/10/02 15:05:13 dwmw2 Exp $
+ * $Id: docecc.c,v 1.5 2003/05/21 15:15:06 dwmw2 Exp $
  *
  * 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
@@ -519,6 +519,8 @@
     return nb_errors;
 }
 
+EXPORT_SYMBOL_GPL(doc_decode_ecc);
+
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Fabrice Bellard <fabrice.bellard@netgem.com>");
 MODULE_DESCRIPTION("ECC code for correcting errors detected by DiskOnChip 2000 and Millennium ECC hardware");
diff -Nur linux-2.4.30/drivers/mtd/devices/docprobe.c linux-2.4.30-linux-mips.org/drivers/mtd/devices/docprobe.c
--- linux-2.4.30/drivers/mtd/devices/docprobe.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/docprobe.c	2005-05-12 18:01:29.000000000 +0200
@@ -4,7 +4,7 @@
 /* (C) 1999 Machine Vision Holdings, Inc.			*/
 /* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>		*/
 
-/* $Id: docprobe.c,v 1.33 2003/01/24 14:02:47 dwmw2 Exp $	*/
+/* $Id: docprobe.c,v 1.41 2003/12/03 10:19:57 dwmw2 Exp $	*/
 
 
 
@@ -31,14 +31,12 @@
 /* DOC_SINGLE_DRIVER:
    Millennium driver has been merged into DOC2000 driver.
 
-   The newly-merged driver doesn't appear to work for writing. It's the
-   same with the DiskOnChip 2000 and the Millennium. If you have a 
-   Millennium and you want write support to work, remove the definition
-   of DOC_SINGLE_DRIVER below to use the old doc2001-specific driver.
-
-   Otherwise, it's left on in the hope that it'll annoy someone with
-   a Millennium enough that they go through and work out what the 
-   difference is :)
+   The old Millennium-only driver has been retained just in case there
+   are problems with the new code. If the combined driver doesn't work
+   for you, you can try the old one by undefining DOC_SINGLE_DRIVER 
+   below and also enabling it in your configuration. If this fixes the
+   problems, please send a report to the MTD mailing list at 
+   <linux-mtd@lists.infradead.org>.
 */
 #define DOC_SINGLE_DRIVER
 
@@ -47,18 +45,15 @@
 #include <linux/module.h>
 #include <asm/errno.h>
 #include <asm/io.h>
-#include <asm/uaccess.h>
-#include <linux/miscdevice.h>
-#include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
-#include <linux/sched.h>
 #include <linux/init.h>
 #include <linux/types.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/doc2000.h>
+#include <linux/mtd/compatmac.h>
 
 /* Where to look for the devices? */
 #ifndef CONFIG_MTD_DOCPROBE_ADDRESS
@@ -95,14 +90,14 @@
 ##else
 #warning Unknown architecture for DiskOnChip. No default probe locations defined
 #endif
-	0 };
+	0xffffffff };
 
 /* doccheck: Probe a given memory window to see if there's a DiskOnChip present */
 
 static inline int __init doccheck(unsigned long potential, unsigned long physadr)
 {
 	unsigned long window=potential;
-	unsigned char tmp, ChipID;
+	unsigned char tmp, tmpb, tmpc, ChipID;
 #ifndef DOC_PASSIVE_PROBE
 	unsigned char tmp2;
 #endif
@@ -140,26 +135,80 @@
 		 window, DOCControl);
 #endif /* !DOC_PASSIVE_PROBE */	
 
+	/* We need to read the ChipID register four times. For some
+	   newer DiskOnChip 2000 units, the first three reads will
+	   return the DiskOnChip Millennium ident. Don't ask. */
 	ChipID = ReadDOC(window, ChipID);
   
 	switch (ChipID) {
 	case DOC_ChipID_Doc2k:
 		/* Check the TOGGLE bit in the ECC register */
-		tmp = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
-		if ((ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT) != tmp)
+		tmp  = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
+		tmpb = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
+		tmpc = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT;
+		if (tmp != tmpb && tmp == tmpc)
 				return ChipID;
 		break;
 		
 	case DOC_ChipID_DocMil:
+		/* Check for the new 2000 with Millennium ASIC */
+		ReadDOC(window, ChipID);
+		ReadDOC(window, ChipID);
+		if (ReadDOC(window, ChipID) != DOC_ChipID_DocMil)
+			ChipID = DOC_ChipID_Doc2kTSOP;
+
 		/* Check the TOGGLE bit in the ECC register */
-		tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
-		if ((ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT) != tmp)
+		tmp  = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
+		tmpb = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
+		tmpc = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT;
+		if (tmp != tmpb && tmp == tmpc)
 				return ChipID;
 		break;
 		
+	case DOC_ChipID_DocMilPlus16:
+	case DOC_ChipID_DocMilPlus32:
+	case 0:
+		/* Possible Millennium+, need to do more checks */
+#ifndef DOC_PASSIVE_PROBE
+		/* Possibly release from power down mode */
+		for (tmp = 0; (tmp < 4); tmp++)
+			ReadDOC(window, Mplus_Power);
+
+		/* Reset the DiskOnChip ASIC */
+		tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
+			DOC_MODE_BDECT;
+		WriteDOC(tmp, window, Mplus_DOCControl);
+		WriteDOC(~tmp, window, Mplus_CtrlConfirm);
+	
+		mdelay(1);
+		/* Enable the DiskOnChip ASIC */
+		tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
+			DOC_MODE_BDECT;
+		WriteDOC(tmp, window, Mplus_DOCControl);
+		WriteDOC(~tmp, window, Mplus_CtrlConfirm);
+		mdelay(1);
+#endif /* !DOC_PASSIVE_PROBE */	
+
+		ChipID = ReadDOC(window, ChipID);
+
+		switch (ChipID) {
+		case DOC_ChipID_DocMilPlus16:
+		case DOC_ChipID_DocMilPlus32:
+			/* Check the TOGGLE bit in the toggle register */
+			tmp  = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
+			tmpb = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
+			tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT;
+			if (tmp != tmpb && tmp == tmpc)
+					return ChipID;
+		default:
+			break;
+		}
+		/* FALL TRHU */
+
 	default:
-#ifndef CONFIG_MTD_DOCPROBE_55AA
-		printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
+
+#ifdef CONFIG_MTD_DOCPROBE_55AA
+		printk(KERN_DEBUG "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
 		       ChipID, physadr);
 #endif
 #ifndef DOC_PASSIVE_PROBE
@@ -200,6 +249,12 @@
 		return;
 	
 	if ((ChipID = doccheck(docptr, physadr))) {
+		if (ChipID == DOC_ChipID_Doc2kTSOP) {
+			/* Remove this at your own peril. The hardware driver works but nothing prevents you from erasing bad blocks */
+			printk(KERN_NOTICE "Refusing to drive DiskOnChip 2000 TSOP until Bad Block Table is correctly supported by INFTL\n");
+			iounmap((void *)docptr);
+			return;
+		}
 		docfound = 1;
 		mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL);
 
@@ -221,6 +276,12 @@
 		sprintf(namebuf, "with ChipID %2.2X", ChipID);
 
 		switch(ChipID) {
+		case DOC_ChipID_Doc2kTSOP:
+			name="2000 TSOP";
+			im_funcname = "DoC2k_init";
+			im_modname = "doc2000";
+			break;
+			
 		case DOC_ChipID_Doc2k:
 			name="2000";
 			im_funcname = "DoC2k_init";
@@ -237,6 +298,13 @@
 			im_modname = "doc2001";
 #endif /* DOC_SINGLE_DRIVER */
 			break;
+
+		case DOC_ChipID_DocMilPlus16:
+		case DOC_ChipID_DocMilPlus32:
+			name="MillenniumPlus";
+			im_funcname = "DoCMilPlus_init";
+			im_modname = "doc2001plus";
+			break;
 		}
 
 		if (im_funcname)
@@ -248,6 +316,7 @@
 			return;
 		}
 		printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr);
+		kfree(mtd);
 	}
 	iounmap((void *)docptr);
 }
@@ -267,7 +336,7 @@
 		printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location);
 		DoC_Probe(doc_config_location);
 	} else {
-		for (i=0; doc_locations[i]; i++) {
+		for (i=0; (doc_locations[i] != 0xffffffff); i++) {
 			DoC_Probe(doc_locations[i]);
 		}
 	}
@@ -275,11 +344,7 @@
 	   found, so the user knows we at least tried. */
 	if (!docfound)
 		printk(KERN_INFO "No recognised DiskOnChip devices found\n");
-	/* So it looks like we've been used and we get unloaded */
-	MOD_INC_USE_COUNT;
-	MOD_DEC_USE_COUNT;
-	return 0;
-	
+	return -EAGAIN;
 }
 
 module_init(init_doc);
diff -Nur linux-2.4.30/drivers/mtd/devices/lart.c linux-2.4.30-linux-mips.org/drivers/mtd/devices/lart.c
--- linux-2.4.30/drivers/mtd/devices/lart.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/lart.c	2005-05-12 18:01:29.000000000 +0200
@@ -2,7 +2,7 @@
 /*
  * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART.
  *
- * $Id: lart.c,v 1.2 2001/10/02 15:05:13 dwmw2 Exp $
+ * $Id: lart.c,v 1.5 2003/05/20 21:03:07 dwmw2 Exp $
  *
  * Author: Abraham vd Merwe <abraham@2d3d.co.za>
  *
@@ -584,46 +584,41 @@
 
 static struct mtd_info mtd;
 
-static struct mtd_erase_region_info erase_regions[] =
-{
-   /* parameter blocks */
-   {
-	     offset: 0x00000000,
-	  erasesize: FLASH_BLOCKSIZE_PARAM,
-	  numblocks: FLASH_NUMBLOCKS_16m_PARAM
-   },
-   /* main blocks */
-   {
-	     offset: FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM,
-	  erasesize: FLASH_BLOCKSIZE_MAIN,
-	  numblocks: FLASH_NUMBLOCKS_16m_MAIN
-   }
+static struct mtd_erase_region_info erase_regions[] = {
+	/* parameter blocks */
+	{
+		.offset		= 0x00000000,
+		.erasesize	= FLASH_BLOCKSIZE_PARAM,
+		.numblocks	= FLASH_NUMBLOCKS_16m_PARAM,
+	},
+	/* main blocks */
+	{
+		.offset	 = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM,
+		.erasesize	= FLASH_BLOCKSIZE_MAIN,
+		.numblocks	= FLASH_NUMBLOCKS_16m_MAIN,
+	}
 };
 
 #ifdef HAVE_PARTITIONS
-static struct mtd_partition lart_partitions[] =
-{
-   /* blob */
-   {
-	       name: "blob",
-	     offset: BLOB_START,
-	       size: BLOB_LEN,
-	 mask_flags: 0
-   },
-   /* kernel */
-   {
-	       name: "kernel",
-	     offset: KERNEL_START,			/* MTDPART_OFS_APPEND */
-	       size: KERNEL_LEN,
-	 mask_flags: 0
-   },
-   /* initial ramdisk / file system */
-   {
-	       name: "file system",
-	     offset: INITRD_START,			/* MTDPART_OFS_APPEND */
-	       size: INITRD_LEN,			/* MTDPART_SIZ_FULL */
-	 mask_flags: 0
-   }
+static struct mtd_partition lart_partitions[] = {
+	/* blob */
+	{
+		.name	= "blob",
+		.offset	= BLOB_START,
+		.size	= BLOB_LEN,
+	},
+	/* kernel */
+	{
+		.name	= "kernel",
+		.offset	= KERNEL_START,		/* MTDPART_OFS_APPEND */
+		.size	= KERNEL_LEN,
+	},
+	/* initial ramdisk / file system */
+	{
+		.name	= "file system",
+		.offset	= INITRD_START,		/* MTDPART_OFS_APPEND */
+		.size	= INITRD_LEN,		/* MTDPART_SIZ_FULL */
+	}
 };
 #endif
 
@@ -646,10 +641,10 @@
    mtd.erasesize = FLASH_BLOCKSIZE_MAIN;
    mtd.numeraseregions = NB_OF (erase_regions);
    mtd.eraseregions = erase_regions;
-   mtd.module = THIS_MODULE;
    mtd.erase = flash_erase;
    mtd.read = flash_read;
    mtd.write = flash_write;
+   mtd.owner = THIS_MODULE;
 
 #ifdef LART_DEBUG
    printk (KERN_DEBUG
diff -Nur linux-2.4.30/drivers/mtd/devices/Makefile linux-2.4.30-linux-mips.org/drivers/mtd/devices/Makefile
--- linux-2.4.30/drivers/mtd/devices/Makefile	2002-11-29 00:53:13.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -1,9 +1,12 @@
 #
 # linux/drivers/devices/Makefile
 #
-# $Id: Makefile,v 1.4 2001/06/26 21:10:05 spse Exp $
+# $Id: Makefile.common,v 1.4 2003/08/21 17:52:29 joern Exp $
 
+ifeq ($(PATCHLEVEL),4)
 O_TARGET	:= devlink.o
+export-objs	:= docecc.o
+endif
 
 #                       *** BIG UGLY NOTE ***
 #
@@ -12,15 +15,16 @@
 # here where previously there was none.  We now have to ensure that
 # doc200[01].o are linked before docprobe.o
 
-obj-$(CONFIG_MTD_DOC1000)	+= doc1000.o
 obj-$(CONFIG_MTD_DOC2000)	+= doc2000.o
 obj-$(CONFIG_MTD_DOC2001)	+= doc2001.o
+obj-$(CONFIG_MTD_DOC2001PLUS)	+= doc2001plus.o
 obj-$(CONFIG_MTD_DOCPROBE)	+= docprobe.o docecc.o
 obj-$(CONFIG_MTD_SLRAM)		+= slram.o
+obj-$(CONFIG_MTD_PHRAM)		+= phram.o
 obj-$(CONFIG_MTD_PMC551)	+= pmc551.o
 obj-$(CONFIG_MTD_MS02NV)	+= ms02-nv.o
 obj-$(CONFIG_MTD_MTDRAM)	+= mtdram.o
 obj-$(CONFIG_MTD_LART)		+= lart.o
 obj-$(CONFIG_MTD_BLKMTD)	+= blkmtd.o
 
-include $(TOPDIR)/Rules.make
+-include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/drivers/mtd/devices/ms02-nv.c linux-2.4.30-linux-mips.org/drivers/mtd/devices/ms02-nv.c
--- linux-2.4.30/drivers/mtd/devices/ms02-nv.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/ms02-nv.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,12 +1,12 @@
 /*
- *      Copyright (c) 2001 Maciej W. Rozycki
+ *	Copyright (c) 2001 Maciej W. Rozycki
  *
- *      This program is free software; you can redistribute it and/or
- *      modify it under the terms of the GNU General Public License
- *      as published by the Free Software Foundation; either version
- *      2 of the License, or (at your option) any later version.
+ *	This program is 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.
  *
- *	$Id: ms02-nv.c,v 1.2 2003/01/24 14:05:17 dwmw2 Exp $
+ *	$Id: ms02-nv.c,v 1.6 2003/08/19 09:25:36 dwmw2 Exp $
  */
 
 #include <linux/init.h>
@@ -29,7 +29,7 @@
 
 
 static char version[] __initdata =
-        "ms02-nv.c: v.1.0.0  13 Aug 2001  Maciej W. Rozycki.\n";
+	"ms02-nv.c: v.1.0.0  13 Aug 2001  Maciej W. Rozycki.\n";
 
 MODULE_AUTHOR("Maciej W. Rozycki <macro@ds2.pg.gda.pl>");
 MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver");
@@ -38,9 +38,9 @@
 
 /*
  * Addresses we probe for an MS02-NV at.  Modules may be located
- * at any 8MB boundary within a 0MB up to 112MB range or at any 32MB
- * boundary within a 0MB up to 448MB range.  We don't support a module
- * at 0MB, though.
+ * at any 8MiB boundary within a 0MiB up to 112MiB range or at any 32MiB
+ * boundary within a 0MiB up to 448MiB range.  We don't support a module
+ * at 0MiB, though.
  */
 static ulong ms02nv_addrs[] __initdata = {
 	0x07000000, 0x06800000, 0x06000000, 0x05800000, 0x05000000,
@@ -130,7 +130,7 @@
 
 	int ret = -ENODEV;
 
-	/* The module decodes 8MB of address space. */
+	/* The module decodes 8MiB of address space. */
 	mod_res = kmalloc(sizeof(*mod_res), GFP_KERNEL);
 	if (!mod_res)
 		return -ENOMEM;
@@ -222,7 +222,7 @@
 	mtd->flags = MTD_CAP_RAM | MTD_XIP;
 	mtd->size = fixsize;
 	mtd->name = (char *)ms02nv_name;
-	mtd->module = THIS_MODULE;
+	mtd->owner = THIS_MODULE;
 	mtd->read = ms02nv_read;
 	mtd->write = ms02nv_write;
 
@@ -233,7 +233,7 @@
 		goto err_out_csr_res;
 	}
 
-	printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMB.\n",
+	printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMiB.\n",
 		mtd->index, ms02nv_name, addr, size >> 20);
 
 	mp->next = root_ms02nv_mtd;
@@ -293,12 +293,12 @@
 
 	switch (mips_machtype) {
 	case MACH_DS5000_200:
-		csr = (volatile u32 *)KN02_CSR_ADDR;
+		csr = (volatile u32 *)KN02_CSR_BASE;
 		if (*csr & KN02_CSR_BNK32M)
 			stride = 2;
 		break;
 	case MACH_DS5000_2X0:
-	case MACH_DS5000:
+	case MACH_DS5900:
 		csr = (volatile u32 *)KN03_MCR_BASE;
 		if (*csr & KN03_MCR_BNK32M)
 			stride = 2;
diff -Nur linux-2.4.30/drivers/mtd/devices/ms02-nv.h linux-2.4.30-linux-mips.org/drivers/mtd/devices/ms02-nv.h
--- linux-2.4.30/drivers/mtd/devices/ms02-nv.h	2002-11-29 00:53:13.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/ms02-nv.h	2005-05-12 18:01:29.000000000 +0200
@@ -1,32 +1,96 @@
 /*
- *      Copyright (c) 2001 Maciej W. Rozycki
+ *	Copyright (c) 2001, 2003  Maciej W. Rozycki
  *
- *      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.
+ *	DEC MS02-NV (54-20948-01) battery backed-up NVRAM module for
+ *	DECstation/DECsystem 5000/2x0 and DECsystem 5900 and 5900/260
+ *	systems.
+ *
+ *	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.
+ *
+ *	$Id: ms02-nv.h,v 1.3 2003/08/19 09:25:36 dwmw2 Exp $
  */
 
 #include <linux/ioport.h>
 #include <linux/mtd/mtd.h>
 
+/*
+ * Addresses are decoded as follows:
+ *
+ * 0x000000 - 0x3fffff	SRAM
+ * 0x400000 - 0x7fffff	CSR
+ *
+ * Within the SRAM area the following ranges are forced by the system
+ * firmware:
+ *
+ * 0x000000 - 0x0003ff	diagnostic area, destroyed upon a reboot
+ * 0x000400 - ENDofRAM	storage area, available to operating systems
+ *
+ * but we can't really use the available area right from 0x000400 as
+ * the first word is used by the firmware as a status flag passed
+ * from an operating system.  If anything but the valid data magic
+ * ID value is found, the firmware considers the SRAM clean, i.e.
+ * containing no valid data, and disables the battery resulting in
+ * data being erased as soon as power is switched off.  So the choice
+ * for the start address of the user-available is 0x001000 which is
+ * nicely page aligned.  The area between 0x000404 and 0x000fff may
+ * be used by the driver for own needs.
+ *
+ * The diagnostic area defines two status words to be read by an
+ * operating system, a magic ID to distinguish a MS02-NV board from
+ * anything else and a status information providing results of tests
+ * as well as the size of SRAM available, which can be 1MiB or 2MiB
+ * (that's what the firmware handles; no idea if 2MiB modules ever
+ * existed).
+ *
+ * The firmware only handles the MS02-NV board if installed in the
+ * last (15th) slot, so for any other location the status information
+ * stored in the SRAM cannot be relied upon.  But from the hardware
+ * point of view there is no problem using up to 14 such boards in a
+ * system -- only the 1st slot needs to be filled with a DRAM module.
+ * The MS02-NV board is ECC-protected, like other MS02 memory boards.
+ *
+ * The state of the battery as provided by the CSR is reflected on
+ * the two onboard LEDs.  When facing the battery side of the board,
+ * with the LEDs at the top left and the battery at the bottom right
+ * (i.e. looking from the back side of the system box), their meaning
+ * is as follows (the system has to be powered on):
+ *
+ * left LED		battery disable status: lit = enabled
+ * right LED		battery condition status: lit = OK
+ */
+
 /* MS02-NV iomem register offsets. */
 #define MS02NV_CSR		0x400000	/* control & status register */
 
+/* MS02-NV CSR status bits. */
+#define MS02NV_CSR_BATT_OK	0x01		/* battery OK */
+#define MS02NV_CSR_BATT_OFF	0x02		/* battery disabled */
+
+
 /* MS02-NV memory offsets. */
 #define MS02NV_DIAG		0x0003f8	/* diagnostic status */
 #define MS02NV_MAGIC		0x0003fc	/* MS02-NV magic ID */
-#define MS02NV_RAM		0x000400	/* general-purpose RAM start */
+#define MS02NV_VALID		0x000400	/* valid data magic ID */
+#define MS02NV_RAM		0x001000	/* user-exposed RAM start */
 
-/* MS02-NV diagnostic status constants. */
-#define MS02NV_DIAG_SIZE_MASK	0xf0		/* RAM size mask */
-#define MS02NV_DIAG_SIZE_SHIFT	0x10		/* RAM size shift (left) */
+/* MS02-NV diagnostic status bits. */
+#define MS02NV_DIAG_TEST	0x01		/* SRAM test done (?) */
+#define MS02NV_DIAG_RO		0x02		/* SRAM r/o test done */
+#define MS02NV_DIAG_RW		0x04		/* SRAM r/w test done */
+#define MS02NV_DIAG_FAIL	0x08		/* SRAM test failed */
+#define MS02NV_DIAG_SIZE_MASK	0xf0		/* SRAM size mask */
+#define MS02NV_DIAG_SIZE_SHIFT	0x10		/* SRAM size shift (left) */
 
 /* MS02-NV general constants. */
 #define MS02NV_ID		0x03021966	/* MS02-NV magic ID value */
+#define MS02NV_VALID_ID		0xbd100248	/* valid data magic ID value */
 #define MS02NV_SLOT_SIZE	0x800000	/* size of the address space
 						   decoded by the module */
 
+
 typedef volatile u32 ms02nv_uint;
 
 struct ms02nv_private {
diff -Nur linux-2.4.30/drivers/mtd/devices/mtdram.c linux-2.4.30-linux-mips.org/drivers/mtd/devices/mtdram.c
--- linux-2.4.30/drivers/mtd/devices/mtdram.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/mtdram.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,6 +1,6 @@
 /*
  * mtdram - a test mtd device
- * $Id: mtdram.c,v 1.29 2002/10/21 13:40:06 jocke Exp $
+ * $Id: mtdram.c,v 1.32 2003/05/21 15:15:07 dwmw2 Exp $
  * Author: Alexander Larsson <alex@cendio.se>
  *
  * Copyright (c) 1999 Alexander Larsson <alex@cendio.se>
@@ -13,6 +13,8 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/ioport.h>
+#include <linux/vmalloc.h>
+#include <linux/init.h>
 #include <linux/mtd/compatmac.h>
 #include <linux/mtd/mtd.h>
 
@@ -136,7 +138,7 @@
    mtd->erasesize = MTDRAM_ERASE_SIZE;
    mtd->priv = mapped_address;
 
-   mtd->module = THIS_MODULE;
+   mtd->owner = THIS_MODULE;
    mtd->erase = ram_erase;
    mtd->point = ram_point;
    mtd->unpoint = ram_unpoint;
diff -Nur linux-2.4.30/drivers/mtd/devices/phram.c linux-2.4.30-linux-mips.org/drivers/mtd/devices/phram.c
--- linux-2.4.30/drivers/mtd/devices/phram.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/phram.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,362 @@
+/**
+ *
+ * $Id: phram.c,v 1.1 2003/08/21 17:52:30 joern Exp $
+ *
+ * Copyright (c) Jochen Schaeuble <psionic@psionic.de>
+ * 07/2003	rewritten by Joern Engel <joern@wh.fh-wedel.de>
+ *
+ * DISCLAIMER:  This driver makes use of Rusty's excellent module code,
+ * so it will not work for 2.4 without changes and it wont work for 2.4
+ * as a module without major changes.  Oh well!
+ *
+ * Usage:
+ *
+ * one commend line parameter per device, each in the form:
+ *   phram=<name>,<start>,<len>
+ * <name> may be up to 63 characters.
+ * <start> and <len> can be octal, decimal or hexadecimal.  If followed
+ * by "k", "M" or "G", the numbers will be interpreted as kilo, mega or
+ * gigabytes.
+ *
+ */
+
+#include <asm/io.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/mtd/mtd.h>
+
+#define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args)
+
+struct phram_mtd_list {
+	struct list_head list;
+	struct mtd_info *mtdinfo;
+};
+
+static LIST_HEAD(phram_list);
+
+
+
+int phram_erase(struct mtd_info *mtd, struct erase_info *instr)
+{
+	u_char *start = (u_char *)mtd->priv;
+
+	if (instr->addr + instr->len > mtd->size)
+		return -EINVAL;
+	
+	memset(start + instr->addr, 0xff, instr->len);
+
+	/* This'll catch a few races. Free the thing before returning :) 
+	 * I don't feel at all ashamed. This kind of thing is possible anyway
+	 * with flash, but unlikely.
+	 */
+
+	instr->state = MTD_ERASE_DONE;
+
+	if (instr->callback)
+		(*(instr->callback))(instr);
+	else
+		kfree(instr);
+
+	return 0;
+}
+
+int phram_point(struct mtd_info *mtd, loff_t from, size_t len,
+		size_t *retlen, u_char **mtdbuf)
+{
+	u_char *start = (u_char *)mtd->priv;
+
+	if (from + len > mtd->size)
+		return -EINVAL;
+	
+	*mtdbuf = start + from;
+	*retlen = len;
+	return 0;
+}
+
+void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len)
+{
+}
+
+int phram_read(struct mtd_info *mtd, loff_t from, size_t len,
+		size_t *retlen, u_char *buf)
+{
+	u_char *start = (u_char *)mtd->priv;
+
+	if (from + len > mtd->size)
+		return -EINVAL;
+	
+	memcpy(buf, start + from, len);
+
+	*retlen = len;
+	return 0;
+}
+
+int phram_write(struct mtd_info *mtd, loff_t to, size_t len,
+		size_t *retlen, const u_char *buf)
+{
+	u_char *start = (u_char *)mtd->priv;
+
+	if (to + len > mtd->size)
+		return -EINVAL;
+	
+	memcpy(start + to, buf, len);
+
+	*retlen = len;
+	return 0;
+}
+
+
+
+static void unregister_devices(void)
+{
+	struct phram_mtd_list *this;
+
+	list_for_each_entry(this, &phram_list, list) {
+		del_mtd_device(this->mtdinfo);
+		iounmap(this->mtdinfo->priv);
+		kfree(this->mtdinfo);
+		kfree(this);
+	}
+}
+
+static int register_device(char *name, unsigned long start, unsigned long len)
+{
+	struct phram_mtd_list *new;
+	int ret = -ENOMEM;
+
+	new = kmalloc(sizeof(*new), GFP_KERNEL);
+	if (!new)
+		goto out0;
+
+	new->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
+	if (!new->mtdinfo)
+		goto out1;
+	
+	memset(new->mtdinfo, 0, sizeof(struct mtd_info));
+
+	ret = -EIO;
+	new->mtdinfo->priv = ioremap(start, len);
+	if (!new->mtdinfo->priv) {
+		ERROR("ioremap failed\n");
+		goto out2;
+	}
+
+
+	new->mtdinfo->name = name;
+	new->mtdinfo->size = len;
+	new->mtdinfo->flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE;
+        new->mtdinfo->erase = phram_erase;
+	new->mtdinfo->point = phram_point;
+	new->mtdinfo->unpoint = phram_unpoint;
+	new->mtdinfo->read = phram_read;
+	new->mtdinfo->write = phram_write;
+	new->mtdinfo->owner = THIS_MODULE;
+	new->mtdinfo->type = MTD_RAM;
+	new->mtdinfo->erasesize = 0x0;
+
+	ret = -EAGAIN;
+	if (add_mtd_device(new->mtdinfo)) {
+		ERROR("Failed to register new device\n");
+		goto out3;
+	}
+
+	list_add_tail(&new->list, &phram_list);
+	return 0;	
+
+out3:
+	iounmap(new->mtdinfo->priv);
+out2:
+	kfree(new->mtdinfo);
+out1:
+	kfree(new);
+out0:
+	return ret;
+}
+
+static int ustrtoul(const char *cp, char **endp, unsigned int base)
+{
+	unsigned long result = simple_strtoul(cp, endp, base);
+
+	switch (**endp) {
+	case 'G':
+		result *= 1024;
+	case 'M':
+		result *= 1024;
+	case 'k':
+		result *= 1024;
+		endp++;
+	}
+	return result;
+}
+
+static int parse_num32(uint32_t *num32, const char *token)
+{
+	char *endp;
+	unsigned long n;
+
+	n = ustrtoul(token, &endp, 0);
+	if (*endp)
+		return -EINVAL;
+
+	*num32 = n;
+	return 0;
+}
+
+static int parse_name(char **pname, const char *token)
+{
+	size_t len;
+	char *name;
+
+	len = strlen(token) + 1;
+	if (len > 64)
+		return -ENOSPC;
+
+	name = kmalloc(len, GFP_KERNEL);
+	if (!name)
+		return -ENOMEM;
+
+	strcpy(name, token);
+
+	*pname = name;
+	return 0;
+}
+
+#define parse_err(fmt, args...) do {	\
+	ERROR(fmt , ## args);	\
+	return 0;		\
+} while (0)
+
+static int phram_setup(const char *val, struct kernel_param *kp)
+{
+	char buf[64+12+12], *str = buf;
+	char *token[3];
+	char *name;
+	uint32_t start;
+	uint32_t len;
+	int i, ret;
+
+	if (strnlen(val, sizeof(str)) >= sizeof(str))
+		parse_err("parameter too long\n");
+
+	strcpy(str, val);
+
+	for (i=0; i<3; i++)
+		token[i] = strsep(&str, ",");
+
+	if (str)
+		parse_err("too many arguments\n");
+
+	if (!token[2])
+		parse_err("not enough arguments\n");
+
+	ret = parse_name(&name, token[0]);
+	if (ret == -ENOMEM)
+		parse_err("out of memory\n");
+	if (ret == -ENOSPC)
+		parse_err("name too long\n");
+	if (ret)
+		return 0;
+
+	ret = parse_num32(&start, token[1]);
+	if (ret)
+		parse_err("illegal start address\n");
+
+	ret = parse_num32(&len, token[2]);
+	if (ret)
+		parse_err("illegal device length\n");
+
+	register_device(name, start, len);
+
+	return 0;
+}
+
+module_param_call(phram, phram_setup, NULL, NULL, 000);
+MODULE_PARM_DESC(phram, "Memory region to map. \"map=<name>,<start><length>\"");
+
+/*
+ * Just for compatibility with slram, this is horrible and should go someday.
+ */
+static int __init slram_setup(const char *val, struct kernel_param *kp)
+{
+	char buf[256], *str = buf;
+
+	if (!val || !val[0])
+		parse_err("no arguments to \"slram=\"\n");
+
+	if (strnlen(val, sizeof(str)) >= sizeof(str))
+		parse_err("parameter too long\n");
+
+	strcpy(str, val);
+
+	while (str) {
+		char *token[3];
+		char *name;
+		uint32_t start;
+		uint32_t len;
+		int i, ret;
+
+		for (i=0; i<3; i++) {
+			token[i] = strsep(&str, ",");
+			if (token[i])
+				continue;
+			parse_err("wrong number of arguments to \"slram=\"\n");
+		}
+
+		/* name */
+		ret = parse_name(&name, token[0]);
+		if (ret == -ENOMEM)
+			parse_err("of memory\n");
+		if (ret == -ENOSPC)
+			parse_err("too long\n");
+		if (ret)
+			return 1;
+
+		/* start */
+		ret = parse_num32(&start, token[1]);
+		if (ret)
+			parse_err("illegal start address\n");
+
+		/* len */
+		if (token[2][0] == '+')
+			ret = parse_num32(&len, token[2] + 1);
+		else
+			ret = parse_num32(&len, token[2]);
+
+		if (ret)
+			parse_err("illegal device length\n");
+
+		if (token[2][0] != '+') {
+			if (len < start)
+				parse_err("end < start\n");
+			len -= start;
+		}
+
+		register_device(name, start, len);
+	}
+	return 1;
+}
+
+module_param_call(slram, slram_setup, NULL, NULL, 000);
+MODULE_PARM_DESC(slram, "List of memory regions to map. \"map=<name>,<start><length/end>\"");
+
+
+int __init init_phram(void)
+{
+	printk(KERN_ERR "phram loaded\n");
+	return 0;
+}
+
+static void __exit cleanup_phram(void)
+{
+	unregister_devices();
+}
+
+module_init(init_phram);
+module_exit(cleanup_phram);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Jörn Engel <joern@wh.fh-wedel.de>");
+MODULE_DESCRIPTION("MTD driver for physical RAM");
diff -Nur linux-2.4.30/drivers/mtd/devices/pmc551.c linux-2.4.30-linux-mips.org/drivers/mtd/devices/pmc551.c
--- linux-2.4.30/drivers/mtd/devices/pmc551.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/pmc551.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: pmc551.c,v 1.22 2003/01/24 13:34:30 dwmw2 Exp $
+ * $Id: pmc551.c,v 1.25 2003/06/23 12:24:01 dwmw2 Exp $
  *
  * PMC551 PCI Mezzanine Ram Device
  *
@@ -681,11 +681,6 @@
 
         printk(KERN_INFO PMC551_VERSION);
 
-        if(!pci_present()) {
-                printk(KERN_NOTICE "pmc551: PCI not enabled.\n");
-                return -ENODEV;
-        }
-
         /*
          * PCU-bus chipset probe.
          */
@@ -787,10 +782,10 @@
                 mtd->write 	= pmc551_write;
                 mtd->point 	= pmc551_point;
                 mtd->unpoint 	= pmc551_unpoint;
-                mtd->module 	= THIS_MODULE;
                 mtd->type 	= MTD_RAM;
                 mtd->name 	= "PMC551 RAM board";
                 mtd->erasesize 	= 0x10000;
+		mtd->owner = THIS_MODULE;
 
                 if (add_mtd_device(mtd)) {
                         printk(KERN_NOTICE "pmc551: Failed to register new device\n");
diff -Nur linux-2.4.30/drivers/mtd/devices/slram.c linux-2.4.30-linux-mips.org/drivers/mtd/devices/slram.c
--- linux-2.4.30/drivers/mtd/devices/slram.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/devices/slram.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,6 +1,6 @@
 /*======================================================================
 
-  $Id: slram.c,v 1.28 2003/01/24 13:35:34 dwmw2 Exp $
+  $Id: slram.c,v 1.30 2003/05/20 21:03:08 dwmw2 Exp $
 
   This driver provides a method to access memory not used by the kernel
   itself (i.e. if the kernel commandline mem=xxx is used). To actually
@@ -199,7 +199,7 @@
 	(*curmtd)->mtdinfo->unpoint = slram_unpoint;
 	(*curmtd)->mtdinfo->read = slram_read;
 	(*curmtd)->mtdinfo->write = slram_write;
-	(*curmtd)->mtdinfo->module = THIS_MODULE;
+	(*curmtd)->mtdinfo->owner = THIS_MODULE;
 	(*curmtd)->mtdinfo->type = MTD_RAM;
 	(*curmtd)->mtdinfo->erasesize = 0x0;
 
diff -Nur linux-2.4.30/drivers/mtd/ftl.c linux-2.4.30-linux-mips.org/drivers/mtd/ftl.c
--- linux-2.4.30/drivers/mtd/ftl.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/ftl.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /* This version ported to the Linux-MTD system by dwmw2@infradead.org
- * $Id: ftl.c,v 1.45 2003/01/24 23:31:27 dwmw2 Exp $
+ * $Id: ftl.c,v 1.52 2003/08/11 09:00:44 dwmw2 Exp $
  *
  * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br>
  * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups
@@ -55,8 +55,8 @@
     contact M-Systems (http://www.m-sys.com) directly.
       
 ======================================================================*/
+#include <linux/mtd/blktrans.h>
 #include <linux/module.h>
-#include <linux/mtd/compatmac.h>
 #include <linux/mtd/mtd.h>
 /*#define PSYCHO_DEBUG */
 
@@ -68,43 +68,13 @@
 #include <linux/timer.h>
 #include <linux/major.h>
 #include <linux/fs.h>
-#include <linux/ioctl.h>
+#include <linux/init.h>
 #include <linux/hdreg.h>
-
-#if (LINUX_VERSION_CODE >= 0x20100)
 #include <linux/vmalloc.h>
-#endif
-#if (LINUX_VERSION_CODE >= 0x20303)
 #include <linux/blkpg.h>
-#endif
+#include <asm/uaccess.h>
 
 #include <linux/mtd/ftl.h>
-/*====================================================================*/
-/* Stuff which really ought to be in compatmac.h */
-
-#if (LINUX_VERSION_CODE < 0x20328)
-#define register_disk(dev, drive, minors, ops, size) \
-    do { (dev)->part[(drive)*(minors)].nr_sects = size; \
-        if (size == 0) (dev)->part[(drive)*(minors)].start_sect = -1; \
-        resetup_one_dev(dev, drive); } while (0)
-#endif
-
-#if (LINUX_VERSION_CODE < 0x20320)
-#define BLK_DEFAULT_QUEUE(n)    blk_dev[n].request_fn
-#define blk_init_queue(q, req)  q = (req)
-#define blk_cleanup_queue(q)    q = NULL
-#define request_arg_t           void
-#else
-#define request_arg_t           request_queue_t *q
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
-#else
-#define BLK_INC_USE_COUNT do {} while(0)
-#define BLK_DEC_USE_COUNT do {} while(0)
-#endif
 
 /*====================================================================*/
 
@@ -119,19 +89,6 @@
 #define FTL_MAJOR	44
 #endif
 
-/* Funky stuff for setting up a block device */
-#define MAJOR_NR		FTL_MAJOR
-#define DEVICE_NAME		"ftl"
-#define DEVICE_REQUEST		do_ftl_request
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-
-#define DEVICE_NR(minor)	((minor)>>5)
-#define REGION_NR(minor)	(((minor)>>3)&3)
-#define PART_NR(minor)		((minor)&7)
-#define MINOR_NR(dev,reg,part)	(((dev)<<5)+((reg)<<3)+(part))
-
-#include <linux/blk.h>
 
 /*====================================================================*/
 
@@ -142,8 +99,7 @@
 #define MAX_REGION	4
 
 /* Maximum number of partitions in an FTL region */
-#define PART_BITS	3
-#define MAX_PART	8
+#define PART_BITS	4
 
 /* Maximum number of outstanding erase requests per socket */
 #define MAX_ERASE	8
@@ -154,7 +110,7 @@
 
 /* Each memory region corresponds to a minor device */
 typedef struct partition_t {
-    struct mtd_info	*mtd;
+    struct mtd_blktrans_dev mbd;
     u_int32_t		state;
     u_int32_t		*VirtualBlockMap;
     u_int32_t		*VirtualPageMap;
@@ -179,21 +135,10 @@
     region_info_t	region;
     memory_handle_t	handle;
 #endif
-    atomic_t		open;
 } partition_t;
 
-partition_t *myparts[MAX_MTD_DEVICES];
-
-static void ftl_notify_add(struct mtd_info *mtd);
-static void ftl_notify_remove(struct mtd_info *mtd);
-
 void ftl_freepart(partition_t *part);
 
-static struct mtd_notifier ftl_notifier = {
-	add:	ftl_notify_add,
-	remove:	ftl_notify_remove,
-};
-
 /* Partition state flags */
 #define FTL_FORMATTED	0x01
 
@@ -204,51 +149,11 @@
 #define XFER_PREPARED	0x03
 #define XFER_FAILED	0x04
 
-static struct hd_struct ftl_hd[MINOR_NR(MAX_DEV, 0, 0)];
-static int ftl_sizes[MINOR_NR(MAX_DEV, 0, 0)];
-static int ftl_blocksizes[MINOR_NR(MAX_DEV, 0, 0)];
-
-static struct gendisk ftl_gendisk = {
-    major:		FTL_MAJOR,
-    major_name:		"ftl",
-    minor_shift:	PART_BITS,
-    max_p:		MAX_PART,
-#if (LINUX_VERSION_CODE < 0x20328)
-    max_nr:		MAX_DEV*MAX_PART,
-#endif
-    part:		ftl_hd,
-    sizes:		ftl_sizes,
-};
-
 /*====================================================================*/
 
-static int ftl_ioctl(struct inode *inode, struct file *file,
-		     u_int cmd, u_long arg);
-static int ftl_open(struct inode *inode, struct file *file);
-static release_t ftl_close(struct inode *inode, struct file *file);
-static int ftl_reread_partitions(int minor);
 
 static void ftl_erase_callback(struct erase_info *done);
 
-#if LINUX_VERSION_CODE < 0x20326
-static struct file_operations ftl_blk_fops = {
-    open:	ftl_open,
-    release:	ftl_close,
-    ioctl:	ftl_ioctl,
-    read:	block_read,
-    write:	block_write,
-    fsync:	block_fsync
-};
-#else
-static struct block_device_operations ftl_blk_fops = {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
-    owner:	THIS_MODULE,
-#endif
-    open:	ftl_open,
-    release:	ftl_close,
-    ioctl:	ftl_ioctl,
-};
-#endif
 
 /*======================================================================
 
@@ -264,13 +169,13 @@
     loff_t offset, max_offset;
     int ret;
     part->header.FormattedSize = 0;
-    max_offset = (0x100000<part->mtd->size)?0x100000:part->mtd->size;
+    max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size;
     /* Search first megabyte for a valid FTL header */
     for (offset = 0;
 	 (offset + sizeof(header)) < max_offset;
-	 offset += part->mtd->erasesize ? : 0x2000) {
+	 offset += part->mbd.mtd->erasesize ? : 0x2000) {
 
-	ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret, 
+	ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &ret, 
 			      (unsigned char *)&header);
 	
 	if (ret) 
@@ -283,15 +188,15 @@
 	printk(KERN_NOTICE "ftl_cs: FTL header not found.\n");
 	return -ENOENT;
     }
-    if ((le16_to_cpu(header.NumEraseUnits) > 65536) || header.BlockSize != 9 ||
+    if (header.BlockSize != 9 ||
 	(header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) ||
 	(header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) {
 	printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n");
 	return -1;
     }
-    if ((1 << header.EraseUnitSize) != part->mtd->erasesize) {
+    if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) {
 	printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n",
-	       1 << header.EraseUnitSize,part->mtd->erasesize);
+	       1 << header.EraseUnitSize,part->mbd.mtd->erasesize);
 	return -1;
     }
     part->header = header;
@@ -326,7 +231,7 @@
     for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) {
 	offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN))
 		      << part->header.EraseUnitSize);
-	ret = part->mtd->read(part->mtd, offset, sizeof(header), &retval, 
+	ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &retval, 
 			      (unsigned char *)&header);
 	
 	if (ret) 
@@ -391,7 +296,7 @@
 	part->EUNInfo[i].Deleted = 0;
 	offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset);
 	
-	ret = part->mtd->read(part->mtd, offset,  
+	ret = part->mbd.mtd->read(part->mbd.mtd, offset,  
 			      part->BlocksPerUnit * sizeof(u_int32_t), &retval, 
 			      (unsigned char *)part->bam_cache);
 	
@@ -456,7 +361,7 @@
     erase->len = 1 << part->header.EraseUnitSize;
     erase->priv = (u_long)part;
     
-    ret = part->mtd->erase(part->mtd, erase);
+    ret = part->mbd.mtd->erase(part->mbd.mtd, erase);
 
     if (!ret)
 	    xfer->EraseCount++;
@@ -523,7 +428,7 @@
     header.LogicalEUN = cpu_to_le16(0xffff);
     header.EraseCount = cpu_to_le32(xfer->EraseCount);
 
-    ret = part->mtd->write(part->mtd, xfer->Offset, sizeof(header),
+    ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset, sizeof(header),
 			   &retlen, (u_char *)&header);
 
     if (ret) {
@@ -539,7 +444,7 @@
 
     for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) {
 
-	ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t), 
+	ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t), 
 			       &retlen, (u_char *)&ctl);
 
 	if (ret)
@@ -586,7 +491,7 @@
 
 	offset = eun->Offset + le32_to_cpu(part->header.BAMOffset);
 
-	ret = part->mtd->read(part->mtd, offset, 
+	ret = part->mbd.mtd->read(part->mbd.mtd, offset, 
 			      part->BlocksPerUnit * sizeof(u_int32_t),
 			      &retlen, (u_char *) (part->bam_cache));
 
@@ -604,7 +509,7 @@
     offset = xfer->Offset + 20; /* Bad! */
     unit = cpu_to_le16(0x7fff);
 
-    ret = part->mtd->write(part->mtd, offset, sizeof(u_int16_t),
+    ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int16_t),
 			   &retlen, (u_char *) &unit);
     
     if (ret) {
@@ -624,7 +529,7 @@
 	    break;
 	case BLOCK_DATA:
 	case BLOCK_REPLACEMENT:
-	    ret = part->mtd->read(part->mtd, src, SECTOR_SIZE,
+	    ret = part->mbd.mtd->read(part->mbd.mtd, src, SECTOR_SIZE,
                         &retlen, (u_char *) buf);
 	    if (ret) {
 		printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n");
@@ -632,7 +537,7 @@
             }
 
 
-	    ret = part->mtd->write(part->mtd, dest, SECTOR_SIZE,
+	    ret = part->mbd.mtd->write(part->mbd.mtd, dest, SECTOR_SIZE,
                         &retlen, (u_char *) buf);
 	    if (ret)  {
 		printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n");
@@ -651,7 +556,7 @@
     }
 
     /* Write the BAM to the transfer unit */
-    ret = part->mtd->write(part->mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset), 
+    ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset), 
                     part->BlocksPerUnit * sizeof(int32_t), &retlen, 
 		    (u_char *)part->bam_cache);
     if (ret) {
@@ -661,7 +566,7 @@
 
     
     /* All clear? Then update the LogicalEUN again */
-    ret = part->mtd->write(part->mtd, xfer->Offset + 20, sizeof(u_int16_t),
+    ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(u_int16_t),
 			   &retlen, (u_char *)&srcunitswap);
 
     if (ret) {
@@ -749,8 +654,8 @@
 	    if (queued) {
 		DEBUG(1, "ftl_cs: waiting for transfer "
 		      "unit to be prepared...\n");
-		if (part->mtd->sync)
-			part->mtd->sync(part->mtd);
+		if (part->mbd.mtd->sync)
+			part->mbd.mtd->sync(part->mbd.mtd);
 	    } else {
 		static int ne = 0;
 		if (++ne < 5)
@@ -848,7 +753,7 @@
 	/* Invalidate cache */
 	part->bam_index = 0xffff;
 
-	ret = part->mtd->read(part->mtd, 
+	ret = part->mbd.mtd->read(part->mbd.mtd, 
 		       part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset),
 		       part->BlocksPerUnit * sizeof(u_int32_t),
 		       &retlen, (u_char *) (part->bam_cache));
@@ -877,78 +782,6 @@
     
 } /* find_free */
 
-/*======================================================================
-
-    This gets a memory handle for the region corresponding to the
-    minor device number.
-    
-======================================================================*/
-
-static int ftl_open(struct inode *inode, struct file *file)
-{
-    int minor = MINOR(inode->i_rdev);
-    partition_t *partition;
-
-    if (minor>>4 >= MAX_MTD_DEVICES)
-	return -ENODEV;
-
-    partition = myparts[minor>>4];
-
-    if (!partition)
-	return -ENODEV;
-
-    if (partition->state != FTL_FORMATTED)
-	return -ENXIO;
-    
-    if (ftl_gendisk.part[minor].nr_sects == 0)
-	return -ENXIO;
-
-    BLK_INC_USE_COUNT;
-
-    if (!get_mtd_device(partition->mtd, -1)) {
-	    BLK_DEC_USE_COUNT;
-	    return -ENXIO;
-    }
-    
-    if ((file->f_mode & 2) && !(partition->mtd->flags & MTD_CLEAR_BITS) ) {
-	    put_mtd_device(partition->mtd);
-	    BLK_DEC_USE_COUNT;
-            return -EROFS;
-    }
-    
-    DEBUG(0, "ftl_cs: ftl_open(%d)\n", minor);
-
-    atomic_inc(&partition->open);
-
-    return 0;
-}
-
-/*====================================================================*/
-
-static release_t ftl_close(struct inode *inode, struct file *file)
-{
-    int minor = MINOR(inode->i_rdev);
-    partition_t *part = myparts[minor >> 4];
-    int i;
-    
-    DEBUG(0, "ftl_cs: ftl_close(%d)\n", minor);
-
-    /* Wait for any pending erase operations to complete */
-    if (part->mtd->sync)
-	    part->mtd->sync(part->mtd);
-    
-    for (i = 0; i < part->header.NumTransferUnits; i++) {
-	if (part->XferInfo[i].state == XFER_ERASED)
-	    prepare_xfer(part, i);
-    }
-
-    atomic_dec(&part->open);
-
-    put_mtd_device(part->mtd);
-    BLK_DEC_USE_COUNT;
-    release_return(0);
-} /* ftl_close */
-
 
 /*======================================================================
 
@@ -983,7 +816,7 @@
 	else {
 	    offset = (part->EUNInfo[log_addr / bsize].Offset
 			  + (log_addr % bsize));
-	    ret = part->mtd->read(part->mtd, offset, SECTOR_SIZE,
+	    ret = part->mbd.mtd->read(part->mbd.mtd, offset, SECTOR_SIZE,
 			   &retlen, (u_char *) buffer);
 
 	    if (ret) {
@@ -1022,7 +855,7 @@
 		  le32_to_cpu(part->header.BAMOffset));
     
 #ifdef PSYCHO_DEBUG
-    ret = part->mtd->read(part->mtd, offset, sizeof(u_int32_t),
+    ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(u_int32_t),
                         &retlen, (u_char *)&old_addr);
     if (ret) {
 	printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret);
@@ -1059,7 +892,7 @@
 #endif
 	part->bam_cache[blk] = le_virt_addr;
     }
-    ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t),
+    ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t),
                             &retlen, (u_char *)&le_virt_addr);
 
     if (ret) {
@@ -1119,7 +952,7 @@
 	part->EUNInfo[part->bam_index].Deleted++;
 	offset = (part->EUNInfo[part->bam_index].Offset +
 		      blk * SECTOR_SIZE);
-	ret = part->mtd->write(part->mtd, offset, SECTOR_SIZE, &retlen, 
+	ret = part->mbd.mtd->write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, 
                                      buffer);
 
 	if (ret) {
@@ -1151,164 +984,32 @@
     return 0;
 } /* ftl_write */
 
-/*======================================================================
-
-    IOCTL calls for getting device parameters.
-
-======================================================================*/
-
-static int ftl_ioctl(struct inode *inode, struct file *file,
-		     u_int cmd, u_long arg)
+static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
 {
-    struct hd_geometry *geo = (struct hd_geometry *)arg;
-    int ret = 0, minor = MINOR(inode->i_rdev);
-    partition_t *part= myparts[minor >> 4];
-    u_long sect;
-
-    if (!part)
-	return -ENODEV; /* How? */
-
-    switch (cmd) {
-    case HDIO_GETGEO:
-	ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo));
-	if (ret) return ret;
-	/* Sort of arbitrary: round size down to 4K boundary */
-	sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
-	put_user(1, (char *)&geo->heads);
-	put_user(8, (char *)&geo->sectors);
-	put_user((sect>>3), (short *)&geo->cylinders);
-	put_user(ftl_hd[minor].start_sect, (u_long *)&geo->start);
-	break;
-    case BLKGETSIZE:
-	ret = put_user(ftl_hd[minor].nr_sects, (unsigned long *)arg);
-	break;
-#ifdef BLKGETSIZE64
-    case BLKGETSIZE64:
-	ret = put_user((u64)ftl_hd[minor].nr_sects << 9, (u64 *)arg);
-	break;
-#endif
-    case BLKRRPART:
-	ret = ftl_reread_partitions(minor);
-	break;
-#if (LINUX_VERSION_CODE < 0x20303)
-    case BLKFLSBUF:
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-	if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-#endif
-	fsync_dev(inode->i_rdev);
-	invalidate_buffers(inode->i_rdev);
-	break;
-    RO_IOCTLS(inode->i_rdev, arg);
-#else
-    case BLKROSET:
-    case BLKROGET:
-    case BLKFLSBUF:
-	ret = blk_ioctl(inode->i_rdev, cmd, arg);
-	break;
-#endif
-    default:
-	ret = -EINVAL;
-    }
-
-    return ret;
-} /* ftl_ioctl */
+	partition_t *part = (void *)dev;
+	u_long sect;
 
-/*======================================================================
+	/* Sort of arbitrary: round size down to 4KiB boundary */
+	sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE;
 
-    Handler for block device requests
+	geo->heads = 1;
+	geo->sectors = 8;
+	geo->cylinders = sect >> 3;
 
-======================================================================*/
+	return 0;
+}
 
-static int ftl_reread_partitions(int minor)
+static int ftl_readsect(struct mtd_blktrans_dev *dev,
+			      unsigned long block, char *buf)
 {
-    partition_t *part = myparts[minor >> 4];
-    int i, whole;
-
-    DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor);
-    if ((atomic_read(&part->open) > 1)) {
-	    return -EBUSY;
-    }
-    whole = minor & ~(MAX_PART-1);
-
-    i = MAX_PART - 1;
-    while (i-- > 0) {
-	if (ftl_hd[whole+i].nr_sects > 0) {
-	    kdev_t rdev = MKDEV(FTL_MAJOR, whole+i);
-
-	    invalidate_device(rdev, 1);
-	}
-	ftl_hd[whole+i].start_sect = 0;
-	ftl_hd[whole+i].nr_sects = 0;
-    }
-
-    scan_header(part);
-
-    register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART,
-		  &ftl_blk_fops, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE);
-
-#ifdef PCMCIA_DEBUG
-    for (i = 0; i < MAX_PART; i++) {
-	if (ftl_hd[whole+i].nr_sects > 0)
-	    printk(KERN_INFO "  %d: start %ld size %ld\n", i,
-		   ftl_hd[whole+i].start_sect,
-		   ftl_hd[whole+i].nr_sects);
-    }
-#endif
-    return 0;
+	return ftl_read((void *)dev, buf, block, 1);
 }
 
-/*======================================================================
-
-    Handler for block device requests
-
-======================================================================*/
-
-static void do_ftl_request(request_arg_t)
+static int ftl_writesect(struct mtd_blktrans_dev *dev,
+			      unsigned long block, char *buf)
 {
-    int ret, minor;
-    partition_t *part;
-
-    do {
-      //	    sti();
-	INIT_REQUEST;
-
-	minor = MINOR(CURRENT->rq_dev);
-	
-	part = myparts[minor >> 4];
-	if (part) {
-	  ret = 0;
-	  
-	  switch (CURRENT->cmd) {
-	  case READ:
-	    ret = ftl_read(part, CURRENT->buffer,
-			   CURRENT->sector+ftl_hd[minor].start_sect,
-			   CURRENT->current_nr_sectors);
-	    if (ret) printk("ftl_read returned %d\n", ret);
-	    break;
-	    
-	  case WRITE:
-	    ret = ftl_write(part, CURRENT->buffer,
-			    CURRENT->sector+ftl_hd[minor].start_sect,
-			    CURRENT->current_nr_sectors);
-	    if (ret) printk("ftl_write returned %d\n", ret);
-	    break;
-	    
-	  default:
-	    panic("ftl_cs: unknown block command!\n");
-	    
-	  }
-	} else {
-	  ret = 1;
-	  printk("NULL part in ftl_request\n");
-	}
-	 
-	if (!ret) {
-	  CURRENT->sector += CURRENT->current_nr_sectors;
-	}
-	
-	end_request((ret == 0) ? 1 : 0);
-    } while (1);
-} /* do_ftl_request */
+	return ftl_write((void *)dev, buf, block, 1);
+}
 
 /*====================================================================*/
 
@@ -1337,19 +1038,9 @@
     
 } /* ftl_freepart */
 
-static void ftl_notify_add(struct mtd_info *mtd)
+static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 {
 	partition_t *partition;
-	int device;
-
-	for (device=0; device < MAX_MTD_DEVICES && myparts[device]; device++)
-		;
-
-	if (device == MAX_MTD_DEVICES) {
-		printk(KERN_NOTICE "Maximum number of FTL partitions reached\n"
-		       "Not scanning <%s>\n", mtd->name);
-		return;
-	}
 
 	partition = kmalloc(sizeof(partition_t), GFP_KERNEL);
 		
@@ -1361,92 +1052,55 @@
 
 	memset(partition, 0, sizeof(partition_t));
 
-	partition->mtd = mtd;
+	partition->mbd.mtd = mtd;
 
 	if ((scan_header(partition) == 0) && 
 	    (build_maps(partition) == 0)) {
 		
 		partition->state = FTL_FORMATTED;
-		atomic_set(&partition->open, 0);
-		myparts[device] = partition;
-		ftl_reread_partitions(device << 4);
 #ifdef PCMCIA_DEBUG
-		printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n",
+		printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n",
 		       le32_to_cpu(partition->header.FormattedSize) >> 10);
 #endif
+		partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9;
+		partition->mbd.blksize = SECTOR_SIZE;
+		partition->mbd.tr = tr;
+		partition->mbd.devnum = -1;
+		if (add_mtd_blktrans_dev((void *)partition))
+			kfree(partition);
+	
 	} else
 		kfree(partition);
 }
 
-static void ftl_notify_remove(struct mtd_info *mtd)
+static void ftl_remove_dev(struct mtd_blktrans_dev *dev)
 {
-        int i,j;
-
-	/* Q: What happens if you try to remove a device which has
-	 *    a currently-open FTL partition on it?
-	 *
-	 * A: You don't. The ftl_open routine is responsible for
-	 *    increasing the use count of the driver module which
-	 *    it uses.
-	 */
-
-	/* That's the theory, anyway :) */
-
-	for (i=0; i< MAX_MTD_DEVICES; i++)
-		if (myparts[i] && myparts[i]->mtd == mtd) {
-
-			if (myparts[i]->state == FTL_FORMATTED)
-				ftl_freepart(myparts[i]);
-			
-			myparts[i]->state = 0;
-			for (j=0; j<16; j++) {
-				ftl_gendisk.part[j].nr_sects=0;
-				ftl_gendisk.part[j].start_sect=0;
-			}
-			kfree(myparts[i]);
-			myparts[i] = NULL;
-		}
+	del_mtd_blktrans_dev(dev);
+	kfree(dev);
 }
 
+struct mtd_blktrans_ops ftl_tr = {
+	.name		= "ftl",
+	.major		= FTL_MAJOR,
+	.part_bits	= PART_BITS,
+	.readsect	= ftl_readsect,
+	.writesect	= ftl_writesect,
+	.getgeo		= ftl_getgeo,
+	.add_mtd	= ftl_add_mtd,
+	.remove_dev	= ftl_remove_dev,
+	.owner		= THIS_MODULE,
+};
+
 int init_ftl(void)
 {
-    int i;
+	DEBUG(0, "$Id: ftl.c,v 1.52 2003/08/11 09:00:44 dwmw2 Exp $\n");
 
-    memset(myparts, 0, sizeof(myparts));
-    
-    DEBUG(0, "$Id: ftl.c,v 1.45 2003/01/24 23:31:27 dwmw2 Exp $\n");
-    
-    if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) {
-	printk(KERN_NOTICE "ftl_cs: unable to grab major "
-	       "device number!\n");
-	return -EAGAIN;
-    }
-    
-    for (i = 0; i < MINOR_NR(MAX_DEV, 0, 0); i++)
-	ftl_blocksizes[i] = 1024;
-    for (i = 0; i < MAX_DEV*MAX_PART; i++) {
-	ftl_hd[i].nr_sects = 0;
-	ftl_hd[i].start_sect = 0;
-    }
-    blksize_size[FTL_MAJOR] = ftl_blocksizes;
-    ftl_gendisk.major = FTL_MAJOR;
-    blk_init_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR), &do_ftl_request);
-    add_gendisk(&ftl_gendisk);
-    
-    register_mtd_user(&ftl_notifier);
-    
-    return 0;
+	return register_mtd_blktrans(&ftl_tr);
 }
 
 static void __exit cleanup_ftl(void)
 {
-    unregister_mtd_user(&ftl_notifier);
-
-    unregister_blkdev(FTL_MAJOR, "ftl");
-    blk_cleanup_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR));
-    blksize_size[FTL_MAJOR] = NULL;
-
-    del_gendisk(&ftl_gendisk);
+	deregister_mtd_blktrans(&ftl_tr);
 }
 
 module_init(init_ftl);
diff -Nur linux-2.4.30/drivers/mtd/inftlcore.c linux-2.4.30-linux-mips.org/drivers/mtd/inftlcore.c
--- linux-2.4.30/drivers/mtd/inftlcore.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/inftlcore.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,900 @@
+/* 
+ * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL)
+ *
+ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
+ *
+ * Based heavily on the nftlcore.c code which is:
+ * (c) 1999 Machine Vision Holdings, Inc.
+ * Author: David Woodhouse <dwmw2@infradead.org>
+ *
+ * $Id: inftlcore.c,v 1.14 2003/06/26 08:28:26 dwmw2 Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/kmod.h>
+#include <linux/hdreg.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nftl.h>
+#include <linux/mtd/inftl.h>
+#include <asm/uaccess.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+
+/*
+ * Maximum number of loops while examining next block, to have a
+ * chance to detect consistency problems (they should never happen
+ * because of the checks done in the mounting.
+ */
+#define MAX_LOOPS 10000
+
+extern void INFTL_dumptables(struct INFTLrecord *inftl);
+extern void INFTL_dumpVUchains(struct INFTLrecord *inftl);
+
+static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+{
+	struct INFTLrecord *inftl;
+	unsigned long temp;
+
+	if (mtd->ecctype != MTD_ECC_RS_DiskOnChip)
+		return;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name);
+
+	inftl = kmalloc(sizeof(*inftl), GFP_KERNEL);
+
+	if (!inftl) {
+		printk(KERN_WARNING "INFTL: Out of memory for data structures\n");
+		return;
+	}
+	memset(inftl, 0, sizeof(*inftl));
+
+	inftl->mbd.mtd = mtd;
+	inftl->mbd.devnum = -1;
+	inftl->mbd.blksize = 512;
+	inftl->mbd.tr = tr;
+
+        if (INFTL_mount(inftl) < 0) {
+		printk(KERN_WARNING "INFTL: could not mount device\n");
+		kfree(inftl);
+		return;
+        }
+
+	/* OK, it's a new one. Set up all the data structures. */
+
+	/* Calculate geometry */
+	inftl->cylinders = 1024;
+	inftl->heads = 16;
+
+	temp = inftl->cylinders * inftl->heads;
+	inftl->sectors = inftl->mbd.size / temp;
+	if (inftl->mbd.size % temp) {
+		inftl->sectors++;
+		temp = inftl->cylinders * inftl->sectors;
+		inftl->heads = inftl->mbd.size / temp;
+
+		if (inftl->mbd.size % temp) {
+			inftl->heads++;
+			temp = inftl->heads * inftl->sectors;
+			inftl->cylinders = inftl->mbd.size / temp;
+		}
+	}
+
+	if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) {
+		/*
+		  Oh no we don't have 
+		   mbd.size == heads * cylinders * sectors
+		*/
+		printk(KERN_WARNING "INFTL: cannot calculate a geometry to "
+		       "match size of 0x%lx.\n", inftl->mbd.size);
+		printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d "
+			"(== 0x%lx sects)\n",
+			inftl->cylinders, inftl->heads , inftl->sectors, 
+			(long)inftl->cylinders * (long)inftl->heads *
+			(long)inftl->sectors );
+	}
+
+	if (add_mtd_blktrans_dev(&inftl->mbd)) {
+		if (inftl->PUtable)
+			kfree(inftl->PUtable);
+		if (inftl->VUtable)
+			kfree(inftl->VUtable);
+		kfree(inftl);
+		return;
+	}
+#ifdef PSYCHO_DEBUG
+	printk(KERN_INFO "INFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
+#endif
+	return;
+}
+
+static void inftl_remove_dev(struct mtd_blktrans_dev *dev)
+{
+	struct INFTLrecord *inftl = (void *)dev;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: remove_dev (i=%d)\n", dev->devnum);
+
+	del_mtd_blktrans_dev(dev);
+
+	if (inftl->PUtable)
+		kfree(inftl->PUtable);
+	if (inftl->VUtable)
+		kfree(inftl->VUtable);
+	kfree(inftl);
+}
+
+/*
+ * Actual INFTL access routines.
+ */
+
+/*
+ * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition.
+ *	This function is used when the give Virtual Unit Chain.
+ */
+static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate)
+{
+	u16 pot = inftl->LastFreeEUN;
+	int silly = inftl->nb_blocks;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findfreeblock(inftl=0x%x,"
+		"desperate=%d)\n", (int)inftl, desperate);
+
+	/*
+	 * Normally, we force a fold to happen before we run out of free
+	 * blocks completely.
+	 */
+	if (!desperate && inftl->numfreeEUNs < 2) {
+		DEBUG(MTD_DEBUG_LEVEL1, "INFTL: there are too few free "
+			"EUNs (%d)\n", inftl->numfreeEUNs);
+		return 0xffff;
+	}
+
+	/* Scan for a free block */
+	do {
+		if (inftl->PUtable[pot] == BLOCK_FREE) {
+			inftl->LastFreeEUN = pot;
+			return pot;
+		}
+
+		if (++pot > inftl->lastEUN)
+			pot = 0;
+
+		if (!silly--) {
+			printk(KERN_WARNING "INFTL: no free blocks found!  "
+				"EUN range = %d - %d\n", 0, inftl->LastFreeEUN);
+			return BLOCK_NIL;
+		}
+	} while (pot != inftl->LastFreeEUN);
+
+	return BLOCK_NIL;
+}
+
+static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock)
+{
+	u16 BlockMap[MAX_SECTORS_PER_UNIT];
+	unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
+	unsigned int thisEUN, prevEUN, status;
+	int block, silly;
+	unsigned int targetEUN;
+	struct inftl_oob oob;
+        size_t retlen;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=0x%x,thisVUC=%d,"
+		"pending=%d)\n", (int)inftl, thisVUC, pendingblock);
+
+	memset(BlockMap, 0xff, sizeof(BlockMap));
+	memset(BlockDeleted, 0, sizeof(BlockDeleted));
+
+	thisEUN = targetEUN = inftl->VUtable[thisVUC];
+
+	if (thisEUN == BLOCK_NIL) {
+		printk(KERN_WARNING "INFTL: trying to fold non-existent "
+		       "Virtual Unit Chain %d!\n", thisVUC);
+		return BLOCK_NIL;
+	}
+	
+	/*
+	 * Scan to find the Erase Unit which holds the actual data for each
+	 * 512-byte block within the Chain.
+	 */
+        silly = MAX_LOOPS;
+	while (thisEUN < inftl->nb_blocks) {
+		for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) {
+			if ((BlockMap[block] != 0xffff) || BlockDeleted[block])
+				continue;
+
+			if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
+			     + (block * SECTORSIZE), 16 , &retlen,
+			     (char *)&oob) < 0)
+				status = SECTOR_IGNORE;
+			else
+                        	status = oob.b.Status | oob.b.Status1;
+
+			switch(status) {
+			case SECTOR_FREE:
+			case SECTOR_IGNORE:
+				break;
+			case SECTOR_USED:
+				BlockMap[block] = thisEUN;
+				continue;
+			case SECTOR_DELETED:
+				BlockDeleted[block] = 1;
+				continue;
+			default:
+				printk(KERN_WARNING "INFTL: unknown status "
+					"for block %d in EUN %d: %x\n",
+					block, thisEUN, status);
+				break;
+			}
+		}
+
+		if (!silly--) {
+			printk(KERN_WARNING "INFTL: infinite loop in Virtual "
+				"Unit Chain 0x%x\n", thisVUC);
+			return BLOCK_NIL;
+		}
+		
+		thisEUN = inftl->PUtable[thisEUN];
+	}
+
+	/*
+	 * OK. We now know the location of every block in the Virtual Unit
+	 * Chain, and the Erase Unit into which we are supposed to be copying.
+	 * Go for it.
+	 */
+	DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n",
+		thisVUC, targetEUN);
+
+	for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) {
+		unsigned char movebuf[SECTORSIZE];
+		int ret;
+
+		/*
+		 * If it's in the target EUN already, or if it's pending write,
+		 * do nothing.
+		 */
+		if (BlockMap[block] == targetEUN || (pendingblock ==
+		    (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) {
+			continue;
+		}
+
+                /*
+		 * Copy only in non free block (free blocks can only
+                 * happen in case of media errors or deleted blocks).
+		 */
+                if (BlockMap[block] == BLOCK_NIL)
+                        continue;
+                
+                ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize *
+			BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE,
+			&retlen, movebuf, (char *)&oob, NULL); 
+                if (ret < 0) {
+			ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize *
+				BlockMap[block]) + (block * SECTORSIZE),
+				SECTORSIZE, &retlen, movebuf, (char *)&oob,
+				NULL); 
+			if (ret != -EIO) 
+                        	DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
+					"away on retry?\n");
+                }
+                MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
+			(block * SECTORSIZE), SECTORSIZE, &retlen,
+			movebuf, (char *)&oob, NULL);
+	}
+
+	/*
+	 * Newest unit in chain now contains data from _all_ older units.
+	 * So go through and erase each unit in chain, oldest first. (This
+	 * is important, by doing oldest first if we crash/reboot then it
+	 * it is relatively simple to clean up the mess).
+	 */
+	DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n",
+		thisVUC);
+
+	for (;;) {
+		/* Find oldest unit in chain. */
+		thisEUN = inftl->VUtable[thisVUC];
+		prevEUN = BLOCK_NIL;
+		while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
+			prevEUN = thisEUN;
+			thisEUN = inftl->PUtable[thisEUN];
+		}
+
+		/* Check if we are all done */
+		if (thisEUN == targetEUN)
+			break;
+
+                if (INFTL_formatblock(inftl, thisEUN) < 0) {
+			/*
+			 * Could not erase : mark block as reserved.
+			 * FixMe: Update Bad Unit Table on disk.
+			 */
+			inftl->PUtable[thisEUN] = BLOCK_RESERVED;
+                } else {
+			/* Correctly erased : mark it as free */
+			inftl->PUtable[thisEUN] = BLOCK_FREE;
+			inftl->PUtable[prevEUN] = BLOCK_NIL;
+			inftl->numfreeEUNs++;
+                }
+	}
+
+	return targetEUN;
+}
+
+u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock)
+{
+	/*
+	 * This is the part that needs some cleverness applied. 
+	 * For now, I'm doing the minimum applicable to actually
+	 * get the thing to work.
+	 * Wear-levelling and other clever stuff needs to be implemented
+	 * and we also need to do some assessment of the results when
+	 * the system loses power half-way through the routine.
+	 */
+	u16 LongestChain = 0;
+	u16 ChainLength = 0, thislen;
+	u16 chain, EUN;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_makefreeblock(inftl=0x%x,"
+		"pending=%d)\n", (int)inftl, pendingblock);
+
+	for (chain = 0; chain < inftl->nb_blocks; chain++) {
+		EUN = inftl->VUtable[chain];
+		thislen = 0;
+
+		while (EUN <= inftl->lastEUN) {
+			thislen++;
+			EUN = inftl->PUtable[EUN];
+			if (thislen > 0xff00) {
+				printk(KERN_WARNING "INFTL: endless loop in "
+					"Virtual Chain %d: Unit %x\n",
+					chain, EUN);
+				/*
+				 * Actually, don't return failure.
+				 * Just ignore this chain and get on with it.
+				 */
+				thislen = 0;
+				break;
+			}
+		}
+
+		if (thislen > ChainLength) {
+			ChainLength = thislen;
+			LongestChain = chain;
+		}
+	}
+
+	if (ChainLength < 2) {
+		printk(KERN_WARNING "INFTL: no Virtual Unit Chains available "
+			"for folding. Failing request\n");
+		return BLOCK_NIL;
+	}
+
+	return INFTL_foldchain(inftl, LongestChain, pendingblock);
+}
+
+static int nrbits(unsigned int val, int bitcount)
+{
+	int i, total = 0;
+
+	for (i = 0; (i < bitcount); i++)
+		total += (((0x1 << i) & val) ? 1 : 0);
+	return total;
+}
+
+/*
+ * INFTL_findwriteunit: Return the unit number into which we can write 
+ *                      for this block. Make it available if it isn't already.
+ */
+static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block)
+{
+	unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE);
+	unsigned int thisEUN, writeEUN, prev_block, status;
+	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1);
+	struct inftl_oob oob;
+	struct inftl_bci bci;
+	unsigned char anac, nacs, parity;
+	size_t retlen;
+	int silly, silly2 = 3;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=0x%x,"
+		"block=%d)\n", (int)inftl, block);
+
+	do {
+		/*
+		 * Scan the media to find a unit in the VUC which has
+		 * a free space for the block in question.
+		 */
+		writeEUN = BLOCK_NIL;
+		thisEUN = inftl->VUtable[thisVUC];
+		silly = MAX_LOOPS;
+
+		while (thisEUN <= inftl->lastEUN) {
+			MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
+				blockofs, 8, &retlen, (char *)&bci);
+
+                        status = bci.Status | bci.Status1;
+			DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in "
+				"EUN %d is %x\n", block , writeEUN, status);
+
+			switch(status) {
+			case SECTOR_FREE:
+				writeEUN = thisEUN;
+				break;
+			case SECTOR_DELETED:
+			case SECTOR_USED:
+				/* Can't go any further */
+				goto hitused;
+			case SECTOR_IGNORE:
+				break;
+			default:
+				/*
+				 * Invalid block. Don't use it any more.
+				 * Must implement.
+				 */
+				break;			
+			}
+			
+			if (!silly--) { 
+				printk(KERN_WARNING "INFTL: infinite loop in "
+					"Virtual Unit Chain 0x%x\n", thisVUC);
+				return 0xffff;
+			}
+
+			/* Skip to next block in chain */
+			thisEUN = inftl->PUtable[thisEUN];
+		}
+
+hitused:
+		if (writeEUN != BLOCK_NIL)
+			return writeEUN;
+
+
+		/*
+		 * OK. We didn't find one in the existing chain, or there 
+		 * is no existing chain. Allocate a new one.
+		 */
+		writeEUN = INFTL_findfreeblock(inftl, 0);
+
+		if (writeEUN == BLOCK_NIL) {
+			/*
+			 * That didn't work - there were no free blocks just
+			 * waiting to be picked up. We're going to have to fold
+			 * a chain to make room.
+			 */
+			thisEUN = INFTL_makefreeblock(inftl, 0xffff);
+
+			/*
+			 * Hopefully we free something, lets try again.
+			 * This time we are desperate...
+			 */
+			DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 "
+				"to find free EUN to accommodate write to "
+				"VUC %d\n", thisVUC);
+			writeEUN = INFTL_findfreeblock(inftl, 1);
+			if (writeEUN == BLOCK_NIL) {
+				/*
+				 * Ouch. This should never happen - we should
+				 * always be able to make some room somehow. 
+				 * If we get here, we've allocated more storage 
+				 * space than actual media, or our makefreeblock
+				 * routine is missing something.
+				 */
+				printk(KERN_WARNING "INFTL: cannot make free "
+					"space.\n");
+#ifdef DEBUG
+				INFTL_dumptables(inftl);
+				INFTL_dumpVUchains(inftl);
+#endif
+				return BLOCK_NIL;
+			}			
+		}
+
+		/*
+		 * Insert new block into virtual chain. Firstly update the
+		 * block headers in flash...
+		 */
+		anac = 0;
+		nacs = 0;
+		thisEUN = inftl->VUtable[thisVUC];
+		if (thisEUN != BLOCK_NIL) {
+			MTD_READOOB(inftl->mbd.mtd, thisEUN * inftl->EraseSize
+				+ 8, 8, &retlen, (char *)&oob.u);
+			anac = oob.u.a.ANAC + 1;
+			nacs = oob.u.a.NACs + 1;
+		}
+
+		prev_block = inftl->VUtable[thisVUC];
+		if (prev_block < inftl->nb_blocks)
+			prev_block -= inftl->firstEUN;
+
+		parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0;
+		parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0;
+		parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0;
+		parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0;
+ 
+		oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC);
+		oob.u.a.prevUnitNo = cpu_to_le16(prev_block);
+		oob.u.a.ANAC = anac;
+		oob.u.a.NACs = nacs;
+		oob.u.a.parityPerField = parity;
+		oob.u.a.discarded = 0xaa;
+
+		MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 8, 8,
+			&retlen, (char *)&oob.u);
+
+		/* Also back up header... */
+		oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC);
+		oob.u.b.prevUnitNo = cpu_to_le16(prev_block);
+		oob.u.b.ANAC = anac;
+		oob.u.b.NACs = nacs;
+		oob.u.b.parityPerField = parity;
+		oob.u.b.discarded = 0xaa;
+
+		MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 
+			SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u);
+
+		inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC];
+		inftl->VUtable[thisVUC] = writeEUN;
+
+		inftl->numfreeEUNs--;
+		return writeEUN;
+
+	} while (silly2--);
+
+	printk(KERN_WARNING "INFTL: error folding to make room for Virtual "
+		"Unit Chain 0x%x\n", thisVUC);
+	return 0xffff;
+}
+
+/*
+ * Given a Virtual Unit Chain, see if it can be deleted, and if so do it.
+ */
+static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC)
+{
+	unsigned char BlockUsed[MAX_SECTORS_PER_UNIT];
+	unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT];
+	unsigned int thisEUN, status;
+	int block, silly;
+	struct inftl_bci bci;
+	size_t retlen;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_trydeletechain(inftl=0x%x,"
+		"thisVUC=%d)\n", (int)inftl, thisVUC);
+
+	memset(BlockUsed, 0, sizeof(BlockUsed));
+	memset(BlockDeleted, 0, sizeof(BlockDeleted));
+
+	thisEUN = inftl->VUtable[thisVUC];
+	if (thisEUN == BLOCK_NIL) {
+		printk(KERN_WARNING "INFTL: trying to delete non-existent "
+		       "Virtual Unit Chain %d!\n", thisVUC);
+		return;
+	}
+	
+	/*
+	 * Scan through the Erase Units to determine whether any data is in
+	 * each of the 512-byte blocks within the Chain.
+	 */
+	silly = MAX_LOOPS;
+	while (thisEUN < inftl->nb_blocks) {
+		for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) {
+			if (BlockUsed[block] || BlockDeleted[block])
+				continue;
+
+			if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize)
+			    + (block * SECTORSIZE), 8 , &retlen,
+			    (char *)&bci) < 0)
+				status = SECTOR_IGNORE;
+			else
+				status = bci.Status | bci.Status1;
+
+			switch(status) {
+			case SECTOR_FREE:
+			case SECTOR_IGNORE:
+				break;
+			case SECTOR_USED:
+				BlockUsed[block] = 1;
+				continue;
+			case SECTOR_DELETED:
+				BlockDeleted[block] = 1;
+				continue;
+			default:
+				printk(KERN_WARNING "INFTL: unknown status "
+					"for block %d in EUN %d: 0x%x\n",
+					block, thisEUN, status);
+			}
+		}
+
+		if (!silly--) {
+			printk(KERN_WARNING "INFTL: infinite loop in Virtual "
+				"Unit Chain 0x%x\n", thisVUC);
+			return;
+		}
+		
+		thisEUN = inftl->PUtable[thisEUN];
+	}
+
+	for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++)
+		if (BlockUsed[block])
+			return;
+
+	/*
+	 * For each block in the chain free it and make it available
+	 * for future use. Erase from the oldest unit first.
+	 */
+	DEBUG(MTD_DEBUG_LEVEL1, "INFTL: deleting empty VUC %d\n", thisVUC);
+
+	for (;;) {
+		u16 *prevEUN = &inftl->VUtable[thisVUC];
+		thisEUN = *prevEUN;
+
+		/* If the chain is all gone already, we're done */
+		if (thisEUN == BLOCK_NIL) {
+			DEBUG(MTD_DEBUG_LEVEL2, "INFTL: Empty VUC %d for deletion was already absent\n", thisEUN);
+			return;
+		}
+
+		/* Find oldest unit in chain. */
+		while (inftl->PUtable[thisEUN] != BLOCK_NIL) {
+			BUG_ON(thisEUN >= inftl->nb_blocks);
+
+			prevEUN = &inftl->PUtable[thisEUN];
+			thisEUN = *prevEUN;
+		}
+
+		DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n",
+		      thisEUN, thisVUC);
+
+                if (INFTL_formatblock(inftl, thisEUN) < 0) {
+			/*
+			 * Could not erase : mark block as reserved.
+			 * FixMe: Update Bad Unit Table on medium.
+			 */
+			inftl->PUtable[thisEUN] = BLOCK_RESERVED;
+                } else {
+			/* Correctly erased : mark it as free */
+			inftl->PUtable[thisEUN] = BLOCK_FREE;
+			inftl->numfreeEUNs++;
+		}
+
+		/* Now sort out whatever was pointing to it... */
+		*prevEUN = BLOCK_NIL;
+
+		/* Ideally we'd actually be responsive to new
+		   requests while we're doing this -- if there's
+		   free space why should others be made to wait? */
+		cond_resched();
+	}
+
+	inftl->VUtable[thisVUC] = BLOCK_NIL;
+}
+
+static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block)
+{
+	unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
+	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
+	unsigned int status;
+	int silly = MAX_LOOPS;
+	size_t retlen;
+	struct inftl_bci bci;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_deleteblock(inftl=0x%x,"
+		"block=%d)\n", (int)inftl, block);
+
+	while (thisEUN < inftl->nb_blocks) {
+		if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
+		    blockofs, 8, &retlen, (char *)&bci) < 0)
+			status = SECTOR_IGNORE;
+		else
+			status = bci.Status | bci.Status1;
+
+		switch (status) {
+		case SECTOR_FREE:
+		case SECTOR_IGNORE:
+			break;
+		case SECTOR_DELETED:
+			thisEUN = BLOCK_NIL;
+			goto foundit;
+		case SECTOR_USED:
+			goto foundit;
+		default:
+			printk(KERN_WARNING "INFTL: unknown status for "
+				"block %d in EUN %d: 0x%x\n",
+				block, thisEUN, status);
+			break;
+		}
+
+		if (!silly--) {
+			printk(KERN_WARNING "INFTL: infinite loop in Virtual "
+				"Unit Chain 0x%x\n",
+				block / (inftl->EraseSize / SECTORSIZE));
+			return 1;
+		}
+		thisEUN = inftl->PUtable[thisEUN];
+	}
+
+foundit:
+	if (thisEUN != BLOCK_NIL) {
+		loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
+
+		if (MTD_READOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0)
+			return -EIO;
+		bci.Status = bci.Status1 = SECTOR_DELETED;
+		if (MTD_WRITEOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0)
+			return -EIO;
+		INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE));
+	}
+	return 0;
+}
+
+static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, 
+			    char *buffer)
+{
+	struct INFTLrecord *inftl = (void *)mbd;
+	unsigned int writeEUN;
+	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
+	size_t retlen;
+	u8 eccbuf[6];
+	char *p, *pend;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=0x%x,block=%d,"
+		"buffer=0x%x)\n", (int)inftl, block, (int)buffer);
+
+	/* Is block all zero? */
+	pend = buffer + SECTORSIZE;
+	for (p = buffer; p < pend && !*p; p++)
+		;
+
+	if (p < pend) {
+		writeEUN = INFTL_findwriteunit(inftl, block);
+
+		if (writeEUN == BLOCK_NIL) {
+			printk(KERN_WARNING "inftl_writeblock(): cannot find "
+				"block to write to\n");
+			/*
+			 * If we _still_ haven't got a block to use,
+			 * we're screwed.
+			 */
+			return 1;
+		}
+
+		MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
+			blockofs, SECTORSIZE, &retlen, (char *)buffer,
+			(char *)eccbuf, NULL);
+		/*
+		 * No need to write SECTOR_USED flags since they are written
+		 * in mtd_writeecc
+		 */
+	} else {
+		INFTL_deleteblock(inftl, block);
+	}
+
+	return 0;
+}
+
+static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
+			   char *buffer)
+{
+	struct INFTLrecord *inftl = (void *)mbd;
+	unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)];
+	unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1);
+        unsigned int status;
+	int silly = MAX_LOOPS;
+        struct inftl_bci bci;
+	size_t retlen;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=0x%x,block=%d,"
+		"buffer=0x%x)\n", (int)inftl, block, (int)buffer);
+
+	while (thisEUN < inftl->nb_blocks) {
+		if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) +
+		     blockofs, 8, &retlen, (char *)&bci) < 0)
+			status = SECTOR_IGNORE;
+		else
+			status = bci.Status | bci.Status1;
+
+		switch (status) {
+		case SECTOR_DELETED:
+			thisEUN = BLOCK_NIL;
+			goto foundit;
+		case SECTOR_USED:
+			goto foundit;
+		case SECTOR_FREE:
+		case SECTOR_IGNORE:
+			break;
+		default:
+			printk(KERN_WARNING "INFTL: unknown status for "
+				"block %ld in EUN %d: 0x%04x\n",
+				block, thisEUN, status);
+			break;
+		}
+
+		if (!silly--) {
+			printk(KERN_WARNING "INFTL: infinite loop in "
+				"Virtual Unit Chain 0x%lx\n",
+				block / (inftl->EraseSize / SECTORSIZE));
+			return 1;
+		}
+
+		thisEUN = inftl->PUtable[thisEUN];
+	}
+
+foundit:
+	if (thisEUN == BLOCK_NIL) {
+		/* The requested block is not on the media, return all 0x00 */
+		memset(buffer, 0, SECTORSIZE);
+	} else {
+        	size_t retlen;
+		loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs;
+		u_char eccbuf[6];
+		if (MTD_READECC(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen,
+		    buffer, eccbuf, NULL))
+			return -EIO;
+	}
+	return 0;
+}
+
+static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo)
+{
+	struct INFTLrecord *inftl = (void *)dev;
+
+	geo->heads = inftl->heads;
+	geo->sectors = inftl->sectors;
+	geo->cylinders = inftl->cylinders;
+
+	return 0;
+}
+
+struct mtd_blktrans_ops inftl_tr = {
+	.name		= "inftl",
+	.major		= INFTL_MAJOR,
+	.part_bits	= INFTL_PARTN_BITS,
+	.getgeo		= inftl_getgeo,
+	.readsect	= inftl_readblock,
+	.writesect	= inftl_writeblock,
+	.add_mtd	= inftl_add_mtd,
+	.remove_dev	= inftl_remove_dev,
+	.owner		= THIS_MODULE,
+};
+
+extern char inftlmountrev[];
+
+int __init init_inftl(void)
+{
+	printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.14 $, "
+		"inftlmount.c %s\n", inftlmountrev);
+
+	return register_mtd_blktrans(&inftl_tr);
+}
+
+static void __exit cleanup_inftl(void)
+{
+	deregister_mtd_blktrans(&inftl_tr);
+}
+
+module_init(init_inftl);
+module_exit(cleanup_inftl);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
+MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus");
diff -Nur linux-2.4.30/drivers/mtd/inftlmount.c linux-2.4.30-linux-mips.org/drivers/mtd/inftlmount.c
--- linux-2.4.30/drivers/mtd/inftlmount.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/inftlmount.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,817 @@
+/* 
+ * inftlmount.c -- INFTL mount code with extensive checks.
+ *
+ * Author: Greg Ungerer (gerg@snapgear.com)
+ * (C) Copyright 2002-2003, Greg Ungerer (gerg@snapgear.com)
+ *
+ * Based heavily on the nftlmount.c code which is:
+ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) 
+ * Copyright (C) 2000 Netgem S.A.
+ *
+ * $Id: inftlmount.c,v 1.12 2003/06/26 07:31:36 dwmw2 Exp $
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <asm/errno.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nftl.h>
+#include <linux/mtd/inftl.h>
+#include <linux/mtd/compatmac.h>
+
+char inftlmountrev[]="$Revision: 1.12 $";
+
+/*
+ * find_boot_record: Find the INFTL Media Header and its Spare copy which
+ *	contains the various device information of the INFTL partition and
+ *	Bad Unit Table. Update the PUtable[] table according to the Bad
+ *	Unit Table. PUtable[] is used for management of Erase Unit in
+ *	other routines in inftlcore.c and inftlmount.c.
+ */
+static int find_boot_record(struct INFTLrecord *inftl)
+{
+	struct inftl_unittail h1;
+	//struct inftl_oob oob;
+	unsigned int i, block, boot_record_count = 0;
+	u8 buf[SECTORSIZE];
+	struct INFTLMediaHeader *mh = &inftl->MediaHdr;
+	struct INFTLPartition *ip;
+	int retlen;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=0x%x)\n",
+		(int)inftl);
+
+        /*
+	 * Assume logical EraseSize == physical erasesize for starting the
+	 * scan. We'll sort it out later if we find a MediaHeader which says
+	 * otherwise.
+	 */
+	inftl->EraseSize = inftl->mbd.mtd->erasesize;
+        inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
+
+	inftl->MediaUnit = BLOCK_NIL;
+	inftl->SpareMediaUnit = BLOCK_NIL;
+
+	/* Search for a valid boot record */
+	for (block = 0; block < inftl->nb_blocks; block++) {
+		int ret;
+
+		/*
+		 * Check for BNAND header first. Then whinge if it's found
+		 * but later checks fail.
+		 */
+		if ((ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize,
+		    SECTORSIZE, &retlen, buf))) {
+			static int warncount = 5;
+
+			if (warncount) {
+				printk(KERN_WARNING "INFTL: block read at 0x%x "
+					"of mtd%d failed: %d\n",
+					block * inftl->EraseSize,
+					inftl->mbd.mtd->index, ret);
+				if (!--warncount)
+					printk(KERN_WARNING "INFTL: further "
+						"failures for this block will "
+						"not be printed\n");
+			}
+			continue;
+		}
+
+		if (retlen < 6 || memcmp(buf, "BNAND", 6)) {
+			/* BNAND\0 not found. Continue */
+			continue;
+		}
+
+		/* To be safer with BIOS, also use erase mark as discriminant */
+		if ((ret = MTD_READOOB(inftl->mbd.mtd, block * inftl->EraseSize +
+		    SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) {
+			printk(KERN_WARNING "INFTL: ANAND header found at "
+				"0x%x in mtd%d, but OOB data read failed "
+				"(err %d)\n", block * inftl->EraseSize,
+				inftl->mbd.mtd->index, ret);
+			continue;
+		}
+
+		if (boot_record_count) {
+			/*
+			 * We've already processed one. So we just check if
+			 * this one is the same as the first one we found.
+			 */
+			if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) {
+				printk(KERN_WARNING "INFTL: Media Headers at "
+					"0x%x and 0x%x disagree.\n",
+					inftl->MediaUnit * inftl->EraseSize,
+					block * inftl->EraseSize);
+				return -1;
+			}
+			if (boot_record_count == 1)
+				inftl->SpareMediaUnit = block;
+
+			/*
+			 * Mark this boot record (INFTL MediaHeader) block as
+			 * reserved.
+			 */
+			inftl->PUtable[block] = BLOCK_RESERVED;
+
+			boot_record_count++;
+			continue;
+		}
+
+		/*
+		 * This is the first we've seen.
+		 * Copy the media header structure into place.
+		 */
+		memcpy(mh, buf, sizeof(struct INFTLMediaHeader));
+		mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
+		mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
+		mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions);
+		mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits);
+		mh->FormatFlags = le32_to_cpu(mh->FormatFlags);
+		mh->PercentUsed = le32_to_cpu(mh->PercentUsed);
+
+#ifdef CONFIG_MTD_DEBUG_VERBOSE
+		if (CONFIG_MTD_DEBUG_VERBOSE >= 2) {
+			printk("INFTL: Media Header ->\n"
+				"    bootRecordID          = %s\n"
+				"    NoOfBootImageBlocks   = %d\n"
+				"    NoOfBinaryPartitions  = %d\n"
+				"    NoOfBDTLPartitions    = %d\n"
+				"    BlockMultiplerBits    = %d\n"
+				"    FormatFlgs            = %d\n"
+				"    OsakVersion           = 0x%x\n"
+				"    PercentUsed           = %d\n",
+				mh->bootRecordID, mh->NoOfBootImageBlocks,
+				mh->NoOfBinaryPartitions,
+				mh->NoOfBDTLPartitions,
+				mh->BlockMultiplierBits, mh->FormatFlags,
+				mh->OsakVersion, mh->PercentUsed);
+		}
+#endif
+
+		if (mh->NoOfBDTLPartitions == 0) {
+			printk(KERN_WARNING "INFTL: Media Header sanity check "
+				"failed: NoOfBDTLPartitions (%d) == 0, "
+				"must be at least 1\n", mh->NoOfBDTLPartitions);
+			return -1;
+		}
+
+		if ((mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions) > 4) {
+			printk(KERN_WARNING "INFTL: Media Header sanity check "
+				"failed: Total Partitions (%d) > 4, "
+				"BDTL=%d Binary=%d\n", mh->NoOfBDTLPartitions +
+				mh->NoOfBinaryPartitions,
+				mh->NoOfBDTLPartitions,
+				mh->NoOfBinaryPartitions);
+			return -1;
+		}
+
+		if (mh->BlockMultiplierBits > 1) {
+			printk(KERN_WARNING "INFTL: sorry, we don't support "
+				"UnitSizeFactor 0x%02x\n",
+				mh->BlockMultiplierBits);
+			return -1;
+		} else if (mh->BlockMultiplierBits == 1) {
+			printk(KERN_WARNING "INFTL: support for INFTL with "
+				"UnitSizeFactor 0x%02x is experimental\n",
+				mh->BlockMultiplierBits);
+			inftl->EraseSize = inftl->mbd.mtd->erasesize <<
+				(0xff - mh->BlockMultiplierBits);
+			inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
+		}
+
+		/* Scan the partitions */
+		for (i = 0; (i < 4); i++) {
+			ip = &mh->Partitions[i];
+			ip->virtualUnits = le32_to_cpu(ip->virtualUnits);
+			ip->firstUnit = le32_to_cpu(ip->firstUnit);
+			ip->lastUnit = le32_to_cpu(ip->lastUnit);
+			ip->flags = le32_to_cpu(ip->flags);
+			ip->spareUnits = le32_to_cpu(ip->spareUnits);
+			ip->Reserved0 = le32_to_cpu(ip->Reserved0);
+
+#ifdef CONFIG_MTD_DEBUG_VERBOSE
+			if (CONFIG_MTD_DEBUG_VERBOSE >= 2) {
+				printk("    PARTITION[%d] ->\n"
+					"        virtualUnits    = %d\n"
+					"        firstUnit       = %d\n"
+					"        lastUnit        = %d\n"
+					"        flags           = 0x%x\n"
+					"        spareUnits      = %d\n",
+					i, ip->virtualUnits, ip->firstUnit,
+					ip->lastUnit, ip->flags,
+					ip->spareUnits);
+			}
+#endif
+
+			if (ip->Reserved0 != ip->firstUnit) {
+				struct erase_info *instr = &inftl->instr;
+
+				/*
+				 * 	Most likely this is using the
+				 * 	undocumented qiuck mount feature.
+				 * 	We don't support that, we will need
+				 * 	to erase the hidden block for full
+				 * 	compatibility.
+				 */
+				instr->addr = ip->Reserved0 * inftl->EraseSize;
+				instr->len = inftl->EraseSize;
+				MTD_ERASE(inftl->mbd.mtd, instr);
+			}
+			if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) {
+				printk(KERN_WARNING "INFTL: Media Header "
+					"Partition %d sanity check failed\n"
+					"    firstUnit %d : lastUnit %d  >  "
+					"virtualUnits %d\n", i, ip->lastUnit,
+					ip->firstUnit, ip->Reserved0);
+				return -1;
+			}
+			if (ip->Reserved1 != 0) {
+				printk(KERN_WARNING "INFTL: Media Header "
+					"Partition %d sanity check failed: "
+					"Reserved1 %d != 0\n",
+					i, ip->Reserved1);
+				return -1;
+			}
+
+			if (ip->flags & INFTL_BDTL)
+				break;
+		}
+
+		if (i >= 4) {
+			printk(KERN_WARNING "INFTL: Media Header Partition "
+				"sanity check failed:\n       No partition "
+				"marked as Disk Partition\n");
+			return -1;
+		}
+
+		inftl->nb_boot_blocks = ip->firstUnit;
+		inftl->numvunits = ip->virtualUnits;
+		if (inftl->numvunits > (inftl->nb_blocks -
+		    inftl->nb_boot_blocks - 2)) {
+			printk(KERN_WARNING "INFTL: Media Header sanity check "
+				"failed:\n        numvunits (%d) > nb_blocks "
+				"(%d) - nb_boot_blocks(%d) - 2\n",
+				inftl->numvunits, inftl->nb_blocks,
+				inftl->nb_boot_blocks);
+			return -1;
+		}
+		
+		inftl->mbd.size  = inftl->numvunits *
+			(inftl->EraseSize / SECTORSIZE);
+
+		/*
+		 * Block count is set to last used EUN (we won't need to keep
+		 * any meta-data past that point).
+		 */
+		inftl->firstEUN = ip->firstUnit;
+		inftl->lastEUN = ip->lastUnit;
+		inftl->nb_blocks = ip->lastUnit + 1;
+
+		/* Memory alloc */
+		inftl->PUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL);
+		if (!inftl->PUtable) {
+			printk(KERN_WARNING "INFTL: allocation of PUtable "
+				"failed (%d bytes)\n",
+				inftl->nb_blocks * sizeof(u16));
+			return -ENOMEM;
+		}
+
+		inftl->VUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL);
+		if (!inftl->VUtable) {
+			kfree(inftl->PUtable);
+			printk(KERN_WARNING "INFTL: allocation of VUtable "
+				"failed (%d bytes)\n",
+				inftl->nb_blocks * sizeof(u16));
+			return -ENOMEM;
+		}
+		
+		/* Mark the blocks before INFTL MediaHeader as reserved */
+		for (i = 0; i < inftl->nb_boot_blocks; i++)
+			inftl->PUtable[i] = BLOCK_RESERVED;
+		/* Mark all remaining blocks as potentially containing data */
+		for (; i < inftl->nb_blocks; i++)
+			inftl->PUtable[i] = BLOCK_NOTEXPLORED;
+
+		/* Mark this boot record (NFTL MediaHeader) block as reserved */
+		inftl->PUtable[block] = BLOCK_RESERVED;
+
+#if 0
+		/* Read Bad Erase Unit Table and modify PUtable[] accordingly */
+		for (i = 0; i < inftl->nb_blocks; i++) {
+			if ((i & (SECTORSIZE - 1)) == 0) {
+				/* read one sector for every SECTORSIZE of blocks */
+				if ((ret = MTD_READECC(inftl->mbd.mtd,
+				    block * inftl->EraseSize + i + SECTORSIZE,
+				    SECTORSIZE, &retlen, buf,
+				    (char *)&oob, NULL)) < 0) {
+					printk(KERN_WARNING "INFTL: read of "
+						"bad sector table failed "
+						"(err %d)\n", ret);
+					kfree(inftl->VUtable);
+					kfree(inftl->PUtable);
+					return -1;
+				}
+			}
+			/* Mark the Bad Erase Unit as RESERVED in PUtable */
+			if (buf[i & (SECTORSIZE - 1)] != 0xff)
+				inftl->PUtable[i] = BLOCK_RESERVED;
+		}
+#endif
+
+		inftl->MediaUnit = block;
+		boot_record_count++;
+	}
+		
+	return boot_record_count ? 0 : -1;
+}
+
+static int memcmpb(void *a, int c, int n)
+{
+	int i;
+	for (i = 0; i < n; i++) {
+		if (c != ((unsigned char *)a)[i])
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * check_free_sector: check if a free sector is actually FREE,
+ *	i.e. All 0xff in data and oob area.
+ */
+static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address,
+	int len, int check_oob)
+{
+	int i, retlen;
+	u8 buf[SECTORSIZE];
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=0x%x,"
+		"address=0x%x,len=%d,check_oob=%d)\n", (int)inftl,
+		address, len, check_oob);
+
+	for (i = 0; i < len; i += SECTORSIZE) {
+		/*
+		 * We want to read the sector without ECC check here since a
+		 * free sector does not have ECC syndrome on it yet.
+		 */
+		if (MTD_READ(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0)
+			return -1;
+		if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
+			return -1;
+
+		if (check_oob) {
+			if (MTD_READOOB(inftl->mbd.mtd, address,
+			    inftl->mbd.mtd->oobsize, &retlen, buf) < 0)
+				return -1;
+			if (memcmpb(buf, 0xff, inftl->mbd.mtd->oobsize) != 0)
+				return -1;
+		}
+		address += SECTORSIZE;
+	}
+
+	return 0;
+}
+
+/*
+ * INFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase
+ *		 Unit and Update INFTL metadata. Each erase operation is
+ *		 checked with check_free_sectors.
+ *
+ * Return: 0 when succeed, -1 on error.
+ *
+ * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? 
+ *       2. UnitSizeFactor != 0xFF
+ */
+int INFTL_formatblock(struct INFTLrecord *inftl, int block)
+{
+	int retlen;
+	struct inftl_unittail uci;
+	struct erase_info *instr = &inftl->instr;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=0x%x,"
+		"block=%d)\n", (int)inftl, block);
+
+	memset(instr, 0, sizeof(struct erase_info));
+
+	/* FIXME: Shouldn't we be setting the 'discarded' flag to zero
+	   _first_? */
+
+	/* Use async erase interface, test return code */
+	instr->addr = block * inftl->EraseSize;
+	instr->len = inftl->EraseSize;
+	MTD_ERASE(inftl->mbd.mtd, instr);
+
+	if (instr->state == MTD_ERASE_FAILED) {
+		/*
+		 * Could not format, FixMe: We should update the BadUnitTable 
+		 * both in memory and on disk.
+		 */
+		printk(KERN_WARNING "INFTL: error while formatting block %d\n",
+			block);
+		return -1;
+	}
+
+	/*
+	 * Check the "freeness" of Erase Unit before updating metadata.
+	 * FixMe: is this check really necessary? Since we have check the
+	 *        return code after the erase operation.
+	 */
+	if (check_free_sectors(inftl, instr->addr, inftl->EraseSize, 1) != 0)
+		return -1;
+
+	uci.EraseMark = cpu_to_le16(ERASE_MARK);
+	uci.EraseMark1 = cpu_to_le16(ERASE_MARK);
+	uci.Reserved[0] = 0;
+	uci.Reserved[1] = 0;
+	uci.Reserved[2] = 0;
+	uci.Reserved[3] = 0;
+	if (MTD_WRITEOOB(inftl->mbd.mtd, block * inftl->EraseSize + SECTORSIZE * 2 +
+	    8, 8, &retlen, (char *)&uci) < 0)
+		return -1;
+	return 0;
+}
+
+/*
+ * format_chain: Format an invalid Virtual Unit chain. It frees all the Erase
+ *	Units in a Virtual Unit Chain, i.e. all the units are disconnected.
+ *
+ *	Since the chain is invalid then we will have to erase it from its
+ *	head (normally for INFTL we go from the oldest). But if it has a
+ *	loop then there is no oldest...
+ */
+static void format_chain(struct INFTLrecord *inftl, unsigned int first_block)
+{
+	unsigned int block = first_block, block1;
+
+	printk(KERN_WARNING "INFTL: formatting chain at block %d\n",
+		first_block);
+
+	for (;;) {
+		block1 = inftl->PUtable[block];
+
+		printk(KERN_WARNING "INFTL: formatting block %d\n", block);
+		if (INFTL_formatblock(inftl, block) < 0) {
+			/*
+			 * Cannot format !!!! Mark it as Bad Unit,
+			 * FixMe: update the BadUnitTable on disk.
+			 */
+			inftl->PUtable[block] = BLOCK_RESERVED;
+		} else {
+			inftl->PUtable[block] = BLOCK_FREE;
+		}
+
+		/* Goto next block on the chain */
+		block = block1;
+
+		if (block == BLOCK_NIL || block >= inftl->lastEUN)
+			break;
+	}
+}
+
+void INFTL_dumptables(struct INFTLrecord *s)
+{
+	int i;
+
+	printk("-------------------------------------------"
+		"----------------------------------\n");
+
+	printk("VUtable[%d] ->", s->nb_blocks);
+	for (i = 0; i < s->nb_blocks; i++) {
+		if ((i % 8) == 0)
+			printk("\n%04x: ", i);
+		printk("%04x ", s->VUtable[i]);
+	}
+
+	printk("\n-------------------------------------------"
+		"----------------------------------\n");
+
+	printk("PUtable[%d-%d=%d] ->", s->firstEUN, s->lastEUN, s->nb_blocks);
+	for (i = 0; i <= s->lastEUN; i++) {
+		if ((i % 8) == 0)
+			printk("\n%04x: ", i);
+		printk("%04x ", s->PUtable[i]);
+	}
+
+	printk("\n-------------------------------------------"
+		"----------------------------------\n");
+
+	printk("INFTL ->\n"
+		"  EraseSize       = %d\n"
+		"  h/s/c           = %d/%d/%d\n"
+		"  numvunits       = %d\n"
+		"  firstEUN        = %d\n"
+		"  lastEUN         = %d\n"
+		"  numfreeEUNs     = %d\n"
+		"  LastFreeEUN     = %d\n"
+		"  nb_blocks       = %d\n"
+		"  nb_boot_blocks  = %d",
+		s->EraseSize, s->heads, s->sectors, s->cylinders,
+		s->numvunits, s->firstEUN, s->lastEUN, s->numfreeEUNs,
+		s->LastFreeEUN, s->nb_blocks, s->nb_boot_blocks);
+
+	printk("\n-------------------------------------------"
+		"----------------------------------\n");
+}
+
+void INFTL_dumpVUchains(struct INFTLrecord *s)
+{
+	int logical, block, i;
+
+	printk("-------------------------------------------"
+		"----------------------------------\n");
+
+	printk("INFTL Virtual Unit Chains:\n");
+	for (logical = 0; logical < s->nb_blocks; logical++) {
+		block = s->VUtable[logical];
+		if (block > s->nb_blocks)
+			continue;
+		printk("  LOGICAL %d --> %d ", logical, block);
+		for (i = 0; i < s->nb_blocks; i++) {
+			if (s->PUtable[block] == BLOCK_NIL)
+				break;
+			block = s->PUtable[block];
+			printk("%d ", block);
+		}
+		printk("\n");
+	}
+
+	printk("-------------------------------------------"
+		"----------------------------------\n");
+}
+
+int INFTL_mount(struct INFTLrecord *s)
+{
+	unsigned int block, first_block, prev_block, last_block;
+	unsigned int first_logical_block, logical_block, erase_mark;
+	int chain_length, do_format_chain;
+	struct inftl_unithead1 h0;
+	struct inftl_unittail h1;
+	int i, retlen;
+	u8 *ANACtable, ANAC;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=0x%x)\n", (int)s);
+
+	/* Search for INFTL MediaHeader and Spare INFTL Media Header */
+	if (find_boot_record(s) < 0) {
+		printk(KERN_WARNING "INFTL: could not find valid boot record?\n");
+		return -1;
+	}
+
+	/* Init the logical to physical table */
+	for (i = 0; i < s->nb_blocks; i++)
+		s->VUtable[i] = BLOCK_NIL;
+
+	logical_block = block = BLOCK_NIL;
+
+	/* Temporary buffer to store ANAC numbers. */
+	ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL);
+	memset(ANACtable, 0, s->nb_blocks);
+
+	/*
+	 * First pass is to explore each physical unit, and construct the
+	 * virtual chains that exist (newest physical unit goes into VUtable).
+	 * Any block that is in any way invalid will be left in the
+	 * NOTEXPLORED state. Then at the end we will try to format it and
+	 * mark it as free.
+	 */
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 1, explore each unit\n");
+	for (first_block = s->firstEUN; first_block <= s->lastEUN; first_block++) {
+		if (s->PUtable[first_block] != BLOCK_NOTEXPLORED)
+			continue;
+
+		do_format_chain = 0;
+		first_logical_block = BLOCK_NIL;
+		last_block = BLOCK_NIL;
+		block = first_block;
+
+		for (chain_length = 0; ; chain_length++) {
+
+			if ((chain_length == 0) && 
+			    (s->PUtable[block] != BLOCK_NOTEXPLORED)) {
+				/* Nothing to do here, onto next block */
+				break;
+			}
+
+			if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8,
+			    8, &retlen, (char *)&h0) < 0 ||
+			    MTD_READOOB(s->mbd.mtd, block * s->EraseSize +
+			    2 * SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) {
+				/* Should never happen? */
+				do_format_chain++;
+				break;
+			}
+
+			logical_block = le16_to_cpu(h0.virtualUnitNo);
+			prev_block = le16_to_cpu(h0.prevUnitNo);
+			erase_mark = le16_to_cpu((h1.EraseMark | h1.EraseMark1));
+			ANACtable[block] = h0.ANAC;
+
+			/* Previous block is relative to start of Partition */
+			if (prev_block < s->nb_blocks)
+				prev_block += s->firstEUN;
+
+			/* Already explored partial chain? */
+			if (s->PUtable[block] != BLOCK_NOTEXPLORED) {
+				/* Check if chain for this logical */
+				if (logical_block == first_logical_block) {
+					if (last_block != BLOCK_NIL)
+						s->PUtable[last_block] = block;
+				}
+				break;
+			}
+
+			/* Check for invalid block */
+			if (erase_mark != ERASE_MARK) {
+				printk(KERN_WARNING "INFTL: corrupt block %d "
+					"in chain %d, chain length %d, erase "
+					"mark 0x%x?\n", block, first_block,
+					chain_length, erase_mark);
+				/*
+				 * Assume end of chain, probably incomplete
+				 * fold/erase...
+				 */
+				if (chain_length == 0)
+					do_format_chain++;
+				break;
+			}
+
+			/* Check for it being free already then... */
+			if ((logical_block == BLOCK_FREE) ||
+			    (logical_block == BLOCK_NIL)) {
+				s->PUtable[block] = BLOCK_FREE;
+				break;
+			}
+
+			/* Sanity checks on block numbers */
+			if ((logical_block >= s->nb_blocks) ||
+			    ((prev_block >= s->nb_blocks) &&
+			     (prev_block != BLOCK_NIL))) {
+				if (chain_length > 0) {
+					printk(KERN_WARNING "INFTL: corrupt "
+						"block %d in chain %d?\n",
+						block, first_block);
+					do_format_chain++;
+				}
+				break;
+			}
+
+			if (first_logical_block == BLOCK_NIL) {
+				first_logical_block = logical_block;
+			} else {
+				if (first_logical_block != logical_block) {
+					/* Normal for folded chain... */
+					break;
+				}
+			}
+
+			/*
+			 * Current block is valid, so if we followed a virtual
+			 * chain to get here then we can set the previous
+			 * block pointer in our PUtable now. Then move onto
+			 * the previous block in the chain.
+			 */
+			s->PUtable[block] = BLOCK_NIL;
+			if (last_block != BLOCK_NIL)
+				s->PUtable[last_block] = block;
+			last_block = block;
+			block = prev_block;
+
+			/* Check for end of chain */
+			if (block == BLOCK_NIL)
+				break;
+
+			/* Validate next block before following it... */
+			if (block > s->lastEUN) {
+				printk(KERN_WARNING "INFTL: invalid previous "
+					"block %d in chain %d?\n", block,
+					first_block);
+				do_format_chain++;
+				break;
+			}
+		}
+
+		if (do_format_chain) {
+			format_chain(s, first_block);
+			continue;
+		}
+
+		/*
+		 * Looks like a valid chain then. It may not really be the
+		 * newest block in the chain, but it is the newest we have
+		 * found so far. We might update it in later iterations of
+		 * this loop if we find something newer.
+		 */
+		s->VUtable[first_logical_block] = first_block;
+		logical_block = BLOCK_NIL;
+	}
+
+#ifdef CONFIG_MTD_DEBUG_VERBOSE
+	if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
+		INFTL_dumptables(s);
+#endif
+
+	/*
+	 * Second pass, check for infinite loops in chains. These are
+	 * possible because we don't update the previous pointers when
+	 * we fold chains. No big deal, just fix them up in PUtable.
+	 */
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 2, validate virtual chains\n");
+	for (logical_block = 0; logical_block < s->numvunits; logical_block++) {
+		block = s->VUtable[logical_block];
+		last_block = BLOCK_NIL;
+
+		/* Check for free/reserved/nil */
+		if (block >= BLOCK_RESERVED)
+			continue;
+
+		ANAC = ANACtable[block];
+		for (i = 0; i < s->numvunits; i++) {
+			if (s->PUtable[block] == BLOCK_NIL)
+				break;
+			if (s->PUtable[block] > s->lastEUN) {
+				printk(KERN_WARNING "INFTL: invalid prev %d, "
+					"in virtual chain %d\n",
+					s->PUtable[block], logical_block);
+				s->PUtable[block] = BLOCK_NIL;
+					
+			}
+			if (ANACtable[block] != ANAC) {
+				/*
+				 * Chain must point back to itself. This is ok,
+				 * but we will need adjust the tables with this
+				 * newest block and oldest block.
+				 */
+				s->VUtable[logical_block] = block;
+				s->PUtable[last_block] = BLOCK_NIL;
+				break;
+			}
+
+			ANAC--;
+			last_block = block;
+			block = s->PUtable[block];
+		}
+
+		if (i >= s->nb_blocks) {
+			/*
+			 * Uhoo, infinite chain with valid ANACS!
+			 * Format whole chain...
+			 */
+			format_chain(s, first_block);
+		}
+	}
+
+#ifdef CONFIG_MTD_DEBUG_VERBOSE
+	if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
+		INFTL_dumptables(s);
+	if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
+		INFTL_dumpVUchains(s);
+#endif
+
+	/*
+	 * Third pass, format unreferenced blocks and init free block count.
+	 */
+	s->numfreeEUNs = 0;
+	s->LastFreeEUN = BLOCK_NIL;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 3, format unused blocks\n");
+	for (block = s->firstEUN; block <= s->lastEUN; block++) {
+		if (s->PUtable[block] == BLOCK_NOTEXPLORED) {
+			printk("INFTL: unreferenced block %d, formatting it\n",
+				block);
+			if (INFTL_formatblock(s, block) < 0)
+				s->PUtable[block] = BLOCK_RESERVED;
+			else
+				s->PUtable[block] = BLOCK_FREE;
+		}
+		if (s->PUtable[block] == BLOCK_FREE) {
+			s->numfreeEUNs++;
+			if (s->LastFreeEUN == BLOCK_NIL)
+				s->LastFreeEUN = block;
+		}
+	}
+
+	kfree(ANACtable);
+	return 0;
+}
diff -Nur linux-2.4.30/drivers/mtd/Makefile linux-2.4.30-linux-mips.org/drivers/mtd/Makefile
--- linux-2.4.30/drivers/mtd/Makefile	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -1,30 +1,7 @@
 #
 # Makefile for the memory technology device drivers.
 #
-# 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 inherited from the
-# parent makes..
-#
-# $Id: Makefile,v 1.65 2002/03/22 07:10:34 dwmw2 Exp $
-
-
-obj-y           += chips/chipslink.o maps/mapslink.o \
-			devices/devlink.o nand/nandlink.o
-obj-m           :=
-obj-n           :=
-obj-            :=
-
-O_TARGET	:= mtdlink.o
-
-export-objs	:= mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o mtdconcat.o
-list-multi	:= nftl.o
-
-mod-subdirs	:= 
-subdir-y	:= chips maps devices nand
-subdir-m	:= $(subdir-y)
+# $Id: Makefile.common,v 1.2 2003/05/23 11:38:29 dwmw2 Exp $
 
 #                       *** BIG UGLY NOTE ***
 #
@@ -52,15 +29,44 @@
 
 # 'Users' - code which presents functionality to userspace.
 obj-$(CONFIG_MTD_CHAR)		+= mtdchar.o
-obj-$(CONFIG_MTD_BLOCK)		+= mtdblock.o
-obj-$(CONFIG_MTD_BLOCK_RO)	+= mtdblock_ro.o
-obj-$(CONFIG_FTL)		+= ftl.o
-obj-$(CONFIG_NFTL)		+= nftl.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
 
-nftl-objs	:= nftlcore.o nftlmount.o
+ifeq ($(PATCHLEVEL),4)
+
+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
+
+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
+
+mod-subdirs	:= 
+subdir-y	:= chips maps devices nand
+subdir-m	:= $(subdir-y)
 
 include $(TOPDIR)/Rules.make
 
 nftl.o: $(nftl-objs)
 	$(LD) -r -o $@ $(nftl-objs)
 
+inftl.o: $(inftl-objs)
+	$(LD) -r -o $@ $(inftl-objs)
+
+mtd_blkdevs.o: $(mtd_blkdevs-objs)
+	$(LD) -r -o $@ $(mtd_blkdevs-objs)
+
+else
+obj-y		+= chips/ maps/ devices/ nand/
+endif
diff -Nur linux-2.4.30/drivers/mtd/maps/alchemy-flash.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/alchemy-flash.c
--- linux-2.4.30/drivers/mtd/maps/alchemy-flash.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/alchemy-flash.c	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,261 @@
+/*
+ * Flash memory access on AMD Alchemy evaluation boards
+ * 
+ * $Id: db1550-flash.c,v 1.7 2004/11/04 13:24:14 gleixner Exp $
+ *
+ * (C) 2004 Embedded Edge, LLC, based on db1550-flash.c:
+ * (C) 2003, 2004 Pete Popov <ppopov@embeddedalley.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>
+
+#ifdef 	DEBUG_RW
+#define	DBG(x...)	printk(x)
+#else
+#define	DBG(x...)	
+#endif
+
+#ifdef CONFIG_MIPS_PB1000
+#define BOARD_MAP_NAME "Pb1000 Flash"
+#define BOARD_FLASH_SIZE 0x00800000 /* 8MB */
+#define BOARD_FLASH_WIDTH 4 /* 32-bits */
+#endif
+
+#ifdef CONFIG_MIPS_PB1500
+#define BOARD_MAP_NAME "Pb1500 Flash"
+#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */
+#define BOARD_FLASH_WIDTH 4 /* 32-bits */
+#endif
+
+#ifdef CONFIG_MIPS_PB1100
+#define BOARD_MAP_NAME "Pb1100 Flash"
+#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */
+#define BOARD_FLASH_WIDTH 4 /* 32-bits */
+#endif
+
+#ifdef CONFIG_MIPS_PB1550
+#define BOARD_MAP_NAME "Pb1550 Flash"
+#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */
+#define BOARD_FLASH_WIDTH 4 /* 32-bits */
+#endif
+
+#ifdef CONFIG_MIPS_PB1200
+#define BOARD_MAP_NAME "Pb1200 Flash"
+#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */
+#define BOARD_FLASH_WIDTH 2 /* 16-bits */
+#endif
+
+#ifdef CONFIG_MIPS_DB1000
+#define BOARD_MAP_NAME "Db1000 Flash"
+#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
+#define BOARD_FLASH_WIDTH 4 /* 32-bits */
+#endif
+
+#ifdef CONFIG_MIPS_DB1500
+#define BOARD_MAP_NAME "Db1500 Flash"
+#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
+#define BOARD_FLASH_WIDTH 4 /* 32-bits */
+#endif
+
+#ifdef CONFIG_MIPS_DB1100
+#define BOARD_MAP_NAME "Db1100 Flash"
+#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
+#define BOARD_FLASH_WIDTH 4 /* 32-bits */
+#endif
+
+#ifdef CONFIG_MIPS_DB1550
+#define BOARD_MAP_NAME "Db1550 Flash"
+#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */
+#define BOARD_FLASH_WIDTH 4 /* 32-bits */
+#endif
+
+#ifdef CONFIG_MIPS_DB1200
+#define BOARD_MAP_NAME "Db1200 Flash"
+#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */
+#define BOARD_FLASH_WIDTH 2 /* 16-bits */
+#endif
+
+#ifdef CONFIG_MIPS_HYDROGEN3
+#define BOARD_MAP_NAME "Hydrogen3 Flash"
+#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */
+#define BOARD_FLASH_WIDTH 4 /* 32-bits */
+#define USE_LOCAL_ACCESSORS /* why? */
+#endif
+
+#ifdef CONFIG_MIPS_BOSPORUS
+#define BOARD_MAP_NAME "Bosporus Flash"
+#define BOARD_FLASH_SIZE 0x01000000 /* 16MB */
+#define BOARD_FLASH_WIDTH 2 /* 16-bits */
+#endif
+
+#ifdef CONFIG_MIPS_MIRAGE
+#define BOARD_MAP_NAME "Mirage Flash"
+#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */
+#define BOARD_FLASH_WIDTH 4 /* 32-bits */
+#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[] = {
+        {
+                .name = "User FS",
+                .size = BOARD_FLASH_SIZE - 0x00400000,
+                .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,
+        }
+};
+
+#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
+
+static struct mtd_info *mymtd;
+
+int __init alchemy_mtd_init(void)
+{
+	struct mtd_partition *parts;
+	int nb_parts = 0;
+	unsigned long window_addr;
+	unsigned long window_size;
+	
+	/* Default flash buswidth */
+	alchemy_map.buswidth = BOARD_FLASH_WIDTH;
+
+	window_addr = 0x20000000 - BOARD_FLASH_SIZE;
+	window_size = BOARD_FLASH_SIZE;
+#ifdef CONFIG_MIPS_MIRAGE_WHY
+	/* Boot ROM flash bank only; no user bank */
+	window_addr = 0x1C000000;
+	window_size = 0x04000000;
+	/* USERFS from 0x1C00 0000 to 0x1FC00000 */
+	alchemy_partitions[0].size = 0x03C00000;
+#endif
+
+	/*
+	 * Static partition definition selection
+	 */
+	parts = alchemy_partitions;
+	nb_parts = NB_OF(alchemy_partitions);
+	alchemy_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 BOARD_MAP_NAME ": probing %d-bit flash bus\n", 
+			alchemy_map.buswidth*8);
+	alchemy_map.virt = ioremap(window_addr, window_size);
+	mymtd = do_map_probe("cfi_probe", &alchemy_map);
+	if (!mymtd) return -ENXIO;
+	mymtd->owner = THIS_MODULE;
+
+	add_mtd_partitions(mymtd, parts, nb_parts);
+	return 0;
+}
+
+static void __exit alchemy_mtd_cleanup(void)
+{
+	if (mymtd) {
+		del_mtd_partitions(mymtd);
+		map_destroy(mymtd);
+		iounmap((void *) alchemy_map.virt);
+	}
+}
+
+module_init(alchemy_mtd_init);
+module_exit(alchemy_mtd_cleanup);
+
+MODULE_AUTHOR("Embedded Edge, LLC");
+MODULE_DESCRIPTION(BOARD_MAP_NAME " MTD driver");
+MODULE_LICENSE("GPL");
+
diff -Nur linux-2.4.30/drivers/mtd/maps/amd76xrom.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/amd76xrom.c
--- linux-2.4.30/drivers/mtd/maps/amd76xrom.c	2004-11-17 12:54:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/amd76xrom.c	2005-05-12 18:01:29.000000000 +0200
@@ -2,12 +2,13 @@
  * amd76xrom.c
  *
  * Normal mappings of chips in physical memory
- * $Id: amd76xrom.c,v 1.1 2002/10/18 22:45:48 eric Exp $
+ * $Id: amd76xrom.c,v 1.9 2003/10/23 23:10:59 thayne Exp $
  */
 
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -16,77 +17,59 @@
 #include <linux/pci_ids.h>
 
 
+#define xstr(s) str(s)
+#define str(s) #s
+#define MOD_NAME xstr(KBUILD_BASENAME)
+
+#define MTD_DEV_NAME_LENGTH 16
+
 struct amd76xrom_map_info {
 	struct map_info map;
 	struct mtd_info *mtd;
 	unsigned long window_addr;
 	u32 window_start, window_size;
 	struct pci_dev *pdev;
+	struct resource window_rsrc;
+	struct resource rom_rsrc;
+	char mtd_name[MTD_DEV_NAME_LENGTH];
 };
 
-static __u8 amd76xrom_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-static __u16 amd76xrom_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
-
-static __u32 amd76xrom_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
 
-static void amd76xrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
+static struct amd76xrom_map_info amd76xrom_map = {
+	.map = {
+		.name = MOD_NAME,
+		.size = 0,
+		.buswidth = 1,
+	}
+	/* remaining fields of structure are initialized to 0 */
+};
 
-static void amd76xrom_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
 
-static void amd76xrom_write16(struct map_info *map, __u16 d, unsigned long adr)
+static void amd76xrom_cleanup(struct amd76xrom_map_info *info)
 {
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
+	u8 byte;
 
-static void amd76xrom_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
+	/* Disable writes through the rom window */
+	pci_read_config_byte(info->pdev, 0x40, &byte);
+	pci_write_config_byte(info->pdev, 0x40, byte & ~1);
 
-static void amd76xrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio(map->map_priv_1 + to, from, len);
+	if (info->mtd) {
+		del_mtd_device(info->mtd);
+		map_destroy(info->mtd);
+		info->mtd = NULL;
+		info->map.virt = 0;
+	}
+	if (info->rom_rsrc.parent)
+		release_resource(&info->rom_rsrc);
+	if (info->window_rsrc.parent)
+		release_resource(&info->window_rsrc);
+
+	if (info->window_addr) {
+		iounmap((void *)(info->window_addr));
+		info->window_addr = 0;
+	}
 }
 
-static struct amd76xrom_map_info amd76xrom_map = {
-	map: {
-		name: "AMD76X rom",
-		size: 0,
-		buswidth: 1,
-		read8: amd76xrom_read8,
-		read16: amd76xrom_read16,
-		read32: amd76xrom_read32,
-		copy_from: amd76xrom_copy_from,
-		write8: amd76xrom_write8,
-		write16: amd76xrom_write16,
-		write32: amd76xrom_write32,
-		copy_to: amd76xrom_copy_to,
-		/* The standard rom socket is for single power supply chips
-		 * that don't have an extra vpp.
-		 */
-	},
-	mtd: 0,
-	window_addr: 0,
-};
 
 static int __devinit amd76xrom_init_one (struct pci_dev *pdev,
 	const struct pci_device_id *ent)
@@ -97,6 +80,10 @@
 		u8 segen_bits;
 	};
 	static struct rom_window rom_window[] = {
+		/*
+		 * Need the 5MiB window for chips that have block lock/unlock
+		 * registers located below 4MiB window.
+		 */
 		{ 0xffb00000, 5*1024*1024, (1<<7) | (1<<6), },
 		{ 0xffc00000, 4*1024*1024, (1<<7), },
 		{ 0xffff0000, 64*1024,     0 },
@@ -112,73 +99,128 @@
 	int i;
 	u32 rom_size;
 
+	info->pdev = pdev;
 	window = &rom_window[0];
-#if 0
-	while(window->size) {
-		if (request_mem_region(window->start, window->size, "amd76xrom")) {
-			break;
+
+	while (window->size) {
+		/*
+		 * Try to reserve the window mem region.  If this fails then
+		 * it is likely due to a fragment of the window being
+		 * "reseved" by the BIOS.  In the case that the
+		 * request_mem_region() fails then once the rom size is
+		 * discovered we will try to reserve the unreserved fragment.
+		 */
+		info->window_rsrc.name = MOD_NAME;
+		info->window_rsrc.start = window->start;
+		info->window_rsrc.end = window->start + window->size - 1;
+		info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+		if (request_resource(&iomem_resource, &info->window_rsrc)) {
+			info->window_rsrc.parent = NULL;
+			printk(KERN_ERR MOD_NAME
+			       " %s(): Unable to register resource"
+			       " 0x%.08lx-0x%.08lx - kernel bug?\n",
+			       __func__,
+			       info->window_rsrc.start, info->window_rsrc.end);
 		}
-		window++;
-	}
-	if (!window->size) {
-		printk(KERN_ERR "amd76xrom: cannot reserve rom window\n");
-		goto err_out_none;
-	}
-#endif
 
-	/* Enable the selected rom window */
-	pci_read_config_byte(pdev, 0x43, &byte);
-	pci_write_config_byte(pdev, 0x43, byte | window->segen_bits);
-
-	/* Enable writes through the rom window */
-	pci_read_config_byte(pdev, 0x40, &byte);
-	pci_write_config_byte(pdev, 0x40, byte | 1);
-
-	/* FIXME handle registers 0x80 - 0x8C the bios region locks */
-
-	printk(KERN_NOTICE "amd76xrom window : %x at %x\n", 
-		window->size, window->start);
-	/* For write accesses caches are useless */
-	info->window_addr = (unsigned long)ioremap_nocache(window->start, window->size);
-
-	if (!info->window_addr) {
-		printk(KERN_ERR "Failed to ioremap\n");
-		goto err_out_free_mmio_region;
-	}
-	info->mtd = 0;
-	for(i = 0; (rom_size = rom_probe_sizes[i]); i++) {
-		char **chip_type;
-		if (rom_size > window->size) {
+		/* Enable the selected rom window */
+		pci_read_config_byte(pdev, 0x43, &byte);
+		pci_write_config_byte(pdev, 0x43, byte | window->segen_bits);
+
+		/* Enable writes through the rom window */
+		pci_read_config_byte(pdev, 0x40, &byte);
+		pci_write_config_byte(pdev, 0x40, byte | 1);
+
+		/* FIXME handle registers 0x80 - 0x8C the bios region locks */
+
+		printk(KERN_NOTICE MOD_NAME " window : %x at %x\n", 
+		       window->size, window->start);
+		/* For write accesses caches are useless */
+		info->window_addr =
+			(unsigned long)ioremap_nocache(window->start,
+						       window->size);
+
+		if (!info->window_addr) {
+			printk(KERN_ERR "Failed to ioremap\n");
 			continue;
 		}
-		info->map.map_priv_1 = 
-			info->window_addr + window->size - rom_size;
-		info->map.size = rom_size;
-		chip_type = rom_probe_types;
-		for(; !info->mtd && *chip_type; chip_type++) {
-			info->mtd = do_map_probe(*chip_type, &amd76xrom_map.map);
-		}
-		if (info->mtd) {
-			break;
+
+		info->mtd = NULL;
+
+		for(i = 0; (rom_size = rom_probe_sizes[i]); i++) {
+			char **chip_type;
+			if (rom_size > window->size) {
+				continue;
+			}
+			info->map.phys = window->start + window->size - rom_size;
+			info->map.virt = 
+				info->window_addr + window->size - rom_size;
+			info->map.size = rom_size;
+			simple_map_init(&info->map);
+			chip_type = rom_probe_types;
+			for(; !info->mtd && *chip_type; chip_type++) {
+				info->mtd = do_map_probe(*chip_type, &amd76xrom_map.map);
+			}
+			if (info->mtd) goto found_mtd;
 		}
+		iounmap((void *)(info->window_addr));
+		info->window_addr = 0;
+
+		/* Disable writes through the rom window */
+		pci_read_config_byte(pdev, 0x40, &byte);
+		pci_write_config_byte(pdev, 0x40, byte & ~1);
+
+		window++;
 	}
-	if (!info->mtd) {
-		goto err_out_iounmap;
-	}
-	printk(KERN_NOTICE "amd76xrom chip at offset: %x\n",
+	goto failed;
+
+ found_mtd:
+	printk(KERN_NOTICE MOD_NAME " chip at offset: 0x%x\n",
 		window->size - rom_size);
-		
-	info->mtd->module = THIS_MODULE;
+
+	info->mtd->owner = THIS_MODULE;
+
+	if (!info->window_rsrc.parent) {
+		/* failed to reserve entire window - try fragments */
+		info->window_rsrc.name = MOD_NAME;
+		info->window_rsrc.start = window->start;
+		info->window_rsrc.end = window->start + window->size - rom_size - 1;
+		info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+		if (request_resource(&iomem_resource, &info->window_rsrc)) {
+			printk(KERN_ERR MOD_NAME
+			       ": cannot reserve window resource fragment\n");
+			goto failed;
+		}
+	}
+
 	add_mtd_device(info->mtd);
 	info->window_start = window->start;
 	info->window_size = window->size;
+
+	if (info->window_rsrc.parent) {
+		/*
+		 * Registering the MTD device in iomem may not be possible
+		 * if there is a BIOS "reserved" and BUSY range.  If this
+		 * fails then continue anyway.
+		 */
+		snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH,
+			 "mtd%d", info->mtd->index);
+
+		info->rom_rsrc.name = info->mtd_name;
+		info->rom_rsrc.start = window->start + window->size - rom_size;
+		info->rom_rsrc.end = window->start + window->size - 1;
+		info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+		if (request_resource(&info->window_rsrc, &info->rom_rsrc)) {
+			printk(KERN_ERR MOD_NAME
+			       ": cannot reserve MTD resource\n");
+			info->rom_rsrc.parent = NULL;
+		}
+	}
+
 	return 0;
 
-err_out_iounmap:
-	iounmap((void *)(info->window_addr));
-err_out_free_mmio_region:
-	release_mem_region(window->start, window->size);
-//err_out_none:
+ failed:
+	amd76xrom_cleanup(info);
 	return -ENODEV;
 }
 
@@ -186,21 +228,8 @@
 static void __devexit amd76xrom_remove_one (struct pci_dev *pdev)
 {
 	struct amd76xrom_map_info *info = &amd76xrom_map;
-	u8 byte;
 
-	del_mtd_device(info->mtd);
-	map_destroy(info->mtd);
-	info->mtd = 0;
-	info->map.map_priv_1 = 0;
-
-	iounmap((void *)(info->window_addr));
-	info->window_addr = 0;
-
-	/* Disable writes through the rom window */
-	pci_read_config_byte(pdev, 0x40, &byte);
-	pci_write_config_byte(pdev, 0x40, byte & ~1);
-	
-	release_mem_region(info->window_start, info->window_size);
+	amd76xrom_cleanup(info);
 }
 
 static struct pci_device_id amd76xrom_pci_tbl[] __devinitdata = {
@@ -208,6 +237,7 @@
 		PCI_ANY_ID, PCI_ANY_ID, },
 	{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7440,  
 		PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_AMD, 0x7468 }, /* amd8111 support */
 	{ 0, }
 };
 
@@ -215,10 +245,10 @@
 
 #if 0
 static struct pci_driver amd76xrom_driver = {
-	name:	  "amd76xrom",
-	id_table: amd76xrom_pci_tbl,
-	probe:    amd76xrom_init_one,
-	remove:   amd76xrom_remove_one,
+	.name =		MOD_NAME,
+	.id_table =	amd76xrom_pci_tbl,
+	.probe =	amd76xrom_init_one,
+	.remove =	amd76xrom_remove_one,
 };
 #endif
 
diff -Nur linux-2.4.30/drivers/mtd/maps/arctic-mtd.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/arctic-mtd.c
--- linux-2.4.30/drivers/mtd/maps/arctic-mtd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/arctic-mtd.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,135 @@
+/*
+ * $Id: arctic-mtd.c,v 1.10 2003/06/02 16:37:59 trini Exp $
+ * 
+ * drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for 
+ *                              IBM 405LP Arctic boards.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright (C) 2002, International Business Machines Corporation
+ * All Rights Reserved.
+ *
+ * Bishop Brock
+ * IBM Research, Austin Center for Low-Power Computing
+ * bcbrock@us.ibm.com
+ * March 2002
+ *
+ * modified for Arctic by,
+ * David Gibson
+ * IBM OzLabs, Canberra, Australia
+ * <arctic@gibson.dropbear.id.au>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/ibm4xx.h>
+
+/*
+ * 0 : 0xFE00 0000 - 0xFEFF FFFF : Filesystem 1 (16MiB)
+ * 1 : 0xFF00 0000 - 0xFF4F FFFF : kernel (5.12MiB)
+ * 2 : 0xFF50 0000 - 0xFFF5 FFFF : Filesystem 2 (10.624MiB) (if non-XIP)
+ * 3 : 0xFFF6 0000 - 0xFFFF FFFF : PIBS Firmware (640KiB)
+ */
+
+#define FFS1_SIZE	0x01000000 /* 16MiB */
+#define KERNEL_SIZE	0x00500000 /* 5.12MiB */
+#define FFS2_SIZE	0x00a60000 /* 10.624MiB */
+#define FIRMWARE_SIZE	0x000a0000 /* 640KiB */
+
+
+#define NAME     	"Arctic Linux Flash"
+#define PADDR    	SUBZERO_BOOTFLASH_PADDR
+#define BUSWIDTH	2
+#define SIZE		SUBZERO_BOOTFLASH_SIZE
+#define PARTITIONS	4
+
+/* Flash memories on these boards are memory resources, accessed big-endian. */
+
+{
+  /* do nothing for now */
+}
+
+static struct map_info arctic_mtd_map = {
+	.name		= NAME,
+	.size		= SIZE,
+	.buswidth	= BUSWIDTH,
+	.phys		= PADDR,
+};
+
+static struct mtd_info *arctic_mtd;
+
+static struct mtd_partition arctic_partitions[PARTITIONS] = {
+	{ .name		= "Filesystem",
+	  .size		= FFS1_SIZE,
+	  .offset	= 0,},
+        { .name		= "Kernel",
+	  .size		= KERNEL_SIZE,
+	  .offset	= FFS1_SIZE,},
+	{ .name		= "Filesystem",
+	  .size		= FFS2_SIZE,
+	  .offset	= FFS1_SIZE + KERNEL_SIZE,},
+	{ .name		= "Firmware",
+	  .size		= FIRMWARE_SIZE,
+	  .offset	= SUBZERO_BOOTFLASH_SIZE - FIRMWARE_SIZE,},
+};
+
+static int __init
+init_arctic_mtd(void)
+{
+	printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
+
+	arctic_mtd_map.virt = (unsigned long) ioremap(PADDR, SIZE);
+
+	if (!arctic_mtd_map.virt) {
+		printk("%s: failed to ioremap 0x%x\n", NAME, PADDR);
+		return -EIO;
+	}
+	simple_map_init(&arctic_mtd_map);
+
+	printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8);
+	arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map);
+
+	if (!arctic_mtd)
+		return -ENXIO;
+
+	arctic_mtd->owner = THIS_MODULE;
+
+	return add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS);
+}
+
+static void __exit
+cleanup_arctic_mtd(void)
+{
+	if (arctic_mtd) {
+		del_mtd_partitions(arctic_mtd);
+		map_destroy(arctic_mtd);
+		iounmap((void *) arctic_mtd_map.virt);
+	}
+}
+
+module_init(init_arctic_mtd);
+module_exit(cleanup_arctic_mtd);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Gibson <arctic@gibson.dropbear.id.au>");
+MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Arctic boards");
diff -Nur linux-2.4.30/drivers/mtd/maps/autcpu12-nvram.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/autcpu12-nvram.c
--- linux-2.4.30/drivers/mtd/maps/autcpu12-nvram.c	2002-08-03 02:39:44.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/autcpu12-nvram.c	2005-05-12 18:01:29.000000000 +0200
@@ -2,7 +2,7 @@
  * NV-RAM memory access on autcpu12 
  * (C) 2002 Thomas Gleixner (gleixner@autronix.de)
  *
- * $Id: autcpu12-nvram.c,v 1.1 2002/02/22 09:30:24 gleixner Exp $ 
+ * $Id: autcpu12-nvram.c,v 1.5 2003/05/21 12:45:18 dwmw2 Exp $ 
  *
  * 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
@@ -24,6 +24,7 @@
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/ioport.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <asm/sizes.h>
 #include <asm/hardware.h>
@@ -32,81 +33,28 @@
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
 
-__u8 autcpu12_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-__u16 autcpu12_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
-
-__u32 autcpu12_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
-
-void autcpu12_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void autcpu12_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void autcpu12_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void autcpu12_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-void autcpu12_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	while(len) {
-		__raw_writeb(*(unsigned char *) from, map->map_priv_1 + to);
-		from++;
-		to++;
-		len--;
-	}
-}
 
 static struct mtd_info *sram_mtd;
 
 struct map_info autcpu12_sram_map = {
-	name: "SRAM",
-	size: 32768,
-	buswidth: 8,
-	read8: autcpu12_read8,
-	read16: autcpu12_read16,
-	read32: autcpu12_read32,
-	copy_from: autcpu12_copy_from,
-	write8: autcpu12_write8,
-	write16: autcpu12_write16,
-	write32: autcpu12_write32,
-	copy_to: autcpu12_copy_to
+	.name = "SRAM",
+	.size = 32768,
+	.buswidth = 4,
+	.phys = 0x12000000,
 };
 
 static int __init init_autcpu12_sram (void)
 {
 	int err, save0, save1;
 
-	autcpu12_sram_map.map_priv_1 = (unsigned long)ioremap(0x12000000, SZ_128K);
-	if (!autcpu12_sram_map.map_priv_1) {
+	autcpu12_sram_map.virt = (unsigned long)ioremap(0x12000000, SZ_128K);
+	if (!autcpu12_sram_map.virt) {
 		printk("Failed to ioremap autcpu12 NV-RAM space\n");
 		err = -EIO;
 		goto out;
 	}
-	
+	simple_map_init(&autcpu_sram_map);
+
 	/* 
 	 * Check for 32K/128K 
 	 * read ofs 0 
@@ -115,20 +63,20 @@
 	 * Read	and check result on ofs 0x0
 	 * Restore contents
 	 */
-	save0 = autcpu12_read32(&autcpu12_sram_map,0);
-	save1 = autcpu12_read32(&autcpu12_sram_map,0x10000);
-	autcpu12_write32(&autcpu12_sram_map,~save0,0x10000);
+	save0 = map_read32(&autcpu12_sram_map,0);
+	save1 = map_read32(&autcpu12_sram_map,0x10000);
+	map_write32(&autcpu12_sram_map,~save0,0x10000);
 	/* if we find this pattern on 0x0, we have 32K size 
 	 * restore contents and exit
 	 */
-	if ( autcpu12_read32(&autcpu12_sram_map,0) != save0) {
-		autcpu12_write32(&autcpu12_sram_map,save0,0x0);
+	if ( map_read32(&autcpu12_sram_map,0) != save0) {
+		map_write32(&autcpu12_sram_map,save0,0x0);
 		goto map;
 	}
 	/* We have a 128K found, restore 0x10000 and set size
 	 * to 128K
 	 */
-	autcpu12_write32(&autcpu12_sram_map,save1,0x10000);
+	ma[_write32(&autcpu12_sram_map,save1,0x10000);
 	autcpu12_sram_map.size = SZ_128K;
 
 map:
@@ -139,7 +87,7 @@
 		goto out_ioremap;
 	}
 
-	sram_mtd->module = THIS_MODULE;
+	sram_mtd->owner = THIS_MODULE;
 	sram_mtd->erasesize = 16;
 	
 	if (add_mtd_device(sram_mtd)) {
@@ -148,7 +96,7 @@
 		goto out_probe;
 	}
 
-	printk("NV-RAM device size %ldK registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K);
+	printk("NV-RAM device size %ldKiB registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K);
 		
 	return 0;
 
@@ -157,7 +105,7 @@
 	sram_mtd = 0;
 
 out_ioremap:
-	iounmap((void *)autcpu12_sram_map.map_priv_1);
+	iounmap((void *)autcpu12_sram_map.virt);
 out:
 	return err;
 }
@@ -167,7 +115,7 @@
 	if (sram_mtd) {
 		del_mtd_device(sram_mtd);
 		map_destroy(sram_mtd);
-		iounmap((void *)autcpu12_sram_map.map_priv_1);
+		iounmap((void *)autcpu12_sram_map.virt);
 	}
 }
 
diff -Nur linux-2.4.30/drivers/mtd/maps/beech-mtd.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/beech-mtd.c
--- linux-2.4.30/drivers/mtd/maps/beech-mtd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/beech-mtd.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,112 @@
+/*
+ * $Id: beech-mtd.c,v 1.7 2003/05/21 12:45:18 dwmw2 Exp $
+ * 
+ * drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for 
+ *                              IBM 405LP Beech boards.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright (C) 2002, International Business Machines Corporation
+ * All Rights Reserved.
+ *
+ * Bishop Brock
+ * IBM Research, Austin Center for Low-Power Computing
+ * bcbrock@us.ibm.com
+ * March 2002
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/ibm4xx.h>
+
+#define NAME     "Beech Linux Flash"
+#define PADDR    BEECH_BIGFLASH_PADDR
+#define SIZE     BEECH_BIGFLASH_SIZE
+#define BUSWIDTH 1
+
+/* Flash memories on these boards are memory resources, accessed big-endian. */
+
+
+static struct map_info beech_mtd_map = {
+	.name =		NAME,
+	.size =		SIZE,
+	.buswidth =	BUSWIDTH,
+	.phys =		PADDR
+};
+
+static struct mtd_info *beech_mtd;
+
+static struct mtd_partition beech_partitions[2] = {
+	{
+	      .name = "Linux Kernel",
+	      .size = BEECH_KERNEL_SIZE,
+	      .offset = BEECH_KERNEL_OFFSET
+	}, {
+	      .name = "Free Area",
+	      .size = BEECH_FREE_AREA_SIZE,
+	      .offset = BEECH_FREE_AREA_OFFSET
+	}
+};
+
+static int __init
+init_beech_mtd(void)
+{
+	printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
+
+	beech_mtd_map.virt = (unsigned long) ioremap(PADDR, SIZE);
+
+	if (!beech_mtd_map.virt) {
+		printk("%s: failed to ioremap 0x%x\n", NAME, PADDR);
+		return -EIO;
+	}
+
+	simple_map_init(&beech_mtd_map);
+
+	printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8);
+	beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map);
+
+	if (!beech_mtd)
+		return -ENXIO;
+
+	beech_mtd->owner = THIS_MODULE;
+
+	return add_mtd_partitions(beech_mtd, beech_partitions, 2);
+}
+
+static void __exit
+cleanup_beech_mtd(void)
+{
+	if (beech_mtd) {
+		del_mtd_partitions(beech_mtd);
+		map_destroy(beech_mtd);
+		iounmap((void *) beech_mtd_map.virt);
+	}
+}
+
+module_init(init_beech_mtd);
+module_exit(cleanup_beech_mtd);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Bishop Brock <bcbrock@us.ibm.com>");
+MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Beech boards");
diff -Nur linux-2.4.30/drivers/mtd/maps/cdb89712.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/cdb89712.c
--- linux-2.4.30/drivers/mtd/maps/cdb89712.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/cdb89712.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,13 +1,14 @@
 /*
  * Flash on Cirrus CDB89712
  *
- * $Id: cdb89712.c,v 1.3 2001/10/02 15:14:43 rmk Exp $
+ * $Id: cdb89712.c,v 1.7 2003/05/21 12:45:18 dwmw2 Exp $
  */
 
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/ioport.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <asm/arch/hardware.h>
 #include <linux/mtd/mtd.h>
@@ -16,77 +17,21 @@
 
 
 
-__u8 cdb89712_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-__u16 cdb89712_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
-
-__u32 cdb89712_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
-
-void cdb89712_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void cdb89712_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void cdb89712_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void cdb89712_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	// printk ("cdb89712_copy_from: 0x%x@0x%x -> 0x%x\n", len, from, to);
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-void cdb89712_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	while(len) {
-		__raw_writeb(*(unsigned char *) from, map->map_priv_1 + to);
-		from++;
-		to++;
-		len--;
-	}
-}
-
 
 static struct mtd_info *flash_mtd;
 
 struct map_info cdb89712_flash_map = {
-	name: "flash",
-	size: FLASH_SIZE,
-	buswidth: FLASH_WIDTH,
-	read8: cdb89712_read8,
-	read16: cdb89712_read16,
-	read32: cdb89712_read32,
-	copy_from: cdb89712_copy_from,
-	write8: cdb89712_write8,
-	write16: cdb89712_write16,
-	write32: cdb89712_write32,
-	copy_to: cdb89712_copy_to
+	.name = "flash",
+	.size = FLASH_SIZE,
+	.buswidth = FLASH_WIDTH,
+	.phys = FLASH_START,
 };
 
 struct resource cdb89712_flash_resource = {
-	name:   "Flash",
-	start:  FLASH_START,
-	end:    FLASH_START + FLASH_SIZE - 1,
-	flags:  IORESOURCE_IO | IORESOURCE_BUSY,
+	.name =   "Flash",
+	.start =  FLASH_START,
+	.end =    FLASH_START + FLASH_SIZE - 1,
+	.flags =  IORESOURCE_IO | IORESOURCE_BUSY,
 };
 
 static int __init init_cdb89712_flash (void)
@@ -99,13 +44,13 @@
 		goto out;
 	}
 	
-	cdb89712_flash_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE);
-	if (!cdb89712_flash_map.map_priv_1) {
+	cdb89712_flash_map.virt = (unsigned long)ioremap(FLASH_START, FLASH_SIZE);
+	if (!cdb89712_flash_map.virt) {
 		printk(KERN_NOTICE "Failed to ioremap Cdb89712 FLASH space\n");
 		err = -EIO;
 		goto out_resource;
 	}
-
+	simple_map_init(&cdb89712_flash_map);
 	flash_mtd = do_map_probe("cfi_probe", &cdb89712_flash_map);
 	if (!flash_mtd) {
 		flash_mtd = do_map_probe("map_rom", &cdb89712_flash_map);
@@ -118,7 +63,7 @@
 		goto out_ioremap;
 	}
 
-	flash_mtd->module = THIS_MODULE;
+	flash_mtd->owner = THIS_MODULE;
 	
 	if (add_mtd_device(flash_mtd)) {
 		printk("FLASH device addition failed\n");
@@ -132,7 +77,7 @@
 	map_destroy(flash_mtd);
 	flash_mtd = 0;
 out_ioremap:
-	iounmap((void *)cdb89712_flash_map.map_priv_1);
+	iounmap((void *)cdb89712_flash_map.virt);
 out_resource:
 	release_resource (&cdb89712_flash_resource);
 out:
@@ -146,24 +91,17 @@
 static struct mtd_info *sram_mtd;
 
 struct map_info cdb89712_sram_map = {
-	name: "SRAM",
-	size: SRAM_SIZE,
-	buswidth: SRAM_WIDTH,
-	read8: cdb89712_read8,
-	read16: cdb89712_read16,
-	read32: cdb89712_read32,
-	copy_from: cdb89712_copy_from,
-	write8: cdb89712_write8,
-	write16: cdb89712_write16,
-	write32: cdb89712_write32,
-	copy_to: cdb89712_copy_to
+	.name = "SRAM",
+	.size = SRAM_SIZE,
+	.buswidth = SRAM_WIDTH,
+	.phys = SRAM_START,
 };
 
 struct resource cdb89712_sram_resource = {
-	name:   "SRAM",
-	start:  SRAM_START,
-	end:    SRAM_START + SRAM_SIZE - 1,
-	flags:  IORESOURCE_IO | IORESOURCE_BUSY,
+	.name =   "SRAM",
+	.start =  SRAM_START,
+	.end =    SRAM_START + SRAM_SIZE - 1,
+	.flags =  IORESOURCE_IO | IORESOURCE_BUSY,
 };
 
 static int __init init_cdb89712_sram (void)
@@ -176,13 +114,13 @@
 		goto out;
 	}
 	
-	cdb89712_sram_map.map_priv_1 = (unsigned long)ioremap(SRAM_START, SRAM_SIZE);
-	if (!cdb89712_sram_map.map_priv_1) {
+	cdb89712_sram_map.virt = (unsigned long)ioremap(SRAM_START, SRAM_SIZE);
+	if (!cdb89712_sram_map.virt) {
 		printk(KERN_NOTICE "Failed to ioremap Cdb89712 SRAM space\n");
 		err = -EIO;
 		goto out_resource;
 	}
-
+	simple_map_init(&cdb89712_sram_map);
 	sram_mtd = do_map_probe("map_ram", &cdb89712_sram_map);
 	if (!sram_mtd) {
 		printk("SRAM probe failed\n");
@@ -190,7 +128,7 @@
 		goto out_ioremap;
 	}
 
-	sram_mtd->module = THIS_MODULE;
+	sram_mtd->owner = THIS_MODULE;
 	sram_mtd->erasesize = 16;
 	
 	if (add_mtd_device(sram_mtd)) {
@@ -205,7 +143,7 @@
 	map_destroy(sram_mtd);
 	sram_mtd = 0;
 out_ioremap:
-	iounmap((void *)cdb89712_sram_map.map_priv_1);
+	iounmap((void *)cdb89712_sram_map.virt);
 out_resource:
 	release_resource (&cdb89712_sram_resource);
 out:
@@ -221,20 +159,17 @@
 static struct mtd_info *bootrom_mtd;
 
 struct map_info cdb89712_bootrom_map = {
-	name: "BootROM",
-	size: BOOTROM_SIZE,
-	buswidth: BOOTROM_WIDTH,
-	read8: cdb89712_read8,
-	read16: cdb89712_read16,
-	read32: cdb89712_read32,
-	copy_from: cdb89712_copy_from,
+	.name = "BootROM",
+	.size = BOOTROM_SIZE,
+	.buswidth = BOOTROM_WIDTH,
+	.phys = BOOTROM_START,
 };
 
 struct resource cdb89712_bootrom_resource = {
-	name:   "BootROM",
-	start:  BOOTROM_START,
-	end:    BOOTROM_START + BOOTROM_SIZE - 1,
-	flags:  IORESOURCE_IO | IORESOURCE_BUSY,
+	.name =   "BootROM",
+	.start =  BOOTROM_START,
+	.end =    BOOTROM_START + BOOTROM_SIZE - 1,
+	.flags =  IORESOURCE_IO | IORESOURCE_BUSY,
 };
 
 static int __init init_cdb89712_bootrom (void)
@@ -247,13 +182,13 @@
 		goto out;
 	}
 	
-	cdb89712_bootrom_map.map_priv_1 = (unsigned long)ioremap(BOOTROM_START, BOOTROM_SIZE);
-	if (!cdb89712_bootrom_map.map_priv_1) {
+	cdb89712_bootrom_map.virt = (unsigned long)ioremap(BOOTROM_START, BOOTROM_SIZE);
+	if (!cdb89712_bootrom_map.virt) {
 		printk(KERN_NOTICE "Failed to ioremap Cdb89712 BootROM space\n");
 		err = -EIO;
 		goto out_resource;
 	}
-
+	simple_map_init(&cdb89712_bootrom_map);
 	bootrom_mtd = do_map_probe("map_rom", &cdb89712_bootrom_map);
 	if (!bootrom_mtd) {
 		printk("BootROM probe failed\n");
@@ -261,7 +196,7 @@
 		goto out_ioremap;
 	}
 
-	bootrom_mtd->module = THIS_MODULE;
+	bootrom_mtd->owner = THIS_MODULE;
 	bootrom_mtd->erasesize = 0x10000;
 	
 	if (add_mtd_device(bootrom_mtd)) {
@@ -276,7 +211,7 @@
 	map_destroy(bootrom_mtd);
 	bootrom_mtd = 0;
 out_ioremap:
-	iounmap((void *)cdb89712_bootrom_map.map_priv_1);
+	iounmap((void *)cdb89712_bootrom_map.virt);
 out_resource:
 	release_resource (&cdb89712_bootrom_resource);
 out:
@@ -306,21 +241,21 @@
 	if (sram_mtd) {
 		del_mtd_device(sram_mtd);
 		map_destroy(sram_mtd);
-		iounmap((void *)cdb89712_sram_map.map_priv_1);
+		iounmap((void *)cdb89712_sram_map.virt);
 		release_resource (&cdb89712_sram_resource);
 	}
 	
 	if (flash_mtd) {
 		del_mtd_device(flash_mtd);
 		map_destroy(flash_mtd);
-		iounmap((void *)cdb89712_flash_map.map_priv_1);
+		iounmap((void *)cdb89712_flash_map.virt);
 		release_resource (&cdb89712_flash_resource);
 	}
 
 	if (bootrom_mtd) {
 		del_mtd_device(bootrom_mtd);
 		map_destroy(bootrom_mtd);
-		iounmap((void *)cdb89712_bootrom_map.map_priv_1);
+		iounmap((void *)cdb89712_bootrom_map.virt);
 		release_resource (&cdb89712_bootrom_resource);
 	}
 }
diff -Nur linux-2.4.30/drivers/mtd/maps/ceiva.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/ceiva.c
--- linux-2.4.30/drivers/mtd/maps/ceiva.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/ceiva.c	2005-05-12 18:01:29.000000000 +0200
@@ -11,7 +11,7 @@
  *
  * (C) 2000 Nicolas Pitre <nico@cam.org>
  *
- * $Id: ceiva.c,v 1.2 2002/10/14 12:50:22 rmk Exp $
+ * $Id: ceiva.c,v 1.8 2003/05/21 12:45:18 dwmw2 Exp $
  */
 
 #include <linux/config.h>
@@ -19,6 +19,7 @@
 #include <linux/types.h>
 #include <linux/ioport.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -31,62 +32,10 @@
 #include <asm/sizes.h>
 
 /*
- * This isnt complete yet, so...
+ * This isn't complete yet, so...
  */
 #define CONFIG_MTD_CEIVA_STATICMAP
 
-static __u8 clps_read8(struct map_info *map, unsigned long ofs)
-{
-	return readb(map->map_priv_1 + ofs);
-}
-
-static __u16 clps_read16(struct map_info *map, unsigned long ofs)
-{
-	return readw(map->map_priv_1 + ofs);
-}
-
-static __u32 clps_read32(struct map_info *map, unsigned long ofs)
-{
-	return readl(map->map_priv_1 + ofs);
-}
-
-static void clps_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy(to, (void *)(map->map_priv_1 + from), len);
-}
-
-static void clps_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	writeb(d, map->map_priv_1 + adr);
-}
-
-static void clps_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	writew(d, map->map_priv_1 + adr);
-}
-
-static void clps_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	writel(d, map->map_priv_1 + adr);
-}
-
-static void clps_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy((void *)(map->map_priv_1 + to), from, len);
-}
-
-static struct map_info clps_map __initdata = {
-	name:		"clps flash",
-	read8:		clps_read8,
-	read16:		clps_read16,
-	read32:		clps_read32,
-	copy_from:	clps_copy_from,
-	write8:		clps_write8,
-	write16:	clps_write16,
-	write32:	clps_write32,
-	copy_to:	clps_copy_to,
-};
-
 #ifdef CONFIG_MTD_CEIVA_STATICMAP
 /*
  * See include/linux/mtd/partitions.h for definition of the mtd_partition
@@ -176,7 +125,7 @@
 	maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL);
 	if (!maps)
 		return -ENOMEM;
-
+	memset(maps, 0, sizeof(struct map_info) * nr);
 	/*
 	 * Claim and then map the memory regions.
 	 */
@@ -191,7 +140,9 @@
 		}
 
 		clps[i].map = maps + i;
-		memcpy(clps[i].map, &clps_map, sizeof(struct map_info));
+
+		clps[i].map->name = "clps flash";
+		clps[i].map->phys = clps[i].base;
 
 		clps[i].vbase = ioremap(clps[i].base, clps[i].size);
 		if (!clps[i].vbase) {
@@ -199,16 +150,18 @@
 			break;
 		}
 
-		clps[i].map->map_priv_1 = (unsigned long)clps[i].vbase;
+		clps[i].map->virt = (unsigned long)clps[i].vbase;
 		clps[i].map->buswidth = clps[i].width;
 		clps[i].map->size = clps[i].size;
 
+		simple_map_init(&clps[i].map);
+
 		clps[i].mtd = do_map_probe("jedec_probe", clps[i].map);
 		if (clps[i].mtd == NULL) {
 			ret = -ENXIO;
 			break;
 		}
-		clps[i].mtd->module = THIS_MODULE;
+		clps[i].mtd->owner = THIS_MODULE;
 		subdev[i] = clps[i].mtd;
 
 		printk(KERN_INFO "clps flash: JEDEC device at 0x%08lx, %dMiB, "
@@ -318,10 +271,8 @@
 	return nr;
 }
 
-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
-extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *);
-
 static struct mtd_partition *parsed_parts;
+static const char *probes[] = { "cmdlinepart", "RedBoot", NULL };
 
 static void __init clps_locate_partitions(struct mtd_info *mtd)
 {
@@ -331,20 +282,11 @@
 		/*
 		 * Partition selection stuff.
 		 */
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-		nr_parts = parse_cmdline_partitions(mtd, &parsed_parts, "clps");
+		nr_parts = parse_mtd_partitions(mtd, probes, &parsed_parts, 0);
 		if (nr_parts > 0) {
 			part_type = "command line";
 			break;
 		}
-#endif
-#ifdef CONFIG_MTD_REDBOOT_PARTS
-		nr_parts = parse_redboot_partitions(mtd, &parsed_parts);
-		if (nr_parts > 0) {
-			part_type = "RedBoot";
-			break;
-		}
-#endif
 #ifdef CONFIG_MTD_CEIVA_STATICMAP
 		nr_parts = clps_static_partitions(&parsed_parts);
 		if (nr_parts > 0) {
diff -Nur linux-2.4.30/drivers/mtd/maps/cfi_flagadm.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/cfi_flagadm.c
--- linux-2.4.30/drivers/mtd/maps/cfi_flagadm.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/cfi_flagadm.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,7 +1,7 @@
 /*
  *  Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson <kd@flaga.is>
  *
- *  $Id: cfi_flagadm.c,v 1.7 2001/10/02 15:05:13 dwmw2 Exp $
+ *  $Id: cfi_flagadm.c,v 1.11 2003/05/21 12:45:18 dwmw2 Exp $
  *  
  *  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
@@ -27,6 +27,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -55,83 +56,33 @@
 #define FLASH_PARTITION3_ADDR 0x00240000
 #define FLASH_PARTITION3_SIZE 0x001C0000
 
-__u8 flagadm_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-__u16 flagadm_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
-
-__u32 flagadm_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
-
-void flagadm_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-void flagadm_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void flagadm_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void flagadm_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void flagadm_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio(map->map_priv_1 + to, from, len);
-}
 
 struct map_info flagadm_map = {
-		name: "FlagaDM flash device",
-		size: FLASH_SIZE,
-		buswidth: 2,
-		read8: flagadm_read8,
-		read16: flagadm_read16,
-		read32: flagadm_read32,
-		copy_from: flagadm_copy_from,
-		write8: flagadm_write8,
-		write16: flagadm_write16,
-		write32: flagadm_write32,
-		copy_to: flagadm_copy_to
+		.name =		"FlagaDM flash device",
+		.size =		FLASH_SIZE,
+		.buswidth =	2,
 };
 
 struct mtd_partition flagadm_parts[] = {
 	{
-		name	: "Bootloader",
-		offset	: FLASH_PARTITION0_ADDR,
-		size	: FLASH_PARTITION0_SIZE
+		.name =		"Bootloader",
+		.offset	=	FLASH_PARTITION0_ADDR,
+		.size =		FLASH_PARTITION0_SIZE
 	},
 	{
-		name	: "Kernel image",
-		offset	: FLASH_PARTITION1_ADDR,
-		size	: FLASH_PARTITION1_SIZE
+		.name =		"Kernel image",
+		.offset =	FLASH_PARTITION1_ADDR,
+		.size =		FLASH_PARTITION1_SIZE
 	},
 	{
-		name	: "Initial ramdisk image",
-		offset	: FLASH_PARTITION2_ADDR,
-		size	: FLASH_PARTITION2_SIZE
+		.name =		"Initial ramdisk image",
+		.offset =	FLASH_PARTITION2_ADDR,
+		.size =		FLASH_PARTITION2_SIZE
 	},
 	{	
-		name	: "Persistant storage",
-		offset	: FLASH_PARTITION3_ADDR,
-		size	: FLASH_PARTITION3_SIZE
+		.name =		"Persistant storage",
+		.offset =	FLASH_PARTITION3_ADDR,
+		.size =		FLASH_PARTITION3_SIZE
 	}
 };
 
@@ -144,22 +95,26 @@
 	printk(KERN_NOTICE "FlagaDM flash device: %x at %x\n",
 			FLASH_SIZE, FLASH_PHYS_ADDR);
 	
-	flagadm_map.map_priv_1 = (unsigned long)ioremap(FLASH_PHYS_ADDR,
+	flagadm_map.phys = FLASH_PHYS_ADDR;
+	flagadm_map.virt = (unsigned long)ioremap(FLASH_PHYS_ADDR,
 					FLASH_SIZE);
 
-	if (!flagadm_map.map_priv_1) {
+	if (!flagadm_map.virt) {
 		printk("Failed to ioremap\n");
 		return -EIO;
 	}
+
+	simple_map_init(&flagadm_map);
+
 	mymtd = do_map_probe("cfi_probe", &flagadm_map);
 	if (mymtd) {
-		mymtd->module = THIS_MODULE;
+		mymtd->owner = THIS_MODULE;
 		add_mtd_partitions(mymtd, flagadm_parts, PARTITION_COUNT);
 		printk(KERN_NOTICE "FlagaDM flash device initialized\n");
 		return 0;
 	}
 
-	iounmap((void *)flagadm_map.map_priv_1);
+	iounmap((void *)flagadm_map.virt);
 	return -ENXIO;
 }
 
@@ -169,9 +124,9 @@
 		del_mtd_partitions(mymtd);
 		map_destroy(mymtd);
 	}
-	if (flagadm_map.map_priv_1) {
-		iounmap((void *)flagadm_map.map_priv_1);
-		flagadm_map.map_priv_1 = 0;
+	if (flagadm_map.virt) {
+		iounmap((void *)flagadm_map.virt);
+		flagadm_map.virt = 0;
 	}
 }
 
diff -Nur linux-2.4.30/drivers/mtd/maps/Config.in linux-2.4.30-linux-mips.org/drivers/mtd/maps/Config.in
--- linux-2.4.30/drivers/mtd/maps/Config.in	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/Config.in	2005-05-12 18:01:30.000000000 +0200
@@ -1,17 +1,14 @@
 # drivers/mtd/maps/Config.in
 
-# $Id: Config.in,v 1.43 2003/01/24 14:26:38 dwmw2 Exp $
+# $Id: Config.in,v 1.67 2004/04/12 06:38:39 ppopov Exp $
 
 mainmenu_option next_comment
 
 comment 'Mapping drivers for chip access'
 
-dep_tristate '  CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_GEN_PROBE
-if [ "$CONFIG_MTD_PHYSMAP" = "y" -o "$CONFIG_MTD_PHYSMAP" = "m" ]; 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 '    Bus width in octets' CONFIG_MTD_PHYSMAP_BUSWIDTH 2
-fi
+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_SPARC" = "y" -o "$CONFIG_SPARC64" = "y" ]; then
    dep_tristate '  Sun Microsystems userflash support' CONFIG_MTD_SUN_UFLASH $CONFIG_MTD_CFI
@@ -21,41 +18,74 @@
    dep_tristate '  CFI Flash device mapped on Photron PNC-2000' CONFIG_MTD_PNC2000 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS
    dep_tristate '  CFI Flash device mapped on AMD SC520 CDP' CONFIG_MTD_SC520CDP $CONFIG_MTD_CFI
    dep_tristate '  CFI Flash device mapped on AMD NetSc520'  CONFIG_MTD_NETSC520 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS
-   dep_tristate '  CFI Flash device mapped on Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS
-   dep_tristate '  CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS
+   dep_tristate '  CFI Flash device mapped on Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_COMPLEX_MAPPINGS
+   dep_tristate '  CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_COMPLEX_MAPPINGS
    dep_tristate '  CFI Flash device mapped on DIL/Net PC' CONFIG_MTD_DILNETPC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_CONCAT
    if [ "$CONFIG_MTD_DILNETPC" = "y" -o "$CONFIG_MTD_DILNETPC" = "m" ]; then
      hex '    Size of boot partition' CONFIG_MTD_DILNETPC_BOOTSIZE 0x80000
    fi
-   dep_tristate '  JEDEC Flash device mapped on Mixcom piggyback card' CONFIG_MTD_MIXMEM $CONFIG_MTD_JEDEC
-   dep_tristate '  JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC
-   dep_tristate '  JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC
+   dep_tristate '  JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC $CONFIG_MTD_COMPLEX_MAPPINGS
+   dep_tristate '  JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC $CONFIG_MTD_COMPLEX_MAPPINGS
    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' CONFIG_MTD_ICH2ROM $CONFIG_MTD_JEDECPROBE
+   dep_tristate '  ROM connected to Intel Hub Controller 2/3/4/5' CONFIG_MTD_ICHXROM $CONFIG_MTD_JEDECPROBE $CONFIG_MTD_COMPLEX_MAPPINGS
    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
 
-if [ "$CONFIG_PPC" = "y" ]; then
-   dep_tristate '  CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI $CONFIG_TQM8xxL
-   dep_tristate '  CFI Flash device mapped on RPX Lite or CLLF' CONFIG_MTD_RPXLITE $CONFIG_MTD_CFI
-   dep_tristate '  System flash on MBX860 board' CONFIG_MTD_MBX860 $CONFIG_MTD_CFI
-   dep_tristate '  CFI Flash device mapped on D-Box2' CONFIG_MTD_DBOX2 $CONFIG_MTD_CFI
-   dep_tristate '  CFI Flash device mapping on FlagaDM' CONFIG_MTD_CFI_FLAGADM $CONFIG_MTD_CFI
-   dep_tristate '  CFI Flash device mapped on IBM Redwood-4/5' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI
-fi
-
-if [ "$CONFIG_MIPS" = "y" ]; then
-   dep_tristate '  Pb1000 MTD support' CONFIG_MTD_PB1000 $CONFIG_MIPS_PB1000
-   dep_tristate '  Pb1500 MTD support' CONFIG_MTD_PB1500 $CONFIG_MIPS_PB1500
-   dep_tristate '  Pb1100 MTD support' CONFIG_MTD_PB1100 $CONFIG_MIPS_PB1100
-   if [ "$CONFIG_MTD_PB1500" = "y" -o "$CONFIG_MTD_PB1500" = "m" \
-	-o "$CONFIG_MTD_PB1100" = "y" -o "$CONFIG_MTD_PB1100" = "m" ]; then
-      bool '  Pb[15]00 boot flash device' CONFIG_MTD_PB1500_BOOT 
-      bool '  Pb[15]00 user flash device (2nd 32MiB bank)' CONFIG_MTD_PB1500_USER
+if [ "$CONFIG_PPC32" = "y" ]; then
+  if [ "$CONFIG_6xx" = "y" -a "$CONFIG_8260" = "y" ]; then
+    dep_tristate '  Flash device on SBC8240' CONFIG_MTD_SBC8240 $CONFIG_MTD_JEDECPROBE
+  fi
+  if [ "$CONFIG_8xx" = "y" ]; then
+    if [ "$CONFIG_TQM8xxL" = "y" ]; then
+      dep_tristate '  CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI
+    fi
+    if [ "$CONFIG_RPXLITE" = "y" -o "$CONFIG_RPXCLASSIC" = "y" ]; then
+      dep_tristate '  CFI Flash device mapped on RPX Lite or CLLF' CONFIG_MTD_RPXLITE $CONFIG_MTD_CFI
+    fi
+    if [ "$CONFIG_MBX" = "y" ]; then
+      dep_tristate '  System flash on MBX860 board' CONFIG_MTD_MBX860 $CONFIG_MTD_CFI
+    fi
+    if [ "$CONFIG_DBOX2" = "y" ]; then
+      dep_tristate '  CFI Flash device mapped on D-Box2' CONFIG_MTD_DBOX2 $CONFIG_MTD_CFI
+    fi
+    dep_tristate '  CFI Flash device mapping on FlagaDM' CONFIG_MTD_CFI_FLAGADM $CONFIG_MTD_CFI
+  fi
+  if [ "$CONFIG_4xx" = "y" ]; then
+    if [ "$CONFIG_40x" = "y" ]; then
+      if [ "$CONFIG_REDWOOD_4" = "y" -o "$CONFIG_REDWOOD_5" = "y" -o "$CONFIG_REDWOOD_6" = "y" ]; then
+        dep_tristate '  CFI Flash device mapped on IBM Redwood' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI
+      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
+    fi
+    if [ "$CONFIG_440" = "y" ]; then
+      dep_tristate '  Flash devices mapped on IBM Ebony' CONFIG_MTD_EBONY $CONFIG_MTD_CFI $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
    fi
+
+   dep_tristate '  XXS1500 boot flash device' CONFIG_MTD_XXS1500 $CONFIG_MIPS_XXS1500
    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
@@ -63,7 +93,7 @@
       int '    Bus width in octets' CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH 2
    fi
    dep_tristate '  Momenco Ocelot boot flash device' CONFIG_MTD_OCELOT $CONFIG_MOMENCO_OCELOT
-   dep_tristate '  LASAT flash device' CONFIG_MTD_LASAT $CONFIG_MTD_CFI $CONFIG_LASAT
+   dep_tristate '  LASAT flash device' CONFIG_MTD_LASAT $CONFIG_LASAT
 fi
 
 if [ "$CONFIG_SUPERH" = "y" ]; then
@@ -75,21 +105,24 @@
 fi
 
 if [ "$CONFIG_ARM" = "y" ]; then
-   dep_tristate '  CFI Flash device mapped on Nora' CONFIG_MTD_NORA $CONFIG_MTD_CFI
    dep_tristate '  CFI Flash device mapped on ARM Integrator/P720T' CONFIG_MTD_ARM_INTEGRATOR $CONFIG_MTD_CFI
    dep_tristate '  Cirrus CDB89712 evaluation board mappings' CONFIG_MTD_CDB89712 $CONFIG_MTD_CFI $CONFIG_ARCH_CDB89712
    dep_tristate '  CFI Flash device mapped on StrongARM SA11x0' CONFIG_MTD_SA1100 $CONFIG_MTD_CFI $CONFIG_ARCH_SA1100 $CONFIG_MTD_PARTITIONS
-   dep_tristate '  CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE
+   dep_tristate '  CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE $CONFIG_MTD_COMPLEX_MAPPINGS
    dep_tristate '  CFI Flash device mapped on the XScale IQ80310 board' CONFIG_MTD_IQ80310 $CONFIG_MTD_CFI $CONFIG_ARCH_IQ80310
+   dep_tristate '  CFI Flash device mapped on the XScale Lubbock board' CONFIG_MTD_LUBBOCK $CONFIG_MTD_CFI $CONFIG_ARCH_LUBBOCK
+   dep_tristate '  CFI Flash device mapped on XScale IXP425 systems' CONFIG_MTD_IXP425 $CONFIG_MTD_CFI $CONFIG_MTD_COMPLEX_MAPPINGS 
    dep_tristate '  CFI Flash device mapped on Epxa10db' CONFIG_MTD_EPXA10DB $CONFIG_MTD_CFI  $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT
    dep_tristate '  CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_SA1100_FORTUNET
    dep_tristate '  NV-RAM mapping AUTCPU12 board' CONFIG_MTD_AUTCPU12 $CONFIG_ARCH_AUTCPU12
    dep_tristate '  CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_MTD_CFI
+   dep_tristate '  CFI Flash device mapped on Hynix evaluation boards' CONFIG_MTD_H720X $CONFIG_MTD_CFI
    dep_tristate '  JEDEC Flash device mapped on impA7' CONFIG_MTD_IMPA7 $CONFIG_MTD_JEDECPROBE
    dep_tristate '  JEDEC Flash device mapped on Ceiva/Polaroid PhotoMax Digital Picture Frame' CONFIG_MTD_CEIVA $CONFIG_MTD_JEDECPROBE  $CONFIG_ARCH_CEIVA
+   dep_tristate '  NOR Flash device on TOTO board' CONFIG_MTD_NOR_TOTO $CONFIG_MTD $CONFIG_OMAP_TOTO
 fi
 if [ "$CONFIG_ALPHA" = "y" ]; then
-   dep_tristate '  Flash chip mapping on TSUNAMI' CONFIG_MTD_TSUNAMI $CONFIG_MTD_GENPROBE
+   dep_tristate '  Flash chip mapping on TSUNAMI' CONFIG_MTD_TSUNAMI $CONFIG_MTD_GENPROBE $CONFIG_MTD_COMPLEX_MAPPINGS
 fi
 
 if [ "$CONFIG_UCLINUX" = "y" ]; then
@@ -97,7 +130,7 @@
 fi
 
 # This needs CFI or JEDEC, depending on the cards found.
-dep_tristate '  PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI
-dep_tristate '  PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA
+dep_tristate '  PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI $CONFIG_MTD_COMPLEX_MAPPINGS
+dep_tristate '  PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA $CONFIG_MTD_COMPLEX_MAPPINGS
 
 endmenu
diff -Nur linux-2.4.30/drivers/mtd/maps/cstm_mips_ixx.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/cstm_mips_ixx.c
--- linux-2.4.30/drivers/mtd/maps/cstm_mips_ixx.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/cstm_mips_ixx.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: cstm_mips_ixx.c,v 1.5 2001/10/02 15:05:14 dwmw2 Exp $
+ * $Id: cstm_mips_ixx.c,v 1.9 2003/05/21 12:45:18 dwmw2 Exp $
  *
  * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions.
  * Config with both CFI and JEDEC device support.
@@ -33,55 +33,13 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
 #include <linux/config.h>
-
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
 #include <linux/delay.h>
-#endif
-
-__u8 cstm_mips_ixx_read8(struct map_info *map, unsigned long ofs)
-{
-	return *(__u8 *)(map->map_priv_1 + ofs);
-}
-
-__u16 cstm_mips_ixx_read16(struct map_info *map, unsigned long ofs)
-{
-	return *(__u16 *)(map->map_priv_1 + ofs);
-}
-
-__u32 cstm_mips_ixx_read32(struct map_info *map, unsigned long ofs)
-{
-	return *(__u32 *)(map->map_priv_1 + ofs);
-}
-
-void cstm_mips_ixx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-void cstm_mips_ixx_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	*(__u8 *)(map->map_priv_1 + adr) = d;
-}
-
-void cstm_mips_ixx_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	*(__u16 *)(map->map_priv_1 + adr) = d;
-}
-
-void cstm_mips_ixx_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	*(__u32 *)(map->map_priv_1 + adr) = d;
-}
-
-void cstm_mips_ixx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio(map->map_priv_1 + to, from, len);
-}
 
 #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
 #define CC_GCR             0xB4013818
@@ -97,56 +55,47 @@
 #define CC_GPAICR          0xB4013804
 #endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */
 
+#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
 void cstm_mips_ixx_set_vpp(struct map_info *map,int vpp)
 {
-  if (vpp) {
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
-        __u16	data;
-        __u8	data1;
-	static u8 first = 1;
-
-	// Set GPIO port B pin3 to high
-	data = *(__u16 *)(CC_GPBCR);
-	data = (data & 0xff0f) | 0x0040;
-	*(__u16 *)CC_GPBCR = data;
-	*(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) | 0x08;
-	if (first) {
-		first = 0;
-		/* need to have this delay for first
-		   enabling vpp after powerup */
-		udelay(40);
+	static spinlock_t vpp_lock = SPIN_LOCK_UNLOCKED;
+	static int vpp_count = 0;
+	unsigned long flags;
+
+	spin_lock_irqsave(&vpp_lock, flags);
+
+	if (vpp) {
+		if (!vpp_count++) {
+			__u16	data;
+			__u8	data1;
+			static u8 first = 1;
+		
+			// Set GPIO port B pin3 to high
+			data = *(__u16 *)(CC_GPBCR);
+			data = (data & 0xff0f) | 0x0040;
+			*(__u16 *)CC_GPBCR = data;
+			*(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) | 0x08;
+			if (first) {
+				first = 0;
+				/* need to have this delay for first
+				   enabling vpp after powerup */
+				udelay(40);
+			}
+		}
+	} else {
+		if (!--vpp_count) {
+			__u16	data;
+		
+			// Set GPIO port B pin3 to high
+			data = *(__u16 *)(CC_GPBCR);
+			data = (data & 0xff3f) | 0x0040;
+			*(__u16 *)CC_GPBCR = data;
+			*(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) & 0xf7;
+		}
 	}
-#endif /* CONFIG_MIPS_ITE8172 */
-  }
-  else {
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
-        __u16	data;
-
-	// Set GPIO port B pin3 to high
-	data = *(__u16 *)(CC_GPBCR);
-	data = (data & 0xff3f) | 0x0040;
-	*(__u16 *)CC_GPBCR = data;
-	*(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) & 0xf7;
-#endif /* CONFIG_MIPS_ITE8172 */
-  }
+	spin_unlock_irqrestore(&vpp_lock, flags);
 }
-
-const struct map_info basic_cstm_mips_ixx_map = {
-	NULL,
-	0,
-	0,
-	cstm_mips_ixx_read8,
-	cstm_mips_ixx_read16,
-	cstm_mips_ixx_read32,
-	cstm_mips_ixx_copy_from,
-	cstm_mips_ixx_write8,
-	cstm_mips_ixx_write16,
-	cstm_mips_ixx_write32,
-	cstm_mips_ixx_copy_to,
-        cstm_mips_ixx_set_vpp,
-	0,
-	0
-};
+#endif
 
 /* board and partition description */
 
@@ -175,9 +124,9 @@
 static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = {
 {   // 28F128J3A in 2x16 configuration
 	{
-		name: "main partition ",
-		size: 0x02000000, // 128 x 2 x 128k byte sectors
-		offset: 0,
+		.name = "main partition ",
+		.size = 0x02000000, // 128 x 2 x 128k byte sectors
+		.offset = 0,
 	},
 },
 };
@@ -197,9 +146,9 @@
 static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = {
 { 
 	{
-		name: "main partition",
-		size:  CONFIG_MTD_CSTM_MIPS_IXX_LEN,
-		offset: 0,
+		.name = "main partition",
+		.size =  CONFIG_MTD_CSTM_MIPS_IXX_LEN,
+		.offset = 0,
 	},
 },
 };
@@ -216,17 +165,24 @@
 
 	/* Initialize mapping */
 	for (i=0;i<PHYSMAP_NUMBER;i++) {
-		printk(KERN_NOTICE "cstm_mips_ixx flash device: %lx at %lx\n", cstm_mips_ixx_board_desc[i].window_size, cstm_mips_ixx_board_desc[i].window_addr);
-                memcpy((char *)&cstm_mips_ixx_map[i],(char *)&basic_cstm_mips_ixx_map,sizeof(struct map_info));
-		cstm_mips_ixx_map[i].map_priv_1 = (unsigned long)ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size);
-		if (!cstm_mips_ixx_map[i].map_priv_1) {
+		printk(KERN_NOTICE "cstm_mips_ixx flash device: 0x%lx at 0x%lx\n", 
+		       cstm_mips_ixx_board_desc[i].window_size, cstm_mips_ixx_board_desc[i].window_addr);
+
+
+		cstm_mips_ixx_map[i].phys = cstm_mips_ixx_board_desc[i].window_addr;
+		cstm_mips_ixx_map[i].virt = (unsigned long)ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size);
+		if (!cstm_mips_ixx_map[i].virt) {
 			printk(KERN_WARNING "Failed to ioremap\n");
 			return -EIO;
 	        }
 		cstm_mips_ixx_map[i].name = cstm_mips_ixx_board_desc[i].name;
 		cstm_mips_ixx_map[i].size = cstm_mips_ixx_board_desc[i].window_size;
 		cstm_mips_ixx_map[i].buswidth = cstm_mips_ixx_board_desc[i].buswidth;
-		//printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].map_priv_1));
+#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+                cstm_mips_ixx_map[i].set_vpp = cstm_mips_ixx_set_vpp;
+#endif
+		simple_map_init(&cstm_mips_ixx_map[i]);
+		//printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].virt));
 	}
 
 #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
@@ -244,7 +200,7 @@
 		        printk(KERN_NOTICE "cstm_mips_ixx %d jedec: mymtd is %x\n",i,(unsigned int)mymtd);
 		}
 		if (mymtd) {
-			mymtd->module = THIS_MODULE;
+			mymtd->owner = THIS_MODULE;
 
 	                cstm_mips_ixx_map[i].map_priv_2 = (unsigned long)mymtd;
 		        add_mtd_partitions(mymtd, parts, cstm_mips_ixx_board_desc[i].num_partitions);
@@ -266,9 +222,9 @@
 			del_mtd_partitions(mymtd);
 			map_destroy(mymtd);
 		}
-		if (cstm_mips_ixx_map[i].map_priv_1) {
-			iounmap((void *)cstm_mips_ixx_map[i].map_priv_1);
-			cstm_mips_ixx_map[i].map_priv_1 = 0;
+		if (cstm_mips_ixx_map[i].virt) {
+			iounmap((void *)cstm_mips_ixx_map[i].virt);
+			cstm_mips_ixx_map[i].virt = 0;
 		}
 	}
 }
diff -Nur linux-2.4.30/drivers/mtd/maps/dbox2-flash.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/dbox2-flash.c
--- linux-2.4.30/drivers/mtd/maps/dbox2-flash.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/dbox2-flash.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,12 +1,13 @@
 /*
- * $Id: dbox2-flash.c,v 1.4 2001/10/02 15:05:14 dwmw2 Exp $
+ * $Id: dbox2-flash.c,v 1.9 2003/05/21 12:45:18 dwmw2 Exp $
  *
- * Nokia / Sagem D-Box 2 flash driver
+ * D-Box 2 flash driver
  */
 
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -16,22 +17,44 @@
 /* partition_info gives details on the logical partitions that the split the
  * single flash device into. If the size if zero we use up to the end of the
  * device. */
-static struct mtd_partition partition_info[]= {{name: "BR bootloader",		// raw
-						      size: 128 * 1024, 
-						      offset: 0,                  
-						      mask_flags: MTD_WRITEABLE},
-                                                     {name: "PPC bootloader",		// flfs
-						      size: 128 * 1024, 
-						      offset: MTDPART_OFS_APPEND, 
-						      mask_flags: 0},
-                                                     {name: "Kernel",			// idxfs
-						      size: 768 * 1024, 
-						      offset: MTDPART_OFS_APPEND, 
-						      mask_flags: 0},
-                                                     {name: "System",			// jffs
-						      size: MTDPART_SIZ_FULL, 
-						      offset: MTDPART_OFS_APPEND, 
-						      mask_flags: 0}};
+static struct mtd_partition partition_info[]= {
+	{
+	.name		= "BR bootloader",
+	.size		= 128 * 1024, 
+	.offset		= 0,                  
+	.mask_flags	= MTD_WRITEABLE
+	},
+	{
+	.name		= "flfs (ppcboot)",
+	.size		= 128 * 1024, 
+	.offset		= MTDPART_OFS_APPEND, 
+	.mask_flags	= 0
+	},
+	{
+	.name		= "root (cramfs)",	
+	.size		= 7040 * 1024, 
+	.offset		= MTDPART_OFS_APPEND, 
+	.mask_flags	= 0
+	},
+	{
+	.name		= "var (jffs2)",
+	.size		= 896 * 1024, 
+	.offset		= MTDPART_OFS_APPEND, 
+	.mask_flags	= 0
+	},
+	{
+	.name		= "flash without bootloader",	
+	.size		= MTDPART_SIZ_FULL, 
+	.offset		= 128 * 1024, 
+	.mask_flags	= 0
+	},
+	{
+	.name		= "complete flash",	
+	.size		= MTDPART_SIZ_FULL, 
+	.offset		= 0, 
+	.mask_flags	= MTD_WRITEABLE
+	}
+};
 
 #define NUM_PARTITIONS (sizeof(partition_info) / sizeof(partition_info[0]))
 
@@ -40,72 +63,24 @@
 
 static struct mtd_info *mymtd;
 
-__u8 dbox2_flash_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-__u16 dbox2_flash_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
-
-__u32 dbox2_flash_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
-
-void dbox2_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-void dbox2_flash_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void dbox2_flash_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void dbox2_flash_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void dbox2_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio(map->map_priv_1 + to, from, len);
-}
 
 struct map_info dbox2_flash_map = {
-	name: "D-Box 2 flash memory",
-	size: WINDOW_SIZE,
-	buswidth: 4,
-	read8: dbox2_flash_read8,
-	read16: dbox2_flash_read16,
-	read32: dbox2_flash_read32,
-	copy_from: dbox2_flash_copy_from,
-	write8: dbox2_flash_write8,
-	write16: dbox2_flash_write16,
-	write32: dbox2_flash_write32,
-	copy_to: dbox2_flash_copy_to
+	.name		= "D-Box 2 flash memory",
+	.size		= WINDOW_SIZE,
+	.buswidth	= 4,
+	.phys		= WINDOW_ADDR,
 };
 
 int __init init_dbox2_flash(void)
 {
        	printk(KERN_NOTICE "D-Box 2 flash driver (size->0x%X mem->0x%X)\n", WINDOW_SIZE, WINDOW_ADDR);
-	dbox2_flash_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+	dbox2_flash_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
 
-	if (!dbox2_flash_map.map_priv_1) {
+	if (!dbox2_flash_map.virt) {
 		printk("Failed to ioremap\n");
 		return -EIO;
 	}
+	simple_map_init(&dbox2_flash_map);
 
 	// Probe for dual Intel 28F320 or dual AMD
 	mymtd = do_map_probe("cfi_probe", &dbox2_flash_map);
@@ -117,7 +92,7 @@
 	}
 	    
 	if (mymtd) {
-		mymtd->module = THIS_MODULE;
+		mymtd->owner = THIS_MODULE;
 
                 /* Create MTD devices for each partition. */
 	        add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS);
@@ -125,7 +100,7 @@
 		return 0;
 	}
 
-	iounmap((void *)dbox2_flash_map.map_priv_1);
+	iounmap((void *)dbox2_flash_map.virt);
 	return -ENXIO;
 }
 
@@ -135,9 +110,9 @@
 		del_mtd_partitions(mymtd);
 		map_destroy(mymtd);
 	}
-	if (dbox2_flash_map.map_priv_1) {
-		iounmap((void *)dbox2_flash_map.map_priv_1);
-		dbox2_flash_map.map_priv_1 = 0;
+	if (dbox2_flash_map.virt) {
+		iounmap((void *)dbox2_flash_map.virt);
+		dbox2_flash_map.virt = 0;
 	}
 }
 
@@ -146,5 +121,5 @@
 
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>");
-MODULE_DESCRIPTION("MTD map driver for Nokia/Sagem D-Box 2 board");
+MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>, Bastian Blank <waldi@tuxbox.org>, Alexander Wild <wild@te-elektronik.com>");
+MODULE_DESCRIPTION("MTD map driver for D-Box 2 board");
diff -Nur linux-2.4.30/drivers/mtd/maps/dc21285.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/dc21285.c
--- linux-2.4.30/drivers/mtd/maps/dc21285.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/dc21285.c	2005-05-12 18:01:29.000000000 +0200
@@ -5,12 +5,14 @@
  *
  * This code is GPL
  * 
- * $Id: dc21285.c,v 1.9 2002/10/14 12:22:10 rmk Exp $
+ * $Id: dc21285.c,v 1.18 2004/04/15 23:04:38 gleixner Exp $
  */
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -18,9 +20,38 @@
 
 #include <asm/io.h>
 #include <asm/hardware/dec21285.h>
+#include <asm/mach-types.h>
 
 
-static struct mtd_info *mymtd;
+static struct mtd_info *dc21285_mtd;
+
+#ifdef CONFIG_ARCH_NETWINDER
+/* 
+ * This is really ugly, but it seams to be the only
+ * realiable way to do it, as the cpld state machine 
+ * is unpredictible. So we have a 25us penalty per
+ * write access.
+ */
+static void nw_en_write(void) {
+	extern spinlock_t gpio_lock;
+	unsigned long flags;
+
+	/*
+	 * we want to write a bit pattern XXX1 to Xilinx to enable
+	 * the write gate, which will be open for about the next 2ms.
+	 */
+	spin_lock_irqsave(&gpio_lock, flags);
+	cpld_modify(1, 1);
+	spin_unlock_irqrestore(&gpio_lock, flags);
+
+	/*
+	 * let the ISA bus to catch on...
+	 */
+	udelay(25);
+}
+#else
+#define nw_en_write() do { } while (0)
+#endif
 
 __u8 dc21285_read8(struct map_info *map, unsigned long ofs)
 {
@@ -44,6 +75,8 @@
 
 void dc21285_write8(struct map_info *map, __u8 d, unsigned long adr)
 {
+	if (machine_is_netwinder())
+		nw_en_write();
 	*CSR_ROMWRITEREG = adr & 3;
 	adr &= ~3;
 	*(__u8*)(map->map_priv_1 + adr) = d;
@@ -51,6 +84,8 @@
 
 void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr)
 {
+	if (machine_is_netwinder())
+		nw_en_write();
 	*CSR_ROMWRITEREG = adr & 3;
 	adr &= ~3;
 	*(__u16*)(map->map_priv_1 + adr) = d;
@@ -58,6 +93,8 @@
 
 void dc21285_write32(struct map_info *map, __u32 d, unsigned long adr)
 {
+	if (machine_is_netwinder())
+		nw_en_write();
 	*(__u32*)(map->map_priv_1 + adr) = d;
 }
 
@@ -92,26 +129,33 @@
 }
 
 struct map_info dc21285_map = {
-	name: "DC21285 flash",
-	size: 16*1024*1024,
-	read8: dc21285_read8,
-	read16: dc21285_read16,
-	read32: dc21285_read32,
-	copy_from: dc21285_copy_from,
-	write8: dc21285_write8,
-	write16: dc21285_write16,
-	write32: dc21285_write32,
-	copy_to: dc21285_copy_to
+	.name = "DC21285 flash",
+	.phys = NO_XIP,
+	.size = 16*1024*1024,
+	.read8 = dc21285_read8,
+	.read16 = dc21285_read16,
+	.read32 = dc21285_read32,
+	.copy_from = dc21285_copy_from,
+	.write8 = dc21285_write8,
+	.write16 = dc21285_write16,
+	.write32 = dc21285_write32,
+	.copy_to = dc21285_copy_to
 };
 
 
 /* Partition stuff */
 static struct mtd_partition *dc21285_parts;
-		      
-extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **);
-
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+#endif
+  
 int __init init_dc21285(void)
 {
+
+#ifdef CONFIG_MTD_PARTITIONS
+	int nrparts;
+#endif
+
 	/* Determine buswidth */
 	switch (*CSR_SA110_CNTL & (3<<14)) {
 		case SA110_CNTL_ROMWIDTH_8: 
@@ -137,24 +181,28 @@
 		return -EIO;
 	}
 
-	mymtd = do_map_probe("cfi_probe", &dc21285_map);
-	if (mymtd) {
-		int nrparts = 0;
+	if (machine_is_ebsa285()) {
+		dc21285_mtd = do_map_probe("cfi_probe", &dc21285_map);
+	} else {
+		dc21285_mtd = do_map_probe("jedec_probe", &dc21285_map);
+	}
 
-		mymtd->module = THIS_MODULE;
+	if (!dc21285_mtd) {
+		iounmap((void *)dc21285_map.map_priv_1);
+		return -ENXIO;
+	}	
+	
+	dc21285_mtd->owner = THIS_MODULE;
+
+#ifdef CONFIG_MTD_PARTITIONS
+	nrparts = parse_mtd_partitions(dc21285_mtd, probes, &dc21285_parts, (void *)0);
+	if (nrparts > 0)
+		add_mtd_partitions(dc21285_mtd, dc21285_parts, nrparts);
+	else	
+#endif	
+		add_mtd_device(dc21285_mtd);
 			
-		/* partition fixup */
-
-#ifdef CONFIG_MTD_REDBOOT_PARTS
-		nrparts = parse_redboot_partitions(mymtd, &dc21285_parts);
-#endif
-		if (nrparts > 0) {
-			add_mtd_partitions(mymtd, dc21285_parts, nrparts);
-		} else if (nrparts == 0) {
-			printk(KERN_NOTICE "RedBoot partition table failed\n");
-			add_mtd_device(mymtd);
-		}
-
+	if(machine_is_ebsa285()) {
 		/* 
 		 * Flash timing is determined with bits 19-16 of the
 		 * CSR_SA110_CNTL.  The value is the number of wait cycles, or
@@ -167,27 +215,23 @@
 		*CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20));
 		/* tristate time */
 		*CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24));
-
-		return 0;
 	}
-
-	iounmap((void *)dc21285_map.map_priv_1);
-	return -ENXIO;
+	
+	return 0;
 }
 
 static void __exit cleanup_dc21285(void)
 {
-	if (mymtd) {
-		del_mtd_device(mymtd);
-		map_destroy(mymtd);
-		mymtd = NULL;
-	}
-	if (dc21285_map.map_priv_1) {
-		iounmap((void *)dc21285_map.map_priv_1);
-		dc21285_map.map_priv_1 = 0;
-	}
-	if(dc21285_parts)
+#ifdef CONFIG_MTD_PARTITIONS
+	if (dc21285_parts) {
+		del_mtd_partitions(dc21285_mtd);
 		kfree(dc21285_parts);
+	} else
+#endif
+		del_mtd_device(dc21285_mtd);
+
+	map_destroy(dc21285_mtd);
+	iounmap((void *)dc21285_map.map_priv_1);
 }
 
 module_init(init_dc21285);
diff -Nur linux-2.4.30/drivers/mtd/maps/dilnetpc.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/dilnetpc.c
--- linux-2.4.30/drivers/mtd/maps/dilnetpc.c	2002-08-03 02:39:44.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/dilnetpc.c	2005-05-12 18:01:29.000000000 +0200
@@ -14,7 +14,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: dilnetpc.c,v 1.8 2002/03/12 13:07:26 rkaiser Exp $
+ * $Id: dilnetpc.c,v 1.12 2003/05/21 12:45:18 dwmw2 Exp $
  *
  * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems
  * featuring the AMD Elan SC410 processor. There are two variants of this
@@ -29,6 +29,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -36,7 +37,7 @@
 #include <linux/mtd/concat.h>
 
 /*
-** The DIL/NetPC keeps it's BIOS in two distinct flash blocks.
+** The DIL/NetPC keeps its BIOS in two distinct flash blocks.
 ** Destroying any of these blocks transforms the DNPC into
 ** a paperweight (albeit not a very useful one, considering
 ** it only weighs a few grams).
@@ -189,45 +190,6 @@
 }
 
 
-static __u8 dnpc_read8(struct map_info *map, unsigned long ofs)
-{
-	return readb(map->map_priv_1 + ofs);
-}
-
-static __u16 dnpc_read16(struct map_info *map, unsigned long ofs)
-{
-	return readw(map->map_priv_1 + ofs);
-}
-
-static __u32 dnpc_read32(struct map_info *map, unsigned long ofs)
-{
-	return readl(map->map_priv_1 + ofs);
-}
-
-static void dnpc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
-}
-
-static void dnpc_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	writeb(d, map->map_priv_1 + adr);
-}
-
-static void dnpc_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	writew(d, map->map_priv_1 + adr);
-}
-
-static void dnpc_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	writel(d, map->map_priv_1 + adr);
-}
-
-static void dnpc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio((void *)(map->map_priv_1 + to), from, len);
-}
 
 /*
 ************************************************************
@@ -285,22 +247,14 @@
 
 #define DNP_WINDOW_SIZE		0x00200000	/*  DNP flash size is 2MiB  */
 #define ADNP_WINDOW_SIZE	0x00400000	/* ADNP flash size is 4MiB */
-#define WINDOW_ADDR			FLASH_BASE
+#define WINDOW_ADDR		FLASH_BASE
 
 static struct map_info dnpc_map = {
-	name: "ADNP Flash Bank",
-	size: ADNP_WINDOW_SIZE,
-	buswidth: 1,
-	read8: dnpc_read8,
-	read16: dnpc_read16,
-	read32: dnpc_read32,
-	copy_from: dnpc_copy_from,
-	write8: dnpc_write8,
-	write16: dnpc_write16,
-	write32: dnpc_write32,
-	copy_to: dnpc_copy_to,
-	set_vpp: adnp_set_vpp,
-	map_priv_2: WINDOW_ADDR
+	.name = "ADNP Flash Bank",
+	.size = ADNP_WINDOW_SIZE,
+	.buswidth = 1,
+	.set_vpp = adnp_set_vpp,
+	.phys = WINDOW_ADDR
 };
 
 /*
@@ -316,29 +270,29 @@
 static struct mtd_partition partition_info[]=
 {
 	{ 
-		name:		"ADNP boot", 
-		offset:		0, 
-		size:		0xf0000,
+		.name =		"ADNP boot", 
+		.offset =	0, 
+		.size =		0xf0000,
 	},
 	{ 
-		name:		"ADNP system BIOS", 
-		offset:		MTDPART_OFS_NXTBLK,
-		size:		0x10000,
+		.name =		"ADNP system BIOS", 
+		.offset =	MTDPART_OFS_NXTBLK,
+		.size =		0x10000,
 #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
-		mask_flags:	MTD_WRITEABLE,
+		.mask_flags =	MTD_WRITEABLE,
 #endif
 	},
 	{
-		name:		"ADNP file system",
-		offset:		MTDPART_OFS_NXTBLK,
-		size:		0x2f0000,
+		.name =		"ADNP file system",
+		.offset =	MTDPART_OFS_NXTBLK,
+		.size =		0x2f0000,
 	},
 	{
-		name:		"ADNP system BIOS entry", 
-		offset:		MTDPART_OFS_NXTBLK,
-		size:		MTDPART_SIZ_FULL,
+		.name =		"ADNP system BIOS entry", 
+		.offset =	MTDPART_OFS_NXTBLK,
+		.size =		MTDPART_SIZ_FULL,
 #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
-		mask_flags:	MTD_WRITEABLE,
+		.mask_flags =	MTD_WRITEABLE,
 #endif
 	},
 };
@@ -369,21 +323,21 @@
 static struct mtd_partition higlvl_partition_info[]=
 {
 	{ 
-		name:		"ADNP boot block", 
-		offset:		0, 
-		size:		CONFIG_MTD_DILNETPC_BOOTSIZE,
+		.name =		"ADNP boot block", 
+		.offset =	0, 
+		.size =		CONFIG_MTD_DILNETPC_BOOTSIZE,
 	},
 	{
-		name:		"ADNP file system space",
-		offset:		MTDPART_OFS_NXTBLK,
-		size:		ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000,
+		.name =		"ADNP file system space",
+		.offset =	MTDPART_OFS_NXTBLK,
+		.size =		ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000,
 	},
 	{ 
-		name:		"ADNP system BIOS + BIOS Entry", 
-		offset:		MTDPART_OFS_NXTBLK,
-		size:		MTDPART_SIZ_FULL,
+		.name =		"ADNP system BIOS + BIOS Entry", 
+		.offset =	MTDPART_OFS_NXTBLK,
+		.size =		MTDPART_SIZ_FULL,
 #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED
-		mask_flags:	MTD_WRITEABLE,
+		.mask_flags =	MTD_WRITEABLE,
 #endif
 	},
 };
@@ -447,18 +401,19 @@
 	}
 
 	printk(KERN_NOTICE "DIL/Net %s flash: 0x%lx at 0x%lx\n", 
-		is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.map_priv_2);
+		is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.phys);
 
-	dnpc_map.map_priv_1 = (unsigned long)ioremap_nocache(dnpc_map.map_priv_2, dnpc_map.size);
+	dnpc_map.virt = (unsigned long)ioremap_nocache(dnpc_map.phys, dnpc_map.size);
 
-	dnpc_map_flash(dnpc_map.map_priv_2, dnpc_map.size);
+	dnpc_map_flash(dnpc_map.phys, dnpc_map.size);
 
-	if (!dnpc_map.map_priv_1) {
+	if (!dnpc_map.virt) {
 		printk("Failed to ioremap_nocache\n");
 		return -EIO;
 	}
+	simple_map_init(&dnpc_map);
 
-	printk("FLASH virtual address: 0x%lx\n", dnpc_map.map_priv_1);
+	printk("FLASH virtual address: 0x%lx\n", dnpc_map.virt);
 
 	mymtd = do_map_probe("jedec_probe", &dnpc_map);
 
@@ -475,11 +430,11 @@
 			mymtd->erasesize = 0x10000;
 
 	if (!mymtd) {
-		iounmap((void *)dnpc_map.map_priv_1);
+		iounmap((void *)dnpc_map.virt);
 		return -ENXIO;
 	}
 		
-	mymtd->module = THIS_MODULE;
+	mymtd->owner = THIS_MODULE;
 
 	/*
 	** Supply pointers to lowlvl_parts[] array to add_mtd_partitions()
@@ -525,10 +480,10 @@
 		del_mtd_partitions(mymtd);
 		map_destroy(mymtd);
 	}
-	if (dnpc_map.map_priv_1) {
-		iounmap((void *)dnpc_map.map_priv_1);
+	if (dnpc_map.virt) {
+		iounmap((void *)dnpc_map.virt);
 		dnpc_unmap_flash();
-		dnpc_map.map_priv_1 = 0;
+		dnpc_map.virt = 0;
 	}
 }
 
diff -Nur linux-2.4.30/drivers/mtd/maps/ebony.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/ebony.c
--- linux-2.4.30/drivers/mtd/maps/ebony.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/ebony.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,164 @@
+/*
+ * $Id: ebony.c,v 1.9 2004/04/26 12:35:36 gleixner Exp $
+ * 
+ * Mapping for Ebony user flash
+ *
+ * Matt Porter <mporter@mvista.com>
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/config.h>
+#include <asm/io.h>
+#include <asm/ibm44x.h>
+#include <platforms/ebony.h>
+
+static struct mtd_info *flash;
+
+static struct map_info ebony_small_map = {
+	.name =		"Ebony small flash",
+	.size =		EBONY_SMALL_FLASH_SIZE,
+	.buswidth =	1,
+};
+
+static struct map_info ebony_large_map = {
+	.name =		"Ebony large flash",
+	.size =		EBONY_LARGE_FLASH_SIZE,
+	.buswidth =	1,
+};
+
+static struct mtd_partition ebony_small_partitions[] = {
+	{
+		.name =   "OpenBIOS",
+		.offset = 0x0,
+		.size =   0x80000,
+	}
+};
+
+static struct mtd_partition ebony_large_partitions[] = {
+	{
+		.name =   "fs",
+		.offset = 0,
+		.size =   0x380000,
+	},
+	{
+		.name =   "firmware",
+		.offset = 0x380000,
+		.size =   0x80000,
+	}
+};
+
+int __init init_ebony(void)
+{
+	u8 fpga0_reg;
+	unsigned long fpga0_adr;
+	unsigned long long small_flash_base, large_flash_base;
+
+	fpga0_adr = ioremap64(EBONY_FPGA_ADDR, 16);
+	if (!fpga0_adr)
+		return -ENOMEM;
+
+	fpga0_reg = readb(fpga0_adr);
+	iounmap(fpga0_adr);
+
+	if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) &&
+			!EBONY_FLASH_SEL(fpga0_reg))
+		small_flash_base = EBONY_SMALL_FLASH_HIGH2;
+	else if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) &&
+			EBONY_FLASH_SEL(fpga0_reg))
+		small_flash_base = EBONY_SMALL_FLASH_HIGH1;
+	else if (!EBONY_BOOT_SMALL_FLASH(fpga0_reg) &&
+			!EBONY_FLASH_SEL(fpga0_reg))
+		small_flash_base = EBONY_SMALL_FLASH_LOW2;
+	else
+		small_flash_base = EBONY_SMALL_FLASH_LOW1;
+			
+	if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) &&
+			!EBONY_ONBRD_FLASH_EN(fpga0_reg))
+		large_flash_base = EBONY_LARGE_FLASH_LOW;
+	else
+		large_flash_base = EBONY_LARGE_FLASH_HIGH;
+
+	ebony_small_map.phys = small_flash_base;
+	ebony_small_map.virt =
+		(unsigned long)ioremap64(small_flash_base,
+					 ebony_small_map.size);
+
+	if (!ebony_small_map.virt) {
+		printk("Failed to ioremap flash\n");
+		return -EIO;
+	}
+
+	simple_map_init(&ebony_small_map);
+
+	flash = do_map_probe("map_rom", &ebony_small_map);
+	if (flash) {
+		flash->owner = THIS_MODULE;
+		add_mtd_partitions(flash, ebony_small_partitions,
+					ARRAY_SIZE(ebony_small_partitions));
+	} else {
+		printk("map probe failed for flash\n");
+		return -ENXIO;
+	}
+
+	ebony_large_map.phys = large_flash_base;
+	ebony_large_map.virt =
+		(unsigned long)ioremap64(large_flash_base,
+					 ebony_large_map.size);
+
+	if (!ebony_large_map.virt) {
+		printk("Failed to ioremap flash\n");
+		return -EIO;
+	}
+
+	simple_map_init(&ebony_large_map);
+
+	flash = do_map_probe("cfi_probe", &ebony_large_map);
+	if (flash) {
+		flash->owner = THIS_MODULE;
+		add_mtd_partitions(flash, ebony_large_partitions,
+					ARRAY_SIZE(ebony_large_partitions));
+	} else {
+		printk("map probe failed for flash\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static void __exit cleanup_ebony(void)
+{
+	if (flash) {
+		del_mtd_partitions(flash);
+		map_destroy(flash);
+	}
+
+	if (ebony_small_map.virt) {
+		iounmap((void *)ebony_small_map.virt);
+		ebony_small_map.virt = 0;
+	}
+
+	if (ebony_large_map.virt) {
+		iounmap((void *)ebony_large_map.virt);
+		ebony_large_map.virt = 0;
+	}
+}
+
+module_init(init_ebony);
+module_exit(cleanup_ebony);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Matt Porter <mporter@mvista.com>");
+MODULE_DESCRIPTION("MTD map and partitions for IBM 440GP Ebony boards");
diff -Nur linux-2.4.30/drivers/mtd/maps/edb7312.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/edb7312.c
--- linux-2.4.30/drivers/mtd/maps/edb7312.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/edb7312.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: edb7312.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $
+ * $Id: edb7312.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $
  *
  * Handle mapping of the NOR flash on Cogent EDB7312 boards
  *
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -35,61 +36,11 @@
 
 static struct mtd_info *mymtd;
 
-__u8 edb7312nor_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-__u16 edb7312nor_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
-
-__u32 edb7312nor_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
-
-void edb7312nor_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-void edb7312nor_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void edb7312nor_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void edb7312nor_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void edb7312nor_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio(map->map_priv_1 + to, from, len);
-}
-
 struct map_info edb7312nor_map = {
-	name: "NOR flash on EDB7312",
-	size: WINDOW_SIZE,
-	buswidth: BUSWIDTH,
-	read8: edb7312nor_read8,
-	read16: edb7312nor_read16,
-	read32: edb7312nor_read32,
-	copy_from: edb7312nor_copy_from,
-	write8: edb7312nor_write8,
-	write16: edb7312nor_write16,
-	write32: edb7312nor_write32,
-	copy_to: edb7312nor_copy_to
+	.name = "NOR flash on EDB7312",
+	.size = WINDOW_SIZE,
+	.buswidth = BUSWIDTH,
+	.phys = WINDOW_ADDR,
 };
 
 #ifdef CONFIG_MTD_PARTITIONS
@@ -99,30 +50,24 @@
  */
 static struct mtd_partition static_partitions[3] =
 {
-    {
-	name: "ARMboot",
-	  size: 0x40000,
-	  offset: 0
-    },
-    {
-	name: "Kernel",
-	  size: 0x200000,
-	  offset: 0x40000
-    },
-    {
-	name: "RootFS",
-	  size: 0xDC0000,
-	  offset: 0x240000
-    },
+	{
+		.name = "ARMboot",
+		.size = 0x40000,
+		.offset = 0
+	},
+	{
+		.name = "Kernel",
+		.size = 0x200000,
+		.offset = 0x40000
+	},
+	{
+		.name = "RootFS",
+		.size = 0xDC0000,
+		.offset = 0x240000
+	},
 };
 
-#define NB_OF(x) (sizeof (x) / sizeof (x[0]))
-
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-int parse_cmdline_partitions(struct mtd_info *master, 
-			     struct mtd_partition **pparts,
-			     const char *mtd_id);
-#endif
+static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
 
 #endif
 
@@ -137,13 +82,15 @@
 
        	printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n", 
 	       WINDOW_SIZE, WINDOW_ADDR);
-	edb7312nor_map.map_priv_1 = (unsigned long)
+	edb7312nor_map.virt = (unsigned long)
 	  ioremap(WINDOW_ADDR, WINDOW_SIZE);
 
-	if (!edb7312nor_map.map_priv_1) {
+	if (!edb7312nor_map.virt) {
 		printk(MSG_PREFIX "failed to ioremap\n");
 		return -EIO;
 	}
+	
+	simple_map_init(&edb7312nor_map);
 
 	mymtd = 0;
 	type = rom_probe_types;
@@ -151,18 +98,17 @@
 		mymtd = do_map_probe(*type, &edb7312nor_map);
 	}
 	if (mymtd) {
-		mymtd->module = THIS_MODULE;
+		mymtd->owner = THIS_MODULE;
 
 #ifdef CONFIG_MTD_PARTITIONS
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-		mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts, MTDID);
+		mtd_parts_nb = parse_mtd_partitions(mymtd, probes, &mtd_parts, MTDID);
 		if (mtd_parts_nb > 0)
-		  part_type = "command line";
-#endif
+		  part_type = "detected";
+
 		if (mtd_parts_nb == 0)
 		{
 			mtd_parts = static_partitions;
-			mtd_parts_nb = NB_OF(static_partitions);
+			mtd_parts_nb = ARRAY_SIZE(static_partitions);
 			part_type = "static";
 		}
 #endif
@@ -178,7 +124,7 @@
 		return 0;
 	}
 
-	iounmap((void *)edb7312nor_map.map_priv_1);
+	iounmap((void *)edb7312nor_map.virt);
 	return -ENXIO;
 }
 
@@ -188,9 +134,9 @@
 		del_mtd_device(mymtd);
 		map_destroy(mymtd);
 	}
-	if (edb7312nor_map.map_priv_1) {
-		iounmap((void *)edb7312nor_map.map_priv_1);
-		edb7312nor_map.map_priv_1 = 0;
+	if (edb7312nor_map.virt) {
+		iounmap((void *)edb7312nor_map.virt);
+		edb7312nor_map.virt = 0;
 	}
 }
 
diff -Nur linux-2.4.30/drivers/mtd/maps/elan-104nc.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/elan-104nc.c
--- linux-2.4.30/drivers/mtd/maps/elan-104nc.c	2004-11-17 12:54:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/elan-104nc.c	2005-05-12 18:01:29.000000000 +0200
@@ -16,7 +16,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: elan-104nc.c,v 1.13 2002/02/13 15:30:20 dwmw2 Exp $
+   $Id: elan-104nc.c,v 1.18 2003/06/23 07:37:02 dwmw2 Exp $
 
 The ELAN-104NC has up to 8 Mibyte of Intel StrataFlash (28F320/28F640) in x16
 mode.  This drivers uses the CFI probe and Intel Extended Command Set drivers.
@@ -40,6 +40,7 @@
 #include <asm/io.h>
 
 #include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
 
 #define WINDOW_START 0xb0000
@@ -59,14 +60,14 @@
  * single flash device into. If the size if zero we use up to the end of the
  * device. */
 static struct mtd_partition partition_info[]={
-    { name: "ELAN-104NC flash boot partition", 
-      offset: 0, 
-      size: 640*1024 },
-    { name: "ELAN-104NC flash partition 1", 
-      offset: 640*1024, 
-      size: 896*1024 },
-    { name: "ELAN-104NC flash partition 2", 
-      offset: (640+896)*1024 }
+    { .name = "ELAN-104NC flash boot partition", 
+      .offset = 0, 
+      .size = 640*1024 },
+    { .name = "ELAN-104NC flash partition 1", 
+      .offset = 640*1024, 
+      .size = 896*1024 },
+    { .name = "ELAN-104NC flash partition 2", 
+      .offset = (640+896)*1024 }
 };
 #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
 
@@ -147,7 +148,7 @@
 		elan_104nc_page(map, from);
 		memcpy_fromio(to, iomapadr + (from & WINDOW_MASK), thislen);
 		spin_unlock(&elan_104nc_spin);
-		to += thislen;
+		(__u8*)to += thislen;
 		from += thislen;
 		len -= thislen;
 	}
@@ -195,19 +196,20 @@
 }
 
 static struct map_info elan_104nc_map = {
-	name: "ELAN-104NC flash",
-	size: 8*1024*1024, /* this must be set to a maximum possible amount
+	.name = "ELAN-104NC flash",
+	.phys = NO_XIP,
+	.size = 8*1024*1024, /* this must be set to a maximum possible amount
 			of flash so the cfi probe routines find all
 			the chips */
-	buswidth: 2,
-	read8: elan_104nc_read8,
-	read16: elan_104nc_read16,
-	read32: elan_104nc_read32,
-	copy_from: elan_104nc_copy_from,
-	write8: elan_104nc_write8,
-	write16: elan_104nc_write16,
-	write32: elan_104nc_write32,
-	copy_to: elan_104nc_copy_to
+	.buswidth = 2,
+	.read8 = elan_104nc_read8,
+	.read16 = elan_104nc_read16,
+	.read32 = elan_104nc_read32,
+	.copy_from = elan_104nc_copy_from,
+	.write8 = elan_104nc_write8,
+	.write16 = elan_104nc_write16,
+	.write32 = elan_104nc_write32,
+	.copy_to = elan_104nc_copy_to
 };
 
 /* MTD device for all of the flash. */
@@ -221,20 +223,13 @@
 	}
 
 	iounmap((void *)iomapadr);
-	release_region(PAGE_IO,PAGE_IO_SIZE);
 }
 
 int __init init_elan_104nc(void)
 {
-	/* Urg! We use I/O port 0x22 without request_region()ing it */
-	/*
-	if (check_region(PAGE_IO,PAGE_IO_SIZE) != 0) {
-		printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n",
-			elan_104nc_map.name,
-			PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 );
-		return -EAGAIN;
-	}
-	*/
+	/* Urg! We use I/O port 0x22 without request_region()ing it,
+	   because it's already allocated to the PIC. */
+
   	iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
 	if (!iomapadr) {
 		printk( KERN_ERR"%s: failed to ioremap memory region\n",
@@ -242,10 +237,6 @@
 		return -EIO;
 	}
 
-	/*
-	request_region( PAGE_IO, PAGE_IO_SIZE, "ELAN-104NC flash" );
-	*/
-
 	printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n",
 		elan_104nc_map.name,
 		PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1,
@@ -260,7 +251,7 @@
 		return -ENXIO;
 	}
 	
-	all_mtd->module=THIS_MODULE;
+	all_mtd->owner = THIS_MODULE;
 
 	/* Create MTD devices for each partition. */
 	add_mtd_partitions( all_mtd, partition_info, NUM_PARTITIONS );
diff -Nur linux-2.4.30/drivers/mtd/maps/epxa10db-flash.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/epxa10db-flash.c
--- linux-2.4.30/drivers/mtd/maps/epxa10db-flash.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/epxa10db-flash.c	2005-05-12 18:01:29.000000000 +0200
@@ -5,7 +5,7 @@
  *  Copyright (C) 2001 Altera Corporation
  *  Copyright (C) 2001 Red Hat, Inc.
  *
- * $Id: epxa10db-flash.c,v 1.4 2002/08/22 10:46:19 cdavies Exp $ 
+ * $Id: epxa10db-flash.c,v 1.10 2003/05/21 12:45:18 dwmw2 Exp $ 
  *
  * 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
@@ -26,6 +26,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -43,87 +44,38 @@
 
 static struct mtd_info *mymtd;
 
-extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **);
 static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts);
 
-static __u8 epxa_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-static __u16 epxa_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
-
-static __u32 epxa_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
-
-static void epxa_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
-}
-
-static void epxa_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
-
-static void epxa_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
-
-static void epxa_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
-
-static void epxa_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio((void *)(map->map_priv_1 + to), from, len);
-}
-
-
 
 static struct map_info epxa_map = {
-	name:		"EPXA flash",
-	size:		FLASH_SIZE,
-	buswidth:	2,
-	read8:		epxa_read8,
-	read16:		epxa_read16,
-	read32:		epxa_read32,
-	copy_from:	epxa_copy_from,
-	write8:		epxa_write8,
-	write16:	epxa_write16,
-	write32:	epxa_write32,
-	copy_to:	epxa_copy_to
+	.name =		"EPXA flash",
+	.size =		FLASH_SIZE,
+	.buswidth =	2,
+	.phys =		FLASH_START,
 };
 
+static const char *probes[] = { "RedBoot", "afs", NULL };
 
 static int __init epxa_mtd_init(void)
 {
 	int i;
 	
-	printk(KERN_NOTICE "%s flash device: %x at %x\n", BOARD_NAME, FLASH_SIZE, FLASH_START);
-	epxa_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE);
-	if (!epxa_map.map_priv_1) {
+	printk(KERN_NOTICE "%s flash device: 0x%x at 0x%x\n", BOARD_NAME, FLASH_SIZE, FLASH_START);
+
+	epxa_map.virt = (unsigned long)ioremap(FLASH_START, FLASH_SIZE);
+	if (!epxa_map.virt) {
 		printk("Failed to ioremap %s flash\n",BOARD_NAME);
 		return -EIO;
 	}
+	simple_map_init(&epxa_map);
 
 	mymtd = do_map_probe("cfi_probe", &epxa_map);
 	if (!mymtd) {
-		iounmap((void *)epxa_map.map_priv_1);
+		iounmap((void *)epxa_map.virt);
 		return -ENXIO;
 	}
 
-	mymtd->module = THIS_MODULE;
+	mymtd->owner = THIS_MODULE;
 
 	/* Unlock the flash device. */
 	if(mymtd->unlock){
@@ -135,23 +87,14 @@
 		}
 	}
 
-#ifdef CONFIG_MTD_REDBOOT_PARTS
-	nr_parts = parse_redboot_partitions(mymtd, &parts);
-
-	if (nr_parts > 0) {
-		add_mtd_partitions(mymtd, parts, nr_parts);
-		return 0;
-	}
-#endif
-#ifdef CONFIG_MTD_AFS_PARTS
-	nr_parts = parse_afs_partitions(mymtd, &parts);
+#ifdef CONFIG_MTD_PARTITIONS
+	nr_parts = parse_mtd_partitions(mymtd, probes, &parts, 0);
 
 	if (nr_parts > 0) {
 		add_mtd_partitions(mymtd, parts, nr_parts);
 		return 0;
 	}
 #endif
-
 	/* No recognised partitioning schemes found - use defaults */
 	nr_parts = epxa_default_partitions(mymtd, &parts);
 	if (nr_parts > 0) {
@@ -173,9 +116,9 @@
 			del_mtd_device(mymtd);
 		map_destroy(mymtd);
 	}
-	if (epxa_map.map_priv_1) {
-		iounmap((void *)epxa_map.map_priv_1);
-		epxa_map.map_priv_1 = 0;
+	if (epxa_map.virt) {
+		iounmap((void *)epxa_map.virt);
+		epxa_map.virt = 0;
 	}
 }
 
diff -Nur linux-2.4.30/drivers/mtd/maps/fortunet.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/fortunet.c
--- linux-2.4.30/drivers/mtd/maps/fortunet.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/fortunet.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,11 +1,12 @@
 /* fortunet.c memory map
  *
- * $Id: fortunet.c,v 1.2 2002/10/14 12:50:22 rmk Exp $
+ * $Id: fortunet.c,v 1.6 2003/05/21 12:45:18 dwmw2 Exp $
  */
 
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -23,7 +24,7 @@
 
 struct map_region
 {
-	int			window_addr_phyical;
+	int			window_addr_physical;
 	int			altbuswidth;
 	struct map_info		map_info;
 	struct mtd_info		*mymtd;
@@ -37,57 +38,10 @@
 static int			map_regions_parts[MAX_NUM_REGIONS] = {0,0,0,0};
 
 
-__u8 fortunet_read8(struct map_info *map, unsigned long ofs)
-{
-	return *(__u8 *)(map->map_priv_1 + ofs);
-}
-
-__u16 fortunet_read16(struct map_info *map, unsigned long ofs)
-{
-	return *(__u16 *)(map->map_priv_1 + ofs);
-}
-
-__u32 fortunet_read32(struct map_info *map, unsigned long ofs)
-{
-	return *(__u32 *)(map->map_priv_1 + ofs);
-}
-
-void fortunet_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy(to, (void *)(map->map_priv_1 + from), len);
-}
-
-void fortunet_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	*(__u8 *)(map->map_priv_1 + adr) = d;
-}
-
-void fortunet_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	*(__u16 *)(map->map_priv_1 + adr) = d;
-}
-
-void fortunet_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	*(__u32 *)(map->map_priv_1 + adr) = d;
-}
-
-void fortunet_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy((void *)(map->map_priv_1 + to), from, len);
-}
 
 struct map_info default_map = {
-	size: DEF_WINDOW_SIZE,
-	buswidth: 4,
-	read8: fortunet_read8,
-	read16: fortunet_read16,
-	read32: fortunet_read32,
-	copy_from: fortunet_copy_from,
-	write8: fortunet_write8,
-	write16: fortunet_write16,
-	write32: fortunet_write32,
-	copy_to: fortunet_copy_to
+	.size = DEF_WINDOW_SIZE,
+	.buswidth = 4,
 };
 
 static char * __init get_string_option(char *dest,int dest_size,char *sor)
@@ -147,7 +101,7 @@
 	get_options (get_string_option(string,sizeof(string),line),6,params);
 	if(params[0]<1)
 	{
-		printk(MTD_FORTUNET_PK "Bad paramters for MTD Region "
+		printk(MTD_FORTUNET_PK "Bad parameters for MTD Region "
 			" name,region-number[,base,size,buswidth,altbuswidth]\n");
 		return 1;
 	}
@@ -161,14 +115,14 @@
 	memcpy(&map_regions[params[1]].map_info,
 		&default_map,sizeof(map_regions[params[1]].map_info));
         map_regions_set[params[1]] = 1;
-        map_regions[params[1]].window_addr_phyical = DEF_WINDOW_ADDR_PHY;
+        map_regions[params[1]].window_addr_physical = DEF_WINDOW_ADDR_PHY;
         map_regions[params[1]].altbuswidth = 2;
         map_regions[params[1]].mymtd = NULL;
 	map_regions[params[1]].map_info.name = map_regions[params[1]].map_name;
 	strcpy(map_regions[params[1]].map_info.name,string);
 	if(params[0]>1)
 	{
-		map_regions[params[1]].window_addr_phyical = params[2];
+		map_regions[params[1]].window_addr_physical = params[2];
 	}
 	if(params[0]>2)
 	{
@@ -185,14 +139,14 @@
 	return 1;
 }
 
-static int __init MTD_New_Partion(char *line)
+static int __init MTD_New_Partition(char *line)
 {
 	char	string[MAX_NAME_SIZE];
 	int	params[4];
 	get_options (get_string_option(string,sizeof(string),line),4,params);
 	if(params[0]<3)
 	{
-		printk(MTD_FORTUNET_PK "Bad paramters for MTD Partion "
+		printk(MTD_FORTUNET_PK "Bad parameters for MTD Partition "
 			" name,region-number,size,offset\n");
 		return 1;
 	}
@@ -204,7 +158,7 @@
 	}
 	if(map_regions_parts[params[1]]>=MAX_NUM_PARTITIONS)
 	{
-		printk(MTD_FORTUNET_PK "Out of space for partion in this region\n");
+		printk(MTD_FORTUNET_PK "Out of space for partition in this region\n");
 		return 1;
 	}
 	map_regions[params[1]].parts[map_regions_parts[params[1]]].name =
@@ -220,7 +174,10 @@
 }
 
 __setup("MTD_Region=", MTD_New_Region);
-__setup("MTD_Partion=", MTD_New_Partion);
+__setup("MTD_Partition=", MTD_New_Partition);
+
+/* Backwards-spelling-compatibility */
+__setup("MTD_Partion=", MTD_New_Partition);
 
 int __init init_fortunet(void)
 {
@@ -229,13 +186,13 @@
 	{
 		if(map_regions_parts[ix]&&(!map_regions_set[ix]))
 		{
-			printk(MTD_FORTUNET_PK "Region %d is not setup (Seting to default)\n",
+			printk(MTD_FORTUNET_PK "Region %d is not setup (Setting to default)\n",
 				ix);
 			memset(&map_regions[ix],0,sizeof(map_regions[ix]));
 			memcpy(&map_regions[ix].map_info,&default_map,
 				sizeof(map_regions[ix].map_info));
 			map_regions_set[ix] = 1;
-			map_regions[ix].window_addr_phyical = DEF_WINDOW_ADDR_PHY;
+			map_regions[ix].window_addr_physical = DEF_WINDOW_ADDR_PHY;
 			map_regions[ix].altbuswidth = 2;
 			map_regions[ix].mymtd = NULL;
 			map_regions[ix].map_info.name = map_regions[ix].map_name;
@@ -244,30 +201,35 @@
 		if(map_regions_set[ix])
 		{
 			iy++;
-			printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at phyicaly "
+			printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at physically "
 				" address %x size %x\n",
 				map_regions[ix].map_info.name,
-				map_regions[ix].window_addr_phyical,
+				map_regions[ix].window_addr_physical,
 				map_regions[ix].map_info.size);
-			map_regions[ix].map_info.map_priv_1 =
+
+			map_regions[ix].map_info.phys =	map_regions[ix].window_addr_physical,
+
+			map_regions[ix].map_info.virt =
 				(int)ioremap_nocache(
-				map_regions[ix].window_addr_phyical,
+				map_regions[ix].window_addr_physical,
 				map_regions[ix].map_info.size);
-			if(!map_regions[ix].map_info.map_priv_1)
+			if(!map_regions[ix].map_info.virt)
 			{
 				printk(MTD_FORTUNET_PK "%s flash failed to ioremap!\n",
 					map_regions[ix].map_info.name);
 				return -ENXIO;
 			}
-			printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is veritualy at: %x\n",
+			simple_map_init(&map_regions[ix].map_info);
+
+			printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is virtually at: %x\n",
 				map_regions[ix].map_info.name,
-				map_regions[ix].map_info.map_priv_1);
+				map_regions[ix].map_info.virt);
 			map_regions[ix].mymtd = do_map_probe("cfi_probe",
 				&map_regions[ix].map_info);
 			if((!map_regions[ix].mymtd)&&(
 				map_regions[ix].altbuswidth!=map_regions[ix].map_info.buswidth))
 			{
-				printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternet buswidth "
+				printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternate buswidth "
 					"for %s flash.\n",
 					map_regions[ix].map_info.name);
 				map_regions[ix].map_info.buswidth =
@@ -275,7 +237,7 @@
 				map_regions[ix].mymtd = do_map_probe("cfi_probe",
 					&map_regions[ix].map_info);
 			}
-			map_regions[ix].mymtd->module = THIS_MODULE;
+			map_regions[ix].mymtd->owner = THIS_MODULE;
 			add_mtd_partitions(map_regions[ix].mymtd,
 				map_regions[ix].parts,map_regions_parts[ix]);
 		}
@@ -297,7 +259,7 @@
 				del_mtd_partitions( map_regions[ix].mymtd );
 				map_destroy( map_regions[ix].mymtd );
 			}
-			iounmap((void *)map_regions[ix].map_info.map_priv_1);
+			iounmap((void *)map_regions[ix].map_info.virt);
 		}
 	}
 }
diff -Nur linux-2.4.30/drivers/mtd/maps/h720x-flash.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/h720x-flash.c
--- linux-2.4.30/drivers/mtd/maps/h720x-flash.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/h720x-flash.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,142 @@
+/*
+ * Flash memory access on Hynix GMS30C7201/HMS30C7202 based 
+ * evaluation boards
+ * 
+ * (C) 2002 Jungjun Kim <jungjun.kim@hynix.com>
+ *     2003 Thomas Gleixner <tglx@linutronix.de>	
+*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <asm/hardware.h>
+#include <asm/io.h>
+
+static struct mtd_info *mymtd;
+
+static struct map_info h720x_map = {
+	.name =		"H720X",
+	.buswidth =	4,
+	.size =		FLASH_SIZE,
+	.phys =		FLASH_PHYS,
+};
+
+static struct mtd_partition h720x_partitions[] = {
+        {
+                .name = "ArMon",
+                .size = 0x00080000,
+                .offset = 0,
+                .mask_flags = MTD_WRITEABLE
+        },{
+                .name = "Env",
+                .size = 0x00040000,
+                .offset = 0x00080000,
+                .mask_flags = MTD_WRITEABLE
+        },{
+                .name = "Kernel",
+                .size = 0x00180000,
+                .offset = 0x000c0000,
+                .mask_flags = MTD_WRITEABLE
+        },{
+                .name = "Ramdisk",
+                .size = 0x00400000,
+                .offset = 0x00240000,
+                .mask_flags = MTD_WRITEABLE
+        },{
+                .name = "jffs2",
+                .size = MTDPART_SIZ_FULL,
+                .offset = MTDPART_OFS_APPEND
+        }
+};
+
+#define NUM_PARTITIONS  (sizeof(h720x_partitions)/sizeof(h720x_partitions[0]))
+
+static int                   nr_mtd_parts;
+static struct mtd_partition *mtd_parts;
+static const char *probes[] = { "cmdlinepart", NULL };
+
+/*
+ * Initialize FLASH support
+ */
+int __init h720x_mtd_init(void)
+{
+
+	char	*part_type = NULL;
+	
+	h720x_map.virt = (unsigned long)ioremap(FLASH_PHYS, FLASH_SIZE);
+
+	if (!h720x_map.virt) {
+		printk(KERN_ERR "H720x-MTD: ioremap failed\n");
+		return -EIO;
+	}
+
+	simple_map_init(&h720x_map);
+
+	// Probe for flash buswidth 4
+	printk (KERN_INFO "H720x-MTD probing 32bit FLASH\n");
+	mymtd = do_map_probe("cfi_probe", &h720x_map);
+	if (!mymtd) {
+		printk (KERN_INFO "H720x-MTD probing 16bit FLASH\n");
+	    // Probe for buswidth 2
+	    h720x_map.buswidth = 2;
+	    mymtd = do_map_probe("cfi_probe", &h720x_map);
+	}
+	    
+	if (mymtd) {
+		mymtd->owner = THIS_MODULE;
+
+#ifdef CONFIG_MTD_PARTITIONS
+		nr_mtd_parts = parse_mtd_partitions(mymtd, probes, &mtd_parts, 0);
+		if (nr_mtd_parts > 0)
+			part_type = "command line";
+#endif
+		if (nr_mtd_parts <= 0) {
+			mtd_parts = h720x_partitions;
+			nr_mtd_parts = NUM_PARTITIONS;
+			part_type = "builtin";
+		}
+		printk(KERN_INFO "Using %s partition table\n", part_type);
+		add_mtd_partitions(mymtd, mtd_parts, nr_mtd_parts);
+		return 0;
+	}
+
+	iounmap((void *)h720x_map.virt);
+	return -ENXIO;
+}
+
+/*
+ * Cleanup
+ */
+static void __exit h720x_mtd_cleanup(void)
+{
+
+	if (mymtd) {
+		del_mtd_partitions(mymtd);
+		map_destroy(mymtd);
+	}
+	
+	/* Free partition info, if commandline partition was used */
+	if (mtd_parts && (mtd_parts != h720x_partitions))
+		kfree (mtd_parts);
+	
+	if (h720x_map.virt) {
+		iounmap((void *)h720x_map.virt);
+		h720x_map.virt = 0;
+	}
+}
+
+
+module_init(h720x_mtd_init);
+module_exit(h720x_mtd_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>");
+MODULE_DESCRIPTION("MTD map driver for Hynix evaluation boards");
diff -Nur linux-2.4.30/drivers/mtd/maps/ichxrom.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/ichxrom.c
--- linux-2.4.30/drivers/mtd/maps/ichxrom.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/ichxrom.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,380 @@
+/*
+ * ichxrom.c
+ *
+ * Normal mappings of chips in physical memory
+ * $Id: ichxrom.c,v 1.1 2003/10/27 19:49:23 thayne Exp $
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+
+#define xstr(s) str(s)
+#define str(s) #s
+#define MOD_NAME xstr(KBUILD_BASENAME)
+
+#define MTD_DEV_NAME_LENGTH 16
+
+#define RESERVE_MEM_REGION 0
+
+#define ICHX_FWH_REGION_START	0xFF000000UL
+#define ICHX_FWH_REGION_SIZE	0x01000000UL
+#define BIOS_CNTL	0x4e
+#define FWH_DEC_EN1	0xE3
+#define FWH_DEC_EN2	0xF0
+#define FWH_SEL1	0xE8
+#define FWH_SEL2	0xEE
+
+struct ichxrom_map_info {
+	struct map_info map;
+	struct mtd_info *mtd;
+	unsigned long window_addr;
+	struct pci_dev *pdev;
+	struct resource window_rsrc;
+	struct resource rom_rsrc;
+	char mtd_name[MTD_DEV_NAME_LENGTH];
+};
+
+static inline unsigned long addr(struct map_info *map, unsigned long ofs)
+{
+	unsigned long offset;
+	offset = ((8*1024*1024) - map->size) + ofs;
+	if (offset >= (4*1024*1024)) {
+		offset += 0x400000;
+	}
+	return map->map_priv_1 + 0x400000 + offset;
+}
+
+static inline unsigned long dbg_addr(struct map_info *map, unsigned long addr)
+{
+	return addr - map->map_priv_1 + ICHX_FWH_REGION_START;
+}
+	
+static __u8 ichxrom_read8(struct map_info *map, unsigned long ofs)
+{
+	return __raw_readb(addr(map, ofs));
+}
+
+static __u16 ichxrom_read16(struct map_info *map, unsigned long ofs)
+{
+	return __raw_readw(addr(map, ofs));
+}
+
+static __u32 ichxrom_read32(struct map_info *map, unsigned long ofs)
+{
+	return __raw_readl(addr(map, ofs));
+}
+
+static void ichxrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+{
+	memcpy_fromio(to, addr(map, from), len);
+}
+
+static void ichxrom_write8(struct map_info *map, __u8 d, unsigned long ofs)
+{
+	__raw_writeb(d, addr(map,ofs));
+	mb();
+}
+
+static void ichxrom_write16(struct map_info *map, __u16 d, unsigned long ofs)
+{
+	__raw_writew(d, addr(map, ofs));
+	mb();
+}
+
+static void ichxrom_write32(struct map_info *map, __u32 d, unsigned long ofs)
+{
+	__raw_writel(d, addr(map, ofs));
+	mb();
+}
+
+static void ichxrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+{
+	memcpy_toio(addr(map, to), from, len);
+}
+
+static struct ichxrom_map_info ichxrom_map = {
+	.map = {
+		.name = MOD_NAME,
+		.phys = NO_XIP,
+		.size = 0,
+		.buswidth = 1,
+		.read8 = ichxrom_read8,
+		.read16 = ichxrom_read16,
+		.read32 = ichxrom_read32,
+		.copy_from = ichxrom_copy_from,
+		.write8 = ichxrom_write8,
+		.write16 = ichxrom_write16,
+		.write32 = ichxrom_write32,
+		.copy_to = ichxrom_copy_to,
+		/* Firmware hubs only use vpp when being programmed
+		 * in a factory setting.  So in-place programming
+		 * needs to use a different method.
+		 */
+	},
+	/* remaining fields of structure are initialized to 0 */
+};
+
+enum fwh_lock_state {
+	FWH_DENY_WRITE = 1,
+	FWH_IMMUTABLE  = 2,
+	FWH_DENY_READ  = 4,
+};
+
+static void ichxrom_cleanup(struct ichxrom_map_info *info)
+{
+	u16 word;
+
+	/* Disable writes through the rom window */
+	pci_read_config_word(info->pdev, BIOS_CNTL, &word);
+	pci_write_config_word(info->pdev, BIOS_CNTL, word & ~1);
+
+	if (info->mtd) {
+		del_mtd_device(info->mtd);
+		map_destroy(info->mtd);
+		info->mtd = NULL;
+		info->map.virt = 0;
+	}
+	if (info->rom_rsrc.parent)
+		release_resource(&info->rom_rsrc);
+	if (info->window_rsrc.parent)
+		release_resource(&info->window_rsrc);
+
+	if (info->window_addr) {
+		iounmap((void *)(info->window_addr));
+		info->window_addr = 0;
+	}
+}
+
+
+static int ichxrom_set_lock_state(struct mtd_info *mtd, loff_t ofs, size_t len,
+	enum fwh_lock_state state)
+{
+	struct map_info *map = mtd->priv;
+	unsigned long start = ofs;
+	unsigned long end = start + len -1;
+
+	/* FIXME do I need to guard against concurrency here? */
+	/* round down to 64K boundaries */
+	start = start & ~0xFFFF;
+	end = end & ~0xFFFF;
+	while (start <= end) {
+		unsigned long ctrl_addr;
+		ctrl_addr = addr(map, start) - 0x400000 + 2;
+		writeb(state, ctrl_addr);
+		start = start + 0x10000;
+	}
+	return 0;
+}
+
+static int ichxrom_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+	return ichxrom_set_lock_state(mtd, ofs, len, FWH_DENY_WRITE);
+}
+
+static int ichxrom_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+	return ichxrom_set_lock_state(mtd, ofs, len, 0);
+}
+
+static int __devinit ichxrom_init_one (struct pci_dev *pdev,
+	const struct pci_device_id *ent)
+{
+	u16 word;
+	struct ichxrom_map_info *info = &ichxrom_map;
+	unsigned long map_size;
+
+	/* For now I just handle the ichx and I assume there
+	 * are not a lot of resources up at the top of the address
+	 * space.  It is possible to handle other devices in the
+	 * top 16MB but it is very painful.  Also since
+	 * you can only really attach a FWH to an ICHX there
+	 * a number of simplifications you can make.
+	 *
+	 * Also you can page firmware hubs if an 8MB window isn't enough 
+	 * but don't currently handle that case either.
+	 */
+
+	info->pdev = pdev;
+
+	/*
+	 * Try to reserve the window mem region.  If this fails then
+	 * it is likely due to the window being "reseved" by the BIOS.
+	 */
+	info->window_rsrc.name = MOD_NAME;
+	info->window_rsrc.start = ICHX_FWH_REGION_START;
+	info->window_rsrc.end = ICHX_FWH_REGION_START + ICHX_FWH_REGION_SIZE - 1;
+	info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+	if (request_resource(&iomem_resource, &info->window_rsrc)) {
+		info->window_rsrc.parent = NULL;
+		printk(KERN_ERR MOD_NAME
+		       " %s(): Unable to register resource"
+		       " 0x%.08lx-0x%.08lx - kernel bug?\n",
+		       __func__,
+		       info->window_rsrc.start, info->window_rsrc.end);
+	}
+	
+	/* Enable writes through the rom window */
+	pci_read_config_word(pdev, BIOS_CNTL, &word);
+	if (!(word & 1)  && (word & (1<<1))) {
+		/* The BIOS will generate an error if I enable
+		 * this device, so don't even try.
+		 */
+		printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n");
+		goto failed;
+	}
+	pci_write_config_word(pdev, BIOS_CNTL, word | 1);
+
+
+	/* Map the firmware hub into my address space. */
+	/* Does this use too much virtual address space? */
+	info->window_addr = (unsigned long)ioremap(
+		ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
+	if (!info->window_addr) {
+		printk(KERN_ERR "Failed to ioremap\n");
+		goto failed;
+	}
+
+	/* For now assume the firmware has setup all relevant firmware
+	 * windows.  We don't have enough information to handle this case
+	 * intelligently.
+	 */
+
+	/* FIXME select the firmware hub and enable a window to it. */
+
+	info->mtd = 0;
+	info->map.map_priv_1 = info->window_addr;
+
+	map_size = ICHX_FWH_REGION_SIZE;
+	while(!info->mtd && (map_size > 0)) {
+		info->map.size = map_size;
+		info->mtd = do_map_probe("jedec_probe", &ichxrom_map.map);
+		map_size -= 512*1024;
+	}
+	if (!info->mtd) {
+		goto failed;
+	}
+	/* I know I can only be a firmware hub here so put
+	 * in the special lock and unlock routines.
+	 */
+	info->mtd->lock = ichxrom_lock;
+	info->mtd->unlock = ichxrom_unlock;
+		
+	info->mtd->owner = THIS_MODULE;
+	add_mtd_device(info->mtd);
+
+	if (info->window_rsrc.parent) {
+		/*
+		 * Registering the MTD device in iomem may not be possible
+		 * if there is a BIOS "reserved" and BUSY range.  If this
+		 * fails then continue anyway.
+		 */
+		snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH,
+			 "mtd%d", info->mtd->index);
+
+		info->rom_rsrc.name = info->mtd_name;
+		info->rom_rsrc.start = ICHX_FWH_REGION_START
+			+ ICHX_FWH_REGION_SIZE - map_size;
+		info->rom_rsrc.end = ICHX_FWH_REGION_START
+			+ ICHX_FWH_REGION_SIZE;
+		info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+		if (request_resource(&info->window_rsrc, &info->rom_rsrc)) {
+			printk(KERN_ERR MOD_NAME
+			       ": cannot reserve MTD resource\n");
+			info->rom_rsrc.parent = NULL;
+		}
+	}
+
+	return 0;
+
+ failed:
+	ichxrom_cleanup(info);
+	return -ENODEV;
+}
+
+
+static void __devexit ichxrom_remove_one (struct pci_dev *pdev)
+{
+	struct ichxrom_map_info *info = &ichxrom_map;
+	u16 word;
+
+	del_mtd_device(info->mtd);
+	map_destroy(info->mtd);
+	info->mtd = 0;
+	info->map.map_priv_1 = 0;
+
+	iounmap((void *)(info->window_addr));
+	info->window_addr = 0;
+
+	/* Disable writes through the rom window */
+	pci_read_config_word(pdev, BIOS_CNTL, &word);
+	pci_write_config_word(pdev, BIOS_CNTL, word & ~1);
+
+#if RESERVE_MEM_REGION	
+	release_mem_region(ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE);
+#endif
+}
+
+static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = {
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, 
+	  PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, 
+	  PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, 
+	  PCI_ANY_ID, PCI_ANY_ID, },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,
+	  PCI_ANY_ID, PCI_ANY_ID, },
+	{ 0, },
+};
+
+MODULE_DEVICE_TABLE(pci, ichxrom_pci_tbl);
+
+#if 0
+static struct pci_driver ichxrom_driver = {
+	.name =		MOD_NAME,
+	.id_table =	ichxrom_pci_tbl,
+	.probe =	ichxrom_init_one,
+	.remove =	ichxrom_remove_one,
+};
+#endif
+
+static struct pci_dev *mydev;
+int __init init_ichxrom(void)
+{
+	struct pci_dev *pdev;
+	struct pci_device_id *id;
+	pdev = 0;
+	for(id = ichxrom_pci_tbl; id->vendor; id++) {
+		pdev = pci_find_device(id->vendor, id->device, 0);
+		if (pdev) {
+			break;
+		}
+	}
+	if (pdev) {
+		mydev = pdev;
+		return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]);
+	}
+	return -ENXIO;
+#if 0
+	return pci_module_init(&ichxrom_driver);
+#endif
+}
+
+static void __exit cleanup_ichxrom(void)
+{
+	ichxrom_remove_one(mydev);
+}
+
+module_init(init_ichxrom);
+module_exit(cleanup_ichxrom);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>");
+MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ICHX southbridge");
diff -Nur linux-2.4.30/drivers/mtd/maps/impa7.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/impa7.c
--- linux-2.4.30/drivers/mtd/maps/impa7.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/impa7.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: impa7.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $
+ * $Id: impa7.c,v 1.9 2003/06/23 11:47:43 dwmw2 Exp $
  *
  * Handle mapping of the NOR flash on implementa A7 boards
  *
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -37,75 +38,17 @@
 
 static struct mtd_info *impa7_mtd[NUM_FLASHBANKS] = { 0 };
 
-__u8 impa7_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-__u16 impa7_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
-
-__u32 impa7_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
-
-void impa7_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-void impa7_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void impa7_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void impa7_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void impa7_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio(map->map_priv_1 + to, from, len);
-}
 
 static struct map_info impa7_map[NUM_FLASHBANKS] = {
 	{
-	name: "impA7 NOR Flash Bank #0",
-	size: WINDOW_SIZE0,
-	buswidth: BUSWIDTH,
-	read8: impa7_read8,
-	read16: impa7_read16,
-	read32: impa7_read32,
-	copy_from: impa7_copy_from,
-	write8: impa7_write8,
-	write16: impa7_write16,
-	write32: impa7_write32,
-	copy_to: impa7_copy_to
+		.name = "impA7 NOR Flash Bank #0",
+		.size = WINDOW_SIZE0,
+		.buswidth = BUSWIDTH,
 	},
 	{
-	name: "impA7 NOR Flash Bank #1",
-	size: WINDOW_SIZE1,
-	buswidth: BUSWIDTH,
-	read8: impa7_read8,
-	read16: impa7_read16,
-	read32: impa7_read32,
-	copy_from: impa7_copy_from,
-	write8: impa7_write8,
-	write16: impa7_write16,
-	write32: impa7_write32,
-	copy_to: impa7_copy_to
+		.name = "impA7 NOR Flash Bank #1",
+		.size = WINDOW_SIZE1,
+		.buswidth = BUSWIDTH,
 	},
 };
 
@@ -116,25 +59,19 @@
  */
 static struct mtd_partition static_partitions[] =
 {
-    {
-	name: "FileSystem",
-	  size: 0x800000,
-	  offset: 0x00000000
-    },
+	{
+		.name = "FileSystem",
+		.size = 0x800000,
+		.offset = 0x00000000
+	},
 };
 
-#define NB_OF(x) (sizeof (x) / sizeof (x[0]))
+static int mtd_parts_nb[NUM_FLASHBANKS];
+static struct mtd_partition *mtd_parts[NUM_FLASHBANKS];
 
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-int parse_cmdline_partitions(struct mtd_info *master, 
-			     struct mtd_partition **pparts,
-			     const char *mtd_id);
 #endif
 
-#endif
-
-static int                   mtd_parts_nb = 0;
-static struct mtd_partition *mtd_parts    = 0;
+static const char *probes[] = { "cmdlinepart", NULL };
 
 int __init init_impa7(void)
 {
@@ -146,20 +83,21 @@
 	  { WINDOW_ADDR0, WINDOW_SIZE0 },
 	  { WINDOW_ADDR1, WINDOW_SIZE1 },
         };
-	char mtdid[10];
 	int devicesfound = 0;
 
 	for(i=0; i<NUM_FLASHBANKS; i++)
 	{
 		printk(KERN_NOTICE MSG_PREFIX "probing 0x%08lx at 0x%08lx\n",
 		       pt[i].size, pt[i].addr);
-		impa7_map[i].map_priv_1 = (unsigned long)
-		  ioremap(pt[i].addr, pt[i].size);
 
-		if (!impa7_map[i].map_priv_1) {
+		impa7_map[i].phys = pt[i].addr;
+		impa7_map[i].virt = (unsigned long)
+		  ioremap(pt[i].addr, pt[i].size);
+		if (!impa7_map[i].virt) {
 			printk(MSG_PREFIX "failed to ioremap\n");
 			return -EIO;
 		}
+		simple_map_init(&impa7_map[i]);
 
 		impa7_mtd[i] = 0;
 		type = rom_probe_types;
@@ -167,43 +105,34 @@
 			impa7_mtd[i] = do_map_probe(*type, &impa7_map[i]);
 		}
 
-		if (impa7_mtd[i]) 
-		{
-			impa7_mtd[i]->module = THIS_MODULE;
-			add_mtd_device(impa7_mtd[i]);
+		if (impa7_mtd[i]) {
+			impa7_mtd[i]->owner = THIS_MODULE;
 			devicesfound++;
 #ifdef CONFIG_MTD_PARTITIONS
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-			sprintf(mtdid, MTDID, i);
-			mtd_parts_nb = parse_cmdline_partitions(impa7_mtd[i], 
-								&mtd_parts, 
-								mtdid);
-			if (mtd_parts_nb > 0)
-			  part_type = "command line";
-#endif
-			if (mtd_parts_nb <= 0)
-			{
-				mtd_parts = static_partitions;
-				mtd_parts_nb = NB_OF(static_partitions);
+			mtd_parts_nb[i] = parse_mtd_partitions(impa7_mtd[i], 
+							       probes,
+							       &mtd_parts[i], 
+							       0);
+			if (mtd_parts_nb[i] > 0) {
+				part_type = "command line";
+			} else {
+				mtd_parts[i] = static_partitions;
+				mtd_parts_nb[i] = ARRAY_SIZE(static_partitions);
 				part_type = "static";
 			}
-			if (mtd_parts_nb <= 0)
-			{
-				printk(KERN_NOTICE MSG_PREFIX 
-				       "no partition info available\n");
-			}
-			else
-			{
-				printk(KERN_NOTICE MSG_PREFIX
-				       "using %s partition definition\n", 
-				       part_type);
-				add_mtd_partitions(impa7_mtd[i], 
-						   mtd_parts, mtd_parts_nb);
-			}
+
+			printk(KERN_NOTICE MSG_PREFIX
+			       "using %s partition definition\n", 
+			       part_type);
+			add_mtd_partitions(impa7_mtd[i], 
+					   mtd_parts[i], mtd_parts_nb[i]);
+#else
+			add_mtd_device(impa7_mtd[i]);
+
 #endif
 		}
 		else 
-		  iounmap((void *)impa7_map[i].map_priv_1);
+			iounmap((void *)impa7_map[i].virt);
 	}
 	return devicesfound == 0 ? -ENXIO : 0;
 }
@@ -211,17 +140,16 @@
 static void __exit cleanup_impa7(void)
 {
 	int i;
-	for (i=0; i<NUM_FLASHBANKS; i++) 
-	{
-		if (impa7_mtd[i]) 
-		{
+	for (i=0; i<NUM_FLASHBANKS; i++) {
+		if (impa7_mtd[i]) {
+#ifdef CONFIG_MTD_PARTITIONS
+			del_mtd_partitions(impa7_mtd[i]);
+#else
 			del_mtd_device(impa7_mtd[i]);
+#endif
 			map_destroy(impa7_mtd[i]);
-		}
-		if (impa7_map[i].map_priv_1)
-		{
-			iounmap((void *)impa7_map[i].map_priv_1);
-			impa7_map[i].map_priv_1 = 0;
+			iounmap((void *)impa7_map[i].virt);
+			impa7_map[i].virt = 0;
 		}
 	}
 }
diff -Nur linux-2.4.30/drivers/mtd/maps/integrator-flash.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/integrator-flash.c
--- linux-2.4.30/drivers/mtd/maps/integrator-flash.c	2002-08-03 02:39:44.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/integrator-flash.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,8 +1,9 @@
 /*======================================================================
 
-    drivers/mtd/maps/armflash.c: ARM Flash Layout/Partitioning
+    drivers/mtd/maps/integrator-flash.c: ARM Integrator flash map driver
   
     Copyright (C) 2000 ARM Limited
+    Copyright (C) 2003 Deep Blue Solutions Ltd.
   
    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
@@ -21,7 +22,7 @@
    This is access code for flashes using ARM's flash partitioning 
    standards.
 
-   $Id: integrator-flash.c,v 1.7 2001/11/01 20:55:47 rmk Exp $
+   $Id: integrator-flash.c,v 1.15 2004/02/27 22:37:39 rmk Exp $
 
 ======================================================================*/
 
@@ -31,268 +32,181 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/ioport.h>
+#include <linux/device.h>
 #include <linux/init.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
 
+#include <asm/mach/flash.h>
 #include <asm/hardware.h>
 #include <asm/io.h>
 #include <asm/system.h>
 
-extern int parse_afs_partitions(struct mtd_info *, struct mtd_partition **);
-
-// board specific stuff - sorry, it should be in arch/arm/mach-*.
-#ifdef CONFIG_ARCH_INTEGRATOR
-
-#define FLASH_BASE	INTEGRATOR_FLASH_BASE
-#define FLASH_SIZE	INTEGRATOR_FLASH_SIZE
-
-#define FLASH_PART_SIZE 0x400000
-
-#define SC_CTRLC	(IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET)
-#define SC_CTRLS	(IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET)
-#define EBI_CSR1	(IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET)
-#define EBI_LOCK	(IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET)
-
-/*
- * Initialise the flash access systems:
- *  - Disable VPP
- *  - Assert WP
- *  - Set write enable bit in EBI reg
- */
-static void armflash_flash_init(void)
-{
-	unsigned int tmp;
-
-	__raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
-
-	tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE;
-	__raw_writel(tmp, EBI_CSR1);
-
-	if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) {
-		__raw_writel(0xa05f, EBI_LOCK);
-		__raw_writel(tmp, EBI_CSR1);
-		__raw_writel(0, EBI_LOCK);
-	}
-}
-
-/*
- * Shutdown the flash access systems:
- *  - Disable VPP
- *  - Assert WP
- *  - Clear write enable bit in EBI reg
- */
-static void armflash_flash_exit(void)
-{
-	unsigned int tmp;
-
-	__raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
-
-	/*
-	 * Clear the write enable bit in system controller EBI register.
-	 */
-	tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE;
-	__raw_writel(tmp, EBI_CSR1);
-
-	if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) {
-		__raw_writel(0xa05f, EBI_LOCK);
-		__raw_writel(tmp, EBI_CSR1);
-		__raw_writel(0, EBI_LOCK);
-	}
-}
-
-static void armflash_flash_wp(int on)
-{
-	unsigned int reg;
-
-	if (on)
-		reg = SC_CTRLC;
-	else
-		reg = SC_CTRLS;
-
-	__raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg);
-}
-
-static void armflash_set_vpp(struct map_info *map, int on)
-{
-	unsigned int reg;
-
-	if (on)
-		reg = SC_CTRLS;
-	else
-		reg = SC_CTRLC;
-
-	__raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg);
-}
-#endif
-
 #ifdef CONFIG_ARCH_P720T
-
 #define FLASH_BASE		(0x04000000)
 #define FLASH_SIZE		(64*1024*1024)
-
-#define FLASH_PART_SIZE 	(4*1024*1024)
-#define FLASH_BLOCK_SIZE	(128*1024)
-
-static void armflash_flash_init(void)
-{
-}
-
-static void armflash_flash_exit(void)
-{
-}
-
-static void armflash_flash_wp(int on)
-{
-}
-
-static void armflash_set_vpp(struct map_info *map, int on)
-{
-}
 #endif
 
-static __u8 armflash_read8(struct map_info *map, unsigned long ofs)
-{
-	return readb(ofs + map->map_priv_2);
-}
-
-static __u16 armflash_read16(struct map_info *map, unsigned long ofs)
-{
-	return readw(ofs + map->map_priv_2);
-}
-
-static __u32 armflash_read32(struct map_info *map, unsigned long ofs)
-{
-	return readl(ofs + map->map_priv_2);
-}
+struct armflash_info {
+	struct flash_platform_data *plat;
+	struct resource		*res;
+	struct mtd_partition	*parts;
+	struct mtd_info		*mtd;
+	struct map_info		map;
+};
 
-static void armflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+static void armflash_set_vpp(struct map_info *map, int on)
 {
-	memcpy(to, (void *) (from + map->map_priv_2), len);
-}
+	struct armflash_info *info = container_of(map, struct armflash_info, map);
 
-static void armflash_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	writeb(d, adr + map->map_priv_2);
+	if (info->plat && info->plat->set_vpp)
+		info->plat->set_vpp(on);
 }
 
-static void armflash_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	writew(d, adr + map->map_priv_2);
-}
+static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL };
 
-static void armflash_write32(struct map_info *map, __u32 d, unsigned long adr)
+static int armflash_probe(struct device *_dev)
 {
-	writel(d, adr + map->map_priv_2);
-}
+	struct platform_device *dev = to_platform_device(_dev);
+	struct flash_platform_data *plat = dev->dev.platform_data;
+	struct resource *res = dev->resource;
+	unsigned int size = res->end - res->start + 1;
+	struct armflash_info *info;
+	int err;
+	void *base;
 
-static void armflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy((void *) (to + map->map_priv_2), from, len);
-}
+	info = kmalloc(sizeof(struct armflash_info), GFP_KERNEL);
+	if (!info) {
+		err = -ENOMEM;
+		goto out;
+	}
 
-static struct map_info armflash_map =
-{
-	name:		"AFS",
-	read8:		armflash_read8,
-	read16:		armflash_read16,
-	read32:		armflash_read32,
-	copy_from:	armflash_copy_from,
-	write8:		armflash_write8,
-	write16:	armflash_write16,
-	write32:	armflash_write32,
-	copy_to:	armflash_copy_to,
-	set_vpp:	armflash_set_vpp,
-};
+	memset(info, 0, sizeof(struct armflash_info));
 
-static struct mtd_info *mtd;
-static struct mtd_partition *parts;
+	info->plat = plat;
+	if (plat && plat->init) {
+		err = plat->init();
+		if (err)
+			goto no_resource;
+	}
 
-static int __init armflash_cfi_init(void *base, u_int size)
-{
-	int ret;
+	info->res = request_mem_region(res->start, size, "armflash");
+	if (!info->res) {
+		err = -EBUSY;
+		goto no_resource;
+	}
 
-	armflash_flash_init();
-	armflash_flash_wp(1);
+	base = ioremap(res->start, size);
+	if (!base) {
+		err = -ENOMEM;
+		goto no_mem;
+	}
 
 	/*
 	 * look for CFI based flash parts fitted to this board
 	 */
-	armflash_map.size       = size;
-	armflash_map.buswidth   = 4;
-	armflash_map.map_priv_2 = (unsigned long) base;
+	info->map.size		= size;
+	info->map.buswidth	= plat->width;
+	info->map.phys		= res->start;
+	info->map.virt		= (unsigned long) base;
+	info->map.name		= dev->dev.bus_id;
+	info->map.set_vpp	= armflash_set_vpp;
+
+	simple_map_init(&info->map);
 
 	/*
 	 * Also, the CFI layer automatically works out what size
 	 * of chips we have, and does the necessary identification
 	 * for us automatically.
 	 */
-	mtd = do_map_probe("cfi_probe", &armflash_map);
-	if (!mtd)
-		return -ENXIO;
-
-	mtd->module = THIS_MODULE;
-
-	ret = parse_afs_partitions(mtd, &parts);
-	if (ret > 0) {
-		ret = add_mtd_partitions(mtd, parts, ret);
-		if (ret)
-			printk(KERN_ERR "mtd partition registration "
-				"failed: %d\n", ret);
+	info->mtd = do_map_probe(plat->map_name, &info->map);
+	if (!info->mtd) {
+		err = -ENXIO;
+		goto no_device;
 	}
 
+	info->mtd->owner = THIS_MODULE;
+
+	err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0);
+	if (err > 0) {
+		err = add_mtd_partitions(info->mtd, info->parts, err);
+		if (err)
+			printk(KERN_ERR
+			       "mtd partition registration failed: %d\n", err);
+	}
+
+	if (err == 0)
+		dev_set_drvdata(&dev->dev, info);
+
 	/*
 	 * If we got an error, free all resources.
 	 */
-	if (ret < 0) {
-		del_mtd_partitions(mtd);
-		map_destroy(mtd);
-	}
-
-	return ret;
-}
+	if (err < 0) {
+		if (info->mtd) {
+			del_mtd_partitions(info->mtd);
+			map_destroy(info->mtd);
+		}
+		if (info->parts)
+			kfree(info->parts);
 
-static void armflash_cfi_exit(void)
-{
-	if (mtd) {
-		del_mtd_partitions(mtd);
-		map_destroy(mtd);
+ no_device:
+		iounmap(base);
+ no_mem:
+		release_mem_region(res->start, size);
+ no_resource:
+		if (plat && plat->exit)
+			plat->exit();
+		kfree(info);
 	}
-	if (parts)
-		kfree(parts);
+ out:
+	return err;
 }
 
-static int __init armflash_init(void)
+static int armflash_remove(struct device *_dev)
 {
-	int err = -EBUSY;
-	void *base;
+	struct platform_device *dev = to_platform_device(_dev);
+	struct armflash_info *info = dev_get_drvdata(&dev->dev);
 
-	if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL)
-		goto out;
+	dev_set_drvdata(&dev->dev, NULL);
 
-	base = ioremap(FLASH_BASE, FLASH_SIZE);
-	err = -ENOMEM;
-	if (base == NULL)
-		goto release;
+	if (info) {
+		if (info->mtd) {
+			del_mtd_partitions(info->mtd);
+			map_destroy(info->mtd);
+		}
+		if (info->parts)
+			kfree(info->parts);
 
-	err = armflash_cfi_init(base, FLASH_SIZE);
-	if (err) {
-		iounmap(base);
-release:
-		release_mem_region(FLASH_BASE, FLASH_SIZE);
+		iounmap((void *)info->map.virt);
+		release_resource(info->res);
+		kfree(info->res);
+
+		if (info->plat && info->plat->exit)
+			info->plat->exit();
+
+		kfree(info);
 	}
-out:
-	return err;
+
+	return 0;
+}
+
+static struct device_driver armflash_driver = {
+	.name		= "armflash",
+	.bus		= &platform_bus_type,
+	.probe		= armflash_probe,
+	.remove		= armflash_remove,
+};
+
+static int __init armflash_init(void)
+{
+	return driver_register(&armflash_driver);
 }
 
 static void __exit armflash_exit(void)
 {
-	armflash_cfi_exit();
-	iounmap((void *)armflash_map.map_priv_2);
-	release_mem_region(FLASH_BASE, FLASH_SIZE);
-	armflash_flash_exit();
+	driver_unregister(&armflash_driver);
 }
 
 module_init(armflash_init);
diff -Nur linux-2.4.30/drivers/mtd/maps/integrator-flash-v24.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/integrator-flash-v24.c
--- linux-2.4.30/drivers/mtd/maps/integrator-flash-v24.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/integrator-flash-v24.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,258 @@
+/*======================================================================
+
+    drivers/mtd/maps/armflash.c: ARM Flash Layout/Partitioning
+  
+    Copyright (C) 2000 ARM Limited
+  
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+  
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+  
+   You should have received a copy of the GNU General Public License
+   along with this program; if not, write to the Free Software
+   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+  
+   This is access code for flashes using ARM's flash partitioning 
+   standards.
+
+   $Id: integrator-flash-v24.c,v 1.12 2003/10/11 09:43:58 rmk Exp $
+
+======================================================================*/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/ioport.h>
+#include <linux/init.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+// board specific stuff - sorry, it should be in arch/arm/mach-*.
+#ifdef CONFIG_ARCH_INTEGRATOR
+
+#define FLASH_BASE	INTEGRATOR_FLASH_BASE
+#define FLASH_SIZE	INTEGRATOR_FLASH_SIZE
+
+#define FLASH_PART_SIZE 0x400000
+
+#define SC_CTRLC	(IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET)
+#define SC_CTRLS	(IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET)
+#define EBI_CSR1	(IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET)
+#define EBI_LOCK	(IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET)
+
+/*
+ * Initialise the flash access systems:
+ *  - Disable VPP
+ *  - Assert WP
+ *  - Set write enable bit in EBI reg
+ */
+static void armflash_flash_init(void)
+{
+	unsigned int tmp;
+
+	__raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
+
+	tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE;
+	__raw_writel(tmp, EBI_CSR1);
+
+	if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) {
+		__raw_writel(0xa05f, EBI_LOCK);
+		__raw_writel(tmp, EBI_CSR1);
+		__raw_writel(0, EBI_LOCK);
+	}
+}
+
+/*
+ * Shutdown the flash access systems:
+ *  - Disable VPP
+ *  - Assert WP
+ *  - Clear write enable bit in EBI reg
+ */
+static void armflash_flash_exit(void)
+{
+	unsigned int tmp;
+
+	__raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC);
+
+	/*
+	 * Clear the write enable bit in system controller EBI register.
+	 */
+	tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE;
+	__raw_writel(tmp, EBI_CSR1);
+
+	if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) {
+		__raw_writel(0xa05f, EBI_LOCK);
+		__raw_writel(tmp, EBI_CSR1);
+		__raw_writel(0, EBI_LOCK);
+	}
+}
+
+static void armflash_flash_wp(int on)
+{
+	unsigned int reg;
+
+	if (on)
+		reg = SC_CTRLC;
+	else
+		reg = SC_CTRLS;
+
+	__raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg);
+}
+
+static void armflash_set_vpp(struct map_info *map, int on)
+{
+	unsigned int reg;
+
+	if (on)
+		reg = SC_CTRLS;
+	else
+		reg = SC_CTRLC;
+
+	__raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg);
+}
+#endif
+
+#ifdef CONFIG_ARCH_P720T
+
+#define FLASH_BASE		(0x04000000)
+#define FLASH_SIZE		(64*1024*1024)
+
+#define FLASH_PART_SIZE 	(4*1024*1024)
+#define FLASH_BLOCK_SIZE	(128*1024)
+
+static void armflash_flash_init(void)
+{
+}
+
+static void armflash_flash_exit(void)
+{
+}
+
+static void armflash_flash_wp(int on)
+{
+}
+
+static void armflash_set_vpp(struct map_info *map, int on)
+{
+}
+#endif
+
+
+static struct map_info armflash_map =
+{
+	.name =		"AFS",
+	.set_vpp =	armflash_set_vpp,
+	.phys =		FLASH_BASE,
+};
+
+static struct mtd_info *mtd;
+static struct mtd_partition *parts;
+static const char *probes[] = { "RedBoot", "afs", NULL };
+
+static int __init armflash_cfi_init(void *base, u_int size)
+{
+	int ret;
+
+	armflash_flash_init();
+	armflash_flash_wp(1);
+
+	/*
+	 * look for CFI based flash parts fitted to this board
+	 */
+	armflash_map.size       = size;
+	armflash_map.buswidth   = 4;
+	armflash_map.virt = (unsigned long) base;
+
+	simple_map_init(&armflash_map);
+
+	/*
+	 * Also, the CFI layer automatically works out what size
+	 * of chips we have, and does the necessary identification
+	 * for us automatically.
+	 */
+	mtd = do_map_probe("cfi_probe", &armflash_map);
+	if (!mtd)
+		return -ENXIO;
+
+	mtd->owner = THIS_MODULE;
+
+	ret = parse_mtd_partitions(mtd, probes, &parts, (void *)0);
+	if (ret > 0) {
+		ret = add_mtd_partitions(mtd, parts, ret);
+		if (ret)
+			printk(KERN_ERR "mtd partition registration "
+				"failed: %d\n", ret);
+	}
+
+	/*
+	 * If we got an error, free all resources.
+	 */
+	if (ret < 0) {
+		del_mtd_partitions(mtd);
+		map_destroy(mtd);
+	}
+
+	return ret;
+}
+
+static void armflash_cfi_exit(void)
+{
+	if (mtd) {
+		del_mtd_partitions(mtd);
+		map_destroy(mtd);
+	}
+	if (parts)
+		kfree(parts);
+}
+
+static int __init armflash_init(void)
+{
+	int err = -EBUSY;
+	void *base;
+
+	if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL)
+		goto out;
+
+	base = ioremap(FLASH_BASE, FLASH_SIZE);
+	err = -ENOMEM;
+	if (base == NULL)
+		goto release;
+
+	err = armflash_cfi_init(base, FLASH_SIZE);
+	if (err) {
+		iounmap(base);
+release:
+		release_mem_region(FLASH_BASE, FLASH_SIZE);
+	}
+out:
+	return err;
+}
+
+static void __exit armflash_exit(void)
+{
+	armflash_cfi_exit();
+	iounmap((void *)armflash_map.virt);
+	release_mem_region(FLASH_BASE, FLASH_SIZE);
+	armflash_flash_exit();
+}
+
+module_init(armflash_init);
+module_exit(armflash_exit);
+
+MODULE_AUTHOR("ARM Ltd");
+MODULE_DESCRIPTION("ARM Integrator CFI map driver");
+MODULE_LICENSE("GPL");
diff -Nur linux-2.4.30/drivers/mtd/maps/iq80310.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/iq80310.c
--- linux-2.4.30/drivers/mtd/maps/iq80310.c	2002-08-03 02:39:44.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/iq80310.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: iq80310.c,v 1.9 2002/01/01 22:45:02 rmk Exp $
+ * $Id: iq80310.c,v 1.17 2003/06/23 11:48:18 dwmw2 Exp $
  *
  * Mapping for the Intel XScale IQ80310 evaluation board
  *
@@ -14,6 +14,8 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -26,127 +28,72 @@
 
 static struct mtd_info *mymtd;
 
-static __u8 iq80310_read8(struct map_info *map, unsigned long ofs)
-{
-	return *(__u8 *)(map->map_priv_1 + ofs);
-}
-
-static __u16 iq80310_read16(struct map_info *map, unsigned long ofs)
-{
-	return *(__u16 *)(map->map_priv_1 + ofs);
-}
-
-static __u32 iq80310_read32(struct map_info *map, unsigned long ofs)
-{
-	return *(__u32 *)(map->map_priv_1 + ofs);
-}
-
-static void iq80310_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy(to, (void *)(map->map_priv_1 + from), len);
-}
-
-static void iq80310_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	*(__u8 *)(map->map_priv_1 + adr) = d;
-}
-
-static void iq80310_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	*(__u16 *)(map->map_priv_1 + adr) = d;
-}
-
-static void iq80310_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	*(__u32 *)(map->map_priv_1 + adr) = d;
-}
-
-static void iq80310_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy((void *)(map->map_priv_1 + to), from, len);
-}
-
 static struct map_info iq80310_map = {
-	name: "IQ80310 flash",
-	size: WINDOW_SIZE,
-	buswidth: BUSWIDTH,
-	read8:		iq80310_read8,
-	read16:		iq80310_read16,
-	read32:		iq80310_read32,
-	copy_from:	iq80310_copy_from,
-	write8:		iq80310_write8,
-	write16:	iq80310_write16,
-	write32:	iq80310_write32,
-	copy_to:	iq80310_copy_to
+	.name = "IQ80310 flash",
+	.size = WINDOW_SIZE,
+	.buswidth = BUSWIDTH,
+	.phys = WINDOW_ADDR
 };
 
 static struct mtd_partition iq80310_partitions[4] = {
 	{
-		name:		"Firmware",
-		size:		0x00080000,
-		offset:		0,
-		mask_flags:	MTD_WRITEABLE  /* force read-only */
+		.name =		"Firmware",
+		.size =		0x00080000,
+		.offset =	0,
+		.mask_flags =	MTD_WRITEABLE  /* force read-only */
 	},{
-		name:		"Kernel",
-		size:		0x000a0000,
-		offset:		0x00080000,
+		.name =		"Kernel",
+		.size =		0x000a0000,
+		.offset =	0x00080000,
 	},{
-		name:		"Filesystem",
-		size:		0x00600000,
-		offset:		0x00120000
+		.name =		"Filesystem",
+		.size =		0x00600000,
+		.offset =	0x00120000
 	},{
-		name:		"RedBoot",
-		size:		0x000e0000,
-		offset:		0x00720000,
-		mask_flags:	MTD_WRITEABLE
+		.name =		"RedBoot",
+		.size =		0x000e0000,
+		.offset =	0x00720000,
+		.mask_flags =	MTD_WRITEABLE
 	}
 };
 
-#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
-
 static struct mtd_info *mymtd;
 static struct mtd_partition *parsed_parts;
-
-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
+static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
 
 static int __init init_iq80310(void)
 {
 	struct mtd_partition *parts;
 	int nb_parts = 0;
 	int parsed_nr_parts = 0;
-	char *part_type = "static";
+	int ret;
 
-	iq80310_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
-	if (!iq80310_map.map_priv_1) {
+	iq80310_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+	if (!iq80310_map.virt) {
 		printk("Failed to ioremap\n");
 		return -EIO;
 	}
+	simple_map_init(&iq80310_map);
+
 	mymtd = do_map_probe("cfi_probe", &iq80310_map);
 	if (!mymtd) {
-		iounmap((void *)iq80310_map.map_priv_1);
+		iounmap((void *)iq80310_map.virt);
 		return -ENXIO;
 	}
-	mymtd->module = THIS_MODULE;
+	mymtd->owner = THIS_MODULE;
 
-#ifdef CONFIG_MTD_REDBOOT_PARTS
-	if (parsed_nr_parts == 0) {
-		int ret = parse_redboot_partitions(mymtd, &parsed_parts);
-
-		if (ret > 0) {
-			part_type = "RedBoot";
-			parsed_nr_parts = ret;
-		}
-	}
-#endif
+	ret = parse_mtd_partitions(mymtd, probes, &parsed_parts, 0);
+
+	if (ret > 0)
+		parsed_nr_parts = ret;
 
 	if (parsed_nr_parts > 0) {
 		parts = parsed_parts;
 		nb_parts = parsed_nr_parts;
 	} else {
 		parts = iq80310_partitions;
-		nb_parts = NB_OF(iq80310_partitions);
+		nb_parts = ARRAY_SIZE(iq80310_partitions);
 	}
-	printk(KERN_NOTICE "Using %s partition definition\n", part_type);
 	add_mtd_partitions(mymtd, parts, nb_parts);
 	return 0;
 }
@@ -159,8 +106,8 @@
 		if (parsed_parts)
 			kfree(parsed_parts);
 	}
-	if (iq80310_map.map_priv_1)
-		iounmap((void *)iq80310_map.map_priv_1);
+	if (iq80310_map.virt)
+		iounmap((void *)iq80310_map.virt);
 }
 
 module_init(init_iq80310);
diff -Nur linux-2.4.30/drivers/mtd/maps/ixp425.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/ixp425.c
--- linux-2.4.30/drivers/mtd/maps/ixp425.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/ixp425.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,220 @@
+/*
+ * $Id: ixp425.c,v 1.1 2003/11/26 21:26:09 dsaxena Exp $
+ *
+ * drivers/mtd/maps/ixp425.c
+ *
+ * MTD Map file for IXP425 based systems. Please do not make per-board
+ * map driver as the code will be 90% identical. For now just add
+ * if(machine_is_XXX()) checks to the code. I'll clean this stuff to
+ * use platform_data in the the future so we can get rid of that too.
+ *
+ * Original Author: Intel Corporation
+ * Maintainer: Deepak Saxena <dsaxena@mvista.com>
+ *
+ * Copyright (C) 2002 Intel Corporation
+ * Copyright (C) 2003 MontaVista Software, Inc.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+
+#include <linux/reboot.h>
+
+#define WINDOW_ADDR 	0x50000000
+#define BUSWIDTH 	2
+
+#ifndef __ARMEB__
+#define	BYTE0(h)	((h) & 0xFF)
+#define	BYTE1(h)	(((h) >> 8) & 0xFF)
+#else
+#define	BYTE0(h)	(((h) >> 8) & 0xFF)
+#define	BYTE1(h)	((h) & 0xFF)
+#endif
+
+static __u16
+ixp425_read16(struct map_info *map, unsigned long ofs)
+{
+	return *(__u16 *) (map->map_priv_1 + ofs);
+}
+
+/*
+ * The IXP425 expansion bus only allows 16-bit wide acceses
+ * when attached to a 16-bit wide device (such as the 28F128J3A),
+ * so we can't just memcpy_fromio().
+ */
+static void
+ixp425_copy_from(struct map_info *map, void *to,
+		 unsigned long from, ssize_t len)
+{
+	int i;
+	u8 *dest = (u8 *) to;
+	u16 *src = (u16 *) (map->map_priv_1 + from);
+	u16 data;
+
+	for (i = 0; i < (len / 2); i++) {
+		data = src[i];
+		dest[i * 2] = BYTE0(data);
+		dest[i * 2 + 1] = BYTE1(data);
+	}
+
+	if (len & 1)
+		dest[len - 1] = BYTE0(src[i]);
+}
+
+static void
+ixp425_write16(struct map_info *map, __u16 d, unsigned long adr)
+{
+	*(__u16 *) (map->map_priv_1 + adr) = d;
+}
+
+static struct map_info ixp425_map = {
+	.name		= "IXP425 Flash",
+	.buswidth	= BUSWIDTH,
+	.read16		= ixp425_read16,
+	.copy_from	= ixp425_copy_from,
+	.write16	= ixp425_write16,
+};
+
+/*
+ * Put flash back in read mode so RedBoot can boot properly.
+ */
+int ixp425_mtd_reboot(struct notifier_block *n, unsigned long code, void *p)
+{
+	if (code != SYS_RESTART)
+		return NOTIFY_DONE;
+
+	ixp425_write16(&ixp425_map, 0xff, 0x55 * 0x2);
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block ixp425_mtd_notifier = {
+	notifier_call:ixp425_mtd_reboot,
+	next:NULL,
+	priority:0
+};
+
+static struct mtd_partition *parsed_parts;
+static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+
+static struct mtd_partition ixp425_partitions[] = {
+	{
+		.name	= "image",
+		.offset	= 0x00040000,
+		.size	= 0x00400000,
+	}, {
+		.name	= "user",
+		.offset	= 0x00440000,
+		.size	= MTDPART_SIZ_FULL
+	}
+};
+
+#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
+
+static struct mtd_info *ixp425_mtd;
+static struct resource *mtd_resource;
+
+static void
+ixp425_exit(void)
+{
+	if (ixp425_mtd) {
+		del_mtd_partitions(ixp425_mtd);
+		map_destroy(ixp425_mtd);
+	}
+	if (ixp425_map.map_priv_1)
+		iounmap((void *) ixp425_map.map_priv_1);
+	if (mtd_resource)
+		release_mem_region(WINDOW_ADDR, ixp425_map.size);
+
+	if (parsed_parts)
+		kfree(parsed_parts);
+
+	unregister_reboot_notifier(&ixp425_mtd_notifier);
+
+	/* Disable flash write */
+	*IXP425_EXP_CS0 &= ~IXP425_FLASH_WRITABLE;
+
+	if(machine_is_adi_coyote())
+		*IXP425_EXP_CS1 &= ~IXP425_FLASH_WRITABLE;
+}
+
+static int __init
+ixp425_init(void)
+{
+	int res = -1, npart;
+
+	/* Enable flash write */
+	*IXP425_EXP_CS0 |= IXP425_FLASH_WRITABLE;
+
+	/*
+	 * Coyote requires CS1 write to be enabled and has 32MB flash.
+	 * This will move to the platform init code in 2.6
+	 */
+	if(machine_is_adi_coyote()) {
+		*IXP425_EXP_CS1 |= IXP425_FLASH_WRITABLE;
+		ixp425_map.size = 0x02000000;
+	} else
+		ixp425_map.size = 0x01000000;
+
+	ixp425_map.map_priv_1 = 0;
+	mtd_resource =
+	    request_mem_region(WINDOW_ADDR, ixp425_map.size, "IXP425 Flash");
+	if (!mtd_resource) {
+		printk(KERN_ERR
+		       "ixp425 flash: Could not request mem region.\n");
+		res = -ENOMEM;
+		goto Error;
+	}
+
+	ixp425_map.map_priv_1 =
+	    (unsigned long) ioremap(WINDOW_ADDR, ixp425_map.size);
+	if (!ixp425_map.map_priv_1) {
+		printk("ixp425 Flash: Failed to map IO region. (ioremap)\n");
+		res = -EIO;
+		goto Error;
+	}
+
+	ixp425_mtd = do_map_probe("cfi_probe", &ixp425_map);
+	if (!ixp425_mtd) {
+		res = -ENXIO;
+		goto Error;
+	}
+	ixp425_mtd->owner = THIS_MODULE;
+
+	/* Try to parse RedBoot partitions */
+	npart = parse_mtd_partitions(ixp425_mtd, probes, &parsed_parts, 0);
+	if (npart > 0)
+		res = add_mtd_partitions(ixp425_mtd, parsed_parts, npart);
+	else {
+		printk("IXP425 Flash: Using static MTD partitions.\n");
+		res = add_mtd_partitions(ixp425_mtd, ixp425_partitions,
+					 NB_OF(ixp425_partitions));
+	}
+
+	if (res)
+		goto Error;
+
+	register_reboot_notifier(&ixp425_mtd_notifier);
+
+	return res;
+
+Error:
+	ixp425_exit();
+	return res;
+}
+
+module_init(ixp425_init);
+module_exit(ixp425_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MTD map driver for ixp425 evaluation board");
+MODULE_AUTHOR("Deepak Saxena");
diff -Nur linux-2.4.30/drivers/mtd/maps/l440gx.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/l440gx.c
--- linux-2.4.30/drivers/mtd/maps/l440gx.c	2002-08-03 02:39:44.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/l440gx.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: l440gx.c,v 1.8 2002/01/10 20:27:40 eric Exp $
+ * $Id: l440gx.c,v 1.12 2003/05/21 12:45:19 dwmw2 Exp $
  *
  * BIOS Flash chip on Intel 440GX board.
  *
@@ -9,6 +9,7 @@
 #include <linux/module.h>
 #include <linux/pci.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -27,48 +28,6 @@
 
 static struct mtd_info *mymtd;
 
-__u8 l440gx_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-__u16 l440gx_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
-
-__u32 l440gx_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
-
-void l440gx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-void l440gx_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void l440gx_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void l440gx_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void l440gx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio(map->map_priv_1 + to, from, len);
-}
 
 /* Is this really the vpp port? */
 void l440gx_set_vpp(struct map_info *map, int vpp)
@@ -85,22 +44,15 @@
 }
 
 struct map_info l440gx_map = {
-	name: "L440GX BIOS",
-	size: WINDOW_SIZE,
-	buswidth: BUSWIDTH,
-	read8: l440gx_read8,
-	read16: l440gx_read16,
-	read32: l440gx_read32,
-	copy_from: l440gx_copy_from,
-	write8: l440gx_write8,
-	write16: l440gx_write16,
-	write32: l440gx_write32,
-	copy_to: l440gx_copy_to,
+	.name = "L440GX BIOS",
+	.size = WINDOW_SIZE,
+	.buswidth = BUSWIDTH,
+	.phys = WINDOW_ADDR,
 #if 0
 	/* FIXME verify that this is the 
 	 * appripriate code for vpp enable/disable
 	 */
-	set_vpp: l440gx_set_vpp
+	.set_vpp = l440gx_set_vpp
 #endif
 };
 
@@ -113,7 +65,6 @@
 	dev = pci_find_device(PCI_VENDOR_ID_INTEL, 
 		PCI_DEVICE_ID_INTEL_82371AB_0, NULL);
 
-
 	pm_dev = pci_find_device(PCI_VENDOR_ID_INTEL, 
 		PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
 
@@ -122,15 +73,14 @@
 		return -ENODEV;
 	}
 
+	l440gx_map.virt = (unsigned long)ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE);
 
-	l440gx_map.map_priv_1 = (unsigned long)ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE);
-
-	if (!l440gx_map.map_priv_1) {
+	if (!l440gx_map.virt) {
 		printk(KERN_WARNING "Failed to ioremap L440GX flash region\n");
 		return -ENOMEM;
 	}
-
-	printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.map_priv_1);
+	simple_map_init(&l440gx_map);
+	printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.virt);
 
 	/* Setup the pm iobase resource 
 	 * This code should move into some kind of generic bridge
@@ -153,7 +103,7 @@
 		/* Allocate the resource region */
 		if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) {
 			printk(KERN_WARNING "Could not allocate pm iobase resource\n");
-			iounmap((void *)l440gx_map.map_priv_1);
+			iounmap((void *)l440gx_map.virt);
 			return -ENXIO;
 		}
 	}
@@ -181,13 +131,13 @@
 		mymtd = do_map_probe("map_rom", &l440gx_map);
 	}
 	if (mymtd) {
-		mymtd->module = THIS_MODULE;
+		mymtd->owner = THIS_MODULE;
 
 		add_mtd_device(mymtd);
 		return 0;
 	}
 
-	iounmap((void *)l440gx_map.map_priv_1);
+	iounmap((void *)l440gx_map.virt);
 	return -ENXIO;
 }
 
@@ -196,7 +146,7 @@
 	del_mtd_device(mymtd);
 	map_destroy(mymtd);
 	
-	iounmap((void *)l440gx_map.map_priv_1);
+	iounmap((void *)l440gx_map.virt);
 }
 
 module_init(init_l440gx);
diff -Nur linux-2.4.30/drivers/mtd/maps/lasat.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/lasat.c
--- linux-2.4.30/drivers/mtd/maps/lasat.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/lasat.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,111 +1,73 @@
 /*
- * Flash device on lasat 100 and 200 boards
+ * Flash device on Lasat 100 and 200 boards
  *
- * Presumably (C) 2002 Brian Murphy <brian@murphy.dk> or whoever he
- * works for.
+ * (C) 2002 Brian Murphy <brian@murphy.dk>
  *
  * 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: lasat.c,v 1.1 2003/01/24 14:26:38 dwmw2 Exp $
+ * $Id: lasat.c,v 1.6 2003/09/02 16:36:40 brm Exp $
  *
  */
 
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
 #include <linux/config.h>
 #include <asm/lasat/lasat.h>
-#include <asm/lasat/lasat_mtd.h>
 
-static struct mtd_info *mymtd;
+static struct mtd_info *lasat_mtd;
 
-static __u8 sp_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-static __u16 sp_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
-
-static __u32 sp_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
-
-static void sp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-static void sp_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
-
-static void sp_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
-
-static void sp_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
+static struct mtd_partition partition_info[LASAT_MTD_LAST];
+static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Filesystem", "Config"};
 
-static void sp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+static void lasat_set_vpp(struct map_info *map, int vpp)
 {
-	memcpy_toio(map->map_priv_1 + to, from, len);
+	if (vpp)
+	    *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit;
+	else
+	    *lasat_misc->flash_wp_reg &= ~(1 << lasat_misc->flash_wp_bit);
 }
 
-static struct map_info sp_map = {
-	.name = "SP flash",
+static struct map_info lasat_map = {
+	.name = "LASAT flash",
 	.buswidth = 4,
-	.read8 = sp_read8,
-	.read16 = sp_read16,
-	.read32 = sp_read32,
-	.copy_from = sp_copy_from,
-	.write8 = sp_write8,
-	.write16 = sp_write16,
-	.write32 = sp_write32,
-	.copy_to = sp_copy_to
+	.set_vpp = lasat_set_vpp
 };
 
-static struct mtd_partition partition_info[LASAT_MTD_LAST];
-static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Filesystem", "Config"};
-
-static int __init init_sp(void)
+static int __init init_lasat(void)
 {
 	int i;
-	/* this does not play well with the old flash code which 
-	 * protects and uprotects the flash when necessary */
-       	printk(KERN_NOTICE "Unprotecting flash\n");
-	*lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit;
+	/* since we use AMD chips and set_vpp is not implimented
+	 * for these (yet) we still have to permanently enable flash write */
+	printk(KERN_NOTICE "Unprotecting flash\n");
+	ENABLE_VPP((&lasat_map));
+
+	lasat_map.phys = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER);
+	lasat_map.virt = (unsigned long)ioremap_nocache(
+		        lasat_map.phys, lasat_board_info.li_flash_size);
+	lasat_map.size = lasat_board_info.li_flash_size;
 
-	sp_map.map_priv_1 = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER);
-	sp_map.size = lasat_board_info.li_flash_size;
-
-       	printk(KERN_NOTICE "sp flash device: %lx at %lx\n", 
-			sp_map.size, sp_map.map_priv_1);
+	simple_map_init(&lasat_map);
 
 	for (i=0; i < LASAT_MTD_LAST; i++)
 		partition_info[i].name = lasat_mtd_partnames[i];
 
-	mymtd = do_map_probe("cfi_probe", &sp_map);
-	if (mymtd) {
+	lasat_mtd = do_map_probe("cfi_probe", &lasat_map);
+
+	if (!lasat_mtd)
+	    lasat_mtd = do_map_probe("jedec_probe", &lasat_map);
+
+	if (lasat_mtd) {
 		u32 size, offset = 0;
 
-		mymtd->module = THIS_MODULE;
+		lasat_mtd->owner = THIS_MODULE;
 
 		for (i=0; i < LASAT_MTD_LAST; i++) {
 			size = lasat_flash_partition_size(i);
@@ -114,26 +76,26 @@
 			offset += size;
 		}
 
-		add_mtd_partitions( mymtd, partition_info, LASAT_MTD_LAST );
+		add_mtd_partitions( lasat_mtd, partition_info, LASAT_MTD_LAST );
 		return 0;
 	}
 
 	return -ENXIO;
 }
 
-static void __exit cleanup_sp(void)
+static void __exit cleanup_lasat(void)
 {
-	if (mymtd) {
-		del_mtd_partitions(mymtd);
-		map_destroy(mymtd);
+	if (lasat_mtd) {
+		del_mtd_partitions(lasat_mtd);
+		map_destroy(lasat_mtd);
 	}
-	if (sp_map.map_priv_1) {
-		sp_map.map_priv_1 = 0;
+	if (lasat_map.virt) {
+		lasat_map.virt = 0;
 	}
 }
 
-module_init(init_sp);
-module_exit(cleanup_sp);
+module_init(init_lasat);
+module_exit(cleanup_lasat);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Brian Murphy <brian@murphy.dk>");
diff -Nur linux-2.4.30/drivers/mtd/maps/lubbock-flash.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/lubbock-flash.c
--- linux-2.4.30/drivers/mtd/maps/lubbock-flash.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/lubbock-flash.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,151 @@
+/*
+ * $Id: lubbock-flash.c,v 1.11 2004/04/30 15:28:33 nico Exp $
+ *
+ * Map driver for the Lubbock developer platform.
+ *
+ * Author:	Nicolas Pitre
+ * Copyright:	(C) 2001 MontaVista Software Inc.
+ * 
+ * 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/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <asm/hardware.h>
+
+
+#define ROM_ADDR	0x00000000
+#define FLASH_ADDR	0x04000000
+
+#define WINDOW_SIZE 	64*1024*1024
+
+static struct map_info lubbock_maps[2] = { {
+	.size =		WINDOW_SIZE,
+	.phys =		0x00000000,
+}, {
+	.size =		WINDOW_SIZE,
+	.phys =		0x04000000,
+} };
+
+static struct mtd_partition lubbock_partitions[] = {
+	{
+		.name =		"Bootloader",
+		.size =		0x00040000,
+		.offset =	0,
+		.mask_flags =	MTD_WRITEABLE  /* force read-only */
+	},{
+		.name =		"Kernel",
+		.size =		0x00100000,
+		.offset =	0x00040000,
+	},{
+		.name =		"Filesystem",
+		.size =		MTDPART_SIZ_FULL,
+		.offset =	0x00140000
+	}
+};
+
+static struct mtd_info *mymtds[2];
+static struct mtd_partition *parsed_parts[2];
+static int nr_parsed_parts[2];
+
+static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+
+static int __init init_lubbock(void)
+{
+	int flashboot = (LUB_CONF_SWITCHES & 1);
+	int ret = 0, i;
+
+	lubbock_maps[0].buswidth = lubbock_maps[1].buswidth = 
+		(BOOT_DEF & 1) ? 2 : 4;
+
+	/* Compensate for the nROMBT switch which swaps the flash banks */
+	printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n",
+	       flashboot?"Flash":"ROM", flashboot);
+
+	lubbock_maps[flashboot^1].name = "Lubbock Application Flash";
+	lubbock_maps[flashboot].name = "Lubbock Boot ROM";
+
+	for (i = 0; i < 2; i++) {
+		lubbock_maps[i].virt = (unsigned long)ioremap(lubbock_maps[i].phys, WINDOW_SIZE);
+		if (!lubbock_maps[i].virt) {
+			printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name);
+			if (!ret)
+				ret = -ENOMEM;
+			continue;
+		}
+		simple_map_init(&lubbock_maps[i]);
+
+		printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit buswidth)\n",
+		       lubbock_maps[i].name, lubbock_maps[i].phys, 
+		       lubbock_maps[i].buswidth * 8);
+
+		mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]);
+		
+		if (!mymtds[i]) {
+			iounmap((void *)lubbock_maps[i].virt);
+			if (!ret)
+				ret = -EIO;
+			continue;
+		}
+		mymtds[i]->owner = THIS_MODULE;
+
+		ret = parse_mtd_partitions(mymtds[i], probes,
+					   &parsed_parts[i], 0);
+
+		if (ret > 0)
+			nr_parsed_parts[i] = ret;
+	}
+
+	if (!mymtds[0] && !mymtds[1])
+		return ret;
+	
+	for (i = 0; i < 2; i++) {
+		if (!mymtds[i]) {
+			printk(KERN_WARNING "%s is absent. Skipping\n", lubbock_maps[i].name);
+		} else if (nr_parsed_parts[i]) {
+			add_mtd_partitions(mymtds[i], parsed_parts[i], nr_parsed_parts[i]);
+		} else if (!i) {
+			printk("Using static partitions on %s\n", lubbock_maps[i].name);
+			add_mtd_partitions(mymtds[i], lubbock_partitions, ARRAY_SIZE(lubbock_partitions));
+		} else {
+			printk("Registering %s as whole device\n", lubbock_maps[i].name);
+			add_mtd_device(mymtds[i]);
+		}
+	}
+	return 0;
+}
+
+static void __exit cleanup_lubbock(void)
+{
+	int i;
+	for (i = 0; i < 2; i++) {
+		if (!mymtds[i])
+			continue;
+
+		if (nr_parsed_parts[i] || !i)
+			del_mtd_partitions(mymtds[i]);
+		else
+			del_mtd_device(mymtds[i]);			
+
+		map_destroy(mymtds[i]);
+		iounmap((void *)lubbock_maps[i].virt);
+
+		if (parsed_parts[i])
+			kfree(parsed_parts[i]);
+	}
+}
+
+module_init(init_lubbock);
+module_exit(cleanup_lubbock);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
+MODULE_DESCRIPTION("MTD map driver for Intel Lubbock");
diff -Nur linux-2.4.30/drivers/mtd/maps/Makefile linux-2.4.30-linux-mips.org/drivers/mtd/maps/Makefile
--- linux-2.4.30/drivers/mtd/maps/Makefile	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/Makefile	2005-05-12 18:01:30.000000000 +0200
@@ -1,12 +1,16 @@
 #
 # linux/drivers/maps/Makefile
 #
-# $Id: Makefile,v 1.37 2003/01/24 14:26:38 dwmw2 Exp $
+# $Id: Makefile.common,v 1.10 2004/04/12 06:40:41 ppopov Exp $
 
-BELOW25		:= $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/)
-
-ifeq ($(BELOW25),y)
+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
@@ -21,19 +25,13 @@
 obj-$(CONFIG_MTD_IQ80310)	+= iq80310.o
 obj-$(CONFIG_MTD_L440GX)	+= l440gx.o
 obj-$(CONFIG_MTD_AMD76XROM)	+= amd76xrom.o
-obj-$(CONFIG_MTD_ICH2ROM)	+= ich2rom.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_NORA)		+= nora.o
 obj-$(CONFIG_MTD_CEIVA)		+= ceiva.o
 obj-$(CONFIG_MTD_OCTAGON)	+= octagon-5066.o
-ifneq ($(CONFIG_MTD_PHYSMAP),n)
-  ifeq ($(CONFIG_MTD_PHYSMAP_BUSWIDTH),8)
-    obj-$(CONFIG_MTD_PHYSMAP)	+= physmap64.o 
-  else
-    obj-$(CONFIG_MTD_PHYSMAP)	+= physmap.o 
-  endif
-endif
+obj-$(CONFIG_MTD_PHYSMAP)	+= physmap.o 
 obj-$(CONFIG_MTD_PNC2000)	+= pnc2000.o
 obj-$(CONFIG_MTD_PCMCIA)	+= pcmciamtd.o
 obj-$(CONFIG_MTD_RPXLITE)	+= rpxlite.o
@@ -49,9 +47,7 @@
 obj-$(CONFIG_MTD_OCELOT)	+= ocelot.o
 obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
 obj-$(CONFIG_MTD_PCI)		+= pci.o
-obj-$(CONFIG_MTD_PB1000)	+= pb1xxx-flash.o
-obj-$(CONFIG_MTD_PB1100)	+= pb1xxx-flash.o
-obj-$(CONFIG_MTD_PB1500)	+= pb1xxx-flash.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
@@ -61,5 +57,13 @@
 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 $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/drivers/mtd/maps/map_funcs.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/map_funcs.c
--- linux-2.4.30/drivers/mtd/maps/map_funcs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/map_funcs.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,96 @@
+/*
+ * $Id: map_funcs.c,v 1.4 2004/04/02 01:25:23 sjhill Exp $
+ *
+ * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS
+ * is enabled.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <asm/io.h>
+
+#include <linux/mtd/map.h>
+
+static u8 simple_map_read8(struct map_info *map, unsigned long ofs)
+{
+	return __raw_readb(map->virt + ofs);
+}
+
+static u16 simple_map_read16(struct map_info *map, unsigned long ofs)
+{
+	return __raw_readw(map->virt + ofs);
+}
+
+static u32 simple_map_read32(struct map_info *map, unsigned long ofs)
+{
+	return __raw_readl(map->virt + ofs);
+}
+
+static u64 simple_map_read64(struct map_info *map, unsigned long ofs)
+{
+#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
+	BUG();
+	return 0;
+#else
+	return __raw_readq(map->virt + ofs);
+#endif
+}
+
+static void simple_map_write8(struct map_info *map, u8 datum, unsigned long ofs)
+{
+	__raw_writeb(datum, map->virt + ofs);
+	mb();
+}
+
+static void simple_map_write16(struct map_info *map, u16 datum, unsigned long ofs)
+{
+	__raw_writew(datum, map->virt + ofs);
+	mb();
+}
+
+static void simple_map_write32(struct map_info *map, u32 datum, unsigned long ofs)
+{
+	__raw_writel(datum, map->virt + ofs);
+	mb();
+}
+
+static void simple_map_write64(struct map_info *map, u64 datum, unsigned long ofs)
+{
+#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
+	BUG();
+#else
+	__raw_writeq(datum, map->virt + ofs);
+	mb();
+#endif /* CFI_B8 */
+}
+
+static void simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+{
+	memcpy_fromio(to, map->virt + from, len);
+}
+
+static void simple_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+{
+	memcpy_toio(map->virt + to, from, len);
+}
+
+void simple_map_init(struct map_info *map)
+{
+	map->read8 = simple_map_read8;
+	map->read16 = simple_map_read16;
+	map->read32 = simple_map_read32;
+	map->read64 = simple_map_read64;
+	map->write8 = simple_map_write8;
+	map->write16 = simple_map_write16;
+	map->write32 = simple_map_write32;
+	map->write64 = simple_map_write64;
+	map->copy_from = simple_map_copy_from;
+	map->copy_to = simple_map_copy_to;
+}
+
+EXPORT_SYMBOL(simple_map_init);
+
+MODULE_LICENSE("GPL");
diff -Nur linux-2.4.30/drivers/mtd/maps/mbx860.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/mbx860.c
--- linux-2.4.30/drivers/mtd/maps/mbx860.c	2002-08-03 02:39:44.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/mbx860.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: mbx860.c,v 1.1 2001/11/18 19:43:09 dwmw2 Exp $
+ * $Id: mbx860.c,v 1.5 2003/05/21 12:45:19 dwmw2 Exp $
  *
  * Handle mapping of the flash on MBX860 boards
  *
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -36,91 +37,46 @@
  * single flash device into. If the size if zero we use up to the end of the
  * device. */
 static struct mtd_partition partition_info[]={
-	{ name: "MBX flash BOOT partition",
-	offset: 0,
-	size:   BOOT_PARTITION_SIZE_KiB*1024 },
-	{ name: "MBX flash DATA partition",
-	offset: BOOT_PARTITION_SIZE_KiB*1024,
-	size: (KERNEL_PARTITION_SIZE_KiB)*1024 },
-	{ name: "MBX flash APPLICATION partition",
-	offset: (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 }
+	{ .name = "MBX flash BOOT partition",
+	.offset = 0,
+	.size =   BOOT_PARTITION_SIZE_KiB*1024 },
+	{ .name = "MBX flash DATA partition",
+	.offset = BOOT_PARTITION_SIZE_KiB*1024,
+	.size = (KERNEL_PARTITION_SIZE_KiB)*1024 },
+	{ .name = "MBX flash APPLICATION partition",
+	.offset = (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 }
 };
 				   
 
 static struct mtd_info *mymtd;
 
-__u8 mbx_read8(struct map_info *map, unsigned long ofs)
-{
-	return readb(map->map_priv_1 + ofs);
-}
-
-__u16 mbx_read16(struct map_info *map, unsigned long ofs)
-{
-	return readw(map->map_priv_1 + ofs);
-}
-
-__u32 mbx_read32(struct map_info *map, unsigned long ofs)
-{
-	return readl(map->map_priv_1 + ofs);
-}
-
-void mbx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
-}
-
-void mbx_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	writeb(d, map->map_priv_1 + adr);
-}
-
-void mbx_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	writew(d, map->map_priv_1 + adr);
-}
-
-void mbx_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	writel(d, map->map_priv_1 + adr);
-}
-
-void mbx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio((void *)(map->map_priv_1 + to), from, len);
-}
-
 struct map_info mbx_map = {
-	name: "MBX flash",
-	size: WINDOW_SIZE,
-	buswidth: 4,
-	read8: mbx_read8,
-	read16: mbx_read16,
-	read32: mbx_read32,
-	copy_from: mbx_copy_from,
-	write8: mbx_write8,
-	write16: mbx_write16,
-	write32: mbx_write32,
-	copy_to: mbx_copy_to
+	.name = "MBX flash",
+	.size = WINDOW_SIZE,
+	.phys = WINDOW_ADDR,
+	.buswidth = 4,
 };
 
 int __init init_mbx(void)
 {
-	printk(KERN_NOTICE "Motorola MBX flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR);
-	mbx_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
+	printk(KERN_NOTICE "Motorola MBX flash device: 0x%x at 0x%x\n", WINDOW_SIZE*4, WINDOW_ADDR);
+	mbx_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
 
-	if (!mbx_map.map_priv_1) {
+	if (!mbx_map.virt) {
 		printk("Failed to ioremap\n");
 		return -EIO;
 	}
+	simple_map_init(&mbx_map);
+
 	mymtd = do_map_probe("jedec_probe", &mbx_map);
 	if (mymtd) {
-		mymtd->module = THIS_MODULE;
+		mymtd->owner = THIS_MODULE;
 		add_mtd_device(mymtd);
                 add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS);
 		return 0;
 	}
 
-	iounmap((void *)mbx_map.map_priv_1);
+	iounmap((void *)mbx_map.virt);
 	return -ENXIO;
 }
 
@@ -130,9 +86,9 @@
 		del_mtd_device(mymtd);
 		map_destroy(mymtd);
 	}
-	if (mbx_map.map_priv_1) {
-		iounmap((void *)mbx_map.map_priv_1);
-		mbx_map.map_priv_1 = 0;
+	if (mbx_map.virt) {
+		iounmap((void *)mbx_map.virt);
+		mbx_map.virt = 0;
 	}
 }
 
diff -Nur linux-2.4.30/drivers/mtd/maps/mpc1211.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/mpc1211.c
--- linux-2.4.30/drivers/mtd/maps/mpc1211.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/mpc1211.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,79 @@
+/*
+ * Flash on MPC-1211
+ *
+ * (C) 2002 Interface, Saito.K & Jeanne
+ *
+ * GPL'd
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <linux/config.h>
+
+static struct mtd_info *flash_mtd;
+static struct mtd_partition *parsed_parts;
+
+struct map_info mpc1211_flash_map = {
+	.name		= "MPC-1211 FLASH",
+	.size		= 0x80000,
+	.buswidth	= 1,
+};
+
+static struct mtd_partition mpc1211_partitions[] = {
+	{
+		.name	= "IPL & ETH-BOOT",
+		.offset	= 0x00000000,
+		.size	= 0x10000,
+	},
+	{
+		.name	= "Flash FS",
+		.offset	= 0x00010000,
+		.size	= MTDPART_SIZ_FULL,
+	}
+};
+
+static int __init init_mpc1211_maps(void)
+{
+	int nr_parts;
+
+	mpc1211_flash_map.phys = 0;
+	mpc1211_flash_map.virt = P2SEGADDR(0);
+
+	simple_map_init(&mpc1211_flash_map);
+
+	printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n");
+	flash_mtd = do_map_probe("jedec_probe", &mpc1211_flash_map);
+	if (!flash_mtd) {
+		printk(KERN_NOTICE "Flash chips not detected at either possible location.\n");
+		return -ENXIO;
+	}
+	printk(KERN_NOTICE "MPC-1211: Flash at 0x%08lx\n", mpc1211_flash_map.virt & 0x1fffffff);
+	flash_mtd->module = THIS_MODULE;
+
+	parsed_parts = mpc1211_partitions;
+	nr_parts = ARRAY_SIZE(mpc1211_partitions);
+
+	add_mtd_partitions(flash_mtd, parsed_parts, nr_parts);
+	return 0;
+}
+
+static void __exit cleanup_mpc1211_maps(void)
+{
+	if (parsed_parts)
+		del_mtd_partitions(flash_mtd);
+	else
+		del_mtd_device(flash_mtd);
+	map_destroy(flash_mtd);
+}
+
+module_init(init_mpc1211_maps);
+module_exit(cleanup_mpc1211_maps);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Saito.K & Jeanne <ksaito@interface.co.jp>");
+MODULE_DESCRIPTION("MTD map driver for MPC-1211 boards. Interface");
diff -Nur linux-2.4.30/drivers/mtd/maps/mtx-1.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/mtx-1.c
--- linux-2.4.30/drivers/mtd/maps/mtx-1.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/mtx-1.c	2005-05-12 17:57:26.000000000 +0200
@@ -0,0 +1,181 @@
+/*
+ * Flash memory access on 4G Systems MTX-1 board
+ * 
+ * (C) 2003 Pete Popov <ppopov@mvista.com>
+ *	    Bruno Randolf <bruno.randolf@4g-systems.de>
+ */
+
+#include <linux/config.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
+
+#ifdef CONFIG_MIPS_MTX1
+#define WINDOW_ADDR 0x1E000000
+#define WINDOW_SIZE 0x2000000
+#endif
+
+__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);
+}
+
+
+
+static struct map_info mtx1_map = {
+	name:		"MTX-1 flash",
+	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,
+};
+
+
+static unsigned long flash_size = 0x01000000;
+static unsigned char flash_buswidth = 4;
+static struct mtd_partition mtx1_partitions[] = {
+        {
+                name: "user fs",
+                size: 0x1c00000,
+                offset: 0,
+        },{
+                name: "yamon",
+                size: 0x0100000,
+                offset: MTDPART_OFS_APPEND,
+                mask_flags: MTD_WRITEABLE
+        },{
+                name: "raw kernel",
+                size: 0x02c0000,
+                offset: MTDPART_OFS_APPEND,
+        },{
+                name: "yamon env vars",
+                size: 0x0040000,
+                offset: MTDPART_OFS_APPEND,
+                mask_flags: MTD_WRITEABLE
+        }
+};
+
+
+#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
+
+static struct mtd_partition *parsed_parts;
+static struct mtd_info *mymtd;
+
+int __init mtx1_mtd_init(void)
+{
+	struct mtd_partition *parts;
+	int nb_parts = 0;
+	char *part_type;
+	
+	/* Default flash buswidth */
+	mtx1_map.buswidth = flash_buswidth;
+
+	/*
+	 * Static partition definition selection
+	 */
+	part_type = "static";
+	parts = mtx1_partitions;
+	nb_parts = NB_OF(mtx1_partitions);
+	mtx1_map.size = flash_size;
+
+	/*
+	 * Now let's probe for the actual flash.  Do it here since
+	 * specific machine settings might have been set above.
+	 */
+	printk(KERN_NOTICE "MTX-1 flash: probing %d-bit flash bus\n",
+			mtx1_map.buswidth*8);
+	mtx1_map.map_priv_1 =
+		(unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+	mymtd = do_map_probe("cfi_probe", &mtx1_map);
+	if (!mymtd) return -ENXIO;
+	mymtd->module = THIS_MODULE;
+
+	add_mtd_partitions(mymtd, parts, nb_parts);
+	return 0;
+}
+
+static void __exit mtx1_mtd_cleanup(void)
+{
+	if (mymtd) {
+		del_mtd_partitions(mymtd);
+		map_destroy(mymtd);
+		if (parsed_parts)
+			kfree(parsed_parts);
+	}
+}
+
+module_init(mtx1_mtd_init);
+module_exit(mtx1_mtd_cleanup);
+
+MODULE_AUTHOR("Pete Popov");
+MODULE_DESCRIPTION("MTX-1 CFI map driver");
+MODULE_LICENSE("GPL");
diff -Nur linux-2.4.30/drivers/mtd/maps/netsc520.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/netsc520.c
--- linux-2.4.30/drivers/mtd/maps/netsc520.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/netsc520.c	2005-05-12 18:01:29.000000000 +0200
@@ -3,7 +3,7 @@
  * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com)
  *	based on sc520cdp.c by Sysgo Real-Time Solutions GmbH
  *
- * $Id: netsc520.c,v 1.5 2001/10/02 15:05:14 dwmw2 Exp $
+ * $Id: netsc520.c,v 1.9 2003/05/21 12:45:19 dwmw2 Exp $
  *
  * 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
@@ -27,6 +27,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -50,95 +51,41 @@
 ** recoverable afterwards.
 */
 
-static __u8 netsc520_read8(struct map_info *map, unsigned long ofs)
-{
-	return readb(map->map_priv_1 + ofs);
-}
-
-static __u16 netsc520_read16(struct map_info *map, unsigned long ofs)
-{
-	return readw(map->map_priv_1 + ofs);
-}
-
-static __u32 netsc520_read32(struct map_info *map, unsigned long ofs)
-{
-	return readl(map->map_priv_1 + ofs);
-}
-
-static void netsc520_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
-}
-
-static void netsc520_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	writeb(d, map->map_priv_1 + adr);
-}
-
-static void netsc520_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	writew(d, map->map_priv_1 + adr);
-}
-
-static void netsc520_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	writel(d, map->map_priv_1 + adr);
-}
-
-static void netsc520_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio((void *)(map->map_priv_1 + to), from, len);
-}
-
 /* partition_info gives details on the logical partitions that the split the 
  * single flash device into. If the size if zero we use up to the end of the
  * device. */
 static struct mtd_partition partition_info[]={
     { 
-	    name: "NetSc520 boot kernel", 
-	    offset: 0, 
-	    size: 	0xc0000
+	    .name = "NetSc520 boot kernel", 
+	    .offset = 0, 
+	    .size = 0xc0000
     },
     { 
-	    name: "NetSc520 Low BIOS", 
-	    offset: 0xc0000, 
-	    size: 	0x40000
+	    .name = "NetSc520 Low BIOS", 
+	    .offset = 0xc0000, 
+	    .size = 0x40000
     },
     { 
-	    name: "NetSc520 file system", 
-	    offset: 0x100000, 
-	    size: 	0xe80000
+	    .name = "NetSc520 file system", 
+	    .offset = 0x100000, 
+	    .size = 0xe80000
     },
     { 
-	    name: "NetSc520 High BIOS", 
-	    offset: 0xf80000, 
-	    size: 0x80000
+	    .name = "NetSc520 High BIOS", 
+	    .offset = 0xf80000, 
+	    .size = 0x80000
     },
 };
 #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
 
-/*
- * If no idea what is going on here.  This is taken from the FlashFX stuff.
- */
-#define ROMCS 1
-
-
 #define WINDOW_SIZE	0x00100000
 #define WINDOW_ADDR	0x00200000
 
 static struct map_info netsc520_map = {
-	name: "netsc520 Flash Bank",
-	size: WINDOW_SIZE,
-	buswidth: 4,
-	read8: netsc520_read8,
-	read16: netsc520_read16,
-	read32: netsc520_read32,
-	copy_from: netsc520_copy_from,
-	write8: netsc520_write8,
-	write16: netsc520_write16,
-	write32: netsc520_write32,
-	copy_to: netsc520_copy_to,
-	map_priv_2: WINDOW_ADDR
+	.name = "netsc520 Flash Bank",
+	.size = WINDOW_SIZE,
+	.buswidth = 4,
+	.phys = WINDOW_ADDR,
 };
 
 #define NUM_FLASH_BANKS	(sizeof(netsc520_map)/sizeof(struct map_info))
@@ -147,13 +94,16 @@
 
 static int __init init_netsc520(void)
 {
-	printk(KERN_NOTICE "NetSc520 flash device: %lx at %lx\n", netsc520_map.size, netsc520_map.map_priv_2);
-	netsc520_map.map_priv_1 = (unsigned long)ioremap_nocache(netsc520_map.map_priv_2, netsc520_map.size);
+	printk(KERN_NOTICE "NetSc520 flash device: 0x%lx at 0x%lx\n", netsc520_map.size, netsc520_map.phys);
+	netsc520_map.virt = (unsigned long)ioremap_nocache(netsc520_map.phys, netsc520_map.size);
 
-	if (!netsc520_map.map_priv_1) {
+	if (!netsc520_map.virt) {
 		printk("Failed to ioremap_nocache\n");
 		return -EIO;
 	}
+
+	simple_map_init(&netsc520_map);
+
 	mymtd = do_map_probe("cfi_probe", &netsc520_map);
 	if(!mymtd)
 		mymtd = do_map_probe("map_ram", &netsc520_map);
@@ -161,11 +111,11 @@
 		mymtd = do_map_probe("map_rom", &netsc520_map);
 
 	if (!mymtd) {
-		iounmap((void *)netsc520_map.map_priv_1);
+		iounmap((void *)netsc520_map.virt);
 		return -ENXIO;
 	}
 		
-	mymtd->module = THIS_MODULE;
+	mymtd->owner = THIS_MODULE;
 	add_mtd_partitions( mymtd, partition_info, NUM_PARTITIONS );
 	return 0;
 }
@@ -176,9 +126,9 @@
 		del_mtd_partitions(mymtd);
 		map_destroy(mymtd);
 	}
-	if (netsc520_map.map_priv_1) {
-		iounmap((void *)netsc520_map.map_priv_1);
-		netsc520_map.map_priv_1 = 0;
+	if (netsc520_map.virt) {
+		iounmap((void *)netsc520_map.virt);
+		netsc520_map.virt = 0;
 	}
 }
 
diff -Nur linux-2.4.30/drivers/mtd/maps/nettel.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/nettel.c
--- linux-2.4.30/drivers/mtd/maps/nettel.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/nettel.c	2005-05-12 18:01:29.000000000 +0200
@@ -6,7 +6,7 @@
  *      (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com)
  *      (C) Copyright 2001-2002, SnapGear (www.snapgear.com)
  *
- *	$Id: nettel.c,v 1.1 2002/08/08 06:30:13 gerg Exp $
+ *	$Id: nettel.c,v 1.4 2003/05/20 20:59:30 dwmw2 Exp $
  */
 
 /****************************************************************************/
@@ -59,128 +59,72 @@
 
 /****************************************************************************/
 
-static __u8 nettel_read8(struct map_info *map, unsigned long ofs)
-{
-	return(readb(map->map_priv_1 + ofs));
-}
-
-static __u16 nettel_read16(struct map_info *map, unsigned long ofs)
-{
-	return(readw(map->map_priv_1 + ofs));
-}
-
-static __u32 nettel_read32(struct map_info *map, unsigned long ofs)
-{
-	return(readl(map->map_priv_1 + ofs));
-}
-
-static void nettel_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-static void nettel_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	writeb(d, map->map_priv_1 + adr);
-}
-
-static void nettel_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	writew(d, map->map_priv_1 + adr);
-}
-
-static void nettel_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	writel(d, map->map_priv_1 + adr);
-}
-
-static void nettel_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio(map->map_priv_1 + to, from, len);
-}
-
 /****************************************************************************/
 
 #ifdef CONFIG_MTD_CFI_INTELEXT
 static struct map_info nettel_intel_map = {
-	name: "SnapGear Intel",
-	size: 0,
-	buswidth: INTEL_BUSWIDTH,
-	read8: nettel_read8,
-	read16: nettel_read16,
-	read32: nettel_read32,
-	copy_from: nettel_copy_from,
-	write8: nettel_write8,
-	write16: nettel_write16,
-	write32: nettel_write32,
-	copy_to: nettel_copy_to
+	.name = "SnapGear Intel",
+	.size = 0,
+	.buswidth = INTEL_BUSWIDTH,
 };
 
 static struct mtd_partition nettel_intel_partitions[] = {
 	{
-		name: "SnapGear kernel",
-		offset: 0,
-		size: 0x000e0000
+		.name = "SnapGear kernel",
+		.offset = 0,
+		.size = 0x000e0000
 	},
 	{
-		name: "SnapGear filesystem",
-		offset: 0x00100000,
+		.name = "SnapGear filesystem",
+		.offset = 0x00100000,
 	},
 	{
-		name: "SnapGear config",
-		offset: 0x000e0000,
-		size: 0x00020000
+		.name = "SnapGear config",
+		.offset = 0x000e0000,
+		.size = 0x00020000
 	},
 	{
-		name: "SnapGear Intel",
-		offset: 0
+		.name = "SnapGear Intel",
+		.offset = 0
 	},
 	{
-		name: "SnapGear BIOS Config",
-		offset: 0x007e0000,
-		size: 0x00020000
+		.name = "SnapGear BIOS Config",
+		.offset = 0x007e0000,
+		.size = 0x00020000
 	},
 	{
-		name: "SnapGear BIOS",
-		offset: 0x007e0000,
-		size: 0x00020000
+		.name = "SnapGear BIOS",
+		.offset = 0x007e0000,
+		.size = 0x00020000
 	},
 };
 #endif
 
 static struct map_info nettel_amd_map = {
-	name: "SnapGear AMD",
-	size: AMD_WINDOW_MAXSIZE,
-	buswidth: AMD_BUSWIDTH,
-	read8: nettel_read8,
-	read16: nettel_read16,
-	read32: nettel_read32,
-	copy_from: nettel_copy_from,
-	write8: nettel_write8,
-	write16: nettel_write16,
-	write32: nettel_write32,
-	copy_to: nettel_copy_to
+	.name = "SnapGear AMD",
+	.size = AMD_WINDOW_MAXSIZE,
+	.buswidth = AMD_BUSWIDTH,
 };
 
 static struct mtd_partition nettel_amd_partitions[] = {
 	{
-		name: "SnapGear BIOS config",
-		offset: 0x000e0000,
-		size: 0x00010000
+		.name = "SnapGear BIOS config",
+		.offset = 0x000e0000,
+		.size = 0x00010000
 	},
 	{
-		name: "SnapGear BIOS",
-		offset: 0x000f0000,
-		size: 0x00010000
+		.name = "SnapGear BIOS",
+		.offset = 0x000f0000,
+		.size = 0x00010000
 	},
 	{
-		name: "SnapGear AMD",
-		offset: 0
+		.name = "SnapGear AMD",
+		.offset = 0
 	},
 	{
-		name: "SnapGear high BIOS",
-		offset: 0x001f0000,
-		size: 0x00010000
+		.name = "SnapGear high BIOS",
+		.offset = 0x001f0000,
+		.size = 0x00010000
 	}
 };
 
@@ -328,18 +272,20 @@
 	*amdpar = SC520_PAR(SC520_PAR_BOOTCS, amdaddr, maxsize);
 	__asm__ ("wbinvd");
 
-	nettel_amd_map.map_priv_1 = (unsigned long)
+	nettel_amd_map.phys = amdaddr;
+	nettel_amd_map.virt = (unsigned long)
 		ioremap_nocache(amdaddr, maxsize);
-	if (!nettel_amd_map.map_priv_1) {
+	if (!nettel_amd_map.virt) {
 		printk("SNAPGEAR: failed to ioremap() BOOTCS\n");
 		return(-EIO);
 	}
+	simple_map_init(&nettel_amd_map);
 
 	if ((amd_mtd = do_map_probe("jedec_probe", &nettel_amd_map))) {
 		printk(KERN_NOTICE "SNAPGEAR: AMD flash device size = %dK\n",
 			amd_mtd->size>>10);
 
-		amd_mtd->module = THIS_MODULE;
+		amd_mtd->owner = THIS_MODULE;
 
 		/* The high BIOS partition is only present for 2MB units */
 		num_amd_partitions = NUM_AMD_PARTITIONS;
@@ -387,8 +333,8 @@
 
 		/* Destroy useless AMD MTD mapping */
 		amd_mtd = NULL;
-		iounmap((void *) nettel_amd_map.map_priv_1);
-		nettel_amd_map.map_priv_1 = (unsigned long) NULL;
+		iounmap((void *) nettel_amd_map.virt);
+		nettel_amd_map.virt = (unsigned long) NULL;
 #else
 		/* Only AMD flash supported */
 		return(-ENXIO);
@@ -411,16 +357,18 @@
 
 	/* Probe for the the size of the first Intel flash */
 	nettel_intel_map.size = maxsize;
-	nettel_intel_map.map_priv_1 = (unsigned long)
+	nettel_intel_map.phys = intel0addr;
+	nettel_intel_map.virt = (unsigned long)
 		ioremap_nocache(intel0addr, maxsize);
-	if (!nettel_intel_map.map_priv_1) {
+	if (!nettel_intel_map.virt) {
 		printk("SNAPGEAR: failed to ioremap() ROMCS1\n");
 		return(-EIO);
 	}
+	simple_map_init(&nettel_intel_map);
 
 	intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map);
 	if (! intel_mtd) {
-		iounmap((void *) nettel_intel_map.map_priv_1);
+		iounmap((void *) nettel_intel_map.virt);
 		return(-ENXIO);
 	}
 
@@ -441,19 +389,19 @@
 	/* Delete the old map and probe again to do both chips */
 	map_destroy(intel_mtd);
 	intel_mtd = NULL;
-	iounmap((void *) nettel_intel_map.map_priv_1);
+	iounmap((void *) nettel_intel_map.virt);
 
 	nettel_intel_map.size = maxsize;
-	nettel_intel_map.map_priv_1 = (unsigned long)
+	nettel_intel_map.virt = (unsigned long)
 		ioremap_nocache(intel0addr, maxsize);
-	if (!nettel_intel_map.map_priv_1) {
+	if (!nettel_intel_map.virt) {
 		printk("SNAPGEAR: failed to ioremap() ROMCS1/2\n");
 		return(-EIO);
 	}
 
 	intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map);
 	if (! intel_mtd) {
-		iounmap((void *) nettel_intel_map.map_priv_1);
+		iounmap((void *) nettel_intel_map.virt);
 		return(-ENXIO);
 	}
 
@@ -468,7 +416,7 @@
 	printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %dK\n",
 		(intel_mtd->size >> 10));
 
-	intel_mtd->module = THIS_MODULE;
+	intel_mtd->owner = THIS_MODULE;
 
 #ifndef CONFIG_BLK_DEV_INITRD
 	ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 1);
@@ -523,18 +471,18 @@
 		del_mtd_partitions(amd_mtd);
 		map_destroy(amd_mtd);
 	}
-	if (nettel_amd_map.map_priv_1) {
-		iounmap((void *)nettel_amd_map.map_priv_1);
-		nettel_amd_map.map_priv_1 = 0;
+	if (nettel_amd_map.virt) {
+		iounmap((void *)nettel_amd_map.virt);
+		nettel_amd_map.virt = 0;
 	}
 #ifdef CONFIG_MTD_CFI_INTELEXT
 	if (intel_mtd) {
 		del_mtd_partitions(intel_mtd);
 		map_destroy(intel_mtd);
 	}
-	if (nettel_intel_map.map_priv_1) {
-		iounmap((void *)nettel_intel_map.map_priv_1);
-		nettel_intel_map.map_priv_1 = 0;
+	if (nettel_intel_map.virt) {
+		iounmap((void *)nettel_intel_map.virt);
+		nettel_intel_map.virt = 0;
 	}
 #endif
 }
diff -Nur linux-2.4.30/drivers/mtd/maps/ocelot.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/ocelot.c
--- linux-2.4.30/drivers/mtd/maps/ocelot.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/ocelot.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: ocelot.c,v 1.6 2001/10/02 15:05:14 dwmw2 Exp $
+ * $Id: ocelot.c,v 1.12 2003/05/21 12:45:19 dwmw2 Exp $
  *
  * Flash on Momenco Ocelot
  */
@@ -7,6 +7,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -20,47 +21,23 @@
 #define NVRAM_WINDOW_SIZE 0x00007FF0
 #define NVRAM_BUSWIDTH 1
 
-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
-
 static unsigned int cacheflush = 0;
 
 static struct mtd_info *flash_mtd;
 static struct mtd_info *nvram_mtd;
 
-__u8 ocelot_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-void ocelot_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	cacheflush = 1;
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void ocelot_copy_from_cache(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	if (cacheflush) {
-		dma_cache_inv(map->map_priv_2, map->size);
-		cacheflush = 0;
-	}
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-void ocelot_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf)
 {
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
+        struct map_info *map = (struct map_info *)mtd->priv;
+	size_t done = 0;
 
-void ocelot_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
 	/* If we use memcpy, it does word-wide writes. Even though we told the 
 	   GT64120A that it's an 8-bit wide region, word-wide writes don't work.
 	   We end up just writing the first byte of the four to all four bytes.
 	   So we have this loop instead */
+	*retlen = len;
 	while(len) {
-		__raw_writeb(*(unsigned char *) from, map->map_priv_1 + to);
+		__raw_writeb(*(unsigned char *) from, map->virt + to);
 		from++;
 		to++;
 		len--;
@@ -70,24 +47,21 @@
 static struct mtd_partition *parsed_parts;
 
 struct map_info ocelot_flash_map = {
-	name: "Ocelot boot flash",
-	size: FLASH_WINDOW_SIZE,
-	buswidth: FLASH_BUSWIDTH,
-	read8: ocelot_read8,
-	copy_from: ocelot_copy_from_cache,
-	write8: ocelot_write8,
+	.name = "Ocelot boot flash",
+	.size = FLASH_WINDOW_SIZE,
+	.buswidth = FLASH_BUSWIDTH,
+	.phys = FLASH_WINDOW_ADDR,
 };
 
 struct map_info ocelot_nvram_map = {
-	name: "Ocelot NVRAM",
-	size: NVRAM_WINDOW_SIZE,
-	buswidth: NVRAM_BUSWIDTH,
-	read8: ocelot_read8,
-	copy_from: ocelot_copy_from,
-	write8: ocelot_write8,
-	copy_to: ocelot_copy_to
+	.name = "Ocelot NVRAM",
+	.size = NVRAM_WINDOW_SIZE,
+	.buswidth = NVRAM_BUSWIDTH,
+	.phys = NVRAM_WINDOW_ADDR,
 };
 
+static const char *probes[] = { "RedBoot", NULL };
+
 static int __init init_ocelot_maps(void)
 {
 	void *pld;
@@ -107,12 +81,13 @@
 	iounmap(pld);
 
 	/* Now ioremap the NVRAM space */
-	ocelot_nvram_map.map_priv_1 = (unsigned long)ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE);
-	if (!ocelot_nvram_map.map_priv_1) {
+	ocelot_nvram_map.virt = (unsigned long)ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE);
+	if (!ocelot_nvram_map.virt) {
 		printk(KERN_NOTICE "Failed to ioremap Ocelot NVRAM space\n");
 		return -EIO;
 	}
-	//	ocelot_nvram_map.map_priv_2 = ocelot_nvram_map.map_priv_1;
+
+	simple_map_init(&ocelot_nvram_map);
 
 	/* And do the RAM probe on it to get an MTD device */
 	nvram_mtd = do_map_probe("map_ram", &ocelot_nvram_map);
@@ -120,22 +95,21 @@
 		printk("NVRAM probe failed\n");
 		goto fail_1;
 	}
-	nvram_mtd->module = THIS_MODULE;
+	nvram_mtd->owner = THIS_MODULE;
 	nvram_mtd->erasesize = 16;
+	/* Override the write() method */
+	nvram_mtd->write = ocelot_ram_write;
 
 	/* Now map the flash space */
-	ocelot_flash_map.map_priv_1 = (unsigned long)ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE);
-	if (!ocelot_flash_map.map_priv_1) {
+	ocelot_flash_map.virt = (unsigned long)ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE);
+	if (!ocelot_flash_map.virt) {
 		printk(KERN_NOTICE "Failed to ioremap Ocelot flash space\n");
 		goto fail_2;
 	}
 	/* Now the cached version */
-	ocelot_flash_map.map_priv_2 = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0);
+	ocelot_flash_map.cached = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0);
 
-	if (!ocelot_flash_map.map_priv_2) {
-		/* Doesn't matter if it failed. Just use the uncached version */
-		ocelot_flash_map.map_priv_2 = ocelot_flash_map.map_priv_1;
-	}
+	simple_map_init(&ocelot_flash_map);
 
 	/* Only probe for flash if the write jumper is present */
 	if (brd_status & 0x40) {
@@ -155,10 +129,10 @@
 
 	add_mtd_device(nvram_mtd);
 
-	flash_mtd->module = THIS_MODULE;
-	nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts);
+	flash_mtd->owner = THIS_MODULE;
+	nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0);
 
-	if (nr_parts)
+	if (nr_parts > 0)
 		add_mtd_partitions(flash_mtd, parsed_parts, nr_parts);
 	else
 		add_mtd_device(flash_mtd);
@@ -166,14 +140,13 @@
 	return 0;
 	
  fail3:	
-	iounmap((void *)ocelot_flash_map.map_priv_1);
-	if (ocelot_flash_map.map_priv_2 &&
-	    ocelot_flash_map.map_priv_2 != ocelot_flash_map.map_priv_1)
-			iounmap((void *)ocelot_flash_map.map_priv_2);
+	iounmap((void *)ocelot_flash_map.virt);
+	if (ocelot_flash_map.cached)
+			iounmap((void *)ocelot_flash_map.cached);
  fail_2:
 	map_destroy(nvram_mtd);
  fail_1:
-	iounmap((void *)ocelot_nvram_map.map_priv_1);
+	iounmap((void *)ocelot_nvram_map.virt);
 
 	return -ENXIO;
 }
@@ -182,16 +155,16 @@
 {
 	del_mtd_device(nvram_mtd);
 	map_destroy(nvram_mtd);
-	iounmap((void *)ocelot_nvram_map.map_priv_1);
+	iounmap((void *)ocelot_nvram_map.virt);
 
 	if (parsed_parts)
 		del_mtd_partitions(flash_mtd);
 	else
 		del_mtd_device(flash_mtd);
 	map_destroy(flash_mtd);
-	iounmap((void *)ocelot_flash_map.map_priv_1);
-	if (ocelot_flash_map.map_priv_2 != ocelot_flash_map.map_priv_1)
-		iounmap((void *)ocelot_flash_map.map_priv_2);
+	iounmap((void *)ocelot_flash_map.virt);
+	if (ocelot_flash_map.cached)
+		iounmap((void *)ocelot_flash_map.cached);
 }
 
 module_init(init_ocelot_maps);
diff -Nur linux-2.4.30/drivers/mtd/maps/octagon-5066.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/octagon-5066.c
--- linux-2.4.30/drivers/mtd/maps/octagon-5066.c	2003-08-25 13:44:42.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/octagon-5066.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,4 +1,4 @@
-// $Id: octagon-5066.c,v 1.20 2003/01/07 17:21:55 dwmw2 Exp $
+// $Id: octagon-5066.c,v 1.24 2003/05/21 15:15:07 dwmw2 Exp $
 /* ######################################################################
 
    Octagon 5066 MTD Driver. 
@@ -31,6 +31,7 @@
 #include <asm/io.h>
 
 #include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
 
 #define WINDOW_START 0xe8000
 #define WINDOW_LENGTH 0x8000
@@ -151,32 +152,34 @@
 
 static struct map_info oct5066_map[2] = {
 	{
-		name: "Octagon 5066 Socket",
-		size: 512 * 1024,
-		buswidth: 1,
-		read8: oct5066_read8,
-		read16: oct5066_read16,
-		read32: oct5066_read32,
-		copy_from: oct5066_copy_from,
-		write8: oct5066_write8,
-		write16: oct5066_write16,
-		write32: oct5066_write32,
-		copy_to: oct5066_copy_to,
-		map_priv_1: 1<<6
+		.name = "Octagon 5066 Socket",
+		.phys = NO_XIP,
+		.size = 512 * 1024,
+		.buswidth = 1,
+		.read8 = oct5066_read8,
+		.read16 = oct5066_read16,
+		.read32 = oct5066_read32,
+		.copy_from = oct5066_copy_from,
+		.write8 = oct5066_write8,
+		.write16 = oct5066_write16,
+		.write32 = oct5066_write32,
+		.copy_to = oct5066_copy_to,
+		.map_priv_1 = 1<<6
 	},
 	{
-		name: "Octagon 5066 Internal Flash",
-		size: 2 * 1024 * 1024,
-		buswidth: 1,
-		read8: oct5066_read8,
-		read16: oct5066_read16,
-		read32: oct5066_read32,
-		copy_from: oct5066_copy_from,
-		write8: oct5066_write8,
-		write16: oct5066_write16,
-		write32: oct5066_write32,
-		copy_to: oct5066_copy_to,
-		map_priv_1: 2<<6
+		.name = "Octagon 5066 Internal Flash",
+		.phys = NO_XIP,
+		.size = 2 * 1024 * 1024,
+		.buswidth = 1,
+		.read8 = oct5066_read8,
+		.read16 = oct5066_read16,
+		.read32 = oct5066_read32,
+		.copy_from = oct5066_copy_from,
+		.write8 = oct5066_write8,
+		.write16 = oct5066_write16,
+		.write32 = oct5066_write32,
+		.copy_to = oct5066_copy_to,
+		.map_priv_1 = 2<<6
 	}
 };
 
@@ -244,6 +247,7 @@
 	}
 	if (OctProbe() != 0) {
 		printk(KERN_NOTICE "5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n");
+		iounmap((void *)iomapadr);
 		ret = -EAGAIN;
 		goto out_unmap;
 	}
@@ -261,7 +265,7 @@
 		if (!oct5066_mtd[i])
 			oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]);
 		if (oct5066_mtd[i]) {
-			oct5066_mtd[i]->module = THIS_MODULE;
+			oct5066_mtd[i]->owner = THIS_MODULE;
 			add_mtd_device(oct5066_mtd[i]);
 		}
 	}
diff -Nur linux-2.4.30/drivers/mtd/maps/omap-toto-flash.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/omap-toto-flash.c
--- linux-2.4.30/drivers/mtd/maps/omap-toto-flash.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/omap-toto-flash.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,137 @@
+/*
+ * NOR Flash memory access on TI Toto board
+ *
+ * jzhang@ti.com (C) 2003 Texas Instruments.
+ *
+ *  (C) 2002 MontVista Software, Inc.
+ *
+ * $Id: omap-toto-flash.c,v 1.1 2003/10/21 13:55:21 dwmw2 Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/errno.h>
+#include <linux/init.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/hardware.h>
+#include <asm/io.h>
+
+
+#ifndef CONFIG_ARCH_OMAP
+#error This is for OMAP architecture only
+#endif
+
+//these lines need be moved to a hardware header file
+#define OMAP_TOTO_FLASH_BASE 0xd8000000
+#define OMAP_TOTO_FLASH_SIZE 0x80000
+
+static struct map_info omap_toto_map_flash = {
+	.name =		"OMAP Toto flash",
+	.buswidth =	2,
+	.virt =		OMAP_TOTO_FLASH_BASE,
+};
+
+ 
+static struct mtd_partition toto_flash_partitions[] = {
+	{
+		.name =		"BootLoader",
+		.size =		0x00040000,     /* hopefully u-boot will stay 128k + 128*/
+		.offset =	0,
+		.mask_flags =	MTD_WRITEABLE,  /* force read-only */
+	}, {
+		.name =		"ReservedSpace",
+		.size =		0x00030000,
+		.offset =	MTDPART_OFS_APPEND,
+		//mask_flags:	MTD_WRITEABLE,  /* force read-only */
+	}, {
+		.name =		"EnvArea",      /* bottom 64KiB for env vars */
+		.size =		MTDPART_SIZ_FULL,
+		.offset =	MTDPART_OFS_APPEND,
+	} 
+};
+
+static struct mtd_partition *parsed_parts;
+
+static struct mtd_info *flash_mtd;
+ 
+static int __init init_flash (void)   
+{
+
+	struct mtd_partition *parts;
+	int nb_parts = 0;
+	int parsed_nr_parts = 0;
+	const char *part_type;
+ 
+	/*
+	 * Static partition definition selection
+	 */
+	part_type = "static";
+
+ 	parts = toto_flash_partitions;
+	nb_parts = ARRAY_SIZE(toto_flash_partitions);
+	omap_toto_map_flash.size = OMAP_TOTO_FLASH_SIZE;
+	omap_toto_map_flash.phys = virt_to_phys(OMAP_TOTO_FLASH_BASE);
+
+	simple_map_init(&omap_toto_map_flash);
+	/*
+	 * Now let's probe for the actual flash.  Do it here since
+	 * specific machine settings might have been set above.
+	 */
+	printk(KERN_NOTICE "OMAP toto flash: probing %d-bit flash bus\n",
+		omap_toto_map_flash.buswidth*8);
+	flash_mtd = do_map_probe("jedec_probe", &omap_toto_map_flash);
+	if (!flash_mtd)
+		return -ENXIO;
+ 
+ 	if (parsed_nr_parts > 0) {
+		parts = parsed_parts;
+		nb_parts = parsed_nr_parts;
+	}
+
+	if (nb_parts == 0) {
+		printk(KERN_NOTICE "OMAP toto flash: no partition info available,"
+			"registering whole flash at once\n");
+		if (add_mtd_device(flash_mtd)){
+            return -ENXIO;
+        }
+	} else {
+		printk(KERN_NOTICE "Using %s partition definition\n",
+			part_type);
+		return add_mtd_partitions(flash_mtd, parts, nb_parts);
+	}
+	return 0;
+}
+ 
+int __init omap_toto_mtd_init(void)  
+{
+	int status;
+
+ 	if (status = init_flash()) {
+		printk(KERN_ERR "OMAP Toto Flash: unable to init map for toto flash\n");
+	}
+    return status;
+}
+
+static void  __exit omap_toto_mtd_cleanup(void)  
+{
+	if (flash_mtd) {
+		del_mtd_partitions(flash_mtd);
+		map_destroy(flash_mtd);
+		if (parsed_parts)
+			kfree(parsed_parts);
+	}
+}
+
+module_init(omap_toto_mtd_init);
+module_exit(omap_toto_mtd_cleanup);
+
+MODULE_AUTHOR("Jian Zhang");
+MODULE_DESCRIPTION("OMAP Toto board map driver");
+MODULE_LICENSE("GPL");
diff -Nur linux-2.4.30/drivers/mtd/maps/pb1xxx-flash.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/pb1xxx-flash.c
--- linux-2.4.30/drivers/mtd/maps/pb1xxx-flash.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/pb1xxx-flash.c	1970-01-01 01:00:00.000000000 +0100
@@ -1,253 +0,0 @@
-/*
- * Flash memory access on Alchemy Pb1xxx boards
- * 
- * (C) 2001 Pete Popov <ppopov@mvista.com>
- * 
- * $Id: pb1xxx-flash.c,v 1.4 2002/09/13 13:51:54 dwmw2 Exp $
- */
-
-#include <linux/config.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
-
-#ifdef CONFIG_MIPS_PB1000
-#define WINDOW_ADDR 0x1F800000
-#define WINDOW_SIZE 0x800000
-#endif
-
-__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);
-}
-
-
-
-static struct map_info pb1xxx_map = {
-	name:		"Pb1xxx flash",
-	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,
-};
-
-
-#ifdef CONFIG_MIPS_PB1000
-
-static unsigned long flash_size = 0x00800000;
-static unsigned char flash_buswidth = 4;
-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)
-
-static unsigned char flash_buswidth = 4;
-#if defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER)
-/* both 32MiB banks will be used. Combine the first 32MiB bank and the
- * first 28MiB of the second bank together into a single jffs/jffs2
- * partition.
- */
-static unsigned long flash_size = 0x04000000;
-#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)
-static unsigned long flash_size = 0x02000000;
-#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)
-static unsigned long flash_size = 0x02000000;
-#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 NB_OF(x)  (sizeof(x)/sizeof(x[0]))
-
-static struct mtd_partition *parsed_parts;
-static struct mtd_info *mymtd;
-
-int __init pb1xxx_mtd_init(void)
-{
-	struct mtd_partition *parts;
-	int nb_parts = 0;
-	char *part_type;
-	
-	/* Default flash buswidth */
-	pb1xxx_map.buswidth = flash_buswidth;
-
-	/*
-	 * Static partition definition selection
-	 */
-	part_type = "static";
-	parts = pb1xxx_partitions;
-	nb_parts = NB_OF(pb1xxx_partitions);
-	pb1xxx_map.size = flash_size;
-
-	/*
-	 * 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", 
-			pb1xxx_map.buswidth*8);
-	pb1xxx_map.map_priv_1 = 
-		(unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
-	mymtd = do_map_probe("cfi_probe", &pb1xxx_map);
-	if (!mymtd) return -ENXIO;
-	mymtd->module = THIS_MODULE;
-
-	add_mtd_partitions(mymtd, parts, nb_parts);
-	return 0;
-}
-
-static void __exit pb1xxx_mtd_cleanup(void)
-{
-	if (mymtd) {
-		del_mtd_partitions(mymtd);
-		map_destroy(mymtd);
-		if (parsed_parts)
-			kfree(parsed_parts);
-	}
-}
-
-module_init(pb1xxx_mtd_init);
-module_exit(pb1xxx_mtd_cleanup);
-
-MODULE_AUTHOR("Pete Popov");
-MODULE_DESCRIPTION("Pb1xxx CFI map driver");
-MODULE_LICENSE("GPL");
diff -Nur linux-2.4.30/drivers/mtd/maps/pci.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/pci.c
--- linux-2.4.30/drivers/mtd/maps/pci.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/pci.c	2005-05-12 18:01:29.000000000 +0200
@@ -7,7 +7,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  *
- *  $Id: pci.c,v 1.2 2003/01/24 13:11:43 dwmw2 Exp $
+ *  $Id: pci.c,v 1.5 2003/05/20 20:59:31 dwmw2 Exp $
  * 
  * Generic PCI memory map driver.  We support the following boards:
  *  - Intel IQ80310 ATU.
@@ -98,10 +98,10 @@
 }
 
 static struct mtd_pci_info intel_iq80310_info = {
-	init:		intel_iq80310_init,
-	exit:		intel_iq80310_exit,
-	translate:	intel_iq80310_translate,
-	map_name:	"cfi_probe",
+	.init =		intel_iq80310_init,
+	.exit =		intel_iq80310_exit,
+	.translate =	intel_iq80310_translate,
+	.map_name =	"cfi_probe",
 };
 
 /*
@@ -181,10 +181,10 @@
 }
 
 static struct mtd_pci_info intel_dc21285_info = {
-	init:		intel_dc21285_init,
-	exit:		intel_dc21285_exit,
-	translate:	intel_dc21285_translate,
-	map_name:	"jedec_probe",
+	.init =		intel_dc21285_init,
+	.exit =		intel_dc21285_exit,
+	.translate =	intel_dc21285_translate,
+	.map_name =	"jedec_probe",
 };
 
 /*
@@ -193,22 +193,20 @@
 
 static struct pci_device_id mtd_pci_ids[] __devinitdata = {
 	{
-		vendor:		PCI_VENDOR_ID_INTEL,
-		device:		0x530d,
-		subvendor:	PCI_ANY_ID,
-		subdevice:	PCI_ANY_ID,
-		class:		PCI_CLASS_MEMORY_OTHER << 8,
-		class_mask:	0xffff00,
-		driver_data:	(unsigned long)&intel_iq80310_info,
+		.vendor =	PCI_VENDOR_ID_INTEL,
+		.device =	0x530d,
+		.subvendor =	PCI_ANY_ID,
+		.subdevice =	PCI_ANY_ID,
+		.class =	PCI_CLASS_MEMORY_OTHER << 8,
+		.class_mask =	0xffff00,
+		.driver_data =	(unsigned long)&intel_iq80310_info,
 	},
 	{
-		vendor:		PCI_VENDOR_ID_DEC,
-		device:		PCI_DEVICE_ID_DEC_21285,
-		subvendor:	0,	/* DC21285 defaults to 0 on reset */
-		subdevice:	0,	/* DC21285 defaults to 0 on reset */
-		class:		0,
-		class_mask:	0,
-		driver_data:	(unsigned long)&intel_dc21285_info,
+		.vendor =	PCI_VENDOR_ID_DEC,
+		.device =	PCI_DEVICE_ID_DEC_21285,
+		.subvendor =	0,	/* DC21285 defaults to 0 on reset */
+		.subdevice =	0,	/* DC21285 defaults to 0 on reset */
+		.driver_data =	(unsigned long)&intel_dc21285_info,
 	},
 	{ 0, }
 };
@@ -275,14 +273,15 @@
 }
 
 static struct map_info mtd_pci_map = {
-	read8:		mtd_pci_read8,
-	read16:		mtd_pci_read16,
-	read32:		mtd_pci_read32,
-	copy_from:	mtd_pci_copyfrom,
-	write8:		mtd_pci_write8,
-	write16:	mtd_pci_write16,
-	write32:	mtd_pci_write32,
-	copy_to:	mtd_pci_copyto,
+	.phys =		NO_XIP,
+	.read8 =	mtd_pci_read8,
+	.read16 =	mtd_pci_read16,
+	.read32 =	mtd_pci_read32,
+	.copy_from =	mtd_pci_copyfrom,
+	.write8 =	mtd_pci_write8,
+	.write16 =	mtd_pci_write16,
+	.write32 =	mtd_pci_write32,
+	.copy_to =	mtd_pci_copyto,
 };
 
 static int __devinit
@@ -322,7 +321,7 @@
 	if (!mtd)
 		goto release;
 
-	mtd->module = THIS_MODULE;
+	mtd->owner = THIS_MODULE;
 	add_mtd_device(mtd);
 
 	pci_set_drvdata(dev, mtd);
@@ -359,10 +358,10 @@
 }
 
 static struct pci_driver mtd_pci_driver = {
-	name:		"MTD PCI",
-	probe:		mtd_pci_probe,
-	remove:		__devexit_p(mtd_pci_remove),
-	id_table:	mtd_pci_ids,
+	.name =		"MTD PCI",
+	.probe =	mtd_pci_probe,
+	.remove =	__devexit_p(mtd_pci_remove),
+	.id_table =	mtd_pci_ids,
 };
 
 static int __init mtd_pci_maps_init(void)
diff -Nur linux-2.4.30/drivers/mtd/maps/pcmciamtd.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/pcmciamtd.c
--- linux-2.4.30/drivers/mtd/maps/pcmciamtd.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/pcmciamtd.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: pcmciamtd.c,v 1.39 2003/01/06 17:51:38 spse Exp $
+ * $Id: pcmciamtd.c,v 1.48 2003/06/24 07:14:38 spse Exp $
  *
  * pcmciamtd.c - MTD driver for PCMCIA flash memory cards
  *
@@ -14,6 +14,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 #include <linux/timer.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <asm/system.h>
 
@@ -24,6 +25,7 @@
 #include <pcmcia/ds.h>
 
 #include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
 
 #ifdef CONFIG_MTD_DEBUG
 static int debug = CONFIG_MTD_DEBUG_VERBOSE;
@@ -47,7 +49,7 @@
 
 
 #define DRIVER_DESC	"PCMCIA Flash memory card driver"
-#define DRIVER_VERSION	"$Revision: 1.39 $"
+#define DRIVER_VERSION	"$Revision: 1.48 $"
 
 /* Size of the PCMCIA address space: 26 bits = 64 MB */
 #define MAX_PCMCIA_ADDR	0x4000000
@@ -96,7 +98,7 @@
 MODULE_PARM(mem_speed, "i");
 MODULE_PARM_DESC(mem_speed, "Set memory access speed in ns");
 MODULE_PARM(force_size, "i");
-MODULE_PARM_DESC(force_size, "Force size of card in MB (1-64)");
+MODULE_PARM_DESC(force_size, "Force size of card in MiB (1-64)");
 MODULE_PARM(setvpp, "i");
 MODULE_PARM_DESC(setvpp, "Set Vpp (0=Never, 1=On writes, 2=Always on, default=0)");
 MODULE_PARM(vpp, "i");
@@ -106,11 +108,13 @@
 
 
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,69)
 static inline void cs_error(client_handle_t handle, int func, int ret)
 {
 	error_info_t err = { func, ret };
 	CardServices(ReportError, handle, &err);
 }
+#endif
 
 
 /* read/write{8,16} copy_{from,to} routines with window remapping to access whole card */
@@ -529,6 +533,7 @@
 
 	card_settings(dev, link, &new_name);
 
+	dev->pcmcia_map.phys = NO_XIP;
 	dev->pcmcia_map.read8 = pcmcia_read8_remap;
 	dev->pcmcia_map.read16 = pcmcia_read16_remap;
 	dev->pcmcia_map.copy_from = pcmcia_copy_from_remap;
@@ -539,7 +544,7 @@
 		dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp;
 
 	/* Request a memory window for PCMCIA. Some architeures can map windows upto the maximum
-	   that PCMCIA can support (64Mb) - this is ideal and we aim for a window the size of the
+	   that PCMCIA can support (64MiB) - this is ideal and we aim for a window the size of the
 	   whole card - otherwise we try smaller windows until we succeed */
 
 	req.Attributes =  WIN_MEMORY_TYPE_CM | WIN_ENABLE;
@@ -552,7 +557,7 @@
 
 	do {
 		int ret;
-		DEBUG(2, "requesting window with size = %dKB memspeed = %d",
+		DEBUG(2, "requesting window with size = %dKiB memspeed = %d",
 		      req.Size >> 10, req.AccessSpeed);
 		link->win = (window_handle_t)link->handle;
 		ret = CardServices(RequestWindow, &link->win, &req);
@@ -560,7 +565,7 @@
 		if(ret) {
 			req.Size >>= 1;
 		} else {
-			DEBUG(2, "Got window of size %dKB", req.Size >> 10);
+			DEBUG(2, "Got window of size %dKiB", req.Size >> 10);
 			dev->win_size = req.Size;
 			break;
 		}
@@ -573,7 +578,7 @@
 		pcmciamtd_release((u_long)link);
 		return;
 	}
-	DEBUG(1, "Allocated a window of %dKB", dev->win_size >> 10);
+	DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10);
 		
 	/* Get write protect status */
 	CS_CHECK(GetStatus, link->handle, &status);
@@ -642,21 +647,21 @@
 	}
 
 	dev->mtd_info = mtd;
-	mtd->module = THIS_MODULE;
+	mtd->owner = THIS_MODULE;
 
 	if(new_name) {
 		int size = 0;
 		char unit = ' ';
 		/* Since we are using a default name, make it better by adding in the
 		   size */
-		if(mtd->size < 1048576) { /* <1MB in size, show size in K */
+		if(mtd->size < 1048576) { /* <1MiB in size, show size in KiB */
 			size = mtd->size >> 10;
 			unit = 'K';
 		} else {
 			size = mtd->size >> 20;
 			unit = 'M';
 		}
-		snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%cB %s", size, unit, "PCMCIA Memory card");
+		snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%ciB %s", size, unit, "PCMCIA Memory card");
 	}
 
 	/* If the memory found is fits completely into the mapped PCMCIA window,
@@ -828,16 +833,20 @@
 }
 
 
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68)
+static struct pcmcia_driver pcmciamtd_driver = {
+	.drv		= {
+		.name	= "pcmciamtd"
+	},
+	.attach		= pcmciamtd_attach,
+	.detach		= pcmciamtd_detach,
+	.owner		= THIS_MODULE
+};
+#endif
+
+
 static int __init init_pcmciamtd(void)
 {
-	servinfo_t serv;
-
-	info(DRIVER_DESC " " DRIVER_VERSION);
-	CardServices(GetCardServicesInfo, &serv);
-	if (serv.Revision != CS_RELEASE_CODE) {
-		err("Card Services release does not match!");
-		return -1;
-	}
 
 	if(buswidth && buswidth != 1 && buswidth != 2) {
 		info("bad buswidth (%d), using default", buswidth);
@@ -851,15 +860,24 @@
 		info("bad mem_type (%d), using default", mem_type);
 		mem_type = 0;
 	}
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68)
+	return pcmcia_register_driver(&pcmciamtd_driver);
+#else
 	register_pccard_driver(&dev_info, &pcmciamtd_attach, &pcmciamtd_detach);
 	return 0;
+#endif
 }
 
 
 static void __exit exit_pcmciamtd(void)
 {
 	DEBUG(1, DRIVER_DESC " unloading");
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68)
+	pcmcia_unregister_driver(&pcmciamtd_driver);
+#else
 	unregister_pccard_driver(&dev_info);
+#endif
 
 	while(dev_list) {
 		dev_link_t *link = dev_list;
diff -Nur linux-2.4.30/drivers/mtd/maps/physmap.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/physmap.c
--- linux-2.4.30/drivers/mtd/maps/physmap.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/physmap.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,179 +1,114 @@
 /*
- * $Id: physmap.c,v 1.21 2002/09/05 05:12:54 acurtis Exp $
+ * $Id: physmap.c,v 1.30 2003/11/11 19:05:18 jsun Exp $
  *
  * Normal mappings of chips in physical memory
+ *
+ * Copyright (C) 2003 MontaVista Software Inc.
+ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
+ *
+ * 031022 - [jsun] add run-time configure and partition setup
  */
 
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/config.h>
-
-#ifdef CONFIG_MTD_PARTITIONS
 #include <linux/mtd/partitions.h>
-#endif
-
-#define WINDOW_ADDR CONFIG_MTD_PHYSMAP_START
-#define WINDOW_SIZE CONFIG_MTD_PHYSMAP_LEN
-#define BUSWIDTH CONFIG_MTD_PHYSMAP_BUSWIDTH
 
 static struct mtd_info *mymtd;
 
-__u8 physmap_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-__u16 physmap_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
+struct map_info physmap_map = {.name = "phys_mapped_flash"};
 
-__u32 physmap_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
-
-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
+#ifdef CONFIG_MTD_PARTITIONS
+static struct mtd_partition *mtd_parts;
+static int                   mtd_parts_nb;
 
-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
+static int num_physmap_partitions;
+static struct mtd_partition *physmap_partitions;
 
-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
+char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL};
 
-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+void physmap_set_partitions(struct mtd_partition *parts, int num_parts)
 {
-	memcpy_toio(map->map_priv_1 + to, from, len);
+	physmap_partitions=parts;
+	num_physmap_partitions=num_parts;
 }
-
-struct map_info physmap_map = {
-	name: "Physically mapped flash",
-	size: WINDOW_SIZE,
-	buswidth: BUSWIDTH,
-	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
-};
-
-#ifdef CONFIG_MTD_PARTITIONS
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-static struct mtd_partition *mtd_parts = 0;
-static int                   mtd_parts_nb = 0;
-#else
-static struct mtd_partition physmap_partitions[] = {
-/* Put your own partition definitions here */
-#if 0
-	{
-		name:		"bootROM",
-		size:		0x80000,
-		offset:		0,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
-	}, {
-		name:		"zImage",
-		size:		0x100000,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
-	}, {
-		name:		"ramdisk.gz",
-		size:		0x300000,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
-	}, {
-		name:		"User FS",
-		size:		MTDPART_SIZ_FULL,
-		offset:		MTDPART_OFS_APPEND,
-	}
-#endif
-};
-
-#define NUM_PARTITIONS	(sizeof(physmap_partitions)/sizeof(struct mtd_partition))
-
-#endif
-#endif
+#endif /* CONFIG_MTD_PARTITIONS */
 
 int __init init_physmap(void)
 {
 	static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", 0 };
 	const char **type;
 
-       	printk(KERN_NOTICE "physmap flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
-	physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+       	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);
 
-	if (!physmap_map.map_priv_1) {
+	if (!physmap_map.virt) {
 		printk("Failed to ioremap\n");
 		return -EIO;
 	}
-	
+
+	simple_map_init(&physmap_map);
+
 	mymtd = 0;
 	type = rom_probe_types;
 	for(; !mymtd && *type; type++) {
 		mymtd = do_map_probe(*type, &physmap_map);
 	}
 	if (mymtd) {
-		mymtd->module = THIS_MODULE;
+		mymtd->owner = THIS_MODULE;
 
-		add_mtd_device(mymtd);
 #ifdef CONFIG_MTD_PARTITIONS
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-		mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts, 
-							"phys");
+		mtd_parts_nb = parse_mtd_partitions(mymtd, part_probes, 
+						    &mtd_parts, 0);
+
 		if (mtd_parts_nb > 0)
 		{
-			printk(KERN_NOTICE 
-			       "Using command line partition definition\n");
 			add_mtd_partitions (mymtd, mtd_parts, mtd_parts_nb);
+			return 0;
 		}
-#else
-		if (NUM_PARTITIONS != 0) 
+
+		if (num_physmap_partitions != 0) 
 		{
 			printk(KERN_NOTICE 
 			       "Using physmap partition definition\n");
-			add_mtd_partitions (mymtd, physmap_partitions, NUM_PARTITIONS);
+			add_mtd_partitions (mymtd, physmap_partitions, num_physmap_partitions);
+			return 0;
 		}
 
 #endif
-#endif
+		add_mtd_device(mymtd);
+
 		return 0;
 	}
 
-	iounmap((void *)physmap_map.map_priv_1);
+	iounmap((void *)physmap_map.virt);
 	return -ENXIO;
 }
 
 static void __exit cleanup_physmap(void)
 {
-	if (mymtd) {
+#ifdef CONFIG_MTD_PARTITIONS
+	if (mtd_parts_nb) {
+		del_mtd_partitions(mymtd);
+		kfree(mtd_parts);
+	} else if (num_physmap_partitions) {
+		del_mtd_partitions(mymtd);
+	} else {
 		del_mtd_device(mymtd);
-		map_destroy(mymtd);
-	}
-	if (physmap_map.map_priv_1) {
-		iounmap((void *)physmap_map.map_priv_1);
-		physmap_map.map_priv_1 = 0;
 	}
+#else
+	del_mtd_device(mymtd);
+#endif
+	map_destroy(mymtd);
+
+	iounmap((void *)physmap_map.virt);
+	physmap_map.virt = 0;
 }
 
 module_init(init_physmap);
diff -Nur linux-2.4.30/drivers/mtd/maps/pnc2000.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/pnc2000.c
--- linux-2.4.30/drivers/mtd/maps/pnc2000.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/pnc2000.c	2005-05-12 18:01:29.000000000 +0200
@@ -5,12 +5,13 @@
  *
  * This code is GPL
  *
- * $Id: pnc2000.c,v 1.10 2001/10/02 15:05:14 dwmw2 Exp $
+ * $Id: pnc2000.c,v 1.14 2003/05/21 12:45:19 dwmw2 Exp $
  */
 
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -24,58 +25,13 @@
  * MAP DRIVER STUFF
  */
 
-__u8 pnc_read8(struct map_info *map, unsigned long ofs)
-{
-  return *(__u8 *)(WINDOW_ADDR + ofs);
-}
-
-__u16 pnc_read16(struct map_info *map, unsigned long ofs)
-{
-  return *(__u16 *)(WINDOW_ADDR + ofs);
-}
-
-__u32 pnc_read32(struct map_info *map, unsigned long ofs)
-{
-  return *(volatile unsigned int *)(WINDOW_ADDR + ofs);
-}
-
-void pnc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-  memcpy(to, (void *)(WINDOW_ADDR + from), len);
-}
-
-void pnc_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-  *(__u8 *)(WINDOW_ADDR + adr) = d;
-}
-
-void pnc_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-  *(__u16 *)(WINDOW_ADDR + adr) = d;
-}
-
-void pnc_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-  *(__u32 *)(WINDOW_ADDR + adr) = d;
-}
-
-void pnc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-  memcpy((void *)(WINDOW_ADDR + to), from, len);
-}
 
 struct map_info pnc_map = {
-	name: "PNC-2000",
-	size: WINDOW_SIZE,
-	buswidth: 4,
-	read8: pnc_read8,
-	read16: pnc_read16,
-	read32: pnc_read32,
-	copy_from: pnc_copy_from,
-	write8: pnc_write8,
-	write16: pnc_write16,
-	write32: pnc_write32,
-	copy_to: pnc_copy_to
+	.name = "PNC-2000",
+	.size = WINDOW_SIZE,
+	.buswidth = 4,
+	.phys = 0xFFFFFFFF,
+	.virt = WINDOW_ADDR,
 };
 
 
@@ -84,19 +40,19 @@
  */
 static struct mtd_partition pnc_partitions[3] = {
 	{
-		name: "PNC-2000 boot firmware",
-		size: 0x20000,
-		offset: 0
+		.name = "PNC-2000 boot firmware",
+		.size = 0x20000,
+		.offset = 0
 	},
 	{
-		name: "PNC-2000 kernel",
-		size: 0x1a0000,
-		offset: 0x20000
+		.name = "PNC-2000 kernel",
+		.size = 0x1a0000,
+		.offset = 0x20000
 	},
 	{
-		name: "PNC-2000 filesystem",
-		size: 0x240000,
-		offset: 0x1c0000
+		.name = "PNC-2000 filesystem",
+		.size = 0x240000,
+		.offset = 0x1c0000
 	}
 };
 
@@ -110,9 +66,11 @@
 {
 	printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR);
 
+	simple_map_init(&pnc_map);
+
 	mymtd = do_map_probe("cfi_probe", &pnc_map);
 	if (mymtd) {
-		mymtd->module = THIS_MODULE;
+		mymtd->owner = THIS_MODULE;
 		return add_mtd_partitions(mymtd, pnc_partitions, 3);
 	}
 
diff -Nur linux-2.4.30/drivers/mtd/maps/redwood.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/redwood.c
--- linux-2.4.30/drivers/mtd/maps/redwood.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/redwood.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,38 +1,23 @@
 /*
- * $Id:
+ * $Id: redwood.c,v 1.7 2003/11/21 23:09:52 trini Exp $
  *
- * redwood.c - mapper for IBM Redwood-4/5 board.
- *	
- * Copyright 2001 MontaVista Softare Inc. 
- *  
- * 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.
+ * drivers/mtd/maps/redwood.c
  *
- *  History: 12/17/2001 - Armin
- *  		migrated to use do_map_probe
+ * FLASH map for the IBM Redwood 4/5/6 boards.
  *
+ * Author: MontaVista Software, Inc. <source@mvista.com>
+ *
+ * 2001-2003 (c) MontaVista, Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
  */
 
+#include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -40,134 +25,146 @@
 
 #include <asm/io.h>
 
+#if !defined (CONFIG_REDWOOD_6)
+
 #define WINDOW_ADDR 0xffc00000
 #define WINDOW_SIZE 0x00400000
 
-__u8 redwood_flash_read8(struct map_info *map, unsigned long ofs)
-{
-        return *(__u8 *)(map->map_priv_1 + ofs);
-}
-
-__u16 redwood_flash_read16(struct map_info *map, unsigned long ofs)
-{
-        return *(__u16 *)(map->map_priv_1 + ofs);
-}
-
-__u32 redwood_flash_read32(struct map_info *map, unsigned long ofs)
-{
-        return *(volatile unsigned int *)(map->map_priv_1 + ofs);
-}
+#define RW_PART0_OF	0
+#define RW_PART0_SZ	0x10000
+#define RW_PART1_OF	RW_PART0_SZ
+#define RW_PART1_SZ	0x200000 - 0x10000
+#define RW_PART2_OF	0x200000
+#define RW_PART2_SZ	0x10000
+#define RW_PART3_OF	0x210000
+#define RW_PART3_SZ	0x200000 - (0x10000 + 0x20000)
+#define RW_PART4_OF	0x3e0000
+#define RW_PART4_SZ	0x20000
 
-void redwood_flash_copy_from(struct map_info *map, void *to,
-                             unsigned long from, ssize_t len)
-{
-        memcpy(to, (void *)(map->map_priv_1 + from), len);
-}
-
-void redwood_flash_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-        *(__u8 *)(map->map_priv_1 + adr) = d;
-}
+static struct mtd_partition redwood_flash_partitions[] = {
+	{
+		.name = "Redwood OpenBIOS Vital Product Data",
+		.offset = RW_PART0_OF,
+		.size = RW_PART0_SZ,
+		.mask_flags = MTD_WRITEABLE	/* force read-only */
+	},
+	{
+		.name = "Redwood kernel",
+		.offset = RW_PART1_OF,
+		.size = RW_PART1_SZ
+	},
+	{
+		.name = "Redwood OpenBIOS non-volatile storage",
+		.offset = RW_PART2_OF,
+		.size = RW_PART2_SZ,
+		.mask_flags = MTD_WRITEABLE	/* force read-only */
+	},
+	{
+		.name = "Redwood filesystem",
+		.offset = RW_PART3_OF,
+		.size = RW_PART3_SZ
+	},
+	{
+		.name = "Redwood OpenBIOS",
+		.offset = RW_PART4_OF,
+		.size = RW_PART4_SZ,
+		.mask_flags = MTD_WRITEABLE	/* force read-only */
+	}
+};
 
-void redwood_flash_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-        *(__u16 *)(map->map_priv_1 + adr) = d;
-}
+#else /* CONFIG_REDWOOD_6 */
+/* FIXME: the window is bigger - armin */
+#define WINDOW_ADDR 0xff800000
+#define WINDOW_SIZE 0x00800000
+
+#define RW_PART0_OF	0
+#define RW_PART0_SZ	0x400000	/* 4 MiB data */
+#define RW_PART1_OF	RW_PART0_OF + RW_PART0_SZ 
+#define RW_PART1_SZ	0x10000		/* 64K VPD */
+#define RW_PART2_OF	RW_PART1_OF + RW_PART1_SZ
+#define RW_PART2_SZ	0x400000 - (0x10000 + 0x20000)
+#define RW_PART3_OF	RW_PART2_OF + RW_PART2_SZ
+#define RW_PART3_SZ	0x20000
 
-void redwood_flash_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-        *(__u32 *)(map->map_priv_1 + adr) = d;
-}
+static struct mtd_partition redwood_flash_partitions[] = {
+	{
+		.name = "Redwood filesystem",
+		.offset = RW_PART0_OF,
+		.size = RW_PART0_SZ
+	},
+	{
+		.name = "Redwood OpenBIOS Vital Product Data",
+		.offset = RW_PART1_OF,
+		.size = RW_PART1_SZ,
+		.mask_flags = MTD_WRITEABLE	/* force read-only */
+	},
+	{
+		.name = "Redwood kernel",
+		.offset = RW_PART2_OF,
+		.size = RW_PART2_SZ
+	},
+	{
+		.name = "Redwood OpenBIOS",
+		.offset = RW_PART3_OF,
+		.size = RW_PART3_SZ,
+		.mask_flags = MTD_WRITEABLE	/* force read-only */
+	}
+};
 
-void redwood_flash_copy_to(struct map_info *map, unsigned long to,
-                           const void *from, ssize_t len)
-{
-        memcpy((void *)(map->map_priv_1 + to), from, len);
-}
+#endif /* CONFIG_REDWOOD_6 */
 
 struct map_info redwood_flash_map = {
-        name: "IBM Redwood",
-        size: WINDOW_SIZE,
-        buswidth: 2,
-        read8: redwood_flash_read8,
-        read16: redwood_flash_read16,
-        read32: redwood_flash_read32,
-        copy_from: redwood_flash_copy_from,
-        write8: redwood_flash_write8,
-        write16: redwood_flash_write16,
-        write32: redwood_flash_write32,
-        copy_to: redwood_flash_copy_to
+	.name = "IBM Redwood",
+	.size = WINDOW_SIZE,
+	.buswidth = 2,
+	.phys = WINDOW_ADDR,
 };
 
 
-static struct mtd_partition redwood_flash_partitions[] = {
-        {
-                name: "Redwood OpenBIOS Vital Product Data",
-                offset: 0,
-                size:   0x10000,
-                mask_flags: MTD_WRITEABLE       /* force read-only */
-        },
-        {
-                name: "Redwood kernel",
-                offset: 0x10000,
-                size:   0x200000 - 0x10000
-        },
-        {
-                name: "Redwood OpenBIOS non-volatile storage",
-                offset: 0x200000,
-                size:   0x10000,
-                mask_flags: MTD_WRITEABLE       /* force read-only */
-        },
-        {
-                name: "Redwood filesystem",
-                offset: 0x210000,
-                size:   0x200000 - (0x10000 + 0x20000)
-        },
-        {
-                name: "Redwood OpenBIOS",
-                offset: 0x3e0000,
-                size:   0x20000,
-                mask_flags: MTD_WRITEABLE       /* force read-only */
-        }
-};
 #define NUM_REDWOOD_FLASH_PARTITIONS \
-        (sizeof(redwood_flash_partitions)/sizeof(redwood_flash_partitions[0]))
+	(sizeof(redwood_flash_partitions)/sizeof(redwood_flash_partitions[0]))
 
 static struct mtd_info *redwood_mtd;
 
 int __init init_redwood_flash(void)
 {
-        printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n",
-               WINDOW_SIZE, WINDOW_ADDR);
+	printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n",
+			WINDOW_SIZE, WINDOW_ADDR);
 
-        redwood_flash_map.map_priv_1 =
-                (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+	redwood_flash_map.virt =
+		(unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
 
-        if (!redwood_flash_map.map_priv_1) {
-                printk("init_redwood_flash: failed to ioremap\n");
-                return -EIO;
-        }
-
-        redwood_mtd = do_map_probe("cfi_probe",&redwood_flash_map);
-
-        if (redwood_mtd) {
-                redwood_mtd->module = THIS_MODULE;
-                return add_mtd_partitions(redwood_mtd,
-                                          redwood_flash_partitions,
-                                          NUM_REDWOOD_FLASH_PARTITIONS);
-        }
+	if (!redwood_flash_map.virt) {
+		printk("init_redwood_flash: failed to ioremap\n");
+		return -EIO;
+	}
+	simple_map_init(&redwood_flash_map);
+
+	redwood_mtd = do_map_probe("cfi_probe",&redwood_flash_map);
+
+	if (redwood_mtd) {
+		redwood_mtd->owner = THIS_MODULE;
+		return add_mtd_partitions(redwood_mtd,
+				redwood_flash_partitions,
+				NUM_REDWOOD_FLASH_PARTITIONS);
+	}
 
-        return -ENXIO;
+	return -ENXIO;
 }
 
 static void __exit cleanup_redwood_flash(void)
 {
-        if (redwood_mtd) {
-                del_mtd_partitions(redwood_mtd);
-                iounmap((void *)redwood_flash_map.map_priv_1);
-                map_destroy(redwood_mtd);
-        }
+	if (redwood_mtd) {
+		del_mtd_partitions(redwood_mtd);
+		/* moved iounmap after map_destroy - armin */
+		map_destroy(redwood_mtd);
+		iounmap((void *)redwood_flash_map.virt);
+	}
 }
 
 module_init(init_redwood_flash);
 module_exit(cleanup_redwood_flash);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("MontaVista Software <source@mvista.com>");
+MODULE_DESCRIPTION("MTD map driver for the IBM Redwood reference boards");
diff -Nur linux-2.4.30/drivers/mtd/maps/rpxlite.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/rpxlite.c
--- linux-2.4.30/drivers/mtd/maps/rpxlite.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/rpxlite.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp $
+ * $Id: rpxlite.c,v 1.19 2003/05/21 12:45:19 dwmw2 Exp $
  *
  * Handle mapping of the flash on the RPX Lite and CLLF boards
  */
@@ -7,6 +7,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -17,80 +18,31 @@
 
 static struct mtd_info *mymtd;
 
-__u8 rpxlite_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-__u16 rpxlite_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
-
-__u32 rpxlite_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
-
-void rpxlite_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
-}
-
-void rpxlite_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void rpxlite_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio((void *)(map->map_priv_1 + to), from, len);
-}
-
-struct map_info rpxlite_map = {
-	name: "RPX",
-	size: WINDOW_SIZE,
-	buswidth: 4,
-	read8: rpxlite_read8,
-	read16: rpxlite_read16,
-	read32: rpxlite_read32,
-	copy_from: rpxlite_copy_from,
-	write8: rpxlite_write8,
-	write16: rpxlite_write16,
-	write32: rpxlite_write32,
-	copy_to: rpxlite_copy_to
+static struct map_info rpxlite_map = {
+	.name = "RPX",
+	.size = WINDOW_SIZE,
+	.buswidth = 4,
+	.phys = WINDOW_ADDR,
 };
 
 int __init init_rpxlite(void)
 {
 	printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR);
-	rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
+	rpxlite_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4);
 
-	if (!rpxlite_map.map_priv_1) {
+	if (!rpxlite_map.virt) {
 		printk("Failed to ioremap\n");
 		return -EIO;
 	}
+	simple_map_init(&rpxlite_map);
 	mymtd = do_map_probe("cfi_probe", &rpxlite_map);
 	if (mymtd) {
-		mymtd->module = THIS_MODULE;
+		mymtd->owner = THIS_MODULE;
 		add_mtd_device(mymtd);
 		return 0;
 	}
 
-	iounmap((void *)rpxlite_map.map_priv_1);
+	iounmap((void *)rpxlite_map.virt);
 	return -ENXIO;
 }
 
@@ -100,9 +52,9 @@
 		del_mtd_device(mymtd);
 		map_destroy(mymtd);
 	}
-	if (rpxlite_map.map_priv_1) {
-		iounmap((void *)rpxlite_map.map_priv_1);
-		rpxlite_map.map_priv_1 = 0;
+	if (rpxlite_map.virt) {
+		iounmap((void *)rpxlite_map.virt);
+		rpxlite_map.virt = 0;
 	}
 }
 
diff -Nur linux-2.4.30/drivers/mtd/maps/sa1100-flash.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/sa1100-flash.c
--- linux-2.4.30/drivers/mtd/maps/sa1100-flash.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/sa1100-flash.c	2005-05-12 18:01:29.000000000 +0200
@@ -3,7 +3,7 @@
  * 
  * (C) 2000 Nicolas Pitre <nico@cam.org>
  * 
- * $Id: sa1100-flash.c,v 1.29 2002/09/06 14:36:19 abz Exp $
+ * $Id: sa1100-flash.c,v 1.38 2004/01/22 19:11:08 rmk Exp $
  */
 
 #include <linux/config.h>
@@ -11,278 +11,212 @@
 #include <linux/types.h>
 #include <linux/ioport.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
+#include <linux/mtd/concat.h>
 
 #include <asm/hardware.h>
+#include <asm/mach-types.h>
 #include <asm/io.h>
+#include <asm/sizes.h>
 
+#include <asm/arch/h3600.h>
 
 #ifndef CONFIG_ARCH_SA1100
 #error This is for SA1100 architecture only
 #endif
 
+/*
+ * This isnt complete yet, so...
+ */
+#define CONFIG_MTD_SA1100_STATICMAP 1
 
-#define WINDOW_ADDR 0xe8000000
-
-static __u8 sa1100_read8(struct map_info *map, unsigned long ofs)
-{
-	return readb(map->map_priv_1 + ofs);
-}
-
-static __u16 sa1100_read16(struct map_info *map, unsigned long ofs)
-{
-	return readw(map->map_priv_1 + ofs);
-}
-
-static __u32 sa1100_read32(struct map_info *map, unsigned long ofs)
-{
-	return readl(map->map_priv_1 + ofs);
-}
-
-static void sa1100_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy(to, (void *)(map->map_priv_1 + from), len);
-}
-
-static void sa1100_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	writeb(d, map->map_priv_1 + adr);
-}
-
-static void sa1100_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	writew(d, map->map_priv_1 + adr);
-}
-
-static void sa1100_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	writel(d, map->map_priv_1 + adr);
-}
-
-static void sa1100_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy((void *)(map->map_priv_1 + to), from, len);
-}
-
-static struct map_info sa1100_map = {
-	name:		"SA1100 flash",
-	read8:		sa1100_read8,
-	read16:		sa1100_read16,
-	read32:		sa1100_read32,
-	copy_from:	sa1100_copy_from,
-	write8:		sa1100_write8,
-	write16:	sa1100_write16,
-	write32:	sa1100_write32,
-	copy_to:	sa1100_copy_to,
-
-	map_priv_1:	WINDOW_ADDR,
-	map_priv_2:	-1,
-};
-
-
+#ifdef CONFIG_MTD_SA1100_STATICMAP
 /*
  * Here are partition information for all known SA1100-based devices.
  * See include/linux/mtd/partitions.h for definition of the mtd_partition
  * structure.
  *
- * The *_max_flash_size is the maximum possible mapped flash size which
- * is not necessarily the actual flash size.  It must be no more than
- * the value specified in the "struct map_desc *_io_desc" mapping
- * definition for the corresponding machine.
+ * Please note:
+ *  1. We no longer support static flash mappings via the machine io_desc
+ *     structure.
+ *  2. The flash size given should be the largest flash size that can
+ *     be accommodated.
+ *
+ * The MTD layer will detect flash chip aliasing and reduce the size of
+ * the map accordingly.
  *
  * Please keep these in alphabetical order, and formatted as per existing
  * entries.  Thanks.
  */
 
 #ifdef CONFIG_SA1100_ADSBITSY
-#define ADSBITSY_FLASH_SIZE		0x02000000
 static struct mtd_partition adsbitsy_partitions[] = {
 	{
-		name:		"bootROM",
-		size:		0x80000,
-		offset:		0,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
-	}, {
-		name:		"zImage",
-		size:		0x100000,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
-	}, {
-		name:		"ramdisk.gz",
-		size:		0x300000,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
-	}, {
-		name:		"User FS",
-		size:		MTDPART_SIZ_FULL,
-		offset:		MTDPART_OFS_APPEND,
+		.name		= "bootROM",
+		.size		= 0x80000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
+	}, {
+		.name		= "zImage",
+		.size		= 0x100000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
+	}, {
+		.name		= "ramdisk.gz",
+		.size		= 0x300000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
+	}, {
+		.name		= "User FS",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
 	}
 };
 #endif
 
 #ifdef CONFIG_SA1100_ASSABET
 /* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */
-#define ASSABET4_FLASH_SIZE		0x00400000
 static struct mtd_partition assabet4_partitions[] = {
 	{
-		name:		"bootloader",
-		size:		0x00020000,
-		offset:		0,
-		mask_flags:	MTD_WRITEABLE,
-	}, {
-		name:		"bootloader params",
-		size:		0x00020000,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,
-	}, {
-		name:		"jffs",
-		size:		MTDPART_SIZ_FULL,
-		offset:		MTDPART_OFS_APPEND,
+		.name		= "bootloader",
+		.size		= 0x00020000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "bootloader params",
+		.size		= 0x00020000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "jffs",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
 	}
 };
 
 /* Phase 5 Assabet has two 28F128J3A flash parts in bank 0: */
-#define ASSABET5_FLASH_SIZE		0x02000000
 static struct mtd_partition assabet5_partitions[] = {
 	{
-		name:		"bootloader",
-		size:		0x00040000,
-		offset:		0,
-		mask_flags:	MTD_WRITEABLE,
-	}, {
-		name:		"bootloader params",
-		size:		0x00040000,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,
-	}, {
-		name:		"jffs",
-		size:		MTDPART_SIZ_FULL,
-		offset:		MTDPART_OFS_APPEND,
+		.name		= "bootloader",
+		.size		= 0x00040000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "bootloader params",
+		.size		= 0x00040000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "jffs",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
 	}
 };
 
-#define ASSABET_FLASH_SIZE	ASSABET5_FLASH_SIZE
 #define assabet_partitions	assabet5_partitions
 #endif
 
 #ifdef CONFIG_SA1100_BADGE4
-
 /*
- * 1 x Intel 28F320C3BA100 Advanced+ Boot Block Flash (32 Mi bit)
+ * 1 x Intel 28F320C3 Advanced+ Boot Block Flash (32 Mi bit)
  *   Eight 4 KiW Parameter Bottom Blocks (64 KiB)
  *   Sixty-three 32 KiW Main Blocks (4032 Ki b)
+ *
+ * <or>
+ *
+ * 1 x Intel 28F640C3 Advanced+ Boot Block Flash (64 Mi bit)
+ *   Eight 4 KiW Parameter Bottom Blocks (64 KiB)
+ *   One-hundred-twenty-seven 32 KiW Main Blocks (8128 Ki b)
  */
-#define BADGE4_FLASH_SIZE		0x00400000
 static struct mtd_partition badge4_partitions[] = {
 	{
-		name:		"BLOB boot loader",
-		offset:		0,
-		size:		0x0000A000
-	}, {
-		name:		"params",
-		offset:		MTDPART_OFS_APPEND,
-		size:		0x00006000
-	}, {
-		name:		"kernel",
-		offset:		MTDPART_OFS_APPEND,
-		size:		0x00100000
-	}, {
-		name:		"root",
-		offset:		MTDPART_OFS_APPEND,
-		size:		MTDPART_SIZ_FULL
+		.name		= "BLOB boot loader",
+		.offset		= 0,
+		.size		= 0x0000A000
+	}, {
+		.name		= "params",
+		.offset		= MTDPART_OFS_APPEND,
+		.size		= 0x00006000
+	}, {
+		.name		= "root",
+		.offset		= MTDPART_OFS_APPEND,
+		.size		= MTDPART_SIZ_FULL
 	}
 };
-
 #endif
 
 
 #ifdef CONFIG_SA1100_CERF
 #ifdef CONFIG_SA1100_CERF_FLASH_32MB
-#define CERF_FLASH_SIZE			0x02000000
-static struct mtd_partition cerf_partitions[] = {
-	{
-		name:		"firmware",
-		size:		0x00040000,
-		offset:		0,
-	}, {
-		name:		"params",
-		size:		0x00040000,
-		offset:		0x00040000,
-	}, {
-		name:		"kernel",
-		size:		0x00100000,
-		offset:		0x00080000,
-	}, {
-		name:		"rootdisk",
-		size:		0x01E80000,
-		offset:		0x00180000,
-	}
-};
+#  define CERF_FLASH_SIZE	0x02000000
 #elif defined CONFIG_SA1100_CERF_FLASH_16MB
-#define CERF_FLASH_SIZE			0x01000000
+#  define CERF_FLASH_SIZE	0x01000000
+#elif defined CONFIG_SA1100_CERF_FLASH_8MB
+#  define CERF_FLASH_SIZE	0x00800000
+#else
+#  error "Undefined flash size for CERF in sa1100-flash.c"
+#endif
+
 static struct mtd_partition cerf_partitions[] = {
 	{
-		name:		"firmware",
-		size:		0x00020000,
-		offset:		0,
-	}, {
-		name:		"params",
-		size:		0x00020000,
-		offset:		0x00020000,
-	}, {
-		name:		"kernel",
-		size:		0x00100000,
-		offset:		0x00040000,
-	}, {
-		name:		"rootdisk",
-		size:		0x00EC0000,
-		offset:		0x00140000,
+		.name		= "Bootloader",
+		.size		= 0x00020000,
+		.offset		= 0x00000000,
+	}, {
+		.name		= "Params",
+		.size		= 0x00040000,
+		.offset		= 0x00020000,
+	}, {
+		.name		= "Kernel",
+		.size		= 0x00100000,
+		.offset		= 0x00060000,
+	}, {
+		.name		= "Filesystem",
+		.size		= CERF_FLASH_SIZE-0x00160000,
+		.offset		= 0x00160000,
 	}
 };
-#elif defined CONFIG_SA1100_CERF_FLASH_8MB
-#   error "Unwritten type definition"
-#else
-#   error "Undefined memory orientation for CERF in sa1100-flash.c"
-#endif
 #endif
 
 #ifdef CONFIG_SA1100_CONSUS
-#define CONSUS_FLASH_SIZE		0x02000000
 static struct mtd_partition consus_partitions[] = {
 	{
-		name:		"Consus boot firmware",
-		offset:		0,
-		size:		0x00040000,
-		mask_flags:	MTD_WRITABLE, /* force read-only */
-	}, {
-		name:		"Consus kernel",
-		offset:		0x00040000,
-		size:		0x00100000,
-		mask_flags:	0,
+		.name		= "Consus boot firmware",
+		.offset		= 0,
+		.size		= 0x00040000,
+		.mask_flags	= MTD_WRITABLE, /* force read-only */
+	}, {
+		.name		= "Consus kernel",
+		.offset		= 0x00040000,
+		.size		= 0x00100000,
+		.mask_flags	= 0,
 	}, {
-		name:		"Consus disk",
-		offset:		0x00140000,
+		.name		= "Consus disk",
+		.offset		= 0x00140000,
 		/* The rest (up to 16M) for jffs.  We could put 0 and
 		   make it find the size automatically, but right now
 		   i have 32 megs.  jffs will use all 32 megs if given
 		   the chance, and this leads to horrible problems
 		   when you try to re-flash the image because blob
 		   won't erase the whole partition. */
-		size:		0x01000000 - 0x00140000,
-		mask_flags:	0,
+		.size		= 0x01000000 - 0x00140000,
+		.mask_flags	= 0,
 	}, {
 		/* this disk is a secondary disk, which can be used as
 		   needed, for simplicity, make it the size of the other
 		   consus partition, although realistically it could be
 		   the remainder of the disk (depending on the file
 		   system used) */
-		 name:		"Consus disk2",
-		 offset:	0x01000000,
-		 size:		0x01000000 - 0x00140000,
-		 mask_flags:	0,
+		 .name		= "Consus disk2",
+		 .offset	= 0x01000000,
+		 .size		= 0x01000000 - 0x00140000,
+		 .mask_flags	= 0,
 	}
 };
 #endif
@@ -292,96 +226,95 @@
 #define FLEXANET_FLASH_SIZE		0x02000000
 static struct mtd_partition flexanet_partitions[] = {
 	{
-		name:		"bootloader",
-		size:		0x00040000,
-		offset:		0,
-		mask_flags:	MTD_WRITEABLE,
-	}, {
-		name:		"bootloader params",
-		size:		0x00040000,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,
-	}, {
-		name:		"kernel",
-		size:		0x000C0000,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,
-	}, {
-		name:		"altkernel",
-		size:		0x000C0000,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,
-	}, {
-		name:		"root",
-		size:		0x00400000,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,
-	}, {
-		name:		"free1",
-		size:		0x00300000,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,
-	}, {
-		name:		"free2",
-		size:		0x00300000,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,
-	}, {
-		name:		"free3",
-		size:		MTDPART_SIZ_FULL,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,
+		.name		= "bootloader",
+		.size		= 0x00040000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "bootloader params",
+		.size		= 0x00040000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "kernel",
+		.size		= 0x000C0000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "altkernel",
+		.size		= 0x000C0000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "root",
+		.size		= 0x00400000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "free1",
+		.size		= 0x00300000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "free2",
+		.size		= 0x00300000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,
+	}, {
+		.name		= "free3",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,
 	}
 };
 #endif
 
 #ifdef CONFIG_SA1100_FREEBIRD
-#define FREEBIRD_FLASH_SIZE		0x02000000
 static struct mtd_partition freebird_partitions[] = {
-#if CONFIG_SA1100_FREEBIRD_NEW
+#ifdef CONFIG_SA1100_FREEBIRD_NEW
 	{
-		name:		"firmware",
-		size:		0x00040000,
-		offset:		0,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
-	}, {
-		name:		"kernel",
-		size:		0x00080000,
-		offset:		0x00040000,
-	}, {
-		name:		"params",
-		size:		0x00040000,
-		offset:		0x000C0000,
-	}, {
-		name:		"initrd",
-		size:		0x00100000,
-		offset:		0x00100000,
-	}, {
-		name:		"root cramfs",
-		size:		0x00300000,
-		offset:		0x00200000,
-	}, {
-		name:		"usr cramfs",
-		size:		0x00C00000,
-		offset:		0x00500000,
-	}, {
-		name:		"local",
-		size:		MTDPART_SIZ_FULL,
-		offset:		0x01100000,
+		.name		= "firmware",
+		.size		= 0x00040000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
+	}, {
+		.name		= "kernel",
+		.size		= 0x00080000,
+		.offset		= 0x00040000,
+	}, {
+		.name		= "params",
+		.size		= 0x00040000,
+		.offset		= 0x000C0000,
+	}, {
+		.name		= "initrd",
+		.size		= 0x00100000,
+		.offset		= 0x00100000,
+	}, {
+		.name		= "root cramfs",
+		.size		= 0x00300000,
+		.offset		= 0x00200000,
+	}, {
+		.name		= "usr cramfs",
+		.size		= 0x00C00000,
+		.offset		= 0x00500000,
+	}, {
+		.name		= "local",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= 0x01100000,
 	}
 #else
 	{
-		size:		0x00040000,
-		offset:		0,
+		.size		= 0x00040000,
+		.offset		= 0,
 	}, {
-		size:		0x000c0000,
-		offset:		MTDPART_OFS_APPEND,
+		.size		= 0x000c0000,
+		.offset		= MTDPART_OFS_APPEND,
 	}, {
-		size:		0x00400000,
-		offset:		MTDPART_OFS_APPEND,
+		.size		= 0x00400000,
+		.offset		= MTDPART_OFS_APPEND,
 	}, {
-		size:		MTDPART_SIZ_FULL,
-		offset:		MTDPART_OFS_APPEND,
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
 	}
 #endif
 };
@@ -389,178 +322,215 @@
 
 #ifdef CONFIG_SA1100_FRODO
 /* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */
-#define FRODO_FLASH_SIZE		0x02000000
 static struct mtd_partition frodo_partitions[] =
 {
 	{
-		name:		"Boot Loader",
-		size:		0x00040000,
-		offset:		0x00000000
-	}, {
-		name:		"Parameter Block",
-		size:		0x00040000,
-		offset:		MTDPART_OFS_APPEND
-	}, {
-		name:		"Linux Kernel",
-		size:		0x00100000,
-		offset:		MTDPART_OFS_APPEND
-	}, {
-		name:		"Ramdisk",
-		size:		0x00680000,
-		offset:		MTDPART_OFS_APPEND
-	}, {
-		name:		"Flash File System",
-		size:		MTDPART_SIZ_FULL,
-		offset:		MTDPART_OFS_APPEND
+		.name		= "bootloader",
+		.size		= 0x00040000,
+		.offset		= 0x00000000,
+		.mask_flags	= MTD_WRITEABLE
+	}, {
+		.name		= "bootloader params",
+		.size		= 0x00040000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE
+	}, {
+		.name		= "kernel",
+		.size		= 0x00100000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE
+	}, {
+		.name		= "ramdisk",
+		.size		= 0x00400000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE
+	}, {
+		.name		= "file system",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND
 	}
 };
 #endif
 
 #ifdef CONFIG_SA1100_GRAPHICSCLIENT
-#define GRAPHICSCLIENT_FLASH_SIZE	0x02000000
 static struct mtd_partition graphicsclient_partitions[] = {
 	{
-		name:		"zImage",
-		size:		0x100000,
-		offset:		0,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
-	}, {
-		name:		"ramdisk.gz",
-		size:		0x300000,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
-	}, {
-		name:		"User FS",
-		size:		MTDPART_SIZ_FULL,
-		offset:		MTDPART_OFS_APPEND,
+		.name		= "zImage",
+		.size		= 0x100000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
+	}, {
+		.name		= "ramdisk.gz",
+		.size		= 0x300000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
+	}, {
+		.name		= "User FS",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
 	}
 };
 #endif
 
 #ifdef CONFIG_SA1100_GRAPHICSMASTER
-#define GRAPHICSMASTER_FLASH_SIZE	0x01000000
 static struct mtd_partition graphicsmaster_partitions[] = {
 	{
-		name:		"zImage",
-		size:		0x100000,
-		offset:		0,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
+		.name		= "zImage",
+		.size		= 0x100000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
 	},
 	{
-		name:		"ramdisk.gz",
-		size:		0x300000,
-		offset:		MTDPART_OFS_APPEND,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
+		.name		= "ramdisk.gz",
+		.size		= 0x300000,
+		.offset		= MTDPART_OFS_APPEND,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
 	},
 	{
-		name:		"User FS",
-		size:		MTDPART_SIZ_FULL,
-		offset:		MTDPART_OFS_APPEND,
+		.name		= "User FS",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
 	}
 };
 #endif
 
-#ifdef CONFIG_SA1100_H3600
-#define H3600_FLASH_SIZE		0x02000000
-static struct mtd_partition h3600_partitions[] = {
+#ifdef CONFIG_SA1100_H3XXX
+static struct mtd_partition h3xxx_partitions[] = {
 	{
-		name:		"H3600 boot firmware",
-		size:		0x00040000,
-		offset:		0,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
-	}, {
-		name:		"H3600 kernel",
-		size:		0x00080000,
-		offset:		0x00040000,
+		.name		= "H3XXX boot firmware",
+		.size		= 0x00040000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
 	}, {
-		name:		"H3600 params",
-		size:		0x00040000,
-		offset:		0x000C0000,
+#ifdef CONFIG_MTD_2PARTS_IPAQ
+		.name		= "H3XXX root jffs2",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= 0x00040000,
+#else
+		.name		= "H3XXX kernel",
+		.size		= 0x00080000,
+		.offset		= 0x00040000,
+	}, {
+		.name		= "H3XXX params",
+		.size		= 0x00040000,
+		.offset		= 0x000C0000,
 	}, {
 #ifdef CONFIG_JFFS2_FS
-		name:		"H3600 root jffs2",
-		size:		MTDPART_SIZ_FULL,
-		offset:		0x00100000,
+		.name		= "H3XXX root jffs2",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= 0x00100000,
 #else
-		name:		"H3600 initrd",
-		size:		0x00100000,
-		offset:		0x00100000,
+		.name		= "H3XXX initrd",
+		.size		= 0x00100000,
+		.offset		= 0x00100000,
 	}, {
-		name:		"H3600 root cramfs",
-		size:		0x00300000,
-		offset:		0x00200000,
+		.name		= "H3XXX root cramfs",
+		.size		= 0x00300000,
+		.offset		= 0x00200000,
 	}, {
-		name:		"H3600 usr cramfs",
-		size:		0x00800000,
-		offset:		0x00500000,
+		.name		= "H3XXX usr cramfs",
+		.size		= 0x00800000,
+		.offset		= 0x00500000,
 	}, {
-		name:		"H3600 usr local",
-		size:		MTDPART_SIZ_FULL,
-		offset:		0x00d00000,
+		.name		= "H3XXX usr local",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= 0x00d00000,
+#endif
 #endif
 	}
 };
 
-static void h3600_set_vpp(struct map_info *map, int vpp)
+static void h3xxx_set_vpp(struct map_info *map, int vpp)
 {
 	assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, vpp);
 }
+#else
+#define h3xxx_set_vpp NULL
+#endif
+
+#ifdef CONFIG_SA1100_HACKKIT
+static struct mtd_partition hackkit_partitions[] = {
+	{
+		.name		= "BLOB",
+		.size		= 0x00040000,
+		.offset		= 0x00000000,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
+	}, {
+		.name		= "config",
+		.size		= 0x00040000,
+		.offset		= MTDPART_OFS_APPEND,
+	}, {
+		.name		= "kernel",
+		.size		= 0x00100000,
+		.offset		= MTDPART_OFS_APPEND,
+	}, {
+		.name		= "initrd",
+		.size		= 0x00180000,
+		.offset		= MTDPART_OFS_APPEND,
+	}, {
+		.name		= "rootfs",
+		.size		= 0x700000,
+		.offset		= MTDPART_OFS_APPEND,
+	}, {
+		.name		= "data",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
+	}
+};
 #endif
 
 #ifdef CONFIG_SA1100_HUW_WEBPANEL
-#define HUW_WEBPANEL_FLASH_SIZE		0x01000000
 static struct mtd_partition huw_webpanel_partitions[] = {
 	{
-		name:		"Loader",
-		size:		0x00040000,
-		offset:		0,
-	}, {
-		name:		"Sector 1",
-		size:		0x00040000,
-		offset:		MTDPART_OFS_APPEND,
+		.name		= "Loader",
+		.size		= 0x00040000,
+		.offset		= 0,
+	}, {
+		.name		= "Sector 1",
+		.size		= 0x00040000,
+		.offset		= MTDPART_OFS_APPEND,
 	}, {
-		size:		MTDPART_SIZ_FULL,
-		offset:		MTDPART_OFS_APPEND,
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
 	}
 };
 #endif
 
 #ifdef CONFIG_SA1100_JORNADA720
-#define JORNADA720_FLASH_SIZE		0x02000000
 static struct mtd_partition jornada720_partitions[] = {
 	{
-		name:		"JORNADA720 boot firmware",
-		size:		0x00040000,
-		offset:		0,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
+		.name		= "JORNADA720 boot firmware",
+		.size		= 0x00040000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
 	}, {
-		name:		"JORNADA720 kernel",
-		size:		0x000c0000,
-		offset:		0x00040000,
+		.name		= "JORNADA720 kernel",
+		.size		= 0x000c0000,
+		.offset		= 0x00040000,
 	}, {
-		name:		"JORNADA720 params",
-		size:		0x00040000,
-		offset:		0x00100000,
+		.name		= "JORNADA720 params",
+		.size		= 0x00040000,
+		.offset		= 0x00100000,
 	}, {
-		name:		"JORNADA720 initrd",
-		size:		0x00100000,
-		offset:		0x00140000,
+		.name		= "JORNADA720 initrd",
+		.size		= 0x00100000,
+		.offset		= 0x00140000,
 	}, {
-		name:		"JORNADA720 root cramfs",
-		size:		0x00300000,
-		offset:		0x00240000,
+		.name		= "JORNADA720 root cramfs",
+		.size		= 0x00300000,
+		.offset		= 0x00240000,
 	}, {
-		name:		"JORNADA720 usr cramfs",
-		size:		0x00800000,
-		offset:		0x00540000,
+		.name		= "JORNADA720 usr cramfs",
+		.size		= 0x00800000,
+		.offset		= 0x00540000,
 	}, {
-		name:		"JORNADA720 usr local",
-		size:		0  /* will expand to the end of the flash */
-		offset:		0x00d00000,
+		.name		= "JORNADA720 usr local",
+		.size		= 0,  /* will expand to the end of the flash */
+		.offset		= 0x00d00000,
 	}
 };
 
-static void jornada720_set_vpp(int vpp)
+static void jornada720_set_vpp(struct map_info *map, int vpp)
 {
 	if (vpp)
 		PPSR |= 0x80;
@@ -568,454 +538,811 @@
 		PPSR &= ~0x80;
 	PPDR |= 0x80;
 }
-
+#else
+#define jornada720_set_vpp NULL
 #endif
 
 #ifdef CONFIG_SA1100_PANGOLIN
-#define PANGOLIN_FLASH_SIZE		0x04000000
 static struct mtd_partition pangolin_partitions[] = {
 	{
-		name:		"boot firmware",
-		size:		0x00080000,
-		offset:		0x00000000,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
-	}, {
-		name:		"kernel",
-		size:		0x00100000,
-		offset:		0x00080000,
-	}, {
-		name:		"initrd",
-		size:		0x00280000,
-		offset:		0x00180000,
-	}, {
-		name:		"initrd-test",
-		size:		0x03C00000,
-		offset:		0x00400000,
+		.name		= "boot firmware",
+		.size		= 0x00080000,
+		.offset		= 0x00000000,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
+	}, {
+		.name		= "kernel",
+		.size		= 0x00100000,
+		.offset		= 0x00080000,
+	}, {
+		.name		= "initrd",
+		.size		= 0x00280000,
+		.offset		= 0x00180000,
+	}, {
+		.name		= "initrd-test",
+		.size		= 0x03C00000,
+		.offset		= 0x00400000,
 	}
 };
 #endif
 
 #ifdef CONFIG_SA1100_PT_SYSTEM3
 /* erase size is 0x40000 == 256k partitions have to have this boundary */
-#define SYSTEM3_FLASH_SIZE		0x01000000
 static struct mtd_partition system3_partitions[] = {
 	{
-		name:		"BLOB",
-		size:		0x00040000,
-		offset:		0x00000000,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
-	}, {
-		name:		"config",
-		size:		0x00040000,
-		offset:		MTDPART_OFS_APPEND,
-	}, {
-		name:		"kernel",
-		size:		0x00100000,
-		offset:		MTDPART_OFS_APPEND,
-	}, {
-		name:		"root",
-		size:		MTDPART_SIZ_FULL,
-		offset:		MTDPART_OFS_APPEND,
+		.name		= "BLOB",
+		.size		= 0x00040000,
+		.offset		= 0x00000000,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
+	}, {
+		.name		= "config",
+		.size		= 0x00040000,
+		.offset		= MTDPART_OFS_APPEND,
+	}, {
+		.name		= "kernel",
+		.size		= 0x00100000,
+		.offset		= MTDPART_OFS_APPEND,
+	}, {
+		.name		= "root",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
 	}
 };
 #endif
 
 #ifdef CONFIG_SA1100_SHANNON
-#define SHANNON_FLASH_SIZE		0x00400000
 static struct mtd_partition shannon_partitions[] = {
 	{
-		name: "BLOB boot loader",
-		offset: 0,
-		size: 0x20000
+		.name		= "BLOB boot loader",
+		.offset		= 0,
+		.size		= 0x20000
 	},
 	{
-		name: "kernel",
-		offset: MTDPART_OFS_APPEND,
-		size: 0xe0000
+		.name		= "kernel",
+		.offset		= MTDPART_OFS_APPEND,
+		.size		= 0xe0000
 	},
 	{ 
-		name: "initrd",
-		offset: MTDPART_OFS_APPEND,	
-		size: MTDPART_SIZ_FULL
+		.name		= "initrd",
+		.offset		= MTDPART_OFS_APPEND,	
+		.size		= MTDPART_SIZ_FULL
 	}
 };
 
 #endif
 
 #ifdef CONFIG_SA1100_SHERMAN
-#define SHERMAN_FLASH_SIZE		0x02000000
 static struct mtd_partition sherman_partitions[] = {
 	{
-		size:		0x50000,
-		offset:		0,
+		.size		= 0x50000,
+		.offset		= 0,
 	}, {
-		size:		0x70000,
-		offset:		MTDPART_OFS_APPEND,
+		.size		= 0x70000,
+		.offset		= MTDPART_OFS_APPEND,
 	}, {
-		size:		0x600000,
-		offset:		MTDPART_OFS_APPEND,
+		.size		= 0x600000,
+		.offset		= MTDPART_OFS_APPEND,
 	}, {
-		size:		0xA0000,
-		offset:		MTDPART_OFS_APPEND,
+		.size		= 0xA0000,
+		.offset		= MTDPART_OFS_APPEND,
 	}
 };
 #endif
 
 #ifdef CONFIG_SA1100_SIMPAD
-#define SIMPAD_FLASH_SIZE		0x02000000
 static struct mtd_partition simpad_partitions[] = {
 	{
-		name:		"SIMpad boot firmware",
-		size:		0x00080000,
-		offset:		0,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
-	}, {
-		name:		"SIMpad kernel",
-		size:		0x00100000,
-		offset:		0x00080000,
-	}, {
-#ifdef CONFIG_JFFS2_FS
-		name:		"SIMpad root jffs2",
-		size:		MTDPART_SIZ_FULL,
-		offset:		0x00180000,
+		.name		= "SIMpad boot firmware",
+		.size		= 0x00080000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
+	}, {
+		.name		= "SIMpad kernel",
+		.size		= 0x00100000,
+		.offset		= MTDPART_OFS_APPEND,
+	}, {
+#ifdef CONFIG_ROOT_CRAMFS
+		.name		= "SIMpad root cramfs",
+		.size	     	=0x00D80000,
+		.offset		= MTDPART_OFS_APPEND
+
+	}, {
+		.name		= "SIMpad local jffs2",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND
 #else
-		name:		"SIMpad initrd",
-		size:		0x00300000,
-		offset:		0x00180000,
-	}, {
-		name:		"SIMpad root cramfs",
-		size:		0x00300000,
-		offset:		0x00480000,
-	}, {
-		name:		"SIMpad usr cramfs",
-		size:		0x005c0000,
-		offset:		0x00780000,
-	}, {
-		name:		"SIMpad usr local",
-		size:		MTDPART_SIZ_FULL,
-		offset:		0x00d40000,
+		.name		= "SIMpad root jffs2",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND
 #endif
 	}
 };
 #endif /* CONFIG_SA1100_SIMPAD */
 
 #ifdef CONFIG_SA1100_STORK
-#define STORK_FLASH_SIZE		0x02000000
 static struct mtd_partition stork_partitions[] = {
 	{
-		name:		"STORK boot firmware",
-		size:		0x00040000,
-		offset:		0,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
-	}, {
-		name:		"STORK params",
-		size:		0x00040000,
-		offset:		0x00040000,
-	}, {
-		name:		"STORK kernel",
-		size:		0x00100000,
-		offset:		0x00080000,
+		.name		= "STORK boot firmware",
+		.size		= 0x00040000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
+	}, {
+		.name		= "STORK params",
+		.size		= 0x00040000,
+		.offset		= 0x00040000,
+	}, {
+		.name		= "STORK kernel",
+		.size		= 0x00100000,
+		.offset		= 0x00080000,
 	}, {
 #ifdef CONFIG_JFFS2_FS
-		name:		"STORK root jffs2",
-		offset:		0x00180000,
-		size:		MTDPART_SIZ_FULL,
+		.name		= "STORK root jffs2",
+		.offset		= 0x00180000,
+		.size		= MTDPART_SIZ_FULL,
 #else
-		name:		"STORK initrd",
-		size:		0x00100000,
-		offset:		0x00180000,
-	}, {
-		name:		"STORK root cramfs",
-		size:		0x00300000,
-		offset:		0x00280000,
-	}, {
-		name:		"STORK usr cramfs",
-		size:		0x00800000,
-		offset:		0x00580000,
-	}, {
-		name:		"STORK usr local",
-		offset:		0x00d80000,
-		size:		MTDPART_SIZ_FULL,
+		.name		= "STORK initrd",
+		.size		= 0x00100000,
+		.offset		= 0x00180000,
+	}, {
+		.name		= "STORK root cramfs",
+		.size		= 0x00300000,
+		.offset		= 0x00280000,
+	}, {
+		.name		= "STORK usr cramfs",
+		.size		= 0x00800000,
+		.offset		= 0x00580000,
+	}, {
+		.name		= "STORK usr local",
+		.offset		= 0x00d80000,
+		.size		= MTDPART_SIZ_FULL,
 #endif
 	}
 };
 #endif
 
+#ifdef CONFIG_SA1100_TRIZEPS
+static struct mtd_partition trizeps_partitions[] = {
+	{
+		.name		= "Bootloader",
+		.size		= 0x00100000,
+		.offset		= 0,
+	}, {
+		.name		= "Kernel",
+		.size		= 0x00100000,
+		.offset		= MTDPART_OFS_APPEND,
+	}, {
+		.name		= "root",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
+	}
+};
+#endif
+
 #ifdef CONFIG_SA1100_YOPY
-#define YOPY_FLASH_SIZE			0x08000000
 static struct mtd_partition yopy_partitions[] = {
 	{
-		name:		"boot firmware",
-		size:		0x00040000,
-		offset:		0x00000000,
-		mask_flags:	MTD_WRITEABLE,  /* force read-only */
+		.name		= "boot firmware",
+		.size		= 0x00040000,
+		.offset		= 0x00000000,
+		.mask_flags	= MTD_WRITEABLE,  /* force read-only */
 	}, {
-		name:		"kernel",
-		size:		0x00080000,
-		offset:		0x00080000,
+		.name		= "kernel",
+		.size		= 0x00080000,
+		.offset		= 0x00080000,
 	}, {
-		name:		"initrd",
-		size:		0x00300000,
-		offset:		0x00100000,
+		.name		= "initrd",
+		.size		= 0x00300000,
+		.offset		= 0x00100000,
 	}, {
-		name:		"root",
-		size:		0x01000000,
-		offset:		0x00400000,
+		.name		= "root",
+		.size		= 0x01000000,
+		.offset		= 0x00400000,
 	}
 };
 #endif
 
-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
-extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts);
-
-static struct mtd_partition *parsed_parts;
-static struct mtd_info *mymtd;
-
-int __init sa1100_mtd_init(void)
+static int __init sa1100_static_partitions(struct mtd_partition **parts)
 {
-	struct mtd_partition *parts;
-	int nb_parts = 0, ret;
-	int parsed_nr_parts = 0;
-	const char *part_type;
-	unsigned long base = -1UL;
-
-	/* Default flash buswidth */
-	sa1100_map.buswidth = (MSC0 & MSC_RBW) ? 2 : 4;
-
-	/*
-	 * Static partition definition selection
-	 */
-	part_type = "static";
+	int nb_parts = 0;
 
 #ifdef CONFIG_SA1100_ADSBITSY
 	if (machine_is_adsbitsy()) {
-		parts = adsbitsy_partitions;
-		nb_parts = ARRAY_SIZE(adsbitsy_partitions);
-		sa1100_map.size = ADSBITSY_FLASH_SIZE;
-		sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4;
+		*parts       = adsbitsy_partitions;
+		nb_parts     = ARRAY_SIZE(adsbitsy_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_ASSABET
 	if (machine_is_assabet()) {
-		parts = assabet_partitions;
-		nb_parts = ARRAY_SIZE(assabet_partitions);
-		sa1100_map.size = ASSABET_FLASH_SIZE;
+		*parts       = assabet_partitions;
+		nb_parts     = ARRAY_SIZE(assabet_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_BADGE4
 	if (machine_is_badge4()) {
-		parts = badge4_partitions;
-		nb_parts = ARRAY_SIZE(badge4_partitions);
-		sa1100_map.size = BADGE4_FLASH_SIZE;
+		*parts       = badge4_partitions;
+		nb_parts     = ARRAY_SIZE(badge4_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_CERF
 	if (machine_is_cerf()) {
-		parts = cerf_partitions;
-		nb_parts = ARRAY_SIZE(cerf_partitions);
-		sa1100_map.size = CERF_FLASH_SIZE;
+		*parts       = cerf_partitions;
+		nb_parts     = ARRAY_SIZE(cerf_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_CONSUS
 	if (machine_is_consus()) {
-		parts = consus_partitions;
-		nb_parts = ARRAY_SIZE(consus_partitions);
-		sa1100_map.size = CONSUS_FLASH_SIZE;
+		*parts       = consus_partitions;
+		nb_parts     = ARRAY_SIZE(consus_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_FLEXANET
 	if (machine_is_flexanet()) {
-		parts = flexanet_partitions;
-		nb_parts = ARRAY_SIZE(flexanet_partitions);
-		sa1100_map.size = FLEXANET_FLASH_SIZE;
+		*parts       = flexanet_partitions;
+		nb_parts     = ARRAY_SIZE(flexanet_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_FREEBIRD
 	if (machine_is_freebird()) {
-		parts = freebird_partitions;
-		nb_parts = ARRAY_SIZE(freebird_partitions);
-		sa1100_map.size = FREEBIRD_FLASH_SIZE;
+		*parts       = freebird_partitions;
+		nb_parts     = ARRAY_SIZE(freebird_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_FRODO
 	if (machine_is_frodo()) {
-		parts = frodo_partitions;
-		nb_parts = ARRAY_SIZE(frodo_partitions);
-		sa1100_map.size = FRODO_FLASH_SIZE;
-		base = 0x00000000;
+		*parts       = frodo_partitions;
+		nb_parts     = ARRAY_SIZE(frodo_partitions);
 	}
-#endif
+#endif	
 #ifdef CONFIG_SA1100_GRAPHICSCLIENT
 	if (machine_is_graphicsclient()) {
-		parts = graphicsclient_partitions;
-		nb_parts = ARRAY_SIZE(graphicsclient_partitions);
-		sa1100_map.size = GRAPHICSCLIENT_FLASH_SIZE;
-		sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4;
+		*parts       = graphicsclient_partitions;
+		nb_parts     = ARRAY_SIZE(graphicsclient_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_GRAPHICSMASTER
 	if (machine_is_graphicsmaster()) {
-		parts = graphicsmaster_partitions;
-		nb_parts = ARRAY_SIZE(graphicsmaster_partitions);
-		sa1100_map.size = GRAPHICSMASTER_FLASH_SIZE;
-		sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4;
+		*parts       = graphicsmaster_partitions;
+		nb_parts     = ARRAY_SIZE(graphicsmaster_partitions);
 	}
 #endif
-#ifdef CONFIG_SA1100_H3600
-	if (machine_is_h3600()) {
-		parts = h3600_partitions;
-		nb_parts = ARRAY_SIZE(h3600_partitions);
-		sa1100_map.size = H3600_FLASH_SIZE;
-		sa1100_map.set_vpp = h3600_set_vpp;
+#ifdef CONFIG_SA1100_H3XXX
+	if (machine_is_h3xxx()) {
+		*parts       = h3xxx_partitions;
+		nb_parts     = ARRAY_SIZE(h3xxx_partitions);
+	}
+#endif
+#ifdef CONFIG_SA1100_HACKKIT
+	if (machine_is_hackkit()) {
+		*parts = hackkit_partitions;
+		nb_parts = ARRAY_SIZE(hackkit_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_HUW_WEBPANEL
 	if (machine_is_huw_webpanel()) {
-		parts = huw_webpanel_partitions;
-		nb_parts = ARRAY_SIZE(huw_webpanel_partitions);
-		sa1100_map.size = HUW_WEBPANEL_FLASH_SIZE;
+		*parts       = huw_webpanel_partitions;
+		nb_parts     = ARRAY_SIZE(huw_webpanel_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_JORNADA720
 	if (machine_is_jornada720()) {
-		parts = jornada720_partitions;
-		nb_parts = ARRAY_SIZE(jornada720_partitions);
-		sa1100_map.size = JORNADA720_FLASH_SIZE;
-		sa1100_map.set_vpp = jornada720_set_vpp;
+		*parts       = jornada720_partitions;
+		nb_parts     = ARRAY_SIZE(jornada720_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_PANGOLIN
 	if (machine_is_pangolin()) {
-		parts = pangolin_partitions;
-		nb_parts = ARRAY_SIZE(pangolin_partitions);
-		sa1100_map.size = PANGOLIN_FLASH_SIZE;
+		*parts       = pangolin_partitions;
+		nb_parts     = ARRAY_SIZE(pangolin_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_PT_SYSTEM3
 	if (machine_is_pt_system3()) {
-		parts = system3_partitions;
-		nb_parts = ARRAY_SIZE(system3_partitions);
-		sa1100_map.size = SYSTEM3_FLASH_SIZE;
+		*parts       = system3_partitions;
+		nb_parts     = ARRAY_SIZE(system3_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_SHANNON
 	if (machine_is_shannon()) {
-		parts = shannon_partitions;
-		nb_parts = ARRAY_SIZE(shannon_partitions);
-		sa1100_map.size = SHANNON_FLASH_SIZE;
+		*parts       = shannon_partitions;
+		nb_parts     = ARRAY_SIZE(shannon_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_SHERMAN
 	if (machine_is_sherman()) {
-		parts = sherman_partitions;
-		nb_parts = ARRAY_SIZE(sherman_partitions);
-		sa1100_map.size = SHERMAN_FLASH_SIZE;
+		*parts       = sherman_partitions;
+		nb_parts     = ARRAY_SIZE(sherman_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_SIMPAD
 	if (machine_is_simpad()) {
-		parts = simpad_partitions;
-		nb_parts = ARRAY_SIZE(simpad_partitions);
-		sa1100_map.size = SIMPAD_FLASH_SIZE;
+		*parts       = simpad_partitions;
+		nb_parts     = ARRAY_SIZE(simpad_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_STORK
 	if (machine_is_stork()) {
-		parts = stork_partitions;
-		nb_parts = ARRAY_SIZE(stork_partitions);
-		sa1100_map.size = STORK_FLASH_SIZE;
+		*parts       = stork_partitions;
+		nb_parts     = ARRAY_SIZE(stork_partitions);
+	}
+#endif
+#ifdef CONFIG_SA1100_TRIZEPS
+	if (machine_is_trizeps()) {
+		*parts       = trizeps_partitions;
+		nb_parts     = ARRAY_SIZE(trizeps_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_YOPY
 	if (machine_is_yopy()) {
-		parts = yopy_partitions;
-		nb_parts = ARRAY_SIZE(yopy_partitions);
-		sa1100_map.size = YOPY_FLASH_SIZE;
+		*parts       = yopy_partitions;
+		nb_parts     = ARRAY_SIZE(yopy_partitions);
 	}
 #endif
 
+	return nb_parts;
+}
+#endif
+
+struct sa_info {
+	unsigned long base;
+	unsigned long size;
+	int width;
+	void *vbase;
+        void (*set_vpp)(struct map_info *, int);
+	struct map_info *map;
+	struct mtd_info *mtd;
+	struct resource *res;
+};
+
+#define NR_SUBMTD 4
+
+static struct sa_info info[NR_SUBMTD];
+
+static int __init sa1100_setup_mtd(struct sa_info *sa, int nr, struct mtd_info **rmtd)
+{
+	struct mtd_info *subdev[nr];
+	struct map_info *maps;
+	int i, found = 0, ret = 0;
+
 	/*
-	 * For simple flash devices, use ioremap to map the flash.
+	 * Allocate the map_info structs in one go.
 	 */
-	if (base != (unsigned long)-1) {
-		if (!request_mem_region(base, sa1100_map.size, "flash"))
-			return -EBUSY;
-		sa1100_map.map_priv_2 = base;
-		sa1100_map.map_priv_1 = (unsigned long)
-				ioremap(base, sa1100_map.size);
-		ret = -ENOMEM;
-		if (!sa1100_map.map_priv_1)
-			goto out_err;
-	}
+	maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL);
+	if (!maps)
+		return -ENOMEM;
+
+	memset(maps, 0, sizeof(struct map_info) * nr);
 
 	/*
-	 * Now let's probe for the actual flash.  Do it here since
-	 * specific machine settings might have been set above.
+	 * Claim and then map the memory regions.
 	 */
-	printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8);
-	mymtd = do_map_probe("cfi_probe", &sa1100_map);
-	ret = -ENXIO;
-	if (!mymtd)
-		goto out_err;
-	mymtd->module = THIS_MODULE;
+	for (i = 0; i < nr; i++) {
+		if (sa[i].base == (unsigned long)-1)
+			break;
+
+		sa[i].res = request_mem_region(sa[i].base, sa[i].size, "sa1100 flash");
+		if (!sa[i].res) {
+			ret = -EBUSY;
+			break;
+		}
+
+		sa[i].map = maps + i;
+
+		sa[i].vbase = ioremap(sa[i].base, sa[i].size);
+		if (!sa[i].vbase) {
+			ret = -ENOMEM;
+			break;
+		}
+
+		sa[i].map->virt = (unsigned long)sa[i].vbase;
+		sa[i].map->phys = sa[i].base;
+		sa[i].map->set_vpp = sa[i].set_vpp;
+		sa[i].map->buswidth = sa[i].width;
+		sa[i].map->size = sa[i].size;
+
+		simple_map_init(sa[i].map);
+
+		/*
+		 * Now let's probe for the actual flash.  Do it here since
+		 * specific machine settings might have been set above.
+		 */
+		sa[i].mtd = do_map_probe("cfi_probe", sa[i].map);
+		if (sa[i].mtd == NULL) {
+			ret = -ENXIO;
+			break;
+		}
+		sa[i].mtd->owner = THIS_MODULE;
+		subdev[i] = sa[i].mtd;
+
+		printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, "
+			"%d-bit\n", sa[i].base, sa[i].mtd->size >> 20,
+			sa[i].width * 8);
+		found += 1;
+	}
 
 	/*
-	 * Dynamic partition selection stuff (might override the static ones)
+	 * ENXIO is special.  It means we didn't find a chip when
+	 * we probed.  We need to tear down the mapping, free the
+	 * resource and mark it as such.
 	 */
-#ifdef CONFIG_MTD_REDBOOT_PARTS
-	if (parsed_nr_parts == 0) {
-		int ret = parse_redboot_partitions(mymtd, &parsed_parts);
-
-		if (ret > 0) {
-			part_type = "RedBoot";
-			parsed_nr_parts = ret;
-		}
+	if (ret == -ENXIO) {
+		iounmap(sa[i].vbase);
+		sa[i].vbase = NULL;
+		release_resource(sa[i].res);
+		sa[i].res = NULL;
 	}
+
+	/*
+	 * If we found one device, don't bother with concat support.
+	 * If we found multiple devices, use concat if we have it
+	 * available, otherwise fail.
+	 */
+	if (ret == 0 || ret == -ENXIO) {
+		if (found == 1) {
+			*rmtd = subdev[0];
+			ret = 0;
+		} else if (found > 1) {
+			/*
+			 * We detected multiple devices.  Concatenate
+			 * them together.
+			 */
+#ifdef CONFIG_MTD_CONCAT
+			*rmtd = mtd_concat_create(subdev, found,
+						  "sa1100 flash");
+			if (*rmtd == NULL)
+				ret = -ENXIO;
+#else
+			printk(KERN_ERR "SA1100 flash: multiple devices "
+			       "found but MTD concat support disabled.\n");
+			ret = -ENXIO;
 #endif
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-	if (parsed_nr_parts == 0) {
-		int ret = parse_cmdline_partitions(mymtd, &parsed_parts, "sa1100");
-		if (ret > 0) {
-			part_type = "Command Line";
-			parsed_nr_parts = ret;
 		}
 	}
+
+	/*
+	 * If we failed, clean up.
+	 */
+	if (ret) {
+		do {
+			if (sa[i].mtd)
+				map_destroy(sa[i].mtd);
+			if (sa[i].vbase)
+				iounmap(sa[i].vbase);
+			if (sa[i].res)
+				release_resource(sa[i].res);
+		} while (i--);
+
+		kfree(maps);
+	}
+
+	return ret;
+}
+
+static void __exit sa1100_destroy_mtd(struct sa_info *sa, struct mtd_info *mtd)
+{
+	int i;
+
+	del_mtd_partitions(mtd);
+
+#ifdef CONFIG_MTD_CONCAT
+	if (mtd != sa[0].mtd)
+		mtd_concat_destroy(mtd);
 #endif
 
-	if (parsed_nr_parts > 0) {
-		parts = parsed_parts;
-		nb_parts = parsed_nr_parts;
+	for (i = NR_SUBMTD; i >= 0; i--) {
+		if (sa[i].mtd)
+			map_destroy(sa[i].mtd);
+		if (sa[i].vbase)
+			iounmap(sa[i].vbase);
+		if (sa[i].res)
+			release_resource(sa[i].res);
 	}
+	kfree(sa[0].map);
+}
 
-	if (nb_parts == 0) {
-		printk(KERN_NOTICE "SA1100 flash: no partition info available, registering whole flash at once\n");
-		add_mtd_device(mymtd);
-	} else {
-		printk(KERN_NOTICE "Using %s partition definition\n", part_type);
-		add_mtd_partitions(mymtd, parts, nb_parts);
+/*
+ * A Thought: can we automatically detect the flash?
+ *  - Check to see if the region is busy (yes -> failure)
+ *  - Is the MSC setup for flash (no -> failure)
+ *  - Probe for flash
+ */
+
+static struct map_info sa1100_probe_map __initdata = {
+	.name		= "SA1100-flash",
+};
+
+static void __init sa1100_probe_one_cs(unsigned int msc, unsigned long phys)
+{
+	struct mtd_info *mtd;
+
+	printk(KERN_INFO "* Probing 0x%08lx: MSC = 0x%04x %d bit ",
+		phys, msc & 0xffff, msc & MSC_RBW ? 16 : 32);
+
+	if (check_mem_region(phys, 0x08000000)) {
+		printk("busy\n");
+		return;
+	}
+
+	if ((msc & 3) == 1) {
+		printk("wrong type\n");
+		return;
+	}
+
+	sa1100_probe_map.buswidth = msc & MSC_RBW ? 2 : 4;
+	sa1100_probe_map.size = SZ_1M;
+	sa1100_probe_map.phys = phys;
+	sa1100_probe_map.virt = (unsigned long)ioremap(phys, SZ_1M);
+	if (sa1100_probe_map.virt == 0)
+		goto fail;
+	simple_map_init(&sa1100_probe_map);
+
+	/* Shame cfi_probe blurts out kernel messages... */
+	mtd = do_map_probe("cfi_probe", &sa1100_probe_map);
+	if (mtd)
+		map_destroy(mtd);
+	iounmap((void *)sa1100_probe_map.virt);
+
+	if (!mtd)
+		goto fail;
+
+	printk("pass\n");
+	return;
+
+ fail:
+	printk("failed\n");
+}
+
+static void __init sa1100_probe_flash(void)
+{
+	printk(KERN_INFO "-- SA11xx Flash probe.  Please report results.\n");
+	sa1100_probe_one_cs(MSC0, SA1100_CS0_PHYS);
+	sa1100_probe_one_cs(MSC0 >> 16, SA1100_CS1_PHYS);
+	sa1100_probe_one_cs(MSC1, SA1100_CS2_PHYS);
+	sa1100_probe_one_cs(MSC1 >> 16, SA1100_CS3_PHYS);
+	sa1100_probe_one_cs(MSC2, SA1100_CS4_PHYS);
+	sa1100_probe_one_cs(MSC2 >> 16, SA1100_CS5_PHYS);
+	printk(KERN_INFO "-- SA11xx Flash probe complete.\n");
+}
+
+static int __init sa1100_locate_flash(void)
+{
+	int i, nr = -ENODEV;
+
+	sa1100_probe_flash();
+
+	if (machine_is_adsbitsy()) {
+		info[0].base = SA1100_CS1_PHYS;
+		info[0].size = SZ_32M;
+		nr = 1;
+	}
+	if (machine_is_assabet()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_32M;
+		info[1].base = SA1100_CS1_PHYS; /* neponset */
+		info[1].size = SZ_32M;
+		nr = 2;
+	}
+	if (machine_is_badge4()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_64M;
+		nr = 1;
+	}
+	if (machine_is_cerf()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_32M;
+		nr = 1;
+	}
+	if (machine_is_consus()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_32M;
+		nr = 1;
+	}
+	if (machine_is_flexanet()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_32M;
+		nr = 1;
+	}
+	if (machine_is_freebird()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_32M;
+		nr = 1;
+	}
+	if (machine_is_frodo()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_32M;
+		nr = 1;
+	}
+	if (machine_is_graphicsclient()) {
+		info[0].base = SA1100_CS1_PHYS;
+		info[0].size = SZ_32M;
+		nr = 1;
+	}
+	if (machine_is_graphicsmaster()) {
+		info[0].base = SA1100_CS1_PHYS;
+		info[0].size = SZ_16M;
+		nr = 1;
+	}
+	if (machine_is_h3xxx()) {
+		info[0].set_vpp = h3xxx_set_vpp;
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_32M;
+		nr = 1;
+	}
+	if (machine_is_huw_webpanel()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_16M;
+		nr = 1;
+	}
+	if (machine_is_itsy()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_32M;
+		nr = 1;
+	}
+	if (machine_is_jornada720()) {
+		info[0].set_vpp = jornada720_set_vpp;
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_32M;
+		nr = 1;
+	}
+	if (machine_is_nanoengine()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[1].size = SZ_32M;
+		nr = 1;
+	}
+	if (machine_is_pangolin()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_64M;
+		nr = 1;
+	}
+	if (machine_is_pfs168()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_32M;
+		nr = 1;
+	}
+	if (machine_is_pleb()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_4M;
+		info[1].base = SA1100_CS1_PHYS;
+		info[1].size = SZ_4M;
+		nr = 2;
+	}
+	if (machine_is_pt_system3()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_16M;
+		nr = 1;
+	}
+	if (machine_is_shannon()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_4M;
+		nr = 1;
+	}
+	if (machine_is_sherman()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_32M;
+		nr = 1;
+	}
+	if (machine_is_simpad()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_16M;
+		info[1].base = SA1100_CS1_PHYS;
+		info[1].size = SZ_16M;
+		nr = 2;
+	}
+	if (machine_is_stork()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_32M;
+		nr = 1;
+	}
+	if (machine_is_trizeps()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_16M;
+		nr = 1;
+	}
+	if (machine_is_victor()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_2M;
+		nr = 1;
+	}
+	if (machine_is_yopy()) {
+		info[0].base = SA1100_CS0_PHYS;
+		info[0].size = SZ_64M;
+		info[1].base = SA1100_CS1_PHYS;
+		info[1].size = SZ_64M;
+		nr = 2;
 	}
-	return 0;
 
- out_err:
-	if (sa1100_map.map_priv_2 != -1) {
-		iounmap((void *)sa1100_map.map_priv_1);
-		release_mem_region(sa1100_map.map_priv_2, sa1100_map.size);
+	if (nr < 0)
+		return nr;
+
+	/*
+	 * Retrieve the buswidth from the MSC registers.
+	 * We currently only implement CS0 and CS1 here.
+	 */
+	for (i = 0; i < nr; i++) {
+		switch (info[i].base) {
+		default:
+			printk(KERN_WARNING "SA1100 flash: unknown base address "
+				"0x%08lx, assuming CS0\n", info[i].base);
+		case SA1100_CS0_PHYS:
+			info[i].width = (MSC0 & MSC_RBW) ? 2 : 4;
+			break;
+
+		case SA1100_CS1_PHYS:
+			info[i].width = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4;
+			break;
+		}
 	}
+
+	return nr;
+}
+
+static struct mtd_partition *parsed_parts;
+const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
+
+static void __init sa1100_locate_partitions(struct mtd_info *mtd)
+{
+	const char *part_type = NULL;
+	int nr_parts = 0;
+
+	do {
+		/*
+		 * Partition selection stuff.
+		 */
+#ifdef CONFIG_MTD_PARTITIONS
+		nr_parts = parse_mtd_partitions(mtd, part_probes, &parsed_parts, 0);
+		if (nr_parts > 0) {
+			part_type = "dynamic";
+			break;
+		}
+#endif
+#ifdef CONFIG_MTD_SA1100_STATICMAP
+		nr_parts = sa1100_static_partitions(&parsed_parts);
+		if (nr_parts > 0) {
+			part_type = "static";
+			break;
+		}
+#endif
+	} while (0);
+
+	if (nr_parts == 0) {
+		printk(KERN_NOTICE "SA1100 flash: no partition info "
+			"available, registering whole flash\n");
+		add_mtd_device(mtd);
+	} else {
+		printk(KERN_NOTICE "SA1100 flash: using %s partition "
+			"definition\n", part_type);
+		add_mtd_partitions(mtd, parsed_parts, nr_parts);
+	}
+
+	/* Always succeeds. */
+}
+
+static void __exit sa1100_destroy_partitions(void)
+{
+	if (parsed_parts)
+		kfree(parsed_parts);
+}
+
+static struct mtd_info *mymtd;
+
+static int __init sa1100_mtd_init(void)
+{
+	int ret;
+	int nr;
+
+	nr = sa1100_locate_flash();
+	if (nr < 0)
+		return nr;
+
+	ret = sa1100_setup_mtd(info, nr, &mymtd);
+	if (ret == 0)
+		sa1100_locate_partitions(mymtd);
+
 	return ret;
 }
 
 static void __exit sa1100_mtd_cleanup(void)
 {
-	if (mymtd) {
-		del_mtd_partitions(mymtd);
-		map_destroy(mymtd);
-		if (parsed_parts)
-			kfree(parsed_parts);
-	}
-	if (sa1100_map.map_priv_2 != -1) {
-		iounmap((void *)sa1100_map.map_priv_1);
-		release_mem_region(sa1100_map.map_priv_2, sa1100_map.size);
-	}
+	sa1100_destroy_mtd(info, mymtd);
+	sa1100_destroy_partitions();
 }
 
 module_init(sa1100_mtd_init);
diff -Nur linux-2.4.30/drivers/mtd/maps/sbc8240.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/sbc8240.c
--- linux-2.4.30/drivers/mtd/maps/sbc8240.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/sbc8240.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,417 @@
+/*
+ * Handle mapping of the flash memory access routines on the SBC8240 board.
+ *
+ * Carolyn Smith, Tektronix, Inc.
+ *
+ * This code is GPLed
+ *
+ * $Id: sbc8240.c,v 1.2 2003/09/30 19:37:00 thayne Exp $
+ *
+ */
+
+/*
+ * The SBC8240 has 2 flash banks.
+ * Bank 0 is a 512 KiB AMD AM29F040B; 8 x 64 KiB sectors.
+ * It contains the U-Boot code (7 sectors) and the environment (1 sector).
+ * Bank 1 is 4 x 1 MiB AMD AM29LV800BT; 15 x 64 KiB sectors, 1 x 32 KiB sector,
+ * 2 x 8 KiB sectors, 1 x 16 KiB sectors.
+ * Both parts are JEDEC compatible.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <asm/io.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/cfi.h>
+
+#ifdef CONFIG_MTD_PARTITIONS
+#include <linux/mtd/partitions.h>
+#endif
+
+#define	DEBUG
+
+#ifdef	DEBUG
+# define debugk(fmt,args...)	printk(fmt ,##args)
+#else
+# define debugk(fmt,args...)
+#endif
+
+
+#define WINDOW_ADDR0	0xFFF00000		/* 512 KiB */
+#define WINDOW_SIZE0	0x00080000
+#define BUSWIDTH0	1
+
+#define WINDOW_ADDR1	0xFF000000		/* 4 MiB */
+#define WINDOW_SIZE1	0x00400000
+#define BUSWIDTH1	8
+
+#define MSG_PREFIX "sbc8240:"	/* prefix for our printk()'s */
+#define MTDID	   "sbc8240-%d"	/* for mtdparts= partitioning */
+
+
+static __u8 sbc8240_read8 (struct map_info *map, unsigned long ofs)
+{
+	return __raw_readb(map->map_priv_1 + ofs);
+}
+
+static __u16 sbc8240_read16 (struct map_info *map, unsigned long ofs)
+{
+	return __raw_readw(map->map_priv_1 + ofs);
+}
+
+static __u32 sbc8240_read32 (struct map_info *map, unsigned long ofs)
+{
+	return __raw_readl(map->map_priv_1 + ofs);
+}
+
+unsigned long long result64;
+
+static __u64 sbc8240_read64 (struct map_info *map, unsigned long ofs)
+{
+	unsigned long flags, msr, saved_msr;
+	volatile long saved_fr[2];
+	volatile unsigned long long result;
+	volatile unsigned long *p;
+
+	save_flags(flags);
+	cli();
+
+	/* turn off floating point unavailable exceptions */
+
+	__asm__ __volatile__ (
+		"mfmsr %0" 
+		: "=r" (msr) :);
+
+	saved_msr = msr;
+	msr |= MSR_FP;
+	msr &= ~(MSR_FE0 | MSR_FE1);
+
+	__asm__ __volatile__ (
+		"mtmsr %0\n"
+		"isync\n"
+		: : "r" (msr));
+
+	/* read the data via a floating point register */
+
+	ofs = map->map_priv_1 + ofs;
+	p = (unsigned long *) &result64;
+
+	__asm__ __volatile__ (
+		"lfd  1,0(%1)\n"
+		"stfd 1,0(%0)\n"
+		: : "r" (p), "r" (ofs)
+		);
+
+	/* restore state */
+
+	__asm__ __volatile__ (
+		"mtmsr %0\n"
+		"isync\n"
+		: : "r" (saved_msr));
+
+	restore_flags(flags);
+
+	p = (unsigned long *) &result64;
+	debugk("sbc8240_read64 ofs 0x%x result 0x%08x%08x\n", ofs, *p, *(p+1));
+
+	return result64;
+}
+
+static void sbc8240_copy_from (struct map_info *map,
+			       void *to, unsigned long from, ssize_t len)
+{
+	memcpy_fromio (to, (void *) (map->map_priv_1 + from), len);
+}
+
+static void sbc8240_write8 (struct map_info *map, __u8 d, unsigned long adr)
+{
+	__raw_writeb(d, map->map_priv_1 + adr);
+	mb();
+}
+
+static void sbc8240_write16 (struct map_info *map, __u16 d,
+			     unsigned long adr)
+{
+	__raw_writew(d, map->map_priv_1 + adr);
+	mb();
+}
+
+static void sbc8240_write32 (struct map_info *map, __u32 d,
+			     unsigned long adr)
+{
+	__raw_writel(d, map->map_priv_1 + adr);
+	mb();
+}
+
+static void sbc8240_write64 (struct map_info *map, __u64 data,
+			     unsigned long adr)
+{
+	unsigned long long tmp;
+	unsigned long flags, msr, saved_msr, *p;
+	volatile long saved_fr[2];
+
+	save_flags(flags);
+	cli();
+
+	/* turn off floating point unavailable exceptions */
+
+	__asm__ __volatile__ (
+		"mfmsr %0" 
+		: "=r" (msr) :);
+
+	saved_msr = msr;
+	msr |= MSR_FP;
+	msr &= ~(MSR_FE0 | MSR_FE1);
+
+	__asm__ __volatile__ (
+		"mtmsr %0\n"
+		"isync\n"
+		: : "r" (msr));
+
+
+	/* write the data via a floating point register */
+
+	tmp = data;
+	p = (unsigned long *) &tmp;
+	adr = map->map_priv_1 + adr;
+	debugk("sbc8240_write64 adr 0x%x data 0x%08x%08x\n", adr, *p, *(p+1));
+
+	__asm__ __volatile__ (
+		"stfd 1,0(%2)\n"
+		"lfd  1,0(%0)\n"
+		"stfd 1,0(%1)\n"
+		"lfd  1,0(%2)\n"
+		: : "r" (p), "r" (adr), "b" (saved_fr)
+		);
+
+	/* restore state */
+
+	__asm__ __volatile__ (
+		"mtmsr %0\n"
+		"isync\n"
+		: : "r" (saved_msr));
+
+	restore_flags(flags);
+}
+
+static void sbc8240_copy_to (struct map_info *map,
+			     unsigned long to, const void *from, ssize_t len)
+{
+	memcpy_toio ((void *) (map->map_priv_1 + to), from, len);
+}
+
+static struct map_info sbc8240_map[2] = {
+	{
+		.name           = "sbc8240 Flash Bank #0",
+		.size           = WINDOW_SIZE0,
+		.buswidth       = BUSWIDTH0,
+		.read8          = sbc8240_read8,
+		.read16         = sbc8240_read16,
+		.read32         = sbc8240_read32,
+		.read64         = sbc8240_read64,
+		.copy_from      = sbc8240_copy_from,
+		.write8         = sbc8240_write8,
+		.write16        = sbc8240_write16,
+		.write32        = sbc8240_write32,
+		.write64        = sbc8240_write64,
+		.copy_to        = sbc8240_copy_to
+	},
+	{
+		.name           = "sbc8240 Flash Bank #1",
+		.size           = WINDOW_SIZE1,
+		.buswidth       = BUSWIDTH1,
+		.read8          = sbc8240_read8,
+		.read16         = sbc8240_read16,
+		.read32         = sbc8240_read32,
+		.read64         = sbc8240_read64,
+		.copy_from      = sbc8240_copy_from,
+		.write8         = sbc8240_write8,
+		.write16        = sbc8240_write16,
+		.write32        = sbc8240_write32,
+		.write64        = sbc8240_write64,
+		.copy_to        = sbc8240_copy_to
+	}
+};
+
+#define NUM_FLASH_BANKS	(sizeof(sbc8240_map) / sizeof(struct map_info))
+
+/*
+ * The following defines the partition layout of SBC8240 boards.
+ *
+ * See include/linux/mtd/partitions.h for definition of the
+ * mtd_partition structure.
+ *
+ * The *_max_flash_size is the maximum possible mapped flash size
+ * which is not necessarily the actual flash size. It must correspond
+ * to the value specified in the mapping definition defined by the
+ * "struct map_desc *_io_desc" for the corresponding machine.
+ */
+
+#ifdef CONFIG_MTD_PARTITIONS
+
+static struct mtd_partition sbc8240_uboot_partitions [] = {
+	/* Bank 0 */
+	{
+		.name =	"U-boot",			/* U-Boot Firmware	*/
+		.offset =	0,
+		.size =	0x00070000,			/*  7 x 64 KiB sectors 	*/
+		.mask_flags = MTD_WRITEABLE,		/*  force read-only	*/
+	},
+	{
+		.name =	"environment",			/* U-Boot environment	*/
+		.offset =	0x00070000,
+		.size =	0x00010000,			/*  1 x 64 KiB sector	*/
+	},
+};
+
+static struct mtd_partition sbc8240_fs_partitions [] = {
+	{
+		.name =	"jffs",				/* JFFS  filesystem	*/
+		.offset =	0,
+		.size =	0x003C0000,			/*  4 * 15 * 64KiB	*/
+	},
+	{
+		.name =	"tmp32",
+		.offset =	0x003C0000,
+		.size =	0x00020000,			/*  4 * 32KiB		*/
+	},
+	{
+		.name =	"tmp8a",
+		.offset =	0x003E0000,
+		.size =	0x00008000,			/*  4 * 8KiB		*/
+	},
+	{
+		.name =	"tmp8b",
+		.offset =	0x003E8000,
+		.size =	0x00008000,			/*  4 * 8KiB		*/
+	},
+	{
+		.name =	"tmp16",
+		.offset =	0x003F0000,
+		.size =	0x00010000,			/*  4 * 16KiB		*/
+	}
+};
+
+#define NB_OF(x) (sizeof (x) / sizeof (x[0]))
+
+/* trivial struct to describe partition information */
+struct mtd_part_def
+{
+	int nums;
+	unsigned char *type;
+	struct mtd_partition* mtd_part;
+};
+
+static struct mtd_info *sbc8240_mtd[NUM_FLASH_BANKS];
+static struct mtd_part_def sbc8240_part_banks[NUM_FLASH_BANKS];
+
+
+#endif	/* CONFIG_MTD_PARTITIONS */
+
+
+int __init init_sbc8240_mtd (void)
+{
+	static struct _cjs {
+		u_long addr;
+		u_long size;
+	} pt[NUM_FLASH_BANKS] = {
+		{
+			.addr = WINDOW_ADDR0,
+			.size = WINDOW_SIZE0
+		},
+		{
+			.addr = WINDOW_ADDR1,
+			.size = WINDOW_SIZE1
+		},
+	};
+
+	int devicesfound = 0;
+	int i;
+
+	for (i = 0; i < NUM_FLASH_BANKS; i++) {
+		printk (KERN_NOTICE MSG_PREFIX
+			"Probing 0x%08lx at 0x%08lx\n", pt[i].size, pt[i].addr);
+
+		sbc8240_map[i].map_priv_1 =
+			(unsigned long) ioremap (pt[i].addr, pt[i].size);
+		if (!sbc8240_map[i].map_priv_1) {
+			printk (MSG_PREFIX "failed to ioremap\n");
+			return -EIO;
+		}
+
+		sbc8240_mtd[i] = do_map_probe("jedec_probe", &sbc8240_map[i]);
+
+		if (sbc8240_mtd[i]) {
+			sbc8240_mtd[i]->module = THIS_MODULE;
+			devicesfound++;
+		}
+	}
+
+	if (!devicesfound) {
+		printk(KERN_NOTICE MSG_PREFIX
+		       "No suppported flash chips found!\n");
+		return -ENXIO;
+	}
+
+#ifdef CONFIG_MTD_PARTITIONS
+	sbc8240_part_banks[0].mtd_part   = sbc8240_uboot_partitions;
+	sbc8240_part_banks[0].type       = "static image";
+	sbc8240_part_banks[0].nums       = NB_OF(sbc8240_uboot_partitions);
+	sbc8240_part_banks[1].mtd_part   = sbc8240_fs_partitions;
+	sbc8240_part_banks[1].type       = "static file system";
+	sbc8240_part_banks[1].nums       = NB_OF(sbc8240_fs_partitions);
+
+	for (i = 0; i < NUM_FLASH_BANKS; i++) {
+
+		if (!sbc8240_mtd[i]) continue;
+		if (sbc8240_part_banks[i].nums == 0) {
+			printk (KERN_NOTICE MSG_PREFIX
+				"No partition info available, registering whole device\n");
+			add_mtd_device(sbc8240_mtd[i]);
+		} else {
+			printk (KERN_NOTICE MSG_PREFIX
+				"Using %s partition definition\n", sbc8240_part_banks[i].mtd_part->name);
+			add_mtd_partitions (sbc8240_mtd[i], 
+					    sbc8240_part_banks[i].mtd_part,
+					    sbc8240_part_banks[i].nums);
+		}
+	}
+#else
+	printk(KERN_NOTICE MSG_PREFIX
+	       "Registering %d flash banks at once\n", devicesfound);
+
+	for (i = 0; i < devicesfound; i++) {
+		add_mtd_device(sbc8240_mtd[i]);
+	}
+#endif	/* CONFIG_MTD_PARTITIONS */
+
+	return devicesfound == 0 ? -ENXIO : 0;
+}
+
+static void __exit cleanup_sbc8240_mtd (void)
+{
+	int i;
+
+	for (i = 0; i < NUM_FLASH_BANKS; i++) {
+		if (sbc8240_mtd[i]) {
+			del_mtd_device (sbc8240_mtd[i]);
+			map_destroy (sbc8240_mtd[i]);
+		}
+		if (sbc8240_map[i].map_priv_1) {
+			iounmap ((void *) sbc8240_map[i].map_priv_1);
+			sbc8240_map[i].map_priv_1 = 0;
+		}
+	}
+}
+
+module_init (init_sbc8240_mtd);
+module_exit (cleanup_sbc8240_mtd);
+
+MODULE_LICENSE ("GPL");
+MODULE_AUTHOR ("Carolyn Smith <carolyn.smith@tektronix.com>");
+MODULE_DESCRIPTION ("MTD map driver for SBC8240 boards");
+
diff -Nur linux-2.4.30/drivers/mtd/maps/sbc_gxx.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/sbc_gxx.c
--- linux-2.4.30/drivers/mtd/maps/sbc_gxx.c	2004-11-17 12:54:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/sbc_gxx.c	2005-05-12 18:01:29.000000000 +0200
@@ -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: sbc_gxx.c,v 1.21 2003/01/24 13:40:14 dwmw2 Exp $
+   $Id: sbc_gxx.c,v 1.26 2003/05/26 08:50:36 dwmw2 Exp $
 
 The SBC-MediaGX / SBC-GXx has up to 16 MiB of 
 Intel StrataFlash (28F320/28F640) in x8 mode.  
@@ -91,14 +91,14 @@
  * single flash device into. If the size if zero we use up to the end of the
  * device. */
 static struct mtd_partition partition_info[]={
-    { name: "SBC-GXx flash boot partition", 
-      offset: 0, 
-      size:   BOOT_PARTITION_SIZE_KiB*1024 },
-    { name: "SBC-GXx flash data partition", 
-      offset: BOOT_PARTITION_SIZE_KiB*1024, 
-      size: (DATA_PARTITION_SIZE_KiB)*1024 },
-    { name: "SBC-GXx flash application partition", 
-      offset: (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 }
+    { .name = "SBC-GXx flash boot partition", 
+      .offset = 0, 
+      .size =   BOOT_PARTITION_SIZE_KiB*1024 },
+    { .name = "SBC-GXx flash data partition", 
+      .offset = BOOT_PARTITION_SIZE_KiB*1024, 
+      .size = (DATA_PARTITION_SIZE_KiB)*1024 },
+    { .name = "SBC-GXx flash application partition", 
+      .offset = (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 }
 };
 
 #define NUM_PARTITIONS 3
@@ -155,7 +155,7 @@
 		sbc_gxx_page(map, from);
 		memcpy_fromio(to, iomapadr + (from & WINDOW_MASK), thislen);
 		spin_unlock(&sbc_gxx_spin);
-		to += thislen;
+		(__u8*)to += thislen;
 		from += thislen;
 		len -= thislen;
 	}
@@ -203,19 +203,20 @@
 }
 
 static struct map_info sbc_gxx_map = {
-	name: "SBC-GXx flash",
-	size: MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount
+	.name = "SBC-GXx flash",
+	.phys = NO_XIP,
+	.size = MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount
 			 of flash so the cfi probe routines find all
 			 the chips */
-	buswidth: 1,
-	read8: sbc_gxx_read8,
-	read16: sbc_gxx_read16,
-	read32: sbc_gxx_read32,
-	copy_from: sbc_gxx_copy_from,
-	write8: sbc_gxx_write8,
-	write16: sbc_gxx_write16,
-	write32: sbc_gxx_write32,
-	copy_to: sbc_gxx_copy_to
+	.buswidth = 1,
+	.read8 = sbc_gxx_read8,
+	.read16 = sbc_gxx_read16,
+	.read32 = sbc_gxx_read32,
+	.copy_from = sbc_gxx_copy_from,
+	.write8 = sbc_gxx_write8,
+	.write16 = sbc_gxx_write16,
+	.write32 = sbc_gxx_write32,
+	.copy_to = sbc_gxx_copy_to
 };
 
 /* MTD device for all of the flash. */
@@ -234,12 +235,6 @@
 
 int __init init_sbc_gxx(void)
 {
-	if (check_region(PAGE_IO,PAGE_IO_SIZE) != 0) {
-		printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n",
-			sbc_gxx_map.name,
-			PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 );
-		return -EAGAIN;
-	}
   	iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
 	if (!iomapadr) {
 		printk( KERN_ERR"%s: failed to ioremap memory region\n",
@@ -247,7 +242,14 @@
 		return -EIO;
 	}
 	
-	request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash" );
+	if (!request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash")) {
+		printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n",
+			sbc_gxx_map.name,
+			PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 );
+		iounmap((void *)iomapadr);
+		return -EAGAIN;
+	}
+		
 	
 	printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n",
 		sbc_gxx_map.name,
@@ -261,7 +263,7 @@
 		return -ENXIO;
 	}
 	
-	all_mtd->module=THIS_MODULE;
+	all_mtd->owner = THIS_MODULE;
 
 	/* Create MTD devices for each partition. */
 	add_mtd_partitions(all_mtd, partition_info, NUM_PARTITIONS );
diff -Nur linux-2.4.30/drivers/mtd/maps/sc520cdp.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/sc520cdp.c
--- linux-2.4.30/drivers/mtd/maps/sc520cdp.c	2002-08-03 02:39:44.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/sc520cdp.c	2005-05-12 18:01:29.000000000 +0200
@@ -16,7 +16,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: sc520cdp.c,v 1.11 2002/03/08 16:34:35 rkaiser Exp $
+ * $Id: sc520cdp.c,v 1.15 2003/05/21 12:45:20 dwmw2 Exp $
  *
  *
  * The SC520CDP is an evaluation board for the Elan SC520 processor available
@@ -29,6 +29,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -84,88 +85,25 @@
 #define WINDOW_SIZE_1	0x00800000
 #define WINDOW_SIZE_2	0x00080000
 
-static __u8 sc520cdp_read8(struct map_info *map, unsigned long ofs)
-{
-	return readb(map->map_priv_1 + ofs);
-}
-
-static __u16 sc520cdp_read16(struct map_info *map, unsigned long ofs)
-{
-	return readw(map->map_priv_1 + ofs);
-}
-
-static __u32 sc520cdp_read32(struct map_info *map, unsigned long ofs)
-{
-	return readl(map->map_priv_1 + ofs);
-}
-
-static void sc520cdp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
-}
-
-static void sc520cdp_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	writeb(d, map->map_priv_1 + adr);
-}
-
-static void sc520cdp_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	writew(d, map->map_priv_1 + adr);
-}
-
-static void sc520cdp_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	writel(d, map->map_priv_1 + adr);
-}
-
-static void sc520cdp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio((void *)(map->map_priv_1 + to), from, len);
-}
 
 static struct map_info sc520cdp_map[] = {
 	{
-		name: "SC520CDP Flash Bank #0",
-		size: WINDOW_SIZE_0,
-		buswidth: 4,
-		read8: sc520cdp_read8,
-		read16: sc520cdp_read16,
-		read32: sc520cdp_read32,
-		copy_from: sc520cdp_copy_from,
-		write8: sc520cdp_write8,
-		write16: sc520cdp_write16,
-		write32: sc520cdp_write32,
-		copy_to: sc520cdp_copy_to,
-		map_priv_2: WINDOW_ADDR_0
+		.name = "SC520CDP Flash Bank #0",
+		.size = WINDOW_SIZE_0,
+		.buswidth = 4,
+		.phys = WINDOW_ADDR_0
 	},
 	{
-		name: "SC520CDP Flash Bank #1",
-		size: WINDOW_SIZE_1,
-		buswidth: 4,
-		read8: sc520cdp_read8,
-		read16: sc520cdp_read16,
-		read32: sc520cdp_read32,
-		copy_from: sc520cdp_copy_from,
-		write8: sc520cdp_write8,
-		write16: sc520cdp_write16,
-		write32: sc520cdp_write32,
-		copy_to: sc520cdp_copy_to,
-		map_priv_2: WINDOW_ADDR_1
+		.name = "SC520CDP Flash Bank #1",
+		.size = WINDOW_SIZE_1,
+		.buswidth = 4,
+		.phys = WINDOW_ADDR_1
 	},
 	{
-		name: "SC520CDP DIL Flash",
-		size: WINDOW_SIZE_2,
-		buswidth: 1,
-		read8: sc520cdp_read8,
-		read16: sc520cdp_read16,
-		read32: sc520cdp_read32,
-		copy_from: sc520cdp_copy_from,
-		write8: sc520cdp_write8,
-		write16: sc520cdp_write16,
-		write32: sc520cdp_write32,
-		copy_to: sc520cdp_copy_to,
-		map_priv_2: WINDOW_ADDR_2
+		.name = "SC520CDP DIL Flash",
+		.size = WINDOW_SIZE_2,
+		.buswidth = 1,
+		.phys = WINDOW_ADDR_2
 	},
 };
 
@@ -255,9 +193,9 @@
 	/* map in SC520's MMCR area */
 	mmcr = (unsigned long *)ioremap_nocache(SC520_MMCR_BASE, SC520_MMCR_EXTENT);
 	if(!mmcr) { /* ioremap_nocache failed: skip the PAR reprogramming */
-		/* force map_priv_2 fields to BIOS defaults: */
+		/* force physical address fields to BIOS defaults: */
 		for(i = 0; i < NUM_FLASH_BANKS; i++)
-			sc520cdp_map[i].map_priv_2 = par_table[i].default_address;
+			sc520cdp_map[i].phys = par_table[i].default_address;
 		return;
 	}
 
@@ -282,7 +220,7 @@
 				sc520cdp_map[i].name);
 			printk(KERN_NOTICE "Trying default address 0x%lx\n",
 				par_table[i].default_address);
-			sc520cdp_map[i].map_priv_2 = par_table[i].default_address;
+			sc520cdp_map[i].phys = par_table[i].default_address;
 		}
 	}
 	iounmap((void *)mmcr);
@@ -300,13 +238,18 @@
 #endif
 
 	for (i = 0; i < NUM_FLASH_BANKS; i++) {
-		printk(KERN_NOTICE "SC520 CDP flash device: %lx at %lx\n", sc520cdp_map[i].size, sc520cdp_map[i].map_priv_2);
-		sc520cdp_map[i].map_priv_1 = (unsigned long)ioremap_nocache(sc520cdp_map[i].map_priv_2, sc520cdp_map[i].size);
+		printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n",
+		       sc520cdp_map[i].size, sc520cdp_map[i].phys);
 
-		if (!sc520cdp_map[i].map_priv_1) {
+		sc520cdp_map[i].virt = (unsigned long)ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size);
+
+		if (!sc520cdp_map[i].virt) {
 			printk("Failed to ioremap_nocache\n");
 			return -EIO;
 		}
+
+		simple_map_init(&sc520cdp_map[i]);
+
 		mymtd[i] = do_map_probe("cfi_probe", &sc520cdp_map[i]);
 		if(!mymtd[i])
 			mymtd[i] = do_map_probe("jedec_probe", &sc520cdp_map[i]);
@@ -314,11 +257,11 @@
 			mymtd[i] = do_map_probe("map_rom", &sc520cdp_map[i]);
 
 		if (mymtd[i]) {
-			mymtd[i]->module = THIS_MODULE;
+			mymtd[i]->owner = THIS_MODULE;
 			++devices_found;
 		}
 		else {
-			iounmap((void *)sc520cdp_map[i].map_priv_1);
+			iounmap((void *)sc520cdp_map[i].virt);
 		}
 	}
 	if(devices_found >= 2) {
@@ -346,9 +289,9 @@
 	for (i = 0; i < NUM_FLASH_BANKS; i++) {
 		if (mymtd[i])
 			map_destroy(mymtd[i]);
-		if (sc520cdp_map[i].map_priv_1) {
-			iounmap((void *)sc520cdp_map[i].map_priv_1);
-			sc520cdp_map[i].map_priv_1 = 0;
+		if (sc520cdp_map[i].virt) {
+			iounmap((void *)sc520cdp_map[i].virt);
+			sc520cdp_map[i].virt = 0;
 		}
 	}
 }
diff -Nur linux-2.4.30/drivers/mtd/maps/scb2_flash.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/scb2_flash.c
--- linux-2.4.30/drivers/mtd/maps/scb2_flash.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/scb2_flash.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,6 +1,6 @@
 /*
  * MTD map driver for BIOS Flash on Intel SCB2 boards
- * $Id: scb2_flash.c,v 1.2 2003/01/24 13:09:56 dwmw2 Exp $
+ * $Id: scb2_flash.c,v 1.6 2003/05/21 12:45:20 dwmw2 Exp $
  * Copyright (C) 2002 Sun Microsystems, Inc.
  * Tim Hockin <thockin@sun.com>
  *
@@ -14,7 +14,7 @@
  * try to request it here, but if it fails, we carry on anyway.
  *
  * This is how the chip is attached, so said the schematic:
- * * a 4 MiB (32 Mb) 16 bit chip
+ * * a 4 MiB (32 Mib) 16 bit chip
  * * a 1 MiB memory region
  * * A20 and A21 pulled up
  * * D8-D15 ignored
@@ -48,6 +48,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -60,65 +61,13 @@
 #define SCB2_ADDR	0xfff00000
 #define SCB2_WINDOW	0x00100000
 
-static __u8 scb2_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-static __u16 scb2_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
-
-static __u32 scb2_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
-
-static void scb2_copy_from(struct map_info *map, void *to,
-				 unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-static void scb2_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
-
-static void scb2_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
-
-static void scb2_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
-
-static void scb2_copy_to(struct map_info *map, unsigned long to,
-			       const void *from, ssize_t len)
-{
-	memcpy_toio(map->map_priv_1 + to, from, len);
-}
 
 static void *scb2_ioaddr;
 static struct mtd_info *scb2_mtd;
 struct map_info scb2_map = {
-	name:      "SCB2 BIOS Flash",
-	size:      0,
-	buswidth:  1,
-	read8:     scb2_read8,
-	read16:    scb2_read16,
-	read32:    scb2_read32,
-	copy_from: scb2_copy_from,
-	write8:    scb2_write8,
-	write16:   scb2_write16,
-	write32:   scb2_write32,
-	copy_to:   scb2_copy_to,
+	.name =      "SCB2 BIOS Flash",
+	.size =      0,
+	.buswidth =  1,
 };
 static int region_fail;
 
@@ -137,6 +86,8 @@
 		return -1;
 	}
 
+	/* I wasn't here. I didn't see. dwmw2. */
+
 	/* the chip is sometimes bigger than the map - what a waste */
 	mtd->size = map->size;
 
@@ -211,9 +162,12 @@
 		return -ENOMEM;
 	}
 
-	scb2_map.map_priv_1 = (unsigned long)scb2_ioaddr;
+	scb2_map.phys = SCB2_ADDR;
+	scb2_map.virt = (unsigned long)scb2_ioaddr;
 	scb2_map.size = SCB2_WINDOW;
 
+	simple_map_init(&scb2_map);
+
 	/* try to find a chip */
 	scb2_mtd = do_map_probe("cfi_probe", &scb2_map);
 
@@ -225,7 +179,7 @@
 		return -ENODEV;
 	}
 
-	scb2_mtd->module = THIS_MODULE;
+	scb2_mtd->owner = THIS_MODULE;
 	if (scb2_fixup_mtd(scb2_mtd) < 0) {
 		del_mtd_device(scb2_mtd);
 		map_destroy(scb2_mtd);
@@ -235,7 +189,7 @@
 		return -ENODEV;
 	}
 
-	printk(KERN_NOTICE MODNAME ": chip size %x at offset %x\n",
+	printk(KERN_NOTICE MODNAME ": chip size 0x%x at offset 0x%x\n",
 	       scb2_mtd->size, SCB2_WINDOW - scb2_mtd->size);
 
 	add_mtd_device(scb2_mtd);
@@ -266,19 +220,19 @@
 
 static struct pci_device_id scb2_flash_pci_ids[] __devinitdata = {
 	{
-	  vendor: PCI_VENDOR_ID_SERVERWORKS,
-	  device: PCI_DEVICE_ID_SERVERWORKS_CSB5,
-	  subvendor: PCI_ANY_ID,
-	  subdevice: PCI_ANY_ID
+	  .vendor = PCI_VENDOR_ID_SERVERWORKS,
+	  .device = PCI_DEVICE_ID_SERVERWORKS_CSB5,
+	  .subvendor = PCI_ANY_ID,
+	  .subdevice = PCI_ANY_ID
 	},
 	{ 0, }
 };
 
 static struct pci_driver scb2_flash_driver = {
-	name:     "Intel SCB2 BIOS Flash",
-	id_table: scb2_flash_pci_ids,
-	probe:    scb2_flash_probe,
-	remove:   __devexit_p(scb2_flash_remove),
+	.name =     "Intel SCB2 BIOS Flash",
+	.id_table = scb2_flash_pci_ids,
+	.probe =    scb2_flash_probe,
+	.remove =   __devexit_p(scb2_flash_remove),
 };
 
 static int __init
diff -Nur linux-2.4.30/drivers/mtd/maps/scx200_docflash.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/scx200_docflash.c
--- linux-2.4.30/drivers/mtd/maps/scx200_docflash.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/scx200_docflash.c	2005-05-12 18:01:29.000000000 +0200
@@ -2,7 +2,7 @@
 
    Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
 
-   $Id: scx200_docflash.c,v 1.1 2003/01/24 13:20:40 dwmw2 Exp $ 
+   $Id: scx200_docflash.c,v 1.5 2003/05/21 12:45:20 dwmw2 Exp $ 
 
    National Semiconductor SCx200 flash mapped with DOCCS
 */
@@ -11,6 +11,7 @@
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
@@ -75,46 +76,9 @@
 #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0]))
 #endif
 
-static __u8 scx200_docflash_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
-
-static __u16 scx200_docflash_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
-
-static void scx200_docflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-static void scx200_docflash_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
-
-static void scx200_docflash_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
-
-static void scx200_docflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio(map->map_priv_1 + to, from, len);
-}
 
 static struct map_info scx200_docflash_map = {
 	.name      = "NatSemi SCx200 DOCCS Flash",
-	.read8     = scx200_docflash_read8,
-	.read16    = scx200_docflash_read16,
-	.copy_from = scx200_docflash_copy_from,
-	.write8    = scx200_docflash_write8,
-	.write16   = scx200_docflash_write16,
-	.copy_to   = scx200_docflash_copy_to
 };
 
 int __init init_scx200_docflash(void)
@@ -213,8 +177,11 @@
 	else
 		scx200_docflash_map.buswidth = 2;
 
-	scx200_docflash_map.map_priv_1 = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size);
-	if (!scx200_docflash_map.map_priv_1) {
+	simple_map_init(&scx200_docflash_map);
+
+	scx200_docflash_map.phys = docmem.start;
+	scx200_docflash_map.virt = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size);
+	if (!scx200_docflash_map.virt) {
 		printk(KERN_ERR NAME ": failed to ioremap the flash\n");
 		release_resource(&docmem);
 		return -EIO;
@@ -223,7 +190,7 @@
 	mymtd = do_map_probe(flashtype, &scx200_docflash_map);
 	if (!mymtd) {
 		printk(KERN_ERR NAME ": unable to detect flash\n");
-		iounmap((void *)scx200_docflash_map.map_priv_1);
+		iounmap((void *)scx200_docflash_map.virt);
 		release_resource(&docmem);
 		return -ENXIO;
 	}
@@ -231,7 +198,7 @@
 	if (size < mymtd->size)
 		printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n");
 
-	mymtd->module = THIS_MODULE;
+	mymtd->owner = THIS_MODULE;
 
 #if PARTITION
 	partition_info[3].offset = mymtd->size-partition_info[3].size;
@@ -253,8 +220,8 @@
 #endif
 		map_destroy(mymtd);
 	}
-	if (scx200_docflash_map.map_priv_1) {
-		iounmap((void *)scx200_docflash_map.map_priv_1);
+	if (scx200_docflash_map.virt) {
+		iounmap((void *)scx200_docflash_map.virt);
 		release_resource(&docmem);
 	}
 }
diff -Nur linux-2.4.30/drivers/mtd/maps/solutionengine.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/solutionengine.c
--- linux-2.4.30/drivers/mtd/maps/solutionengine.c	2002-08-03 02:39:44.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/solutionengine.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: solutionengine.c,v 1.4 2001/11/07 01:20:59 jsiegel Exp $
+ * $Id: solutionengine.c,v 1.11 2004/04/14 21:48:20 dwmw2 Exp $
  *
  * Flash and EPROM on Hitachi Solution Engine and similar boards.
  *
@@ -11,31 +11,13 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
 #include <linux/config.h>
-
-
-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts);
-
-__u32 soleng_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
-
-void soleng_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void soleng_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
+#include <linux/errno.h>
 
 static struct mtd_info *flash_mtd;
 static struct mtd_info *eprom_mtd;
@@ -43,35 +25,33 @@
 static struct mtd_partition *parsed_parts;
 
 struct map_info soleng_eprom_map = {
-	name: "Solution Engine EPROM",
-	size: 0x400000,
-	buswidth: 4,
-	copy_from: soleng_copy_from,
+	.name = "Solution Engine EPROM",
+	.size = 0x400000,
+	.buswidth = 4,
 };
 
 struct map_info soleng_flash_map = {
-	name: "Solution Engine FLASH",
-	size: 0x400000,
-	buswidth: 4,
-	read32: soleng_read32,
-	copy_from: soleng_copy_from,
-	write32: soleng_write32,
+	.name = "Solution Engine FLASH",
+	.size = 0x400000,
+	.buswidth = 4,
 };
 
+static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+
 #ifdef CONFIG_MTD_SUPERH_RESERVE
 static struct mtd_partition superh_se_partitions[] = {
 	/* Reserved for boot code, read-only */
 	{
-		name: "flash_boot",
-		offset: 0x00000000,
-		size: CONFIG_MTD_SUPERH_RESERVE,
-		mask_flags: MTD_WRITEABLE,
+		.name = "flash_boot",
+		.offset = 0x00000000,
+		.size = CONFIG_MTD_SUPERH_RESERVE,
+		.mask_flags = MTD_WRITEABLE,
 	},
 	/* All else is writable (e.g. JFFS) */
 	{
-		name: "Flash FS",
-		offset: MTDPART_OFS_NXTBLK,
-		size: MTDPART_SIZ_FULL,
+		.name = "Flash FS",
+		.offset = MTDPART_OFS_NXTBLK,
+		.size = MTDPART_SIZ_FULL,
 	}
 };
 #endif /* CONFIG_MTD_SUPERH_RESERVE */
@@ -81,16 +61,22 @@
 	int nr_parts = 0;
 
 	/* First probe at offset 0 */
-	soleng_flash_map.map_priv_1 = P2SEGADDR(0);
-	soleng_eprom_map.map_priv_1 = P1SEGADDR(0x01000000);
-
+	soleng_flash_map.phys = 0;
+	soleng_flash_map.virt = P2SEGADDR(0);
+	soleng_eprom_map.phys = 0x01000000;
+	soleng_eprom_map.virt = P1SEGADDR(0x01000000);
+	simple_map_init(&soleng_eprom_map);
+	simple_map_init(&soleng_flash_map);
+	
 	printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n");
 	flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map);
 	if (!flash_mtd) {
 		/* Not there. Try swapping */
 		printk(KERN_NOTICE "Probing for flash chips at 0x01000000:\n");
-		soleng_flash_map.map_priv_1 = P2SEGADDR(0x01000000);
-		soleng_eprom_map.map_priv_1 = P1SEGADDR(0);
+		soleng_flash_map.phys = 0x01000000;
+		soleng_flash_map.virt = P2SEGADDR(0x01000000);
+		soleng_eprom_map.phys = 0;
+		soleng_eprom_map.virt = P1SEGADDR(0);
 		flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map);
 		if (!flash_mtd) {
 			/* Eep. */
@@ -99,25 +85,20 @@
 		}
 	}
 	printk(KERN_NOTICE "Solution Engine: Flash at 0x%08lx, EPROM at 0x%08lx\n",
-	       soleng_flash_map.map_priv_1 & 0x1fffffff,
-	       soleng_eprom_map.map_priv_1 & 0x1fffffff);
-	flash_mtd->module = THIS_MODULE;
+	       soleng_flash_map.phys & 0x1fffffff,
+	       soleng_eprom_map.phys & 0x1fffffff);
+	flash_mtd->owner = THIS_MODULE;
 
 	eprom_mtd = do_map_probe("map_rom", &soleng_eprom_map);
 	if (eprom_mtd) {
-		eprom_mtd->module = THIS_MODULE;
+		eprom_mtd->owner = THIS_MODULE;
 		add_mtd_device(eprom_mtd);
 	}
 
-#ifdef CONFIG_MTD_REDBOOT_PARTS
-	nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts);
-	if (nr_parts > 0)
-		printk(KERN_NOTICE "Found RedBoot partition table.\n");
-	else if (nr_parts < 0)
-		printk(KERN_NOTICE "Error looking for RedBoot partitions.\n");
-#endif /* CONFIG_MTD_REDBOOT_PARTS */
+	nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0);
+
 #if CONFIG_MTD_SUPERH_RESERVE
-	if (nr_parts == 0) {
+	if (nr_parts <= 0) {
 		printk(KERN_NOTICE "Using configured partition at 0x%08x.\n",
 		       CONFIG_MTD_SUPERH_RESERVE);
 		parsed_parts = superh_se_partitions;
diff -Nur linux-2.4.30/drivers/mtd/maps/sun_uflash.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/sun_uflash.c
--- linux-2.4.30/drivers/mtd/maps/sun_uflash.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/sun_uflash.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,4 +1,4 @@
-/* $Id: sun_uflash.c,v 1.4 2001/10/02 15:05:14 dwmw2 Exp $
+/* $Id: sun_uflash.c,v 1.7 2003/05/20 20:59:32 dwmw2 Exp $
  *
  * sun_uflash - Driver implementation for user-programmable flash
  * present on many Sun Microsystems SME boardsets.
@@ -48,60 +48,11 @@
 	struct list_head	list;
 };
 
-__u8 uflash_read8(struct map_info *map, unsigned long ofs)
-{
-	return(__raw_readb(map->map_priv_1 + ofs));
-}
-
-__u16 uflash_read16(struct map_info *map, unsigned long ofs)
-{
-	return(__raw_readw(map->map_priv_1 + ofs));
-}
-
-__u32 uflash_read32(struct map_info *map, unsigned long ofs)
-{
-	return(__raw_readl(map->map_priv_1 + ofs));
-}
-
-void uflash_copy_from(struct map_info *map, void *to, unsigned long from, 
-		      ssize_t len)
-{
-	memcpy_fromio(to, map->map_priv_1 + from, len);
-}
-
-void uflash_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-}
-
-void uflash_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-}
-
-void uflash_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-}
-
-void uflash_copy_to(struct map_info *map, unsigned long to, const void *from,
-		    ssize_t len)
-{
-	memcpy_toio(map->map_priv_1 + to, from, len);
-}
 
 struct map_info uflash_map_templ = {
-		name:		"SUNW,???-????",
-		size:		UFLASH_WINDOW_SIZE,
-		buswidth:	UFLASH_BUSWIDTH,
-		read8:		uflash_read8,
-		read16:		uflash_read16,
-		read32:		uflash_read32,
-		copy_from:	uflash_copy_from,
-		write8:		uflash_write8,
-		write16:	uflash_write16,
-		write32:	uflash_write32,
-		copy_to:	uflash_copy_to
+		.name =		"SUNW,???-????",
+		.size =		UFLASH_WINDOW_SIZE,
+		.buswidth =	UFLASH_BUSWIDTH,
 };
 
 int uflash_devinit(struct linux_ebus_device* edev)
@@ -145,20 +96,22 @@
 	if(0 != pdev->name && 0 < strlen(pdev->name)) {
 		pdev->map.name = pdev->name;
 	}
-
-	pdev->map.map_priv_1 = 
+	pdev->phys = edev->resource[0].start;
+	pdev->virt = 
 		(unsigned long)ioremap_nocache(edev->resource[0].start, pdev->map.size);
-	if(0 == pdev->map.map_priv_1) {
+	if(0 == pdev->map.virt) {
 		printk("%s: failed to map device\n", __FUNCTION__);
 		kfree(pdev->name);
 		kfree(pdev);
 		return(-1);
 	}
 
+	simple_map_init(&pdev->map);
+
 	/* MTD registration */
 	pdev->mtd = do_map_probe("cfi_probe", &pdev->map);
 	if(0 == pdev->mtd) {
-		iounmap((void *)pdev->map.map_priv_1);
+		iounmap((void *)pdev->map.virt);
 		kfree(pdev->name);
 		kfree(pdev);
 		return(-ENXIO);
@@ -166,7 +119,7 @@
 
 	list_add(&pdev->list, &device_list);
 
-	pdev->mtd->module = THIS_MODULE;
+	pdev->mtd->owner = THIS_MODULE;
 
 	add_mtd_device(pdev->mtd);
 	return(0);
@@ -211,9 +164,9 @@
 			del_mtd_device(udev->mtd);
 			map_destroy(udev->mtd);
 		}
-		if(0 != udev->map.map_priv_1) {
-			iounmap((void*)udev->map.map_priv_1);
-			udev->map.map_priv_1 = 0;
+		if(0 != udev->map.virt) {
+			iounmap((void*)udev->map.virt);
+			udev->map.virt = 0;
 		}
 		if(0 != udev->name) {
 			kfree(udev->name);
diff -Nur linux-2.4.30/drivers/mtd/maps/tqm8xxl.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/tqm8xxl.c
--- linux-2.4.30/drivers/mtd/maps/tqm8xxl.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/tqm8xxl.c	2005-05-12 18:01:29.000000000 +0200
@@ -2,7 +2,7 @@
  * Handle mapping of the flash memory access routines 
  * on TQM8xxL based devices.
  *
- * $Id: tqm8xxl.c,v 1.4 2002/06/20 13:41:20 mag Exp $
+ * $Id: tqm8xxl.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $
  *
  * based on rpxlite.c
  *
@@ -26,6 +26,7 @@
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
+#include <linux/init.h>
 #include <asm/io.h>
 
 #include <linux/mtd/mtd.h>
@@ -51,46 +52,6 @@
 static unsigned long num_banks;
 static unsigned long start_scan_addr;
 
-__u8 tqm8xxl_read8(struct map_info *map, unsigned long ofs)
-{
-	return *((__u8 *)(map->map_priv_1 + ofs));
-}
-
-__u16 tqm8xxl_read16(struct map_info *map, unsigned long ofs)
-{
-	return *((__u16 *)(map->map_priv_1 + ofs));
-}
-
-__u32 tqm8xxl_read32(struct map_info *map, unsigned long ofs)
-{
-	return *((__u32 *)(map->map_priv_1 + ofs));
-}
-
-void tqm8xxl_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy_fromio(to, (void *)(map->map_priv_1 + from), len);
-}
-
-void tqm8xxl_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	*((__u8 *)(map->map_priv_1 + adr)) = d;
-}
-
-void tqm8xxl_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	*((__u16 *)( map->map_priv_1 + adr)) = d;
-}
-
-void tqm8xxl_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	*((__u32 *)(map->map_priv_1 + adr)) = d;
-}
-
-void tqm8xxl_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy_toio((void *)(map->map_priv_1 + to), from, len);
-}
-
 /*
  * Here are partition information for all known TQM8xxL series devices.
  * See include/linux/mtd/partitions.h for definition of the mtd_partition
@@ -107,50 +68,48 @@
 static unsigned long tqm8xxl_max_flash_size = 0x00800000;
 
 /* partition definition for first flash bank
- * also ref. to "drivers\char\flash_config.c" 
+ * (cf. "drivers/char/flash_config.c")
  */
 static struct mtd_partition tqm8xxl_partitions[] = {
 	{
-	  name: "ppcboot",
-	  offset: 0x00000000,
-	  size: 0x00020000,           /* 128KB           */
-	  mask_flags: MTD_WRITEABLE,  /* force read-only */
+	  .name = "ppcboot",
+	  .offset = 0x00000000,
+	  .size = 0x00020000,           /* 128KB           */
+	  .mask_flags = MTD_WRITEABLE,  /* force read-only */
 	},
 	{
-	  name: "kernel",             /* default kernel image */
-	  offset: 0x00020000,
-	  size: 0x000e0000,
-	  mask_flags: MTD_WRITEABLE,  /* force read-only */
+	  .name = "kernel",             /* default kernel image */
+	  .offset = 0x00020000,
+	  .size = 0x000e0000,
+	  .mask_flags = MTD_WRITEABLE,  /* force read-only */
 	},
 	{
-	  name: "user",
-	  offset: 0x00100000,
-	  size: 0x00100000,
+	  .name = "user",
+	  .offset = 0x00100000,
+	  .size = 0x00100000,
 	},
 	{
-	  name: "initrd",
-	  offset: 0x00200000,
-	  size: 0x00200000,
+	  .name = "initrd",
+	  .offset = 0x00200000,
+	  .size = 0x00200000,
 	}
 };
-/* partition definition for second flahs bank */
+/* partition definition for second flash bank */
 static struct mtd_partition tqm8xxl_fs_partitions[] = {
 	{
-	  name: "cramfs",
-	  offset: 0x00000000,
-	  size: 0x00200000,
+	  .name = "cramfs",
+	  .offset = 0x00000000,
+	  .size = 0x00200000,
 	},
 	{
-	  name: "jffs",
-	  offset: 0x00200000,
-	  size: 0x00200000,
-	  //size: MTDPART_SIZ_FULL,
+	  .name = "jffs",
+	  .offset = 0x00200000,
+	  .size = 0x00200000,
+	  .//size = MTDPART_SIZ_FULL,
 	}
 };
 #endif
 
-#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
-
 int __init init_tqm_mtd(void)
 {
 	int idx = 0, ret = 0;
@@ -160,67 +119,73 @@
 
 	flash_addr = bd->bi_flashstart;
 	flash_size = bd->bi_flashsize;
-	//request maximum flash size address spzce
+
+	//request maximum flash size address space
 	start_scan_addr = (unsigned long)ioremap(flash_addr, flash_size);
 	if (!start_scan_addr) {
-		//printk("%s:Failed to ioremap address:0x%x\n", __FUNCTION__, FLASH_ADDR);
-		printk("%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr);
+		printk(KERN_WARNING "%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr);
 		return -EIO;
 	}
-	for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++)
-	{
+
+	for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
 		if(mtd_size >= flash_size)
 			break;
 		
-		printk("%s: chip probing count %d\n", __FUNCTION__, idx);
+		printk(KERN_INFO "%s: chip probing count %d\n", __FUNCTION__, idx);
 		
 		map_banks[idx] = (struct map_info *)kmalloc(sizeof(struct map_info), GFP_KERNEL);
-		if(map_banks[idx] == NULL)
-		{
-			//return -ENOMEM;
+		if(map_banks[idx] == NULL) {
 			ret = -ENOMEM;
+			/* FIXME: What if some MTD devices were probed already? */
 			goto error_mem;
 		}
+
 		memset((void *)map_banks[idx], 0, sizeof(struct map_info));
 		map_banks[idx]->name = (char *)kmalloc(16, GFP_KERNEL);
-		if(map_banks[idx]->name == NULL)
-		{
-			//return -ENOMEM;
+
+		if (!map_banks[idx]->name) {
 			ret = -ENOMEM;
+			/* FIXME: What if some MTD devices were probed already? */
 			goto error_mem;
 		}
-		memset((void *)map_banks[idx]->name, 0, 16);
-
 		sprintf(map_banks[idx]->name, "TQM8xxL%d", idx);
+
 		map_banks[idx]->size = flash_size;
 		map_banks[idx]->buswidth = 4;
-		map_banks[idx]->read8 = tqm8xxl_read8;
-		map_banks[idx]->read16 = tqm8xxl_read16;
-		map_banks[idx]->read32 = tqm8xxl_read32;
-		map_banks[idx]->copy_from = tqm8xxl_copy_from;
-		map_banks[idx]->write8 = tqm8xxl_write8;
-		map_banks[idx]->write16 = tqm8xxl_write16;
-		map_banks[idx]->write32 = tqm8xxl_write32;
-		map_banks[idx]->copy_to = tqm8xxl_copy_to;
-		map_banks[idx]->map_priv_1 = 
-		start_scan_addr + ((idx > 0) ? 
+
+		simple_map_init(map_banks[idx]);
+
+		map_banks[idx]->virt = start_scan_addr;
+		map_banks[idx]->phys = flash_addr;
+		/* FIXME: This looks utterly bogus, but I'm trying to
+		   preserve the behaviour of the original (shown here)...
+
+		map_banks[idx]->map_priv_1 =
+		start_scan_addr + ((idx > 0) ?
 		(mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0);
+		*/
+
+		if (idx && mtd_banks[idx-1]) {
+			map_banks[idx]->virt += mtd_banks[idx-1]->size;
+			map_banks[idx]->phys += mtd_banks[idx-1]->size;
+		}
+
 		//start to probe flash chips
 		mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]);
-		if(mtd_banks[idx])
-		{
-			mtd_banks[idx]->module = THIS_MODULE;
+
+		if (mtd_banks[idx]) {
+			mtd_banks[idx]->owner = THIS_MODULE;
 			mtd_size += mtd_banks[idx]->size;
 			num_banks++;
-			printk("%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks, 
+
+			printk(KERN_INFO "%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks, 
 			mtd_banks[idx]->name, mtd_banks[idx]->size);
 		}
 	}
 
 	/* no supported flash chips found */
-	if(!num_banks)
-	{
-		printk("TQM8xxL: No support flash chips found!\n");
+	if (!num_banks) {
+		printk(KERN_NOTICE "TQM8xxL: No support flash chips found!\n");
 		ret = -ENXIO;
 		goto error_mem;
 	}
@@ -231,12 +196,13 @@
 	 */
 	part_banks[0].mtd_part = tqm8xxl_partitions;
 	part_banks[0].type = "Static image";
-	part_banks[0].nums = NB_OF(tqm8xxl_partitions);
+	part_banks[0].nums = ARRAY_SIZE(tqm8xxl_partitions);
+
 	part_banks[1].mtd_part = tqm8xxl_fs_partitions;
 	part_banks[1].type = "Static file system";
-	part_banks[1].nums = NB_OF(tqm8xxl_fs_partitions);
-	for(idx = 0; idx < num_banks ; idx++)
-	{
+	part_banks[1].nums = ARRAY_SIZE(tqm8xxl_fs_partitions);
+
+	for(idx = 0; idx < num_banks ; idx++) {
 		if (part_banks[idx].nums == 0) {
 			printk(KERN_NOTICE "TQM flash%d: no partition info available, registering whole flash at once\n", idx);
 			add_mtd_device(mtd_banks[idx]);
@@ -254,12 +220,9 @@
 #endif
 	return 0;
 error_mem:
-	for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++)
-	{
-		if(map_banks[idx] != NULL)
-		{
-			if(map_banks[idx]->name != NULL)
-			{
+	for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) {
+		if(map_banks[idx] != NULL) {
+			if(map_banks[idx]->name != NULL) {
 				kfree(map_banks[idx]->name);
 				map_banks[idx]->name = NULL;
 			}
@@ -267,18 +230,15 @@
 			map_banks[idx] = NULL;
 		}
 	}
-	//return -ENOMEM;
 error:
 	iounmap((void *)start_scan_addr);
-	//return -ENXIO;
 	return ret;
 }
 
 static void __exit cleanup_tqm_mtd(void)
 {
 	unsigned int idx = 0;
-	for(idx = 0 ; idx < num_banks ; idx++)
-	{
+	for(idx = 0 ; idx < num_banks ; idx++) {
 		/* destroy mtd_info previously allocated */
 		if (mtd_banks[idx]) {
 			del_mtd_partitions(mtd_banks[idx]);
@@ -288,6 +248,7 @@
 		kfree(map_banks[idx]->name);
 		kfree(map_banks[idx]);
 	}
+
 	if (start_scan_addr) {
 		iounmap((void *)start_scan_addr);
 		start_scan_addr = 0;
diff -Nur linux-2.4.30/drivers/mtd/maps/tsunami_flash.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/tsunami_flash.c
--- linux-2.4.30/drivers/mtd/maps/tsunami_flash.c	2002-08-03 02:39:44.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/tsunami_flash.c	2005-05-12 18:01:29.000000000 +0200
@@ -2,11 +2,13 @@
  * tsunami_flash.c
  *
  * flash chip on alpha ds10...
- * $Id: tsunami_flash.c,v 1.1 2002/01/10 22:59:13 eric Exp $
+ * $Id: tsunami_flash.c,v 1.6 2003/05/21 15:15:08 dwmw2 Exp $
  */
 #include <asm/io.h>
 #include <asm/core_tsunami.h>
+#include <linux/init.h>
 #include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
 
 #define FLASH_ENABLE_PORT 0x00C00001
 #define FLASH_ENABLE_BYTE 0x01
@@ -58,18 +60,12 @@
 static struct map_info tsunami_flash_map = {
 	.name = "flash chip on the Tsunami TIG bus",
 	.size = MAX_TIG_FLASH_SIZE,
+	.phys = NO_XIP;
 	.buswidth = 1,
 	.read8 = tsunami_flash_read8,
-	.read16 = 0,
-	.read32 = 0, 
 	.copy_from = tsunami_flash_copy_from,
 	.write8 = tsunami_flash_write8,
-	.write16 = 0,
-	.write32 = 0,
 	.copy_to = tsunami_flash_copy_to,
-	.set_vpp = 0,
-	.map_priv_1 = 0,
-
 };
 
 static struct mtd_info *tsunami_flash_mtd;
@@ -99,7 +95,7 @@
 		tsunami_flash_mtd = do_map_probe(*type, &tsunami_flash_map);
 	}
 	if (tsunami_flash_mtd) {
-		tsunami_flash_mtd->module = THIS_MODULE;
+		tsunami_flash_mtd->owner = THIS_MODULE;
 		add_mtd_device(tsunami_flash_mtd);
 		return 0;
 	}
diff -Nur linux-2.4.30/drivers/mtd/maps/uclinux.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/uclinux.c
--- linux-2.4.30/drivers/mtd/maps/uclinux.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/uclinux.c	2005-05-12 18:01:29.000000000 +0200
@@ -5,7 +5,7 @@
  *
  *	(C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
  *
- * 	$Id: uclinux.c,v 1.2 2002/08/07 00:43:45 gerg Exp $
+ * 	$Id: uclinux.c,v 1.5 2003/05/20 20:59:32 dwmw2 Exp $
  */
 
 /****************************************************************************/
@@ -24,58 +24,11 @@
 
 /****************************************************************************/
 
-__u8 uclinux_read8(struct map_info *map, unsigned long ofs)
-{
-	return(*((__u8 *) (map->map_priv_1 + ofs)));
-}
-
-__u16 uclinux_read16(struct map_info *map, unsigned long ofs)
-{
-	return(*((__u16 *) (map->map_priv_1 + ofs)));
-}
-
-__u32 uclinux_read32(struct map_info *map, unsigned long ofs)
-{
-	return(*((__u32 *) (map->map_priv_1 + ofs)));
-}
-
-void uclinux_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy(to, (void *)(map->map_priv_1 + from), len);
-}
-
-void uclinux_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	*((__u8 *) (map->map_priv_1 + adr)) = d;
-}
-
-void uclinux_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	*((__u16 *) (map->map_priv_1 + adr)) = d;
-}
-
-void uclinux_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	*((__u32 *) (map->map_priv_1 + adr)) = d;
-}
-
-void uclinux_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
-{
-	memcpy((void *) (map->map_priv_1 + to), from, len);
-}
 
 /****************************************************************************/
 
 struct map_info uclinux_ram_map = {
-	name:		"RAM",
-	read8:		uclinux_read8,
-	read16:		uclinux_read16,
-	read32:		uclinux_read32,
-	copy_from:	uclinux_copy_from,
-	write8:		uclinux_write8,
-	write16:	uclinux_write16,
-	write32:	uclinux_write32,
-	copy_to:	uclinux_copy_to,
+	.name = "RAM",
 };
 
 struct mtd_info *uclinux_ram_mtdinfo;
@@ -83,7 +36,7 @@
 /****************************************************************************/
 
 struct mtd_partition uclinux_romfs[] = {
-	{ name: "ROMfs", offset: 0 }
+	{ .name = "ROMfs" }
 };
 
 #define	NUM_PARTITIONS	(sizeof(uclinux_romfs) / sizeof(uclinux_romfs[0]))
@@ -94,7 +47,7 @@
 	size_t *retlen, u_char **mtdbuf)
 {
 	struct map_info *map = (struct map_info *) mtd->priv;
-	*mtdbuf = (u_char *) (map->map_priv_1 + ((int) from));
+	*mtdbuf = (u_char *) (map->virt + ((int) from));
 	*retlen = len;
 	return(0);
 }
@@ -108,29 +61,31 @@
 	extern char _ebss;
 
 	mapp = &uclinux_ram_map;
-	mapp->map_priv_2 = (unsigned long) &_ebss;
+	mapp->phys = (unsigned long) &_ebss;
 	mapp->size = PAGE_ALIGN(*((unsigned long *)((&_ebss) + 8)));
 	mapp->buswidth = 4;
 
 	printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n",
 	       	(int) mapp->map_priv_2, (int) mapp->size);
 
-	mapp->map_priv_1 = (unsigned long)
-		ioremap_nocache(mapp->map_priv_2, mapp->size);
+	mapp->virt = (unsigned long)
+		ioremap_nocache(mapp->phys, mapp->size);
 
-	if (mapp->map_priv_1 == 0) {
+	if (mapp->virt == 0) {
 		printk("uclinux[mtd]: ioremap_nocache() failed\n");
 		return(-EIO);
 	}
 
+	simple_map_init(mapp);
+
 	mtd = do_map_probe("map_ram", mapp);
 	if (!mtd) {
 		printk("uclinux[mtd]: failed to find a mapping?\n");
-		iounmap((void *) mapp->map_priv_1);
+		iounmap((void *) mapp->virt);
 		return(-ENXIO);
 	}
 		
-	mtd->module = THIS_MODULE;
+	mtd->owner = THIS_MODULE;
 	mtd->point = uclinux_point;
 	mtd->priv = mapp;
 
@@ -155,8 +110,8 @@
 		uclinux_ram_mtdinfo = NULL;
 	}
 	if (uclinux_ram_map.map_priv_1) {
-		iounmap((void *) uclinux_ram_map.map_priv_1);
-		uclinux_ram_map.map_priv_1 = 0;
+		iounmap((void *) uclinux_ram_map.virt);
+		uclinux_ram_map.virt = 0;
 	}
 }
 
diff -Nur linux-2.4.30/drivers/mtd/maps/vmax301.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/vmax301.c
--- linux-2.4.30/drivers/mtd/maps/vmax301.c	2001-10-05 00:14:59.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/vmax301.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,4 +1,4 @@
-// $Id: vmax301.c,v 1.24 2001/10/02 15:05:14 dwmw2 Exp $
+// $Id: vmax301.c,v 1.28 2003/05/21 15:15:08 dwmw2 Exp $
 /* ######################################################################
 
    Tempustech VMAX SBC301 MTD Driver.
@@ -24,6 +24,7 @@
 #include <asm/io.h>
 
 #include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
 
 
 #define WINDOW_START 0xd8000
@@ -142,34 +143,36 @@
 
 static struct map_info vmax_map[2] = {
 	{
-		name: "VMAX301 Internal Flash",
-		size: 3*2*1024*1024,
-		buswidth: 1,
-		read8: vmax301_read8,
-		read16: vmax301_read16,
-		read32: vmax301_read32,
-		copy_from: vmax301_copy_from,
-		write8: vmax301_write8,
-		write16: vmax301_write16,
-		write32: vmax301_write32,
-		copy_to: vmax301_copy_to,
-		map_priv_1: WINDOW_START + WINDOW_LENGTH,
-		map_priv_2: 0xFFFFFFFF
+		.name = "VMAX301 Internal Flash",
+		.phys = NO_XIP,
+		.size = 3*2*1024*1024,
+		.buswidth = 1,
+		.read8 = vmax301_read8,
+		.read16 = vmax301_read16,
+		.read32 = vmax301_read32,
+		.copy_from = vmax301_copy_from,
+		.write8 = vmax301_write8,
+		.write16 = vmax301_write16,
+		.write32 = vmax301_write32,
+		.copy_to = vmax301_copy_to,
+		.map_priv_1 = WINDOW_START + WINDOW_LENGTH,
+		.map_priv_2 = 0xFFFFFFFF
 	},
 	{
-		name: "VMAX301 Socket",
-		size: 0,
-		buswidth: 1,
-		read8: vmax301_read8,
-		read16: vmax301_read16,
-		read32: vmax301_read32,
-		copy_from: vmax301_copy_from,
-		write8: vmax301_write8,
-		write16: vmax301_write16,
-		write32: vmax301_write32,
-		copy_to: vmax301_copy_to,
-		map_priv_1: WINDOW_START + (3*WINDOW_LENGTH),
-		map_priv_2: 0xFFFFFFFF
+		.name = "VMAX301 Socket",
+		.phys = NO_XIP,
+		.size = 0,
+		.buswidth = 1,
+		.read8 = vmax301_read8,
+		.read16 = vmax301_read16,
+		.read32 = vmax301_read32,
+		.copy_from = vmax301_copy_from,
+		.write8 = vmax301_write8,
+		.write16 = vmax301_write16,
+		.write32 = vmax301_write32,
+		.copy_to = vmax301_copy_to,
+		.map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH),
+		.map_priv_2 = 0xFFFFFFFF
 	}
 };
 
@@ -206,8 +209,8 @@
 	   address of the first half, because it's used more
 	   often. 
 	*/
-	vmax_map[0].map_priv_1 = iomapadr + WINDOW_START;
-	vmax_map[1].map_priv_1 = iomapadr + (3*WINDOW_START);
+	vmax_map[0].map_priv_2 = iomapadr + WINDOW_START;
+	vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START);
 	
 	for (i=0; i<2; i++) {
 		vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]);
@@ -218,7 +221,7 @@
 		if (!vmax_mtd[i])
 			vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]);
 		if (vmax_mtd[i]) {
-			vmax_mtd[i]->module = THIS_MODULE;
+			vmax_mtd[i]->owner = THIS_MODULE;
 			add_mtd_device(vmax_mtd[i]);
 		}
 	}
diff -Nur linux-2.4.30/drivers/mtd/maps/xxs1500.c linux-2.4.30-linux-mips.org/drivers/mtd/maps/xxs1500.c
--- linux-2.4.30/drivers/mtd/maps/xxs1500.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/maps/xxs1500.c	2005-05-12 17:57:26.000000000 +0200
@@ -0,0 +1,186 @@
+/*
+ * Flash memory access on MyCable XXS1500 board
+ * 
+ * (C) 2003 Pete Popov <ppopov@mvista.com>
+ * 
+ * $Id: xxs1500.c,v 1.1.2.2 2003/08/02 02:06:01 ppopov Exp $
+ */
+
+#include <linux/config.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
+
+#ifdef CONFIG_MIPS_XXS1500
+#define WINDOW_ADDR 0x1F000000
+#define WINDOW_SIZE 0x1000000
+#endif
+
+__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);
+}
+
+
+
+static struct map_info xxs1500_map = {
+	name:		"XXS1500 flash",
+	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,
+};
+
+
+static unsigned long flash_size = 0x00800000;
+static unsigned char flash_buswidth = 4;
+static struct mtd_partition xxs1500_partitions[] = {
+        {
+                name: "kernel image",
+                size: 0x00200000,
+                offset: 0,
+        },{
+                name: "user fs 0",
+                size: (0x00C00000-0x200000),
+                offset: MTDPART_OFS_APPEND,
+        },{
+                name: "yamon",
+                size: 0x00100000,
+                offset: MTDPART_OFS_APPEND,
+                mask_flags: MTD_WRITEABLE
+        },{
+                name: "user fs 1",
+                size: 0x2c0000,
+                offset: MTDPART_OFS_APPEND,
+        },{
+                name: "yamon env vars",
+                size: 0x040000,
+                offset: MTDPART_OFS_APPEND,
+                mask_flags: MTD_WRITEABLE
+        }
+};
+
+
+#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
+
+static struct mtd_partition *parsed_parts;
+static struct mtd_info *mymtd;
+
+int __init xxs1500_mtd_init(void)
+{
+	struct mtd_partition *parts;
+	int nb_parts = 0;
+	char *part_type;
+	
+	/* Default flash buswidth */
+	xxs1500_map.buswidth = flash_buswidth;
+
+	/*
+	 * Static partition definition selection
+	 */
+	part_type = "static";
+	parts = xxs1500_partitions;
+	nb_parts = NB_OF(xxs1500_partitions);
+	xxs1500_map.size = flash_size;
+
+	/*
+	 * Now let's probe for the actual flash.  Do it here since
+	 * specific machine settings might have been set above.
+	 */
+	printk(KERN_NOTICE "XXS1500 flash: probing %d-bit flash bus\n", 
+			xxs1500_map.buswidth*8);
+	xxs1500_map.map_priv_1 = 
+		(unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+	mymtd = do_map_probe("cfi_probe", &xxs1500_map);
+	if (!mymtd) return -ENXIO;
+	mymtd->module = THIS_MODULE;
+
+	add_mtd_partitions(mymtd, parts, nb_parts);
+	return 0;
+}
+
+static void __exit xxs1500_mtd_cleanup(void)
+{
+	if (mymtd) {
+		del_mtd_partitions(mymtd);
+		map_destroy(mymtd);
+		if (parsed_parts)
+			kfree(parsed_parts);
+	}
+}
+
+module_init(xxs1500_mtd_init);
+module_exit(xxs1500_mtd_cleanup);
+
+MODULE_AUTHOR("Pete Popov");
+MODULE_DESCRIPTION("XXS1500 CFI map driver");
+MODULE_LICENSE("GPL");
diff -Nur linux-2.4.30/drivers/mtd/mtd_blkdevs-24.c linux-2.4.30-linux-mips.org/drivers/mtd/mtd_blkdevs-24.c
--- linux-2.4.30/drivers/mtd/mtd_blkdevs-24.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/mtd_blkdevs-24.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,699 @@
+/*
+ * $Id: mtd_blkdevs-24.c,v 1.15 2003/10/10 08:55:03 dwmw2 Exp $
+ *
+ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Interface to Linux 2.4 block layer for MTD 'translation layers'.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/mtd/blktrans.h>
+#include <linux/mtd/mtd.h>
+#include <linux/blkdev.h>
+#include <linux/blk.h>
+#include <linux/blkpg.h>
+#include <linux/spinlock.h>
+#include <linux/hdreg.h>
+#include <linux/init.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+static LIST_HEAD(blktrans_majors);
+
+extern struct semaphore mtd_table_mutex;
+extern struct mtd_info *mtd_table[];
+
+struct mtd_blkcore_priv {
+	devfs_handle_t devfs_dir_handle;
+	int blksizes[256];
+	int sizes[256];
+	struct hd_struct part_table[256];
+	struct gendisk gd;
+	spinlock_t devs_lock; /* See comment in _request function */
+	struct completion thread_dead;
+	int exiting;
+	wait_queue_head_t thread_wq;
+};
+
+static inline struct mtd_blktrans_dev *tr_get_dev(struct mtd_blktrans_ops *tr,
+					   int devnum)
+{
+	struct list_head *this;
+	struct mtd_blktrans_dev *d;
+
+	list_for_each(this, &tr->devs) {
+		d = list_entry(this, struct mtd_blktrans_dev, list);
+
+		if (d->devnum == devnum)
+			return d;
+	}
+	return NULL;
+}
+
+static inline struct mtd_blktrans_ops *get_tr(int major)
+{
+	struct list_head *this;
+	struct mtd_blktrans_ops *t;
+
+	list_for_each(this, &blktrans_majors) {
+		t = list_entry(this, struct mtd_blktrans_ops, list);
+
+		if (t->major == major)
+			return t;
+	}
+	return NULL;
+}
+
+static int do_blktrans_request(struct mtd_blktrans_ops *tr,
+			       struct mtd_blktrans_dev *dev,
+			       struct request *req)
+{
+	unsigned long block, nsect;
+	char *buf;
+	int minor;
+
+	minor = MINOR(req->rq_dev);
+	block = req->sector;
+	nsect = req->current_nr_sectors;
+	buf = req->buffer;
+
+	if (block + nsect > tr->blkcore_priv->part_table[minor].nr_sects) {
+		printk(KERN_WARNING "Access beyond end of device.\n");
+		return 0;
+	}
+	block += tr->blkcore_priv->part_table[minor].start_sect;
+
+	switch(req->cmd) {
+	case READ:
+		for (; nsect > 0; nsect--, block++, buf += 512)
+			if (tr->readsect(dev, block, buf))
+				return 0;
+		return 1;
+
+	case WRITE:
+		if (!tr->writesect)
+			return 0;
+
+		for (; nsect > 0; nsect--, block++, buf += 512)
+			if (tr->writesect(dev, block, buf))
+				return 0;
+		return 1;
+
+	default:
+		printk(KERN_NOTICE "Unknown request cmd %d\n", req->cmd);
+		return 0;
+	}
+}
+
+static int mtd_blktrans_thread(void *arg)
+{
+	struct mtd_blktrans_ops *tr = arg;
+	struct request_queue *rq = BLK_DEFAULT_QUEUE(tr->major);
+
+	/* we might get involved when memory gets low, so use PF_MEMALLOC */
+	current->flags |= PF_MEMALLOC;
+
+	snprintf(current->comm, sizeof(current->comm), "%sd", tr->name);
+
+	/* daemonize() doesn't do this for us since some kernel threads
+	   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();
+
+	while (!tr->blkcore_priv->exiting) {
+		struct request *req;
+		struct mtd_blktrans_dev *dev;
+		int devnum;
+		int res = 0;
+		DECLARE_WAITQUEUE(wait, current);
+
+		spin_lock_irq(&io_request_lock);
+
+		if (list_empty(&rq->queue_head)) {
+
+			add_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
+			set_current_state(TASK_INTERRUPTIBLE);
+
+			spin_unlock_irq(&io_request_lock);
+
+			schedule();
+			remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
+
+			continue;
+		}
+
+		req = blkdev_entry_next_request(&rq->queue_head);
+
+		devnum = MINOR(req->rq_dev) >> tr->part_bits;
+
+		/* The ll_rw_blk code knows not to touch the request
+		   at the head of the queue */
+		spin_unlock_irq(&io_request_lock);
+
+		/* FIXME: Where can we store the dev, on which
+		   we already have a refcount anyway? We need to
+		   lock against concurrent addition/removal of devices,
+		   but if we use the mtd_table_mutex we deadlock when
+		   grok_partitions is called from the registration
+		   callbacks. */
+		spin_lock(&tr->blkcore_priv->devs_lock);
+		dev = tr_get_dev(tr, devnum);
+		spin_unlock(&tr->blkcore_priv->devs_lock);
+
+		BUG_ON(!dev);
+
+		/* Ensure serialisation of requests */
+		down(&dev->sem);
+
+		res = do_blktrans_request(tr, dev, req);
+		up(&dev->sem);
+
+		if (!end_that_request_first(req, res, tr->name)) {
+			spin_lock_irq(&io_request_lock);
+			blkdev_dequeue_request(req);
+			end_that_request_last(req);
+			spin_unlock_irq(&io_request_lock);
+		}
+	}
+	complete_and_exit(&tr->blkcore_priv->thread_dead, 0);
+}
+
+static void mtd_blktrans_request(struct request_queue *rq)
+{
+	struct mtd_blktrans_ops *tr = rq->queuedata;
+	wake_up(&tr->blkcore_priv->thread_wq);
+}
+
+int blktrans_open(struct inode *i, struct file *f)
+{
+	struct mtd_blktrans_ops *tr = NULL;
+	struct mtd_blktrans_dev *dev = NULL;
+	int major_nr = MAJOR(i->i_rdev);
+	int minor_nr = MINOR(i->i_rdev);
+	int devnum;
+	int ret = -ENODEV;
+
+	if (is_read_only(i->i_rdev) && (f->f_mode & FMODE_WRITE))
+		return -EROFS;
+
+	down(&mtd_table_mutex);
+
+	tr = get_tr(major_nr);
+
+	if (!tr)
+		goto out;
+ 
+	devnum = minor_nr >> tr->part_bits;
+
+	dev = tr_get_dev(tr, devnum);
+
+	if (!dev)
+		goto out;
+
+	if (!tr->blkcore_priv->part_table[minor_nr].nr_sects) {
+		ret = -ENODEV;
+		goto out;
+	}
+
+	if (!try_inc_mod_count(dev->mtd->owner))
+		goto out;
+
+	if (!try_inc_mod_count(tr->owner))
+		goto out_tr;
+
+	dev->mtd->usecount++;
+
+	ret = 0;
+	if (tr->open && (ret = tr->open(dev))) {
+		dev->mtd->usecount--;
+		if (dev->mtd->owner)
+			__MOD_DEC_USE_COUNT(dev->mtd->owner);
+	out_tr:
+		if (tr->owner)
+			__MOD_DEC_USE_COUNT(tr->owner);
+	}
+ out:
+	up(&mtd_table_mutex);
+
+	return ret;
+}
+
+int blktrans_release(struct inode *i, struct file *f)
+{
+	struct mtd_blktrans_dev *dev;
+	struct mtd_blktrans_ops *tr;
+	int ret = 0;
+	int devnum;
+
+	down(&mtd_table_mutex);
+
+	tr = get_tr(MAJOR(i->i_rdev));
+	if (!tr) {
+		up(&mtd_table_mutex);
+		return -ENODEV;
+	}
+
+	devnum = MINOR(i->i_rdev) >> tr->part_bits;
+	dev = tr_get_dev(tr, devnum);
+
+	if (!dev) {
+		up(&mtd_table_mutex);
+		return -ENODEV;
+	}
+
+	if (tr->release)
+		ret = tr->release(dev);
+
+	if (!ret) {
+		dev->mtd->usecount--;
+		if (dev->mtd->owner)
+			__MOD_DEC_USE_COUNT(dev->mtd->owner);
+		if (tr->owner)
+			__MOD_DEC_USE_COUNT(tr->owner);
+	}
+	
+	up(&mtd_table_mutex);
+
+	return ret;
+}
+
+static int mtd_blktrans_rrpart(kdev_t rdev, struct mtd_blktrans_ops *tr,
+			       struct mtd_blktrans_dev *dev)
+{
+	struct gendisk *gd = &(tr->blkcore_priv->gd);
+	int i;
+	int minor = MINOR(rdev);
+
+	if (minor & ((1<<tr->part_bits)-1) || !tr->part_bits) {
+		/* BLKRRPART on a partition. Go away. */
+		return -ENOTTY;
+	}
+
+	if (!capable(CAP_SYS_ADMIN))
+	    return -EACCES;
+
+	/* We are required to prevent simultaneous open() ourselves.
+	   The core doesn't do that for us. Did I ever mention how
+	   much the Linux block layer sucks? Sledgehammer approach... */
+	down(&mtd_table_mutex);
+
+	for (i=0; i < (1<<tr->part_bits); i++) {
+		invalidate_device(MKDEV(tr->major, minor+i), 1);
+		gd->part[minor + i].start_sect = 0;
+		gd->part[minor + i].nr_sects = 0;
+	}
+
+	grok_partitions(gd, minor, 1 << tr->part_bits, 
+			tr->blkcore_priv->sizes[minor]);
+	up(&mtd_table_mutex);
+
+	return 0;
+}
+
+static int blktrans_ioctl(struct inode *inode, struct file *file, 
+			      unsigned int cmd, unsigned long arg)
+{
+	struct mtd_blktrans_dev *dev;
+	struct mtd_blktrans_ops *tr;
+	int devnum;
+
+	switch(cmd) {
+	case BLKGETSIZE:
+        case BLKGETSIZE64:
+        case BLKBSZSET:
+        case BLKBSZGET:
+        case BLKROSET:
+        case BLKROGET:
+        case BLKRASET:
+        case BLKRAGET:
+        case BLKPG:
+        case BLKELVGET:
+        case BLKELVSET:
+		return blk_ioctl(inode->i_rdev, cmd, arg);
+	}
+
+	down(&mtd_table_mutex);
+
+	tr = get_tr(MAJOR(inode->i_rdev));
+	if (!tr) {
+		up(&mtd_table_mutex);
+		return -ENODEV;
+	}
+
+	devnum = MINOR(inode->i_rdev) >> tr->part_bits;
+	dev = tr_get_dev(tr, devnum);
+
+	up(&mtd_table_mutex);
+
+	if (!dev)
+		return -ENODEV;
+
+	switch(cmd) {
+	case BLKRRPART:
+		return mtd_blktrans_rrpart(inode->i_rdev, tr, dev);
+		
+        case BLKFLSBUF:
+		blk_ioctl(inode->i_rdev, cmd, arg);
+		if (tr->flush)
+			return tr->flush(dev);
+		/* The core code did the work, we had nothing to do. */
+		return 0;
+
+	case HDIO_GETGEO:
+		if (tr->getgeo) {
+			struct hd_geometry g;
+			struct gendisk *gd = &(tr->blkcore_priv->gd);
+			int ret;
+
+			memset(&g, 0, sizeof(g));
+			ret = tr->getgeo(dev, &g);
+			if (ret)
+				return ret;
+
+			g.start = gd->part[MINOR(inode->i_rdev)].start_sect;
+			if (copy_to_user((void *)arg, &g, sizeof(g)))
+				return -EFAULT;
+			return 0;
+		} /* else */
+	default:
+		return -ENOTTY;
+	}
+}
+
+struct block_device_operations mtd_blktrans_ops = {
+	.owner		= THIS_MODULE,
+	.open		= blktrans_open,
+	.release	= blktrans_release,
+	.ioctl		= blktrans_ioctl,
+};
+
+int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
+{
+	struct mtd_blktrans_ops *tr = new->tr;
+	struct list_head *this;
+	int last_devnum = -1;
+	int i;
+
+	if (!down_trylock(&mtd_table_mutex)) {
+		up(&mtd_table_mutex);
+		BUG();
+	}
+
+	spin_lock(&tr->blkcore_priv->devs_lock);
+
+	list_for_each(this, &tr->devs) {
+		struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list);
+		if (new->devnum == -1) {
+			/* Use first free number */
+			if (d->devnum != last_devnum+1) {
+				/* Found a free devnum. Plug it in here */
+				new->devnum = last_devnum+1;
+				list_add_tail(&new->list, &d->list);
+				goto added;
+			}
+		} else if (d->devnum == new->devnum) {
+			/* Required number taken */
+			spin_unlock(&tr->blkcore_priv->devs_lock);
+			return -EBUSY;
+		} else if (d->devnum > new->devnum) {
+			/* Required number was free */
+			list_add_tail(&new->list, &d->list);
+			goto added;
+		} 
+		last_devnum = d->devnum;
+	}
+	if (new->devnum == -1)
+		new->devnum = last_devnum+1;
+
+	if ((new->devnum << tr->part_bits) > 256) {
+		spin_unlock(&tr->blkcore_priv->devs_lock);
+		return -EBUSY;
+	}
+
+	init_MUTEX(&new->sem);
+	list_add_tail(&new->list, &tr->devs);
+ added:
+	spin_unlock(&tr->blkcore_priv->devs_lock);
+
+	if (!tr->writesect)
+		new->readonly = 1;
+
+	for (i = new->devnum << tr->part_bits;
+	     i < (new->devnum+1) << tr->part_bits; 
+	     i++) {
+		set_device_ro(MKDEV(tr->major, i), new->readonly);
+		tr->blkcore_priv->blksizes[i] = new->blksize;
+		tr->blkcore_priv->sizes[i] = 0;
+		tr->blkcore_priv->part_table[i].nr_sects = 0;
+		tr->blkcore_priv->part_table[i].start_sect = 0;
+	}
+
+	/*
+	  <viro_zzz> dwmw2: BLOCK_SIZE_BITS has nothing to do with block devices
+	  <viro> dwmw2: any code which sets blk_size[][] should be 
+			size >> 10 /+ 2.4 and its dumb units */
+
+	tr->blkcore_priv->sizes[new->devnum << tr->part_bits] = 
+		(new->size * new->blksize) >> 10; /* 2.4 and its dumb units */
+
+	/* But this is still in device's sectors? $DEITY knows */
+	tr->blkcore_priv->part_table[new->devnum << tr->part_bits].nr_sects = new->size;
+
+	if (tr->part_bits) {
+		grok_partitions(&tr->blkcore_priv->gd, new->devnum,
+				1 << tr->part_bits, new->size);
+	}
+#ifdef CONFIG_DEVFS_FS
+	if (!tr->part_bits) {
+		char name[2];
+
+		name[0] = '0' + new->devnum;
+		name[1] = 0;
+
+		new->blkcore_priv = 
+			devfs_register(tr->blkcore_priv->devfs_dir_handle,
+				       name, DEVFS_FL_DEFAULT, tr->major,
+				       new->devnum, S_IFBLK|S_IRUGO|S_IWUGO,
+				       &mtd_blktrans_ops, NULL);
+	}
+#endif
+	return 0;
+}
+
+int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
+{
+	struct mtd_blktrans_ops *tr = old->tr;
+	int i;
+
+	if (!down_trylock(&mtd_table_mutex)) {
+		up(&mtd_table_mutex);
+		BUG();
+	}
+
+#ifdef CONFIG_DEVFS_FS
+	if (!tr->part_bits) {
+		devfs_unregister(old->blkcore_priv);
+		old->blkcore_priv = NULL;
+	} else {
+		devfs_register_partitions(&tr->blkcore_priv->gd,
+					  old->devnum << tr->part_bits, 1);
+	}
+#endif
+	spin_lock(&tr->blkcore_priv->devs_lock);
+	list_del(&old->list);
+	spin_unlock(&tr->blkcore_priv->devs_lock);
+
+	for (i = (old->devnum << tr->part_bits); 
+	     i < ((old->devnum+1) << tr->part_bits); i++) {
+		tr->blkcore_priv->sizes[i] = 0;
+		tr->blkcore_priv->part_table[i].nr_sects = 0;
+		tr->blkcore_priv->part_table[i].start_sect = 0;
+	}
+
+	return 0;
+}
+
+void blktrans_notify_remove(struct mtd_info *mtd)
+{
+	struct list_head *this, *this2, *next;
+
+	list_for_each(this, &blktrans_majors) {
+		struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
+
+		list_for_each_safe(this2, next, &tr->devs) {
+			struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list);
+
+			if (dev->mtd == mtd)
+				tr->remove_dev(dev);
+		}
+	}
+}
+
+void blktrans_notify_add(struct mtd_info *mtd)
+{
+	struct list_head *this;
+
+	if (mtd->type == MTD_ABSENT)
+		return;
+
+	list_for_each(this, &blktrans_majors) {
+		struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
+
+		tr->add_mtd(tr, mtd);
+	}
+
+}
+
+static struct mtd_notifier blktrans_notifier = {
+	.add = blktrans_notify_add,
+	.remove = blktrans_notify_remove,
+};
+      
+int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
+{
+	int ret, i;
+
+	/* Register the notifier if/when the first device type is 
+	   registered, to prevent the link/init ordering from fucking
+	   us over. */
+	if (!blktrans_notifier.list.next)
+		register_mtd_user(&blktrans_notifier);
+
+	tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);
+	if (!tr->blkcore_priv)
+		return -ENOMEM;
+
+	memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv));
+
+	down(&mtd_table_mutex);
+
+	ret = devfs_register_blkdev(tr->major, tr->name, &mtd_blktrans_ops);
+	if (ret) {
+		printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
+		       tr->name, tr->major, ret);
+		kfree(tr->blkcore_priv);
+		up(&mtd_table_mutex);
+		return ret;
+	}
+
+	blk_init_queue(BLK_DEFAULT_QUEUE(tr->major), &mtd_blktrans_request);
+	(BLK_DEFAULT_QUEUE(tr->major))->queuedata = tr;
+	
+	init_completion(&tr->blkcore_priv->thread_dead);
+	init_waitqueue_head(&tr->blkcore_priv->thread_wq);
+
+	ret = kernel_thread(mtd_blktrans_thread, tr, 
+			  CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
+	if (ret < 0) {
+		blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major));
+		devfs_unregister_blkdev(tr->major, tr->name);
+		kfree(tr->blkcore_priv);
+		up(&mtd_table_mutex);
+		return ret;
+	} 
+
+	tr->blkcore_priv->devfs_dir_handle = 
+			devfs_mk_dir(NULL, tr->name, NULL);
+
+	blksize_size[tr->major] = tr->blkcore_priv->blksizes;
+	blk_size[tr->major] = tr->blkcore_priv->sizes;
+
+	tr->blkcore_priv->gd.major = tr->major;
+	tr->blkcore_priv->gd.major_name = tr->name;
+	tr->blkcore_priv->gd.minor_shift = tr->part_bits;
+	tr->blkcore_priv->gd.max_p = (1<<tr->part_bits) - 1;
+	tr->blkcore_priv->gd.part = tr->blkcore_priv->part_table;
+	tr->blkcore_priv->gd.sizes = tr->blkcore_priv->sizes;
+	tr->blkcore_priv->gd.nr_real = 256 >> tr->part_bits;
+
+	spin_lock_init(&tr->blkcore_priv->devs_lock);
+
+	add_gendisk(&tr->blkcore_priv->gd);
+
+	INIT_LIST_HEAD(&tr->devs);
+	list_add(&tr->list, &blktrans_majors);
+
+	for (i=0; i<MAX_MTD_DEVICES; i++) {
+		if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)
+			tr->add_mtd(tr, mtd_table[i]);
+	}
+	up(&mtd_table_mutex);
+
+	return 0;
+}
+
+int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
+{
+	struct list_head *this, *next;
+
+	down(&mtd_table_mutex);
+
+	/* Clean up the kernel thread */
+	tr->blkcore_priv->exiting = 1;
+	wake_up(&tr->blkcore_priv->thread_wq);
+	wait_for_completion(&tr->blkcore_priv->thread_dead);
+	
+	/* Remove it from the list of active majors */
+	list_del(&tr->list);
+
+	/* Remove each of its devices */
+	list_for_each_safe(this, next, &tr->devs) {
+		struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list);
+		tr->remove_dev(dev);
+	}
+
+	blksize_size[tr->major] = NULL;
+	blk_size[tr->major] = NULL;
+
+	del_gendisk(&tr->blkcore_priv->gd);
+
+	blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major));
+	devfs_unregister_blkdev(tr->major, tr->name);
+
+	devfs_unregister(tr->blkcore_priv->devfs_dir_handle);
+
+	up(&mtd_table_mutex);
+
+	kfree(tr->blkcore_priv);
+
+	if (!list_empty(&tr->devs))
+		BUG();
+	return 0;
+}
+
+static void __exit mtd_blktrans_exit(void)
+{
+	/* No race here -- if someone's currently in register_mtd_blktrans
+	   we're screwed anyway. */
+	if (blktrans_notifier.list.next)
+		unregister_mtd_user(&blktrans_notifier);
+}
+
+module_exit(mtd_blktrans_exit);
+
+EXPORT_SYMBOL_GPL(register_mtd_blktrans);
+EXPORT_SYMBOL_GPL(deregister_mtd_blktrans);
+EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev);
+EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev);
+
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'");
diff -Nur linux-2.4.30/drivers/mtd/mtd_blkdevs.c linux-2.4.30-linux-mips.org/drivers/mtd/mtd_blkdevs.c
--- linux-2.4.30/drivers/mtd/mtd_blkdevs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/mtd_blkdevs.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,480 @@
+/*
+ * $Id: mtd_blkdevs.c,v 1.21 2004/03/30 20:59:46 sjhill Exp $
+ *
+ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Interface to Linux 2.5 block layer for MTD 'translation layers'.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/mtd/blktrans.h>
+#include <linux/mtd/mtd.h>
+#include <linux/blkdev.h>
+#include <linux/blkpg.h>
+#include <linux/spinlock.h>
+#include <linux/hdreg.h>
+#include <linux/init.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+#include <linux/devfs_fs_kernel.h>
+
+static LIST_HEAD(blktrans_majors);
+
+extern struct semaphore mtd_table_mutex;
+extern struct mtd_info *mtd_table[];
+
+struct mtd_blkcore_priv {
+	struct completion thread_dead;
+	int exiting;
+	wait_queue_head_t thread_wq;
+	struct request_queue *rq;
+	spinlock_t queue_lock;
+};
+
+static int do_blktrans_request(struct mtd_blktrans_ops *tr,
+			       struct mtd_blktrans_dev *dev,
+			       struct request *req)
+{
+	unsigned long block, nsect;
+	char *buf;
+
+	block = req->sector;
+	nsect = req->current_nr_sectors;
+	buf = req->buffer;
+
+	if (!(req->flags & REQ_CMD))
+		return 0;
+
+	if (block + nsect > get_capacity(req->rq_disk))
+		return 0;
+
+	switch(rq_data_dir(req)) {
+	case READ:
+		for (; nsect > 0; nsect--, block++, buf += 512)
+			if (tr->readsect(dev, block, buf))
+				return 0;
+		return 1;
+
+	case WRITE:
+		if (!tr->writesect)
+			return 0;
+
+		for (; nsect > 0; nsect--, block++, buf += 512)
+			if (tr->writesect(dev, block, buf))
+				return 0;
+		return 1;
+
+	default:
+		printk(KERN_NOTICE "Unknown request %ld\n", rq_data_dir(req));
+		return 0;
+	}
+}
+
+static int mtd_blktrans_thread(void *arg)
+{
+	struct mtd_blktrans_ops *tr = arg;
+	struct request_queue *rq = tr->blkcore_priv->rq;
+
+	/* we might get involved when memory gets low, so use PF_MEMALLOC */
+	current->flags |= PF_MEMALLOC;
+
+	daemonize("%sd", tr->name);
+
+	/* daemonize() doesn't do this for us since some kernel threads
+	   actually want to deal with signals. We can't just call 
+	   exit_sighand() since that'll cause an oops when we finally
+	   do exit. */
+	spin_lock_irq(&current->sighand->siglock);
+	sigfillset(&current->blocked);
+	recalc_sigpending();
+	spin_unlock_irq(&current->sighand->siglock);
+
+	spin_lock_irq(rq->queue_lock);
+		
+	while (!tr->blkcore_priv->exiting) {
+		struct request *req;
+		struct mtd_blktrans_dev *dev;
+		int res = 0;
+		DECLARE_WAITQUEUE(wait, current);
+
+		req = elv_next_request(rq);
+
+		if (!req) {
+			add_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
+			set_current_state(TASK_INTERRUPTIBLE);
+
+			spin_unlock_irq(rq->queue_lock);
+
+			schedule();
+			remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait);
+
+			spin_lock_irq(rq->queue_lock);
+
+			continue;
+		}
+
+		dev = req->rq_disk->private_data;
+		tr = dev->tr;
+
+		spin_unlock_irq(rq->queue_lock);
+
+		down(&dev->sem);
+		res = do_blktrans_request(tr, dev, req);
+		up(&dev->sem);
+
+		spin_lock_irq(rq->queue_lock);
+
+		end_request(req, res);
+	}
+	spin_unlock_irq(rq->queue_lock);
+
+	complete_and_exit(&tr->blkcore_priv->thread_dead, 0);
+}
+
+static void mtd_blktrans_request(struct request_queue *rq)
+{
+	struct mtd_blktrans_ops *tr = rq->queuedata;
+	wake_up(&tr->blkcore_priv->thread_wq);
+}
+
+
+int blktrans_open(struct inode *i, struct file *f)
+{
+	struct mtd_blktrans_dev *dev;
+	struct mtd_blktrans_ops *tr;
+	int ret = -ENODEV;
+
+	dev = i->i_bdev->bd_disk->private_data;
+	tr = dev->tr;
+
+	if (!try_module_get(dev->mtd->owner))
+		goto out;
+
+	if (!try_module_get(tr->owner))
+		goto out_tr;
+
+	/* FIXME: Locking. A hot pluggable device can go away 
+	   (del_mtd_device can be called for it) without its module
+	   being unloaded. */
+	dev->mtd->usecount++;
+
+	ret = 0;
+	if (tr->open && (ret = tr->open(dev))) {
+		dev->mtd->usecount--;
+		module_put(dev->mtd->owner);
+	out_tr:
+		module_put(tr->owner);
+	}
+ out:
+	return ret;
+}
+
+int blktrans_release(struct inode *i, struct file *f)
+{
+	struct mtd_blktrans_dev *dev;
+	struct mtd_blktrans_ops *tr;
+	int ret = 0;
+
+	dev = i->i_bdev->bd_disk->private_data;
+	tr = dev->tr;
+
+	if (tr->release)
+		ret = tr->release(dev);
+
+	if (!ret) {
+		dev->mtd->usecount--;
+		module_put(dev->mtd->owner);
+		module_put(tr->owner);
+	}
+
+	return ret;
+}
+
+
+static int blktrans_ioctl(struct inode *inode, struct file *file, 
+			      unsigned int cmd, unsigned long arg)
+{
+	struct mtd_blktrans_dev *dev = inode->i_bdev->bd_disk->private_data;
+	struct mtd_blktrans_ops *tr = dev->tr;
+
+	switch (cmd) {
+	case BLKFLSBUF:
+		if (tr->flush)
+			return tr->flush(dev);
+		/* The core code did the work, we had nothing to do. */
+		return 0;
+
+	case HDIO_GETGEO:
+		if (tr->getgeo) {
+			struct hd_geometry g;
+			int ret;
+
+			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)))
+				return -EFAULT;
+			return 0;
+		} /* else */
+	default:
+		return -ENOTTY;
+	}
+}
+
+struct block_device_operations mtd_blktrans_ops = {
+	.owner		= THIS_MODULE,
+	.open		= blktrans_open,
+	.release	= blktrans_release,
+	.ioctl		= blktrans_ioctl,
+};
+
+int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
+{
+	struct mtd_blktrans_ops *tr = new->tr;
+	struct list_head *this;
+	int last_devnum = -1;
+	struct gendisk *gd;
+
+	if (!down_trylock(&mtd_table_mutex)) {
+		up(&mtd_table_mutex);
+		BUG();
+	}
+
+	list_for_each(this, &tr->devs) {
+		struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list);
+		if (new->devnum == -1) {
+			/* Use first free number */
+			if (d->devnum != last_devnum+1) {
+				/* Found a free devnum. Plug it in here */
+				new->devnum = last_devnum+1;
+				list_add_tail(&new->list, &d->list);
+				goto added;
+			}
+		} else if (d->devnum == new->devnum) {
+			/* Required number taken */
+			return -EBUSY;
+		} else if (d->devnum > new->devnum) {
+			/* Required number was free */
+			list_add_tail(&new->list, &d->list);
+			goto added;
+		} 
+		last_devnum = d->devnum;
+	}
+	if (new->devnum == -1)
+		new->devnum = last_devnum+1;
+
+	if ((new->devnum << tr->part_bits) > 256) {
+		return -EBUSY;
+	}
+
+	init_MUTEX(&new->sem);
+	list_add_tail(&new->list, &tr->devs);
+ added:
+	if (!tr->writesect)
+		new->readonly = 1;
+
+	gd = alloc_disk(1 << tr->part_bits);
+	if (!gd) {
+		list_del(&new->list);
+		return -ENOMEM;
+	}
+	gd->major = tr->major;
+	gd->first_minor = (new->devnum) << tr->part_bits;
+	gd->fops = &mtd_blktrans_ops;
+	
+	snprintf(gd->disk_name, sizeof(gd->disk_name),
+		 "%s%c", tr->name, (tr->part_bits?'a':'0') + new->devnum);
+	snprintf(gd->devfs_name, sizeof(gd->devfs_name),
+		 "%s/%c", tr->name, (tr->part_bits?'a':'0') + new->devnum);
+
+	/* 2.5 has capacity in units of 512 bytes while still
+	   having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */
+	set_capacity(gd, (new->size * new->blksize) >> 9);
+
+	gd->private_data = new;
+	new->blkcore_priv = gd;
+	gd->queue = tr->blkcore_priv->rq;
+
+	if (new->readonly)
+		set_disk_ro(gd, 1);
+
+	add_disk(gd);
+	
+	return 0;
+}
+
+int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old)
+{
+	if (!down_trylock(&mtd_table_mutex)) {
+		up(&mtd_table_mutex);
+		BUG();
+	}
+
+	list_del(&old->list);
+
+	del_gendisk(old->blkcore_priv);
+	put_disk(old->blkcore_priv);
+		
+	return 0;
+}
+
+void blktrans_notify_remove(struct mtd_info *mtd)
+{
+	struct list_head *this, *this2, *next;
+
+	list_for_each(this, &blktrans_majors) {
+		struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
+
+		list_for_each_safe(this2, next, &tr->devs) {
+			struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list);
+
+			if (dev->mtd == mtd)
+				tr->remove_dev(dev);
+		}
+	}
+}
+
+void blktrans_notify_add(struct mtd_info *mtd)
+{
+	struct list_head *this;
+
+	if (mtd->type == MTD_ABSENT)
+		return;
+
+	list_for_each(this, &blktrans_majors) {
+		struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list);
+
+		tr->add_mtd(tr, mtd);
+	}
+
+}
+
+static struct mtd_notifier blktrans_notifier = {
+	.add = blktrans_notify_add,
+	.remove = blktrans_notify_remove,
+};
+      
+int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
+{
+	int ret, i;
+
+	/* Register the notifier if/when the first device type is 
+	   registered, to prevent the link/init ordering from fucking
+	   us over. */
+	if (!blktrans_notifier.list.next)
+		register_mtd_user(&blktrans_notifier);
+
+	tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL);
+	if (!tr->blkcore_priv)
+		return -ENOMEM;
+
+	memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv));
+
+	down(&mtd_table_mutex);
+
+	ret = register_blkdev(tr->major, tr->name);
+	if (ret) {
+		printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n",
+		       tr->name, tr->major, ret);
+		kfree(tr->blkcore_priv);
+		up(&mtd_table_mutex);
+		return ret;
+	}
+	spin_lock_init(&tr->blkcore_priv->queue_lock);
+	init_completion(&tr->blkcore_priv->thread_dead);
+	init_waitqueue_head(&tr->blkcore_priv->thread_wq);
+
+	tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock);
+	if (!tr->blkcore_priv->rq) {
+		unregister_blkdev(tr->major, tr->name);
+		kfree(tr->blkcore_priv);
+		up(&mtd_table_mutex);
+		return -ENOMEM;
+	}
+
+	tr->blkcore_priv->rq->queuedata = tr;
+
+	ret = kernel_thread(mtd_blktrans_thread, tr, 
+			    CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
+	if (ret < 0) {
+		blk_cleanup_queue(tr->blkcore_priv->rq);
+		unregister_blkdev(tr->major, tr->name);
+		kfree(tr->blkcore_priv);
+		up(&mtd_table_mutex);
+		return ret;
+	} 
+
+	devfs_mk_dir(tr->name);
+
+	INIT_LIST_HEAD(&tr->devs);
+	list_add(&tr->list, &blktrans_majors);
+
+	for (i=0; i<MAX_MTD_DEVICES; i++) {
+		if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT)
+			tr->add_mtd(tr, mtd_table[i]);
+	}
+
+	up(&mtd_table_mutex);
+
+	return 0;
+}
+
+int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr)
+{
+	struct list_head *this, *next;
+
+	down(&mtd_table_mutex);
+
+	/* Clean up the kernel thread */
+	tr->blkcore_priv->exiting = 1;
+	wake_up(&tr->blkcore_priv->thread_wq);
+	wait_for_completion(&tr->blkcore_priv->thread_dead);
+
+	/* Remove it from the list of active majors */
+	list_del(&tr->list);
+
+	list_for_each_safe(this, next, &tr->devs) {
+		struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list);
+		tr->remove_dev(dev);
+	}
+
+	devfs_remove(tr->name);
+	blk_cleanup_queue(tr->blkcore_priv->rq);
+	unregister_blkdev(tr->major, tr->name);
+
+	up(&mtd_table_mutex);
+
+	kfree(tr->blkcore_priv);
+
+	if (!list_empty(&tr->devs))
+		BUG();
+	return 0;
+}
+
+static void __exit mtd_blktrans_exit(void)
+{
+	/* No race here -- if someone's currently in register_mtd_blktrans
+	   we're screwed anyway. */
+	if (blktrans_notifier.list.next)
+		unregister_mtd_user(&blktrans_notifier);
+}
+
+module_exit(mtd_blktrans_exit);
+
+EXPORT_SYMBOL_GPL(register_mtd_blktrans);
+EXPORT_SYMBOL_GPL(deregister_mtd_blktrans);
+EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev);
+EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev);
+
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'");
diff -Nur linux-2.4.30/drivers/mtd/mtdblock.c linux-2.4.30-linux-mips.org/drivers/mtd/mtdblock.c
--- linux-2.4.30/drivers/mtd/mtdblock.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/mtdblock.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,52 +1,25 @@
 /* 
  * Direct MTD block device access
  *
- * $Id: mtdblock.c,v 1.51 2001/11/20 11:42:33 dwmw2 Exp $
+ * $Id: mtdblock.c,v 1.64 2003/10/04 17:14:14 dwmw2 Exp $
  *
- * 02-nov-2000	Nicolas Pitre		Added read-modify-write with cache
+ * (C) 2000-2003 Nicolas Pitre <nico@cam.org>
+ * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
  */
 
 #include <linux/config.h>
 #include <linux/types.h>
 #include <linux/module.h>
 #include <linux/kernel.h>
+#include <linux/fs.h>
+#include <linux/init.h>
 #include <linux/slab.h>
+#include <linux/vmalloc.h>
 #include <linux/mtd/mtd.h>
-#include <linux/mtd/compatmac.h>
-
-#define MAJOR_NR MTD_BLOCK_MAJOR
-#define DEVICE_NAME "mtdblock"
-#define DEVICE_REQUEST mtdblock_request
-#define DEVICE_NR(device) (device)
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-#define DEVICE_NO_RANDOM
-#include <linux/blk.h>
-/* for old kernels... */
-#ifndef QUEUE_EMPTY
-#define QUEUE_EMPTY  (!CURRENT)
-#endif
-#if LINUX_VERSION_CODE < 0x20300
-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync)
-#else
-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged)
-#endif
-
-#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);
-static struct mtd_notifier notifier = {
-        mtd_notify_add,
-        mtd_notify_remove,
-        NULL
-};
-static devfs_handle_t devfs_dir_handle = NULL;
-static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES];
-#endif
+#include <linux/mtd/blktrans.h>
 
 static struct mtdblk_dev {
-	struct mtd_info *mtd; /* Locked */
+	struct mtd_info *mtd;
 	int count;
 	struct semaphore cache_sem;
 	unsigned char *cache_data;
@@ -55,19 +28,6 @@
 	enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state;
 } *mtdblks[MAX_MTD_DEVICES];
 
-static spinlock_t mtdblks_lock;
-
-static int mtd_sizes[MAX_MTD_DEVICES];
-static int mtd_blksizes[MAX_MTD_DEVICES];
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
-#else
-#define BLK_INC_USE_COUNT do {} while(0)
-#define BLK_DEC_USE_COUNT do {} while(0)
-#endif
-
 /*
  * Cache stuff...
  * 
@@ -151,7 +111,7 @@
 		return ret;
 
 	/*
-	 * Here we could argably set the cache state to STATE_CLEAN.
+	 * Here we could argubly set the cache state to STATE_CLEAN.
 	 * However this could lead to inconsistency since we will not 
 	 * be notified if this content is altered on the flash by other 
 	 * means.  Let's declare it empty and leave buffering tasks to
@@ -277,57 +237,47 @@
 	return 0;
 }
 
+static int mtdblock_readsect(struct mtd_blktrans_dev *dev,
+			      unsigned long block, char *buf)
+{
+	struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
+	return do_cached_read(mtdblk, block<<9, 512, buf);
+}
 
+static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
+			      unsigned long block, char *buf)
+{
+	struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
+	if (unlikely(!mtdblk->cache_data)) {
+		mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
+		if (!mtdblk->cache_data)
+			return -EINTR;
+		/* -EINTR is not really correct, but it is the best match
+		 * documented in man 2 write for all cases.  We could also
+		 * return -EAGAIN sometimes, but why bother?
+		 */
+	}
+	return do_cached_write(mtdblk, block<<9, 512, buf);
+}
 
-static int mtdblock_open(struct inode *inode, struct file *file)
+static int mtdblock_open(struct mtd_blktrans_dev *mbd)
 {
 	struct mtdblk_dev *mtdblk;
-	struct mtd_info *mtd;
-	int dev;
+	struct mtd_info *mtd = mbd->mtd;
+	int dev = mbd->devnum;
 
 	DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n");
 	
-	if (!inode)
-		return -EINVAL;
-	
-	dev = MINOR(inode->i_rdev);
-	if (dev >= MAX_MTD_DEVICES)
-		return -EINVAL;
-
-	BLK_INC_USE_COUNT;
-
-	mtd = get_mtd_device(NULL, dev);
-	if (!mtd)
-		return -ENODEV;
-	if (MTD_ABSENT == mtd->type) {
-		put_mtd_device(mtd);
-		BLK_DEC_USE_COUNT;
-		return -ENODEV;
-	}
-	
-	spin_lock(&mtdblks_lock);
-
-	/* If it's already open, no need to piss about. */
 	if (mtdblks[dev]) {
 		mtdblks[dev]->count++;
-		spin_unlock(&mtdblks_lock);
-		put_mtd_device(mtd);
 		return 0;
 	}
 	
-	/* OK, it's not open. Try to find it */
-
-	/* First we have to drop the lock, because we have to
-	   to things which might sleep.
-	*/
-	spin_unlock(&mtdblks_lock);
-
+	/* OK, it's not open. Create cache info for it */
 	mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL);
-	if (!mtdblk) {
-		put_mtd_device(mtd);
-		BLK_DEC_USE_COUNT;
+	if (!mtdblk)
 		return -ENOMEM;
-	}
+
 	memset(mtdblk, 0, sizeof(*mtdblk));
 	mtdblk->count = 1;
 	mtdblk->mtd = mtd;
@@ -337,336 +287,102 @@
 	if ((mtdblk->mtd->flags & MTD_CAP_RAM) != MTD_CAP_RAM &&
 	    mtdblk->mtd->erasesize) {
 		mtdblk->cache_size = mtdblk->mtd->erasesize;
-		mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize);
-		if (!mtdblk->cache_data) {
-			put_mtd_device(mtdblk->mtd);
-			kfree(mtdblk);
-			BLK_DEC_USE_COUNT;
-			return -ENOMEM;
-		}
-	}
-
-	/* OK, we've created a new one. Add it to the list. */
-
-	spin_lock(&mtdblks_lock);
-
-	if (mtdblks[dev]) {
-		/* Another CPU made one at the same time as us. */
-		mtdblks[dev]->count++;
-		spin_unlock(&mtdblks_lock);
-		put_mtd_device(mtdblk->mtd);
-		vfree(mtdblk->cache_data);
-		kfree(mtdblk);
-		return 0;
+		mtdblk->cache_data = NULL;
 	}
 
 	mtdblks[dev] = mtdblk;
-	mtd_sizes[dev] = mtdblk->mtd->size/1024;
-	if (mtdblk->mtd->erasesize)
-		mtd_blksizes[dev] = mtdblk->mtd->erasesize;
-	if (mtd_blksizes[dev] > PAGE_SIZE)
-		mtd_blksizes[dev] = PAGE_SIZE;
-	set_device_ro (inode->i_rdev, !(mtdblk->mtd->flags & MTD_WRITEABLE));
-	
-	spin_unlock(&mtdblks_lock);
 	
 	DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
 
 	return 0;
 }
 
-static release_t mtdblock_release(struct inode *inode, struct file *file)
+static int mtdblock_release(struct mtd_blktrans_dev *mbd)
 {
-	int dev;
-	struct mtdblk_dev *mtdblk;
-   	DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
-
-	if (inode == NULL)
-		release_return(-ENODEV);
+	int dev = mbd->devnum;
+	struct mtdblk_dev *mtdblk = mtdblks[dev];
 
-	dev = MINOR(inode->i_rdev);
-	mtdblk = mtdblks[dev];
+   	DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n");
 
 	down(&mtdblk->cache_sem);
 	write_cached_data(mtdblk);
 	up(&mtdblk->cache_sem);
 
-	spin_lock(&mtdblks_lock);
 	if (!--mtdblk->count) {
 		/* It was the last usage. Free the device */
 		mtdblks[dev] = NULL;
-		spin_unlock(&mtdblks_lock);
 		if (mtdblk->mtd->sync)
 			mtdblk->mtd->sync(mtdblk->mtd);
-		put_mtd_device(mtdblk->mtd);
 		vfree(mtdblk->cache_data);
 		kfree(mtdblk);
-	} else {
-		spin_unlock(&mtdblks_lock);
 	}
-
 	DEBUG(MTD_DEBUG_LEVEL1, "ok\n");
 
-	BLK_DEC_USE_COUNT;
-	release_return(0);
+	return 0;
 }  
 
-
-/* 
- * This is a special request_fn because it is executed in a process context 
- * to be able to sleep independently of the caller.  The io_request_lock 
- * is held upon entry and exit.
- * The head of our request queue is considered active so there is no need 
- * to dequeue requests before we are done.
- */
-static void handle_mtdblock_request(void)
+static int mtdblock_flush(struct mtd_blktrans_dev *dev)
 {
-	struct request *req;
-	struct mtdblk_dev *mtdblk;
-	unsigned int res;
-
-	for (;;) {
-		INIT_REQUEST;
-		req = CURRENT;
-		spin_unlock_irq(&io_request_lock);
-		mtdblk = mtdblks[MINOR(req->rq_dev)];
-		res = 0;
-
-		if (MINOR(req->rq_dev) >= MAX_MTD_DEVICES)
-			panic("%s: minor out of bounds", __FUNCTION__);
-
-		if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9))
-			goto end_req;
-
-		// Handle the request
-		switch (req->cmd)
-		{
-			int err;
-
-			case READ:
-			down(&mtdblk->cache_sem);
-			err = do_cached_read (mtdblk, req->sector << 9, 
-					req->current_nr_sectors << 9,
-					req->buffer);
-			up(&mtdblk->cache_sem);
-			if (!err)
-				res = 1;
-			break;
-
-			case WRITE:
-			// Read only device
-			if ( !(mtdblk->mtd->flags & MTD_WRITEABLE) ) 
-				break;
-
-			// Do the write
-			down(&mtdblk->cache_sem);
-			err = do_cached_write (mtdblk, req->sector << 9,
-					req->current_nr_sectors << 9, 
-					req->buffer);
-			up(&mtdblk->cache_sem);
-			if (!err)
-				res = 1;
-			break;
-		}
+	struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
 
-end_req:
-		spin_lock_irq(&io_request_lock);
-		end_request(res);
-	}
-}
-
-static volatile int leaving = 0;
-static DECLARE_MUTEX_LOCKED(thread_sem);
-static DECLARE_WAIT_QUEUE_HEAD(thr_wq);
-
-int mtdblock_thread(void *dummy)
-{
-	struct task_struct *tsk = current;
-	DECLARE_WAITQUEUE(wait, tsk);
-
-	/* we might get involved when memory gets low, so use PF_MEMALLOC */
-	tsk->flags |= PF_MEMALLOC;
-	strcpy(tsk->comm, "mtdblockd");
-	spin_lock_irq(&tsk->sigmask_lock);
-	sigfillset(&tsk->blocked);
-	recalc_sigpending(tsk);
-	spin_unlock_irq(&tsk->sigmask_lock);
-	daemonize();
-
-	while (!leaving) {
-		add_wait_queue(&thr_wq, &wait);
-		set_current_state(TASK_INTERRUPTIBLE);
-		spin_lock_irq(&io_request_lock);
-		if (QUEUE_EMPTY || QUEUE_PLUGGED) {
-			spin_unlock_irq(&io_request_lock);
-			schedule();
-			remove_wait_queue(&thr_wq, &wait); 
-		} else {
-			remove_wait_queue(&thr_wq, &wait); 
-			set_current_state(TASK_RUNNING);
-			handle_mtdblock_request();
-			spin_unlock_irq(&io_request_lock);
-		}
-	}
+	down(&mtdblk->cache_sem);
+	write_cached_data(mtdblk);
+	up(&mtdblk->cache_sem);
 
-	up(&thread_sem);
+	if (mtdblk->mtd->sync)
+		mtdblk->mtd->sync(mtdblk->mtd);
 	return 0;
 }
 
-#if LINUX_VERSION_CODE < 0x20300
-#define RQFUNC_ARG void
-#else
-#define RQFUNC_ARG request_queue_t *q
-#endif
-
-static void mtdblock_request(RQFUNC_ARG)
+static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 {
-	/* Don't do anything, except wake the thread if necessary */
-	wake_up(&thr_wq);
-}
+	struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
 
+	if (!dev)
+		return;
 
-static int mtdblock_ioctl(struct inode * inode, struct file * file,
-		      unsigned int cmd, unsigned long arg)
-{
-	struct mtdblk_dev *mtdblk;
+	memset(dev, 0, sizeof(*dev));
 
-	mtdblk = mtdblks[MINOR(inode->i_rdev)];
+	dev->mtd = mtd;
+	dev->devnum = mtd->index;
+	dev->blksize = 512;
+	dev->size = mtd->size >> 9;
+	dev->tr = tr;
 
-#ifdef PARANOIA
-	if (!mtdblk)
-		BUG();
-#endif
-
-	switch (cmd) {
-	case BLKGETSIZE:   /* Return device size */
-		return put_user((mtdblk->mtd->size >> 9), (unsigned long *) arg);
-
-#ifdef BLKGETSIZE64
-	case BLKGETSIZE64:
-		return put_user((u64)mtdblk->mtd->size, (u64 *)arg);
-#endif
-		
-	case BLKFLSBUF:
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-		if(!capable(CAP_SYS_ADMIN))
-			return -EACCES;
-#endif
-		fsync_dev(inode->i_rdev);
-		invalidate_buffers(inode->i_rdev);
-		down(&mtdblk->cache_sem);
-		write_cached_data(mtdblk);
-		up(&mtdblk->cache_sem);
-		if (mtdblk->mtd->sync)
-			mtdblk->mtd->sync(mtdblk->mtd);
-		return 0;
+	if (!(mtd->flags & MTD_WRITEABLE))
+		dev->readonly = 1;
 
-	default:
-		return -EINVAL;
-	}
+	add_mtd_blktrans_dev(dev);
 }
 
-#if LINUX_VERSION_CODE < 0x20326
-static struct file_operations mtd_fops =
-{
-	open: mtdblock_open,
-	ioctl: mtdblock_ioctl,
-	release: mtdblock_release,
-	read: block_read,
-	write: block_write
-};
-#else
-static struct block_device_operations mtd_fops = 
+static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
 {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
-	owner: THIS_MODULE,
-#endif
-	open: mtdblock_open,
-	release: mtdblock_release,
-	ioctl: mtdblock_ioctl
-};
-#endif
-
-#ifdef CONFIG_DEVFS_FS
-/* 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 || mtd->type == MTD_ABSENT)
-                return;
-
-        sprintf(name, "%d", mtd->index);
-        devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
-                        DEVFS_FL_DEFAULT, MTD_BLOCK_MAJOR, mtd->index,
-                        S_IFBLK | S_IRUGO | S_IWUGO,
-                        &mtd_fops, NULL);
+	del_mtd_blktrans_dev(dev);
+	kfree(dev);
 }
 
-static void mtd_notify_remove(struct mtd_info* mtd)
-{
-        if (!mtd || mtd->type == MTD_ABSENT)
-                return;
-
-        devfs_unregister(devfs_rw_handle[mtd->index]);
-}
-#endif
+struct mtd_blktrans_ops mtdblock_tr = {
+	.name		= "mtdblock",
+	.major		= 31,
+	.part_bits	= 0,
+	.open		= mtdblock_open,
+	.flush		= mtdblock_flush,
+	.release	= mtdblock_release,
+	.readsect	= mtdblock_readsect,
+	.writesect	= mtdblock_writesect,
+	.add_mtd	= mtdblock_add_mtd,
+	.remove_dev	= mtdblock_remove_dev,
+	.owner		= THIS_MODULE,
+};
 
 int __init init_mtdblock(void)
 {
-	int i;
-
-	spin_lock_init(&mtdblks_lock);
-#ifdef CONFIG_DEVFS_FS
-	if (devfs_register_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME, &mtd_fops))
-	{
-		printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
-			MTD_BLOCK_MAJOR);
-		return -EAGAIN;
-	}
-
-	devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL);
-	register_mtd_user(&notifier);
-#else
-	if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {
-		printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
-		       MTD_BLOCK_MAJOR);
-		return -EAGAIN;
-	}
-#endif
-	
-	/* We fill it in at open() time. */
-	for (i=0; i< MAX_MTD_DEVICES; i++) {
-		mtd_sizes[i] = 0;
-		mtd_blksizes[i] = BLOCK_SIZE;
-	}
-	init_waitqueue_head(&thr_wq);
-	/* Allow the block size to default to BLOCK_SIZE. */
-	blksize_size[MAJOR_NR] = mtd_blksizes;
-	blk_size[MAJOR_NR] = mtd_sizes;
-	
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request);
-	kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND);
-	return 0;
+	return register_mtd_blktrans(&mtdblock_tr);
 }
 
 static void __exit cleanup_mtdblock(void)
 {
-	leaving = 1;
-	wake_up(&thr_wq);
-	down(&thread_sem);
-#ifdef CONFIG_DEVFS_FS
-	unregister_mtd_user(&notifier);
-	devfs_unregister(devfs_dir_handle);
-	devfs_unregister_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME);
-#else
-	unregister_blkdev(MAJOR_NR,DEVICE_NAME);
-#endif
-	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-	blksize_size[MAJOR_NR] = NULL;
-	blk_size[MAJOR_NR] = NULL;
+	deregister_mtd_blktrans(&mtdblock_tr);
 }
 
 module_init(init_mtdblock);
diff -Nur linux-2.4.30/drivers/mtd/mtdblock_ro.c linux-2.4.30-linux-mips.org/drivers/mtd/mtdblock_ro.c
--- linux-2.4.30/drivers/mtd/mtdblock_ro.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/mtdblock_ro.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,301 +1,87 @@
 /*
- * $Id: mtdblock_ro.c,v 1.12 2001/11/20 11:42:33 dwmw2 Exp $
+ * $Id: mtdblock_ro.c,v 1.18 2003/06/23 12:00:08 dwmw2 Exp $
  *
- * Read-only version of the mtdblock device, without the 
- * read/erase/modify/writeback stuff
+ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Simple read-only (writable only for RAM) mtdblock driver
  */
 
-#ifdef MTDBLOCK_DEBUG
-#define DEBUGLVL debug
-#endif							       
-
-
-#include <linux/module.h>
-#include <linux/types.h>
-
+#include <linux/init.h>
+#include <linux/slab.h>
 #include <linux/mtd/mtd.h>
-#include <linux/mtd/compatmac.h>
+#include <linux/mtd/blktrans.h>
 
-#define MAJOR_NR MTD_BLOCK_MAJOR
-#define DEVICE_NAME "mtdblock"
-#define DEVICE_REQUEST mtdblock_request
-#define DEVICE_NR(device) (device)
-#define DEVICE_ON(device)
-#define DEVICE_OFF(device)
-#define DEVICE_NO_RANDOM
-#include <linux/blk.h>
-
-#if LINUX_VERSION_CODE < 0x20300
-#define RQFUNC_ARG void
-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0)
-#else
-#define RQFUNC_ARG request_queue_t *q
-#endif
-
-#ifdef MTDBLOCK_DEBUG
-static int debug = MTDBLOCK_DEBUG;
-MODULE_PARM(debug, "i");
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
-#else
-#define BLK_INC_USE_COUNT do {} while(0)
-#define BLK_DEC_USE_COUNT do {} while(0)
-#endif
-
-static int mtd_sizes[MAX_MTD_DEVICES];
-
-
-static int mtdblock_open(struct inode *inode, struct file *file)
+static int mtdblock_readsect(struct mtd_blktrans_dev *dev,
+			      unsigned long block, char *buf)
 {
-	struct mtd_info *mtd = NULL;
-
-	int dev;
-
-	DEBUG(1,"mtdblock_open\n");
-	
-	if (inode == 0)
-		return -EINVAL;
-	
-	dev = MINOR(inode->i_rdev);
-	
-	mtd = get_mtd_device(NULL, dev);
-	if (!mtd)
-		return -EINVAL;
-	if (MTD_ABSENT == mtd->type) {
-		put_mtd_device(mtd);
-		return -EINVAL;
-	}
-
-	BLK_INC_USE_COUNT;
-
-	mtd_sizes[dev] = mtd->size>>9;
-
-	DEBUG(1, "ok\n");
+	size_t retlen;
 
+	if (dev->mtd->read(dev->mtd, (block * 512), 512, &retlen, buf))
+		return 1;
 	return 0;
 }
 
-static release_t mtdblock_release(struct inode *inode, struct file *file)
+static int mtdblock_writesect(struct mtd_blktrans_dev *dev,
+			      unsigned long block, char *buf)
 {
-	int dev;
-	struct mtd_info *mtd;
+	size_t retlen;
 
-   	DEBUG(1, "mtdblock_release\n");
-
-	if (inode == NULL)
-		release_return(-ENODEV);
-   
-	dev = MINOR(inode->i_rdev);
-	mtd = __get_mtd_device(NULL, dev);
-
-	if (!mtd) {
-		printk(KERN_WARNING "MTD device is absent on mtd_release!\n");
-		BLK_DEC_USE_COUNT;
-		release_return(-ENODEV);
-	}
-	
-	if (mtd->sync)
-		mtd->sync(mtd);
+	if (dev->mtd->write(dev->mtd, (block * 512), 512, &retlen, buf))
+		return 1;
+	return 0;
+}
 
-	put_mtd_device(mtd);
+static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
+{
+	struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
 
-	DEBUG(1, "ok\n");
+	if (!dev)
+		return;
 
-	BLK_DEC_USE_COUNT;
-	release_return(0);
-}  
+	memset(dev, 0, sizeof(*dev));
 
+	dev->mtd = mtd;
+	dev->devnum = mtd->index;
+	dev->blksize = 512;
+	dev->size = mtd->size >> 9;
+	dev->tr = tr;
+	if ((mtd->flags & (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE)) !=
+	    (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE))
+		dev->readonly = 1;
 
-static void mtdblock_request(RQFUNC_ARG)
-{
-   struct request *current_request;
-   unsigned int res = 0;
-   struct mtd_info *mtd;
-
-   while (1)
-   {
-      /* Grab the Request and unlink it from the request list, INIT_REQUEST
-       	 will execute a return if we are done. */
-      INIT_REQUEST;
-      current_request = CURRENT;
-   
-      if (MINOR(current_request->rq_dev) >= MAX_MTD_DEVICES)
-      {
-	 printk("mtd: Unsupported device!\n");
-	 end_request(0);
-	 continue;
-      }
-      
-      // Grab our MTD structure
-
-      mtd = __get_mtd_device(NULL, MINOR(current_request->rq_dev));
-      if (!mtd) {
-	      printk("MTD device %d doesn't appear to exist any more\n", CURRENT_DEV);
-	      end_request(0);
-      }
-
-      if (current_request->sector << 9 > mtd->size ||
-	  (current_request->sector + current_request->current_nr_sectors) << 9 > mtd->size)
-      {
-	 printk("mtd: Attempt to read past end of device!\n");
-	 printk("size: %x, sector: %lx, nr_sectors %lx\n", mtd->size, 
-		current_request->sector, current_request->current_nr_sectors);
-	 end_request(0);
-	 continue;
-      }
-      
-      /* Remove the request we are handling from the request list so nobody messes
-         with it */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-      /* Now drop the lock that the ll_rw_blk functions grabbed for us
-         and process the request. This is necessary due to the extreme time
-         we spend processing it. */
-      spin_unlock_irq(&io_request_lock);
-#endif
-
-      // Handle the request
-      switch (current_request->cmd)
-      {
-         size_t retlen;
-
-	 case READ:
-	 if (MTD_READ(mtd,current_request->sector<<9, 
-		      current_request->current_nr_sectors << 9, 
-		      &retlen, current_request->buffer) == 0)
-	    res = 1;
-	 else
-	    res = 0;
-	 break;
-	 
-	 case WRITE:
-
-	 /* printk("mtdblock_request WRITE sector=%d(%d)\n",current_request->sector,
-		current_request->current_nr_sectors);
-	 */
-
-	 // Read only device
-	 if ((mtd->flags & MTD_CAP_RAM) == 0)
-	 {
-	    res = 0;
-	    break;
-	 }
-
-	 // Do the write
-	 if (MTD_WRITE(mtd,current_request->sector<<9, 
-		       current_request->current_nr_sectors << 9, 
-		       &retlen, current_request->buffer) == 0)
-	    res = 1;
-	 else
-	    res = 0;
-	 break;
-	 
-	 // Shouldn't happen
-	 default:
-	 printk("mtd: unknown request\n");
-	 break;
-      }
-
-      // Grab the lock and re-thread the item onto the linked list
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-	spin_lock_irq(&io_request_lock);
-#endif
-	end_request(res);
-   }
+	add_mtd_blktrans_dev(dev);
 }
 
-
-
-static int mtdblock_ioctl(struct inode * inode, struct file * file,
-		      unsigned int cmd, unsigned long arg)
+static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
 {
-	struct mtd_info *mtd;
-
-	mtd = __get_mtd_device(NULL, MINOR(inode->i_rdev));
-
-	if (!mtd) return -EINVAL;
-
-	switch (cmd) {
-	case BLKGETSIZE:   /* Return device size */
-		return put_user((mtd->size >> 9), (unsigned long *) arg);
-
-#ifdef BLKGETSIZE64
-	case BLKGETSIZE64:
-		return put_user((u64)mtd->size, (u64 *)arg);
-#endif
-
-	case BLKFLSBUF:
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-		if(!capable(CAP_SYS_ADMIN))  return -EACCES;
-#endif
-		fsync_dev(inode->i_rdev);
-		invalidate_buffers(inode->i_rdev);
-		if (mtd->sync)
-			mtd->sync(mtd);
-		return 0;
-
-	default:
-		return -ENOTTY;
-	}
+	del_mtd_blktrans_dev(dev);
+	kfree(dev);
 }
 
-#if LINUX_VERSION_CODE < 0x20326
-static struct file_operations mtd_fops =
-{
-	open: mtdblock_open,
-	ioctl: mtdblock_ioctl,
-	release: mtdblock_release,
-	read: block_read,
-	write: block_write
-};
-#else
-static struct block_device_operations mtd_fops = 
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
-	owner: THIS_MODULE,
-#endif
-	open: mtdblock_open,
-	release: mtdblock_release,
-	ioctl: mtdblock_ioctl
+struct mtd_blktrans_ops mtdblock_tr = {
+	.name		= "mtdblock",
+	.major		= 31,
+	.part_bits	= 0,
+	.readsect	= mtdblock_readsect,
+	.writesect	= mtdblock_writesect,
+	.add_mtd	= mtdblock_add_mtd,
+	.remove_dev	= mtdblock_remove_dev,
+	.owner		= THIS_MODULE,
 };
-#endif
 
-int __init init_mtdblock(void)
+static int __init mtdblock_init(void)
 {
-	int i;
-
-	if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) {
-		printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
-		       MTD_BLOCK_MAJOR);
-		return -EAGAIN;
-	}
-	
-	/* We fill it in at open() time. */
-	for (i=0; i< MAX_MTD_DEVICES; i++) {
-		mtd_sizes[i] = 0;
-	}
-	
-	/* Allow the block size to default to BLOCK_SIZE. */
-	blksize_size[MAJOR_NR] = NULL;
-	blk_size[MAJOR_NR] = mtd_sizes;
-	
-	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request);
-	return 0;
+	return register_mtd_blktrans(&mtdblock_tr);
 }
 
-static void __exit cleanup_mtdblock(void)
+static void __exit mtdblock_exit(void)
 {
-	unregister_blkdev(MAJOR_NR,DEVICE_NAME);
-	blk_size[MAJOR_NR] = NULL;
-	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+	deregister_mtd_blktrans(&mtdblock_tr);
 }
 
-module_init(init_mtdblock);
-module_exit(cleanup_mtdblock);
-
+module_init(mtdblock_init);
+module_exit(mtdblock_exit);
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Erwin Authried <eauth@softsys.co.at> et al.");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
 MODULE_DESCRIPTION("Simple read-only block device emulation access to MTD devices");
diff -Nur linux-2.4.30/drivers/mtd/mtdchar.c linux-2.4.30-linux-mips.org/drivers/mtd/mtdchar.c
--- linux-2.4.30/drivers/mtd/mtdchar.c	2004-08-08 01:26:04.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/mtdchar.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,8 +1,7 @@
 /*
- * $Id: mtdchar.c,v 1.49 2003/01/24 12:02:58 dwmw2 Exp $
+ * $Id: mtdchar.c,v 1.58 2004/04/20 12:22:35 gleixner Exp $
  *
  * Character-device access to raw MTD devices.
- * Pure 2.4 version - compatibility cruft removed to mtdchar-compat.c
  *
  */
 
@@ -10,7 +9,11 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/mtd/mtd.h>
+#include <linux/mtd/compatmac.h>
 #include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <asm/uaccess.h>
 
 #ifdef CONFIG_DEVFS_FS
 #include <linux/devfs_fs_kernel.h>
@@ -18,15 +21,18 @@
 static void mtd_notify_remove(struct mtd_info* mtd);
 
 static struct mtd_notifier notifier = {
-	add:	mtd_notify_add,
-	remove:	mtd_notify_remove,
+	.add	= mtd_notify_add,
+	.remove	= mtd_notify_remove,
 };
 
+#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];
 #endif
 
+#endif
+
 static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
 {
 	struct mtd_info *mtd=(struct mtd_info *)file->private_data;
@@ -60,7 +66,7 @@
 
 static int mtd_open(struct inode *inode, struct file *file)
 {
-	int minor = minor(inode->i_rdev);
+	int minor = iminor(inode);
 	int devnum = minor >> 1;
 	struct mtd_info *mtd;
 
@@ -125,15 +131,11 @@
 	int ret=0;
 	int len;
 	char *kbuf;
-	loff_t pos = *ppos;
 	
 	DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n");
 
-	if (pos < 0 || pos > mtd->size)
-		return 0;
-
-	if (count > mtd->size - pos)
-		count = mtd->size - pos;
+	if (*ppos + count > mtd->size)
+		count = mtd->size - *ppos;
 
 	if (!count)
 		return 0;
@@ -150,9 +152,9 @@
 		if (!kbuf)
 			return -ENOMEM;
 		
-		ret = MTD_READ(mtd, pos, len, &retlen, kbuf);
+		ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf);
 		if (!ret) {
-			pos += retlen;
+			*ppos += retlen;
 			if (copy_to_user(buf, kbuf, retlen)) {
 			        kfree(kbuf);
 				return -EFAULT;
@@ -171,8 +173,6 @@
 		kfree(kbuf);
 	}
 	
-	*ppos = pos;
-	
 	return total_retlen;
 } /* mtd_read */
 
@@ -182,18 +182,17 @@
 	char *kbuf;
 	size_t retlen;
 	size_t total_retlen=0;
-	loff_t pos = *ppos;
 	int ret=0;
 	int len;
 
 	DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n");
 	
-	if (pos < 0 || pos >= mtd->size)
+	if (*ppos == mtd->size)
 		return -ENOSPC;
-
-	if (count > mtd->size - pos)
-		count = mtd->size - pos;
 	
+	if (*ppos + count > mtd->size)
+		count = mtd->size - *ppos;
+
 	if (!count)
 		return 0;
 
@@ -214,9 +213,9 @@
 			return -EFAULT;
 		}
 		
-	        ret = (*(mtd->write))(mtd, pos, len, &retlen, kbuf);
+	        ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf);
 		if (!ret) {
-			pos += retlen;
+			*ppos += retlen;
 			total_retlen += retlen;
 			count -= retlen;
 			buf += retlen;
@@ -228,7 +227,6 @@
 		
 		kfree(kbuf);
 	}
-	*ppos = pos;
 
 	return total_retlen;
 } /* mtd_write */
@@ -450,80 +448,12 @@
 		break;
 	}
 
-	case MEMWRITEDATA:
+	case MEMSETOOBSEL:
 	{
-		struct mtd_oob_buf buf;
-		void *databuf;
-		ssize_t retlen;
-		
-		if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
+		if (copy_from_user(&mtd->oobinfo ,(void *)arg, sizeof(struct nand_oobinfo)))
 			return -EFAULT;
-		
-		if (buf.length > 0x4096)
-			return -EINVAL;
-
-		if (!mtd->write_ecc)
-			ret = -EOPNOTSUPP;
-		else
-			ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);
-
-		if (ret)
-			return ret;
-
-		databuf = kmalloc(buf.length, GFP_KERNEL);
-		if (!databuf)
-			return -ENOMEM;
-		
-		if (copy_from_user(databuf, buf.ptr, buf.length)) {
-			kfree(databuf);
-			return -EFAULT;
-		}
-
-		ret = (mtd->write_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0);
-
-		if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
-			ret = -EFAULT;
-
-		kfree(databuf);
-		break;
-
-	}
-
-	case MEMREADDATA:
-	{
-		struct mtd_oob_buf buf;
-		void *databuf;
-		ssize_t retlen = 0;
-
-		if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
-			return -EFAULT;
-		
-		if (buf.length > 0x4096)
-			return -EINVAL;
-
-		if (!mtd->read_ecc)
-			ret = -EOPNOTSUPP;
-		else
-			ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);
-
-		if (ret)
-			return ret;
-
-		databuf = kmalloc(buf.length, GFP_KERNEL);
-		if (!databuf)
-			return -ENOMEM;
-		
-		ret = (mtd->read_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0);
-
-		if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
-			ret = -EFAULT;
-		else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
-			ret = -EFAULT;
-		
-		kfree(databuf);
 		break;
 	}
-
 		
 	default:
 		DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO);
@@ -534,17 +464,19 @@
 } /* memory_ioctl */
 
 static struct file_operations mtd_fops = {
-	owner:		THIS_MODULE,
-	llseek:		mtd_lseek,     	/* lseek */
-	read:		mtd_read,	/* read */
-	write: 		mtd_write, 	/* write */
-	ioctl:		mtd_ioctl,	/* ioctl */
-	open:		mtd_open,	/* open */
-	release:	mtd_close,	/* release */
+	.owner		= THIS_MODULE,
+	.llseek		= mtd_lseek,
+	.read		= mtd_read,
+	.write		= mtd_write,
+	.ioctl		= mtd_ioctl,
+	.open		= mtd_open,
+	.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. */
 
@@ -576,30 +508,52 @@
 	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)
 {
-#ifdef CONFIG_DEVFS_FS
-	if (devfs_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);
-#else
-	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;
-	}
 #endif
-
 	return 0;
 }
 
@@ -607,11 +561,13 @@
 {
 #ifdef CONFIG_DEVFS_FS
 	unregister_mtd_user(&notifier);
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
 	devfs_unregister(devfs_dir_handle);
-	devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
-#else
-	unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
+#else	
+	devfs_remove ("mtd");
+#endif
 #endif
+	unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
 }
 
 module_init(init_mtdchar);
diff -Nur linux-2.4.30/drivers/mtd/mtdconcat.c linux-2.4.30-linux-mips.org/drivers/mtd/mtdconcat.c
--- linux-2.4.30/drivers/mtd/mtdconcat.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/mtdconcat.c	2005-05-12 18:01:29.000000000 +0200
@@ -3,9 +3,11 @@
  *
  * (C) 2002 Robert Kaiser <rkaiser@sysgo.de>
  *
+ * NAND support by Christian Gan <cgan@iders.ca>
+ *
  * This code is GPL
  *
- * $Id: mtdconcat.c,v 1.3 2002/05/21 21:04:25 dwmw2 Exp $
+ * $Id: mtdconcat.c,v 1.8 2003/06/30 11:01:26 dwmw2 Exp $
  */
 
 #include <linux/module.h>
@@ -24,7 +26,7 @@
  */
 struct mtd_concat {
 	struct mtd_info mtd;
-	int             num_subdev;
+	int num_subdev;
 	struct mtd_info **subdev;
 };
 
@@ -35,21 +37,112 @@
 #define SIZEOF_STRUCT_MTD_CONCAT(num_subdev)	\
 	((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *)))
 
-
 /*
  * Given a pointer to the MTD object in the mtd_concat structure,
  * we can retrieve the pointer to that structure with this macro.
  */
 #define CONCAT(x)  ((struct mtd_concat *)(x))
 
-	
 /* 
  * MTD methods which look up the relevant subdevice, translate the
  * effective address and pass through to the subdevice.
  */
 
-static int concat_read (struct mtd_info *mtd, loff_t from, size_t len, 
-			size_t *retlen, u_char *buf)
+static int
+concat_read(struct mtd_info *mtd, loff_t from, size_t len,
+	    size_t * retlen, u_char * buf)
+{
+	struct mtd_concat *concat = CONCAT(mtd);
+	int err = -EINVAL;
+	int i;
+
+	*retlen = 0;
+
+	for (i = 0; i < concat->num_subdev; i++) {
+		struct mtd_info *subdev = concat->subdev[i];
+		size_t size, retsize;
+
+		if (from >= subdev->size) {
+			/* Not destined for this subdev */
+			size = 0;
+			from -= subdev->size;
+			continue;
+		}
+		if (from + len > subdev->size)
+			/* First part goes into this subdev */
+			size = subdev->size - from;
+		else
+			/* Entire transaction goes into this subdev */
+			size = len;
+
+		err = subdev->read(subdev, from, size, &retsize, buf);
+
+		if (err)
+			break;
+
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
+
+		err = -EINVAL;
+		buf += size;
+		from = 0;
+	}
+	return err;
+}
+
+static int
+concat_write(struct mtd_info *mtd, loff_t to, size_t len,
+	     size_t * retlen, const u_char * buf)
+{
+	struct mtd_concat *concat = CONCAT(mtd);
+	int err = -EINVAL;
+	int i;
+
+	if (!(mtd->flags & MTD_WRITEABLE))
+		return -EROFS;
+
+	*retlen = 0;
+
+	for (i = 0; i < concat->num_subdev; i++) {
+		struct mtd_info *subdev = concat->subdev[i];
+		size_t size, retsize;
+
+		if (to >= subdev->size) {
+			size = 0;
+			to -= subdev->size;
+			continue;
+		}
+		if (to + len > subdev->size)
+			size = subdev->size - to;
+		else
+			size = len;
+
+		if (!(subdev->flags & MTD_WRITEABLE))
+			err = -EROFS;
+		else
+			err = subdev->write(subdev, to, size, &retsize, buf);
+
+		if (err)
+			break;
+
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
+
+		err = -EINVAL;
+		buf += size;
+		to = 0;
+	}
+	return err;
+}
+
+static int
+concat_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)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	int err = -EINVAL;
@@ -57,43 +150,56 @@
 
 	*retlen = 0;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		size_t size, retsize;
 
-		if (from >= subdev->size)
-		{
-			size  = 0;
+		if (from >= subdev->size) {
+			/* Not destined for this subdev */
+			size = 0;
 			from -= subdev->size;
+			continue;
 		}
+
+		if (from + len > subdev->size)
+			/* First part goes into this subdev */
+			size = subdev->size - from;
 		else
-		{
-			if (from + len > subdev->size)
-				size = subdev->size - from;
-			else
-				size = len;
-
-			err = subdev->read(subdev, from, size, &retsize, buf);
-
-			if(err)
-				break;
-
-			*retlen += retsize;
-			len -= size;
-			if(len == 0)
-				break;
+			/* Entire transaction goes into this subdev */
+			size = len;
 
+		if (subdev->read_ecc)
+			err = subdev->read_ecc(subdev, from, size,
+					       &retsize, buf, eccbuf, oobsel);
+		else
 			err = -EINVAL;
-			buf += size;
-			from = 0;
+
+		if (err)
+			break;
+
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
+
+		err = -EINVAL;
+		buf += size;
+		if (eccbuf) {
+			eccbuf += subdev->oobsize;
+			/* in nand.c at least, eccbufs are
+			   tagged with 2 (int)eccstatus'; we
+			   must account for these */
+			eccbuf += 2 * (sizeof (int));
 		}
+		from = 0;
 	}
 	return err;
 }
 
-static int concat_write (struct mtd_info *mtd, loff_t to, size_t len,
-			size_t *retlen, const u_char *buf)
+static int
+concat_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)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	int err = -EINVAL;
@@ -104,47 +210,146 @@
 
 	*retlen = 0;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		size_t size, retsize;
 
-		if (to >= subdev->size)
-		{
-			size  = 0;
+		if (to >= subdev->size) {
+			size = 0;
 			to -= subdev->size;
+			continue;
+		}
+		if (to + len > subdev->size)
+			size = subdev->size - to;
+		else
+			size = len;
+
+		if (!(subdev->flags & MTD_WRITEABLE))
+			err = -EROFS;
+		else if (subdev->write_ecc)
+			err = subdev->write_ecc(subdev, to, size,
+						&retsize, buf, eccbuf, oobsel);
+		else
+			err = -EINVAL;
+
+		if (err)
+			break;
+
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
+
+		err = -EINVAL;
+		buf += size;
+		if (eccbuf)
+			eccbuf += subdev->oobsize;
+		to = 0;
+	}
+	return err;
+}
+
+static int
+concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len,
+		size_t * retlen, u_char * buf)
+{
+	struct mtd_concat *concat = CONCAT(mtd);
+	int err = -EINVAL;
+	int i;
+
+	*retlen = 0;
+
+	for (i = 0; i < concat->num_subdev; i++) {
+		struct mtd_info *subdev = concat->subdev[i];
+		size_t size, retsize;
+
+		if (from >= subdev->size) {
+			/* Not destined for this subdev */
+			size = 0;
+			from -= subdev->size;
+			continue;
 		}
+		if (from + len > subdev->size)
+			/* First part goes into this subdev */
+			size = subdev->size - from;
 		else
-		{
-			if (to + len > subdev->size)
-				size = subdev->size - to;
-			else
-				size = len;
-
-			if (!(subdev->flags & MTD_WRITEABLE))
-				err = -EROFS;
-			else
-				err = subdev->write(subdev, to, size, &retsize, buf);
-
-			if(err)
-				break;
-
-			*retlen += retsize;
-			len -= size;
-			if(len == 0)
-				break;
+			/* Entire transaction goes into this subdev */
+			size = len;
 
+		if (subdev->read_oob)
+			err = subdev->read_oob(subdev, from, size,
+					       &retsize, buf);
+		else
 			err = -EINVAL;
-			buf += size;
-			to = 0;
+
+		if (err)
+			break;
+
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
+
+		err = -EINVAL;
+		buf += size;
+		from = 0;
+	}
+	return err;
+}
+
+static int
+concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
+		 size_t * retlen, const u_char * buf)
+{
+	struct mtd_concat *concat = CONCAT(mtd);
+	int err = -EINVAL;
+	int i;
+
+	if (!(mtd->flags & MTD_WRITEABLE))
+		return -EROFS;
+
+	*retlen = 0;
+
+	for (i = 0; i < concat->num_subdev; i++) {
+		struct mtd_info *subdev = concat->subdev[i];
+		size_t size, retsize;
+
+		if (to >= subdev->size) {
+			size = 0;
+			to -= subdev->size;
+			continue;
 		}
+		if (to + len > subdev->size)
+			size = subdev->size - to;
+		else
+			size = len;
+
+		if (!(subdev->flags & MTD_WRITEABLE))
+			err = -EROFS;
+		else if (subdev->write_oob)
+			err = subdev->write_oob(subdev, to, size, &retsize,
+						buf);
+		else
+			err = -EINVAL;
+
+		if (err)
+			break;
+
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
+
+		err = -EINVAL;
+		buf += size;
+		to = 0;
 	}
 	return err;
 }
 
-static void concat_erase_callback (struct erase_info *instr)
+static void concat_erase_callback(struct erase_info *instr)
 {
-	wake_up((wait_queue_head_t *)instr->priv);
+	wake_up((wait_queue_head_t *) instr->priv);
 }
 
 static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase)
@@ -160,18 +365,18 @@
 
 	erase->mtd = mtd;
 	erase->callback = concat_erase_callback;
-	erase->priv = (unsigned long)&waitq;
-			
+	erase->priv = (unsigned long) &waitq;
+
 	/*
 	 * FIXME: Allow INTERRUPTIBLE. Which means
 	 * not having the wait_queue head on the stack.
 	 */
 	err = mtd->erase(mtd, erase);
-	if (!err)
-	{
+	if (!err) {
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		add_wait_queue(&waitq, &wait);
-		if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED)
+		if (erase->state != MTD_ERASE_DONE
+		    && erase->state != MTD_ERASE_FAILED)
 			schedule();
 		remove_wait_queue(&waitq, &wait);
 		set_current_state(TASK_RUNNING);
@@ -181,7 +386,7 @@
 	return err;
 }
 
-static int concat_erase (struct mtd_info *mtd, struct erase_info *instr)
+static int concat_erase(struct mtd_info *mtd, struct erase_info *instr)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	struct mtd_info *subdev;
@@ -192,10 +397,10 @@
 	if (!(mtd->flags & MTD_WRITEABLE))
 		return -EROFS;
 
-	if(instr->addr > concat->mtd.size)
+	if (instr->addr > concat->mtd.size)
 		return -EINVAL;
 
-	if(instr->len + instr->addr > concat->mtd.size)
+	if (instr->len + instr->addr > concat->mtd.size)
 		return -EINVAL;
 
 	/*
@@ -204,23 +409,22 @@
 	 * region info rather than looking at each particular sub-device
 	 * in turn.
 	 */
-	if (!concat->mtd.numeraseregions)
-	{	/* the easy case: device has uniform erase block size */
-		if(instr->addr & (concat->mtd.erasesize - 1))
+	if (!concat->mtd.numeraseregions) {
+		/* the easy case: device has uniform erase block size */
+		if (instr->addr & (concat->mtd.erasesize - 1))
 			return -EINVAL;
-		if(instr->len & (concat->mtd.erasesize - 1))
+		if (instr->len & (concat->mtd.erasesize - 1))
 			return -EINVAL;
-	}
-	else
-	{	/* device has variable erase size */
-		struct mtd_erase_region_info *erase_regions = concat->mtd.eraseregions;
+	} else {
+		/* device has variable erase size */
+		struct mtd_erase_region_info *erase_regions =
+		    concat->mtd.eraseregions;
 
 		/*
 		 * Find the erase region where the to-be-erased area begins:
 		 */
-		for(i = 0; i < concat->mtd.numeraseregions && 
-		           instr->addr >= erase_regions[i].offset; i++)
-			;
+		for (i = 0; i < concat->mtd.numeraseregions &&
+		     instr->addr >= erase_regions[i].offset; i++) ;
 		--i;
 
 		/*
@@ -228,25 +432,26 @@
 		 * to-be-erased area begins. Verify that the starting
 		 * offset is aligned to this region's erase size:
 		 */
-		if (instr->addr & (erase_regions[i].erasesize-1))
+		if (instr->addr & (erase_regions[i].erasesize - 1))
 			return -EINVAL;
 
 		/*
 		 * now find the erase region where the to-be-erased area ends:
 		 */
-		for(; i < concat->mtd.numeraseregions && 
-		      (instr->addr + instr->len) >=  erase_regions[i].offset ; ++i)
-			;
+		for (; i < concat->mtd.numeraseregions &&
+		     (instr->addr + instr->len) >= erase_regions[i].offset;
+		     ++i) ;
 		--i;
 		/*
 		 * check if the ending offset is aligned to this region's erase size
 		 */
-		if ((instr->addr + instr->len) & (erase_regions[i].erasesize-1))
+		if ((instr->addr + instr->len) & (erase_regions[i].erasesize -
+						  1))
 			return -EINVAL;
 	}
 
 	/* make a local copy of instr to avoid modifying the caller's struct */
-	erase = kmalloc(sizeof(struct erase_info),GFP_KERNEL);
+	erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
 
 	if (!erase)
 		return -ENOMEM;
@@ -258,39 +463,40 @@
 	 * find the subdevice where the to-be-erased area begins, adjust
 	 * starting offset to be relative to the subdevice start
 	 */
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	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
 			break;
-    }
-	if(i >= concat->num_subdev)	/* must never happen since size */
-		BUG();					/* limit has been verified above */
+	}
+
+	/* must never happen since size limit has been verified above */
+	if (i >= concat->num_subdev)
+		BUG();
 
 	/* now do the erase: */
 	err = 0;
-	for(;length > 0; i++)	/* loop for all subevices affected by this request */
-	{
-		subdev = concat->subdev[i];		/* get current subdevice */
+	for (; length > 0; i++) {
+		/* loop for all subdevices affected by this request */
+		subdev = concat->subdev[i];	/* get current subdevice */
 
 		/* limit length to subdevice's size: */
-		if(erase->addr + length > subdev->size)
+		if (erase->addr + length > subdev->size)
 			erase->len = subdev->size - erase->addr;
 		else
 			erase->len = length;
 
-		if (!(subdev->flags & MTD_WRITEABLE))
-		{
+		if (!(subdev->flags & MTD_WRITEABLE)) {
 			err = -EROFS;
 			break;
 		}
 		length -= erase->len;
-		if ((err = concat_dev_erase(subdev, erase)))
-		{
-			if(err == -EINVAL)	/* sanity check: must never happen since */
-				BUG();			/* block alignment has been checked above */
+		if ((err = concat_dev_erase(subdev, erase))) {
+			/* sanity check: should never happen since
+			 * block alignment has been checked above */
+			if (err == -EINVAL)
+				BUG();
 			break;
 		}
 		/*
@@ -313,85 +519,79 @@
 	return 0;
 }
 
-static int concat_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
+static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	int i, err = -EINVAL;
 
-	if ((len + ofs) > mtd->size) 
+	if ((len + ofs) > mtd->size)
 		return -EINVAL;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		size_t size;
 
-		if (ofs >= subdev->size)
-		{
-			size  = 0;
+		if (ofs >= subdev->size) {
+			size = 0;
 			ofs -= subdev->size;
+			continue;
 		}
+		if (ofs + len > subdev->size)
+			size = subdev->size - ofs;
 		else
-		{
-			if (ofs + len > subdev->size)
-				size = subdev->size - ofs;
-			else
-				size = len;
-
-			err = subdev->lock(subdev, ofs, size);
-
-			if(err)
-				break;
-
-			len -= size;
-			if(len == 0)
-				break;
+			size = len;
 
-			err = -EINVAL;
-			ofs = 0;
-		}
+		err = subdev->lock(subdev, ofs, size);
+
+		if (err)
+			break;
+
+		len -= size;
+		if (len == 0)
+			break;
+
+		err = -EINVAL;
+		ofs = 0;
 	}
+
 	return err;
 }
 
-static int concat_unlock (struct mtd_info *mtd, loff_t ofs, size_t len)
+static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
 	int i, err = 0;
 
-	if ((len + ofs) > mtd->size) 
+	if ((len + ofs) > mtd->size)
 		return -EINVAL;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		size_t size;
 
-		if (ofs >= subdev->size)
-		{
-			size  = 0;
+		if (ofs >= subdev->size) {
+			size = 0;
 			ofs -= subdev->size;
+			continue;
 		}
+		if (ofs + len > subdev->size)
+			size = subdev->size - ofs;
 		else
-		{
-			if (ofs + len > subdev->size)
-				size = subdev->size - ofs;
-			else
-				size = len;
-
-			err = subdev->unlock(subdev, ofs, size);
-
-			if(err)
-				break;
-
-			len -= size;
-			if(len == 0)
-				break;
+			size = len;
 
-			err = -EINVAL;
-			ofs = 0;
-		}
+		err = subdev->unlock(subdev, ofs, size);
+
+		if (err)
+			break;
+
+		len -= size;
+		if (len == 0)
+			break;
+
+		err = -EINVAL;
+		ofs = 0;
 	}
+
 	return err;
 }
 
@@ -400,8 +600,7 @@
 	struct mtd_concat *concat = CONCAT(mtd);
 	int i;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		subdev->sync(subdev);
 	}
@@ -412,10 +611,9 @@
 	struct mtd_concat *concat = CONCAT(mtd);
 	int i, rc = 0;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
-		if((rc = subdev->suspend(subdev)) < 0)
+		if ((rc = subdev->suspend(subdev)) < 0)
 			return rc;
 	}
 	return rc;
@@ -426,8 +624,7 @@
 	struct mtd_concat *concat = CONCAT(mtd);
 	int i;
 
-	for(i = 0; i < concat->num_subdev; i++)
-	{
+	for (i = 0; i < concat->num_subdev; i++) {
 		struct mtd_info *subdev = concat->subdev[i];
 		subdev->resume(subdev);
 	}
@@ -439,11 +636,10 @@
  * stored to *new_dev upon success. This function does _not_
  * register any devices: this is the caller's responsibility.
  */
-struct mtd_info *mtd_concat_create(
-	struct mtd_info *subdev[],	/* subdevices to concatenate */
-	int num_devs,				/* number of subdevices      */
-	char *name)					/* name for the new device   */
-{
+struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],	/* subdevices to concatenate */
+				   int num_devs,	/* number of subdevices      */
+				   char *name)
+{				/* name for the new device   */
 	int i;
 	size_t size;
 	struct mtd_concat *concat;
@@ -451,90 +647,103 @@
 	int num_erase_region;
 
 	printk(KERN_NOTICE "Concatenating MTD devices:\n");
-	for(i = 0; i < num_devs; i++)
+	for (i = 0; i < num_devs; i++)
 		printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name);
 	printk(KERN_NOTICE "into device \"%s\"\n", name);
 
 	/* allocate the device structure */
 	size = SIZEOF_STRUCT_MTD_CONCAT(num_devs);
-	concat = kmalloc (size, GFP_KERNEL);
-	if(!concat)
-	{
-		printk ("memory allocation error while creating concatenated device \"%s\"\n",
-				name);
-			return NULL;
+	concat = kmalloc(size, GFP_KERNEL);
+	if (!concat) {
+		printk
+		    ("memory allocation error while creating concatenated device \"%s\"\n",
+		     name);
+		return NULL;
 	}
 	memset(concat, 0, size);
-	concat->subdev = (struct mtd_info **)(concat + 1);
+	concat->subdev = (struct mtd_info **) (concat + 1);
 
 	/*
 	 * Set up the new "super" device's MTD object structure, check for
 	 * incompatibilites between the subdevices.
 	 */
-	concat->mtd.type      = subdev[0]->type;
-	concat->mtd.flags     = subdev[0]->flags;
-	concat->mtd.size      = subdev[0]->size;
+	concat->mtd.type = subdev[0]->type;
+	concat->mtd.flags = subdev[0]->flags;
+	concat->mtd.size = subdev[0]->size;
 	concat->mtd.erasesize = subdev[0]->erasesize;
-	concat->mtd.oobblock  = subdev[0]->oobblock;
-	concat->mtd.oobsize   = subdev[0]->oobsize;
-	concat->mtd.ecctype   = subdev[0]->ecctype;
-	concat->mtd.eccsize   = subdev[0]->eccsize;
-
-	concat->subdev[0]   = subdev[0];
-
-	for(i = 1; i < num_devs; i++)
-	{
-		if(concat->mtd.type != subdev[i]->type)
-		{
+	concat->mtd.oobblock = subdev[0]->oobblock;
+	concat->mtd.oobsize = subdev[0]->oobsize;
+	concat->mtd.ecctype = subdev[0]->ecctype;
+	concat->mtd.eccsize = subdev[0]->eccsize;
+	if (subdev[0]->read_ecc)
+		concat->mtd.read_ecc = concat_read_ecc;
+	if (subdev[0]->write_ecc)
+		concat->mtd.write_ecc = concat_write_ecc;
+	if (subdev[0]->read_oob)
+		concat->mtd.read_oob = concat_read_oob;
+	if (subdev[0]->write_oob)
+		concat->mtd.write_oob = concat_write_oob;
+
+	concat->subdev[0] = subdev[0];
+
+	for (i = 1; i < num_devs; i++) {
+		if (concat->mtd.type != subdev[i]->type) {
 			kfree(concat);
-			printk ("Incompatible device type on \"%s\"\n", subdev[i]->name);
+			printk("Incompatible device type on \"%s\"\n",
+			       subdev[i]->name);
 			return NULL;
 		}
-		if(concat->mtd.flags != subdev[i]->flags)
-		{	/*
-			 * Expect all flags except MTD_WRITEABLE to be equal on
-			 * all subdevices.
+		if (concat->mtd.flags != subdev[i]->flags) {
+			/*
+			 * Expect all flags except MTD_WRITEABLE to be
+			 * equal on all subdevices.
 			 */
-			if((concat->mtd.flags ^ subdev[i]->flags) & ~MTD_WRITEABLE)
-			{
+			if ((concat->mtd.flags ^ subdev[i]->
+			     flags) & ~MTD_WRITEABLE) {
 				kfree(concat);
-				printk ("Incompatible device flags on \"%s\"\n", subdev[i]->name);
+				printk("Incompatible device flags on \"%s\"\n",
+				       subdev[i]->name);
 				return NULL;
-			}
-			else	/* if writeable attribute differs, make super device writeable */
-				concat->mtd.flags |= subdev[i]->flags & MTD_WRITEABLE;
+			} else
+				/* if writeable attribute differs,
+				   make super device writeable */
+				concat->mtd.flags |=
+				    subdev[i]->flags & MTD_WRITEABLE;
 		}
 		concat->mtd.size += subdev[i]->size;
-		if(concat->mtd.oobblock != subdev[i]->oobblock ||
-		   concat->mtd.oobsize  != subdev[i]->oobsize  ||
-		   concat->mtd.ecctype  != subdev[i]->ecctype  ||
-		   concat->mtd.eccsize  != subdev[i]->eccsize)
-		{
+		if (concat->mtd.oobblock   !=  subdev[i]->oobblock ||
+		    concat->mtd.oobsize    !=  subdev[i]->oobsize ||
+		    concat->mtd.ecctype    !=  subdev[i]->ecctype ||
+		    concat->mtd.eccsize    !=  subdev[i]->eccsize ||
+		    !concat->mtd.read_ecc  != !subdev[i]->read_ecc ||
+		    !concat->mtd.write_ecc != !subdev[i]->write_ecc ||
+		    !concat->mtd.read_oob  != !subdev[i]->read_oob ||
+		    !concat->mtd.write_oob != !subdev[i]->write_oob) {
 			kfree(concat);
-			printk ("Incompatible OOB or ECC data on \"%s\"\n", subdev[i]->name);
+			printk("Incompatible OOB or ECC data on \"%s\"\n",
+			       subdev[i]->name);
 			return NULL;
 		}
 		concat->subdev[i] = subdev[i];
-		
+
 	}
 
-	concat->num_subdev  = num_devs;
-	concat->mtd.name    = name;
+	concat->num_subdev = num_devs;
+	concat->mtd.name = name;
 
 	/*
 	 * NOTE: for now, we do not provide any readv()/writev() methods
 	 *       because they are messy to implement and they are not
 	 *       used to a great extent anyway.
 	 */
-	concat->mtd.erase   = concat_erase;
-	concat->mtd.read    = concat_read;
-	concat->mtd.write   = concat_write;
-	concat->mtd.sync    = concat_sync;
-	concat->mtd.lock    = concat_lock;
-	concat->mtd.unlock  = concat_unlock;
+	concat->mtd.erase = concat_erase;
+	concat->mtd.read = concat_read;
+	concat->mtd.write = concat_write;
+	concat->mtd.sync = concat_sync;
+	concat->mtd.lock = concat_lock;
+	concat->mtd.unlock = concat_unlock;
 	concat->mtd.suspend = concat_suspend;
-	concat->mtd.resume  = concat_resume;
-
+	concat->mtd.resume = concat_resume;
 
 	/*
 	 * Combine the erase block size info of the subdevices:
@@ -544,44 +753,44 @@
 	 */
 	max_erasesize = curr_erasesize = subdev[0]->erasesize;
 	num_erase_region = 1;
-	for(i = 0; i < num_devs; i++)
-	{
-		if(subdev[i]->numeraseregions == 0)
-		{	/* current subdevice has uniform erase size */
-			if(subdev[i]->erasesize != curr_erasesize)
-			{	/* if it differs from the last subdevice's erase size, count it */
+	for (i = 0; i < num_devs; i++) {
+		if (subdev[i]->numeraseregions == 0) {
+			/* current subdevice has uniform erase size */
+			if (subdev[i]->erasesize != curr_erasesize) {
+				/* if it differs from the last subdevice's erase size, count it */
 				++num_erase_region;
 				curr_erasesize = subdev[i]->erasesize;
-				if(curr_erasesize > max_erasesize)
+				if (curr_erasesize > max_erasesize)
 					max_erasesize = curr_erasesize;
 			}
-		}
-		else
-		{	/* current subdevice has variable erase size */
+		} else {
+			/* current subdevice has variable erase size */
 			int j;
-			for(j = 0; j < subdev[i]->numeraseregions; j++)
-			{	/* walk the list of erase regions, count any changes */
-				if(subdev[i]->eraseregions[j].erasesize != curr_erasesize)
-				{
+			for (j = 0; j < subdev[i]->numeraseregions; j++) {
+
+				/* walk the list of erase regions, count any changes */
+				if (subdev[i]->eraseregions[j].erasesize !=
+				    curr_erasesize) {
 					++num_erase_region;
-					curr_erasesize = subdev[i]->eraseregions[j].erasesize;
-					if(curr_erasesize > max_erasesize)
+					curr_erasesize =
+					    subdev[i]->eraseregions[j].
+					    erasesize;
+					if (curr_erasesize > max_erasesize)
 						max_erasesize = curr_erasesize;
 				}
 			}
 		}
 	}
 
-	if(num_erase_region == 1)
-	{	/*
+	if (num_erase_region == 1) {
+		/*
 		 * All subdevices have the same uniform erase size.
 		 * This is easy:
 		 */
 		concat->mtd.erasesize = curr_erasesize;
 		concat->mtd.numeraseregions = 0;
-	}
-	else
-	{	/*
+	} else {
+		/*
 		 * erase block size varies across the subdevices: allocate
 		 * space to store the data describing the variable erase regions
 		 */
@@ -590,13 +799,14 @@
 
 		concat->mtd.erasesize = max_erasesize;
 		concat->mtd.numeraseregions = num_erase_region;
-		concat->mtd.eraseregions = erase_region_p = kmalloc (
-		     num_erase_region * sizeof(struct mtd_erase_region_info), GFP_KERNEL);
-		if(!erase_region_p)
-		{
+		concat->mtd.eraseregions = erase_region_p =
+		    kmalloc(num_erase_region *
+			    sizeof (struct mtd_erase_region_info), GFP_KERNEL);
+		if (!erase_region_p) {
 			kfree(concat);
-			printk ("memory allocation error while creating erase region list"
-			        " for device \"%s\"\n", name);
+			printk
+			    ("memory allocation error while creating erase region list"
+			     " for device \"%s\"\n", name);
 			return NULL;
 		}
 
@@ -606,46 +816,53 @@
 		 */
 		curr_erasesize = subdev[0]->erasesize;
 		begin = position = 0;
-		for(i = 0; i < num_devs; i++)
-		{
-			if(subdev[i]->numeraseregions == 0)
-			{	/* current subdevice has uniform erase size */
-				if(subdev[i]->erasesize != curr_erasesize)
-				{	/*
+		for (i = 0; i < num_devs; i++) {
+			if (subdev[i]->numeraseregions == 0) {
+				/* current subdevice has uniform erase size */
+				if (subdev[i]->erasesize != curr_erasesize) {
+					/*
 					 *  fill in an mtd_erase_region_info structure for the area
 					 *  we have walked so far:
 					 */
-					erase_region_p->offset    = begin;
-					erase_region_p->erasesize = curr_erasesize;
-					erase_region_p->numblocks = (position - begin) / curr_erasesize;
+					erase_region_p->offset = begin;
+					erase_region_p->erasesize =
+					    curr_erasesize;
+					erase_region_p->numblocks =
+					    (position - begin) / curr_erasesize;
 					begin = position;
 
 					curr_erasesize = subdev[i]->erasesize;
 					++erase_region_p;
 				}
 				position += subdev[i]->size;
-			}
-			else
-			{	/* current subdevice has variable erase size */
+			} else {
+				/* current subdevice has variable erase size */
 				int j;
-				for(j = 0; j < subdev[i]->numeraseregions; j++)
-				{	/* walk the list of erase regions, count any changes */
-					if(subdev[i]->eraseregions[j].erasesize != curr_erasesize)
-					{
-						erase_region_p->offset    = begin;
-						erase_region_p->erasesize = curr_erasesize;
-						erase_region_p->numblocks = (position - begin) / curr_erasesize;
+				for (j = 0; j < subdev[i]->numeraseregions; j++) {
+					/* walk the list of erase regions, count any changes */
+					if (subdev[i]->eraseregions[j].
+					    erasesize != curr_erasesize) {
+						erase_region_p->offset = begin;
+						erase_region_p->erasesize =
+						    curr_erasesize;
+						erase_region_p->numblocks =
+						    (position -
+						     begin) / curr_erasesize;
 						begin = position;
 
-						curr_erasesize = subdev[i]->eraseregions[j].erasesize;
+						curr_erasesize =
+						    subdev[i]->eraseregions[j].
+						    erasesize;
 						++erase_region_p;
 					}
-					position += subdev[i]->eraseregions[j].numblocks * curr_erasesize;
+					position +=
+					    subdev[i]->eraseregions[j].
+					    numblocks * curr_erasesize;
 				}
 			}
 		}
 		/* Now write the final entry */
-		erase_region_p->offset    = begin;
+		erase_region_p->offset = begin;
 		erase_region_p->erasesize = curr_erasesize;
 		erase_region_p->numblocks = (position - begin) / curr_erasesize;
 	}
@@ -660,16 +877,14 @@
 void mtd_concat_destroy(struct mtd_info *mtd)
 {
 	struct mtd_concat *concat = CONCAT(mtd);
-	if(concat->mtd.numeraseregions)
+	if (concat->mtd.numeraseregions)
 		kfree(concat->mtd.eraseregions);
 	kfree(concat);
 }
 
-
 EXPORT_SYMBOL(mtd_concat_create);
 EXPORT_SYMBOL(mtd_concat_destroy);
 
-
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Robert Kaiser <rkaiser@sysgo.de>");
 MODULE_DESCRIPTION("Generic support for concatenating of MTD devices");
diff -Nur linux-2.4.30/drivers/mtd/mtdcore.c linux-2.4.30-linux-mips.org/drivers/mtd/mtdcore.c
--- linux-2.4.30/drivers/mtd/mtdcore.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/mtdcore.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: mtdcore.c,v 1.34 2003/01/24 23:32:25 dwmw2 Exp $
+ * $Id: mtdcore.c,v 1.39 2003/05/21 15:15:03 dwmw2 Exp $
  *
  * Core registration and callback routines for MTD
  * drivers and users.
@@ -17,6 +17,7 @@
 #include <linux/major.h>
 #include <linux/fs.h>
 #include <linux/ioctl.h>
+#include <linux/init.h>
 #include <linux/mtd/compatmac.h>
 #ifdef CONFIG_PROC_FS
 #include <linux/proc_fs.h>
@@ -24,9 +25,15 @@
 
 #include <linux/mtd/mtd.h>
 
-static DECLARE_MUTEX(mtd_table_mutex);
-static struct mtd_info *mtd_table[MAX_MTD_DEVICES];
-static struct mtd_notifier *mtd_notifiers = NULL;
+/* These are exported solely for the purpose of mtd_blkdevs.c. You 
+   should not use them for _anything_ else */
+DECLARE_MUTEX(mtd_table_mutex);
+struct mtd_info *mtd_table[MAX_MTD_DEVICES];
+
+EXPORT_SYMBOL_GPL(mtd_table_mutex);
+EXPORT_SYMBOL_GPL(mtd_table);
+
+static LIST_HEAD(mtd_notifiers);
 
 /**
  *	add_mtd_device - register an MTD device
@@ -44,21 +51,28 @@
 
 	down(&mtd_table_mutex);
 
-	for (i=0; i< MAX_MTD_DEVICES; i++)
-		if (!mtd_table[i])
-		{
-			struct mtd_notifier *not=mtd_notifiers;
+	for (i=0; i < MAX_MTD_DEVICES; i++)
+		if (!mtd_table[i]) {
+			struct list_head *this;
 
 			mtd_table[i] = mtd;
 			mtd->index = i;
+			mtd->usecount = 0;
+
 			DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name);
-			while (not)
-			{
-				(*(not->add))(mtd);
-				not = not->next;
+			/* No need to get a refcount on the module containing
+			   the notifier, since we hold the mtd_table_mutex */
+			list_for_each(this, &mtd_notifiers) {
+				struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
+				not->add(mtd);
 			}
+			
 			up(&mtd_table_mutex);
-			MOD_INC_USE_COUNT;
+			/* We _know_ we aren't being removed, because
+			   our caller is still holding us here. So none
+			   of this try_ nonsense, and no bitching about it
+			   either. :) */
+			__module_get(THIS_MODULE);
 			return 0;
 		}
 	
@@ -78,29 +92,34 @@
 
 int del_mtd_device (struct mtd_info *mtd)
 {
-	struct mtd_notifier *not=mtd_notifiers;
-	int i;
+	int ret;
 	
 	down(&mtd_table_mutex);
 
-	for (i=0; i < MAX_MTD_DEVICES; i++)
-	{
-		if (mtd_table[i] == mtd)
-		{
-			while (not)
-			{
-				(*(not->remove))(mtd);
-				not = not->next;
-			}
-			mtd_table[i] = NULL;
-			up (&mtd_table_mutex);
-			MOD_DEC_USE_COUNT;
-			return 0;
+	if (mtd_table[mtd->index] != mtd) {
+		ret = -ENODEV;
+	} else if (mtd->usecount) {
+		printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", 
+		       mtd->index, mtd->name, mtd->usecount);
+		ret = -EBUSY;
+	} else {
+		struct list_head *this;
+
+		/* No need to get a refcount on the module containing
+		   the notifier, since we hold the mtd_table_mutex */
+		list_for_each(this, &mtd_notifiers) {
+			struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list);
+			not->remove(mtd);
 		}
+
+		mtd_table[mtd->index] = NULL;
+
+		module_put(THIS_MODULE);
+		ret = 0;
 	}
 
 	up(&mtd_table_mutex);
-	return 1;
+	return ret;
 }
 
 /**
@@ -118,10 +137,9 @@
 
 	down(&mtd_table_mutex);
 
-	new->next = mtd_notifiers;
-	mtd_notifiers = new;
+	list_add(&new->list, &mtd_notifiers);
 
- 	MOD_INC_USE_COUNT;
+ 	__module_get(THIS_MODULE);
 	
 	for (i=0; i< MAX_MTD_DEVICES; i++)
 		if (mtd_table[i])
@@ -142,34 +160,24 @@
 
 int unregister_mtd_user (struct mtd_notifier *old)
 {
-	struct mtd_notifier **prev = &mtd_notifiers;
-	struct mtd_notifier *cur;
 	int i;
 
 	down(&mtd_table_mutex);
 
-	while ((cur = *prev)) {
-		if (cur == old) {
-			*prev = cur->next;
-
-			MOD_DEC_USE_COUNT;
-
-			for (i=0; i< MAX_MTD_DEVICES; i++)
-				if (mtd_table[i])
-					old->remove(mtd_table[i]);
+	module_put(THIS_MODULE);
+
+	for (i=0; i< MAX_MTD_DEVICES; i++)
+		if (mtd_table[i])
+			old->remove(mtd_table[i]);
 			
-			up(&mtd_table_mutex);
-			return 0;
-		}
-		prev = &cur->next;
-	}
+	list_del(&old->list);
 	up(&mtd_table_mutex);
-	return 1;
+	return 0;
 }
 
 
 /**
- *	__get_mtd_device - obtain a validated handle for an MTD device
+ *	get_mtd_device - obtain a validated handle for an MTD device
  *	@mtd: last known address of the required MTD device
  *	@num: internal device number of the required MTD device
  *
@@ -177,11 +185,10 @@
  *	table, if any.	Given an address and num == -1, search the device table
  *	for a device with that address and return if it's still present. Given
  *	both, return the num'th driver only if its address matches. Return NULL
- *	if not. get_mtd_device() increases the use count, but
- *	__get_mtd_device() doesn't - you should generally use get_mtd_device().
+ *	if not.
  */
 	
-struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num)
+struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
 {
 	struct mtd_info *ret = NULL;
 	int i;
@@ -197,11 +204,28 @@
 		if (mtd && mtd != ret)
 			ret = NULL;
 	}
-	
+
+	if (ret && !try_module_get(ret->owner))
+		ret = NULL;
+
+	if (ret)
+		ret->usecount++;
+
 	up(&mtd_table_mutex);
 	return ret;
 }
 
+void put_mtd_device(struct mtd_info *mtd)
+{
+	int c;
+
+	down(&mtd_table_mutex);
+	c = --mtd->usecount;
+	up(&mtd_table_mutex);
+	BUG_ON(c < 0);
+
+	module_put(mtd->owner);
+}
 
 /* default_mtd_writev - default mtd writev method for MTD devices that
  *			dont implement their own
@@ -265,7 +289,8 @@
 
 EXPORT_SYMBOL(add_mtd_device);
 EXPORT_SYMBOL(del_mtd_device);
-EXPORT_SYMBOL(__get_mtd_device);
+EXPORT_SYMBOL(get_mtd_device);
+EXPORT_SYMBOL(put_mtd_device);
 EXPORT_SYMBOL(register_mtd_user);
 EXPORT_SYMBOL(unregister_mtd_user);
 EXPORT_SYMBOL(default_mtd_writev);
diff -Nur linux-2.4.30/drivers/mtd/mtdpart.c linux-2.4.30-linux-mips.org/drivers/mtd/mtdpart.c
--- linux-2.4.30/drivers/mtd/mtdpart.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/mtdpart.c	2005-05-12 18:01:29.000000000 +0200
@@ -5,7 +5,7 @@
  *
  * This code is GPL
  *
- * $Id: mtdpart.c,v 1.32 2002/10/21 13:40:05 jocke Exp $
+ * $Id: mtdpart.c,v 1.44 2004/05/06 08:23:02 gleixner Exp $
  *
  * 	02-21-2002	Thomas Gleixner <gleixner@autronix.de>
  *			added support for read_oob, write_oob
@@ -16,10 +16,11 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/list.h>
-
+#include <linux/config.h>
+#include <linux/kmod.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
-
+#include <linux/mtd/compatmac.h>
 
 /* Our partition linked list */
 static LIST_HEAD(mtd_partitions);
@@ -54,8 +55,12 @@
 		len = 0;
 	else if (from + len > mtd->size)
 		len = mtd->size - from;
-	return part->master->read (part->master, from + part->offset, 
+	if (part->master->read_ecc == NULL)	
+		return part->master->read (part->master, from + part->offset, 
 					len, retlen, buf);
+	else
+		return part->master->read_ecc (part->master, from + part->offset, 
+					len, retlen, buf, NULL, &mtd->oobinfo);
 }
 
 static int part_point (struct mtd_info *mtd, loff_t from, size_t len, 
@@ -78,9 +83,11 @@
 
 
 static int part_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, 
-			size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel)
+			size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel)
 {
 	struct mtd_part *part = PART(mtd);
+	if (oobsel == NULL)
+		oobsel = &mtd->oobinfo;
 	if (from >= mtd->size)
 		len = 0;
 	else if (from + len > mtd->size)
@@ -113,7 +120,7 @@
 			size_t *retlen, u_char *buf)
 {
 	struct mtd_part *part = PART(mtd);
-	return part->master->read_user_prot_reg (part->master, from, 
+	return part->master->read_fact_prot_reg (part->master, from, 
 					len, retlen, buf);
 }
 
@@ -127,17 +134,24 @@
 		len = 0;
 	else if (to + len > mtd->size)
 		len = mtd->size - to;
-	return part->master->write (part->master, to + part->offset, 
+	if (part->master->write_ecc == NULL)	
+		return part->master->write (part->master, to + part->offset, 
 					len, retlen, buf);
+	else
+		return part->master->write_ecc (part->master, to + part->offset, 
+					len, retlen, buf, NULL, &mtd->oobinfo);
+							
 }
 
 static int part_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
 			size_t *retlen, const u_char *buf,
-			 u_char *eccbuf, int oobsel)
+			 u_char *eccbuf, struct nand_oobinfo *oobsel)
 {
 	struct mtd_part *part = PART(mtd);
 	if (!(mtd->flags & MTD_WRITEABLE))
 		return -EROFS;
+	if (oobsel == NULL)
+		oobsel = &mtd->oobinfo;
 	if (to >= mtd->size)
 		len = 0;
 	else if (to + len > mtd->size)
@@ -174,25 +188,37 @@
 	struct mtd_part *part = PART(mtd);
 	if (!(mtd->flags & MTD_WRITEABLE))
 		return -EROFS;
-	return part->master->writev (part->master, vecs, count,
+	if (part->master->writev_ecc == NULL)	
+		return part->master->writev (part->master, vecs, count,
 					to + part->offset, retlen);
+	else
+		return part->master->writev_ecc (part->master, vecs, count,
+					to + part->offset, retlen,
+					NULL, &mtd->oobinfo);
 }
 
 static int part_readv (struct mtd_info *mtd,  struct iovec *vecs,
 			 unsigned long count, loff_t from, size_t *retlen)
 {
 	struct mtd_part *part = PART(mtd);
-	return part->master->readv (part->master, vecs, count,
+	if (part->master->readv_ecc == NULL)	
+		return part->master->readv (part->master, vecs, count,
 					from + part->offset, retlen);
+	else
+		return part->master->readv_ecc (part->master, vecs, count,
+					from + part->offset, retlen, 
+					NULL, &mtd->oobinfo);
 }
 
 static int part_writev_ecc (struct mtd_info *mtd,  const struct iovec *vecs,
 			 unsigned long count, loff_t to, size_t *retlen,
-			 u_char *eccbuf, int oobsel)
+			 u_char *eccbuf,  struct nand_oobinfo *oobsel)
 {
 	struct mtd_part *part = PART(mtd);
 	if (!(mtd->flags & MTD_WRITEABLE))
 		return -EROFS;
+	if (oobsel == NULL)
+		oobsel = &mtd->oobinfo;
 	return part->master->writev_ecc (part->master, vecs, count,
 					to + part->offset, retlen,
 					eccbuf, oobsel);
@@ -200,9 +226,11 @@
 
 static int part_readv_ecc (struct mtd_info *mtd,  struct iovec *vecs,
 			 unsigned long count, loff_t from, size_t *retlen,
-			 u_char *eccbuf, int oobsel)
+			 u_char *eccbuf,  struct nand_oobinfo *oobsel)
 {
 	struct mtd_part *part = PART(mtd);
+	if (oobsel == NULL)
+		oobsel = &mtd->oobinfo;
 	return part->master->readv_ecc (part->master, vecs, count,
 					from + part->offset, retlen, 
 					eccbuf, oobsel);
@@ -253,6 +281,26 @@
 	part->master->resume(part->master);
 }
 
+static int part_block_isbad (struct mtd_info *mtd, loff_t ofs)
+{
+	struct mtd_part *part = PART(mtd);
+	if (ofs >= mtd->size)
+		return -EINVAL;
+	ofs += part->offset;
+	return part->master->block_isbad(part->master, ofs);
+}
+
+static int part_block_markbad (struct mtd_info *mtd, loff_t ofs)
+{
+	struct mtd_part *part = PART(mtd);
+	if (!(mtd->flags & MTD_WRITEABLE))
+		return -EROFS;
+	if (ofs >= mtd->size)
+		return -EINVAL;
+	ofs += part->offset;
+	return part->master->block_markbad(part->master, ofs);
+}
+
 /* 
  * This function unregisters and destroy all slave MTD objects which are 
  * attached to the given master MTD object.
@@ -288,7 +336,7 @@
  */
 
 int add_mtd_partitions(struct mtd_info *master, 
-		       struct mtd_partition *parts,
+		       const struct mtd_partition *parts,
 		       int nbparts)
 {
 	struct mtd_part *slave;
@@ -321,7 +369,7 @@
 
 		slave->mtd.name = parts[i].name;
 		slave->mtd.bank_size = master->bank_size;
-		slave->mtd.module = master->module;
+		slave->mtd.owner = master->owner;
 
 		slave->mtd.read = part_read;
 		slave->mtd.write = part_write;
@@ -363,6 +411,10 @@
 			slave->mtd.lock = part_lock;
 		if (master->unlock)
 			slave->mtd.unlock = part_unlock;
+		if (master->block_isbad)
+			slave->mtd.block_isbad = part_block_isbad;
+		if (master->block_markbad)
+			slave->mtd.block_markbad = part_block_markbad;
 		slave->mtd.erase = part_erase;
 		slave->master = master;
 		slave->offset = parts[i].offset;
@@ -433,6 +485,9 @@
 				parts[i].name);
 		}
 
+		/* copy oobinfo from master */ 
+		memcpy(&slave->mtd.oobinfo, &master->oobinfo, sizeof(slave->mtd.oobinfo));
+
 		if(parts[i].mtdp)
 		{	/* store the object pointer (caller may or may not register it */
 			*parts[i].mtdp = &slave->mtd;
@@ -452,6 +507,75 @@
 EXPORT_SYMBOL(add_mtd_partitions);
 EXPORT_SYMBOL(del_mtd_partitions);
 
+static spinlock_t part_parser_lock = SPIN_LOCK_UNLOCKED;
+static LIST_HEAD(part_parsers);
+
+struct mtd_part_parser *get_partition_parser(const char *name)
+{
+	struct list_head *this;
+	void *ret = NULL;
+	spin_lock(&part_parser_lock);
+
+	list_for_each(this, &part_parsers) {
+		struct mtd_part_parser *p = list_entry(this, struct mtd_part_parser, list);
+
+		if (!strcmp(p->name, name) && try_module_get(p->owner)) {
+			ret = p;
+			break;
+		}
+	}
+	spin_unlock(&part_parser_lock);
+
+	return ret;
+}
+
+int register_mtd_parser(struct mtd_part_parser *p)
+{
+	spin_lock(&part_parser_lock);
+	list_add(&p->list, &part_parsers);
+	spin_unlock(&part_parser_lock);
+
+	return 0;
+}
+
+int deregister_mtd_parser(struct mtd_part_parser *p)
+{
+	spin_lock(&part_parser_lock);
+	list_del(&p->list);
+	spin_unlock(&part_parser_lock);
+	return 0;
+}
+
+int parse_mtd_partitions(struct mtd_info *master, const char **types, 
+			 struct mtd_partition **pparts, unsigned long origin)
+{
+	struct mtd_part_parser *parser;
+	int ret = 0;
+		
+	for ( ; ret <= 0 && *types; types++) {
+		parser = get_partition_parser(*types);
+#ifdef CONFIG_KMOD
+		if (!parser && !request_module("%s", *types))
+				parser = get_partition_parser(*types);
+#endif
+		if (!parser) {
+			printk(KERN_NOTICE "%s partition parsing not available\n",
+			       *types);
+			continue;
+		}
+		ret = (*parser->parse_fn)(master, pparts, origin);
+		if (ret > 0) {
+			printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", 
+			       ret, parser->name, master->name);
+		}
+		put_partition_parser(parser);
+	}
+	return ret;
+}
+
+EXPORT_SYMBOL_GPL(parse_mtd_partitions);
+EXPORT_SYMBOL_GPL(register_mtd_parser);
+EXPORT_SYMBOL_GPL(deregister_mtd_parser);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>");
diff -Nur linux-2.4.30/drivers/mtd/nand/au1550nd.c linux-2.4.30-linux-mips.org/drivers/mtd/nand/au1550nd.c
--- linux-2.4.30/drivers/mtd/nand/au1550nd.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nand/au1550nd.c	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,436 @@
+/*
+ *  drivers/mtd/nand/au1550nd.c
+ *
+ *  Copyright (C) 2004 Embedded Edge, LLC
+ *
+ * $Id: au1550nd.c,v 1.4 2004/05/12 06:23:58 ppopov 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/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#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 */
+#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 */
+#endif
+
+#ifdef CONFIG_MIPS_PB1200
+#define NAND_CS 1
+#endif
+
+#ifdef CONFIG_MIPS_DB1200
+#define NAND_CS 1
+#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];
+
+/*
+ * Define partitions for flash device
+ */
+const static struct mtd_partition partition_info[] = {
+	{ 
+		.name = "NAND FS 0",
+	  	.offset = 0,
+	  	.size = 8*1024*1024 
+	},
+	{ 
+		.name = "NAND FS 1",
+		.offset =  MTDPART_OFS_APPEND,
+ 		.size =    MTDPART_SIZ_FULL
+	}
+};
+#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
+
+static inline void write_cmd_reg(u8 cmd)
+{
+	if (nand_width)
+		*((volatile u8 *)(p_nand + MEM_STNAND_CMD)) = cmd;
+	else
+		*((volatile u16 *)(p_nand + MEM_STNAND_CMD)) = cmd;
+	au_sync();
+}
+
+static inline void write_addr_reg(u8 addr)
+{
+	if (nand_width)
+		*((volatile u8 *)(p_nand + MEM_STNAND_ADDR)) = addr;
+	else
+		*((volatile u16 *)(p_nand + MEM_STNAND_ADDR)) = addr;
+	au_sync();
+}
+
+static inline void write_data_reg(u8 data)
+{
+	if (nand_width)
+		*((volatile u8 *)(p_nand + MEM_STNAND_DATA)) = data;
+	else
+		*((volatile u16 *)(p_nand + MEM_STNAND_DATA)) = data;
+	au_sync();
+}
+
+static inline u32 read_data_reg(void)
+{
+	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;
+}
+
+void au1xxx_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+}
+
+int au1xxx_device_ready(struct mtd_info *mtd)
+{
+	int ready;
+	ready = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0;
+	return ready;
+}
+
+static u_char au1xxx_nand_read_byte(struct mtd_info *mtd)
+{
+	u_char ret;
+	ret = read_data_reg();
+	return ret;
+}
+
+static void au1xxx_nand_write_byte(struct mtd_info *mtd, u_char byte)
+{
+	write_data_reg((u8)byte);
+}
+
+static void 
+au1xxx_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+
+	for (i=0; i<len; i++)
+		write_data_reg(buf[i]);
+}
+
+static void 
+au1xxx_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+	int i;
+
+	for (i=0; i<len; i++)
+		buf[i] = (u_char)read_data_reg();
+}
+
+static int 
+au1xxx_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+
+	for (i=0; i<len; i++)
+		if (buf[i] != (u_char)read_data_reg())
+			return -EFAULT;
+
+	return 0;
+}
+
+static void au1xxx_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+	switch(chip) {
+	case -1:
+		/* deassert chip enable */
+		au_writel(au_readl(MEM_STNDCTL) & ~(1<<(4+NAND_CS)), MEM_STNDCTL);
+		break;
+	case 0:
+		/* assert (force assert) chip enable */
+		au_writel(au_readl(MEM_STNDCTL) | (1<<(4+NAND_CS)) , MEM_STNDCTL);
+		break;
+
+	default:
+		BUG();
+	}
+}
+
+static void au1xxx_nand_command (struct mtd_info *mtd, unsigned command, 
+		int column, int page_addr)
+{
+	register struct nand_chip *this = mtd->priv;
+
+	/*
+	 * 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;
+
+	/* This applies to read commands */	
+	default:
+		udelay (this->chip_delay);
+	}
+	
+	/* wait until command is processed */
+	while (!this->dev_ready(mtd));
+}
+
+
+/*
+ * Main initialization routine
+ */
+int __init au1xxx_nand_init (void)
+{
+	struct nand_chip *this;
+	u16 boot_swapboot = 0; /* default value */
+	u32 mem_staddr;
+	u32 nand_phys;
+
+	/* Allocate memory for MTD device structure and private data */
+	au1xxx_nand_mtd = kmalloc (sizeof(struct mtd_info) + 
+			sizeof (struct nand_chip), GFP_KERNEL);
+	if (!au1xxx_nand_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]);
+
+	/* Initialize structures */
+	memset((char *) au1xxx_nand_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;
+
+	/* 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);
+
+#ifdef CONFIG_MIPS_PB1550
+	/* 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) {
+		case 0:
+		case 2:
+		case 8:
+		case 0xC:
+		case 0xD:
+			/* x16 NAND Flash */
+			nand_width = 0;
+			printk("Pb1550 NAND: 16-bit NAND not supported by MTD\n");
+			break;
+		case 1:
+		case 9:
+		case 3:
+		case 0xE:
+		case 0xF:
+			/* x8 NAND Flash */
+			nand_width = 1;
+			break;
+		default:
+			printk("Pb1550 NAND: bad boot:swap\n");
+			kfree(au1xxx_nand_mtd);
+			return 1;
+	}
+#endif
+
+	/* Configure chip-select; normally done by boot code, e.g. YAMON */
+#ifdef NAND_STCFG
+	if (NAND_CS == 0) {
+		au_writel(NAND_STCFG,  MEM_STCFG0);
+		au_writel(NAND_STTIME, MEM_STTIME0);
+		au_writel(NAND_STADDR, MEM_STADDR0);
+	}
+	if (NAND_CS == 1) {
+		au_writel(NAND_STCFG,  MEM_STCFG1);
+		au_writel(NAND_STTIME, MEM_STTIME1);
+		au_writel(NAND_STADDR, MEM_STADDR1);
+	}
+	if (NAND_CS == 2) {
+		au_writel(NAND_STCFG,  MEM_STCFG2);
+		au_writel(NAND_STTIME, MEM_STTIME2);
+		au_writel(NAND_STADDR, MEM_STADDR2);
+	}
+	if (NAND_CS == 3) {
+		au_writel(NAND_STCFG,  MEM_STCFG3);
+		au_writel(NAND_STTIME, MEM_STTIME3);
+		au_writel(NAND_STADDR, MEM_STADDR3);
+	}
+#endif
+
+	/* Locate NAND chip-select in order to determine NAND physical 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))
+		mem_staddr = au_readl(MEM_STADDR1);
+	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))
+		mem_staddr = au_readl(MEM_STADDR3);
+
+	if (mem_staddr == 0x00000000) {
+		printk("Au1xxx NAND: ERROR WITH NAND CHIP-SELECT\n");
+		kfree(au1xxx_nand_mtd);
+		return 1;
+	}
+	nand_phys = (mem_staddr << 4) & 0xFFFC0000;
+
+	p_nand = (volatile struct nand_regs *)ioremap(nand_phys, 0x1000);
+
+	/* make controller and MTD agree */
+	if (NAND_CS == 0)
+		nand_width = au_readl(MEM_STCFG0) & (1<<22);
+	if (NAND_CS == 1)
+		nand_width = au_readl(MEM_STCFG1) & (1<<22);
+	if (NAND_CS == 2)
+		nand_width = au_readl(MEM_STCFG2) & (1<<22);
+	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;
+	/* 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;
+
+	/* Scan to find existence of the device */
+	if (nand_scan (au1xxx_nand_mtd, 1)) {
+		kfree (au1xxx_nand_mtd);
+		return -ENXIO;
+	}
+
+	/* Register the partitions */
+	add_mtd_partitions(au1xxx_nand_mtd, partition_info, NB_OF(partition_info));
+
+	return 0;
+}
+
+module_init(au1xxx_nand_init);
+
+/*
+ * Clean up routine
+ */
+#ifdef MODULE
+static void __exit au1xxx_nand_cleanup (void)
+{
+	struct nand_chip *this = (struct nand_chip *) &au1xxx_nand_mtd[1];
+
+	iounmap ((void *)p_nand);
+
+	/* Unregister partitions */
+	del_mtd_partitions(au1xxx_nand_mtd);
+
+	/* Unregister the device */
+	del_mtd_device (au1xxx_nand_mtd);
+
+	/* Free the MTD device structure */
+	kfree (au1xxx_nand_mtd);
+}
+module_exit(au1xxx_nand_cleanup);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Embedded Edge, LLC");
+MODULE_DESCRIPTION("NAND flash support for Alchemy");
diff -Nur linux-2.4.30/drivers/mtd/nand/autcpu12.c linux-2.4.30-linux-mips.org/drivers/mtd/nand/autcpu12.c
--- linux-2.4.30/drivers/mtd/nand/autcpu12.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nand/autcpu12.c	2005-05-12 18:01:29.000000000 +0200
@@ -4,9 +4,9 @@
  *  Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
  *
  *  Derived from drivers/mtd/spia.c
- * 	 Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
+ * 	 Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  * 
- * $Id: autcpu12.c,v 1.6 2002/11/11 15:47:56 gleixner Exp $
+ * $Id: autcpu12.c,v 1.14 2004/04/14 19:00:56 gleixner 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
@@ -25,10 +25,10 @@
  *			added page_cache
  *
  *	10-06-2002 TG	128K card support added
- *
  */
 
 #include <linux/slab.h>
+#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
@@ -70,6 +70,7 @@
 /*
  * Define partitions for flash devices
  */
+extern struct nand_oobinfo jffs2_oobinfo;
 
 static struct mtd_partition partition_info16k[] = {
 	{ name: "AUTCPU12 flash partition 1",
@@ -95,7 +96,7 @@
 	  size:   16 * SZ_1M },
 	{ name: "AUTCPU12 flash partition 2",
 	  offset: 16 * SZ_1M,
-	  size:   48 * SZ_1M},
+	  size:   48 * SZ_1M },
 };
 
 static struct mtd_partition partition_info128k[] = {
@@ -104,7 +105,7 @@
 	  size:   16 * SZ_1M },
 	{ name: "AUTCPU12 flash partition 2",
 	  offset: 16 * SZ_1M,
-	  size:   112 * SZ_1M},
+	  size:   112 * SZ_1M },
 };
 
 #define NUM_PARTITIONS16K 2
@@ -114,7 +115,7 @@
 /* 
  *	hardware specific access to control-lines
 */
-void autcpu12_hwcontrol(int cmd)
+static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd)
 {
 
 	switch(cmd){
@@ -133,12 +134,17 @@
 /*
 *	read device ready pin
 */
-int autcpu12_device_ready(void)
+int autcpu12_device_ready(struct mtd_info *mtd)
 {
 
 	return ( (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0;
 
 }
+
+/* Internal buffers. Page buffer and oob buffer for one block*/
+static u_char data_buf[512 + 16];
+static u_char oob_buf[16 * 32];
+
 /*
  * Main initialization routine
  */
@@ -183,29 +189,16 @@
 	this->chip_delay = 20;		
 	this->eccmode = NAND_ECC_SOFT;
 
+	/* Set internal data buffer */
+	this->data_buf = data_buf;
+	this->oob_buf = oob_buf;
+
 	/* Scan to find existance of the device */
-	if (nand_scan (autcpu12_mtd)) {
+	if (nand_scan (autcpu12_mtd, 1)) {
 		err = -ENXIO;
 		goto out_ior;
 	}
-
-	/* Allocate memory for internal data buffer */
-	this->data_buf = kmalloc (sizeof(u_char) * (autcpu12_mtd->oobblock + autcpu12_mtd->oobsize), GFP_KERNEL);
-	if (!this->data_buf) {
-		printk ("Unable to allocate NAND data buffer for AUTCPU12.\n");
-		err = -ENOMEM;
-		goto out_ior;
-	}
-
-	/* Allocate memory for internal data buffer */
-	this->data_cache = kmalloc (sizeof(u_char) * (autcpu12_mtd->oobblock + autcpu12_mtd->oobsize), GFP_KERNEL);
-	if (!this->data_cache) {
-		printk ("Unable to allocate NAND data cache for AUTCPU12.\n");
-		err = -ENOMEM;
-		goto out_buf;
-	}
-	this->cache_page = -1;
-
+	
 	/* Register the partitions */
 	switch(autcpu12_mtd->size){
 		case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break;
@@ -215,15 +208,11 @@
 		default: {
 			printk ("Unsupported SmartMedia device\n"); 
 			err = -ENXIO;
-			goto out_cac;
+			goto out_ior;
 		}
 	}
 	goto out;
 
-out_cac:
-	kfree (this->data_cache);    
-out_buf:
-	kfree (this->data_buf);    
 out_ior:
 	iounmap((void *)autcpu12_fio_base);
 out_mtd:
@@ -248,10 +237,6 @@
 	/* Unregister the device */
 	del_mtd_device (autcpu12_mtd);
 
-	/* Free internal data buffers */
-	kfree (this->data_buf);
-	kfree (this->data_cache);
-
 	/* unmap physical adress */
 	iounmap((void *)autcpu12_fio_base);
 
diff -Nur linux-2.4.30/drivers/mtd/nand/Config.in linux-2.4.30-linux-mips.org/drivers/mtd/nand/Config.in
--- linux-2.4.30/drivers/mtd/nand/Config.in	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nand/Config.in	2005-05-12 18:01:30.000000000 +0200
@@ -1,6 +1,6 @@
 # drivers/mtd/nand/Config.in
 
-# $Id: Config.in,v 1.11 2002/12/01 13:23:05 gleixner Exp $
+# $Id: Config.in,v 1.17 2004/05/10 07:28:18 ppopov Exp $
 
 mainmenu_option next_comment
 
@@ -11,26 +11,34 @@
    bool '    Verify NAND page writes' CONFIG_MTD_NAND_VERIFY_WRITE
 fi
 
-if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_P720T" = "y" ]; then
-   dep_tristate '  NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND
+if [ "$CONFIG_ARM" = "y" ]; then
+   dep_tristate '  NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND $CONFIG_ARCH_P720T
+   dep_tristate '  NAND Flash device on TOTO board' CONFIG_MTD_NAND_TOTO $CONFIG_MTD_NAND $CONFIG_ARCH_OMAP
+   dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND $CONFIG_ARCH_AUTCPU12
+   dep_tristate '  NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND $CONFIG_ARCH_EDB7312
 fi
 
-if [ "$CONFIG_ARCH_AUTCPU12" = "y" ]; then
-   dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND
+if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then
+   define_bool CONFIG_MTD_NAND_IDS y
+else
+  if [ "$CONFIG_MTD_DOC2001PLUS" = "m" -o "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" -o "$CONFIG_MTD_NAND" = "m" ]; then
+     define_bool CONFIG_MTD_NAND_IDS m
+  fi
 fi
 
-if [ "$CONFIG_ARCH_EDB7312" = "y" ]; then
-   dep_tristate '  NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND
+if [ "$CONFIG_TOSHIBA_RBTX4925" = "y" ]; then
+   dep_tristate '  SmartMedia Card on Toshiba RBTX4925 reference board' CONFIG_MTD_NAND_TX4925NDFMC $CONFIG_MTD_NAND $CONFIG_TOSHIBA_RBTX4925_MPLEX_NAND
 fi
 
-if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then
-   define_bool CONFIG_MTD_NAND_IDS y
+if [ "$CONFIG_TOSHIBA_RBTX4938" = "y" ]; then
+   dep_tristate '  NAND Flash device on Toshiba RBTX4938 reference board' CONFIG_MTD_NAND_TX4938NDFMC $CONFIG_MTD_NAND $CONFIG_TOSHIBA_RBTX4938_MPLEX_NAND
 fi
 
-if [ "$CONFIG_MTD_NAND_IDS" != "y" ]; then
-if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" -o "$CONFIG_MTD_NAND" = "m" ]; then
-   define_bool CONFIG_MTD_NAND_IDS m
-fi
+if [ "$CONFIG_PPCHAMELEONEVB" = "y" ]; then
+    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 
+fi
 endmenu
diff -Nur linux-2.4.30/drivers/mtd/nand/diskonchip.c linux-2.4.30-linux-mips.org/drivers/mtd/nand/diskonchip.c
--- linux-2.4.30/drivers/mtd/nand/diskonchip.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nand/diskonchip.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,534 @@
+/* 
+ * drivers/mtd/nand/diskonchip.c
+ *
+ * (C) 2003 Red Hat, Inc.
+ *
+ * Author: David Woodhouse <dwmw2@infradead.org>
+ *
+ * Interface to generic NAND code for M-Systems DiskOnChip devices
+ *
+ * $Id: diskonchip.c,v 1.9 2004/03/27 19:55:53 gleixner Exp $
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/doc2000.h>
+#include <linux/mtd/compatmac.h>
+
+struct doc_priv {
+	unsigned long virtadr;
+	unsigned long physadr;
+	u_char ChipID;
+	u_char CDSNControl;
+	int chips_per_floor; /* The number of chips detected on each floor */
+	int curfloor;
+	int curchip;
+};
+
+#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
+#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
+
+static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd);
+static void doc200x_select_chip(struct mtd_info *mtd, int chip);
+
+static int debug=0;
+MODULE_PARM(debug, "i");
+
+static int try_dword=1;
+MODULE_PARM(try_dword, "i");
+
+static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
+{
+	volatile char dummy;
+	int i;
+	
+	for (i = 0; i < cycles; i++) {
+		if (DoC_is_Millennium(doc))
+			dummy = ReadDOC(doc->virtadr, NOP);
+		else
+			dummy = ReadDOC(doc->virtadr, DOCStatus);
+	}
+	
+}
+/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
+static int _DoC_WaitReady(struct doc_priv *doc)
+{
+	unsigned long docptr = doc->virtadr;
+	unsigned long timeo = jiffies + (HZ * 10);
+
+	if(debug) printk("_DoC_WaitReady...\n");
+	/* Out-of-line routine to wait for chip response */
+	while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
+		if (time_after(jiffies, timeo)) {
+			printk("_DoC_WaitReady timed out.\n");
+			return -EIO;
+		}
+		udelay(1);
+		cond_resched();
+	}
+
+	return 0;
+}
+
+static inline int DoC_WaitReady(struct doc_priv *doc)
+{
+	unsigned long docptr = doc->virtadr;
+	int ret = 0;
+
+	DoC_Delay(doc, 4);
+
+	if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
+		/* Call the out-of-line routine to wait */
+		ret = _DoC_WaitReady(doc);
+
+	DoC_Delay(doc, 2);
+	if(debug) printk("DoC_WaitReady OK\n");
+	return ret;
+}
+
+static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+
+	if(debug)printk("write_byte %02x\n", datum);
+	WriteDOC(datum, docptr, CDSNSlowIO);
+	WriteDOC(datum, docptr, 2k_CDSN_IO);
+}
+
+static u_char doc2000_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+
+	ReadDOC(docptr, CDSNSlowIO);
+	u_char ret = ReadDOC(docptr, 2k_CDSN_IO);
+	if (debug) printk("read_byte returns %02x\n", ret);
+	return ret;
+}
+static void doc2000_writebuf(struct mtd_info *mtd, 
+			     const u_char *buf, int len)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+	int i;
+	if (debug)printk("writebuf of %d bytes: ", len);
+	for (i=0; i < len; i++) {
+		WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i);
+		if (debug && i < 16)
+			printk("%02x ", buf[i]);
+	}
+	if (debug) printk("\n");
+}
+
+static void doc2000_readbuf(struct mtd_info *mtd, 
+			    u_char *buf, int len)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+ 	int i;
+
+	if (debug)printk("readbuf of %d bytes: ", len);
+
+	for (i=0; i < len; i++) {
+		buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i);
+	}
+}
+
+static void doc2000_readbuf_dword(struct mtd_info *mtd, 
+			    u_char *buf, int len)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+ 	int i;
+
+	if (debug) printk("readbuf_dword of %d bytes: ", len);
+
+	if (unlikely((((unsigned long)buf)|len) & 3)) {
+		for (i=0; i < len; i++) {
+			*(uint8_t *)(&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i);
+		}
+	} else {
+		for (i=0; i < len; i+=4) {
+			*(uint32_t*)(&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i);
+		}
+	}
+}
+
+static int doc2000_verifybuf(struct mtd_info *mtd, 
+			      const u_char *buf, int len)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+	int i;
+
+	for (i=0; i < len; i++)
+		if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO))
+			return -EFAULT;
+	return 0;
+}
+
+static uint16_t doc200x_ident_chip(struct mtd_info *mtd, int nr)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	uint16_t ret;
+
+	doc200x_select_chip(mtd, nr);
+	doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
+	this->write_byte(mtd, NAND_CMD_READID);
+	doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
+	doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
+	this->write_byte(mtd, 0);
+	doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
+	
+	ret = this->read_byte(mtd) << 8;
+	ret |= this->read_byte(mtd);
+
+	if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) {
+		/* First chip probe. See if we get same results by 32-bit access */
+		union {
+			uint32_t dword;
+			uint8_t byte[4];
+		} ident;
+		struct nand_chip *this = mtd->priv;
+		struct doc_priv *doc = (void *)this->priv;
+		unsigned long docptr = doc->virtadr;
+
+		doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
+		doc2000_write_byte(mtd, NAND_CMD_READID);
+		doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
+		doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
+		doc2000_write_byte(mtd, 0);
+		doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
+
+		ident.dword = readl(docptr + DoC_2k_CDSN_IO);
+		if (((ident.byte[0] << 8) | ident.byte[1]) == ret) {
+			printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n");
+			this->read_buf = &doc2000_readbuf_dword;
+		}
+	}
+		
+	return ret;
+}
+
+static void doc2000_count_chips(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	uint16_t mfrid;
+	int i;
+
+	/* Max 4 chips per floor on DiskOnChip 2000 */
+	doc->chips_per_floor = 4;
+
+	/* Find out what the first chip is */
+	mfrid = doc200x_ident_chip(mtd, 0);
+
+	/* Find how many chips in each floor. */
+	for (i = 1; i < 4; i++) {
+		if (doc200x_ident_chip(mtd, i) != mfrid)
+			break;
+	}
+	doc->chips_per_floor = i;
+}
+
+static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
+{
+	struct doc_priv *doc = (void *)this->priv;
+
+	int status;
+	
+	DoC_WaitReady(doc);
+	this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
+	DoC_WaitReady(doc);
+	status = (int)this->read_byte(mtd);
+
+	return status;
+}
+
+static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+
+	WriteDOC(datum, docptr, CDSNSlowIO);
+	WriteDOC(datum, docptr, Mil_CDSN_IO);
+	WriteDOC(datum, docptr, WritePipeTerm);
+}
+
+static u_char doc2001_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+
+	ReadDOC(docptr, CDSNSlowIO);
+	/* 11.4.5 -- delay twice to allow extended length cycle */
+	DoC_Delay(doc, 2);
+	ReadDOC(docptr, ReadPipeInit);
+	return ReadDOC(docptr, Mil_CDSN_IO);
+}
+
+static void doc2001_writebuf(struct mtd_info *mtd, 
+			     const u_char *buf, int len)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+	int i;
+
+	for (i=0; i < len; i++)
+		WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
+	/* Terminate write pipeline */
+	WriteDOC(0x00, docptr, WritePipeTerm);
+}
+
+static void doc2001_readbuf(struct mtd_info *mtd, 
+			    u_char *buf, int len)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+	int i;
+
+	/* Start read pipeline */
+	ReadDOC(docptr, ReadPipeInit);
+
+	for (i=0; i < len-1; i++)
+		buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
+
+	/* Terminate read pipeline */
+	buf[i] = ReadDOC(docptr, LastDataRead);
+}
+static int doc2001_verifybuf(struct mtd_info *mtd, 
+			     const u_char *buf, int len)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+	int i;
+
+	/* Start read pipeline */
+	ReadDOC(docptr, ReadPipeInit);
+
+	for (i=0; i < len-1; i++)
+		if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
+			ReadDOC(docptr, LastDataRead);
+			return i;
+		}
+	if (buf[i] != ReadDOC(docptr, LastDataRead))
+		return i;
+	return 0;
+}
+
+static void doc200x_select_chip(struct mtd_info *mtd, int chip)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+	int floor = 0;
+
+	/* 11.4.4 -- deassert CE before changing chip */
+	doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE);
+
+	if(debug)printk("select chip (%d)\n", chip);
+
+	if (chip == -1)
+		return;
+
+	floor = chip / doc->chips_per_floor;
+	chip -= (floor *  doc->chips_per_floor);
+
+	WriteDOC(floor, docptr, FloorSelect);
+	WriteDOC(chip, docptr, CDSNDeviceSelect);
+
+	doc200x_hwcontrol(mtd, NAND_CTL_SETNCE);
+
+	doc->curchip = chip;
+	doc->curfloor = floor;
+}
+
+static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+
+	switch(cmd) {
+	case NAND_CTL_SETNCE:
+		doc->CDSNControl |= CDSN_CTRL_CE;
+		break;
+	case NAND_CTL_CLRNCE:
+		doc->CDSNControl &= ~CDSN_CTRL_CE;
+		break;
+	case NAND_CTL_SETCLE:
+		doc->CDSNControl |= CDSN_CTRL_CLE;
+		break;
+	case NAND_CTL_CLRCLE:
+		doc->CDSNControl &= ~CDSN_CTRL_CLE;
+		break;
+	case NAND_CTL_SETALE:
+		doc->CDSNControl |= CDSN_CTRL_ALE;
+		break;
+	case NAND_CTL_CLRALE:
+		doc->CDSNControl &= ~CDSN_CTRL_ALE;
+		break;
+	case NAND_CTL_SETWP:
+		doc->CDSNControl |= CDSN_CTRL_WP;
+		break;
+	case NAND_CTL_CLRWP:
+		doc->CDSNControl &= ~CDSN_CTRL_WP;
+		break;
+	}
+	if (debug)printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl);
+	WriteDOC(doc->CDSNControl, docptr, CDSNControl);
+	/* 11.4.3 -- 4 NOPs after CSDNControl write */
+	DoC_Delay(doc, 4);
+}
+
+static int doc200x_dev_ready(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned long docptr = doc->virtadr;
+
+	/* 11.4.2 -- must NOP four times before checking FR/B# */
+	DoC_Delay(doc, 4);
+	if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
+		if(debug)
+			printk("not ready\n");
+		return 0;
+	}
+	/* 11.4.2 -- Must NOP twice if it's ready */
+	DoC_Delay(doc, 2);
+	if (debug)printk("was ready\n");
+	return 1; 
+}	
+
+static int doc200x_block_bad(struct mtd_info *mtd, unsigned long block)
+{
+	/* FIXME: Look it up in the BBT */
+	return 0;
+}
+
+struct doc_priv mydoc = {
+	.physadr = 0xd4000,
+	.curfloor = -1,
+	.curchip = -1,
+};
+
+u_char mydatabuf[528];
+
+struct nand_chip mynand = {
+	.priv = (void *)&mydoc,
+	.select_chip = doc200x_select_chip,
+	.hwcontrol = doc200x_hwcontrol,
+	.dev_ready = doc200x_dev_ready,
+	.waitfunc = doc200x_wait,
+	.block_bad = doc200x_block_bad,
+	.eccmode = NAND_ECC_SOFT,
+	.data_buf = mydatabuf,
+};
+
+struct mtd_info mymtd = {
+	.priv = (void *)&mynand,
+	.owner = THIS_MODULE,
+};
+
+int __init init_nanddoc(void)
+{
+	mydoc.virtadr = (unsigned long)ioremap(mydoc.physadr, DOC_IOREMAP_LEN);
+	int nrchips = 1;
+	char *name;
+
+	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, 
+		 mydoc.virtadr, DOCControl);
+	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, 
+		 mydoc.virtadr, DOCControl);
+
+	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, 
+		 mydoc.virtadr, DOCControl);
+	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, 
+		 mydoc.virtadr, DOCControl);
+
+	mydoc.ChipID = ReadDOC(mydoc.virtadr, ChipID);
+
+	switch(mydoc.ChipID) {
+	case DOC_ChipID_DocMil:
+		mynand.write_byte = doc2001_write_byte;
+		mynand.read_byte = doc2001_read_byte;
+		mynand.write_buf = doc2001_writebuf;
+		mynand.read_buf = doc2001_readbuf;
+		mynand.verify_buf = doc2001_verifybuf;
+
+		ReadDOC(mydoc.virtadr, ChipID);
+		ReadDOC(mydoc.virtadr, ChipID);
+		if (ReadDOC(mydoc.virtadr, ChipID) != DOC_ChipID_DocMil) {
+			/* It's not a Millennium; it's one of the newer
+			   DiskOnChip 2000 units with a similar ASIC. 
+			   Treat it like a Millennium, except that it
+			   can have multiple chips. */
+			doc2000_count_chips(&mymtd);
+			nrchips = 4 * mydoc.chips_per_floor;
+			name = "DiskOnChip 2000 (INFTL Model)";
+		} else {
+			/* Bog-standard Millennium */
+			mydoc.chips_per_floor = 1;
+			nrchips = 1;
+			name = "DiskOnChip Millennium";
+		}
+		break;
+
+	case DOC_ChipID_Doc2k:
+		mynand.write_byte = doc2000_write_byte;
+		mynand.read_byte = doc2000_read_byte;
+		mynand.write_buf = doc2000_writebuf;
+		mynand.read_buf = doc2000_readbuf;
+		mynand.verify_buf = doc2000_verifybuf;
+
+		doc2000_count_chips(&mymtd);
+		nrchips = 4 * mydoc.chips_per_floor;
+		name = "DiskOnChip 2000 (NFTL Model)";
+		mydoc.CDSNControl |= CDSN_CTRL_FLASH_IO;
+
+		break;
+
+	default:
+		return -EIO;
+	}
+	if (nand_scan(&mymtd, nrchips)) {
+		iounmap((void *)mydoc.virtadr);
+		return -EIO;
+	}
+	mymtd.name = name;
+	add_mtd_device(&mymtd);
+
+	return 0;
+}
+
+void __exit cleanup_nanddoc(void)
+{
+	del_mtd_device(&mymtd);
+	iounmap((void *)mydoc.virtadr);
+}
+	
+module_init(init_nanddoc);
+module_exit(cleanup_nanddoc);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("M-Systems DiskOnChip 2000 and Millennium device driver\n");
diff -Nur linux-2.4.30/drivers/mtd/nand/edb7312.c linux-2.4.30-linux-mips.org/drivers/mtd/nand/edb7312.c
--- linux-2.4.30/drivers/mtd/nand/edb7312.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nand/edb7312.c	2005-05-12 18:01:29.000000000 +0200
@@ -6,7 +6,7 @@
  *  Derived from drivers/mtd/nand/autcpu12.c
  *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
  *
- * $Id: edb7312.c,v 1.3 2002/06/06 12:58:16 mag Exp $
+ * $Id: edb7312.c,v 1.7 2003/07/11 15:12:29 dwmw2 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,6 +20,7 @@
 
 #include <linux/slab.h>
 #include <linux/module.h>
+#include <linux/init.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/partitions.h>
@@ -77,16 +78,13 @@
 };
 #define NUM_PARTITIONS 1
 
-extern int parse_cmdline_partitions(struct mtd_info *master, 
-				    struct mtd_partition **pparts,
-				    const char *mtd_id);
 #endif
 
 
 /* 
  *	hardware specific access to control-lines
  */
-static void ep7312_hwcontrol(int cmd) 
+static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd) 
 {
 	switch(cmd) {
 		
@@ -116,10 +114,13 @@
 /*
  *	read device ready pin
  */
-static int ep7312_device_ready(void)
+static int ep7312_device_ready(struct mtd_info *mtd)
 {
 	return 1;
 }
+#ifdef CONFIG_MTD_PARTITIONS
+const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
 
 /*
  * Main initialization routine
@@ -174,7 +175,7 @@
 	this->chip_delay = 15;
 	
 	/* Scan to find existence of the device */
-	if (nand_scan (ep7312_mtd)) {
+	if (nand_scan (ep7312_mtd, 1)) {
 		iounmap((void *)ep7312_fio_base);
 		kfree (ep7312_mtd);
 		return -ENXIO;
@@ -189,27 +190,16 @@
 		return -ENOMEM;
 	}
 	
-	/* Allocate memory for internal data buffer */
-	this->data_cache = kmalloc (sizeof(u_char) * (ep7312_mtd->oobblock + ep7312_mtd->oobsize), GFP_KERNEL);
-	if (!this->data_cache) {
-		printk("Unable to allocate NAND data cache for EDB7312.\n");
-		kfree (this->data_buf);
-		iounmap((void *)ep7312_fio_base);
-		kfree (ep7312_mtd);
-		return -ENOMEM;
-	}
-	this->cache_page = -1;
-	
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-	mtd_parts_nb = parse_cmdline_partitions(ep7312_mtd, &mtd_parts, 
-						"edb7312-nand");
+#ifdef CONFIG_PARTITIONS
+	ep7312_mtd->name = "edb7312-nand";
+	mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes,
+					    &mtd_parts, 0);
 	if (mtd_parts_nb > 0)
-	  part_type = "command line";
+		part_type = "command line";
 	else
-	  mtd_parts_nb = 0;
+		mtd_parts_nb = 0;
 #endif
-	if (mtd_parts_nb == 0)
-	{
+	if (mtd_parts_nb == 0) {
 		mtd_parts = partition_info;
 		mtd_parts_nb = NUM_PARTITIONS;
 		part_type = "static";
@@ -236,7 +226,6 @@
 	
 	/* Free internal data buffer */
 	kfree (this->data_buf);
-	kfree (this->data_cache);
 	
 	/* Free the MTD device structure */
 	kfree (ep7312_mtd);
diff -Nur linux-2.4.30/drivers/mtd/nand/Makefile linux-2.4.30-linux-mips.org/drivers/mtd/nand/Makefile
--- linux-2.4.30/drivers/mtd/nand/Makefile	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nand/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -1,16 +1,22 @@
 #
 # linux/drivers/nand/Makefile
 #
-# $Id: Makefile,v 1.10 2002/12/01 13:23:05 gleixner Exp $
+# $Id: Makefile.common,v 1.6 2004/03/18 09:28:19 gleixner Exp $
 
-O_TARGET	:= nandlink.o
-
-export-objs	:= nand.o nand_ecc.o nand_ids.o
+ifeq ($(PATCHLEVEL),4)
+O_TARGET			:= nandlink.o
+export-objs			:= nand.o nand_ecc.o nand_ids.o
+endif
 
 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 $(TOPDIR)/Rules.make
+-include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/drivers/mtd/nand/nand.c linux-2.4.30-linux-mips.org/drivers/mtd/nand/nand.c
--- linux-2.4.30/drivers/mtd/nand/nand.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nand/nand.c	2005-05-12 18:01:29.000000000 +0200
@@ -8,114 +8,31 @@
  *	Additional technical information is available on
  *	http://www.linux-mtd.infradead.org/tech/nand.html
  *	
- *  Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
+ *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  * 		  2002 Thomas Gleixner (tglx@linutronix.de)
  *
- *  10-29-2001  Thomas Gleixner (tglx@linutronix.de)
- * 		- Changed nand_chip structure for controlline function to
- *		support different hardware structures (Access to
- *		controllines ALE,CLE,NCE via hardware specific function. 
- *		- exit out of "failed erase block" changed, to avoid
- *		driver hangup
- *		- init_waitqueue_head added in function nand_scan !!
- *
- *  01-30-2002  Thomas Gleixner (tglx@linutronix.de)
- *		change in nand_writev to block invalid vecs entries
- *
- *  02-11-2002  Thomas Gleixner (tglx@linutronix.de)
- *		- major rewrite to avoid duplicated code
- *		  common nand_write_page function  
- *		  common get_chip function 
- *		- added oob_config structure for out of band layouts
- *		- write_oob changed for partial programming
- *		- read cache for faster access for subsequent reads
- *		from the same page.
- *		- support for different read/write address
- *		- support for device ready/busy line
- *		- read oob for more than one page enabled
- *
- *  02-27-2002	Thomas Gleixner (tglx@linutronix.de)
- *		- command-delay can be programmed
- *		- fixed exit from erase with callback-function enabled
- *
- *  03-21-2002  Thomas Gleixner (tglx@linutronix.de)
- *		- DEBUG improvements provided by Elizabeth Clarke 
- *		(eclarke@aminocom.com)
- *		- added zero check for this->chip_delay
- *
- *  04-03-2002  Thomas Gleixner (tglx@linutronix.de)
- *		- added added hw-driver supplied command and wait functions
- *		- changed blocking for erase (erase suspend enabled)
- *		- check pointers before accessing flash provided by
- *		John Hall (john.hall@optionexist.co.uk)
- *
- *  04-09-2002  Thomas Gleixner (tglx@linutronix.de)
- *		- nand_wait repaired
- *
- *  04-28-2002  Thomas Gleixner (tglx@linutronix.de)	
- *		- OOB config defines moved to nand.h 
- *
- *  08-01-2002  Thomas Gleixner (tglx@linutronix.de)	
- *		- changed my mailaddress, added pointer to tech/nand.html
- *
- *  08-07-2002 	Thomas Gleixner (tglx@linutronix.de)
- *		forced bad block location to byte 5 of OOB, even if
- *		CONFIG_MTD_NAND_ECC_JFFS2 is not set, to prevent
- *		erase /dev/mtdX from erasing bad blocks and destroying
- *		bad block info
- *
- *  08-10-2002 	Thomas Gleixner (tglx@linutronix.de)
- *		Fixed writing tail of data. Thanks to Alice Hennessy
- *		<ahennessy@mvista.com>.
- *
- *  08-10-2002 	Thomas Gleixner (tglx@linutronix.de)
- *		nand_read_ecc and nand_write_page restructured to support
- *		hardware ECC. Thanks to Steven Hein (ssh@sgi.com)
- *		for basic implementation and suggestions.
- *		3 new pointers in nand_chip structure:
- *		calculate_ecc, correct_data, enabled_hwecc 					 
- *		forcing all hw-drivers to support page cache
- *		eccvalid_pos is now mandatory
- *
- *  08-17-2002	tglx: fixed signed/unsigned missmatch in write.c
- *		Thanks to Ken Offer <koffer@arlut.utexas.edu> 	
- *
- *  08-29-2002  tglx: use buffered read/write only for non pagealigned 
- *		access, speed up the aligned path by using the fs-buffer
- *		reset chip removed from nand_select(), implicit done
- *		only, when erase is interrupted
- *		waitfuntion use yield, instead of schedule_timeout
- *		support for 6byte/512byte hardware ECC
- *		read_ecc, write_ecc extended for different oob-layout
- *		selections: Implemented NAND_NONE_OOB, NAND_JFFS2_OOB,
- *		NAND_YAFFS_OOB. fs-driver gives one of these constants
- *		to select the oob-layout fitting the filesystem.
- *		oobdata can be read together with the raw data, when
- *		the fs-driver supplies a big enough buffer.
- *		size = 12 * number of pages to read (256B pagesize)
- *		       24 * number of pages to read (512B pagesize)
- *		the buffer contains 8/16 byte oobdata and 4/8 byte
- *		returncode from calculate_ecc
- *		oobdata can be given from filesystem to program them
- *		in one go together with the raw data. ECC codes are
- *		filled in at the place selected by oobsel.
- *
- *  09-04-2002  tglx: fixed write_verify (John Hall (john.hall@optionexist.co.uk))
- *
- *  11-11-2002  tglx: fixed debug output in nand_write_page 
- *		(John Hall (john.hall@optionexist.co.uk))
- *
- *  11-25-2002  tglx: Moved device ID/ manufacturer ID from nand_ids.h
- *		Splitted device ID and manufacturer ID table. 
- *		Removed CONFIG_MTD_NAND_ECC, as it defaults to ECC_NONE for
- *		mtd->read / mtd->write and is controllable by the fs driver
- *		for mtd->read_ecc / mtd->write_ecc
- *		some minor cleanups
+ *  02-08-2004  tglx: support for strange chips, which cannot auto increment 
+ *		pages on read / read_oob
  *
- *  12-05-2000  tglx: Dave Ellis (DGE@sixnetio) provided the fix for
- *		WRITE_VERIFY long time ago. Thanks for remembering me.	
+ *  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
+ *
+ * Credits:
+ *	David Woodhouse for adding multichip support  
  *	
- * $Id: nand.c,v 1.36 2002/12/05 20:59:11 gleixner Exp $
+ *	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.
+ *
+ * $Id: nand.c,v 1.85 2004/05/06 08:22:19 gleixner 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
@@ -130,102 +47,417 @@
 #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>
 
-/*
- * Macros for low-level register control
- */
-#define nand_select()	this->hwcontrol(NAND_CTL_SETNCE);
-
-#define nand_deselect() this->hwcontrol(NAND_CTL_CLRNCE);
+/* 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} }
+};
 
-/*
- * out of band configuration for different filesystems
- */
-static int oobconfigs[][6] = {
-	{ 0,0,0,0,0,0},
+static struct nand_oobinfo nand_oob_16 = {
+	.useecc = MTD_NANDECC_AUTOPLACE,
+	.eccbytes = 6,
+	.eccpos = {0, 1, 2, 3, 6, 7},
+	.oobfree = { {8, 8} }
+};
 
-	{ NAND_JFFS2_OOB_ECCPOS0, NAND_JFFS2_OOB_ECCPOS1, NAND_JFFS2_OOB_ECCPOS2,
-	NAND_JFFS2_OOB_ECCPOS3, NAND_JFFS2_OOB_ECCPOS4, NAND_JFFS2_OOB_ECCPOS5 },
+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} }
+};
 
-	{ NAND_YAFFS_OOB_ECCPOS0, NAND_YAFFS_OOB_ECCPOS1, NAND_YAFFS_OOB_ECCPOS2,
-	NAND_YAFFS_OOB_ECCPOS3, NAND_YAFFS_OOB_ECCPOS4, NAND_YAFFS_OOB_ECCPOS5 }	
+/* 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, int oobsel);
+			  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, int oobsel);
+			   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 iovec *vecs,
 			unsigned long count, loff_t to, size_t * retlen);
 static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs,
-			unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, int oobsel);
+			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);
-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, int col, 
-			int last, u_char *oob_buf, int oobsel);
+
+/* 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 void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state, int *erase_state);
+
+/* Deselect and wake up anyone waiting on the device */
+static void nand_release_chip (struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+
+	/* De-select the NAND device */
+	this->select_chip(mtd, -1);
+	/* Release the chip */
+	spin_lock_bh (&this->chip_lock);
+	this->state = FL_READY;
+	wake_up (&this->wq);
+	spin_unlock_bh (&this->chip_lock);
+}
+
+static u_char nand_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	return readb(this->IO_ADDR_R);
+}
+
+static void nand_write_byte(struct mtd_info *mtd, u_char byte)
+{
+	struct nand_chip *this = mtd->priv;
+	writeb(byte, this->IO_ADDR_W);
+}
+
+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));
+}
+
+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);
+}
+
+static u16 nand_read_word(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	return readw(this->IO_ADDR_R);
+}
+
+static void nand_write_word(struct mtd_info *mtd, u16 word)
+{
+	struct nand_chip *this = mtd->priv;
+	writew(word, this->IO_ADDR_W);
+}
+
+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();
+	}
+}
+
+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);
+}
+
+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);
+}
+
+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;
+}
+
+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);
+		
+}
+
+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);
+}
+
+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;
+}
+
+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) {
+		/* Shift to get page */
+		page = ((int) ofs) >> this->page_shift;
+		chipnr = (int)((unsigned long) ofs / this->chipsize);
+
+		/* Grab the lock and see if the device is available */
+		nand_get_chip (this, mtd, FL_READING, NULL);
+
+		/* 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_chip(mtd);
+	}	
+	
+	return res;
+}
+
+/* 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;
+	
+	ofs += mtd->oobsize + (this->badblockpos & ~0x01);
+	
+	/* We write two bytes, so we dont have to mess with 16 bit access */
+	return nand_write_oob (mtd, ofs , 2, &retlen, buf);
+}
+
+/* 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) & 0x80) ? 0 : 1; 
+}
+
 /*
  * Send command to NAND device
  */
 static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
 {
 	register struct nand_chip *this = mtd->priv;
-	register unsigned long NAND_IO_ADDR = this->IO_ADDR_W;
+
+	/* Adjust columns for 16 bit buswidth */
+	if (this->options & NAND_BUSWIDTH_16)
+		column >>= 1;
 
 	/* Begin command latch cycle */
-	this->hwcontrol (NAND_CTL_SETCLE);
+	this->hwcontrol(mtd, NAND_CTL_SETCLE);
 	/*
 	 * Write out the command to the device.
 	 */
-	if (command != NAND_CMD_SEQIN)
-		writeb (command, NAND_IO_ADDR);
-	else {
-		if (mtd->oobblock == 256 && column >= 256) {
-			column -= 256;
-			writeb (NAND_CMD_READOOB, NAND_IO_ADDR);
-			writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
-		} else if (mtd->oobblock == 512 && column >= 256) {
-			if (column < 512) {
-				column -= 256;
-				writeb (NAND_CMD_READ1, NAND_IO_ADDR);
-				writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
-			} else {
-				column -= 512;
-				writeb (NAND_CMD_READOOB, NAND_IO_ADDR);
-				writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
-			}
+	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 {
-			writeb (NAND_CMD_READ0, NAND_IO_ADDR);
-			writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
+			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 (NAND_CTL_CLRCLE);
+	this->hwcontrol(mtd, NAND_CTL_CLRCLE);
 
 	if (column != -1 || page_addr != -1) {
-		this->hwcontrol (NAND_CTL_SETALE);
+		this->hwcontrol(mtd, NAND_CTL_SETALE);
 
 		/* Serially input address */
 		if (column != -1)
-			writeb (column, NAND_IO_ADDR);
+			this->write_byte(mtd, column);
 		if (page_addr != -1) {
-			writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR);
-			writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR);
+			this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
+			this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
 			/* One more address cycle for higher density devices */
-			if (mtd->size & 0x0c000000) 
-				writeb ((unsigned char) ((page_addr >> 16) & 0x0f), NAND_IO_ADDR);
+			if (this->chipsize & 0x0c000000) 
+				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) & 0x40));
+		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;
+		}	
+	}
+	
+	/* wait until command is processed */
+	while (!this->dev_ready(mtd));
+}
+
+/*
+ * 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;
+	}
+	
+	/* Adjust columns for 16 bit buswidth */
+	if (this->options & NAND_BUSWIDTH_16)
+		column >>= 1;
+		
+	/* Begin command latch cycle */
+	this->hwcontrol(mtd, NAND_CTL_SETCLE);
+	/* Write out the command to the device. */
+	this->write_byte(mtd, command);
+	/* 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) {
+			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 (NAND_CTL_CLRALE);
+		this->hwcontrol(mtd, NAND_CTL_CLRALE);
 	}
 	
 	/* 
@@ -234,6 +466,7 @@
 	*/
 	switch (command) {
 			
+	case NAND_CMD_CACHEDPROG:
 	case NAND_CMD_PAGEPROG:
 	case NAND_CMD_ERASE1:
 	case NAND_CMD_ERASE2:
@@ -241,15 +474,26 @@
 	case NAND_CMD_STATUS:
 		return;
 
+
 	case NAND_CMD_RESET:
 		if (this->dev_ready)	
 			break;
-		this->hwcontrol (NAND_CTL_SETCLE);
-		writeb (NAND_CMD_STATUS, NAND_IO_ADDR);
-		this->hwcontrol (NAND_CTL_CLRCLE);
-		while ( !(readb (this->IO_ADDR_R) & 0x40));
+		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) & 0x40));
 		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:
 		/* 
@@ -263,42 +507,29 @@
 	}
 	
 	/* wait until command is processed */
-	while (!this->dev_ready());
+	while (!this->dev_ready(mtd));
 }
 
 /*
  *	Get chip for selected access
  */
-static inline void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state, int *erase_state)
+static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state, int *erase_state)
 {
 
 	DECLARE_WAITQUEUE (wait, current);
 
 	/* 
 	 * Grab the lock and see if the device is available 
-	 * For erasing, we keep the spinlock until the
-	 * erase command is written. 
 	*/
 retry:
 	spin_lock_bh (&this->chip_lock);
 
 	if (this->state == FL_READY) {
 		this->state = new_state;
-		if (new_state != FL_ERASING)
-			spin_unlock_bh (&this->chip_lock);
+		spin_unlock_bh (&this->chip_lock);
 		return;
 	}
 
-	if (this->state == FL_ERASING) {
-		if (new_state != FL_ERASING) {
-			this->state = new_state;
-			spin_unlock_bh (&this->chip_lock);
-			nand_select ();	/* select in any case */
-			this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
-			return;
-		}
-	}
-
 	set_current_state (TASK_UNINTERRUPTIBLE);
 	add_wait_queue (&this->wq, &wait);
 	spin_unlock_bh (&this->chip_lock);
@@ -312,6 +543,10 @@
  * Erase can take up to 400ms and program up to 20ms according to 
  * general NAND and SmartMedia specs
  *
+ * @mtd:	MTD device structure
+ * @this:	NAND chip structure
+ * @state:	state to select the max. timeout value
+ *
 */
 static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
 {
@@ -334,17 +569,17 @@
 			return 0;
 		}
 		if (this->dev_ready) {
-			if (this->dev_ready ())
+			if (this->dev_ready(mtd))
 				break;
 		}
-		if (readb (this->IO_ADDR_R) & 0x40)
+		if (this->read_byte(mtd) & 0x40)
 			break;
 						
 		spin_unlock_bh (&this->chip_lock);
 		yield ();
 		spin_lock_bh (&this->chip_lock);
 	}
-	status = (int) readb (this->IO_ADDR_R);
+	status = (int) this->read_byte(mtd);
 	spin_unlock_bh (&this->chip_lock);
 
 	return status;
@@ -352,215 +587,239 @@
 
 /*
  *	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 :)
+ *
+ * @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
  */
-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this,
-			    int page, int col, int last, u_char *oob_buf, int oobsel)
+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[6], *oob_data;
-	int	eccmode = oobsel ? this->eccmode : NAND_ECC_NONE;
-	int  	*oob_config = oobconfigs[oobsel];
-
-	/* pad oob area, if we have no oob buffer from fs-driver */
-	if (!oob_buf) {
-		oob_data = &this->data_buf[mtd->oobblock];
-		for (i = 0; i < mtd->oobsize; i++)
-			oob_data[i] = 0xff;
-	} else 
-		oob_data = oob_buf;
+	u_char	ecc_code[6];
+	int	eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
+	int  	*oob_config = oobsel->eccpos;
+	int	datidx = 0, eccidx = 0, eccsteps = this->eccsteps;
+	
+	/* FIXME: Enable cached programming */
+	cached = 0;
 	
-	/* software ecc 3 Bytes ECC / 256 Byte Data ? */
-	if (eccmode == NAND_ECC_SOFT) {
-		/* Read back previous written data, if col > 0 */
-		if (col) {
-			this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
-			for (i = 0; i < col; i++)
-				this->data_poi[i] = readb (this->IO_ADDR_R);
-		}
-		if ((col < this->eccsize) && (last >= this->eccsize)) {
-			this->calculate_ecc (&this->data_poi[0], &(ecc_code[0]));
-			for (i = 0; i < 3; i++)
-				oob_data[oob_config[i]] = ecc_code[i];
-		}
-		/* Calculate and write the second ECC if we have enough data */
-		if ((mtd->oobblock == 512) && (last == 512)) {
-			this->calculate_ecc (&this->data_poi[256], &(ecc_code[3]));
-			for (i = 3; i < 6; i++)
-				oob_data[oob_config[i]] = ecc_code[i];
-		} 
-	} else {
-		/* For hardware ECC skip ECC, if we have no full page write */
-		if (eccmode != NAND_ECC_NONE && (col || last != mtd->oobblock))
-			eccmode = NAND_ECC_NONE;
-	}			
-
-	/* Prepad for partial page programming !!! */
-	for (i = 0; i < col; i++)
-		this->data_poi[i] = 0xff;
-
-	/* Postpad for partial page programming !!! oob is already padded */
-	for (i = last; i < mtd->oobblock; i++)
-		this->data_poi[i] = 0xff;
-
 	/* 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 (this->eccmode) {
-	/* No ecc and software ecc 3/256, write all */
+	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 (i = 0; i < mtd->oobblock; i++) 
-			writeb ( this->data_poi[i] , this->IO_ADDR_W);
+		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;
 		
-	/* Hardware ecc 3 byte / 256 data, write first half, get ecc, then second, if 512 byte pagesize */	
+	/* Hardware ecc 3 byte / 256 data, write 256 byte */	
+	/* Hardware ecc 3 byte / 512 byte data, write 512 bytee */	
 	case NAND_ECC_HW3_256:		
-		this->enable_hwecc (NAND_ECC_WRITE);	/* enable hardware ecc logic for write */
-		for (i = 0; i < mtd->eccsize; i++) 
-			writeb ( this->data_poi[i] , this->IO_ADDR_W);
-		
-		this->calculate_ecc (NULL, &(ecc_code[0]));
-		for (i = 0; i < 3; i++)
-			oob_data[oob_config[i]] = ecc_code[i];
-			
-		if (mtd->oobblock == 512) {
-			this->enable_hwecc (NAND_ECC_WRITE);	/* enable hardware ecc logic for write*/
-			for (i = mtd->eccsize; i < mtd->oobblock; i++) 
-				writeb ( this->data_poi[i] , this->IO_ADDR_W);
-			this->calculate_ecc (NULL, &(ecc_code[3]));
-			for (i = 3; i < 6; i++)
-				oob_data[oob_config[i]] = ecc_code[i];
+	case NAND_ECC_HW3_512:
+		for (; eccsteps; eccsteps--) {
+			this->enable_hwecc(mtd, NAND_ECC_WRITE);	/* enable hardware ecc logic for write */
+			this->write_buf(mtd, &this->data_poi[datidx], this->eccsize);
+			this->calculate_ecc(mtd, NULL, ecc_code);
+			for (i = 0; i < 3; i++, eccidx++)
+				oob_buf[oob_config[eccidx]] = ecc_code[i];
+			datidx += this->eccsize;
 		}
 		break;
 				
-	/* Hardware ecc 3 byte / 512 byte data, write full page */	
-	case NAND_ECC_HW3_512:	
-		this->enable_hwecc (NAND_ECC_WRITE);	/* enable hardware ecc logic */
-		for (i = 0; i < mtd->oobblock; i++) 
-			writeb ( this->data_poi[i] , this->IO_ADDR_W);
-		this->calculate_ecc (NULL, &(ecc_code[0]));
-		for (i = 0; i < 3; i++)
-			oob_data[oob_config[i]] = ecc_code[i];
-		break;
-
-	/* Hardware ecc 6 byte / 512 byte data, write full page */	
+	/* Hardware ecc 6 byte / 512 byte data, write 512 byte */	
 	case NAND_ECC_HW6_512:	
-		this->enable_hwecc (NAND_ECC_WRITE);	/* enable hardware ecc logic */
-		for (i = 0; i < mtd->oobblock; i++) 
-			writeb ( this->data_poi[i] , this->IO_ADDR_W);
-		this->calculate_ecc (NULL, &(ecc_code[0]));
-		for (i = 0; i < 6; i++)
-			oob_data[oob_config[i]] = ecc_code[i];
+		for (; eccsteps; eccsteps--) {
+			this->enable_hwecc(mtd, NAND_ECC_WRITE);	/* enable hardware ecc logic for write */
+			this->write_buf(mtd, &this->data_poi[datidx], this->eccsize);
+			this->calculate_ecc(mtd, NULL, ecc_code);
+			for (i = 0; i < 6; i++, eccidx++)
+				oob_buf[oob_config[eccidx]] = ecc_code[i];
+			datidx += this->eccsize;
+		}
 		break;
 		
 	default:
 		printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
 		BUG();	
-	}	
-	
+	}
+										
 	/* Write out OOB data */
-	for (i = 0; i <  mtd->oobsize; i++)
-		writeb ( oob_data[i] , this->IO_ADDR_W);
+	this->write_buf(mtd, oob_buf, mtd->oobsize);
 
 	/* Send command to actually program the data */
-	this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
-
-	/* call wait ready function */
-	status = this->waitfunc (mtd, this, FL_WRITING);
+	this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1);
 
-	/* See if device thinks it succeeded */
-	if (status & 0x01) {
-		DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
-		return -EIO;
+	if (!cached) {
+		/* call wait ready function */
+		status = this->waitfunc (mtd, this, FL_WRITING);
+		/* See if device thinks it succeeded */
+		if (status & 0x01) {
+			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
-	/*
-	 * 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.
-	 */
+/*
+ * 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.
+ *
+ * @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
+ */
+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, datidx = 0, oobofs = 0, res = -EIO;
+	u_char oobdata[64];
+
+	/* Send command to read back the first page */
+	this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
 
-	/* Send command to read back the page */
-	this->cmdfunc (mtd, NAND_CMD_READ0, col, page);
-	/* Loop through and verify the data */
-	for (i = col; i < last; i++) {
-		if (this->data_poi[i] != readb (this->IO_ADDR_R)) {
+	for(;;) {	
+
+		/* Loop through and verify the data */
+		if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->oobblock)) {
 			DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
-			return -EIO;
+			goto out;
 		}
-	}
-
-	/* check, if we have a fs-supplied oob-buffer */
-	if (oob_buf) {
-		for (i = 0; i < mtd->oobsize; i++) {
-			if (oob_data[i] != readb (this->IO_ADDR_R)) {
+		datidx += mtd->oobblock;
+		/* 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)) {
 				DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
-				return -EIO;
-			}
-		}
-	} else {
-		if (eccmode != NAND_ECC_NONE && !col && last == mtd->oobblock) {
-			int ecc_bytes = 0;
-
-			switch (this->eccmode) {
-			case NAND_ECC_SOFT:
-			case NAND_ECC_HW3_256: ecc_bytes = (mtd->oobblock == 512) ? 6 : 3; break;
-			case NAND_ECC_HW3_512: ecc_bytes = 3; break;
-			case NAND_ECC_HW6_512: ecc_bytes = 6; break;
+				goto out;
 			}
+		} else {
+			/* Read always, else autoincrement fails */
+			this->read_buf(mtd, oobdata, mtd->oobsize);
 
-			for (i = 0; i < mtd->oobsize; i++)
-				oob_data[i] = readb (this->IO_ADDR_R);
-
-			for (i = 0; i < ecc_bytes; i++) {
-				if (oob_data[oob_config[i]] != ecc_code[i]) {
-					DEBUG (MTD_DEBUG_LEVEL0,
-					       "%s: Failed ECC write "
-				       "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i);
-				return -EIO;
+			if (oobsel->useecc != MTD_NANDECC_OFF) {
+				int ecc_bytes = oobsel->eccbytes;
+		
+				for (i = 0; i < ecc_bytes; 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;
+		page++;
+		numpages--;
+		if (!numpages)
+			break;
+		
+		/* 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
+			while (!this->dev_ready(mtd));	
+			
+		/* Check, if the chip supports auto page increment */ 
+		if (!NAND_CANAUTOINCR(this))
+			this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
 	}
-#endif
-	return 0;
+	res = 0;
+	/* 
+	 * Terminate the read command. This is faster than sending a reset command or 
+	 * applying a 20us delay before issuing the next programm sequence.
+	 * This is not a problem for all chips, but I have found a bunch of them.
+	 */
+out:	 
+	this->select_chip(mtd, -1);
+	this->select_chip(mtd, chipnr);
+	return res;
 }
+#endif
 
 /*
-*	Use NAND read ECC
+ * This function simply calls nand_read_ecc with oob buffer and oobsel = NULL
+ *
+ * @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
 */
 static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
 {
-	return (nand_read_ecc (mtd, from, len, retlen, buf, NULL, 0));
+	return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL);
 }			   
 
 
 /*
  * NAND read 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
+ * @oobsel:	oob selection structure
  */
 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, int oobsel)
+			  size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
 {
-	int j, col, page, end, ecc;
+	int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
 	int erase_state = 0;
 	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[6];
-	u_char ecc_code[6];
-	int	eccmode = oobsel ? this->eccmode : NAND_ECC_NONE;
+	u_char ecc_calc[24];
+	u_char ecc_code[24];
+        int eccmode, eccsteps;
+	int	*oob_config, datidx;
+	int	blockcheck = (mtd->erasesize >> this->page_shift) - 1;
+
 
-	int *oob_config = oobconfigs[oobsel];
-	
 	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 */
@@ -573,83 +832,101 @@
 	/* Grab the lock and see if the device is available */
 	nand_get_chip (this, mtd ,FL_READING, &erase_state);
 
+	/* use userspace supplied oobinfo, if zero */
+	if (oobsel == NULL)
+		oobsel = &mtd->oobinfo;
+	
+	/* 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 */
-	nand_select ();
+	chipnr = (int)((unsigned long)from / this->chipsize);
+	this->select_chip(mtd, chipnr);
 
 	/* First we calculate the starting page */
-	page = from >> this->page_shift;
+	realpage = from >> this->page_shift;
+	page = realpage & this->pagemask;
 
 	/* Get raw starting column */
 	col = from & (mtd->oobblock - 1);
 
 	end = mtd->oobblock;
-	ecc = mtd->eccsize;
+	ecc = this->eccsize;
 
-	/* Send the read command */
-	this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
-	
 	/* Loop until all data read */
 	while (read < len) {
 		
-		/* If we have consequent page reads, apply delay or wait for ready/busy pin */
-		if (read) {
-			if (!this->dev_ready) 
-				udelay (this->chip_delay);
-			else
-				while (!this->dev_ready());	
-		}
-
+		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 (!col && (len - read) >= end)  
+		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) {
+		if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE)
 			oob_data = &this->data_buf[end];
-			oob = 0;
-		} 	
-			
-		j = 0;
+
+		eccsteps = this->eccsteps;
+		
 		switch (eccmode) {
-		case NAND_ECC_NONE:	/* No ECC, Read in a page */		
-			while (j < end)
-				data_poi[j++] = readb (this->IO_ADDR_R);
+		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 */
-			while (j < end)
-				data_poi[j++] = readb (this->IO_ADDR_R);
-			this->calculate_ecc (&data_poi[0], &ecc_calc[0]);
-			if (mtd->oobblock == 512)
-				this->calculate_ecc (&data_poi[256], &ecc_calc[3]);
+			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;	
 			
-		case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data: Read in first 256 byte, get ecc, */
-			this->enable_hwecc (NAND_ECC_READ);	
-			while (j < ecc)
-				data_poi[j++] = readb (this->IO_ADDR_R);
-			this->calculate_ecc (&data_poi[0], &ecc_calc[0]);	/* read from hardware */
-			
-			if (mtd->oobblock == 512) { /* read second, if pagesize = 512 */
-				this->enable_hwecc (NAND_ECC_READ);	
-				while (j < end)
-					data_poi[j++] = readb (this->IO_ADDR_R);
-				this->calculate_ecc (&data_poi[256], &ecc_calc[3]); /* read from hardware */
-			}					
+		case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data */
+		case NAND_ECC_HW3_512: /* Hardware ECC 3 byte /512 byte data */	
+			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) {
+				this->enable_hwecc(mtd, NAND_ECC_READ);	
+				this->read_buf(mtd, &data_poi[datidx], ecc);
+				this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);	/* read from hardware */
+			}
+			break;						
+
+		case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data  */
+			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=6, datidx += ecc) {
+				this->enable_hwecc(mtd, NAND_ECC_READ);	
+				this->read_buf(mtd, &data_poi[datidx], ecc);
+				this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);	/* read from hardware */
+			}
 			break;						
-				
-		case NAND_ECC_HW3_512:	
-		case NAND_ECC_HW6_512: /* Hardware ECC 3/6 byte / 512 byte data : Read in a page  */
-			this->enable_hwecc (NAND_ECC_READ);	
-			while (j < end)
-				data_poi[j++] = readb (this->IO_ADDR_R);
-			this->calculate_ecc (&data_poi[0], &ecc_calc[0]);	/* read from hardware */
-			break;
 
 		default:
 			printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
@@ -657,61 +934,96 @@
 		}
 
 		/* read oobdata */
-		for (j = 0; j <  mtd->oobsize; j++) 
-			oob_data[oob + j] = readb (this->IO_ADDR_R);
+		this->read_buf(mtd, oob_data, mtd->oobsize);
 		
 		/* Skip ECC, if not active */
 		if (eccmode == NAND_ECC_NONE)
 			goto readdata;	
 		
 		/* Pick the ECC bytes out of the oob data */
-		for (j = 0; j < 6; j++)
-			ecc_code[j] = oob_data[oob + oob_config[j]];
+		for (j = 0; j < oobsel->eccbytes; j++)
+			ecc_code[j] = oob_data[oob_config[j]];
 
-		/* correct data, if neccecary */
-		ecc_status = this->correct_data (&data_poi[0], &ecc_code[0], &ecc_calc[0]);
-		/* check, if we have a fs supplied oob-buffer */
-		if (oob_buf) { 
-			oob += mtd->oobsize;
-			*((int *)&oob_data[oob]) = ecc_status;
-			oob += sizeof(int);
-		}
-		if (ecc_status == -1) {	
-			DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
-			ecc_failed++;
-		}
-		
-		if (mtd->oobblock == 512 && eccmode != NAND_ECC_HW3_512) {
-			ecc_status = this->correct_data (&data_poi[256], &ecc_code[3], &ecc_calc[3]);
-			if (oob_buf) {
-				*((int *)&oob_data[oob]) = ecc_status;
-				oob += sizeof(int);
+		/* 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 += eccmode == NAND_ECC_HW6_512 ? 6 : 3;
+			
+			/* check, if we have a fs supplied oob-buffer, 
+			 * This is the legacy mode. Used by YAFFS1
+			 */
+			if (oob_buf && oobsel->useecc != MTD_NANDECC_AUTOPLACE) { 
+				int *p = (int *)(&oob_data[mtd->oobsize]);
+				p[i] = ecc_status;
 			}
-			if (ecc_status == -1) {
+			
+			if (ecc_status == -1) {	
 				DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
 				ecc_failed++;
 			}
+		}		
+
+		/* check, if we have a fs supplied oob-buffer */
+		if (oob_buf) {
+			/* without autoplace. Legacy mode used by YAFFS1 */
+			if (oobsel->useecc != MTD_NANDECC_AUTOPLACE) {
+				oob_data += mtd->oobsize + this->eccsteps * sizeof (int);
+			} else {
+				/* Walk through the autoplace chunks */
+				for (i = 0, j = 0; j < mtd->oobavail; i++) {
+					int from = oobsel->oobfree[i][0];
+					int num = oobsel->oobfree[i][1];
+					memcpy(&oob_buf[oob], &oob_data[from], num);
+					j+= num;
+				}
+				oob += mtd->oobavail;
+			}
 		}
 readdata:
-		if (col || (len - read) < end) { 
+		/* 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;
+
+		if (read == len)
+			break;	
+
 		/* For subsequent reads align to page boundary. */
 		col = 0;
 		/* Increment page address */
-		page++;
-	}
+		realpage++;
 
-	/* De-select the NAND device */
-	nand_deselect ();
+		/* 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
+			while (!this->dev_ready(mtd));	
+			
+		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;				
+	}
 
-	/* Wake up anyone waiting on the device */
-	spin_lock_bh (&this->chip_lock);
-	this->state = FL_READY;
-	wake_up (&this->wq);
-	spin_unlock_bh (&this->chip_lock);
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_chip(mtd);
 
 	/*
 	 * Return success, if no ECC failures, else -EIO
@@ -724,20 +1036,28 @@
 
 /*
  * 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
  */
 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;
+	int i, col, page, chipnr;
 	int erase_state = 0;
 	struct nand_chip *this = mtd->priv;
+	int	blockcheck = (mtd->erasesize >> 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)((unsigned long)from / this->chipsize);
+	
 	/* Mask to get column */
-	col = from & 0x0f;
+	col = from & (mtd->oobsize - 1);
 
 	/* Initialize return length value */
 	*retlen = 0;
@@ -753,141 +1073,306 @@
 	nand_get_chip (this, mtd , FL_READING, &erase_state);
 
 	/* Select the NAND device */
-	nand_select ();
+	this->select_chip(mtd, chipnr);
 
 	/* Send the read command */
-	this->cmdfunc (mtd, NAND_CMD_READOOB, col, page);
+	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 !
 	 */
-	for (i = 0; i < len; i++) {
-		buf[i] = readb (this->IO_ADDR_R);
-		if ((col++ & (mtd->oobsize - 1)) == (mtd->oobsize - 1))
-			udelay (this->chip_delay);
+	i = 0;
+	while (i < len) {
+		int thislen = (mtd->oobsize - col) & (mtd->oobsize - 1);
+		if (!thislen)
+			thislen = mtd->oobsize;
+		thislen = min_t(int, thislen, len);
+		this->read_buf(mtd, &buf[i], thislen);
+		i += thislen;
+		col += thislen;
+		/* Read more ? */
+		if (i < len) {
+			page++;
+			/* 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
+				while (!this->dev_ready(mtd));	
+
+			/* Check, if we cross a chip boundary */
+			if (!(page & this->pagemask)) {
+				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)) {
+				/* For subsequent page reads set offset to 0 */
+			        this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask);
+			}
+		}
 	}
-	/* De-select the NAND device */
-	nand_deselect ();
 
-	/* Wake up anyone waiting on the device */
-	spin_lock_bh (&this->chip_lock);
-	this->state = FL_READY;
-	wake_up (&this->wq);
-	spin_unlock_bh (&this->chip_lock);
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_chip(mtd);
 
 	/* Return happy */
 	*retlen = len;
 	return 0;
 }
 
+/* Prepare the out of band buffer 
+ *
+ * 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.
+ *
+ * @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
+*/
+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->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
+
 /*
-*	Use NAND write ECC
+ * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL
+ *
+ * @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
 */
 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, 0));
+	return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
 }			   
 /*
  * 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
  */
 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, int oobsel)
+			   size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
 {
-	int i, page, col, cnt, ret = 0, oob = 0, written = 0;
+	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 = mtd->erasesize >> 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_oob: Attempt to write past end of page\n");
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n");
 		return -EINVAL;
 	}
 
-	/* Shift to get page */
-	page = ((int) to) >> this->page_shift;
-
-	/* Get the starting column */
-	col = to & (mtd->oobblock - 1);
+	/* 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_chip (this, mtd, FL_WRITING, NULL);
 
+	/* Calculate chipnr */
+	chipnr = (int)((unsigned long) to / this->chipsize);
 	/* Select the NAND device */
-	nand_select ();
+	this->select_chip(mtd, chipnr);
 
-	/* Check the WP bit */
-	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
-	if (!(readb (this->IO_ADDR_R) & 0x80)) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Device is write protected!!!\n");
-		ret = -EIO;
+	/* 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;
+	}	
+
+	/* 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) {
-		/* 
-		 * Check, if we have a full page write, then we can
-		 * use the given buffer, else we have to copy
+
+		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.
 		 */
-		if (!col && (len - written) >= mtd->oobblock) {   
-			this->data_poi = (u_char*) &buf[written];
-			cnt = mtd->oobblock;
-		} else {
-			cnt = 0;
-			for (i = col; i < len && i < mtd->oobblock; i++) {
-				this->data_buf[i] = buf[written + i];
-				cnt++;
-			}
-			this->data_poi = this->data_buf;		
-		}
-		/* We use the same function for write and writev !) */
-		if (eccbuf) {
-			ret = nand_write_page (mtd, this, page, col, cnt ,&eccbuf[oob], oobsel);
-			oob += mtd->oobsize;
-		} else 
-			ret = nand_write_page (mtd, this, page, col, cnt, NULL, oobsel);	
-		
-		if (ret)
+		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 += cnt;
-		/* Next write is aligned */
-		col = 0;
+		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:
-	/* De-select the NAND device */
-	nand_deselect ();
-
-	/* Wake up anyone waiting on the device */
-	spin_lock_bh (&this->chip_lock);
-	this->state = FL_READY;
-	wake_up (&this->wq);
-	spin_unlock_bh (&this->chip_lock);
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_chip(mtd);
 
-	*retlen = written;
 	return ret;
 }
 
+
 /*
  * 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
  */
 static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
 {
-	int i, column, page, status, ret = 0;
+	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)((unsigned long)to / this->chipsize);
 
 	/* Mask to get column */
-	column = to & 0x1f;
+	column = to & (mtd->oobsize - 1);
 
 	/* Initialize return length value */
 	*retlen = 0;
@@ -902,28 +1387,38 @@
 	nand_get_chip (this, mtd, FL_WRITING, NULL);
 
 	/* Select the NAND device */
-	nand_select ();
+	this->select_chip(mtd, chipnr);
 
-	/* Check the WP bit */
-	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
-	if (!(readb (this->IO_ADDR_R) & 0x80)) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Device is write protected!!!\n");
-		ret = -EIO;
+	/* 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);
 	}
-
-	/* Write out desired data */
-	this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page);
-	/* prepad 0xff for partial programming */
-	for (i = 0; i < column; i++)
-		writeb (0xff, this->IO_ADDR_W);
-	/* write data */
-	for (i = 0; i < len; i++)
-		writeb (buf[i], this->IO_ADDR_W);	
-	/* postpad 0xff for partial programming */
-	for (i = len + column; i < mtd->oobsize; i++)
-		writeb (0xff, this->IO_ADDR_W);
-
 	/* Send command to program the OOB data */
 	this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
 
@@ -940,34 +1435,31 @@
 
 #ifdef CONFIG_MTD_NAND_VERIFY_WRITE
 	/* Send command to read back the data */
-	this->cmdfunc (mtd, NAND_CMD_READOOB, column, page);
+	this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask);
 
-	/* Loop through and verify the data */
-	for (i = 0; i < len; i++) {
-		if (buf[i] != readb (this->IO_ADDR_R)) {
-			DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page);
-			ret = -EIO;
-			goto out;
-		}
+	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:
-	/* De-select the NAND device */
-	nand_deselect ();
-
-	/* Wake up anyone waiting on the device */
-	spin_lock_bh (&this->chip_lock);
-	this->state = FL_READY;
-	wake_up (&this->wq);
-	spin_unlock_bh (&this->chip_lock);
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_chip(mtd);
 
 	return ret;
 }
 
 
 /*
- * NAND write with iovec
+ * NAND write with iovec. This just calls the ecc function
+ *
+ * @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
  */
 static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, 
 		loff_t to, size_t * retlen)
@@ -975,11 +1467,28 @@
 	return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, 0));	
 }
 
+/*
+ * NAND 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
+ */
 static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, 
-		loff_t to, size_t * retlen, u_char *eccbuf, int oobsel)
+		loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel)
 {
-	int i, page, col, cnt, len, total_len, ret = 0, written = 0;
+	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 = mtd->erasesize >> this->page_shift;
+	u_char *oobbuf, *bufstart;
+
+	/* Preset written len for early exit */
+	*retlen = 0;
 
 	/* Calculate total length of data */
 	total_len = 0;
@@ -995,42 +1504,73 @@
 		return -EINVAL;
 	}
 
-	/* Shift to get page */
-	page = ((int) to) >> this->page_shift;
-
-	/* Get the starting column */
-	col = to & (mtd->oobblock - 1);
+	/* 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_chip (this, mtd, FL_WRITING, NULL);
 
+	/* Get the current chip-nr */
+	chipnr = (int)((unsigned long) to / this->chipsize);
 	/* Select the NAND device */
-	nand_select ();
+	this->select_chip(mtd, chipnr);
 
-	/* Check the WP bit */
-	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
-	if (!(readb (this->IO_ADDR_R) & 0x80)) {
-		DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Device is write protected!!!\n");
-		ret = -EIO;
+	/* 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;
+	}	
+
+	/* 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 iovecs' data has been written */
-	cnt = col;
 	len = 0;
-	
 	while (count) {
-		/* 
-		 *  Check, if we write from offset 0 and if the tuple
-		 *  gives us not enough data for a full page write. Then we
-		 *  can use the iov direct, else we have to copy into
-		 *  data_buf.		
+		/* If the given tuple is >= pagesize then
+		 * write it out from the iov
 		 */
-		if (!cnt && (vecs->iov_len - len) >= mtd->oobblock) {
-			cnt = mtd->oobblock;
-			this->data_poi = (u_char *) vecs->iov_base;
-			this->data_poi += len;
-			len += mtd->oobblock; 
+		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++;
@@ -1038,48 +1578,54 @@
 				count--;
 			}
 		} else {
-			/*
-			 * Read data out of each tuple until we have a full page
-			 * to write or we've read all the tuples.
-		 	*/
-			while ((cnt < mtd->oobblock) && count) {
-				if (vecs->iov_base != NULL && vecs->iov_len) {
+			/* 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++;
 		}
-		
-		/* We use the same function for write and writev !) */
-		ret = nand_write_page (mtd, this, page, col, cnt, NULL, oobsel);
+
+		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;
 
-		/* Update written bytes count */
-		written += (cnt - col);
-
-		/* Reset written byte counter and column */
-		col = cnt = 0;
-
-		/* Increment page address */
-		page++;
+		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:
-	/* De-select the NAND device */
-	nand_deselect ();
-
-	/* Wake up anyone waiting on the device */
-	spin_lock_bh (&this->chip_lock);
-	this->state = FL_READY;
-	wake_up (&this->wq);
-	spin_unlock_bh (&this->chip_lock);
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_chip(mtd);
 
 	*retlen = written;
 	return ret;
@@ -1087,12 +1633,14 @@
 
 /*
  * NAND erase a block
+ * 
+ * @mtd:	MTD device structure
+ * @instr:	erase instruction
  */
 static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
 {
-	int page, len, status, pages_per_block, ret;
+	int page, len, status, pages_per_block, ret, chipnr;
 	struct nand_chip *this = mtd->priv;
-	DECLARE_WAITQUEUE (wait, current);
 
 	DEBUG (MTD_DEBUG_LEVEL3,
 	       "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
@@ -1120,16 +1668,17 @@
 
 	/* Shift to get first page */
 	page = (int) (instr->addr >> this->page_shift);
+	chipnr = (int)((unsigned long)instr->addr / this->chipsize);
 
 	/* Calculate pages in each block */
-	pages_per_block = mtd->erasesize / mtd->oobblock;
+	pages_per_block = 1 << (this->erase_shift - this->page_shift);
 
 	/* Select the NAND device */
-	nand_select ();
+	this->select_chip(mtd, chipnr);
 
 	/* Check the WP bit */
 	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
-	if (!(readb (this->IO_ADDR_R) & 0x80)) {
+	if (!(this->read_byte(mtd) & 0x80)) {
 		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");
 		instr->state = MTD_ERASE_FAILED;
 		goto erase_exit;
@@ -1142,22 +1691,23 @@
 
 	while (len) {
 		/* Check if we have a bad block, we do not erase bad blocks ! */
-		this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page);
-		if (readb (this->IO_ADDR_R) != 0xff) {
+		if (this->block_bad(mtd, (loff_t) page, 0)) {
 			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;
 
 		/* Send commands to erase a page */
-		this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
+		this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page & this->pagemask);
 		this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
 
-		spin_unlock_bh (&this->chip_lock);
 		status = this->waitfunc (mtd, this, FL_ERASING);
 
-		/* Get spinlock, in case we exit */
-		spin_lock_bh (&this->chip_lock);
 		/* See if block erase succeeded */
 		if (status & 0x01) {
 			DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
@@ -1165,47 +1715,28 @@
 			goto erase_exit;
 		}
 		
-		/* Check, if we were interupted */
-		if (this->state == FL_ERASING) {
-			/* Increment page address and decrement length */
-			len -= mtd->erasesize;
-			page += pages_per_block;
-		}
-		/* Release the spin lock */
-		spin_unlock_bh (&this->chip_lock);
-erase_retry:
-		spin_lock_bh (&this->chip_lock);
-		/* Check the state and sleep if it changed */
-		if (this->state == FL_ERASING || this->state == FL_READY) {
-			/* Select the NAND device again, if we were interrupted */
-			this->state = FL_ERASING;
-			nand_select ();
-			continue;
-		} else {
-			set_current_state (TASK_UNINTERRUPTIBLE);
-			add_wait_queue (&this->wq, &wait);
-			spin_unlock_bh (&this->chip_lock);
-			schedule ();
-			remove_wait_queue (&this->wq, &wait);
-			goto erase_retry;
+		/* Increment page address and decrement length */
+		len -= mtd->erasesize;
+		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);
 		}
 	}
 	instr->state = MTD_ERASE_DONE;
 
 erase_exit:
-	/* De-select the NAND device */
-	nand_deselect ();
-	spin_unlock_bh (&this->chip_lock);
 
 	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;;
 	/* Do call back function */
 	if (!ret && instr->callback)
 		instr->callback (instr);
 
-	/* The device is ready */
-	spin_lock_bh (&this->chip_lock);
-	this->state = FL_READY;
-	spin_unlock_bh (&this->chip_lock);
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_chip(mtd);
 
 	/* Return more or less happy */
 	return ret;
@@ -1213,6 +1744,8 @@
 
 /*
  * NAND sync
+ *
+ * @mtd:	MTD device structure
  */
 static void nand_sync (struct mtd_info *mtd)
 {
@@ -1257,13 +1790,58 @@
 }
 
 /*
+ * 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)
+{
+	struct nand_chip *this = mtd->priv;
+	
+	/* Check for invalid offset */
+	if (ofs > mtd->size) 
+		return -EINVAL;
+
+	return this->block_bad(mtd, ofs, 1);
+}
+
+/*
+ * 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;
+		
+	/* Check for invalid offset */
+	if (ofs > mtd->size) 
+		return -EINVAL;
+
+	return this->block_markbad(mtd, ofs);
+}
+
+/*
  * Scan for the NAND device
+ *
+ * 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.
+ *
+ * @mtd:	MTD device structure
+ * @maxchips:	Number of chips to scan for
  */
-int nand_scan (struct mtd_info *mtd)
+int nand_scan (struct mtd_info *mtd, int maxchips)
 {
-	int i, nand_maf_id, nand_dev_id;
+	int i, nand_maf_id, nand_dev_id, busw;
 	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;
@@ -1276,43 +1854,179 @@
 	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;
+
 	/* Select the device */
-	nand_select ();
+	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 = readb (this->IO_ADDR_R);
-	nand_dev_id = readb (this->IO_ADDR_R);
+	nand_maf_id = this->read_byte(mtd);
+	nand_dev_id = this->read_byte(mtd);
 
 	/* 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 && !mtd->size) {
-			mtd->name = nand_flash_ids[i].name;
+				
+		if (nand_dev_id != nand_flash_ids[i].id) 
+			continue;
+			
+		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 & 0x03)) * (mtd->oobblock / 512);
+			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;
+		
+			/* Do not replace user supplied command function ! */
+			if (this->cmdfunc == nand_command)
+				this->cmdfunc = nand_command_lp;
+		} else {
+			/* Old devices have this data hardcoded in the
+			 * device id table */
 			mtd->erasesize = nand_flash_ids[i].erasesize;
-			mtd->size = (1 << nand_flash_ids[i].chipshift);
-			mtd->eccsize = 256;
-			if (nand_flash_ids[i].page256) {
-				mtd->oobblock = 256;
-				mtd->oobsize = 8;
-				this->page_shift = 8;
-			} else {
-				mtd->oobblock = 512;
-				mtd->oobsize = 16;
-				this->page_shift = 9;
-			}
-			/* Try to identify manufacturer */
-			for (i = 0; nand_manuf_ids[i].id != 0x0; i++) {
-				if (nand_manuf_ids[i].id == nand_maf_id)
-					break;
-			}	
-			printk (KERN_INFO "NAND device: Manufacture ID:"
+			mtd->oobblock = nand_flash_ids[i].pagesize;
+			mtd->oobsize = mtd->oobblock / 32;
+			busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;
+		}
+
+		/* 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[i].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->erase_shift = ffs(mtd->erasesize) - 1;
+
+		/* Set the bad block position */
+		this->badblockpos = mtd->oobblock > 512 ? 
+			NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
+
+		/* Get chip options */
+		this->options = nand_flash_ids[i].options;
+		/* Check if this is a not a samsung device */	
+		if (nand_maf_id != NAND_MFR_SAMSUNG)
+			this->options &= ~NAND_SAMSUNG_LP_OPTIONS;
+		
+		/* Try to identify manufacturer */
+		for (i = 0; nand_manuf_ids[i].id != 0x0; i++) {
+			if (nand_manuf_ids[i].id == nand_maf_id)
+				break;
+		}
+		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[i].name , mtd->name);
+		break;
+	}
+
+	if (!mtd->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);
+	
+	if (!this->oob_buf || !this->data_buf) {
+		printk (KERN_WARNING "nand_scan(): Buffers not set. Scan aborted\n"); 
+		return 1;
+	}
+
+	/* 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->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 */
+	if (this->options & NAND_BUSWIDTH_16) {
+		mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2);
+		if (this->autooob->eccbytes & 0x01)
+			mtd->oobavail--;
+	} else
+		mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1);
 
 	/* 
 	 * check ECC mode, default to software
@@ -1324,6 +2038,7 @@
 	switch (this->eccmode) {
 
 	case NAND_ECC_HW3_512: 
+	case NAND_ECC_HW6_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;
@@ -1340,6 +2055,7 @@
 		BUG();	
 
 	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;
 
@@ -1352,25 +2068,39 @@
 		printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
 		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_HW3_512:
+	case NAND_ECC_HW6_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 */
-	nand_deselect ();
+	this->select_chip(mtd, -1);
 
-	/* Print warning message for no device */
-	if (!mtd->size) {
-		printk (KERN_WARNING "No NAND device found!!!\n");
-		return 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->module = THIS_MODULE;
 	mtd->ecctype = MTD_ECC_SW;
 	mtd->erase = nand_erase;
 	mtd->point = NULL;
@@ -1389,6 +2119,13 @@
 	mtd->unlock = NULL;
 	mtd->suspend = NULL;
 	mtd->resume = NULL;
+	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;
 
 	/* Return happy */
 	return 0;
@@ -1397,5 +2134,5 @@
 EXPORT_SYMBOL (nand_scan);
 
 MODULE_LICENSE ("GPL");
-MODULE_AUTHOR ("Steven J. Hill <sjhill@cotw.com>, Thomas Gleixner <tglx@linutronix.de>");
+MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
 MODULE_DESCRIPTION ("Generic NAND flash driver code");
diff -Nur linux-2.4.30/drivers/mtd/nand/nand_ecc.c linux-2.4.30-linux-mips.org/drivers/mtd/nand/nand_ecc.c
--- linux-2.4.30/drivers/mtd/nand/nand_ecc.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nand/nand_ecc.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,22 +1,44 @@
 /*
- *  drivers/mtd/nand_ecc.c
+ * This file contains an ECC algorithm from Toshiba that detects and
+ * corrects 1 bit errors in a 256 byte block of data.
  *
- *  Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
- *                     Toshiba America Electronics Components, Inc.
+ * drivers/mtd/nand/nand_ecc.c
  *
- * $Id: nand_ecc.c,v 1.8 2002/09/16 09:19:53 dwmw2 Exp $
+ * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
+ *                         Toshiba America Electronics Components, Inc.
  *
- * 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.
+ * $Id: nand_ecc.c,v 1.11 2004/05/05 14:19:42 sjhill Exp $
  *
- * This file contains an ECC algorithm from Toshiba that detects and
- * corrects 1 bit errors in a 256 byte block of data.
+ * 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
+ * Free Software Foundation; either version 2 or (at your option) any
+ * later version.
+ * 
+ * This file 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 file; if not, write to the Free Software Foundation, Inc.,
+ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ * 
+ * As a special exception, if other files instantiate templates or use
+ * macros or inline functions from these files, or you compile these
+ * files and link them with other works to produce a work based on these
+ * files, these files do not by themselves cause the resulting work to be
+ * covered by the GNU General Public License. However the source code for
+ * these files must still be made available in accordance with section (3)
+ * of the GNU General Public License.
+ * 
+ * This exception does not invalidate any other reasons why a work based on
+ * this file might be covered by the GNU General Public License.
  */
 
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
+#include <linux/mtd/nand_ecc.h>
 
 /*
  * Pre-calculated 256-way 1 byte column parity
@@ -84,7 +106,7 @@
 /*
  * Calculate 3 byte ECC code for 256 byte block
  */
-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code)
+void nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
 {
 	u_char idx, reg1, reg2, reg3;
 	int j;
@@ -119,7 +141,7 @@
 /*
  * Detect and correct a 1 bit error for 256 byte block
  */
-int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
 {
 	u_char a, b, c, d1, d2, d3, add, bit, i;
 	
@@ -209,5 +231,5 @@
 EXPORT_SYMBOL(nand_correct_data);
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Steven J. Hill <sjhill@cotw.com>");
+MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>");
 MODULE_DESCRIPTION("Generic NAND ECC support");
diff -Nur linux-2.4.30/drivers/mtd/nand/nand_ids.c linux-2.4.30-linux-mips.org/drivers/mtd/nand/nand_ids.c
--- linux-2.4.30/drivers/mtd/nand/nand_ids.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nand/nand_ids.c	2005-05-12 18:01:29.000000000 +0200
@@ -2,9 +2,8 @@
  *  drivers/mtd/nandids.c
  *
  *  Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
- *
- *
- * $Id: nand_ids.c,v 1.1 2002/12/02 22:06:04 gleixner Exp $
+  *
+ * $Id: nand_ids.c,v 1.7 2004/03/28 23:31:28 gleixner 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
@@ -13,26 +12,91 @@
  */
 #include <linux/module.h>
 #include <linux/mtd/nand.h>
-
 /*
 *	Chip ID list
+*	
+*	Name. ID code, pagesize, chipsize in MegaByte, eraseblock size,
+*	options
+* 
+* 	Pagesize; 0, 256, 512
+*	0 	get this information from the extended chip ID
++	256	256 Byte page size
+*	512	512 Byte page size	
 */
 struct nand_flash_dev nand_flash_ids[] = {
-	{"NAND 1MB 5V", 0x6e, 20, 0x1000, 1},	// 1Mb 5V
-	{"NAND 2MB 5V", 0x64, 21, 0x1000, 1},	// 2Mb 5V
-	{"NAND 4MB 5V", 0x6b, 22, 0x2000, 0},	// 4Mb 5V
-	{"NAND 1MB 3,3V", 0xe8, 20, 0x1000, 1},	// 1Mb 3.3V
-	{"NAND 1MB 3,3V", 0xec, 20, 0x1000, 1},	// 1Mb 3.3V
-	{"NAND 2MB 3,3V", 0xea, 21, 0x1000, 1},	// 2Mb 3.3V
-	{"NAND 4MB 3,3V", 0xd5, 22, 0x2000, 0},	// 4Mb 3.3V
-	{"NAND 4MB 3,3V", 0xe3, 22, 0x2000, 0},	// 4Mb 3.3V
-	{"NAND 4MB 3,3V", 0xe5, 22, 0x2000, 0},	// 4Mb 3.3V
-	{"NAND 8MB 3,3V", 0xd6, 23, 0x2000, 0},	// 8Mb 3.3V
-	{"NAND 8MB 3,3V", 0xe6, 23, 0x2000, 0},	// 8Mb 3.3V
-	{"NAND 16MB 3,3V", 0x73, 24, 0x4000, 0},// 16Mb 3,3V
-	{"NAND 32MB 3,3V", 0x75, 25, 0x4000, 0}, // 32Mb 3,3V
-	{"NAND 64MB 3,3V", 0x76, 26, 0x4000, 0}, // 64Mb 3,3V
-	{"NAND 128MB 3,3V", 0x79, 27, 0x4000, 0}, // 128Mb 3,3V
+	{"NAND 1MiB 5V 8-bit", 		0x6e, 256, 1, 0x1000, 0},
+	{"NAND 2MiB 5V 8-bit", 		0x64, 256, 2, 0x1000, 0},
+	{"NAND 4MiB 5V 8-bit", 		0x6b, 512, 4, 0x2000, 0},
+	{"NAND 1MiB 3,3V 8-bit", 	0xe8, 256, 1, 0x1000, 0},
+	{"NAND 1MiB 3,3V 8-bit", 	0xec, 256, 1, 0x1000, 0},
+	{"NAND 2MiB 3,3V 8-bit", 	0xea, 256, 2, 0x1000, 0},
+	{"NAND 4MiB 3,3V 8-bit", 	0xd5, 512, 4, 0x2000, 0},
+	{"NAND 4MiB 3,3V 8-bit", 	0xe3, 512, 4, 0x2000, 0},
+	{"NAND 4MiB 3,3V 8-bit", 	0xe5, 512, 4, 0x2000, 0},
+	{"NAND 8MiB 3,3V 8-bit", 	0xd6, 512, 8, 0x2000, 0},
+	
+	{"NAND 8MiB 1,8V 8-bit", 	0x39, 512, 8, 0x2000, 0},
+	{"NAND 8MiB 3,3V 8-bit", 	0xe6, 512, 8, 0x2000, 0},
+	{"NAND 8MiB 1,8V 16-bit", 	0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
+	{"NAND 8MiB 3,3V 16-bit", 	0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
+	
+	{"NAND 16MiB 1,8V 8-bit", 	0x33, 512, 16, 0x4000, 0},
+	{"NAND 16MiB 3,3V 8-bit", 	0x73, 512, 16, 0x4000, 0},
+	{"NAND 16MiB 1,8V 16-bit", 	0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},
+	{"NAND 16MiB 3,3V 16-bit", 	0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},
+	
+	{"NAND 32MiB 1,8V 8-bit", 	0x35, 512, 32, 0x4000, 0},
+	{"NAND 32MiB 3,3V 8-bit", 	0x75, 512, 32, 0x4000, 0},
+	{"NAND 32MiB 1,8V 16-bit", 	0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16},
+	{"NAND 32MiB 3,3V 16-bit", 	0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16},
+	
+	{"NAND 64MiB 1,8V 8-bit", 	0x36, 512, 64, 0x4000, 0},
+	{"NAND 64MiB 3,3V 8-bit", 	0x76, 512, 64, 0x4000, 0},
+	{"NAND 64MiB 1,8V 16-bit", 	0x46, 512, 64, 0x4000, NAND_BUSWIDTH_16},
+	{"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 3,3V 8-bit", 	0x79, 512, 128, 0x4000, 0},
+	{"NAND 128MiB 1,8V 16-bit", 	0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
+	{"NAND 128MiB 3,3V 16-bit", 	0x74, 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
+	*/
+	/* 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},
+	{"NAND 256MiB 1,8V 16-bit", 	0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+	{"NAND 256MiB 3,3V 16-bit", 	0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+	
+	/* 4 Gigabit */
+	{"NAND 512MiB 1,8V 8-bit", 	0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
+	{"NAND 512MiB 3,3V 8-bit", 	0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
+	{"NAND 512MiB 1,8V 16-bit", 	0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+	{"NAND 512MiB 3,3V 16-bit", 	0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+	
+	/* 8 Gigabit */
+	{"NAND 1GiB 1,8V 8-bit", 	0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
+	{"NAND 1GiB 3,3V 8-bit", 	0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
+	{"NAND 1GiB 1,8V 16-bit", 	0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+	{"NAND 1GiB 3,3V 16-bit", 	0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
+
+	/* 16 Gigabit */
+	{"NAND 2GiB 1,8V 8-bit", 	0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
+	{"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},
+	
 	{NULL,}
 };
 
@@ -47,7 +111,6 @@
 	{0x0, "Unknown"}
 };
 
-
 EXPORT_SYMBOL (nand_manuf_ids);
 EXPORT_SYMBOL (nand_flash_ids);
 
diff -Nur linux-2.4.30/drivers/mtd/nand/ppchameleonevb.c linux-2.4.30-linux-mips.org/drivers/mtd/nand/ppchameleonevb.c
--- linux-2.4.30/drivers/mtd/nand/ppchameleonevb.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nand/ppchameleonevb.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,430 @@
+/*
+ *  drivers/mtd/nand/ppchameleonevb.c
+ *
+ *  Copyright (C) 2003 DAVE Srl (info@wawnet.biz)
+ *
+ *  Derived from drivers/mtd/nand/edb7312.c
+ *
+ *
+ * $Id: ppchameleonevb.c,v 1.2 2004/05/05 22:09:54 gleixner 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.
+ *
+ *  Overview:
+ *   This is a device driver for the NAND flash devices found on the
+ *   PPChameleon/PPChameleonEVB system.
+ *   PPChameleon options (autodetected):
+ *   - BA model: no NAND
+ *   - ME model: 32MB (Samsung K9F5608U0B)
+ *   - HI model: 128MB (Samsung K9F1G08UOM)
+ *   PPChameleonEVB options:
+ *   - 32MB (Samsung K9F5608U0B)
+ */
+
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <platforms/PPChameleonEVB.h>
+
+#undef USE_READY_BUSY_PIN
+#define USE_READY_BUSY_PIN
+/* see datasheets (tR) */
+#define NAND_BIG_DELAY_US		25
+#define NAND_SMALL_DELAY_US		10
+
+/* handy sizes */
+#define SZ_4M                           0x00400000
+#define NAND_SMALL_SIZE                 0x02000000
+#define NAND_MTD_NAME		"ppchameleon-nand"
+#define NAND_EVB_MTD_NAME	"ppchameleonevb-nand"
+
+/* GPIO pins used to drive NAND chip mounted on processor module */
+#define NAND_nCE_GPIO_PIN 		(0x80000000 >> 1)
+#define NAND_CLE_GPIO_PIN 		(0x80000000 >> 2)
+#define NAND_ALE_GPIO_PIN 		(0x80000000 >> 3)
+#define NAND_RB_GPIO_PIN 		(0x80000000 >> 4)
+/* GPIO pins used to drive NAND chip mounted on EVB */
+#define NAND_EVB_nCE_GPIO_PIN 	(0x80000000 >> 14)
+#define NAND_EVB_CLE_GPIO_PIN 	(0x80000000 >> 15)
+#define NAND_EVB_ALE_GPIO_PIN 	(0x80000000 >> 16)
+#define NAND_EVB_RB_GPIO_PIN 	(0x80000000 >> 31)
+
+/*
+ * MTD structure for PPChameleonEVB board
+ */
+static struct mtd_info *ppchameleon_mtd 	= NULL;
+static struct mtd_info *ppchameleonevb_mtd = NULL;
+
+/*
+ * Module stuff
+ */
+static int ppchameleon_fio_pbase 	= CFG_NAND0_PADDR;
+static int ppchameleonevb_fio_pbase = CFG_NAND1_PADDR;
+
+#ifdef MODULE
+MODULE_PARM(ppchameleon_fio_pbase, "i");
+__setup("ppchameleon_fio_pbase=",ppchameleon_fio_pbase);
+MODULE_PARM(ppchameleonevb_fio_pbase, "i");
+__setup("ppchameleonevb_fio_pbase=",ppchameleonevb_fio_pbase);
+#endif
+
+/* Internal buffers. Page buffer and oob buffer for one block */
+static u_char data_buf[2048 + 64];
+static u_char oob_buf[64 * 64];
+static u_char data_buf_evb[512 + 16];
+static u_char oob_buf_evb[16 * 32];
+
+#ifdef CONFIG_MTD_PARTITIONS
+/*
+ * Define static partitions for flash devices
+ */
+static struct mtd_partition partition_info_hi[] = {
+	{ name: "PPChameleon HI Nand Flash",
+		  offset: 0,
+		  size: 128*1024*1024 }
+};
+
+static struct mtd_partition partition_info_me[] = {
+	{ name: "PPChameleon ME Nand Flash",
+		  offset: 0,
+		  size: 32*1024*1024 }
+};
+
+static struct mtd_partition partition_info_evb[] = {
+	{ name: "PPChameleonEVB Nand Flash",
+		  offset: 0,
+		  size: 32*1024*1024 }
+};
+
+#define NUM_PARTITIONS 1
+
+extern int parse_cmdline_partitions(struct mtd_info *master,
+				    struct mtd_partition **pparts,
+				    const char *mtd_id);
+#endif
+
+
+/*
+ *	hardware specific access to control-lines
+ */
+static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd)
+{
+	switch(cmd) {
+
+	case NAND_CTL_SETCLE:
+        	MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR);
+		break;
+	case NAND_CTL_CLRCLE:
+        	MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR);
+		break;
+	case NAND_CTL_SETALE:
+        	MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR);
+		break;
+	case NAND_CTL_CLRALE:
+        	MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR);
+		break;
+	case NAND_CTL_SETNCE:
+			MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR);
+		break;
+	case NAND_CTL_CLRNCE:
+			MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR);
+		break;
+	}
+}
+
+static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd)
+{
+	switch(cmd) {
+
+	case NAND_CTL_SETCLE:
+        	MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR);
+		break;
+	case NAND_CTL_CLRCLE:
+        	MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR);
+		break;
+	case NAND_CTL_SETALE:
+        	MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR);
+		break;
+	case NAND_CTL_CLRALE:
+        	MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR);
+		break;
+	case NAND_CTL_SETNCE:
+        	MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR);
+		break;
+	case NAND_CTL_CLRNCE:
+        	MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR);
+		break;
+	}
+}
+
+#ifdef USE_READY_BUSY_PIN
+/*
+ *	read device ready pin
+ */
+static int ppchameleon_device_ready(struct mtd_info *minfo)
+{
+	if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_RB_GPIO_PIN)
+		return 1;
+	return 0;
+}
+
+static int ppchameleonevb_device_ready(struct mtd_info *minfo)
+{
+	if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_EVB_RB_GPIO_PIN)
+	return 1;
+	return 0;
+}
+#endif
+
+#ifdef CONFIG_MTD_PARTITIONS
+const char *part_probes[] = { "cmdlinepart", NULL };
+const char *part_probes_evb[] = { "cmdlinepart", NULL };
+#endif
+
+/*
+ * Main initialization routine
+ */
+static int __init ppchameleonevb_init (void)
+{
+	struct nand_chip *this;
+	const char *part_type = 0;
+	int mtd_parts_nb = 0;
+	struct mtd_partition *mtd_parts = 0;
+	int ppchameleon_fio_base;
+	int ppchameleonevb_fio_base;
+
+
+	/*********************************
+	* Processor module NAND (if any) *
+	*********************************/
+	/* Allocate memory for MTD device structure and private data */
+	ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) +
+			     sizeof(struct nand_chip),
+			     GFP_KERNEL);
+	if (!ppchameleon_mtd) {
+		printk("Unable to allocate PPChameleon NAND MTD device structure.\n");
+		return -ENOMEM;
+	}
+
+	/* map physical address */
+	ppchameleon_fio_base = (unsigned long)ioremap(ppchameleon_fio_pbase, SZ_4M);
+	if(!ppchameleon_fio_base) {
+		printk("ioremap PPChameleon NAND flash failed\n");
+		kfree(ppchameleon_mtd);
+		return -EIO;
+	}
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *) (&ppchameleon_mtd[1]);
+
+	/* Initialize structures */
+	memset((char *) ppchameleon_mtd, 0, sizeof(struct mtd_info));
+	memset((char *) this, 0, sizeof(struct nand_chip));
+
+	/* Link the private data with the MTD structure */
+	ppchameleon_mtd->priv = this;
+
+        /* Initialize GPIOs */
+	/* Pin mapping for NAND chip */
+	/*
+		CE	GPIO_01
+		CLE	GPIO_02
+		ALE	GPIO_03
+		R/B	GPIO_04
+	*/
+	/* output select */
+	out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xC0FFFFFF);
+	/* three-state select */
+	out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xC0FFFFFF);
+	/* enable output driver */
+	out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_nCE_GPIO_PIN | NAND_CLE_GPIO_PIN | NAND_ALE_GPIO_PIN);
+#ifdef USE_READY_BUSY_PIN
+	/* three-state select */
+	out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFF3FFFFF);
+	/* high-impedecence */
+	out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_RB_GPIO_PIN));
+	/* input select */
+	out_be32((volatile unsigned*)GPIO0_ISR1H, (in_be32((volatile unsigned*)GPIO0_ISR1H) & 0xFF3FFFFF) | 0x00400000);
+#endif
+
+	/* insert callbacks */
+	this->IO_ADDR_R = ppchameleon_fio_base;
+	this->IO_ADDR_W = ppchameleon_fio_base;
+	this->hwcontrol = ppchameleon_hwcontrol;
+#ifdef USE_READY_BUSY_PIN
+	this->dev_ready = ppchameleon_device_ready;
+#endif
+	this->chip_delay = NAND_BIG_DELAY_US;
+	/* ECC mode */
+	this->eccmode = NAND_ECC_SOFT;
+
+	/* Set internal data buffer */
+	this->data_buf = data_buf;
+	this->oob_buf = oob_buf;
+
+	/* Scan to find existence of the device (it could not be mounted) */
+	if (nand_scan (ppchameleon_mtd, 1)) {
+		iounmap((void *)ppchameleon_fio_base);
+		kfree (ppchameleon_mtd);
+		goto nand_evb_init;
+	}
+
+#ifndef USE_READY_BUSY_PIN
+	/* Adjust delay if necessary */
+	if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
+		this->chip_delay = NAND_SMALL_DELAY_US;
+#endif
+
+#ifdef CONFIG_MTD_PARTITIONS
+	ppchameleon_mtd->name = "ppchameleon-nand";
+	mtd_parts_nb = parse_mtd_partitions(ppchameleon_mtd, part_probes, &mtd_parts, 0);
+	if (mtd_parts_nb > 0)
+	  part_type = "command line";
+	else
+	  mtd_parts_nb = 0;
+#endif
+	if (mtd_parts_nb == 0)
+	{
+		if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
+			mtd_parts = partition_info_me;
+		else
+			mtd_parts = partition_info_hi;
+		mtd_parts_nb = NUM_PARTITIONS;
+		part_type = "static";
+	}
+
+	/* Register the partitions */
+	printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+	add_mtd_partitions(ppchameleon_mtd, mtd_parts, mtd_parts_nb);
+
+nand_evb_init:
+	/****************************
+	* EVB NAND (always present) *
+	****************************/
+	/* Allocate memory for MTD device structure and private data */
+	ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) +
+			     sizeof(struct nand_chip),
+			     GFP_KERNEL);
+	if (!ppchameleonevb_mtd) {
+		printk("Unable to allocate PPChameleonEVB NAND MTD device structure.\n");
+		return -ENOMEM;
+	}
+
+	/* map physical address */
+	ppchameleonevb_fio_base = (unsigned long)ioremap(ppchameleonevb_fio_pbase, SZ_4M);
+	if(!ppchameleonevb_fio_base) {
+		printk("ioremap PPChameleonEVB NAND flash failed\n");
+		kfree(ppchameleonevb_mtd);
+		return -EIO;
+	}
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *) (&ppchameleonevb_mtd[1]);
+
+	/* Initialize structures */
+	memset((char *) ppchameleonevb_mtd, 0, sizeof(struct mtd_info));
+	memset((char *) this, 0, sizeof(struct nand_chip));
+
+	/* Link the private data with the MTD structure */
+	ppchameleonevb_mtd->priv = this;
+
+        /* Initialize GPIOs */
+	/* Pin mapping for NAND chip */
+	/*
+		CE	GPIO_14
+		CLE	GPIO_15
+		ALE	GPIO_16
+		R/B	GPIO_31
+	*/
+	/* output select */
+	out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xFFFFFFF0);
+	out_be32((volatile unsigned*)GPIO0_OSRL, in_be32((volatile unsigned*)GPIO0_OSRL) & 0x3FFFFFFF);
+	/* three-state select */
+	out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFFFFFFF0);
+	out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0x3FFFFFFF);
+	/* enable output driver */
+	out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN | NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN);
+#ifdef USE_READY_BUSY_PIN
+	/* three-state select */
+	out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0xFFFFFFFC);
+	/* high-impedecence */
+	out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_EVB_RB_GPIO_PIN));
+	/* input select */
+	out_be32((volatile unsigned*)GPIO0_ISR1L, (in_be32((volatile unsigned*)GPIO0_ISR1L) & 0xFFFFFFFC) | 0x00000001);
+#endif
+
+
+	/* insert callbacks */
+	this->IO_ADDR_R = ppchameleonevb_fio_base;
+	this->IO_ADDR_W = ppchameleonevb_fio_base;
+	this->hwcontrol = ppchameleonevb_hwcontrol;
+#ifdef USE_READY_BUSY_PIN
+	this->dev_ready = ppchameleonevb_device_ready;
+#endif
+	this->chip_delay = NAND_SMALL_DELAY_US;
+
+	/* ECC mode */
+	this->eccmode = NAND_ECC_SOFT;
+
+	/* Set internal data buffer */
+	this->data_buf = data_buf_evb;
+	this->oob_buf = oob_buf_evb;
+
+	/* Scan to find existence of the device */
+	if (nand_scan (ppchameleonevb_mtd, 1)) {
+		iounmap((void *)ppchameleonevb_fio_base);
+		kfree (ppchameleonevb_mtd);
+		return -ENXIO;
+	}
+
+#ifdef CONFIG_MTD_PARTITIONS
+	ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME;
+	mtd_parts_nb = parse_mtd_partitions(ppchameleonevb_mtd, part_probes_evb, &mtd_parts, 0);
+	if (mtd_parts_nb > 0)
+	  part_type = "command line";
+	else
+	  mtd_parts_nb = 0;
+#endif
+	if (mtd_parts_nb == 0)
+	{
+		mtd_parts = partition_info_evb;
+		mtd_parts_nb = NUM_PARTITIONS;
+		part_type = "static";
+	}
+
+	/* Register the partitions */
+	printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+	add_mtd_partitions(ppchameleonevb_mtd, mtd_parts, mtd_parts_nb);
+
+	/* Return happy */
+	return 0;
+}
+module_init(ppchameleonevb_init);
+
+/*
+ * Clean up routine
+ */
+static void __exit ppchameleonevb_cleanup (void)
+{
+	struct nand_chip *this = (struct nand_chip *) &ppchameleonevb_mtd[1];
+
+	/* Unregister the device */
+	del_mtd_device (ppchameleonevb_mtd);
+
+	/* Free internal data buffer */
+	kfree (this->data_buf);
+
+	/* Free the MTD device structure */
+	kfree (ppchameleonevb_mtd);
+}
+module_exit(ppchameleonevb_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("DAVE Srl <support-ppchameleon@dave-tech.it>");
+MODULE_DESCRIPTION("MTD map driver for DAVE Srl PPChameleonEVB board");
diff -Nur linux-2.4.30/drivers/mtd/nand/spia.c linux-2.4.30-linux-mips.org/drivers/mtd/nand/spia.c
--- linux-2.4.30/drivers/mtd/nand/spia.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nand/spia.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,14 +1,14 @@
 /*
  *  drivers/mtd/nand/spia.c
  *
- *  Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
+ *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  *
  *
  *	10-29-2001 TG	change to support hardwarespecific access
  *			to controllines	(due to change in nand.c)
  *			page_cache added
  *
- * $Id: spia.c,v 1.16 2002/03/05 13:50:47 dwmw2 Exp $
+ * $Id: spia.c,v 1.21 2003/07/11 15:12:29 dwmw2 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,6 +20,8 @@
  *   a 64Mibit (8MiB x 8 bits) NAND flash device.
  */
 
+#include <linux/kernel.h>
+#include <linux/init.h>
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/mtd/mtd.h>
@@ -35,14 +37,14 @@
 /*
  * Values specific to the SPIA board (used with EP7212 processor)
  */
-#define SPIA_IO_ADDR	= 0xd0000000	/* Start of EP7212 IO address space */
-#define SPIA_FIO_ADDR	= 0xf0000000	/* Address where flash is mapped */
-#define SPIA_PEDR	= 0x0080	/*
+#define SPIA_IO_BASE	0xd0000000	/* Start of EP7212 IO address space */
+#define SPIA_FIO_BASE	0xf0000000	/* Address where flash is mapped */
+#define SPIA_PEDR	0x0080		/*
 					 * IO offset to Port E data register
 					 * where the CLE, ALE and NCE pins
 					 * are wired to.
 					 */
-#define SPIA_PEDDR	= 0x00c0	/*
+#define SPIA_PEDDR	0x00c0		/*
 					 * IO offset to Port E data direction
 					 * register so we can control the IO
 					 * lines.
@@ -62,21 +64,20 @@
 MODULE_PARM(spia_pedr, "i");
 MODULE_PARM(spia_peddr, "i");
 
-__setup("spia_io_base=",spia_io_base);
-__setup("spia_fio_base=",spia_fio_base);
-__setup("spia_pedr=",spia_pedr);
-__setup("spia_peddr=",spia_peddr);
-
 /*
  * Define partitions for flash device
  */
 const static struct mtd_partition partition_info[] = {
-	{ name: "SPIA flash partition 1",
-	  offset: 0,
-	  size: 2*1024*1024 },
-	{ name: "SPIA flash partition 2",
-	  offset: 2*1024*1024,
-	  size: 6*1024*1024 }
+	{
+		.name	= "SPIA flash partition 1",
+		.offset	= 0,
+		.size	= 2*1024*1024
+	},
+	{
+		.name	= "SPIA flash partition 2",
+		.offset	= 2*1024*1024,
+		.size	= 6*1024*1024
+	}
 };
 #define NUM_PARTITIONS 2
 
@@ -84,7 +85,7 @@
 /* 
  *	hardware specific access to control-lines
 */
-void spia_hwcontrol(int cmd){
+static void spia_hwcontrol(struct mtd_info *mtd, int cmd){
 
     switch(cmd){
 
@@ -139,7 +140,7 @@
 	this->chip_delay = 15;		
 
 	/* Scan to find existence of the device */
-	if (nand_scan (spia_mtd)) {
+	if (nand_scan (spia_mtd, 1)) {
 		kfree (spia_mtd);
 		return -ENXIO;
 	}
@@ -152,16 +153,6 @@
 		return -ENOMEM;
 	}
 
-	/* Allocate memory for internal data buffer */
-	this->data_cache = kmalloc (sizeof(u_char) * (spia_mtd->oobblock + spia_mtd->oobsize), GFP_KERNEL);
-	if (!this->data_cache) {
-		printk ("Unable to allocate NAND data cache for SPIA.\n");
-		kfree (this->data_buf);
-		kfree (spia_mtd);
-		return = -ENOMEM;
-	}
-	this->cache_page = -1;
-
 	/* Register the partitions */
 	add_mtd_partitions(spia_mtd, partition_info, NUM_PARTITIONS);
 
@@ -183,7 +174,6 @@
 
 	/* Free internal data buffer */
 	kfree (this->data_buf);
-	kfree (this->page_cache);
 
 	/* Free the MTD device structure */
 	kfree (spia_mtd);
@@ -192,5 +182,5 @@
 #endif
 
 MODULE_LICENSE("GPL");
-MODULE_AUTHOR("Steven J. Hill <sjhill@cotw.com");
+MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com");
 MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on SPIA board");
diff -Nur linux-2.4.30/drivers/mtd/nand/toto.c linux-2.4.30-linux-mips.org/drivers/mtd/nand/toto.c
--- linux-2.4.30/drivers/mtd/nand/toto.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nand/toto.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,221 @@
+/*
+ *  drivers/mtd/nand/toto.c
+ *
+ *  Copyright (c) 2003 Texas Instruments
+ *
+ *  Derived from drivers/mtd/autcpu12.c
+ *
+ *  Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
+ *
+ * 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.
+ *
+ *  Overview:
+ *   This is a device driver for the NAND flash device found on the
+ *   TI fido board. It supports 32MiB and 64MiB cards
+ *
+ * $Id: toto.c,v 1.2 2003/10/21 10:04:58 dwmw2 Exp $
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <asm/arch/hardware.h>
+#include <asm/sizes.h>
+#include <asm/arch/toto.h>
+#include <asm/arch-omap1510/hardware.h>
+#include <asm/arch/gpio.h>
+
+/*
+ * MTD structure for TOTO board
+ */
+static struct mtd_info *toto_mtd = NULL;
+
+static int toto_io_base = OMAP_FLASH_1_BASE;
+
+#define CONFIG_NAND_WORKAROUND 1
+
+#define NAND_NCE 0x4000
+#define NAND_CLE 0x1000
+#define NAND_ALE 0x0002
+#define NAND_MASK (NAND_CLE | NAND_ALE | NAND_NCE)
+
+#define T_NAND_CTL_CLRALE(iob)  gpiosetout(NAND_ALE, 0)
+#define T_NAND_CTL_SETALE(iob)  gpiosetout(NAND_ALE, NAND_ALE)
+#ifdef CONFIG_NAND_WORKAROUND     /* "some" dev boards busted, blue wired to rts2 :( */
+#define T_NAND_CTL_CLRCLE(iob)  gpiosetout(NAND_CLE, 0); rts2setout(2, 2)
+#define T_NAND_CTL_SETCLE(iob)  gpiosetout(NAND_CLE, NAND_CLE); rts2setout(2, 0)
+#else
+#define T_NAND_CTL_CLRCLE(iob)  gpiosetout(NAND_CLE, 0)
+#define T_NAND_CTL_SETCLE(iob)  gpiosetout(NAND_CLE, NAND_CLE)
+#endif
+#define T_NAND_CTL_SETNCE(iob)  gpiosetout(NAND_NCE, 0)
+#define T_NAND_CTL_CLRNCE(iob)  gpiosetout(NAND_NCE, NAND_NCE)
+                
+/*
+ * Define partitions for flash devices
+ */
+
+static struct mtd_partition partition_info64M[] = {
+	{ .name =	"toto kernel partition 1",
+	  .offset =	0,
+	  .size	=	2 * SZ_1M },
+	{ .name =	"toto file sys partition 2",
+	  .offset =	2 * SZ_1M,
+	  .size =	14 * SZ_1M },
+	{ .name =	"toto user partition 3",
+	  .offset =	16 * SZ_1M,
+	  .size =	16 * SZ_1M },
+	{ .name =	"toto devboard extra partition 4",
+	  .offset =	32 * SZ_1M,
+	  .size =	32 * SZ_1M },
+};
+
+static struct mtd_partition partition_info32M[] = {
+	{ .name =	"toto kernel partition 1",
+	  .offset =	0,
+	  .size =	2 * SZ_1M },
+	{ .name =	"toto file sys partition 2",
+	  .offset =	2 * SZ_1M,
+	  .size =	14 * SZ_1M },
+	{ .name =	"toto user partition 3",
+	  .offset =	16 * SZ_1M,
+	  .size =	16 * SZ_1M },
+};
+
+#define NUM_PARTITIONS32M 3
+#define NUM_PARTITIONS64M 4
+/* 
+ *	hardware specific access to control-lines
+*/
+
+static void toto_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+
+	udelay(1); /* hopefully enough time for tc make proceding write to clear */
+	switch(cmd){
+
+		case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break;
+		case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break;
+
+		case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break;
+		case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break;
+
+		case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break;
+		case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break;
+	}
+	udelay(1); /* allow time to ensure gpio state to over take memory write */
+}
+
+/*
+ * Main initialization routine
+ */
+int __init toto_init (void)
+{
+	struct nand_chip *this;
+	int err = 0;
+
+	/* Allocate memory for MTD device structure and private data */
+	toto_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
+				GFP_KERNEL);
+	if (!toto_mtd) {
+		printk (KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *) (&toto_mtd[1]);
+
+	/* Initialize structures */
+	memset((char *) toto_mtd, 0, sizeof(struct mtd_info));
+	memset((char *) this, 0, sizeof(struct nand_chip));
+
+	/* Link the private data with the MTD structure */
+	toto_mtd->priv = this;
+
+	/* Set address of NAND IO lines */
+	this->IO_ADDR_R = toto_io_base;
+	this->IO_ADDR_W = toto_io_base;
+	this->hwcontrol = toto_hwcontrol;
+	this->dev_ready = NULL;
+	/* 25 us command delay time */
+	this->chip_delay = 30;		
+	this->eccmode = NAND_ECC_SOFT;
+
+        /* Scan to find existance of the device */
+	if (nand_scan (toto_mtd, 1)) {
+		err = -ENXIO;
+		goto out_mtd;
+	}
+
+	/* Allocate memory for internal data buffer */
+	this->data_buf = kmalloc (sizeof(u_char) * (toto_mtd->oobblock + toto_mtd->oobsize), GFP_KERNEL);
+	if (!this->data_buf) {
+		printk (KERN_WARNING "Unable to allocate NAND data buffer for toto.\n");
+		err = -ENOMEM;
+		goto out_mtd;
+	}
+
+	/* Register the partitions */
+	switch(toto_mtd->size){
+		case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break; 
+		case SZ_32M: add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M); break; 
+		default: {
+			printk (KERN_WARNING "Unsupported Nand device\n"); 
+			err = -ENXIO;
+			goto out_buf;
+		}
+	}
+
+    	gpioreserve(NAND_MASK);  /* claim our gpios */
+    	archflashwp(0,0);	 /* open up flash for writing */
+
+	goto out;
+    
+out_buf:
+	kfree (this->data_buf);    
+out_mtd:
+	kfree (toto_mtd);
+out:
+	return err;
+}
+
+module_init(toto_init);
+
+/*
+ * Clean up routine
+ */
+static void __exit toto_cleanup (void)
+{
+	struct nand_chip *this = (struct nand_chip *) &toto_mtd[1];
+
+	/* Unregister partitions */
+	del_mtd_partitions(toto_mtd);
+	
+	/* Unregister the device */
+	del_mtd_device (toto_mtd);
+
+	/* Free internal data buffers */
+	kfree (this->data_buf);
+
+	/* Free the MTD device structure */
+	kfree (toto_mtd);
+
+	/* stop flash writes */
+	 archflashwp(0,1);
+	
+	/* release gpios to system */
+	 gpiorelease(NAND_MASK);
+}
+module_exit(toto_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Richard Woodruff <r-woodruff2@ti.com>");
+MODULE_DESCRIPTION("Glue layer for NAND flash on toto board");
diff -Nur linux-2.4.30/drivers/mtd/nand/tx4925ndfmc.c linux-2.4.30-linux-mips.org/drivers/mtd/nand/tx4925ndfmc.c
--- linux-2.4.30/drivers/mtd/nand/tx4925ndfmc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nand/tx4925ndfmc.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,442 @@
+/*
+ *  drivers/mtd/tx4925ndfmc.c
+ *
+ *  Overview:
+ *   This is a device driver for the NAND flash device found on the
+ *   Toshiba RBTX4925 reference board, which is a SmartMediaCard. It supports 
+ *   16MiB, 32MiB and 64MiB cards.
+ *
+ * Author: MontaVista Software, Inc.  source@mvista.com
+ *
+ * Derived from drivers/mtd/autcpu12.c
+ *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ * $Id: tx4925ndfmc.c,v 1.2 2004/03/27 19:55:53 gleixner Exp $
+ *
+ * Copyright (C) 2001 Toshiba Corporation 
+ * 
+ * 2003 (c) MontaVista Software, Inc. This file is licensed under
+ * the terms of the GNU General Public License version 2. This program
+ * is licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/tx4925/tx4925_nand.h>
+
+extern struct nand_oobinfo jffs2_oobinfo;
+
+/*
+ * MTD structure for RBTX4925 board
+ */
+static struct mtd_info *tx4925ndfmc_mtd = NULL;
+
+/*
+ * Module stuff
+ */
+#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
+#define tx4925ndfmc_init init_module
+#define tx4925ndfmc_cleanup cleanup_module
+#endif
+
+/*
+ * Define partitions for flash devices
+ */
+
+static struct mtd_partition partition_info16k[] = {
+	{ .name = "RBTX4925 flash partition 1",
+	  .offset =  0,
+	  .size =    8 * 0x00100000 },
+	{ .name = "RBTX4925 flash partition 2",
+	  .offset =  8 * 0x00100000,
+	  .size =    8 * 0x00100000 },
+};
+
+static struct mtd_partition partition_info32k[] = {
+	{ .name = "RBTX4925 flash partition 1",
+	  .offset =  0,
+	  .size =    8 * 0x00100000 },
+	{ .name = "RBTX4925 flash partition 2",
+	  .offset = 8 * 0x00100000,
+	  .size =  24 * 0x00100000 },
+};
+
+static struct mtd_partition partition_info64k[] = {
+	{ .name = "User FS",
+	  .offset =  0,
+	  .size =   16 * 0x00100000 },
+	{ .name = "RBTX4925 flash partition 2",
+	  .offset = 16 * 0x00100000,
+	  .size =   48 * 0x00100000},
+};
+
+static struct mtd_partition partition_info128k[] = {
+	{ .name = "Skip bad section",
+	  .offset =  0,
+	  .size =   16 * 0x00100000 },
+	{ .name = "User FS",
+	  .offset = 16 * 0x00100000,
+	  .size =   112 * 0x00100000 },
+};
+#define NUM_PARTITIONS16K  2
+#define NUM_PARTITIONS32K  2
+#define NUM_PARTITIONS64K  2
+#define NUM_PARTITIONS128K 2
+
+/* 
+ *	hardware specific access to control-lines
+*/
+static void tx4925ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+
+	switch(cmd){
+
+		case NAND_CTL_SETCLE: 
+			tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CLE;
+			break;
+		case NAND_CTL_CLRCLE:
+			tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CLE;
+			break;
+		case NAND_CTL_SETALE:
+			tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ALE;
+			break;
+		case NAND_CTL_CLRALE: 
+			tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ALE;
+			break;
+		case NAND_CTL_SETNCE:
+			tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CE;
+			break;
+		case NAND_CTL_CLRNCE:
+			tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CE;
+			break;
+		case NAND_CTL_SETWP:
+			tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_WE;
+			break;
+		case NAND_CTL_CLRWP:
+			tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_WE;
+			break;
+	}
+}
+
+/*
+*	read device ready pin
+*/
+static int tx4925ndfmc_device_ready(struct mtd_info *mtd)
+{
+	int ready;
+	ready = (tx4925_ndfmcptr->sr & TX4925_NDSFR_BUSY) ? 0 : 1;
+	return ready;
+}
+void tx4925ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+	/* reset first */
+	tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_MASK;
+	tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
+	tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_ENAB;
+}
+static void tx4925ndfmc_disable_ecc(void)
+{
+	tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
+}
+static void tx4925ndfmc_enable_read_ecc(void)
+{
+	tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
+	tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_READ;
+}
+void tx4925ndfmc_readecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){
+	int i;
+	u_char *ecc = ecc_code;
+        tx4925ndfmc_enable_read_ecc();
+	for (i = 0;i < 6;i++,ecc++)
+		*ecc = tx4925_read_nfmc(&(tx4925_ndfmcptr->dtr));
+        tx4925ndfmc_disable_ecc();
+}
+void tx4925ndfmc_device_setup(void)
+{
+
+	*(unsigned char *)0xbb005000 &= ~0x08;
+
+        /* reset NDFMC */
+        tx4925_ndfmcptr->rstr |= TX4925_NDFRSTR_RST;
+	while (tx4925_ndfmcptr->rstr & TX4925_NDFRSTR_RST);       
+
+	/* setup BusSeparete, Hold Time, Strobe Pulse Width */
+	tx4925_ndfmcptr->mcr = TX4925_BSPRT ? TX4925_NDFMCR_BSPRT : 0;
+	tx4925_ndfmcptr->spr = TX4925_HOLD << 4 | TX4925_SPW;             
+}
+static u_char tx4925ndfmc_nand_read_byte(struct mtd_info *mtd)
+{
+        struct nand_chip *this = mtd->priv;
+        return tx4925_read_nfmc(this->IO_ADDR_R);
+}
+
+static void tx4925ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
+{
+        struct nand_chip *this = mtd->priv;
+        tx4925_write_nfmc(byte, this->IO_ADDR_W);
+}
+
+static void tx4925ndfmc_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++)
+		tx4925_write_nfmc(buf[i], this->IO_ADDR_W);
+}
+
+static void tx4925ndfmc_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] = tx4925_read_nfmc(this->IO_ADDR_R);
+}
+
+static int tx4925ndfmc_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] != tx4925_read_nfmc(this->IO_ADDR_R))
+			return -EFAULT;
+
+	return 0;
+}
+
+/*
+ * Send command to NAND device
+ */
+static void tx4925ndfmc_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)
+			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 higher density devices */
+			if (mtd->size & 0x0c000000) 
+				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:
+		/* Turn off WE */
+		this->hwcontrol (mtd, NAND_CTL_CLRWP);
+                return;
+
+	case NAND_CMD_SEQIN:
+		/* Turn on WE */
+		this->hwcontrol (mtd, NAND_CTL_SETWP);
+                return;
+
+	case NAND_CMD_ERASE1:
+	case NAND_CMD_ERASE2:
+	case NAND_CMD_STATUS:
+		return;
+
+	case NAND_CMD_RESET:
+		if (this->dev_ready)	
+			break;
+		this->hwcontrol(mtd, NAND_CTL_SETCLE);
+		this->write_byte(mtd, NAND_CMD_STATUS);
+		this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+		while ( !(this->read_byte(mtd) & 0x40));
+		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;
+		}	
+	}
+	
+	/* wait until command is processed */
+	while (!this->dev_ready(mtd));
+}
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partitio
+n **pparts, char *);
+#endif
+
+/*
+ * Main initialization routine
+ */
+extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
+int __init tx4925ndfmc_init (void)
+{
+	struct nand_chip *this;
+	int err = 0;
+
+	/* Allocate memory for MTD device structure and private data */
+	tx4925ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
+				GFP_KERNEL);
+	if (!tx4925ndfmc_mtd) {
+		printk ("Unable to allocate RBTX4925 NAND MTD device structure.\n");
+		err = -ENOMEM;
+		goto out;
+	}
+
+        tx4925ndfmc_device_setup();
+
+	/* io is indirect via a register so don't need to ioremap address */
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *) (&tx4925ndfmc_mtd[1]);
+
+	/* Initialize structures */
+	memset((char *) tx4925ndfmc_mtd, 0, sizeof(struct mtd_info));
+	memset((char *) this, 0, sizeof(struct nand_chip));
+
+	/* Link the private data with the MTD structure */
+	tx4925ndfmc_mtd->priv = this;
+
+	/* Set address of NAND IO lines */
+	this->IO_ADDR_R = (unsigned long)&(tx4925_ndfmcptr->dtr);
+	this->IO_ADDR_W = (unsigned long)&(tx4925_ndfmcptr->dtr);
+	this->hwcontrol = tx4925ndfmc_hwcontrol;
+	this->enable_hwecc = tx4925ndfmc_enable_hwecc;
+	this->calculate_ecc = tx4925ndfmc_readecc;
+	this->correct_data = nand_correct_data;
+	this->eccmode = NAND_ECC_HW6_512;	
+	this->dev_ready = tx4925ndfmc_device_ready;
+	/* 20 us command delay time */
+	this->chip_delay = 20;		
+        this->read_byte = tx4925ndfmc_nand_read_byte;
+        this->write_byte = tx4925ndfmc_nand_write_byte;
+	this->cmdfunc = tx4925ndfmc_nand_command;
+	this->write_buf = tx4925ndfmc_nand_write_buf;
+	this->read_buf = tx4925ndfmc_nand_read_buf;
+	this->verify_buf = tx4925ndfmc_nand_verify_buf;
+
+	/* Scan to find existance of the device */
+	if (nand_scan (tx4925ndfmc_mtd, 1)) {
+		err = -ENXIO;
+		goto out_ior;
+	}
+
+	/* Allocate memory for internal data buffer */
+	this->data_buf = kmalloc (sizeof(u_char) * (tx4925ndfmc_mtd->oobblock + tx4925ndfmc_mtd->oobsize), GFP_KERNEL);
+	if (!this->data_buf) {
+		printk ("Unable to allocate NAND data buffer for RBTX4925.\n");
+		err = -ENOMEM;
+		goto out_ior;
+	}
+
+	/* Register the partitions */
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+        {
+                int mtd_parts_nb = 0;
+                struct mtd_partition *mtd_parts = 0;
+                mtd_parts_nb = parse_cmdline_partitions(tx4925ndfmc_mtd, &mtd_parts, "tx4925ndfmc");
+                if (mtd_parts_nb > 0)
+                        add_mtd_partitions(tx4925ndfmc_mtd, mtd_parts, mtd_parts_nb);
+                else
+                        add_mtd_device(tx4925ndfmc_mtd);
+        }
+#else /* ifdef CONFIG_MTD_CMDLINE_PARTS */
+	switch(tx4925ndfmc_mtd->size){
+		case 0x01000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info16k, NUM_PARTITIONS16K); break;
+		case 0x02000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info32k, NUM_PARTITIONS32K); break;
+		case 0x04000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info64k, NUM_PARTITIONS64K); break; 
+		case 0x08000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info128k, NUM_PARTITIONS128K); break; 
+		default: {
+			printk ("Unsupported SmartMedia device\n"); 
+			err = -ENXIO;
+			goto out_buf;
+		}
+	}
+#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */
+	goto out;
+
+out_buf:
+	kfree (this->data_buf);    
+out_ior:
+out:
+	return err;
+}
+
+module_init(tx4925ndfmc_init);
+
+/*
+ * Clean up routine
+ */
+#ifdef MODULE
+static void __exit tx4925ndfmc_cleanup (void)
+{
+	struct nand_chip *this = (struct nand_chip *) &tx4925ndfmc_mtd[1];
+
+	/* Unregister partitions */
+	del_mtd_partitions(tx4925ndfmc_mtd);
+	
+	/* Unregister the device */
+	del_mtd_device (tx4925ndfmc_mtd);
+
+	/* Free internal data buffers */
+	kfree (this->data_buf);
+
+	/* Free the MTD device structure */
+	kfree (tx4925ndfmc_mtd);
+}
+module_exit(tx4925ndfmc_cleanup);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
+MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBTX4925");
diff -Nur linux-2.4.30/drivers/mtd/nand/tx4938ndfmc.c linux-2.4.30-linux-mips.org/drivers/mtd/nand/tx4938ndfmc.c
--- linux-2.4.30/drivers/mtd/nand/tx4938ndfmc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nand/tx4938ndfmc.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,422 @@
+/*
+ * drivers/mtd/nand/tx4938ndfmc.c
+ *
+ *  Overview:
+ *   This is a device driver for the NAND flash device connected to
+ *   TX4938 internal NAND Memory Controller.
+ *   TX4938 NDFMC is almost same as TX4925 NDFMC, but register size are 64 bit.
+ *
+ * Author: source@mvista.com
+ *
+ * Based on spia.c by Steven J. Hill
+ *
+ * $Id: tx4938ndfmc.c,v 1.2 2004/03/27 19:55:53 gleixner Exp $
+ *
+ * Copyright (C) 2000-2001 Toshiba Corporation 
+ *
+ * 2003 (c) MontaVista Software, Inc. This file is licensed under the
+ * terms of the GNU General Public License version 2. This program is
+ * licensed "as is" without any warranty of any kind, whether express
+ * or implied.
+ */
+#include <linux/config.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <asm/bootinfo.h>
+#include <linux/delay.h>
+#include <asm/tx4938/rbtx4938.h>
+
+extern struct nand_oobinfo jffs2_oobinfo;
+
+/*
+ * MTD structure for TX4938 NDFMC
+ */
+static struct mtd_info *tx4938ndfmc_mtd;
+
+/*
+ * Define partitions for flash device
+ */
+#define flush_wb()	(void)tx4938_ndfmcptr->mcr;
+
+#define NUM_PARTITIONS  	3
+#define NUMBER_OF_CIS_BLOCKS	24
+#define SIZE_OF_BLOCK		0x00004000
+#define NUMBER_OF_BLOCK_PER_ZONE 1024
+#define SIZE_OF_ZONE		(NUMBER_OF_BLOCK_PER_ZONE * SIZE_OF_BLOCK)
+#ifndef CONFIG_MTD_CMDLINE_PARTS
+/*
+ * You can use the following sample of MTD partitions 
+ * on the NAND Flash Memory 32MB or more.
+ *
+ * The following figure shows the image of the sample partition on
+ * the 32MB NAND Flash Memory. 
+ *
+ *   Block No.
+ *    0 +-----------------------------+ ------
+ *      |             CIS             |   ^
+ *   24 +-----------------------------+   |
+ *      |         kernel image        |   | Zone 0
+ *      |                             |   |
+ *      +-----------------------------+   |
+ * 1023 |         unused area         |   v
+ *      +-----------------------------+ ------
+ * 1024 |            JFFS2            |   ^
+ *      |                             |   |
+ *      |                             |   | Zone 1
+ *      |                             |   |
+ *      |                             |   |
+ *      |                             |   v
+ * 2047 +-----------------------------+ ------
+ *
+ */
+static struct mtd_partition partition_info[NUM_PARTITIONS] = {
+	{
+		.name = "RBTX4938 CIS Area",
+ 		.offset =  0,
+ 		.size =    (NUMBER_OF_CIS_BLOCKS * SIZE_OF_BLOCK),
+ 		.mask_flags  = MTD_WRITEABLE	/* This partition is NOT writable */
+ 	},
+ 	{
+ 		.name = "RBTX4938 kernel image",
+ 		.offset =  MTDPART_OFS_APPEND,
+ 		.size =    8 * 0x00100000,	/* 8MB (Depends on size of kernel image) */
+ 		.mask_flags  = MTD_WRITEABLE	/* This partition is NOT writable */
+ 	},
+ 	{
+ 		.name = "Root FS (JFFS2)",
+ 		.offset =  (0 + SIZE_OF_ZONE),    /* start address of next zone */
+ 		.size =    MTDPART_SIZ_FULL
+ 	},
+};
+#endif
+
+static void tx4938ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+	switch (cmd) {
+		case NAND_CTL_SETCLE:
+			tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CLE;
+			break;
+		case NAND_CTL_CLRCLE:
+			tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CLE;
+			break;
+		case NAND_CTL_SETALE:
+			tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_ALE;
+			break;
+		case NAND_CTL_CLRALE:
+			tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_ALE;
+			break;
+		/* TX4938_NDFMCR_CE bit is 0:high 1:low */
+		case NAND_CTL_SETNCE:
+			tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CE;
+			break;
+		case NAND_CTL_CLRNCE:
+			tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CE;
+			break;
+		case NAND_CTL_SETWP:
+			tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_WE;
+			break;
+		case NAND_CTL_CLRWP:
+			tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_WE;
+			break;
+	}
+}
+static int tx4938ndfmc_dev_ready(struct mtd_info *mtd)
+{
+	flush_wb();
+	return !(tx4938_ndfmcptr->sr & TX4938_NDFSR_BUSY);
+}
+static void tx4938ndfmc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+{
+	u32 mcr = tx4938_ndfmcptr->mcr;
+	mcr &= ~TX4938_NDFMCR_ECC_ALL;
+	tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
+	tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_READ;
+	ecc_code[1] = tx4938_ndfmcptr->dtr;
+	ecc_code[0] = tx4938_ndfmcptr->dtr;
+	ecc_code[2] = tx4938_ndfmcptr->dtr;
+	tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
+}
+static void tx4938ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+	u32 mcr = tx4938_ndfmcptr->mcr;
+	mcr &= ~TX4938_NDFMCR_ECC_ALL;
+	tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_RESET;
+	tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
+	tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_ON;
+}
+
+static u_char tx4938ndfmc_nand_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	return tx4938_read_nfmc(this->IO_ADDR_R);
+}
+
+static void tx4938ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
+{
+	struct nand_chip *this = mtd->priv;
+	tx4938_write_nfmc(byte, this->IO_ADDR_W);
+}
+
+static void tx4938ndfmc_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++)
+		tx4938_write_nfmc(buf[i], this->IO_ADDR_W);
+}
+
+static void tx4938ndfmc_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] = tx4938_read_nfmc(this->IO_ADDR_R);
+}
+
+static int tx4938ndfmc_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] != tx4938_read_nfmc(this->IO_ADDR_R))
+			return -EFAULT;
+
+	return 0;
+}
+
+/*
+ * Send command to NAND device
+ */
+static void tx4938ndfmc_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)
+			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 higher density devices */
+			if (mtd->size & 0x0c000000) 
+				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:
+		/* Turn off WE */
+		this->hwcontrol (mtd, NAND_CTL_CLRWP);
+                return;
+
+	case NAND_CMD_SEQIN:
+		/* Turn on WE */
+		this->hwcontrol (mtd, NAND_CTL_SETWP);
+                return;
+
+	case NAND_CMD_ERASE1:
+	case NAND_CMD_ERASE2:
+	case NAND_CMD_STATUS:
+		return;
+
+	case NAND_CMD_RESET:
+		if (this->dev_ready)	
+			break;
+		this->hwcontrol(mtd, NAND_CTL_SETCLE);
+		this->write_byte(mtd, NAND_CMD_STATUS);
+		this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+		while ( !(this->read_byte(mtd) & 0x40));
+		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;
+		}	
+	}
+	
+	/* wait until command is processed */
+	while (!this->dev_ready(mtd));
+}
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *);
+#endif
+/*
+ * Main initialization routine
+ */
+int __init tx4938ndfmc_init (void)
+{
+	struct nand_chip *this;
+	int bsprt = 0, hold = 0xf, spw = 0xf;
+	int protected = 0;
+
+	if ((*rbtx4938_piosel_ptr & 0x0c) != 0x08) {
+		printk("TX4938 NDFMC: disabled by IOC PIOSEL\n");
+		return -ENODEV;
+	}
+	bsprt = 1;
+	hold = 2;
+	spw = 9 - 1;	/* 8 GBUSCLK = 80ns (@ GBUSCLK 100MHz) */
+
+	if ((tx4938_ccfgptr->pcfg &
+	     (TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL))
+	    != TX4938_PCFG_NDF_SEL) {
+		printk("TX4938 NDFMC: disabled by PCFG.\n");
+		return -ENODEV;
+	}
+
+	/* reset NDFMC */
+	tx4938_ndfmcptr->rstr |= TX4938_NDFRSTR_RST;
+	while (tx4938_ndfmcptr->rstr & TX4938_NDFRSTR_RST)
+		;
+	/* setup BusSeparete, Hold Time, Strobe Pulse Width */
+	tx4938_ndfmcptr->mcr = bsprt ? TX4938_NDFMCR_BSPRT : 0;
+	tx4938_ndfmcptr->spr = hold << 4 | spw;
+
+	/* Allocate memory for MTD device structure and private data */
+	tx4938ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
+				      GFP_KERNEL);
+	if (!tx4938ndfmc_mtd) {
+		printk ("Unable to allocate TX4938 NDFMC MTD device structure.\n");
+		return -ENOMEM;
+	}
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *) (&tx4938ndfmc_mtd[1]);
+
+	/* Initialize structures */
+	memset((char *) tx4938ndfmc_mtd, 0, sizeof(struct mtd_info));
+	memset((char *) this, 0, sizeof(struct nand_chip));
+
+	/* Link the private data with the MTD structure */
+	tx4938ndfmc_mtd->priv = this;
+
+	/* Set address of NAND IO lines */
+	this->IO_ADDR_R = (unsigned long)&tx4938_ndfmcptr->dtr;
+	this->IO_ADDR_W = (unsigned long)&tx4938_ndfmcptr->dtr;
+	this->hwcontrol = tx4938ndfmc_hwcontrol;
+	this->dev_ready = tx4938ndfmc_dev_ready;
+	this->calculate_ecc = tx4938ndfmc_calculate_ecc;
+	this->correct_data = nand_correct_data;
+	this->enable_hwecc = tx4938ndfmc_enable_hwecc;
+	this->eccmode = NAND_ECC_HW3_256;
+	this->chip_delay = 100;
+	this->read_byte = tx4938ndfmc_nand_read_byte;
+	this->write_byte = tx4938ndfmc_nand_write_byte;
+	this->cmdfunc = tx4938ndfmc_nand_command;
+	this->write_buf = tx4938ndfmc_nand_write_buf;
+	this->read_buf = tx4938ndfmc_nand_read_buf;
+	this->verify_buf = tx4938ndfmc_nand_verify_buf;
+
+	/* Scan to find existance of the device */
+	if (nand_scan (tx4938ndfmc_mtd, 1)) {
+		kfree (tx4938ndfmc_mtd);
+		return -ENXIO;
+	}
+
+	/* Allocate memory for internal data buffer */
+	this->data_buf = kmalloc (sizeof(u_char) * (tx4938ndfmc_mtd->oobblock + tx4938ndfmc_mtd->oobsize), GFP_KERNEL);
+	if (!this->data_buf) {
+		printk ("Unable to allocate NAND data buffer for TX4938.\n");
+		kfree (tx4938ndfmc_mtd);
+		return -ENOMEM;
+	}
+
+	if (protected) {
+		printk(KERN_INFO "TX4938 NDFMC: write protected.\n");
+		tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE);
+	}
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+	{
+		int mtd_parts_nb = 0;
+		struct mtd_partition *mtd_parts = 0;
+		mtd_parts_nb = parse_cmdline_partitions(tx4938ndfmc_mtd, &mtd_parts, "tx4938ndfmc");
+		if (mtd_parts_nb > 0)
+			add_mtd_partitions(tx4938ndfmc_mtd, mtd_parts, mtd_parts_nb);
+		else
+			add_mtd_device(tx4938ndfmc_mtd);
+	}
+#else
+	add_mtd_partitions(tx4938ndfmc_mtd, partition_info, NUM_PARTITIONS );
+#endif
+
+	return 0;
+}
+module_init(tx4938ndfmc_init);
+
+/*
+ * Clean up routine
+ */
+static void __exit tx4938ndfmc_cleanup (void)
+{
+	struct nand_chip *this = (struct nand_chip *) tx4938ndfmc_mtd->priv;
+
+	/* Unregister the device */
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+	del_mtd_partitions(tx4938ndfmc_mtd);
+#endif
+	del_mtd_device (tx4938ndfmc_mtd);
+
+	/* Free the MTD device structure */
+	kfree (tx4938ndfmc_mtd);
+
+	/* Free internal data buffer */
+	kfree (this->data_buf);
+}
+module_exit(tx4938ndfmc_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
+MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on TX4938 NDFMC");
diff -Nur linux-2.4.30/drivers/mtd/nftlcore.c linux-2.4.30-linux-mips.org/drivers/mtd/nftlcore.c
--- linux-2.4.30/drivers/mtd/nftlcore.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nftlcore.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,7 +1,7 @@
 /* Linux driver for NAND Flash Translation Layer      */
 /* (c) 1999 Machine Vision Holdings, Inc.             */
 /* Author: David Woodhouse <dwmw2@infradead.org>      */
-/* $Id: nftlcore.c,v 1.87 2002/09/13 14:35:33 dwmw2 Exp $ */
+/* $Id: nftlcore.c,v 1.94 2003/06/23 12:00:08 dwmw2 Exp $ */
 
 /*
   The contents of this file are distributed under the GNU General
@@ -23,15 +23,13 @@
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/init.h>
-#include <linux/blkpg.h>
+#include <linux/hdreg.h>
 
-#ifdef CONFIG_KMOD
 #include <linux/kmod.h>
-#endif
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/nftl.h>
-#include <linux/mtd/compatmac.h>
+#include <linux/mtd/blktrans.h>
 
 /* maximum number of loops while examining next block, to have a
    chance to detect consistency problems (they should never happen
@@ -39,189 +37,97 @@
 
 #define MAX_LOOPS 10000
 
-/* NFTL block device stuff */
-#define MAJOR_NR NFTL_MAJOR
-#define DEVICE_REQUEST nftl_request
-#define DEVICE_OFF(device)
-
-
-#include <linux/blk.h>
-#include <linux/hdreg.h>
-
-/* Linux-specific block device functions */
-
-/* I _HATE_ the Linux block device setup more than anything else I've ever
- *  encountered, except ...
- */
-
-static int nftl_sizes[256];
-static int nftl_blocksizes[256];
-
-/* .. for the Linux partition table handling. */
-struct hd_struct part_table[256];
-
-#if LINUX_VERSION_CODE < 0x20328
-static void dummy_init (struct gendisk *crap)
-{}
-#endif
-
-static struct gendisk nftl_gendisk = {
-	major:		MAJOR_NR,
-	major_name:	"nftl",
-	minor_shift:	NFTL_PARTN_BITS,	/* Bits to shift to get real from partition */
-	max_p:		(1<<NFTL_PARTN_BITS)-1,	/* Number of partitions per real */
-#if LINUX_VERSION_CODE < 0x20328
-	max_nr:		MAX_NFTLS,      /* maximum number of real */
-	init:		dummy_init,     /* init function */
-#endif
-	part:		part_table,     /* hd struct */
-	sizes:		nftl_sizes,     /* block sizes */
-};
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
-#else
-#define BLK_INC_USE_COUNT do {} while(0)
-#define BLK_DEC_USE_COUNT do {} while(0)
-#endif
-
-struct NFTLrecord *NFTLs[MAX_NFTLS];
-
-static void NFTL_setup(struct mtd_info *mtd)
+static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 {
-	int i;
 	struct NFTLrecord *nftl;
 	unsigned long temp;
-	int firstfree = -1;
 
-	DEBUG(MTD_DEBUG_LEVEL1,"NFTL_setup\n");
-
-	for (i = 0; i < MAX_NFTLS; i++) {
-		if (!NFTLs[i] && firstfree == -1)
-			firstfree = i;
-		else if (NFTLs[i] && NFTLs[i]->mtd == mtd) {
-			/* This is a Spare Media Header for an NFTL we've already found */
-			DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n");
-			return;
-		}
-	}
-        if (firstfree == -1) {
-		printk(KERN_WARNING "No more NFTL slot available\n");
+	if (mtd->ecctype != MTD_ECC_RS_DiskOnChip)
 		return;
-        }
+
+	DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name);
 
 	nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL);
+
 	if (!nftl) {
-		printk(KERN_WARNING "Out of memory for NFTL data structures\n");
+		printk(KERN_WARNING "NFTL: out of memory for data structures\n");
 		return;
 	}
+	memset(nftl, 0, sizeof(*nftl));
 
-	init_MUTEX(&nftl->mutex);
-
-	nftl->mtd = mtd;
+	nftl->mbd.mtd = mtd;
+	nftl->mbd.devnum = -1;
+	nftl->mbd.blksize = 512;
+	nftl->mbd.tr = tr;
 
         if (NFTL_mount(nftl) < 0) {
-		printk(KERN_WARNING "Could not mount NFTL device\n");
+		printk(KERN_WARNING "NFTL: could not mount device\n");
 		kfree(nftl);
 		return;
         }
 
 	/* OK, it's a new one. Set up all the data structures. */
-#ifdef PSYCHO_DEBUG
-	printk("Found new NFTL nftl%c\n", firstfree + 'a');
-#endif
 
-        /* linux stuff */
-	nftl->usecount = 0;
+	/* Calculate geometry */
 	nftl->cylinders = 1024;
 	nftl->heads = 16;
 
 	temp = nftl->cylinders * nftl->heads;
-	nftl->sectors = nftl->nr_sects / temp;
-	if (nftl->nr_sects % temp) {
+	nftl->sectors = nftl->mbd.size / temp;
+	if (nftl->mbd.size % temp) {
 		nftl->sectors++;
 		temp = nftl->cylinders * nftl->sectors;
-		nftl->heads = nftl->nr_sects / temp;
+		nftl->heads = nftl->mbd.size / temp;
 
-		if (nftl->nr_sects % temp) {
+		if (nftl->mbd.size % temp) {
 			nftl->heads++;
 			temp = nftl->heads * nftl->sectors;
-			nftl->cylinders = nftl->nr_sects / temp;
+			nftl->cylinders = nftl->mbd.size / temp;
 		}
 	}
 
-	if (nftl->nr_sects != nftl->heads * nftl->cylinders * nftl->sectors) {
-		printk(KERN_WARNING "Cannot calculate an NFTL geometry to "
-		       "match size of 0x%x.\n", nftl->nr_sects);
-		printk(KERN_WARNING "Using C:%d H:%d S:%d (== 0x%lx sects)\n", 
-		       nftl->cylinders, nftl->heads , nftl->sectors, 
-		       (long)nftl->cylinders * (long)nftl->heads * (long)nftl->sectors );
+	if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) {
+		/*
+		  Oh no we don't have 
+		   mbd.size == heads * cylinders * sectors
+		*/
+		printk(KERN_WARNING "NFTL: cannot calculate a geometry to "
+		       "match size of 0x%lx.\n", nftl->mbd.size);
+		printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d "
+			"(== 0x%lx sects)\n",
+			nftl->cylinders, nftl->heads , nftl->sectors, 
+			(long)nftl->cylinders * (long)nftl->heads *
+			(long)nftl->sectors );
+	}
 
-		/* Oh no we don't have nftl->nr_sects = nftl->heads * nftl->cylinders * nftl->sectors; */
+	if (add_mtd_blktrans_dev(&nftl->mbd)) {
+		if (nftl->ReplUnitTable)
+			kfree(nftl->ReplUnitTable);
+		if (nftl->EUNtable)
+			kfree(nftl->EUNtable);
+		kfree(nftl);
+		return;
 	}
-	NFTLs[firstfree] = nftl;
-	/* Finally, set up the block device sizes */
-	nftl_sizes[firstfree * 16] = nftl->nr_sects;
-	//nftl_blocksizes[firstfree*16] = 512;
-	part_table[firstfree * 16].nr_sects = nftl->nr_sects;
-
-	nftl_gendisk.nr_real++;
-
-	/* partition check ... */
-#if LINUX_VERSION_CODE < 0x20328
-	resetup_one_dev(&nftl_gendisk, firstfree);
-#else
-	grok_partitions(&nftl_gendisk, firstfree, 1<<NFTL_PARTN_BITS, nftl->nr_sects);
+#ifdef PSYCHO_DEBUG
+	printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
 #endif
 }
 
-static void NFTL_unsetup(int i)
+static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
 {
-	struct NFTLrecord *nftl = NFTLs[i];
+	struct NFTLrecord *nftl = (void *)dev;
 
-	DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i);
-	
-	NFTLs[i] = NULL;
-	
+	DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum);
+
+	del_mtd_blktrans_dev(dev);
 	if (nftl->ReplUnitTable)
 		kfree(nftl->ReplUnitTable);
 	if (nftl->EUNtable)
 		kfree(nftl->EUNtable);
-		      
-	nftl_gendisk.nr_real--;
 	kfree(nftl);
 }
 
-/* Search the MTD device for NFTL partitions */
-static void NFTL_notify_add(struct mtd_info *mtd)
-{
-	DEBUG(MTD_DEBUG_LEVEL1, "NFTL_notify_add for %s\n", mtd->name);
-
-	if (mtd) {
-		if (!mtd->read_oob) {
-			/* If this MTD doesn't have out-of-band data,
-			   then there's no point continuing */
-			DEBUG(MTD_DEBUG_LEVEL1, "No OOB data, quitting\n");
-			return;
-		}
-		DEBUG(MTD_DEBUG_LEVEL3, "mtd->read = %p, size = %d, erasesize = %d\n", 
-		      mtd->read, mtd->size, mtd->erasesize);
-
-                NFTL_setup(mtd);
-	}
-}
-
-static void NFTL_notify_remove(struct mtd_info *mtd)
-{
-	int i;
-
-	for (i = 0; i < MAX_NFTLS; i++) {
-		if (NFTLs[i] && NFTLs[i]->mtd == mtd)
-			NFTL_unsetup(i);
-	}
-}
-
 #ifdef CONFIG_NFTL_RW
 
 /* Actual NFTL access routines */
@@ -303,7 +209,7 @@
 
 		targetEUN = thisEUN;
 		for (block = 0; block < nftl->EraseSize / 512; block ++) {
-			MTD_READOOB(nftl->mtd,
+			MTD_READOOB(nftl->mbd.mtd,
 				    (thisEUN * nftl->EraseSize) + (block * 512),
 				    16 , &retlen, (char *)&oob);
 			if (block == 2) {
@@ -420,7 +326,7 @@
                chain by selecting the longer one */
             oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS);
             oob.u.c.unused = 0xffffffff;
-            MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, 
+            MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, 
                          8, &retlen, (char *)&oob.u);
         }
 
@@ -444,16 +350,16 @@
                 if (BlockMap[block] == BLOCK_NIL)
                         continue;
                 
-                ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
+                ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
 				  512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); 
                 if (ret < 0) {
-                    ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
+                    ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block])
                                       + (block * 512), 512, &retlen,
                                       movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); 
                     if (ret != -EIO) 
                         printk("Error went away on retry.\n");
                 }
-                MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512),
+                MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512),
                              512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
 	}
         
@@ -462,7 +368,7 @@
                 = cpu_to_le16(thisVUC);
         oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff;
         
-        MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8, 
+        MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 8, 
                      8, &retlen, (char *)&oob.u);
 
 	/* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */
@@ -582,7 +488,7 @@
 
 			lastEUN = writeEUN;
 
-			MTD_READOOB(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
+			MTD_READOOB(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
 				    8, &retlen, (char *)&bci);
 			
 			DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n",
@@ -670,12 +576,12 @@
 		nftl->ReplUnitTable[writeEUN] = BLOCK_NIL;
 
 		/* ... and on the flash itself */
-		MTD_READOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
+		MTD_READOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8,
 			    &retlen, (char *)&oob.u);
 
 		oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC);
 
-		MTD_WRITEOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8,
+		MTD_WRITEOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8,
                              &retlen, (char *)&oob.u);
 
                 /* we link the new block to the chain only after the
@@ -685,13 +591,13 @@
 			/* Both in our cache... */
 			nftl->ReplUnitTable[lastEUN] = writeEUN;
 			/* ... and on the flash itself */
-			MTD_READOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
+			MTD_READOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8,
 				    8, &retlen, (char *)&oob.u);
 
 			oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum
 				= cpu_to_le16(writeEUN);
 
-			MTD_WRITEOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8,
+			MTD_WRITEOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8,
 				     8, &retlen, (char *)&oob.u);
 		}
 
@@ -704,8 +610,10 @@
 	return 0xffff;
 }
 
-static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
+static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block,
+			   char *buffer)
 {
+	struct NFTLrecord *nftl = (void *)mbd;
 	u16 writeEUN;
 	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
 	size_t retlen;
@@ -720,7 +628,7 @@
 		return 1;
 	}
 
-	MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs,
+	MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
 		     512, &retlen, (char *)buffer, (char *)eccbuf, NAND_ECC_DISKONCHIP);
         /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */
 
@@ -728,8 +636,10 @@
 }
 #endif /* CONFIG_NFTL_RW */
 
-static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer)
+static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block,
+			  char *buffer)
 {
+	struct NFTLrecord *nftl = (void *)mbd;
 	u16 lastgoodEUN;
 	u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)];
 	unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1);
@@ -742,7 +652,7 @@
 
         if (thisEUN != BLOCK_NIL) {
 		while (thisEUN < nftl->nb_blocks) {
-			if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs,
+			if (MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + blockofs,
 					8, &retlen, (char *)&bci) < 0)
 				status = SECTOR_IGNORE;
 			else
@@ -761,13 +671,13 @@
 			case SECTOR_IGNORE:
 				break;
 			default:
-				printk("Unknown status for block %d in EUN %d: %x\n",
+				printk("Unknown status for block %ld in EUN %d: %x\n",
 				       block, thisEUN, status);
 				break;
 			}
 
 			if (!silly--) {
-				printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n",
+				printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n",
 				       block / (nftl->EraseSize / 512));
 				return 1;
 			}
@@ -783,264 +693,22 @@
 		loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs;
 		size_t retlen;
 		u_char eccbuf[6];
-		if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP))
+		if (MTD_READECC(nftl->mbd.mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP))
 			return -EIO;
 	}
 	return 0;
 }
 
-static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
-{
-	struct NFTLrecord *nftl;
-	int p;
-
-	nftl = NFTLs[MINOR(inode->i_rdev) >> NFTL_PARTN_BITS];
-
-	if (!nftl) return -EINVAL;
-
-	switch (cmd) {
-	case HDIO_GETGEO: {
-		struct hd_geometry g;
-
-		g.heads = nftl->heads;
-		g.sectors = nftl->sectors;
-		g.cylinders = nftl->cylinders;
-		g.start = part_table[MINOR(inode->i_rdev)].start_sect;
-		return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0;
-	}
-	case BLKGETSIZE:   /* Return device size */
-		return put_user(part_table[MINOR(inode->i_rdev)].nr_sects,
-                                (unsigned long *) arg);
-
-#ifdef BLKGETSIZE64
-	case BLKGETSIZE64:
-		return put_user((u64)part_table[MINOR(inode->i_rdev)].nr_sects << 9,
-                                (u64 *)arg);
-#endif
-
-	case BLKFLSBUF:
-		if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-		fsync_dev(inode->i_rdev);
-		invalidate_buffers(inode->i_rdev);
-		if (nftl->mtd->sync)
-			nftl->mtd->sync(nftl->mtd);
-		return 0;
-
-	case BLKRRPART:
-		if (!capable(CAP_SYS_ADMIN)) return -EACCES;
-		if (nftl->usecount > 1) return -EBUSY;
-		/* 
-		 * We have to flush all buffers and invalidate caches,
-		 * or we won't be able to re-use the partitions,
-		 * if there was a change and we don't want to reboot
-		 */
-		p = (1<<NFTL_PARTN_BITS) - 1;
-		while (p-- > 0) {
-			kdev_t devp = MKDEV(MAJOR(inode->i_dev), MINOR(inode->i_dev)+p);
-			if (part_table[p].nr_sects > 0)
-				invalidate_device (devp, 1);
-
-			part_table[MINOR(inode->i_dev)+p].start_sect = 0;
-			part_table[MINOR(inode->i_dev)+p].nr_sects = 0;
-		}
-		
-#if LINUX_VERSION_CODE < 0x20328
-		resetup_one_dev(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS);
-#else
-		grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS,
-				1<<NFTL_PARTN_BITS, nftl->nr_sects);
-#endif
-		return 0;
-
-#if (LINUX_VERSION_CODE < 0x20303)		
-	RO_IOCTLS(inode->i_rdev, arg);  /* ref. linux/blk.h */
-#else
-	case BLKROSET:
-	case BLKROGET:
-	case BLKSSZGET:
-		return blk_ioctl(inode->i_rdev, cmd, arg);
-#endif
-
-	default:
-		return -EINVAL;
-	}
-}
-
-void nftl_request(RQFUNC_ARG)
-{
-	unsigned int dev, block, nsect;
-	struct NFTLrecord *nftl;
-	char *buffer;
-	struct request *req;
-	int res;
-
-	while (1) {
-		INIT_REQUEST;	/* blk.h */
-		req = CURRENT;
-		
-		/* We can do this because the generic code knows not to
-		   touch the request at the head of the queue */
-		spin_unlock_irq(&io_request_lock);
-
-		DEBUG(MTD_DEBUG_LEVEL2, "NFTL_request\n");
-		DEBUG(MTD_DEBUG_LEVEL3, "NFTL %s request, from sector 0x%04lx for 0x%04lx sectors\n",
-		      (req->cmd == READ) ? "Read " : "Write",
-		      req->sector, req->current_nr_sectors);
-
-		dev = MINOR(req->rq_dev);
-		block = req->sector;
-		nsect = req->current_nr_sectors;
-		buffer = req->buffer;
-		res = 1; /* succeed */
-
-		if (dev >= MAX_NFTLS * (1<<NFTL_PARTN_BITS)) {
-			/* there is no such partition */
-			printk("nftl: bad minor number: device = %s\n",
-			       kdevname(req->rq_dev));
-			res = 0; /* fail */
-			goto repeat;
-		}
-		
-		nftl = NFTLs[dev / (1<<NFTL_PARTN_BITS)];
-		DEBUG(MTD_DEBUG_LEVEL3, "Waiting for mutex\n");
-		down(&nftl->mutex);
-		DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n");
-
-		if (block + nsect > part_table[dev].nr_sects) {
-			/* access past the end of device */
-			printk("nftl%c%d: bad access: block = %d, count = %d\n",
-			       (MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect);
-			up(&nftl->mutex);
-			res = 0; /* fail */
-			goto repeat;
-		}
-		
-		block += part_table[dev].start_sect;
-		
-		if (req->cmd == READ) {
-			DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request of 0x%x sectors @ %x "
-			      "(req->nr_sectors == %lx)\n", nsect, block, req->nr_sectors);
-	
-			for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
-				/* Read a single sector to req->buffer + (512 * i) */
-				if (NFTL_readblock(nftl, block, buffer)) {
-					DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request failed\n");
-					up(&nftl->mutex);
-					res = 0;
-					goto repeat;
-				}
-			}
-
-			DEBUG(MTD_DEBUG_LEVEL2,"NFTL read request completed OK\n");
-			up(&nftl->mutex);
-			goto repeat;
-		} else if (req->cmd == WRITE) {
-			DEBUG(MTD_DEBUG_LEVEL2, "NFTL write request of 0x%x sectors @ %x "
-			      "(req->nr_sectors == %lx)\n", nsect, block,
-			      req->nr_sectors);
-#ifdef CONFIG_NFTL_RW
-			for ( ; nsect > 0; nsect-- , block++, buffer += 512) {
-				/* Read a single sector to req->buffer + (512 * i) */
-				if (NFTL_writeblock(nftl, block, buffer)) {
-					DEBUG(MTD_DEBUG_LEVEL1,"NFTL write request failed\n");
-					up(&nftl->mutex);
-					res = 0;
-					goto repeat;
-				}
-			}
-			DEBUG(MTD_DEBUG_LEVEL2,"NFTL write request completed OK\n");
-#else
-			res = 0; /* Writes always fail */
-#endif /* CONFIG_NFTL_RW */
-			up(&nftl->mutex);
-			goto repeat;
-		} else {
-			DEBUG(MTD_DEBUG_LEVEL0, "NFTL unknown request\n");
-			up(&nftl->mutex);
-			res = 0;
-			goto repeat;
-		}
-	repeat: 
-		DEBUG(MTD_DEBUG_LEVEL3, "end_request(%d)\n", res);
-		spin_lock_irq(&io_request_lock);
-		end_request(res);
-	}
-}
-
-static int nftl_open(struct inode *ip, struct file *fp)
-{
-	int nftlnum = MINOR(ip->i_rdev) >> NFTL_PARTN_BITS;
-	struct NFTLrecord *thisNFTL;
-	thisNFTL = NFTLs[nftlnum];
-
-	DEBUG(MTD_DEBUG_LEVEL2,"NFTL_open\n");
-
-#ifdef CONFIG_KMOD
-	if (!thisNFTL && nftlnum == 0) {
-		request_module("docprobe");
-		thisNFTL = NFTLs[nftlnum];
-	}
-#endif
-	if (!thisNFTL) {
-		DEBUG(MTD_DEBUG_LEVEL2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n", 
-		      nftlnum, ip->i_rdev, ip, fp);
-		return -ENODEV;
-	}
-
-#ifndef CONFIG_NFTL_RW
-	if (fp->f_mode & FMODE_WRITE)
-		return -EROFS;
-#endif /* !CONFIG_NFTL_RW */
-
-	thisNFTL->usecount++;
-	BLK_INC_USE_COUNT;
-	if (!get_mtd_device(thisNFTL->mtd, -1)) {
-		BLK_DEC_USE_COUNT;
-		return -ENXIO;
-	}
-
-	return 0;
-}
-
-static int nftl_release(struct inode *inode, struct file *fp)
+static int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
 {
-	struct NFTLrecord *thisNFTL;
-
-	thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16];
-
-	DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n");
+	struct NFTLrecord *nftl = (void *)dev;
 
-	if (thisNFTL->mtd->sync)
-		thisNFTL->mtd->sync(thisNFTL->mtd);
-	thisNFTL->usecount--;
-	BLK_DEC_USE_COUNT;
-
-	put_mtd_device(thisNFTL->mtd);
+	geo->heads = nftl->heads;
+	geo->sectors = nftl->sectors;
+	geo->cylinders = nftl->cylinders;
 
 	return 0;
 }
-#if LINUX_VERSION_CODE < 0x20326
-static struct file_operations nftl_fops = {
-	read:		block_read,
-	write:		block_write,
-	ioctl:		nftl_ioctl,
-	open:		nftl_open,
-	release:	nftl_release,
-	fsync:		block_fsync,
-};
-#else
-static struct block_device_operations nftl_fops = 
-{
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14)
-	owner:		THIS_MODULE,
-#endif
-	open:		nftl_open,
-	release:	nftl_release,
-	ioctl: 		nftl_ioctl
-};
-#endif
-
-
 
 /****************************************************************************
  *
@@ -1048,49 +716,33 @@
  *
  ****************************************************************************/
 
-static struct mtd_notifier nftl_notifier = {
-	add:	NFTL_notify_add,
-	remove:	NFTL_notify_remove
+
+struct mtd_blktrans_ops nftl_tr = {
+	.name		= "nftl",
+	.major		= NFTL_MAJOR,
+	.part_bits	= NFTL_PARTN_BITS,
+	.getgeo		= nftl_getgeo,
+	.readsect	= nftl_readblock,
+#ifdef CONFIG_NFTL_RW
+	.writesect	= nftl_writeblock,
+#endif
+	.add_mtd	= nftl_add_mtd,
+	.remove_dev	= nftl_remove_dev,
+	.owner		= THIS_MODULE,
 };
 
 extern char nftlmountrev[];
 
 int __init init_nftl(void)
 {
-	int i;
-
-#ifdef PRERELEASE 
-	printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.87 $, nftlmount.c %s\n", nftlmountrev);
-#endif
-
-	if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){
-		printk("unable to register NFTL block device on major %d\n", MAJOR_NR);
-		return -EBUSY;
-	} else {
-		blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request);
+	printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.94 $, nftlmount.c %s\n", nftlmountrev);
 
-		/* set block size to 1kB each */
-		for (i = 0; i < 256; i++) {
-			nftl_blocksizes[i] = 1024;
-		}
-		blksize_size[MAJOR_NR] = nftl_blocksizes;
-
-		add_gendisk(&nftl_gendisk);
-	}
-	
-	register_mtd_user(&nftl_notifier);
-
-	return 0;
+	return register_mtd_blktrans(&nftl_tr);
 }
 
 static void __exit cleanup_nftl(void)
 {
-  	unregister_mtd_user(&nftl_notifier);
-  	unregister_blkdev(MAJOR_NR, "nftl");
-  	
-  	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
-
-	del_gendisk(&nftl_gendisk);
+	deregister_mtd_blktrans(&nftl_tr);
 }
 
 module_init(init_nftl);
diff -Nur linux-2.4.30/drivers/mtd/nftlmount.c linux-2.4.30-linux-mips.org/drivers/mtd/nftlmount.c
--- linux-2.4.30/drivers/mtd/nftlmount.c	2003-06-13 16:51:34.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/mtd/nftlmount.c	2005-05-12 18:01:29.000000000 +0200
@@ -4,7 +4,7 @@
  * Author: Fabrice Bellard (fabrice.bellard@netgem.com) 
  * Copyright (C) 2000 Netgem S.A.
  *
- * $Id: nftlmount.c,v 1.31 2002/11/15 16:34:43 dwmw2 Exp $
+ * $Id: nftlmount.c,v 1.34 2003/05/21 10:54:10 dwmw2 Exp $
  *
  * 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
@@ -21,26 +21,17 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-#define __NO_VERSION__
 #include <linux/kernel.h>
-#include <linux/module.h>
 #include <asm/errno.h>
-#include <asm/io.h>
-#include <asm/uaccess.h>
-#include <linux/miscdevice.h>
-#include <linux/pci.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
-#include <linux/sched.h>
-#include <linux/init.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
 #include <linux/mtd/nftl.h>
-#include <linux/mtd/compatmac.h>
 
 #define SECTORSIZE 512
 
-char nftlmountrev[]="$Revision: 1.31 $";
+char nftlmountrev[]="$Revision: 1.34 $";
 
 /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the
  *	various device information of the NFTL partition and Bad Unit Table. Update
@@ -59,8 +50,8 @@
 
         /* Assume logical EraseSize == physical erasesize for starting the scan. 
 	   We'll sort it out later if we find a MediaHeader which says otherwise */
-	nftl->EraseSize = nftl->mtd->erasesize;
-        nftl->nb_blocks = nftl->mtd->size / nftl->EraseSize;
+	nftl->EraseSize = nftl->mbd.mtd->erasesize;
+        nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize;
 
 	nftl->MediaUnit = BLOCK_NIL;
 	nftl->SpareMediaUnit = BLOCK_NIL;
@@ -71,12 +62,12 @@
 
 		/* Check for ANAND header first. Then can whinge if it's found but later
 		   checks fail */
-		if ((ret = MTD_READ(nftl->mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf))) {
+		if ((ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf))) {
 			static int warncount = 5;
 
 			if (warncount) {
 				printk(KERN_WARNING "Block read at 0x%x of mtd%d failed: %d\n",
-				       block * nftl->EraseSize, nftl->mtd->index, ret);
+				       block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
 				if (!--warncount)
 					printk(KERN_WARNING "Further failures for this block will not be printed\n");
 			}
@@ -87,16 +78,16 @@
 			/* ANAND\0 not found. Continue */
 #if 0
 			printk(KERN_DEBUG "ANAND header not found at 0x%x in mtd%d\n", 
-			       block * nftl->EraseSize, nftl->mtd->index);
+			       block * nftl->EraseSize, nftl->mbd.mtd->index);
 #endif			
 			continue;
 		}
 
 		/* To be safer with BIOS, also use erase mark as discriminant */
-		if ((ret = MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8,
-				8, &retlen, (char *)&h1)) < 0) {
+		if ((ret = MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8,
+				8, &retlen, (char *)&h1) < 0)) {
 			printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n",
-			       block * nftl->EraseSize, nftl->mtd->index, ret);
+			       block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
 			continue;
 		}
 
@@ -106,23 +97,23 @@
       */
 		if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) {
 			printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n",
-			       block * nftl->EraseSize, nftl->mtd->index, 
+			       block * nftl->EraseSize, nftl->mbd.mtd->index, 
 			       le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1));
 			continue;
 		}
 
 		/* Finally reread to check ECC */
-		if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize, SECTORSIZE,
-				&retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) {
+		if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE,
+				&retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP) < 0)) {
 			printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n",
-			       block * nftl->EraseSize, nftl->mtd->index, ret);
+			       block * nftl->EraseSize, nftl->mbd.mtd->index, ret);
 			continue;
 		}
 
 		/* Paranoia. Check the ANAND header is still there after the ECC read */
 		if (memcmp(buf, "ANAND", 6)) {
 			printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n",
-			       block * nftl->EraseSize, nftl->mtd->index);
+			       block * nftl->EraseSize, nftl->mbd.mtd->index);
 			printk(KERN_NOTICE "New data are: %02x %02x %02x %02x %02x %02x\n",
 			       buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]);
 			continue;
@@ -137,7 +128,11 @@
 				printk(KERN_NOTICE "NFTL Media Headers at 0x%x and 0x%x disagree.\n",
 				       nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize);
 				/* if (debug) Print both side by side */
-				return -1;
+				if (boot_record_count < 2) {
+					/* We haven't yet seen two real ones */
+					return -1;
+				}
+				continue;
 			}
 			if (boot_record_count == 1)
 				nftl->SpareMediaUnit = block;
@@ -163,8 +158,8 @@
 		} else if (mh->UnitSizeFactor != 0xff) {
 			printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n",
 			       mh->UnitSizeFactor);
-			nftl->EraseSize = nftl->mtd->erasesize << (0xff - mh->UnitSizeFactor);
-			nftl->nb_blocks = nftl->mtd->size / nftl->EraseSize;
+			nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor);
+			nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize;
 		}
 		nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
 		if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) {
@@ -182,7 +177,7 @@
 			return -1;
 		}
 		
-		nftl->nr_sects  = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
+		nftl->mbd.size  = nftl->numvunits * (nftl->EraseSize / SECTORSIZE);
 
 		/* If we're not using the last sectors in the device for some reason,
 		   reduce nb_blocks accordingly so we forget they're there */
@@ -220,7 +215,7 @@
 		for (i = 0; i < nftl->nb_blocks; i++) {
 			if ((i & (SECTORSIZE - 1)) == 0) {
 				/* read one sector for every SECTORSIZE of blocks */
-				if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize +
+				if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize +
 						       i + SECTORSIZE, SECTORSIZE, &retlen, buf,
 						       (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) {
 					printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
@@ -263,16 +258,16 @@
 	for (i = 0; i < len; i += SECTORSIZE) {
 		/* we want to read the sector without ECC check here since a free
 		   sector does not have ECC syndrome on it yet */
-		if (MTD_READ(nftl->mtd, address, SECTORSIZE, &retlen, buf) < 0)
+		if (MTD_READ(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0)
 			return -1;
 		if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
 			return -1;
 
 		if (check_oob) {
-			if (MTD_READOOB(nftl->mtd, address, nftl->mtd->oobsize,
+			if (MTD_READOOB(nftl->mbd.mtd, address, nftl->mbd.mtd->oobsize,
 					&retlen, buf) < 0)
 				return -1;
-			if (memcmpb(buf, 0xff, nftl->mtd->oobsize) != 0)
+			if (memcmpb(buf, 0xff, nftl->mbd.mtd->oobsize) != 0)
 				return -1;
 		}
 		address += SECTORSIZE;
@@ -297,7 +292,7 @@
 	struct erase_info *instr = &nftl->instr;
 
 	/* Read the Unit Control Information #1 for Wear-Leveling */
-	if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8,
+	if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8,
 			8, &retlen, (char *)&uci) < 0)
 		goto default_uci1;
 
@@ -314,7 +309,7 @@
 	/* XXX: use async erase interface, XXX: test return code */
 	instr->addr = block * nftl->EraseSize;
 	instr->len = nftl->EraseSize;
-	MTD_ERASE(nftl->mtd, instr);
+	MTD_ERASE(nftl->mbd.mtd, instr);
 
 	if (instr->state == MTD_ERASE_FAILED) {
 		/* could not format, FixMe: We should update the BadUnitTable 
@@ -337,7 +332,7 @@
 			return -1;
 
 		uci.WearInfo = le32_to_cpu(nb_erases);
-		if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
+		if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8,
 				 &retlen, (char *)&uci) < 0)
 			return -1;
 		return 0;
@@ -363,7 +358,7 @@
 	block = first_block;
 	for (;;) {
 		for (i = 0; i < sectors_per_block; i++) {
-			if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i * SECTORSIZE,
+			if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i * SECTORSIZE,
 					8, &retlen, (char *)&bci) < 0)
 				status = SECTOR_IGNORE;
 			else
@@ -383,7 +378,7 @@
 					/* sector not free actually : mark it as SECTOR_IGNORE  */
 					bci.Status = SECTOR_IGNORE;
 					bci.Status1 = SECTOR_IGNORE;
-					MTD_WRITEOOB(nftl->mtd,
+					MTD_WRITEOOB(nftl->mbd.mtd,
 						     block * nftl->EraseSize + i * SECTORSIZE,
 						     8, &retlen, (char *)&bci);
 				}
@@ -476,7 +471,7 @@
 	size_t retlen;
 
 	/* check erase mark. */
-	if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, 
+	if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, 
 			&retlen, (char *)&h1) < 0)
 		return -1;
 
@@ -491,7 +486,7 @@
 		h1.EraseMark = cpu_to_le16(ERASE_MARK);
 		h1.EraseMark1 = cpu_to_le16(ERASE_MARK);
 		h1.WearInfo = cpu_to_le32(0);
-		if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, 
+		if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, 
 				 &retlen, (char *)&h1) < 0)
 			return -1;
 	} else {
@@ -503,7 +498,7 @@
 						SECTORSIZE, 0) != 0)
 				return -1;
 
-			if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i,
+			if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i,
 					16, &retlen, buf) < 0)
 				return -1;
 			if (i == SECTORSIZE) {
@@ -533,7 +528,7 @@
 	struct nftl_uci2 uci;
 	size_t retlen;
 
-	if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
+	if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8,
 			8, &retlen, (char *)&uci) < 0)
 		return 0;
 
@@ -572,9 +567,9 @@
 
 			for (;;) {
 				/* read the block header. If error, we format the chain */
-				if (MTD_READOOB(s->mtd, block * s->EraseSize + 8, 8, 
+				if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, 8, 
 						&retlen, (char *)&h0) < 0 ||
-				    MTD_READOOB(s->mtd, block * s->EraseSize + SECTORSIZE + 8, 8, 
+				    MTD_READOOB(s->mbd.mtd, block * s->EraseSize + SECTORSIZE + 8, 8, 
 						&retlen, (char *)&h1) < 0) {
 					s->ReplUnitTable[block] = BLOCK_NIL;
 					do_format_chain = 1;
diff -Nur linux-2.4.30/drivers/mtd/redboot.c linux-2.4.30-linux-mips.org/drivers/mtd/redboot.c
--- linux-2.4.30/drivers/mtd/redboot.c	2001-11-09 23:01:22.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/mtd/redboot.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * $Id: redboot.c,v 1.6 2001/10/25 09:16:06 dwmw2 Exp $
+ * $Id: redboot.c,v 1.13 2004/04/01 10:17:40 gthomas Exp $
  *
  * Parse RedBoot-style Flash Image System (FIS) tables and
  * produce a Linux partition array to match.
@@ -7,6 +7,7 @@
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
+#include <linux/init.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
@@ -34,7 +35,9 @@
 	return 1;
 }
 
-int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts)
+static int parse_redboot_partitions(struct mtd_info *master, 
+                             struct mtd_partition **pparts,
+                             unsigned long fis_origin)
 {
 	int nrparts = 0;
 	struct fis_image_desc *buf;
@@ -43,21 +46,26 @@
 	int ret, i;
 	size_t retlen;
 	char *names;
+	char *nullname;
 	int namelen = 0;
+	int nulllen = 0;
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+	static char nullstring[] = "unallocated";
+#endif
 
-	buf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	buf = kmalloc(master->erasesize, GFP_KERNEL);
 
 	if (!buf)
 		return -ENOMEM;
 
 	/* Read the start of the last erase block */
 	ret = master->read(master, master->size - master->erasesize,
-			   PAGE_SIZE, &retlen, (void *)buf);
+			   master->erasesize, &retlen, (void *)buf);
 
 	if (ret)
 		goto out;
 
-	if (retlen != PAGE_SIZE) {
+	if (retlen != master->erasesize) {
 		ret = -EIO;
 		goto out;
 	}
@@ -75,7 +83,7 @@
 		goto out;
 	}
 
-	for (i = 0; i < PAGE_SIZE / sizeof(struct fis_image_desc); i++) {
+	for (i = 0; i < master->erasesize / sizeof(struct fis_image_desc); i++) {
 		struct fis_list *new_fl, **prev;
 
 		if (buf[i].name[0] == 0xff)
@@ -90,7 +98,11 @@
 			goto out;
 		}
 		new_fl->img = &buf[i];
-		buf[i].flash_base &= master->size-1;
+                if (fis_origin) {
+                        buf[i].flash_base -= fis_origin;
+                } else {
+                        buf[i].flash_base &= master->size-1;
+                }
 
 		/* I'm sure the JFFS2 code has done me permanent damage.
 		 * I now think the following is _normal_
@@ -103,42 +115,69 @@
 
 		nrparts++;
 	}
-	if (fl->img->flash_base)
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+	if (fl->img->flash_base) {
 		nrparts++;
+		nulllen = sizeof(nullstring);
+	}
 
 	for (tmp_fl = fl; tmp_fl->next; tmp_fl = tmp_fl->next) {
-		if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize < tmp_fl->next->img->flash_base)
+		if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize <= tmp_fl->next->img->flash_base) {
 			nrparts++;
+			nulllen = sizeof(nullstring);
+		}
 	}
-	parts = kmalloc(sizeof(*parts)*nrparts + namelen, GFP_KERNEL);
+#endif
+	parts = kmalloc(sizeof(*parts)*nrparts + nulllen + namelen, GFP_KERNEL);
 
 	if (!parts) {
 		ret = -ENOMEM;
 		goto out;
 	}
-	names = (char *)&parts[nrparts];
-	memset(parts, 0, sizeof(*parts)*nrparts + namelen);
+
+	memset(parts, 0, sizeof(*parts)*nrparts + nulllen + namelen);
+
+	nullname = (char *)&parts[nrparts];
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+	if (nulllen > 0) {
+		strcpy(nullname, nullstring);
+	}
+#endif
+	names = nullname + nulllen;
+
 	i=0;
 
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
 	if (fl->img->flash_base) {
-	       parts[0].name = "unallocated space";
+	       parts[0].name = nullname;
 	       parts[0].size = fl->img->flash_base;
 	       parts[0].offset = 0;
+		i++;
 	}
+#endif
 	for ( ; i<nrparts; i++) {
 		parts[i].size = fl->img->size;
 		parts[i].offset = fl->img->flash_base;
 		parts[i].name = names;
 
 		strcpy(names, fl->img->name);
+#ifdef CONFIG_MTD_REDBOOT_PARTS_READONLY
+		if (!memcmp(names, "RedBoot", 8) ||
+				!memcmp(names, "RedBoot config", 15) ||
+				!memcmp(names, "FIS directory", 14)) {
+			parts[i].mask_flags = MTD_WRITEABLE;
+		}
+#endif
 		names += strlen(names)+1;
 
-		if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize < fl->next->img->flash_base) {
+#ifdef CONFIG_MTD_REDBOOT_PARTS_UNALLOCATED
+		if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) {
 			i++;
 			parts[i].offset = parts[i-1].size + parts[i-1].offset;
 			parts[i].size = fl->next->img->flash_base - parts[i].offset;
-			parts[i].name = "unallocated space";
+			parts[i].name = nullname;
 		}
+#endif
 		tmp_fl = fl;
 		fl = fl->next;
 		kfree(tmp_fl);
@@ -155,7 +194,24 @@
 	return ret;
 }
 
-EXPORT_SYMBOL(parse_redboot_partitions);
+static struct mtd_part_parser redboot_parser = {
+	.owner = THIS_MODULE,
+	.parse_fn = parse_redboot_partitions,
+	.name = "RedBoot",
+};
+
+static int __init redboot_parser_init(void)
+{
+	return register_mtd_parser(&redboot_parser);
+}
+
+static void __exit redboot_parser_exit(void)
+{
+	deregister_mtd_parser(&redboot_parser);
+}
+
+module_init(redboot_parser_init);
+module_exit(redboot_parser_exit);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>");
diff -Nur linux-2.4.30/drivers/net/Config.in linux-2.4.30-linux-mips.org/drivers/net/Config.in
--- linux-2.4.30/drivers/net/Config.in	2005-01-19 15:09:56.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/net/Config.in	2005-05-12 18:01:30.000000000 +0200
@@ -70,6 +70,7 @@
       if [ "$CONFIG_MIPS_AU1X00_ENET" = "y" ]; then
          bool '   Broadcom 5222 Dual Phy Support' CONFIG_BCM5222_DUAL_PHY
       fi
+      tristate '  SMSC LAN91C111 over Au1xxx Static Bus support' CONFIG_AU1XXX_SMC91111
    fi
    if [ "$CONFIG_SGI_IP27" = "y" ]; then
       bool '  SGI IOC3 Ethernet' CONFIG_SGI_IOC3_ETH
diff -Nur linux-2.4.30/drivers/net/defxx.c linux-2.4.30-linux-mips.org/drivers/net/defxx.c
--- linux-2.4.30/drivers/net/defxx.c	2004-11-17 12:54:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/net/defxx.c	2005-05-12 17:57:26.000000000 +0200
@@ -10,24 +10,18 @@
  *
  * Abstract:
  *   A Linux device driver supporting the Digital Equipment Corporation
- *   FDDI EISA and PCI controller families.  Supported adapters include:
+ *   FDDI TURBOchannel, EISA and PCI controller families.  Supported
+ *   adapters include:
  *
- *		DEC FDDIcontroller/EISA (DEFEA)
- *		DEC FDDIcontroller/PCI  (DEFPA)
+ *		DEC FDDIcontroller/TURBOchannel (DEFTA)
+ *		DEC FDDIcontroller/EISA         (DEFEA)
+ *		DEC FDDIcontroller/PCI          (DEFPA)
  *
- * Maintainers:
- *   LVS	Lawrence V. Stefani
- *
- * Contact:
- *	 The author may be reached at:
+ * The original author:
+ *   LVS	Lawrence V. Stefani <lstefani@yahoo.com>
  *
- *		Inet: stefani@lkg.dec.com
- *		(NOTE! this address no longer works -jgarzik)
- *
- *		Mail: Digital Equipment Corporation
- *			  550 King Street
- *			  M/S: LKG1-3/M07
- *			  Littleton, MA  01460
+ * Maintainers:
+ *   macro	Maciej W. Rozycki <macro@linux-mips.org>
  *
  * Credits:
  *   I'd like to thank Patricia Cross for helping me get started with
@@ -197,16 +191,16 @@
  *		Sep 2000	tjeerd		Fix leak on unload, cosmetic code cleanup
  *		Feb 2001			Skb allocation fixes
  *		Feb 2001	davej		PCI enable cleanups.
+ *		04 Aug 2003	macro		Converted to the DMA API.
+ *		14 Aug 2004	macro		Fix device names reported.
+ *		26 Sep 2004	macro		TURBOchannel support.
  */
 
 /* Include files */
 
 #include <linux/module.h>
-
 #include <linux/kernel.h>
-#include <linux/sched.h>
 #include <linux/string.h>
-#include <linux/ptrace.h>
 #include <linux/errno.h>
 #include <linux/ioport.h>
 #include <linux/slab.h>
@@ -215,19 +209,33 @@
 #include <linux/delay.h>
 #include <linux/init.h>
 #include <linux/netdevice.h>
+#include <linux/fddidevice.h>
+#include <linux/skbuff.h>
+
 #include <asm/byteorder.h>
 #include <asm/bitops.h>
 #include <asm/io.h>
 
-#include <linux/fddidevice.h>
-#include <linux/skbuff.h>
+#ifdef CONFIG_TC
+#include <asm/dec/tc.h>
+#else
+static int search_tc_card(const char *name) { return -ENODEV; }
+static void claim_tc_card(int slot) { }
+static void release_tc_card(int slot) { }
+static unsigned long get_tc_base_addr(int slot) { return 0; }
+static unsigned long get_tc_irq_nr(int slot) { return -1; }
+#endif
 
 #include "defxx.h"
 
-/* Version information string - should be updated prior to each new release!!! */
+/* Version information string should be updated prior to each new release!  */
+#define DRV_NAME "defxx"
+#define DRV_VERSION "v1.07T"
+#define DRV_RELDATE "2004/09/26"
 
 static char version[] __devinitdata =
-	"defxx.c:v1.05e 2001/02/03  Lawrence V. Stefani and others\n";
+	DRV_NAME ": " DRV_VERSION " " DRV_RELDATE
+	"  Lawrence V. Stefani and others\n";
 
 #define DYNAMIC_BUFFERS 1
 
@@ -243,7 +251,7 @@
 static void		dfx_bus_init(struct net_device *dev);
 static void		dfx_bus_config_check(DFX_board_t *bp);
 
-static int		dfx_driver_init(struct net_device *dev);
+static int		dfx_driver_init(struct net_device *dev, const char *print_name);
 static int		dfx_adap_init(DFX_board_t *bp, int get_buffers);
 
 static int		dfx_open(struct net_device *dev);
@@ -337,48 +345,84 @@
 	int			offset,
 	u8			data
 	)
+{
+	if (bp->bus_type == DFX_BUS_TYPE_TC)
+	{
+	volatile u8 *addr = (void *)(bp->base_addr + offset);
 
+	*addr = data;
+	mb();
+	}
+	else
 	{
 	u16 port = bp->base_addr + offset;
 
 	outb(data, port);
 	}
+}
 
 static inline void dfx_port_read_byte(
 	DFX_board_t	*bp,
 	int			offset,
 	u8			*data
 	)
+{
+	if (bp->bus_type == DFX_BUS_TYPE_TC)
+	{
+	volatile u8 *addr = (void *)(bp->base_addr + offset);
 
+	mb();
+	*data = *addr;
+	}
+	else
 	{
 	u16 port = bp->base_addr + offset;
 
 	*data = inb(port);
 	}
+}
 
 static inline void dfx_port_write_long(
 	DFX_board_t	*bp,
 	int			offset,
 	u32			data
 	)
+{
+	if (bp->bus_type == DFX_BUS_TYPE_TC)
+	{
+	volatile u32 *addr = (void *)(bp->base_addr + offset);
 
+	*addr = data;
+	mb();
+	}
+	else
 	{
 	u16 port = bp->base_addr + offset;
 
 	outl(data, port);
 	}
+}
 
 static inline void dfx_port_read_long(
 	DFX_board_t	*bp,
 	int			offset,
 	u32			*data
 	)
+{
+	if (bp->bus_type == DFX_BUS_TYPE_TC)
+	{
+	volatile u32 *addr = (void *)(bp->base_addr + offset);
 
+	mb();
+	*data = *addr;
+	}
+	else
 	{
 	u16 port = bp->base_addr + offset;
 
 	*data = inl(port);
 	}
+}
 
 
 /*
@@ -393,8 +437,9 @@
  *   Condition code
  *       
  * Arguments:
- *   pdev - pointer to pci device information (NULL for EISA)
- *   ioaddr - pointer to port (NULL for PCI)
+ *   pdev - pointer to pci device information (NULL for EISA or TURBOchannel)
+ *   bus_type - bus type (one of DFX_BUS_TYPE_*)
+ *   handle - bus-specific data: slot (TC), pointer to port (EISA), NULL (PCI)
  *
  * Functional Description:
  *
@@ -410,54 +455,68 @@
  *   initialized and the board resources are read and stored in
  *   the device structure.
  */
-static int __devinit dfx_init_one_pci_or_eisa(struct pci_dev *pdev, long ioaddr)
+static int __devinit dfx_init_one_pci_or_eisa(struct pci_dev *pdev, u32 bus_type, long handle)
 {
+	static int version_disp;
+	char *print_name = DRV_NAME;
 	struct net_device *dev;
 	DFX_board_t	  *bp;			/* board pointer */
+	long ioaddr;				/* pointer to port */
+	unsigned long len;			/* resource length */
+	int alloc_size;				/* total buffer size used */
 	int err;
 
-#ifndef MODULE
-	static int version_disp;
-
-	if (!version_disp)	/* display version info if adapter is found */
-	{
+	if (!version_disp) {	/* display version info if adapter is found */
 		version_disp = 1;	/* set display flag to TRUE so that */
 		printk(version);	/* we only display this string ONCE */
 	}
-#endif
 
-	/*
-	 * init_fddidev() allocates a device structure with private data, clears the device structure and private data,
-	 * and  calls fddi_setup() and register_netdev(). Not much left to do for us here.
-	 */
-	dev = init_fddidev(NULL, sizeof(*bp));
+	if (pdev != NULL)
+		print_name = pdev->slot_name;
+
+	dev = alloc_fddidev(sizeof(*bp));
 	if (!dev) {
-		printk (KERN_ERR "defxx: unable to allocate fddidev, aborting\n");
+		printk(KERN_ERR "%s: unable to allocate fddidev, aborting\n",
+		       print_name);
 		return -ENOMEM;
 	}
 
 	/* Enable PCI device. */
-	if (pdev != NULL) {
+	if (bus_type == DFX_BUS_TYPE_PCI) {
 		err = pci_enable_device (pdev);
 		if (err) goto err_out;
 		ioaddr = pci_resource_start (pdev, 1);
 	}
 
 	SET_MODULE_OWNER(dev);
+	SET_NETDEV_DEV(dev, &pdev->dev);
 
 	bp = dev->priv;
 
-	if (!request_region (ioaddr, pdev ? PFI_K_CSR_IO_LEN : PI_ESIC_K_CSR_IO_LEN, dev->name)) {
-		printk (KERN_ERR "%s: Cannot reserve I/O resource 0x%x @ 0x%lx, aborting\n",
-			dev->name, PFI_K_CSR_IO_LEN, ioaddr);
+	if (bus_type == DFX_BUS_TYPE_TC) {
+		/* TURBOchannel board */
+		bp->slot = handle;
+		claim_tc_card(bp->slot);
+		ioaddr = get_tc_base_addr(handle) + PI_TC_K_CSR_OFFSET;
+		len = PI_TC_K_CSR_LEN;
+	} else if (bus_type == DFX_BUS_TYPE_EISA) {
+		/* EISA board */
+		ioaddr = handle;
+		len = PI_ESIC_K_CSR_IO_LEN;
+	} else
+		/* PCI board */
+		len = PFI_K_CSR_IO_LEN;
+	dev->base_addr			= ioaddr; /* save port (I/O) base address */
+
+	if (!request_region(ioaddr, len, print_name)) {
+		printk(KERN_ERR "%s: Cannot reserve I/O resource "
+		       "0x%lx @ 0x%lx, aborting\n", print_name, len, ioaddr);
 		err = -EBUSY;
 		goto err_out;
 	}
 
 	/* Initialize new device structure */
 
-	dev->base_addr			= ioaddr; /* save port (I/O) base address */
-
 	dev->get_stats			= dfx_ctl_get_stats;
 	dev->open			= dfx_open;
 	dev->stop			= dfx_close;
@@ -465,37 +524,54 @@
 	dev->set_multicast_list		= dfx_ctl_set_multicast_list;
 	dev->set_mac_address		= dfx_ctl_set_mac_address;
 
-	if (pdev == NULL) {
-		/* EISA board */
-		bp->bus_type = DFX_BUS_TYPE_EISA;
+	bp->bus_type = bus_type;
+	if (bus_type == DFX_BUS_TYPE_TC || bus_type == DFX_BUS_TYPE_EISA) {
+		/* TURBOchannel or EISA board */
 		bp->next = root_dfx_eisa_dev;
 		root_dfx_eisa_dev = dev;
 	} else {
 		/* PCI board */
-		bp->bus_type = DFX_BUS_TYPE_PCI;
 		bp->pci_dev = pdev;
 		pci_set_drvdata (pdev, dev);
 		pci_set_master (pdev);
 	}
 
-	if (dfx_driver_init(dev) != DFX_K_SUCCESS) {
+
+	if (dfx_driver_init(dev, print_name) != DFX_K_SUCCESS) {
 		err = -ENODEV;
 		goto err_out_region;
 	}
 
+	err = register_netdev(dev);
+	if (err)
+		goto err_out_kfree;
+
+	printk("%s: registered as %s\n", print_name, dev->name);
 	return 0;
 
+err_out_kfree:
+	alloc_size = sizeof(PI_DESCR_BLOCK) +
+		     PI_CMD_REQ_K_SIZE_MAX + PI_CMD_RSP_K_SIZE_MAX +
+#ifndef DYNAMIC_BUFFERS
+		     (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX) +
+#endif
+		     sizeof(PI_CONSUMER_BLOCK) +
+		     (PI_ALIGN_K_DESC_BLK - 1);
+	if (bp->kmalloced)
+		pci_free_consistent(pdev, alloc_size,
+				    bp->kmalloced, bp->kmalloced_dma);
 err_out_region:
-	release_region(ioaddr, pdev ? PFI_K_CSR_IO_LEN : PI_ESIC_K_CSR_IO_LEN);
+	release_region(ioaddr, len);
 err_out:
-	unregister_netdev(dev);
-	kfree(dev);
+	if (bp->bus_type == DFX_BUS_TYPE_TC)
+		release_tc_card(bp->slot);
+	free_netdev(dev);
 	return err;
 }
 
 static int __devinit dfx_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 {
-	return dfx_init_one_pci_or_eisa(pdev, 0);
+	return dfx_init_one_pci_or_eisa(pdev, DFX_BUS_TYPE_PCI, 0);
 }
 
 static int __init dfx_eisa_init(void)
@@ -507,6 +583,7 @@
 
 	DBG_printk("In dfx_eisa_init...\n");
 
+#ifdef CONFIG_EISA
 	/* Scan for FDDI EISA controllers */
 
 	for (i=0; i < DFX_MAX_EISA_SLOTS; i++)		/* only scan for up to 16 EISA slots */
@@ -517,9 +594,27 @@
 		{
 			port = (i << 12);					/* recalc base addr */
 
-			if (dfx_init_one_pci_or_eisa(NULL, port) == 0) rc = 0;
+			if (dfx_init_one_pci_or_eisa(NULL, DFX_BUS_TYPE_EISA, port) == 0) rc = 0;
 		}
 	}
+#endif
+	return rc;
+}
+
+static int __init dfx_tc_init(void)
+{
+	int rc = -ENODEV;
+	int slot;		/* TC slot number */
+
+	DBG_printk("In dfx_tc_init...\n");
+
+	/* Scan for FDDI TC controllers */
+	while ((slot = search_tc_card("PMAF-F")) >= 0) {
+		if (dfx_init_one_pci_or_eisa(NULL, DFX_BUS_TYPE_TC, slot) == 0)
+			rc = 0;
+		else
+			break;
+	}
 	return rc;
 }
 
@@ -583,8 +678,9 @@
 
 	/* Initialize adapter based on bus type */
 
-	if (bp->bus_type == DFX_BUS_TYPE_EISA)
-		{
+	if (bp->bus_type == DFX_BUS_TYPE_TC) {
+		dev->irq = get_tc_irq_nr(bp->slot);
+	} else if (bp->bus_type == DFX_BUS_TYPE_EISA) {
 		/* Get the interrupt level from the ESIC chip */
 
 		dfx_port_read_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, &val);
@@ -766,6 +862,7 @@
  *       
  * Arguments:
  *   dev - pointer to device information
+ *   print_name - printable device name
  *
  * Functional Description:
  *   This function allocates additional resources such as the host memory
@@ -780,20 +877,21 @@
  *						or read adapter MAC address
  *
  * Assumptions:
- *   Memory allocated from kmalloc() call is physically contiguous, locked
- *   memory whose physical address equals its virtual address.
+ *   Memory allocated from pci_alloc_consistent() call is physically
+ *   contiguous, locked memory.
  *
  * Side Effects:
  *   Adapter is reset and should be in DMA_UNAVAILABLE state before
  *   returning from this routine.
  */
 
-static int __devinit dfx_driver_init(struct net_device *dev)
+static int __devinit dfx_driver_init(struct net_device *dev,
+				     const char *print_name)
 {
 	DFX_board_t *bp = dev->priv;
 	int			alloc_size;			/* total buffer size needed */
 	char		*top_v, *curr_v;	/* virtual addrs into memory block */
-	u32			top_p, curr_p;		/* physical addrs into memory block */
+	dma_addr_t		top_p, curr_p;		/* physical addrs into memory block */
 	u32			data;				/* host data register value */
 
 	DBG_printk("In dfx_driver_init...\n");
@@ -837,26 +935,20 @@
 
 	/*  Read the factory MAC address from the adapter then save it */
 
-	if (dfx_hw_port_ctrl_req(bp,
-							PI_PCTRL_M_MLA,
-							PI_PDATA_A_MLA_K_LO,
-							0,
-							&data) != DFX_K_SUCCESS)
-		{
-		printk("%s: Could not read adapter factory MAC address!\n", dev->name);
+	if (dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_MLA, PI_PDATA_A_MLA_K_LO, 0,
+				 &data) != DFX_K_SUCCESS) {
+		printk("%s: Could not read adapter factory MAC address!\n",
+		       print_name);
 		return(DFX_K_FAILURE);
-		}
+	}
 	memcpy(&bp->factory_mac_addr[0], &data, sizeof(u32));
 
-	if (dfx_hw_port_ctrl_req(bp,
-							PI_PCTRL_M_MLA,
-							PI_PDATA_A_MLA_K_HI,
-							0,
-							&data) != DFX_K_SUCCESS)
-		{
-		printk("%s: Could not read adapter factory MAC address!\n", dev->name);
+	if (dfx_hw_port_ctrl_req(bp, PI_PCTRL_M_MLA, PI_PDATA_A_MLA_K_HI, 0,
+				 &data) != DFX_K_SUCCESS) {
+		printk("%s: Could not read adapter factory MAC address!\n",
+		       print_name);
 		return(DFX_K_FAILURE);
-		}
+	}
 	memcpy(&bp->factory_mac_addr[4], &data, sizeof(u16));
 
 	/*
@@ -867,28 +959,27 @@
 	 */
 
 	memcpy(dev->dev_addr, bp->factory_mac_addr, FDDI_K_ALEN);
-	if (bp->bus_type == DFX_BUS_TYPE_EISA)
-		printk("%s: DEFEA at I/O addr = 0x%lX, IRQ = %d, Hardware addr = %02X-%02X-%02X-%02X-%02X-%02X\n",
-				dev->name,
-				dev->base_addr,
-				dev->irq,
-				dev->dev_addr[0],
-				dev->dev_addr[1],
-				dev->dev_addr[2],
-				dev->dev_addr[3],
-				dev->dev_addr[4],
-				dev->dev_addr[5]);
+	if (bp->bus_type == DFX_BUS_TYPE_TC)
+		printk("%s: DEFTA at addr = 0x%lX, IRQ = %d, "
+		       "Hardware addr = %02X-%02X-%02X-%02X-%02X-%02X\n",
+		       print_name, dev->base_addr, dev->irq,
+		       dev->dev_addr[0], dev->dev_addr[1],
+		       dev->dev_addr[2], dev->dev_addr[3],
+		       dev->dev_addr[4], dev->dev_addr[5]);
+	else if (bp->bus_type == DFX_BUS_TYPE_EISA)
+		printk("%s: DEFEA at I/O addr = 0x%lX, IRQ = %d, "
+		       "Hardware addr = %02X-%02X-%02X-%02X-%02X-%02X\n",
+		       print_name, dev->base_addr, dev->irq,
+		       dev->dev_addr[0], dev->dev_addr[1],
+		       dev->dev_addr[2], dev->dev_addr[3],
+		       dev->dev_addr[4], dev->dev_addr[5]);
 	else
-		printk("%s: DEFPA at I/O addr = 0x%lX, IRQ = %d, Hardware addr = %02X-%02X-%02X-%02X-%02X-%02X\n",
-				dev->name,
-				dev->base_addr,
-				dev->irq,
-				dev->dev_addr[0],
-				dev->dev_addr[1],
-				dev->dev_addr[2],
-				dev->dev_addr[3],
-				dev->dev_addr[4],
-				dev->dev_addr[5]);
+		printk("%s: DEFPA at I/O addr = 0x%lX, IRQ = %d, "
+		       "Hardware addr = %02X-%02X-%02X-%02X-%02X-%02X\n",
+		       print_name, dev->base_addr, dev->irq,
+		       dev->dev_addr[0], dev->dev_addr[1],
+		       dev->dev_addr[2], dev->dev_addr[3],
+		       dev->dev_addr[4], dev->dev_addr[5]);
 
 	/*
 	 * Get memory for descriptor block, consumer block, and other buffers
@@ -903,14 +994,15 @@
 #endif
 					sizeof(PI_CONSUMER_BLOCK) +
 					(PI_ALIGN_K_DESC_BLK - 1);
-	bp->kmalloced = top_v = (char *) kmalloc(alloc_size, GFP_KERNEL);
-	if (top_v == NULL)
-		{
-		printk("%s: Could not allocate memory for host buffers and structures!\n", dev->name);
+	bp->kmalloced = top_v = pci_alloc_consistent(bp->pci_dev, alloc_size,
+						     &bp->kmalloced_dma);
+	if (top_v == NULL) {
+		printk("%s: Could not allocate memory for host buffers "
+		       "and structures!\n", print_name);
 		return(DFX_K_FAILURE);
-		}
+	}
 	memset(top_v, 0, alloc_size);	/* zero out memory before continuing */
-	top_p = virt_to_bus(top_v);		/* get physical address of buffer */
+	top_p = bp->kmalloced_dma;	/* get physical address of buffer */
 
 	/*
 	 *  To guarantee the 8K alignment required for the descriptor block, 8K - 1
@@ -924,7 +1016,7 @@
 	 *		  for allocating the needed memory.
 	 */
 
-	curr_p = (u32) (ALIGN(top_p, PI_ALIGN_K_DESC_BLK));
+	curr_p = ALIGN(top_p, PI_ALIGN_K_DESC_BLK);
 	curr_v = top_v + (curr_p - top_p);
 
 	/* Reserve space for descriptor block */
@@ -965,14 +1057,20 @@
 
 	/* Display virtual and physical addresses if debug driver */
 
-	DBG_printk("%s: Descriptor block virt = %0lX, phys = %0X\n",				dev->name, (long)bp->descr_block_virt,	bp->descr_block_phys);
-	DBG_printk("%s: Command Request buffer virt = %0lX, phys = %0X\n",			dev->name, (long)bp->cmd_req_virt,		bp->cmd_req_phys);
-	DBG_printk("%s: Command Response buffer virt = %0lX, phys = %0X\n",			dev->name, (long)bp->cmd_rsp_virt,		bp->cmd_rsp_phys);
-	DBG_printk("%s: Receive buffer block virt = %0lX, phys = %0X\n",			dev->name, (long)bp->rcv_block_virt,	bp->rcv_block_phys);
-	DBG_printk("%s: Consumer block virt = %0lX, phys = %0X\n",				dev->name, (long)bp->cons_block_virt,	bp->cons_block_phys);
+	DBG_printk("%s: Descriptor block virt = %0lX, phys = %0X\n",
+		   print_name,
+		   (long)bp->descr_block_virt, bp->descr_block_phys);
+	DBG_printk("%s: Command Request buffer virt = %0lX, phys = %0X\n",
+		   print_name, (long)bp->cmd_req_virt, bp->cmd_req_phys);
+	DBG_printk("%s: Command Response buffer virt = %0lX, phys = %0X\n",
+		   print_name, (long)bp->cmd_rsp_virt, bp->cmd_rsp_phys);
+	DBG_printk("%s: Receive buffer block virt = %0lX, phys = %0X\n",
+		   print_name, (long)bp->rcv_block_virt, bp->rcv_block_phys);
+	DBG_printk("%s: Consumer block virt = %0lX, phys = %0X\n",
+		   print_name, (long)bp->cons_block_virt, bp->cons_block_phys);
 
 	return(DFX_K_SUCCESS);
-	}
+}
 
 
 /*
@@ -1218,7 +1316,9 @@
 	
 	/* Register IRQ - support shared interrupts by passing device ptr */
 
-	ret = request_irq(dev->irq, (void *)dfx_interrupt, SA_SHIRQ, dev->name, dev);
+	ret = request_irq(dev->irq, (void *)dfx_interrupt,
+			  (bp->bus_type == DFX_BUS_TYPE_TC) ? 0 : SA_SHIRQ,
+			  dev->name, dev);
 	if (ret) {
 		printk(KERN_ERR "%s: Requested IRQ %d is busy\n", dev->name, dev->irq);
 		return ret;
@@ -1737,7 +1837,7 @@
 		dfx_port_write_long(bp, PFI_K_REG_MODE_CTRL,
 					(PFI_MODE_M_PDQ_INT_ENB + PFI_MODE_M_DMA_ENB));
 		}
-	else
+	else if (bp->bus_type == DFX_BUS_TYPE_EISA)
 		{
 		/* Disable interrupts at the ESIC */
 
@@ -1755,6 +1855,13 @@
 		tmp |= PI_CONFIG_STAT_0_M_INT_ENB;
 		dfx_port_write_byte(bp, PI_ESIC_K_IO_CONFIG_STAT_0, tmp);
 		}
+	else {
+		/* TC doesn't share interrupts so no need to disable them */
+
+		/* Call interrupt service routine for this adapter */
+
+		dfx_int_common(dev);
+	}
 
 	spin_unlock(&bp->lock);
 	}
@@ -2663,12 +2770,12 @@
  
 static void my_skb_align(struct sk_buff *skb, int n)
 {
-	u32 x=(u32)skb->data;	/* We only want the low bits .. */
-	u32 v;
+	unsigned long x = (unsigned long)skb->data;
+	unsigned long v;
 	
-	v=(x+n-1)&~(n-1);	/* Where we want to be */
+	v = ALIGN(x, n);	/* Where we want to be */
 	
-	skb_reserve(skb, v-x);
+	skb_reserve(skb, v - x);
 }
 
 
@@ -2745,7 +2852,10 @@
 			 */
 			 
 			my_skb_align(newskb, 128);
-			bp->descr_block_virt->rcv_data[i+j].long_1 = virt_to_bus(newskb->data);
+			bp->descr_block_virt->rcv_data[i + j].long_1 =
+				(u32)pci_map_single(bp->pci_dev, newskb->data,
+						    NEW_SKB_SIZE,
+						    PCI_DMA_FROMDEVICE);
 			/*
 			 * p_rcv_buff_va is only used inside the
 			 * kernel so we put the skb pointer here.
@@ -2859,9 +2969,17 @@
 						
 						my_skb_align(newskb, 128);
 						skb = (struct sk_buff *)bp->p_rcv_buff_va[entry];
+						pci_unmap_single(bp->pci_dev,
+							bp->descr_block_virt->rcv_data[entry].long_1,
+							NEW_SKB_SIZE,
+							PCI_DMA_FROMDEVICE);
 						skb_reserve(skb, RCV_BUFF_K_PADDING);
 						bp->p_rcv_buff_va[entry] = (char *)newskb;
-						bp->descr_block_virt->rcv_data[entry].long_1 = virt_to_bus(newskb->data);
+						bp->descr_block_virt->rcv_data[entry].long_1 =
+							(u32)pci_map_single(bp->pci_dev,
+								newskb->data,
+								NEW_SKB_SIZE,
+								PCI_DMA_FROMDEVICE);
 					} else
 						skb = NULL;
 				} else
@@ -2934,7 +3052,7 @@
  *   is contained in a single physically contiguous buffer
  *   in which the virtual address of the start of packet
  *   (skb->data) can be converted to a physical address
- *   by using virt_to_bus().
+ *   by using pci_map_single().
  *
  *   Since the adapter architecture requires a three byte
  *   packet request header to prepend the start of packet,
@@ -3082,12 +3200,13 @@
 	 *			skb->data.
 	 *		 6. The physical address of the start of packet
 	 *			can be determined from the virtual address
-	 *			by using virt_to_bus() and is only 32-bits
+	 *			by using pci_map_single() and is only 32-bits
 	 *			wide.
 	 */
 
 	p_xmt_descr->long_0	= (u32) (PI_XMT_DESCR_M_SOP | PI_XMT_DESCR_M_EOP | ((skb->len) << PI_XMT_DESCR_V_SEG_LEN));
-	p_xmt_descr->long_1 = (u32) virt_to_bus(skb->data);
+	p_xmt_descr->long_1 = (u32)pci_map_single(bp->pci_dev, skb->data,
+						  skb->len, PCI_DMA_TODEVICE);
 
 	/*
 	 * Verify that descriptor is actually available
@@ -3171,6 +3290,7 @@
 	{
 	XMT_DRIVER_DESCR	*p_xmt_drv_descr;	/* ptr to transmit driver descriptor */
 	PI_TYPE_2_CONSUMER	*p_type_2_cons;		/* ptr to rcv/xmt consumer block register */
+	u8			comp;			/* local transmit completion index */
 	int 			freed = 0;		/* buffers freed */
 
 	/* Service all consumed transmit frames */
@@ -3188,7 +3308,11 @@
 		bp->xmt_total_bytes += p_xmt_drv_descr->p_skb->len;
 
 		/* Return skb to operating system */
-
+		comp = bp->rcv_xmt_reg.index.xmt_comp;
+		pci_unmap_single(bp->pci_dev,
+				 bp->descr_block_virt->xmt_data[comp].long_1,
+				 p_xmt_drv_descr->p_skb->len,
+				 PCI_DMA_TODEVICE);
 		dev_kfree_skb_irq(p_xmt_drv_descr->p_skb);
 
 		/*
@@ -3297,6 +3421,7 @@
 	{
 	u32			prod_cons;		/* rcv/xmt consumer block longword */
 	XMT_DRIVER_DESCR	*p_xmt_drv_descr;	/* ptr to transmit driver descriptor */
+	u8			comp;			/* local transmit completion index */
 
 	/* Flush all outstanding transmit frames */
 
@@ -3307,7 +3432,11 @@
 		p_xmt_drv_descr = &(bp->xmt_drv_descr_blk[bp->rcv_xmt_reg.index.xmt_comp]);
 
 		/* Return skb to operating system */
-
+		comp = bp->rcv_xmt_reg.index.xmt_comp;
+		pci_unmap_single(bp->pci_dev,
+				 bp->descr_block_virt->xmt_data[comp].long_1,
+				 p_xmt_drv_descr->p_skb->len,
+				 PCI_DMA_TODEVICE);
 		dev_kfree_skb(p_xmt_drv_descr->p_skb);
 
 		/* Increment transmit error counter */
@@ -3337,12 +3466,36 @@
 
 static void __devexit dfx_remove_one_pci_or_eisa(struct pci_dev *pdev, struct net_device *dev)
 {
-	DFX_board_t	  *bp = dev->priv;
+	DFX_board_t	*bp = dev->priv;
+	unsigned long	len;			/* resource length */
+	int		alloc_size;		/* total buffer size used */
 
+	if (bp->bus_type == DFX_BUS_TYPE_TC) {
+		/* TURBOchannel board */
+		len = PI_TC_K_CSR_LEN;
+	} else if (bp->bus_type == DFX_BUS_TYPE_EISA) {
+		/* EISA board */
+		len = PI_ESIC_K_CSR_IO_LEN;
+	} else {
+		len = PFI_K_CSR_IO_LEN;
+	}
 	unregister_netdev(dev);
-	release_region(dev->base_addr,  pdev ? PFI_K_CSR_IO_LEN : PI_ESIC_K_CSR_IO_LEN );
-	if (bp->kmalloced) kfree(bp->kmalloced);
-	kfree(dev);
+	release_region(dev->base_addr, len);
+
+	if (bp->bus_type == DFX_BUS_TYPE_TC)
+		release_tc_card(bp->slot);
+
+	alloc_size = sizeof(PI_DESCR_BLOCK) +
+		     PI_CMD_REQ_K_SIZE_MAX + PI_CMD_RSP_K_SIZE_MAX +
+#ifndef DYNAMIC_BUFFERS
+		     (bp->rcv_bufs_to_post * PI_RCV_DATA_K_SIZE_MAX) +
+#endif
+		     sizeof(PI_CONSUMER_BLOCK) +
+		     (PI_ALIGN_K_DESC_BLK - 1);
+	if (bp->kmalloced)
+		pci_free_consistent(pdev, alloc_size, bp->kmalloced,
+				    bp->kmalloced_dma);
+	free_netdev(dev);
 }
 
 static void __devexit dfx_remove_one (struct pci_dev *pdev)
@@ -3353,21 +3506,22 @@
 	pci_set_drvdata(pdev, NULL);
 }
 
-static struct pci_device_id dfx_pci_tbl[] __devinitdata = {
+static struct pci_device_id dfx_pci_tbl[] = {
 	{ PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_FDDI, PCI_ANY_ID, PCI_ANY_ID, },
 	{ 0, }
 };
 MODULE_DEVICE_TABLE(pci, dfx_pci_tbl);
 
 static struct pci_driver dfx_driver = {
-	name:		"defxx",
-	probe:		dfx_init_one,
-	remove:		__devexit_p(dfx_remove_one),
-	id_table:	dfx_pci_tbl,
+	.name		= "defxx",
+	.probe		= dfx_init_one,
+	.remove		= __devexit_p(dfx_remove_one),
+	.id_table	= dfx_pci_tbl,
 };
 
 static int dfx_have_pci;
 static int dfx_have_eisa;
+static int dfx_have_tc;
 
 
 static void __exit dfx_eisa_cleanup(void)
@@ -3388,12 +3542,7 @@
 
 static int __init dfx_init(void)
 {
-	int rc_pci, rc_eisa;
-
-/* when a module, this is printed whether or not devices are found in probe */
-#ifdef MODULE
-	printk(version);
-#endif
+	int rc_pci, rc_eisa, rc_tc;
 
 	rc_pci = pci_module_init(&dfx_driver);
 	if (rc_pci >= 0) dfx_have_pci = 1;
@@ -3401,20 +3550,27 @@
 	rc_eisa = dfx_eisa_init();
 	if (rc_eisa >= 0) dfx_have_eisa = 1;
 
-	return ((rc_eisa < 0) ? 0 : rc_eisa)  + ((rc_pci < 0) ? 0 : rc_pci); 
+	rc_tc = dfx_tc_init();
+	if (rc_tc >= 0) dfx_have_tc = 1;
+
+	return ((rc_tc < 0) ? 0 : rc_tc) +
+	       ((rc_eisa < 0) ? 0 : rc_eisa) +
+	       ((rc_pci < 0) ? 0 : rc_pci); 
 }
 
 static void __exit dfx_cleanup(void)
 {
 	if (dfx_have_pci)
 		pci_unregister_driver(&dfx_driver);
-	if (dfx_have_eisa)
+	if (dfx_have_eisa || dfx_have_tc)
 		dfx_eisa_cleanup();
-		
 }	
 
 module_init(dfx_init);
 module_exit(dfx_cleanup);
+MODULE_AUTHOR("Lawrence V. Stefani");
+MODULE_DESCRIPTION("DEC FDDIcontroller EISA/PCI (DEFEA/DEFPA) driver "
+		   DRV_VERSION " " DRV_RELDATE);
 MODULE_LICENSE("GPL");
 
 
diff -Nur linux-2.4.30/drivers/net/defxx.h linux-2.4.30-linux-mips.org/drivers/net/defxx.h
--- linux-2.4.30/drivers/net/defxx.h	2001-02-13 22:15:05.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/net/defxx.h	2005-05-12 17:57:26.000000000 +0200
@@ -12,17 +12,11 @@
  *   Contains all definitions specified by port specification and required
  *   by the defxx.c driver.
  *
- * Maintainers:
- *   LVS	Lawrence V. Stefani
- *
- * Contact:
- *	 The author may be reached at:
+ * The original author:
+ *   LVS	Lawrence V. Stefani <lstefani@yahoo.com>
  *
- *		Inet: stefani@lkg.dec.com
- *		Mail: Digital Equipment Corporation
- *			  550 King Street
- *			  M/S: LKG1-3/M07
- *			  Littleton, MA  01460
+ * Maintainers:
+ *   macro	Maciej W. Rozycki <macro@linux-mips.org>
  *
  * Modification History:
  *		Date		Name	Description
@@ -30,6 +24,7 @@
  *		09-Sep-96	LVS		Added group_prom field.  Moved read/write I/O
  *							macros to DEFXX.C.
  *		12-Sep-96	LVS		Removed packet request header pointers.
+ *		04 Aug 2003	macro		Converted to the DMA API.
  */
 
 #ifndef _DEFXX_H_
@@ -1467,6 +1462,11 @@
 
 #endif	/* #ifndef BIG_ENDIAN */
 
+/* Define TC PDQ CSR offset and length */
+
+#define PI_TC_K_CSR_OFFSET		0x100000
+#define PI_TC_K_CSR_LEN			0x80		/* 128 bytes */
+
 /* Define EISA controller register offsets */
 
 #define PI_ESIC_K_BURST_HOLDOFF		0x040
@@ -1634,6 +1634,7 @@
 
 #define DFX_BUS_TYPE_PCI		0			/* type code for DEC FDDIcontroller/PCI */
 #define DFX_BUS_TYPE_EISA		1			/* type code for DEC FDDIcontroller/EISA */
+#define DFX_BUS_TYPE_TC			2			/* type code for DEC FDDIcontroller/TURBOchannel */
 
 #define DFX_FC_PRH2_PRH1_PRH0		0x54003820	/* Packet Request Header bytes + FC */
 #define DFX_PRH0_BYTE			0x20		/* Packet Request Header byte 0 */
@@ -1704,17 +1705,19 @@
 	{
 	/* Keep virtual and physical pointers to locked, physically contiguous memory */
 
-	char				*kmalloced;					/* kfree this on unload */ 
+	char				*kmalloced;					/* pci_free_consistent this on unload */ 
+	dma_addr_t			kmalloced_dma;
+	/* DMA handle for the above */
 	PI_DESCR_BLOCK			*descr_block_virt;				/* PDQ descriptor block virt address */
-	u32				descr_block_phys;				/* PDQ descriptor block phys address */
+	dma_addr_t			descr_block_phys;				/* PDQ descriptor block phys address */
 	PI_DMA_CMD_REQ			*cmd_req_virt;					/* Command request buffer virt address */
-	u32				cmd_req_phys;					/* Command request buffer phys address */
+	dma_addr_t			cmd_req_phys;					/* Command request buffer phys address */
 	PI_DMA_CMD_RSP			*cmd_rsp_virt;					/* Command response buffer virt address */
-	u32				cmd_rsp_phys;					/* Command response buffer phys address */
+	dma_addr_t			cmd_rsp_phys;					/* Command response buffer phys address */
 	char				*rcv_block_virt;				/* LLC host receive queue buf blk virt */
-	u32				rcv_block_phys;					/* LLC host receive queue buf blk phys */
+	dma_addr_t			rcv_block_phys;					/* LLC host receive queue buf blk phys */
 	PI_CONSUMER_BLOCK		*cons_block_virt;				/* PDQ consumer block virt address */
-	u32				cons_block_phys;				/* PDQ consumer block phys address */
+	dma_addr_t			cons_block_phys;				/* PDQ consumer block phys address */
 
 	/* Keep local copies of Type 1 and Type 2 register data */
 
@@ -1758,8 +1761,9 @@
 
 	struct net_device		*dev;						/* pointer to device structure */
 	struct net_device		*next;
-	u32				bus_type;					/* bus type (0 == PCI, 1 == EISA) */
-	u16				base_addr;					/* base I/O address (same as dev->base_addr) */
+	u32				bus_type;					/* bus type (0 == PCI, 1 == EISA, 2 == TC) */
+	long				base_addr;					/* base I/O address (same as dev->base_addr) */
+	int				slot;						/* TC slot number */
 	struct pci_dev *		pci_dev;
 	u32				full_duplex_enb;				/* FDDI Full Duplex enable (1 == on, 2 == off) */
 	u32				req_ttrt;					/* requested TTRT value (in 80ns units) */
diff -Nur linux-2.4.30/drivers/net/hamradio/hdlcdrv.c linux-2.4.30-linux-mips.org/drivers/net/hamradio/hdlcdrv.c
--- linux-2.4.30/drivers/net/hamradio/hdlcdrv.c	2002-02-25 20:37:59.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/net/hamradio/hdlcdrv.c	2005-05-12 17:57:27.000000000 +0200
@@ -587,6 +587,8 @@
 		return -EINVAL;
 	s = (struct hdlcdrv_state *)dev->priv;
 
+	netif_stop_queue(dev);
+
 	if (s->ops && s->ops->close)
 		i = s->ops->close(dev);
 	if (s->skb)
diff -Nur linux-2.4.30/drivers/net/irda/au1k_ir.c linux-2.4.30-linux-mips.org/drivers/net/irda/au1k_ir.c
--- linux-2.4.30/drivers/net/irda/au1k_ir.c	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/net/irda/au1k_ir.c	2005-05-12 17:57:27.000000000 +0200
@@ -81,10 +81,6 @@
 
 #define RUN_AT(x) (jiffies + (x))
 
-#if defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100)
-static BCSR * const bcsr = (BCSR *)0xAE000000;
-#endif
-
 static spinlock_t ir_lock = SPIN_LOCK_UNLOCKED;
 
 /*
diff -Nur linux-2.4.30/drivers/net/Makefile linux-2.4.30-linux-mips.org/drivers/net/Makefile
--- linux-2.4.30/drivers/net/Makefile	2005-01-19 15:09:56.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/net/Makefile	2005-05-12 18:01:30.000000000 +0200
@@ -139,6 +139,7 @@
 obj-$(CONFIG_SK_G16) += sk_g16.o
 obj-$(CONFIG_HP100) += hp100.o
 obj-$(CONFIG_SMC9194) += smc9194.o
+obj-$(CONFIG_AU1XXX_SMC91111) += smc91111.o
 obj-$(CONFIG_ARM_AM79C961A) += am79c961a.o
 obj-$(CONFIG_ARM_ETHERH) += 8390.o
 obj-$(CONFIG_WD80x3) += wd.o 8390.o
diff -Nur linux-2.4.30/drivers/net/smc91111.c linux-2.4.30-linux-mips.org/drivers/net/smc91111.c
--- linux-2.4.30/drivers/net/smc91111.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/net/smc91111.c	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,3992 @@
+/*------------------------------------------------------------------------
+ . smc91111.c
+ . This is a driver for SMSC's 91C111 single-chip Ethernet device.
+ .
+ . Copyright (C) 2001-2004 Standard Microsystems Corporation (SMSC)
+ .       Developed by Simple Network Magic Corporation (SNMC)
+ . Copyright (C) 1996 by Erik Stahlman (ES)
+ .
+ . This program is free software; you can redistribute it and/or modify
+ . it under the terms of the GNU General Public License as published by
+ . the Free Software Foundation; either version 2 of the License, or
+ . (at your option) any later version.
+ .
+ . This program is distributed in the hope that it will be useful,
+ . but WITHOUT ANY WARRANTY; without even the implied warranty of
+ . MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ . GNU General Public License for more details.
+ .
+ . You should have received a copy of the GNU General Public License
+ . along with this program; if not, write to the Free Software
+ . Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ .
+ . Information contained in this file was obtained from the LAN91C111
+ . manual from SMC.  To get a copy, if you really want one, you can find 
+ . information under www.smsc.com.
+ . 
+ .
+ . "Features" of the SMC chip:
+ .   Integrated PHY/MAC for 10/100BaseT Operation
+ .   Supports internal and external MII
+ .   Integrated 8K packet memory
+ .   EEPROM interface for configuration
+ .
+ . Arguments:
+ . 	io	= for the base address
+ .	irq	= for the IRQ
+ .	nowait	= 0 for normal wait states, 1 eliminates additional wait states
+ .
+ . author:
+ . 	Erik Stahlman				( erik@vt.edu )
+ . 	Daris A Nevil				( dnevil@snmc.com )
+ .  	Pramod B Bhardwaj   			(pramod.bhardwaj@smsc.com)
+ .
+ .
+ . Hardware multicast code from Peter Cammaert ( pc@denkart.be )
+ .
+ . Sources:
+ .    o   SMSC LAN91C111 databook (www.smsc.com)
+ .    o   smc9194.c by Erik Stahlman
+ .    o   skeleton.c by Donald Becker ( becker@cesdis.gsfc.nasa.gov )
+ .
+ . History:
+ .    12/13/04  Steven Chew, Added code to reset PHY upon link down
+ .    09/24/01  Pramod B Bhardwaj, Added the changes for Kernel 2.4
+ .    08/21/01  Pramod B Bhardwaj Added support for RevB of LAN91C111
+ .	04/25/01  Daris A Nevil  Initial public release through SMSC
+ .	03/16/01  Daris A Nevil  Modified smc9194.c for use with LAN91C111
+ ----------------------------------------------------------------------------*/
+
+// Use power-down feature of the chip
+#define POWER_DOWN	1
+
+
+static const char version[] =
+"SMSC LAN91C111 Driver (v2.01), (Linux Kernel 2.4 + Support for Odd Byte) 12/13/04\n\n";
+
+#include <linux/module.h>
+#ifdef MODULE
+#include <linux/version.h>
+#endif
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/init.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+//#include <linux/kcomp.h>
+
+#ifdef CONFIG_SYSCTL
+#include <linux/proc_fs.h>
+#include <linux/sysctl.h>
+#endif
+
+#if defined(CONFIG_SOC_AU1X00)
+#define SMC_DEBUG 1 // Must be defined in makefile
+
+#ifdef CONFIG_MIPS_PB1550
+#include <asm/pb1550.h>
+#endif
+#ifdef CONFIG_MIPS_PB1200
+#include <asm/pb1200.h>
+#endif
+#ifdef CONFIG_MIPS_DB1200
+#include <asm/db1200.h>
+#endif
+#ifdef CONFIG_MIPS_FICMMP
+#include <asm/ficmmp.h>
+#endif
+
+#include <asm/au1000.h>
+#undef inb
+#undef inw
+#undef outb
+#undef outw
+
+#define inb(x)		au_readb(x)
+#define inw(x)		au_readw(x)
+#define outb(d,x)	au_writeb(d,x)
+#define outw(d,x)	au_writew(d,x)
+
+/* Can't use io.h version since it inlines incorrect inw() and outw() */
+#undef insw
+static inline void insw(unsigned long port, void *addr, unsigned int count)
+{
+    while (count--) {
+        *(u16 *)addr = inw(port);
+        addr += 2;
+    }
+}
+
+#undef outsw
+static inline void outsw(unsigned long port, void *addr, unsigned int count)
+{
+    while (count--) {
+        outw(*(u16 *)addr, port);
+        addr += 2;
+    }
+}
+
+/* extern functions */
+extern int get_ethernet_addr(char *ethenet_addr);
+
+/* Compiled-in defaults can be over-ridden at boot-time by board setup code */
+uint32_t au1xxx_smc91111_base = KSEG1ADDR(AU1XXX_SMC91111_PHYS_ADDR);
+int au1xxx_smc91111_irq = AU1XXX_SMC91111_IRQ;
+int au1xxx_smc91111_nowait = 1;
+
+#define RPC_DEFAULT (RPC_ANEG | (RPC_LED_100_10 << RPC_LSXA_SHFT) | (RPC_LED_TX_RX << RPC_LSXB_SHFT))
+#endif /* end CONFIG_SOC_AU1X00 */
+
+#include "smc91111.h"
+/*------------------------------------------------------------------------
+ .
+ . Configuration options, for the experienced user to change.
+ .
+ -------------------------------------------------------------------------*/
+
+/*
+ . Do you want to use 32 bit xfers?  This should work on all chips, as
+ . the chipset is designed to accommodate them.
+*/
+#if !defined(CONFIG_SOC_AU1X00)
+#define USE_32_BIT 1
+#endif
+
+
+/*
+ .the LAN91C111 can be at any of the following port addresses.  To change,
+ .for a slightly different card, you can add it to the array.  Keep in
+ .mind that the array must end in zero.
+*/
+static unsigned int smc_portlist[] __initdata =
+   { 0x200, 0x220, 0x240, 0x260, 0x280, 0x2A0, 0x2C0, 0x2E0,
+	 0x300, 0x320, 0x340, 0x360, 0x380, 0x3A0, 0x3C0, 0x3E0, 0};
+
+
+/*
+ . Wait time for memory to be free.  This probably shouldn't be
+ . tuned that much, as waiting for this means nothing else happens
+ . in the system
+*/
+#define MEMORY_WAIT_TIME 16
+
+/*
+ . DEBUGGING LEVELS
+ .
+ . 0 for normal operation
+ . 1 for slightly more details
+ . >2 for various levels of increasingly useless information
+ .    2 for interrupt tracking, status flags
+ .    3 for packet info
+ .    4 for complete packet dumps
+*/
+//#define SMC_DEBUG 3 // Must be defined in makefile
+
+#if (SMC_DEBUG > 2 )
+#define PRINTK3(args...) printk(args)
+#else
+#define PRINTK3(args...)
+#endif
+
+#if SMC_DEBUG > 1
+#define PRINTK2(args...) printk(args)
+#else
+#define PRINTK2(args...)
+#endif
+
+#ifdef SMC_DEBUG
+#define PRINTK(args...) printk(args)
+#else
+#define PRINTK(args...)
+#endif
+
+
+/*------------------------------------------------------------------------
+ .
+ . The internal workings of the driver.  If you are changing anything
+ . here with the SMC stuff, you should have the datasheet and know
+ . what you are doing.
+ .
+ -------------------------------------------------------------------------*/
+#define CARDNAME "LAN91C111"
+
+// Memory sizing constant
+#define LAN91C111_MEMORY_MULTIPLIER	(1024*2)
+
+/* store this information for the driver.. */
+struct smc_local {
+
+ 	// these are things that the kernel wants me to keep, so users
+	// can find out semi-useless statistics of how well the card is
+	// performing
+	struct net_device_stats stats;
+
+	// If I have to wait until memory is available to send
+	// a packet, I will store the skbuff here, until I get the
+	// desired memory.  Then, I'll send it out and free it.
+	struct sk_buff * saved_skb;
+
+ 	// This keeps track of how many packets that I have
+ 	// sent out.  When an TX_EMPTY interrupt comes, I know
+	// that all of these have been sent.
+	int	packets_waiting;
+
+	// Set to true during the auto-negotiation sequence
+	int	autoneg_active;
+
+	// Address of our PHY port
+	word	phyaddr;
+
+	// Type of PHY
+	word	phytype;
+
+	// Last contents of PHY Register 18
+	word	lastPhy18;
+
+	// Contains the current active transmission mode
+	word	tcr_cur_mode;
+
+	// Contains the current active receive mode
+	word	rcr_cur_mode;
+
+	// Contains the current active receive/phy mode
+	word	rpc_cur_mode;
+
+	/* => Pramod, Odd Byte issue */
+	// Contains the Current ChipID
+	unsigned short ChipID;
+
+	//Contains the Current ChipRevision
+	unsigned short ChipRev;
+	/* <= Pramod, Odd Byte issue */
+
+
+#ifdef CONFIG_SYSCTL
+
+	// Root directory /proc/sys/dev
+	// Second entry must be null to terminate the table
+	ctl_table root_table[2];
+
+	// Directory for this device /proc/sys/dev/ethX
+	// Again the second entry must be zero to terminate
+	ctl_table eth_table[2];
+
+	// This is the parameters (file) table
+	ctl_table param_table[CTL_SMC_LAST_ENTRY];
+
+	// Saves the sysctl header returned by register_sysctl_table()
+	// we send this to unregister_sysctl_table()
+	struct ctl_table_header *sysctl_header;
+
+	// Parameter variables (files) go here
+	char ctl_info[1024];
+	int ctl_swfdup;
+	int ctl_ephloop;
+	int ctl_miiop;
+	int ctl_autoneg;
+	int ctl_rfduplx;
+	int ctl_rspeed;
+	int ctl_afduplx;
+	int ctl_aspeed;
+	int ctl_lnkfail;
+	int ctl_forcol;
+	int ctl_filtcar;
+	int ctl_freemem;
+	int ctl_totmem;
+	int ctl_leda;
+	int ctl_ledb;
+	int ctl_chiprev;
+#ifdef SMC_DEBUG
+	int ctl_reg_bsr;
+	int ctl_reg_tcr;
+	int ctl_reg_esr;
+	int ctl_reg_rcr;
+	int ctl_reg_ctrr;
+	int ctl_reg_mir;
+	int ctl_reg_rpcr;
+	int ctl_reg_cfgr;
+	int ctl_reg_bar;
+	int ctl_reg_iar0;
+	int ctl_reg_iar1;
+	int ctl_reg_iar2;
+	int ctl_reg_gpr;
+	int ctl_reg_ctlr;
+	int ctl_reg_mcr;
+	int ctl_reg_pnr;
+	int ctl_reg_fpr;
+	int ctl_reg_ptr;
+	int ctl_reg_dr;
+	int ctl_reg_isr;
+	int ctl_reg_mtr1;
+	int ctl_reg_mtr2;
+	int ctl_reg_mtr3;
+	int ctl_reg_mtr4;
+	int ctl_reg_miir;
+	int ctl_reg_revr;
+	int ctl_reg_ercvr;
+	int ctl_reg_extr;
+	int ctl_phy_ctrl;
+	int ctl_phy_stat;
+	int ctl_phy_id1;
+	int ctl_phy_id2;
+	int ctl_phy_adc;
+	int ctl_phy_remc;
+	int ctl_phy_cfg1;
+	int ctl_phy_cfg2;
+	int ctl_phy_int;
+	int ctl_phy_mask;
+#endif // SMC_DEBUG
+
+
+#endif // CONFIG_SYSCTL
+
+};
+
+MODULE_LICENSE("GPL");
+
+/*-----------------------------------------------------------------
+ .
+ .  The driver can be entered at any of the following entry points.
+ .
+ .------------------------------------------------------------------  */
+
+/*
+ . This is called by  register_netdev().  It is responsible for
+ . checking the portlist for the SMC9000 series chipset.  If it finds
+ . one, then it will initialize the device, find the hardware information,
+ . and sets up the appropriate device parameters.
+ . NOTE: Interrupts are *OFF* when this procedure is called.
+ .
+ . NB:This shouldn't be static since it is referred to externally.
+*/
+int smc_init(struct net_device *dev);
+
+/*
+ . This is called by  unregister_netdev().  It is responsible for
+ . cleaning up before the driver is finally unregistered and discarded.
+*/
+void smc_destructor(struct net_device *dev);
+
+/*
+ . The kernel calls this function when someone wants to use the net_device,
+ . typically 'ifconfig ethX up'.
+*/
+static int smc_open(struct net_device *dev);
+
+/*
+ . This is called by the kernel to send a packet out into the net.  it's
+ . responsible for doing a best-effort send, but if it's simply not possible
+ . to send it, the packet gets dropped.
+*/
+static void smc_timeout (struct net_device *dev);
+/*
+ . This is called by the kernel in response to 'ifconfig ethX down'.  It
+ . is responsible for cleaning up everything that the open routine
+ . does, and maybe putting the card into a powerdown state.
+*/
+static int smc_close(struct net_device *dev);
+
+/*
+ . This routine allows the proc file system to query the driver's
+ . statistics.
+*/
+static struct net_device_stats * smc_query_statistics( struct net_device *dev);
+
+/*
+ . Finally, a call to set promiscuous mode ( for TCPDUMP and related
+ . programs ) and multicast modes.
+*/
+static void smc_set_multicast_list(struct net_device *dev);
+
+/*
+ . Configures the PHY through the MII Management interface
+*/
+static void smc_phy_configure(struct net_device* dev);
+
+/*---------------------------------------------------------------
+ .
+ . Interrupt level calls..
+ .
+ ----------------------------------------------------------------*/
+
+/*
+ . Handles the actual interrupt
+*/
+static void smc_interrupt(int irq, void *, struct pt_regs *regs);
+/*
+ . This is a separate procedure to handle the receipt of a packet, to
+ . leave the interrupt code looking slightly cleaner
+*/
+inline static void smc_rcv( struct net_device *dev );
+/*
+ . This handles a TX interrupt, which is only called when an error
+ . relating to a packet is sent.
+*/
+inline static void smc_tx( struct net_device * dev );
+
+/*
+ . This handles interrupts generated from PHY register 18
+*/
+static void smc_phy_interrupt(struct net_device* dev);
+
+/*
+ . Kicks the PHY out of invalid states
+*/
+static void smc_phy_reset(struct net_device* dev);
+/*
+ ------------------------------------------------------------
+ .
+ . Internal routines
+ .
+ ------------------------------------------------------------
+*/
+
+/*
+ . Test if a given location contains a chip, trying to cause as
+ . little damage as possible if it's not a SMC chip.
+*/
+static int smc_probe(struct net_device *dev, int ioaddr);
+
+/*
+ . A rather simple routine to print out a packet for debugging purposes.
+*/
+#if SMC_DEBUG > 2
+static void print_packet( byte *, int );
+#endif
+
+#define tx_done(dev) 1
+
+/* this is called to actually send the packet to the chip */
+static void smc_hardware_send_packet( struct net_device * dev );
+
+/* Since I am not sure if I will have enough room in the chip's ram
+ . to store the packet, I call this routine, which either sends it
+ . now, or generates an interrupt when the card is ready for the
+ . packet */
+static int  smc_wait_to_send_packet( struct sk_buff * skb, struct net_device *dev );
+
+/* this does a soft reset on the device */
+static void smc_reset( struct net_device* dev );
+
+/* Enable Interrupts, Receive, and Transmit */
+static void smc_enable( struct net_device *dev );
+
+/* this puts the device in an inactive state */
+static void smc_shutdown( int ioaddr );
+
+#ifndef NO_AUTOPROBE
+/* This routine will find the IRQ of the driver if one is not
+ . specified in the input to the device.  */
+static int smc_findirq( int ioaddr );
+#endif
+
+/*
+  this routine will set the hardware multicast table to the specified
+  values given it by the higher level routines
+*/
+static void smc_setmulticast( int ioaddr, int count, struct dev_mc_list *  );
+static int crc32( char *, int );
+
+/* Routines to Read and Write the PHY Registers across the
+   MII Management Interface
+*/
+
+static word smc_read_phy_register(int ioaddr, byte phyaddr, byte phyreg);
+static void smc_write_phy_register(int ioaddr, byte phyaddr, byte phyreg, word phydata);
+
+/* Initilizes our device's sysctl proc filesystem */
+
+#ifdef CONFIG_SYSCTL
+static void smc_sysctl_register(struct net_device *);
+static void smc_sysctl_unregister(struct net_device *);
+#endif /* CONFIG_SYSCTL */ 
+
+/*
+ . Function: smc_reset( struct device* dev )
+ . Purpose:
+ .  	This sets the SMC91111 chip to its normal state, hopefully from whatever
+ . 	mess that any other DOS driver has put it in.
+ .
+ . Maybe I should reset more registers to defaults in here?  SOFTRST  should
+ . do that for me.
+ .
+ . Method:
+ .	1.  send a SOFT RESET
+ .	2.  wait for it to finish
+ .	3.  enable autorelease mode
+ .	4.  reset the memory management unit
+ .	5.  clear all interrupts
+ .
+*/
+static void smc_reset( struct net_device* dev )
+{
+	//struct smc_local *lp 	= (struct smc_local *)dev->priv;
+	int	ioaddr = dev->base_addr;
+
+	PRINTK2("%s:smc_reset\n", dev->name);
+
+	/* This resets the registers mostly to defaults, but doesn't
+	   affect EEPROM.  That seems unnecessary */
+	SMC_SELECT_BANK( 0 );
+	outw( RCR_SOFTRST, ioaddr + RCR_REG );
+
+	/* Setup the Configuration Register */
+	/* This is necessary because the CONFIG_REG is not affected */
+	/* by a soft reset */
+
+	SMC_SELECT_BANK( 1 );
+	outw( CONFIG_DEFAULT, ioaddr + CONFIG_REG);
+
+	/* Setup for fast accesses if requested */
+	/* If the card/system can't handle it then there will */
+	/* be no recovery except for a hard reset or power cycle */
+
+	if (dev->dma)
+		outw( inw( ioaddr + CONFIG_REG ) | CONFIG_NO_WAIT,
+			ioaddr + CONFIG_REG );
+
+#ifdef POWER_DOWN
+	/* Release from possible power-down state */
+	/* Configuration register is not affected by Soft Reset */
+	SMC_SELECT_BANK( 1 );
+	outw( inw( ioaddr + CONFIG_REG ) | CONFIG_EPH_POWER_EN,
+		ioaddr + CONFIG_REG  );
+#endif
+
+	SMC_SELECT_BANK( 0 );
+
+	/* this should pause enough for the chip to be happy */
+	mdelay(10);
+
+	/* Disable transmit and receive functionality */
+	outw( RCR_CLEAR, ioaddr + RCR_REG );
+	outw( TCR_CLEAR, ioaddr + TCR_REG );
+
+	/* set the control register to automatically
+	   release successfully transmitted packets, to make the best
+	   use out of our limited memory */
+	SMC_SELECT_BANK( 1 );
+	outw( inw( ioaddr + CTL_REG ) | CTL_AUTO_RELEASE , ioaddr + CTL_REG );
+
+	/* Reset the MMU */
+	SMC_SELECT_BANK( 2 );
+	outw( MC_RESET, ioaddr + MMU_CMD_REG );
+
+	/* Note:  It doesn't seem that waiting for the MMU busy is needed here,
+	   but this is a place where future chipsets _COULD_ break.  Be wary
+ 	   of issuing another MMU command right after this */
+
+	/* Disable all interrupts */
+	outb( 0, ioaddr + IM_REG );
+}
+
+/*
+ . Function: smc_enable
+ . Purpose: let the chip talk to the outside work
+ . Method:
+ .	1.  Enable the transmitter
+ .	2.  Enable the receiver
+ .	3.  Enable interrupts
+*/
+static void smc_enable( struct net_device *dev )
+{
+	unsigned int ioaddr 	= dev->base_addr;
+	struct smc_local *lp 	= (struct smc_local *)dev->priv;
+
+	PRINTK2("%s:smc_enable\n", dev->name);
+
+	SMC_SELECT_BANK( 0 );
+	/* see the header file for options in TCR/RCR DEFAULT*/
+	outw( lp->tcr_cur_mode, ioaddr + TCR_REG );
+	outw( lp->rcr_cur_mode, ioaddr + RCR_REG );
+
+	/* now, enable interrupts */
+	SMC_SELECT_BANK( 2 );
+	outb( SMC_INTERRUPT_MASK, ioaddr + IM_REG );
+}
+
+/*
+ . Function: smc_shutdown
+ . Purpose:  closes down the SMC91xxx chip.
+ . Method:
+ .	1. zero the interrupt mask
+ .	2. clear the enable receive flag
+ .	3. clear the enable xmit flags
+ .
+ . TODO:
+ .   (1) maybe utilize power down mode.
+ .	Why not yet?  Because while the chip will go into power down mode,
+ .	the manual says that it will wake up in response to any I/O requests
+ .	in the register space.   Empirical results do not show this working.
+*/
+static void smc_shutdown( int ioaddr )
+{
+	PRINTK2("CARDNAME:smc_shutdown\n");
+
+	/* no more interrupts for me */
+	SMC_SELECT_BANK( 2 );
+	outb( 0, ioaddr + IM_REG );
+
+	/* and tell the card to stay away from that nasty outside world */
+	SMC_SELECT_BANK( 0 );
+	outb( RCR_CLEAR, ioaddr + RCR_REG );
+	outb( TCR_CLEAR, ioaddr + TCR_REG );
+
+#ifdef POWER_DOWN
+	/* finally, shut the chip down */
+	SMC_SELECT_BANK( 1 );
+	outw( inw( ioaddr + CONFIG_REG ) & ~CONFIG_EPH_POWER_EN,
+		ioaddr + CONFIG_REG  );
+#endif
+}
+
+
+/*
+ . Function: smc_setmulticast( int ioaddr, int count, dev_mc_list * adds )
+ . Purpose:
+ .    This sets the internal hardware table to filter out unwanted multicast
+ .    packets before they take up memory.
+ .
+ .    The SMC chip uses a hash table where the high 6 bits of the CRC of
+ .    address are the offset into the table.  If that bit is 1, then the
+ .    multicast packet is accepted.  Otherwise, it's dropped silently.
+ .
+ .    To use the 6 bits as an offset into the table, the high 3 bits are the
+ .    number of the 8 bit register, while the low 3 bits are the bit within
+ .    that register.
+ .
+ . This routine is based very heavily on the one provided by Peter Cammaert.
+*/
+
+
+static void smc_setmulticast( int ioaddr, int count, struct dev_mc_list * addrs ) {
+	int			i;
+	unsigned char		multicast_table[ 8 ];
+	struct dev_mc_list	* cur_addr;
+	/* table for flipping the order of 3 bits */
+	unsigned char invert3[] = { 0, 4, 2, 6, 1, 5, 3, 7 };
+
+	PRINTK2("CARDNAME:smc_setmulticast\n");
+
+	/* start with a table of all zeros: reject all */
+	memset( multicast_table, 0, sizeof( multicast_table ) );
+
+	cur_addr = addrs;
+	for ( i = 0; i < count ; i ++, cur_addr = cur_addr->next  ) {
+		int position;
+
+		/* do we have a pointer here? */
+		if ( !cur_addr )
+			break;
+		/* make sure this is a multicast address - shouldn't this
+		   be a given if we have it here ? */
+		if ( !( *cur_addr->dmi_addr & 1 ) )
+			continue;
+
+		/* only use the low order bits */
+		position = crc32( cur_addr->dmi_addr, 6 ) & 0x3f;
+
+		/* do some messy swapping to put the bit in the right spot */
+		multicast_table[invert3[position&7]] |=
+					(1<<invert3[(position>>3)&7]);
+
+	}
+	/* now, the table can be loaded into the chipset */
+	SMC_SELECT_BANK( 3 );
+
+	for ( i = 0; i < 8 ; i++ ) {
+		outb( multicast_table[i], ioaddr + MCAST_REG1 + i );
+	}
+}
+
+/*
+  Finds the CRC32 of a set of bytes.
+  Again, from Peter Cammaert's code.
+*/
+static int crc32( char * s, int length ) {
+	/* indices */
+	int perByte;
+	int perBit;
+	/* crc polynomial for Ethernet */
+	const unsigned long poly = 0xedb88320;
+	/* crc value - preinitialized to all 1's */
+	unsigned long crc_value = 0xffffffff;
+
+	for ( perByte = 0; perByte < length; perByte ++ ) {
+		unsigned char	c;
+
+		c = *(s++);
+		for ( perBit = 0; perBit < 8; perBit++ ) {
+			crc_value = (crc_value>>1)^
+				(((crc_value^c)&0x01)?poly:0);
+			c >>= 1;
+		}
+	}
+	return	crc_value;
+}
+
+
+/*
+ . Function: smc_wait_to_send_packet( struct sk_buff * skb, struct device * )
+ . Purpose:
+ .    Attempt to allocate memory for a packet, if chip-memory is not
+ .    available, then tell the card to generate an interrupt when it
+ .    is available.
+ .
+ . Algorithm:
+ .
+ . o	if the saved_skb is not currently null, then drop this packet
+ .	on the floor.  This should never happen, because of TBUSY.
+ . o	if the saved_skb is null, then replace it with the current packet,
+ . o	See if I can sending it now.
+ . o 	(NO): Enable interrupts and let the interrupt handler deal with it.
+ . o	(YES):Send it now.
+*/
+static int smc_wait_to_send_packet( struct sk_buff * skb, struct net_device * dev )
+{
+	struct smc_local *lp 	= (struct smc_local *)dev->priv;
+	unsigned int ioaddr 	= dev->base_addr;
+	word 			length;
+	unsigned short 		numPages;
+	word			time_out;
+	word			status;
+
+	PRINTK3("%s:smc_wait_to_send_packet\n", dev->name);
+
+	if ( lp->saved_skb) {
+		/* THIS SHOULD NEVER HAPPEN. */
+		lp->stats.tx_aborted_errors++;
+		printk("%s: Bad Craziness - sent packet while busy.\n",
+			dev->name);
+		return 1;
+	}
+	lp->saved_skb = skb;
+
+	length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+
+		
+	/*
+	** The MMU wants the number of pages to be the number of 256 bytes
+	** 'pages', minus 1 ( since a packet can't ever have 0 pages :) )
+	**
+	** The 91C111 ignores the size bits, but the code is left intact
+	** for backwards and future compatibility.
+	**
+	** Pkt size for allocating is data length +6 (for additional status
+	** words, length and ctl!)
+	**
+	** If odd size then last byte is included in this header.
+	*/
+	numPages =   ((length & 0xfffe) + 6);
+	numPages >>= 8; // Divide by 256
+
+	if (numPages > 7 ) {
+		printk("%s: Far too big packet error. \n", dev->name);
+		/* freeing the packet is a good thing here... but should
+		 . any packets of this size get down here?   */
+		dev_kfree_skb (skb);
+		lp->saved_skb = NULL;
+		/* this IS an error, but, i don't want the skb saved */
+		netif_wake_queue(dev);
+		return 0;
+	}
+	/* either way, a packet is waiting now */
+	lp->packets_waiting++;
+
+	/* now, try to allocate the memory */
+	SMC_SELECT_BANK( 2 );
+	outw( MC_ALLOC | numPages, ioaddr + MMU_CMD_REG );
+	/*
+ 	. Performance Hack
+	.
+ 	. wait a short amount of time.. if I can send a packet now, I send
+	. it now.  Otherwise, I enable an interrupt and wait for one to be
+	. available.
+	.
+	. I could have handled this a slightly different way, by checking to
+	. see if any memory was available in the FREE MEMORY register.  However,
+	. either way, I need to generate an allocation, and the allocation works
+	. no matter what, so I saw no point in checking free memory.
+	*/
+	time_out = MEMORY_WAIT_TIME;
+	do {
+		status = inb( ioaddr + INT_REG );
+		if ( status & IM_ALLOC_INT ) {
+			/* acknowledge the interrupt */
+			outb( IM_ALLOC_INT, ioaddr + INT_REG );
+  			break;
+		}
+   	} while ( -- time_out );
+
+   	if ( !time_out ) {
+		/* oh well, wait until the chip finds memory later */
+		SMC_ENABLE_INT( IM_ALLOC_INT );
+
+		/* Check the status bit one more time just in case */
+		/* it snuk in between the time we last checked it */
+		/* and when we set the interrupt bit */
+		status = inb( ioaddr + INT_REG );
+		if ( !(status & IM_ALLOC_INT) ) {
+      			PRINTK2("%s: memory allocation deferred. \n",
+				dev->name);
+			/* it's deferred, but I'll handle it later */
+      			return 0;
+			}
+
+		/* Looks like it did sneak in, so disable */
+		/* the interrupt */
+		SMC_DISABLE_INT( IM_ALLOC_INT );
+   	}
+	/* or YES! I can send the packet now.. */
+	smc_hardware_send_packet(dev);
+	netif_wake_queue(dev);
+	return 0;
+}
+
+/*
+ . Function:  smc_hardware_send_packet(struct device * )
+ . Purpose:
+ .	This sends the actual packet to the SMC9xxx chip.
+ .
+ . Algorithm:
+ . 	First, see if a saved_skb is available.
+ .		( this should NOT be called if there is no 'saved_skb'
+ .	Now, find the packet number that the chip allocated
+ .	Point the data pointers at it in memory
+ .	Set the length word in the chip's memory
+ .	Dump the packet to chip memory
+ .	Check if a last byte is needed ( odd length packet )
+ .		if so, set the control flag right
+ . 	Tell the card to send it
+ .	Enable the transmit interrupt, so I know if it failed
+ . 	Free the kernel data if I actually sent it.
+*/
+static void smc_hardware_send_packet( struct net_device * dev )
+{
+	struct smc_local *lp = (struct smc_local *)dev->priv;
+	byte	 		packet_no;
+	struct sk_buff * 	skb = lp->saved_skb;
+	word			length;
+	unsigned int	ioaddr;
+	byte			* buf;
+
+	PRINTK3("%s:smc_hardware_send_packet\n", dev->name);
+
+	ioaddr = dev->base_addr;
+
+	if ( !skb ) {
+		PRINTK("%s: In XMIT with no packet to send \n", dev->name);
+		return;
+	}
+	length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+	buf = skb->data;
+
+	/* If I get here, I _know_ there is a packet slot waiting for me */
+	packet_no = inb( ioaddr + AR_REG );
+	if ( packet_no & AR_FAILED ) {
+		/* or isn't there?  BAD CHIP! */
+		printk(KERN_DEBUG "%s: Memory allocation failed. \n",
+			dev->name);
+		dev_kfree_skb_any (skb);
+		lp->saved_skb = NULL;
+		netif_wake_queue(dev);
+		return;
+	}
+
+	/* we have a packet address, so tell the card to use it */
+	outb( packet_no, ioaddr + PN_REG );
+
+	/* point to the beginning of the packet */
+	outw( PTR_AUTOINC , ioaddr + PTR_REG );
+
+   	PRINTK3("%s: Trying to xmit packet of length %x\n",
+		dev->name, length);
+
+#if SMC_DEBUG > 2
+	printk("Transmitting Packet\n");
+	print_packet( buf, length );
+#endif
+
+	/* send the packet length ( +6 for status, length and ctl byte )
+ 	   and the status word ( set to zeros ) */
+#ifdef USE_32_BIT
+	outl(  (length +6 ) << 16 , ioaddr + DATA_REG );
+#else
+	outw( 0, ioaddr + DATA_REG );
+	/* send the packet length ( +6 for status words, length, and ctl*/
+	outb( (length+6) & 0xFF,ioaddr + DATA_REG );
+	outb( (length+6) >> 8 , ioaddr + DATA_REG );
+#endif
+
+	/* send the actual data
+	 . I _think_ it's faster to send the longs first, and then
+	 . mop up by sending the last word.  It depends heavily
+ 	 . on alignment, at least on the 486.  Maybe it would be
+ 	 . a good idea to check which is optimal?  But that could take
+	 . almost as much time as is saved?
+	*/
+#ifdef USE_32_BIT
+	outsl(ioaddr + DATA_REG, buf,  length >> 2 );
+	if ( length & 0x2  )
+		outw(*((word *)(buf + (length & 0xFFFFFFFC))),ioaddr +DATA_REG);
+#else
+	outsw(ioaddr + DATA_REG , buf, (length ) >> 1);
+#endif // USE_32_BIT
+
+	/* Send the last byte, if there is one.   */
+	if ( (length & 1) == 0 ) {
+		outw( 0, ioaddr + DATA_REG );
+	} else {
+		outb( buf[length -1 ], ioaddr + DATA_REG );
+		outb( 0x20, ioaddr + DATA_REG); // Set odd bit in CONTROL BYTE
+	}
+
+	/* enable the interrupts */
+	SMC_ENABLE_INT( (IM_TX_INT | IM_TX_EMPTY_INT) );
+
+	/* and let the chipset deal with it */
+	outw( MC_ENQUEUE , ioaddr + MMU_CMD_REG );
+
+	PRINTK2("%s: Sent packet of length %d \n", dev->name, length);
+
+	lp->saved_skb = NULL;
+	dev_kfree_skb_any (skb);
+
+	dev->trans_start = jiffies;
+
+	/* we can send another packet */
+	netif_wake_queue(dev);
+
+
+	return;
+}
+
+/*-------------------------------------------------------------------------
+ |
+ | smc_init( struct device * dev )
+ |   Input parameters:
+ |	dev->base_addr == 0, try to find all possible locations
+ |	dev->base_addr == 1, return failure code
+ |	dev->base_addr == 2, always allocate space,  and return success
+ |	dev->base_addr == <anything else>   this is the address to check
+ |
+ |   Output:
+ |	0 --> there is a device
+ |	anything else, error
+ |
+ ---------------------------------------------------------------------------
+*/
+int __init smc_init(struct net_device *dev)
+{
+	int i;
+	unsigned int base_addr = dev ? dev->base_addr : 0;
+
+	PRINTK2("CARDNAME:smc_init\n");
+
+	SET_MODULE_OWNER (dev);
+
+	/*  try a specific location */
+	if (base_addr > 0x1ff)
+		return smc_probe(dev, base_addr);
+	else if ( 0 != base_addr ) 
+			return -ENXIO;
+		
+	/* check every ethernet address */
+	for (i = 0; smc_portlist[i]; i++) 
+		if ( smc_probe(dev,smc_portlist[i]) ==0)
+			return 0;
+		
+	/* couldn't find anything */
+	return -ENODEV;
+}
+
+
+/*-------------------------------------------------------------------------
+ |
+ | smc_destructor( struct device * dev )
+ |   Input parameters:
+ |	dev, pointer to the device structure
+ |
+ |   Output:
+ |	None.
+ |
+ ---------------------------------------------------------------------------
+*/
+void smc_destructor(struct net_device *dev)
+{
+	PRINTK2("CARDNAME:smc_destructor\n");
+}
+
+
+#ifndef NO_AUTOPROBE
+/*----------------------------------------------------------------------
+ . smc_findirq
+ .
+ . This routine has a simple purpose -- make the SMC chip generate an
+ . interrupt, so an auto-detect routine can detect it, and find the IRQ,
+ ------------------------------------------------------------------------
+*/
+int __init smc_findirq( int ioaddr )
+{
+	int	timeout = 20;
+	unsigned long cookie;
+
+	PRINTK2("CARDNAME:smc_findirq\n");
+
+	/* I have to do a STI() here, because this is called from
+	   a routine that does an CLI during this process, making it
+	   rather difficult to get interrupts for auto detection */
+	sti();
+
+	cookie = probe_irq_on();
+
+	/*
+	 * What I try to do here is trigger an ALLOC_INT. This is done
+	 * by allocating a small chunk of memory, which will give an interrupt
+	 * when done.
+	 */
+
+
+	SMC_SELECT_BANK(2);
+	/* enable ALLOCation interrupts ONLY */
+	outb( IM_ALLOC_INT, ioaddr + IM_REG );
+
+	/*
+ 	 . Allocate 512 bytes of memory.  Note that the chip was just
+	 . reset so all the memory is available
+	*/
+	outw( MC_ALLOC | 1, ioaddr + MMU_CMD_REG );
+
+	/*
+	 . Wait until positive that the interrupt has been generated
+	*/
+	while ( timeout ) {
+		byte	int_status;
+
+		int_status = inb( ioaddr + INT_REG );
+
+		if ( int_status & IM_ALLOC_INT )
+			break;		/* got the interrupt */
+		timeout--;
+	}
+
+	/* there is really nothing that I can do here if timeout fails,
+	   as autoirq_report will return a 0 anyway, which is what I
+	   want in this case.   Plus, the clean up is needed in both
+	   cases.  */
+
+	/* DELAY HERE!
+	   On a fast machine, the status might change before the interrupt
+	   is given to the processor.  This means that the interrupt was
+	   never detected, and autoirq_report fails to report anything.
+	   This should fix autoirq_* problems.
+	*/
+	mdelay(10);
+
+	/* and disable all interrupts again */
+	outb( 0, ioaddr + IM_REG );
+
+	/* clear hardware interrupts again, because that's how it
+	   was when I was called... */
+	cli();
+
+	/* and return what I found */
+	return probe_irq_off(cookie);
+}
+#endif
+
+/*----------------------------------------------------------------------
+ . Function: smc_probe( int ioaddr )
+ .
+ . Purpose:
+ .	Tests to see if a given ioaddr points to an SMC91111 chip.
+ .	Returns a 0 on success
+ .
+ . Algorithm:
+ .	(1) see if the high byte of BANK_SELECT is 0x33
+ . 	(2) compare the ioaddr with the base register's address
+ .	(3) see if I recognize the chip ID in the appropriate register
+ .
+ .---------------------------------------------------------------------
+ */
+/*---------------------------------------------------------------
+ . Here I do typical initialization tasks.
+ .
+ . o  Initialize the structure if needed
+ . o  print out my vanity message if not done so already
+ . o  print out what type of hardware is detected
+ . o  print out the ethernet address
+ . o  find the IRQ
+ . o  set up my private data
+ . o  configure the dev structure with my subroutines
+ . o  actually GRAB the irq.
+ . o  GRAB the region
+ .-----------------------------------------------------------------*/
+
+static int __init smc_probe(struct net_device *dev, int ioaddr )
+{
+	int i, memory, retval;
+	static unsigned version_printed = 0;
+	unsigned int	bank;
+
+        const char *version_string;
+
+	/*registers */
+	word	revision_register;
+	word	base_address_register;
+	word  	memory_info_register;
+	/*=> Pramod */
+	struct smc_local *lp;
+	/*<= Pramod */
+
+	PRINTK2("CARDNAME:smc_probe\n");
+
+	/* Grab the region so that no one else tries to probe our ioports. */
+	if (!request_region(ioaddr, SMC_IO_EXTENT, dev->name)) return -EBUSY;
+
+	/* First, see if the high byte is 0x33 */
+	bank = inw( ioaddr + BANK_SELECT );
+	if ( (bank & 0xFF00) != 0x3300 ) return -ENODEV;
+
+	/* The above MIGHT indicate a device, but I need to write to further test this.  */
+	outw( 0x0, ioaddr + BANK_SELECT );
+	bank = inw( ioaddr + BANK_SELECT );
+	if ( (bank & 0xFF00 ) != 0x3300 )
+	{
+		retval = -ENODEV;
+		goto err_out;
+	}
+
+	/* well, we've already written once, so hopefully another time won't
+ 	   hurt.  This time, I need to switch the bank register to bank 1,
+	   so I can access the base address register */
+	SMC_SELECT_BANK(1);
+	base_address_register = inw( ioaddr + BASE_REG );
+#ifndef CONFIG_SOC_AU1X00
+	if ( ioaddr != ( base_address_register >> 3 & 0x3E0 ) )
+	{
+		printk("CARDNAME: IOADDR %x doesn't match configuration (%x)."
+			"Probably not a SMC chip\n",
+			ioaddr, base_address_register >> 3 & 0x3E0 );
+		/* well, the base address register didn't match.  Must not have
+		   been a SMC chip after all. */
+		retval = -ENODEV;
+		goto err_out;
+	}
+#endif
+
+	/*  check if the revision register is something that I recognize.
+	    These might need to be added to later, as future revisions
+	    could be added.  */
+	SMC_SELECT_BANK(3);
+	revision_register  = inw( ioaddr + REV_REG );
+	if ( !chip_ids[ ( revision_register  >> 4 ) & 0xF  ] )
+	{
+		/* I don't recognize this chip, so... */
+		printk("CARDNAME: IO %x: Unrecognized revision register:"
+			" %x, Contact author. \n",
+			ioaddr, revision_register );
+		retval =  -ENODEV;
+		goto err_out;
+	}
+
+	/* at this point I'll assume that the chip is an SMC9xxx.
+	   It might be prudent to check a listing of MAC addresses
+	   against the hardware address, or do some other tests. */
+
+	if (version_printed++ == 0)
+		printk("%s", version);
+
+	/* fill in some of the fields */
+	dev->base_addr = ioaddr;
+
+	/*
+ 	 . Get the MAC address ( bank 1, regs 4 - 9 )
+	*/
+#ifndef CONFIG_SOC_AU1X00
+	SMC_SELECT_BANK( 1 );
+	for ( i = 0; i < 6; i += 2 )
+	{
+		word	address;
+
+		address = inw( ioaddr + ADDR0_REG + i  );
+		dev->dev_addr[ i + 1] = address >> 8;
+		dev->dev_addr[ i ] = address & 0xFF;
+	}
+#else
+		/* Obtain address from Boot PROM (no EEPROM) */
+		get_ethernet_addr(dev->dev_addr);
+#endif
+
+	/* get the memory information */
+
+	SMC_SELECT_BANK( 0 );
+	memory_info_register = inw( ioaddr + MIR_REG );
+	memory = memory_info_register & (word)0x00ff;
+	memory *= LAN91C111_MEMORY_MULTIPLIER;
+
+	/*
+	 Now, I want to find out more about the chip.  This is sort of
+ 	 redundant, but it's cleaner to have it in both, rather than having
+ 	 one VERY long probe procedure.
+	*/
+	SMC_SELECT_BANK(3);
+	revision_register  = inw( ioaddr + REV_REG );
+	version_string = chip_ids[ ( revision_register  >> 4 ) & 0xF  ];
+	if ( !version_string )
+	{
+		/* I shouldn't get here because this call was done before.... */
+		retval =  -ENODEV;
+		goto err_out;
+	}
+
+	/* now, reset the chip, and put it into a known state */
+	smc_reset( dev );
+
+	/*
+	 . If dev->irq is 0, then the device has to be banged on to see
+	 . what the IRQ is.
+ 	 .
+	 . This banging doesn't always detect the IRQ, for unknown reasons.
+	 . a workaround is to reset the chip and try again.
+	 .
+	 . Interestingly, the DOS packet driver *SETS* the IRQ on the card to
+	 . be what is requested on the command line.   I don't do that, mostly
+	 . because the card that I have uses a non-standard method of accessing
+	 . the IRQs, and because this _should_ work in most configurations.
+	 .
+	 . Specifying an IRQ is done with the assumption that the user knows
+	 . what (s)he is doing.  No checking is done!!!!
+ 	 .
+	*/
+	if ( dev->irq < 2 ) {
+		int	trials;
+
+		trials = 3;
+		while ( trials-- ) {
+			dev->irq = smc_findirq( ioaddr );
+			if ( dev->irq )
+				break;
+			/* kick the card and try again */
+			smc_reset( dev );
+		}
+	}
+	if (dev->irq == 0 ) {
+		printk("%s: Couldn't autodetect your IRQ. Use irq=xx.\n",
+			dev->name);
+		retval =  -ENODEV;
+		goto err_out;
+	}
+
+	if (dev->irq == 2) {
+		/* Fixup for users that don't know that IRQ 2 is really IRQ 9,
+		 * or don't know which one to set.
+		 */
+		dev->irq = 9;
+	}
+
+	/* now, print out the card info, in a short format.. */
+
+	printk("%s: %s(rev:%d) at %#3x IRQ:%d MEMSIZE:%db NOWAIT:%d ",
+		dev->name,
+		version_string, revision_register & 0xF, ioaddr, dev->irq,
+		memory, dev->dma);
+	/*
+	 . Print the Ethernet address
+	*/
+	printk("ADDR: ");
+	for (i = 0; i < 5; i++)
+		printk("%2.2x:", dev->dev_addr[i] );
+	printk("%2.2x \n", dev->dev_addr[5] );
+
+
+	/* Initialize the private structure. */
+	if (dev->priv == NULL) {
+		dev->priv = kmalloc(sizeof(struct smc_local), GFP_KERNEL);
+		if (dev->priv == NULL) {
+			retval = -ENOMEM;
+			goto err_out;
+		}
+	}
+	/* set the private data to zero by default */
+	memset(dev->priv, 0, sizeof(struct smc_local));
+
+	/* Fill in the fields of the device structure with ethernet values. */
+	ether_setup(dev);
+
+	/* Grab the IRQ */
+    retval = request_irq(dev->irq, &smc_interrupt, 0, dev->name, dev);
+    if (retval) {
+       	  printk("%s: unable to get IRQ %d (irqval=%d).\n",
+		dev->name, dev->irq, retval);
+		  kfree (dev->priv);
+		  dev->priv = NULL;
+		  goto err_out;
+      	}
+
+	dev->open		        = smc_open;
+	dev->stop		        = smc_close;
+	dev->hard_start_xmit   	= smc_wait_to_send_packet;
+	dev->tx_timeout			= smc_timeout;
+	dev->get_stats			= smc_query_statistics;
+#ifdef	HAVE_MULTICAST
+	dev->set_multicast_list 	= &smc_set_multicast_list;
+#endif
+
+	/* => Store the ChipRevision and ChipID, to be used in resolving the Odd-Byte issue in RevB of LAN91C111; Pramod */
+	SMC_SELECT_BANK(3);
+	revision_register  = inw( ioaddr + REV_REG );
+	lp = (struct smc_local *)dev->priv;
+	lp->ChipID = (revision_register >> 4) & 0xF;
+	lp->ChipRev = revision_register & 0xF;
+
+	return 0;
+
+err_out:
+	release_region (ioaddr, SMC_IO_EXTENT);
+	return retval;
+}
+
+#if SMC_DEBUG > 2
+static void print_packet( byte * buf, int length )
+{
+#if 1
+	int i;
+	int remainder;
+	int lines;
+
+	printk("Packet of length %d \n", length );
+
+#if SMC_DEBUG > 3
+	lines = length / 16;
+	remainder = length % 16;
+
+	for ( i = 0; i < lines ; i ++ ) {
+		int cur;
+
+		for ( cur = 0; cur < 8; cur ++ ) {
+			byte a, b;
+
+			a = *(buf ++ );
+			b = *(buf ++ );
+			printk("%02x%02x ", a, b );
+		}
+		printk("\n");
+	}
+	for ( i = 0; i < remainder/2 ; i++ ) {
+		byte a, b;
+
+		a = *(buf ++ );
+		b = *(buf ++ );
+		printk("%02x%02x ", a, b );
+	}
+	printk("\n");
+#endif
+#endif
+}
+#endif
+
+
+/*
+ * Open and Initialize the board
+ *
+ * Set up everything, reset the card, etc ..
+ *
+ */
+static int smc_open(struct net_device *dev)
+{
+	struct smc_local *lp 	= (struct smc_local *)dev->priv;
+	int	ioaddr = dev->base_addr;
+	int	i;	/* used to set hw ethernet address */
+
+	PRINTK2("%s:smc_open\n", dev->name);
+
+	/* clear out all the junk that was put here before... */
+	memset(dev->priv, 0, sizeof(struct smc_local));
+
+	netif_start_queue(dev);
+#ifdef MODULE
+	MOD_INC_USE_COUNT;
+#endif
+
+	// Setup the default Register Modes
+	lp->tcr_cur_mode = TCR_DEFAULT;
+	lp->rcr_cur_mode = RCR_DEFAULT;
+	lp->rpc_cur_mode = RPC_DEFAULT;
+
+	// Set default parameters (files)
+	lp->ctl_swfdup = 0;
+	lp->ctl_ephloop = 0;
+	lp->ctl_miiop = 0;
+	lp->ctl_autoneg = 1;
+	lp->ctl_rfduplx = 1;
+	lp->ctl_rspeed = 100;
+	lp->ctl_afduplx = 1;
+	lp->ctl_aspeed = 100;
+	lp->ctl_lnkfail = 1;
+	lp->ctl_forcol = 0;
+	lp->ctl_filtcar = 0;
+
+	/* reset the hardware */
+
+	smc_reset( dev );
+	smc_enable( dev );
+
+	/* Configure the PHY */
+	smc_phy_configure(dev);
+
+	/*
+  		According to Becker, I have to set the hardware address
+		at this point, because the (l)user can set it with an
+		ioctl.  Easily done...
+	*/
+	SMC_SELECT_BANK( 1 );
+	for ( i = 0; i < 6; i += 2 ) {
+		word	address;
+
+		address = dev->dev_addr[ i + 1 ] << 8 ;
+		address  |= dev->dev_addr[ i ];
+		outw( address, ioaddr + ADDR0_REG + i );
+	}
+
+#ifdef CONFIG_SYSCTL
+	smc_sysctl_register(dev);
+#endif /* CONFIG_SYSCTL */ 
+
+	netif_start_queue(dev);
+	return 0;
+}
+
+/*--------------------------------------------------------
+ . Called by the kernel to send a packet out into the void
+ . of the net.  This routine is largely based on
+ . skeleton.c, from Becker.
+ .--------------------------------------------------------
+*/
+static void smc_timeout (struct net_device *dev)
+{
+
+	PRINTK3("%s:smc_timeout\n", dev->name);
+
+	/* If we get here, some higher level has decided we are broken.
+	There should really be a "kick me" function call instead. */
+	printk(KERN_WARNING "%s: transmit timed out, %s?\n",dev->name, tx_done(dev) ? "IRQ conflict" :"network cable problem");
+	/* "kick" the adaptor */
+	smc_reset( dev );
+	smc_enable( dev );
+
+	/* Reconfigure the PHY */
+	smc_phy_configure(dev);
+
+	netif_wake_queue(dev);
+	dev->trans_start = jiffies;
+	/* clear anything saved */
+	((struct smc_local *)dev->priv)->saved_skb = NULL;
+}
+
+/*--------------------------------------------------------------------
+ .
+ . This is the main routine of the driver, to handle the net_device when
+ . it needs some attention.
+ .
+ . So:
+ .   first, save state of the chipset
+ .   branch off into routines to handle each case, and acknowledge
+ .	    each to the interrupt register
+ .   and finally restore state.
+ .
+ ---------------------------------------------------------------------*/
+static void smc_interrupt(int irq, void * dev_id,  struct pt_regs * regs)
+{
+	struct net_device *dev 	= dev_id;
+	int ioaddr 		= dev->base_addr;
+	struct smc_local *lp 	= (struct smc_local *)dev->priv;
+
+	byte	status;
+	word	card_stats;
+	byte	mask;
+	int	timeout;
+	/* state registers */
+	word	saved_bank;
+	word	saved_pointer;
+
+
+
+	PRINTK3("%s: SMC interrupt started \n", dev->name);
+
+	if (dev == NULL) {
+		printk(KERN_WARNING "%s: irq %d for unknown device.\n",
+			dev->name, irq);
+		return;
+	}
+
+/* will Linux let this happen ??  If not, this costs some speed
+	if ( dev->interrupt ) {
+		printk(KERN_WARNING "%s: interrupt inside interrupt.\n",
+			dev->name);
+		return;
+	}
+
+	dev->interrupt = 1; */
+
+	saved_bank = inw( ioaddr + BANK_SELECT );
+
+	SMC_SELECT_BANK(2);
+	saved_pointer = inw( ioaddr + PTR_REG );
+
+	/* read the interrupt status register */
+	mask = inb( ioaddr + IM_REG );
+
+	/* disable all interrupts */
+	outb( 0, ioaddr + IM_REG );
+
+
+	#if defined(CONFIG_MIPS_FICMMP)
+		//Check if the unit is still docked
+		if(((*((u32*)0xB170000C)) >> 15 & 0x01) == 0)
+		//if(au1xxx_gpio_read(215) == 0)	//Ugh!  why isn't this symbol getting exported?!?!?
+		{
+			//Disable the ethernet IRQ.  Removing from dock pulls IRQ high (always on!)
+			free_irq(devSMC91111.irq, &devSMC91111);
+			mask = 0;
+			goto no_device;
+		}
+	#endif
+
+	/* set a timeout value, so I don't stay here forever */
+	timeout = 4;
+
+	PRINTK2(KERN_WARNING "%s: MASK IS %x \n", dev->name, mask);
+	do {
+		/* read the status flag, and mask it */
+		status = inb( ioaddr + INT_REG ) & mask;
+		if (!status )
+			break;
+
+		// Acknowledge the interrupt
+		outb(status, ioaddr + INT_REG );
+
+		PRINTK3(KERN_WARNING "%s: Handling interrupt status %x \n",
+			dev->name, status);
+
+		if (status & IM_RCV_INT) {
+			/* Got a packet(s). */
+			PRINTK2(KERN_WARNING
+				"%s: Receive Interrupt\n", dev->name);
+			smc_rcv(dev);
+		} else if (status & IM_TX_INT ) {
+			PRINTK2(KERN_WARNING "%s: TX ERROR handled\n",
+				dev->name);
+			smc_tx(dev);
+		} else if (status & IM_TX_EMPTY_INT ) {
+			/* update stats */
+			SMC_SELECT_BANK( 0 );
+			card_stats = inw( ioaddr + COUNTER_REG );
+			/* single collisions */
+			lp->stats.collisions += card_stats & 0xF;
+			card_stats >>= 4;
+			/* multiple collisions */
+			lp->stats.collisions += card_stats & 0xF;
+
+			/* these are for when linux supports these statistics */
+#if 0
+			card_stats >>= 4;
+			/* deferred */
+			card_stats >>= 4;
+			/* excess deferred */
+#endif
+			SMC_SELECT_BANK( 2 );
+			PRINTK2(KERN_WARNING "%s: TX_BUFFER_EMPTY handled\n",
+				dev->name);
+			mask &= ~IM_TX_EMPTY_INT;
+			lp->stats.tx_packets += lp->packets_waiting;
+			lp->packets_waiting = 0;
+
+		} else if (status & IM_ALLOC_INT ) {
+			PRINTK2(KERN_DEBUG "%s: Allocation interrupt \n",
+				dev->name);
+			/* clear this interrupt so it doesn't happen again */
+			mask &= ~IM_ALLOC_INT;
+
+			smc_hardware_send_packet( dev );
+
+			/* enable xmit interrupts based on this */
+			mask |= ( IM_TX_EMPTY_INT | IM_TX_INT );
+
+			/* and let the card send more packets to me */
+			netif_wake_queue(dev);
+
+			PRINTK2("%s: Handoff done successfully.\n",
+				dev->name);
+		} else if (status & IM_RX_OVRN_INT ) {
+			lp->stats.rx_errors++;
+			lp->stats.rx_fifo_errors++;
+		} else if (status & IM_EPH_INT ) {
+			PRINTK("%s: UNSUPPORTED: EPH INTERRUPT \n",
+				dev->name);
+		} else if (status & IM_MDINT ) {
+			smc_phy_interrupt(dev);
+		} else if (status & IM_ERCV_INT ) {
+			PRINTK("%s: UNSUPPORTED: ERCV INTERRUPT \n",
+				dev->name);
+		}
+	} while ( timeout -- );
+
+
+	/* restore register states */
+#if defined(CONFIG_MIPS_FICMMP)
+no_device:
+#endif
+
+	SMC_SELECT_BANK( 2 );
+
+	outb( mask, ioaddr + IM_REG );
+
+	PRINTK3( KERN_WARNING "%s: MASK is now %x \n", dev->name, mask);
+	outw( saved_pointer, ioaddr + PTR_REG );
+
+	SMC_SELECT_BANK( saved_bank );
+
+	//dev->interrupt = 0;
+	PRINTK3("%s: Interrupt done\n", dev->name);
+	return;
+}
+
+/*-------------------------------------------------------------
+ .
+ . smc_rcv -  receive a packet from the card
+ .
+ . There is ( at least ) a packet waiting to be read from
+ . chip-memory.
+ .
+ . o Read the status
+ . o If an error, record it
+ . o otherwise, read in the packet
+ --------------------------------------------------------------
+*/
+static void smc_rcv(struct net_device *dev)
+{
+	struct smc_local *lp = (struct smc_local *)dev->priv;
+	int 	ioaddr = dev->base_addr;
+	int 	packet_number;
+	word	status;
+	word	packet_length;
+
+	PRINTK3("%s:smc_rcv\n", dev->name);
+
+	/* assume bank 2 */
+
+	packet_number = inw( ioaddr + RXFIFO_REG );
+
+	if ( packet_number & RXFIFO_REMPTY ) {
+
+		/* we got called , but nothing was on the FIFO */
+		PRINTK("%s: WARNING: smc_rcv with nothing on FIFO. \n",
+			dev->name);
+		/* don't need to restore anything */
+		return;
+	}
+
+	/*  start reading from the start of the packet */
+	outw( PTR_READ | PTR_RCV | PTR_AUTOINC, ioaddr + PTR_REG );
+
+	/* First two words are status and packet_length */
+	status 		= inw( ioaddr + DATA_REG );
+	packet_length 	= inw( ioaddr + DATA_REG );
+
+	packet_length &= 0x07ff;  /* mask off top bits */
+
+	PRINTK2("RCV: STATUS %4x LENGTH %4x\n", status, packet_length );
+
+	if ( !(status & RS_ERRORS ) ){
+		/* do stuff to make a new packet */
+		struct sk_buff  * skb;
+		byte		* data;
+
+		/* set multicast stats */
+		if ( status & RS_MULTICAST )
+			lp->stats.multicast++;
+
+		// Allocate enough memory for entire receive frame, to be safe
+		skb = dev_alloc_skb( packet_length );
+
+		/* Adjust for having already read the first two words */
+		packet_length -= 4;
+
+		if ( skb == NULL ) {
+			printk(KERN_NOTICE "%s: Low memory, packet dropped.\n",
+				dev->name);
+			lp->stats.rx_dropped++;
+			goto done;
+		}
+
+		/*
+		 ! This should work without alignment, but it could be
+		 ! in the worse case
+		*/
+		/* TODO: Should I use 32bit alignment here ? */
+		skb_reserve( skb, 2 );   /* 16 bit alignment */
+
+		skb->dev = dev;
+
+		/* =>
+    ODD-BYTE ISSUE : The odd byte problem has been fixed in the LAN91C111 Rev B.
+		So we check if the Chip Revision, stored in smsc_local->ChipRev, is = 1.
+		If so then we increment the packet length only if RS_ODDFRAME is set.
+		If the Chip's revision is equal to 0, then we blindly increment the packet length
+		by 1, thus always assuming that the packet is odd length, leaving the higher layer
+		to decide the actual length.
+			-- Pramod
+		<= */
+		if ((9 == lp->ChipID) && (1 == lp->ChipRev))
+		{
+			if (status & RS_ODDFRAME)
+				data = skb_put( skb, packet_length + 1 );	
+			else
+				data = skb_put( skb, packet_length);			
+			
+		}
+		else
+		{
+			// set odd length for bug in LAN91C111, REV A
+			// which never sets RS_ODDFRAME
+			data = skb_put( skb, packet_length + 1 );
+		}
+
+#ifdef USE_32_BIT
+		PRINTK3(" Reading %d dwords (and %d bytes) \n",
+			packet_length >> 2, packet_length & 3 );
+		/* QUESTION:  Like in the TX routine, do I want
+		   to send the DWORDs or the bytes first, or some
+		   mixture.  A mixture might improve already slow PIO
+		   performance  */
+		insl(ioaddr + DATA_REG , data, packet_length >> 2 );
+		/* read the left over bytes */
+		insb( ioaddr + DATA_REG, data + (packet_length & 0xFFFFFC),
+			packet_length & 0x3  );
+#else
+		PRINTK3(" Reading %d words and %d byte(s) \n",
+			(packet_length >> 1 ), packet_length & 1 );
+		insw(ioaddr + DATA_REG , data, packet_length >> 1);
+
+#endif // USE_32_BIT
+
+#if	SMC_DEBUG > 2
+		printk("Receiving Packet\n");
+		print_packet( data, packet_length );
+#endif
+
+		skb->protocol = eth_type_trans(skb, dev );
+		netif_rx(skb);
+		lp->stats.rx_packets++;
+	} else {
+		/* error ... */
+		lp->stats.rx_errors++;
+
+		if ( status & RS_ALGNERR )  lp->stats.rx_frame_errors++;
+		if ( status & (RS_TOOSHORT | RS_TOOLONG ) )
+			lp->stats.rx_length_errors++;
+		if ( status & RS_BADCRC)	lp->stats.rx_crc_errors++;
+	}
+
+	while ( inw( ioaddr + MMU_CMD_REG ) & MC_BUSY )
+		udelay(1); // Wait until not busy
+done:
+	/*  error or good, tell the card to get rid of this packet */
+	outw( MC_RELEASE, ioaddr + MMU_CMD_REG );
+
+
+	return;
+}
+
+
+/*************************************************************************
+ . smc_tx
+ .
+ . Purpose:  Handle a transmit error message.   This will only be called
+ .   when an error, because of the AUTO_RELEASE mode.
+ .
+ . Algorithm:
+ .	Save pointer and packet no
+ .	Get the packet no from the top of the queue
+ .	check if it's valid ( if not, is this an error??? )
+ .	read the status word
+ .	record the error
+ .	( resend?  Not really, since we don't want old packets around )
+ .	Restore saved values
+ ************************************************************************/
+static void smc_tx( struct net_device * dev )
+{
+	int	ioaddr = dev->base_addr;
+	struct smc_local *lp = (struct smc_local *)dev->priv;
+	byte saved_packet;
+	byte packet_no;
+	word tx_status;
+
+
+	PRINTK3("%s:smc_tx\n", dev->name);
+
+	/* assume bank 2  */
+
+	saved_packet = inb( ioaddr + PN_REG );
+	packet_no = inw( ioaddr + RXFIFO_REG );
+	packet_no &= 0x7F;
+
+	/* If the TX FIFO is empty then nothing to do */
+	if ( packet_no & TXFIFO_TEMPTY )
+		return;
+
+	/* select this as the packet to read from */
+	outb( packet_no, ioaddr + PN_REG );
+
+	/* read the first word (status word) from this packet */
+	outw( PTR_AUTOINC | PTR_READ, ioaddr + PTR_REG );
+
+	tx_status = inw( ioaddr + DATA_REG );
+	PRINTK3("%s: TX DONE STATUS: %4x \n", dev->name, tx_status);
+
+	lp->stats.tx_errors++;
+	if ( tx_status & TS_LOSTCAR ) lp->stats.tx_carrier_errors++;
+	if ( tx_status & TS_LATCOL  ) {
+		printk(KERN_DEBUG
+			"%s: Late collision occurred on last xmit.\n",
+			dev->name);
+		lp->stats.tx_window_errors++;
+		lp->ctl_forcol = 0; // Reset forced collsion
+	}
+#if 0
+	if ( tx_status & TS_16COL ) { ... }
+#endif
+
+	if ( tx_status & TS_SUCCESS ) {
+		printk("%s: Successful packet caused interrupt \n", dev->name);
+	}
+	/* re-enable transmit */
+	SMC_SELECT_BANK( 0 );
+	outw( inw( ioaddr + TCR_REG ) | TCR_ENABLE, ioaddr + TCR_REG );
+
+	/* kill the packet */
+	SMC_SELECT_BANK( 2 );
+	outw( MC_FREEPKT, ioaddr + MMU_CMD_REG );
+
+	/* one less packet waiting for me */
+	lp->packets_waiting--;
+
+	/* Don't change Packet Number Reg until busy bit is cleared */
+	/* Per LAN91C111 Spec, Page 50 */
+	while ( inw( ioaddr + MMU_CMD_REG ) & MC_BUSY );
+
+	outb( saved_packet, ioaddr + PN_REG );
+	return;
+}
+
+
+/*----------------------------------------------------
+ . smc_close
+ .
+ . this makes the board clean up everything that it can
+ . and not talk to the outside world.   Caused by
+ . an 'ifconfig ethX down'
+ .
+ -----------------------------------------------------*/
+static int smc_close(struct net_device *dev)
+{
+	netif_stop_queue(dev);
+	//dev->start = 0;
+
+	PRINTK2("%s:smc_close\n", dev->name);
+
+#ifdef CONFIG_SYSCTL
+	smc_sysctl_unregister(dev);
+#endif /* CONFIG_SYSCTL */ 
+
+	/* clear everything */
+	smc_shutdown( dev->base_addr );
+
+	/* Update the statistics here. */
+#ifdef MODULE
+	MOD_DEC_USE_COUNT;
+#endif
+
+	return 0;
+}
+
+/*------------------------------------------------------------
+ . Get the current statistics.
+ . This may be called with the card open or closed.
+ .-------------------------------------------------------------*/
+static struct net_device_stats* smc_query_statistics(struct net_device *dev) {
+	struct smc_local *lp = (struct smc_local *)dev->priv;
+
+	PRINTK2("%s:smc_query_statistics\n", dev->name);
+
+	return &lp->stats;
+}
+
+/*-----------------------------------------------------------
+ . smc_set_multicast_list
+ .
+ . This routine will, depending on the values passed to it,
+ . either make it accept multicast packets, go into
+ . promiscuous mode ( for TCPDUMP and cousins ) or accept
+ . a select set of multicast packets
+*/
+static void smc_set_multicast_list(struct net_device *dev)
+{
+	int ioaddr = dev->base_addr;
+
+	PRINTK2("%s:smc_set_multicast_list\n", dev->name);
+
+	SMC_SELECT_BANK(0);
+	if ( dev->flags & IFF_PROMISC )
+		{
+		PRINTK2("%s:smc_set_multicast_list:RCR_PRMS\n", dev->name);
+		outw( inw(ioaddr + RCR_REG ) | RCR_PRMS, ioaddr + RCR_REG );
+		}
+
+/* BUG?  I never disable promiscuous mode if multicasting was turned on.
+   Now, I turn off promiscuous mode, but I don't do anything to multicasting
+   when promiscuous mode is turned on.
+*/
+
+	/* Here, I am setting this to accept all multicast packets.
+	   I don't need to zero the multicast table, because the flag is
+	   checked before the table is
+	*/
+	else if (dev->flags & IFF_ALLMULTI)
+		{
+		outw( inw(ioaddr + RCR_REG ) | RCR_ALMUL, ioaddr + RCR_REG );
+		PRINTK2("%s:smc_set_multicast_list:RCR_ALMUL\n", dev->name);
+		}
+
+	/* We just get all multicast packets even if we only want them
+	 . from one source.  This will be changed at some future
+	 . point. */
+	else if (dev->mc_count )  {
+		/* support hardware multicasting */
+
+		/* be sure I get rid of flags I might have set */
+		outw( inw( ioaddr + RCR_REG ) & ~(RCR_PRMS | RCR_ALMUL),
+			ioaddr + RCR_REG );
+		/* NOTE: this has to set the bank, so make sure it is the
+		   last thing called.  The bank is set to zero at the top */
+		smc_setmulticast( ioaddr, dev->mc_count, dev->mc_list );
+	} else  {
+		PRINTK2("%s:smc_set_multicast_list:~(RCR_PRMS|RCR_ALMUL)\n",
+			dev->name);
+		outw( inw( ioaddr + RCR_REG ) & ~(RCR_PRMS | RCR_ALMUL),
+			ioaddr + RCR_REG );
+
+		/*
+		  since I'm disabling all multicast entirely, I need to
+		  clear the multicast list
+		*/
+		SMC_SELECT_BANK( 3 );
+		outw( 0, ioaddr + MCAST_REG1 );
+		outw( 0, ioaddr + MCAST_REG2 );
+		outw( 0, ioaddr + MCAST_REG3 );
+		outw( 0, ioaddr + MCAST_REG4 );
+	}
+}
+
+//#ifdef MODULE
+
+static struct net_device devSMC91111;
+int io = 0;
+int irq = 0;
+int nowait = 0;
+
+MODULE_PARM(io, "i");
+MODULE_PARM(irq, "i");
+MODULE_PARM(nowait, "i");
+
+/*------------------------------------------------------------
+ . Module initialization function
+ .-------------------------------------------------------------*/
+int __init smc91111_init_module(void)
+{
+	int result;
+
+	PRINTK2("CARDNAME:init_module\n");
+	if (io == 0)
+		printk(KERN_WARNING
+		CARDNAME": You shouldn't use auto-probing with insmod!\n" );
+
+	/* copy the parameters from insmod into the device structure */
+#ifdef CONFIG_SOC_AU1X00
+	if (au1xxx_smc91111_base == 0)
+	{
+		printk(KERN_WARNING
+		CARDNAME": Wrong SMC91111 Base Address!\n" );
+		return -ENODEV;
+	}
+	devSMC91111.base_addr	= au1xxx_smc91111_base;
+	devSMC91111.irq		= au1xxx_smc91111_irq;
+	devSMC91111.dma		= au1xxx_smc91111_nowait;
+#else
+	devSMC91111.base_addr	= io;
+	devSMC91111.irq		= irq;
+	devSMC91111.dma		= nowait; // Use DMA field for nowait		
+#endif
+	devSMC91111.init	= smc_init;/* Kernel 2.4 Changes - Pramod */
+	if ((result = register_netdev(&devSMC91111)) != 0)
+		return result;
+	
+	return 0;
+}
+
+/*------------------------------------------------------------
+ . Cleanup when module is removed with rmmod
+ .-------------------------------------------------------------*/
+void __exit smc91111_cleanup_module(void)
+{
+	/* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+	unregister_netdev(&devSMC91111);
+
+	free_irq(devSMC91111.irq, &devSMC91111);
+	release_region(devSMC91111.base_addr, SMC_IO_EXTENT);
+
+	if (devSMC91111.priv)
+		kfree(devSMC91111.priv); /* Kernel 2.4 Changes - Pramod */
+}
+
+//#endif /* MODULE */
+
+module_init(smc91111_init_module);
+module_exit(smc91111_cleanup_module);
+
+#ifdef CONFIG_SYSCTL
+
+
+/*------------------------------------------------------------
+ . Modify a bit in the LAN91C111 register set
+ .-------------------------------------------------------------*/
+static word smc_modify_regbit(int bank, int ioaddr, int reg,
+	unsigned int bit, int val)
+{
+	word regval;
+
+	SMC_SELECT_BANK( bank );
+
+	regval = inw( ioaddr+reg );
+	if (val)
+		regval |= bit;
+	else
+		regval &= ~bit;
+
+	outw( regval, ioaddr );
+	return(regval);
+}
+
+
+/*------------------------------------------------------------
+ . Retrieve a bit in the LAN91C111 register set
+ .-------------------------------------------------------------*/
+static int smc_get_regbit(int bank, int ioaddr, int reg, unsigned int bit)
+{
+	SMC_SELECT_BANK( bank );
+	if ( inw( ioaddr+reg ) & bit)
+		return(1);
+	else
+		return(0);
+}
+
+
+/*------------------------------------------------------------
+ . Modify a LAN91C111 register (word access only)
+ .-------------------------------------------------------------*/
+static void smc_modify_reg(int bank, int ioaddr, int reg, word val)
+{
+	SMC_SELECT_BANK( bank );
+	outw( val, ioaddr+reg );
+}
+
+
+/*------------------------------------------------------------
+ . Retrieve a LAN91C111 register (word access only)
+ .-------------------------------------------------------------*/
+static int smc_get_reg(int bank, int ioaddr, int reg)
+{
+	SMC_SELECT_BANK( bank );
+	return(inw( ioaddr+reg ));
+}
+
+
+static const char smc_info_string[] =
+"\n"
+"info           Provides this information blurb\n"
+"swver          Prints the software version information of this driver\n"
+"autoneg        Auto-negotiate Mode = 1\n"
+"rspeed         Requested Speed, 100=100Mbps, 10=10Mpbs\n"
+"rfduplx        Requested Full Duplex Operation\n"
+"aspeed         Actual Speed, 100=100Mbps, 10=10Mpbs\n"
+"afduplx        Actual Full Duplex Operation\n"
+"lnkfail        PHY Link Failure when 1\n"
+"miiop          External MII when 1, Internal PHY when 0\n"
+"swfdup         Switched Full Duplex Mode (allowed only in MII operation)\n"
+"ephloop        EPH Block Loopback\n"
+"forcol         Force a collision\n"
+"filtcar        Filter leading edge of carrier sense for 12 bit times\n"
+"freemem        Free buffer memory in bytes\n"
+"totmem         Total buffer memory in bytes\n"
+"leda           Output of LED-A (green)\n"
+"ledb           Output of LED-B (yellow)\n"
+"chiprev        Revision ID of the LAN91C111 chip\n"
+"";
+
+/*------------------------------------------------------------
+ . Sysctl handler for all integer parameters
+ .-------------------------------------------------------------*/
+static int smc_sysctl_handler(ctl_table *ctl, int write, struct file * filp,
+				void *buffer, size_t *lenp)
+{
+	struct net_device *dev = (struct net_device*)ctl->extra1;
+	struct smc_local *lp = (struct smc_local *)ctl->extra2;
+	int ioaddr = dev->base_addr;
+	int *valp = ctl->data;
+	int val;
+	int ret;
+
+	// Update parameters from the real registers
+	switch (ctl->ctl_name)
+	{
+	case CTL_SMC_FORCOL:
+		*valp = smc_get_regbit(0, ioaddr, TCR_REG, TCR_FORCOL);
+		break;
+
+	case CTL_SMC_FREEMEM:
+		*valp = ( (word)smc_get_reg(0, ioaddr, MIR_REG) >> 8 )
+			* LAN91C111_MEMORY_MULTIPLIER;
+		break;
+
+
+	case CTL_SMC_TOTMEM:
+		*valp = ( smc_get_reg(0, ioaddr, MIR_REG) & (word)0x00ff )
+			* LAN91C111_MEMORY_MULTIPLIER;
+		break;
+
+	case CTL_SMC_CHIPREV:
+		*valp = smc_get_reg(3, ioaddr, REV_REG);
+		break;
+
+	case CTL_SMC_AFDUPLX:
+		*valp = (lp->lastPhy18 & PHY_INT_DPLXDET) ? 1 : 0;
+		break;
+
+	case CTL_SMC_ASPEED:
+		*valp = (lp->lastPhy18 & PHY_INT_SPDDET) ? 100 : 10;
+		break;
+
+	case CTL_SMC_LNKFAIL:
+		*valp = (lp->lastPhy18 & PHY_INT_LNKFAIL) ? 1 : 0;
+		break;
+
+	case CTL_SMC_LEDA:
+		*valp = (lp->rpc_cur_mode >> RPC_LSXA_SHFT) & (word)0x0007;
+		break;
+
+	case CTL_SMC_LEDB:
+		*valp = (lp->rpc_cur_mode >> RPC_LSXB_SHFT) & (word)0x0007;
+		break;
+
+	case CTL_SMC_MIIOP:
+		*valp = smc_get_regbit(1, ioaddr, CONFIG_REG, CONFIG_EXT_PHY);
+		break;
+
+#ifdef SMC_DEBUG
+	case CTL_SMC_REG_BSR:	// Bank Select
+		*valp = smc_get_reg(0, ioaddr, BSR_REG);
+		break;
+
+	case CTL_SMC_REG_TCR:	// Transmit Control
+		*valp = smc_get_reg(0, ioaddr, TCR_REG);
+		break;
+
+	case CTL_SMC_REG_ESR:	// EPH Status
+		*valp = smc_get_reg(0, ioaddr, EPH_STATUS_REG);
+		break;
+
+	case CTL_SMC_REG_RCR:	// Receive Control
+		*valp = smc_get_reg(0, ioaddr, RCR_REG);
+		break;
+
+	case CTL_SMC_REG_CTRR:	// Counter
+		*valp = smc_get_reg(0, ioaddr, COUNTER_REG);
+		break;
+
+	case CTL_SMC_REG_MIR:	// Memory Information
+		*valp = smc_get_reg(0, ioaddr, MIR_REG);
+		break;
+
+	case CTL_SMC_REG_RPCR:	// Receive/Phy Control
+		*valp = smc_get_reg(0, ioaddr, RPC_REG);
+		break;
+
+	case CTL_SMC_REG_CFGR:	// Configuration
+		*valp = smc_get_reg(1, ioaddr, CONFIG_REG);
+		break;
+
+	case CTL_SMC_REG_BAR:	// Base Address
+		*valp = smc_get_reg(1, ioaddr, BASE_REG);
+		break;
+
+	case CTL_SMC_REG_IAR0:	// Individual Address
+		*valp = smc_get_reg(1, ioaddr, ADDR0_REG);
+		break;
+
+	case CTL_SMC_REG_IAR1:	// Individual Address
+		*valp = smc_get_reg(1, ioaddr, ADDR1_REG);
+		break;
+
+	case CTL_SMC_REG_IAR2:	// Individual Address
+		*valp = smc_get_reg(1, ioaddr, ADDR2_REG);
+		break;
+
+	case CTL_SMC_REG_GPR:	// General Purpose
+		*valp = smc_get_reg(1, ioaddr, GP_REG);
+		break;
+
+	case CTL_SMC_REG_CTLR:	// Control
+		*valp = smc_get_reg(1, ioaddr, CTL_REG);
+		break;
+
+	case CTL_SMC_REG_MCR:	// MMU Command
+		*valp = smc_get_reg(2, ioaddr, MMU_CMD_REG);
+		break;
+
+	case CTL_SMC_REG_PNR:	// Packet Number
+		*valp = smc_get_reg(2, ioaddr, PN_REG);
+		break;
+
+	case CTL_SMC_REG_FPR:	// Allocation Result/FIFO Ports
+		*valp = smc_get_reg(2, ioaddr, RXFIFO_REG);
+		break;
+
+	case CTL_SMC_REG_PTR:	// Pointer
+		*valp = smc_get_reg(2, ioaddr, PTR_REG);
+		break;
+
+	case CTL_SMC_REG_DR:	// Data 
+		*valp = smc_get_reg(2, ioaddr, DATA_REG);
+		break;
+
+	case CTL_SMC_REG_ISR:	// Interrupt Status/Mask
+		*valp = smc_get_reg(2, ioaddr, INT_REG);
+		break;
+
+	case CTL_SMC_REG_MTR1:	// Multicast Table Entry 1
+		*valp = smc_get_reg(3, ioaddr, MCAST_REG1);
+		break;
+
+	case CTL_SMC_REG_MTR2:	// Multicast Table Entry 2
+		*valp = smc_get_reg(3, ioaddr, MCAST_REG2);
+		break;
+
+	case CTL_SMC_REG_MTR3:	// Multicast Table Entry 3
+		*valp = smc_get_reg(3, ioaddr, MCAST_REG3);
+		break;
+
+	case CTL_SMC_REG_MTR4:	// Multicast Table Entry 4
+		*valp = smc_get_reg(3, ioaddr, MCAST_REG4);
+		break;
+
+	case CTL_SMC_REG_MIIR:	// Management Interface
+		*valp = smc_get_reg(3, ioaddr, MII_REG);
+		break;
+
+	case CTL_SMC_REG_REVR:	// Revision
+		*valp = smc_get_reg(3, ioaddr, REV_REG);
+		break;
+
+	case CTL_SMC_REG_ERCVR:	// Early RCV
+		*valp = smc_get_reg(3, ioaddr, ERCV_REG);
+		break;
+
+	case CTL_SMC_REG_EXTR:	// External
+		*valp = smc_get_reg(7, ioaddr, EXT_REG);
+		break;
+
+	case CTL_SMC_PHY_CTRL:
+		*valp = smc_read_phy_register(ioaddr, lp->phyaddr,
+			PHY_CNTL_REG);
+		break;
+
+	case CTL_SMC_PHY_STAT:
+		*valp = smc_read_phy_register(ioaddr, lp->phyaddr,
+			PHY_STAT_REG);
+		break;
+
+	case CTL_SMC_PHY_ID1:
+		*valp = smc_read_phy_register(ioaddr, lp->phyaddr,
+			PHY_ID1_REG);
+		break;
+
+	case CTL_SMC_PHY_ID2:
+		*valp = smc_read_phy_register(ioaddr, lp->phyaddr,
+			PHY_ID2_REG);
+		break;
+
+	case CTL_SMC_PHY_ADC:
+		*valp = smc_read_phy_register(ioaddr, lp->phyaddr,
+			PHY_AD_REG);
+		break;
+
+	case CTL_SMC_PHY_REMC:
+		*valp = smc_read_phy_register(ioaddr, lp->phyaddr,
+			PHY_RMT_REG);
+		break;
+
+	case CTL_SMC_PHY_CFG1:
+		*valp = smc_read_phy_register(ioaddr, lp->phyaddr,
+			PHY_CFG1_REG);
+		break;
+
+	case CTL_SMC_PHY_CFG2:
+		*valp = smc_read_phy_register(ioaddr, lp->phyaddr,
+			PHY_CFG2_REG);
+		break;
+
+	case CTL_SMC_PHY_INT:
+		*valp = smc_read_phy_register(ioaddr, lp->phyaddr,
+			PHY_INT_REG);
+		break;
+
+	case CTL_SMC_PHY_MASK:
+		*valp = smc_read_phy_register(ioaddr, lp->phyaddr,
+			PHY_MASK_REG);
+		break;
+
+#endif // SMC_DEBUG
+
+	default:
+		// Just ignore unsupported parameters
+		break;
+	}
+
+	// Save old state
+	val = *valp;
+
+	// Perform the generic integer operation	
+	if ((ret = proc_dointvec(ctl, write, filp, buffer, lenp)) != 0)
+		return(ret);
+
+	// Write changes out to the registers
+	if (write && *valp != val) {
+
+		val = *valp;
+		switch (ctl->ctl_name) {
+
+		case CTL_SMC_SWFDUP:
+			if (val)
+				lp->tcr_cur_mode |= TCR_SWFDUP;
+			else
+				lp->tcr_cur_mode &= ~TCR_SWFDUP;
+
+			smc_modify_regbit(0, ioaddr, TCR_REG, TCR_SWFDUP, val);
+			break;
+
+		case CTL_SMC_EPHLOOP:
+			if (val)
+				lp->tcr_cur_mode |= TCR_EPH_LOOP;
+			else
+				lp->tcr_cur_mode &= ~TCR_EPH_LOOP;
+
+			smc_modify_regbit(0, ioaddr, TCR_REG, TCR_EPH_LOOP, val);
+			break;
+
+		case CTL_SMC_FORCOL:
+			if (val)
+				lp->tcr_cur_mode |= TCR_FORCOL;
+			else
+				lp->tcr_cur_mode &= ~TCR_FORCOL;
+
+			// Update the EPH block
+			smc_modify_regbit(0, ioaddr, TCR_REG, TCR_FORCOL, val);
+			break;
+
+		case CTL_SMC_FILTCAR:
+			if (val)
+				lp->rcr_cur_mode |= RCR_FILT_CAR;
+			else
+				lp->rcr_cur_mode &= ~RCR_FILT_CAR;
+
+			// Update the EPH block
+			smc_modify_regbit(0, ioaddr, RCR_REG, RCR_FILT_CAR, val);
+			break;
+
+		case CTL_SMC_RFDUPLX:
+			// Disallow changes if in auto-negotiation mode
+			if (lp->ctl_autoneg)
+				break;
+
+			if (val)
+				{
+				lp->rpc_cur_mode |= RPC_DPLX;
+				}
+			else
+				{
+				lp->rpc_cur_mode &= ~RPC_DPLX;
+				}
+
+			// Reconfigure the PHY
+			smc_phy_configure(dev);
+
+			break;
+
+		case CTL_SMC_RSPEED:
+			// Disallow changes if in auto-negotiation mode
+			if (lp->ctl_autoneg)
+				break;
+
+			if (val > 10)
+				lp->rpc_cur_mode |= RPC_SPEED;
+			else
+				lp->rpc_cur_mode &= ~RPC_SPEED;
+
+			// Reconfigure the PHY
+			smc_phy_configure(dev);
+
+			break;
+
+		case CTL_SMC_AUTONEG:
+			if (val)
+				lp->rpc_cur_mode |= RPC_ANEG;
+			else
+				lp->rpc_cur_mode &= ~RPC_ANEG;
+
+			// Reconfigure the PHY
+			smc_phy_configure(dev);
+
+			break;
+
+		case CTL_SMC_LEDA:
+			val &= 0x07; // Restrict to 3 ls bits
+			lp->rpc_cur_mode &= ~(word)(0x07<<RPC_LSXA_SHFT);
+			lp->rpc_cur_mode |= (word)(val<<RPC_LSXA_SHFT);
+
+			// Update the Internal PHY block
+			smc_modify_reg(0, ioaddr, RPC_REG, lp->rpc_cur_mode);
+			break;
+
+		case CTL_SMC_LEDB:
+			val &= 0x07; // Restrict to 3 ls bits
+			lp->rpc_cur_mode &= ~(word)(0x07<<RPC_LSXB_SHFT);
+			lp->rpc_cur_mode |= (word)(val<<RPC_LSXB_SHFT);
+
+			// Update the Internal PHY block
+			smc_modify_reg(0, ioaddr, RPC_REG, lp->rpc_cur_mode);
+			break;
+
+		case CTL_SMC_MIIOP:
+			// Update the Internal PHY block
+			smc_modify_regbit(1, ioaddr, CONFIG_REG,
+				CONFIG_EXT_PHY, val);
+			break;
+
+#ifdef SMC_DEBUG
+		case CTL_SMC_REG_BSR:	// Bank Select
+			smc_modify_reg(0, ioaddr, BSR_REG, val);
+			break;
+
+		case CTL_SMC_REG_TCR:	// Transmit Control
+			smc_modify_reg(0, ioaddr, TCR_REG, val);
+			break;
+
+		case CTL_SMC_REG_ESR:	// EPH Status
+			smc_modify_reg(0, ioaddr, EPH_STATUS_REG, val);
+			break;
+
+		case CTL_SMC_REG_RCR:	// Receive Control
+			smc_modify_reg(0, ioaddr, RCR_REG, val);
+			break;
+
+		case CTL_SMC_REG_CTRR:	// Counter
+			smc_modify_reg(0, ioaddr, COUNTER_REG, val);
+			break;
+
+		case CTL_SMC_REG_MIR:	// Memory Information
+			smc_modify_reg(0, ioaddr, MIR_REG, val);
+			break;
+
+		case CTL_SMC_REG_RPCR:	// Receive/Phy Control
+			smc_modify_reg(0, ioaddr, RPC_REG, val);
+			break;
+
+		case CTL_SMC_REG_CFGR:	// Configuration
+			smc_modify_reg(1, ioaddr, CONFIG_REG, val);
+			break;
+
+		case CTL_SMC_REG_BAR:	// Base Address
+			smc_modify_reg(1, ioaddr, BASE_REG, val);
+			break;
+
+		case CTL_SMC_REG_IAR0:	// Individual Address
+			smc_modify_reg(1, ioaddr, ADDR0_REG, val);
+			break;
+
+		case CTL_SMC_REG_IAR1:	// Individual Address
+			smc_modify_reg(1, ioaddr, ADDR1_REG, val);
+			break;
+
+		case CTL_SMC_REG_IAR2:	// Individual Address
+			smc_modify_reg(1, ioaddr, ADDR2_REG, val);
+			break;
+
+		case CTL_SMC_REG_GPR:	// General Purpose
+			smc_modify_reg(1, ioaddr, GP_REG, val);
+			break;
+
+		case CTL_SMC_REG_CTLR:	// Control
+			smc_modify_reg(1, ioaddr, CTL_REG, val);
+			break;
+
+		case CTL_SMC_REG_MCR:	// MMU Command
+			smc_modify_reg(2, ioaddr, MMU_CMD_REG, val);
+			break;
+
+		case CTL_SMC_REG_PNR:	// Packet Number
+			smc_modify_reg(2, ioaddr, PN_REG, val);
+			break;
+
+		case CTL_SMC_REG_FPR:	// Allocation Result/FIFO Ports
+			smc_modify_reg(2, ioaddr, RXFIFO_REG, val);
+			break;
+
+		case CTL_SMC_REG_PTR:	// Pointer
+			smc_modify_reg(2, ioaddr, PTR_REG, val);
+			break;
+
+		case CTL_SMC_REG_DR:	// Data 
+			smc_modify_reg(2, ioaddr, DATA_REG, val);
+			break;
+
+		case CTL_SMC_REG_ISR:	// Interrupt Status/Mask
+			smc_modify_reg(2, ioaddr, INT_REG, val);
+			break;
+
+		case CTL_SMC_REG_MTR1:	// Multicast Table Entry 1
+			smc_modify_reg(3, ioaddr, MCAST_REG1, val);
+			break;
+
+		case CTL_SMC_REG_MTR2:	// Multicast Table Entry 2
+			smc_modify_reg(3, ioaddr, MCAST_REG2, val);
+			break;
+
+		case CTL_SMC_REG_MTR3:	// Multicast Table Entry 3
+			smc_modify_reg(3, ioaddr, MCAST_REG3, val);
+			break;
+
+		case CTL_SMC_REG_MTR4:	// Multicast Table Entry 4
+			smc_modify_reg(3, ioaddr, MCAST_REG4, val);
+			break;
+
+		case CTL_SMC_REG_MIIR:	// Management Interface
+			smc_modify_reg(3, ioaddr, MII_REG, val);
+			break;
+
+		case CTL_SMC_REG_REVR:	// Revision
+			smc_modify_reg(3, ioaddr, REV_REG, val);
+			break;
+
+		case CTL_SMC_REG_ERCVR:	// Early RCV
+			smc_modify_reg(3, ioaddr, ERCV_REG, val);
+			break;
+
+		case CTL_SMC_REG_EXTR:	// External
+			smc_modify_reg(7, ioaddr, EXT_REG, val);
+			break;
+
+		case CTL_SMC_PHY_CTRL:
+			smc_write_phy_register(ioaddr, lp->phyaddr,
+				PHY_CNTL_REG, val);
+			break;
+
+		case CTL_SMC_PHY_STAT:
+			smc_write_phy_register(ioaddr, lp->phyaddr,
+				PHY_STAT_REG, val);
+			break;
+
+		case CTL_SMC_PHY_ID1:
+			smc_write_phy_register(ioaddr, lp->phyaddr,
+				PHY_ID1_REG, val);
+			break;
+
+		case CTL_SMC_PHY_ID2:
+			smc_write_phy_register(ioaddr, lp->phyaddr,
+				PHY_ID2_REG, val);
+			break;
+
+		case CTL_SMC_PHY_ADC:
+			smc_write_phy_register(ioaddr, lp->phyaddr,
+				PHY_AD_REG, val);
+			break;
+
+		case CTL_SMC_PHY_REMC:
+			smc_write_phy_register(ioaddr, lp->phyaddr,
+				PHY_RMT_REG, val);
+			break;
+
+		case CTL_SMC_PHY_CFG1:
+			smc_write_phy_register(ioaddr, lp->phyaddr,
+				PHY_CFG1_REG, val);
+			break;
+
+		case CTL_SMC_PHY_CFG2:
+			smc_write_phy_register(ioaddr, lp->phyaddr,
+				PHY_CFG2_REG, val);
+			break;
+
+		case CTL_SMC_PHY_INT:
+			smc_write_phy_register(ioaddr, lp->phyaddr,
+				PHY_INT_REG, val);
+			break;
+
+		case CTL_SMC_PHY_MASK:
+			smc_write_phy_register(ioaddr, lp->phyaddr,
+				PHY_MASK_REG, val);
+			break;
+
+#endif // SMC_DEBUG
+
+		default:
+			// Just ignore unsupported parameters
+			break;
+		} // end switch
+
+	} // end if
+
+        return ret;
+}
+
+/*------------------------------------------------------------
+ . Sysctl registration function for all parameters (files)
+ .-------------------------------------------------------------*/
+static void smc_sysctl_register(struct net_device *dev)
+{
+	struct smc_local *lp = (struct smc_local *)dev->priv;
+	static int ctl_name = CTL_SMC;
+	ctl_table* ct;
+	int i;
+
+	// Make sure the ctl_tables start out as all zeros
+	memset(lp->root_table, 0, sizeof lp->root_table);
+	memset(lp->eth_table, 0, sizeof lp->eth_table);
+	memset(lp->param_table, 0, sizeof lp->param_table);
+
+	// Initialize the root table
+	ct = lp->root_table;
+	ct->ctl_name = CTL_DEV;
+	ct->procname = "dev";
+	ct->maxlen = 0;
+	ct->mode = 0555;
+	ct->child = lp->eth_table;
+	// remaining fields are zero
+
+	// Initialize the ethX table (this device's table)
+	ct = lp->eth_table;
+	ct->ctl_name = ctl_name++; // Must be unique
+	ct->procname = dev->name;
+	ct->maxlen = 0;
+	ct->mode = 0555;
+	ct->child = lp->param_table;
+	// remaining fields are zero
+
+	// Initialize the parameter (files) table
+	// Make sure the last entry remains null
+	ct = lp->param_table;
+	for (i = 0; i < (CTL_SMC_LAST_ENTRY-1); ++i)
+		{
+		// Initialize fields common to all table entries
+		ct[i].proc_handler = smc_sysctl_handler;
+		ct[i].extra1 = (void*)dev; // Save our device pointer
+		ct[i].extra2 = (void*)lp;  // Save our smc_local data pointer
+		}
+
+	// INFO - this is our only string parameter
+	i = 0;
+	ct[i].proc_handler = proc_dostring; // use default handler
+	ct[i].ctl_name = CTL_SMC_INFO;
+	ct[i].procname = "info";
+	ct[i].data = (void*)smc_info_string;
+	ct[i].maxlen = sizeof smc_info_string;
+	ct[i].mode = 0444; // Read only
+
+	// SWVER
+	++i;
+	ct[i].proc_handler = proc_dostring; // use default handler
+	ct[i].ctl_name = CTL_SMC_SWVER;
+	ct[i].procname = "swver";
+	ct[i].data = (void*)version;
+	ct[i].maxlen = sizeof version;
+	ct[i].mode = 0444; // Read only
+
+	// SWFDUP
+	++i;
+	ct[i].ctl_name = CTL_SMC_SWFDUP;
+	ct[i].procname = "swfdup";
+	ct[i].data = (void*)&(lp->ctl_swfdup);
+	ct[i].maxlen = sizeof lp->ctl_swfdup;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// EPHLOOP
+	++i;
+	ct[i].ctl_name = CTL_SMC_EPHLOOP;
+	ct[i].procname = "ephloop";
+	ct[i].data = (void*)&(lp->ctl_ephloop);
+	ct[i].maxlen = sizeof lp->ctl_ephloop;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// MIIOP
+	++i;
+	ct[i].ctl_name = CTL_SMC_MIIOP;
+	ct[i].procname = "miiop";
+	ct[i].data = (void*)&(lp->ctl_miiop);
+	ct[i].maxlen = sizeof lp->ctl_miiop;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// AUTONEG
+	++i;
+	ct[i].ctl_name = CTL_SMC_AUTONEG;
+	ct[i].procname = "autoneg";
+	ct[i].data = (void*)&(lp->ctl_autoneg);
+	ct[i].maxlen = sizeof lp->ctl_autoneg;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// RFDUPLX
+	++i;
+	ct[i].ctl_name = CTL_SMC_RFDUPLX;
+	ct[i].procname = "rfduplx";
+	ct[i].data = (void*)&(lp->ctl_rfduplx);
+	ct[i].maxlen = sizeof lp->ctl_rfduplx;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// RSPEED
+	++i;
+	ct[i].ctl_name = CTL_SMC_RSPEED;
+	ct[i].procname = "rspeed";
+	ct[i].data = (void*)&(lp->ctl_rspeed);
+	ct[i].maxlen = sizeof lp->ctl_rspeed;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// AFDUPLX
+	++i;
+	ct[i].ctl_name = CTL_SMC_AFDUPLX;
+	ct[i].procname = "afduplx";
+	ct[i].data = (void*)&(lp->ctl_afduplx);
+	ct[i].maxlen = sizeof lp->ctl_afduplx;
+	ct[i].mode = 0444; // Read only
+
+	// ASPEED
+	++i;
+	ct[i].ctl_name = CTL_SMC_ASPEED;
+	ct[i].procname = "aspeed";
+	ct[i].data = (void*)&(lp->ctl_aspeed);
+	ct[i].maxlen = sizeof lp->ctl_aspeed;
+	ct[i].mode = 0444; // Read only
+
+	// LNKFAIL
+	++i;
+	ct[i].ctl_name = CTL_SMC_LNKFAIL;
+	ct[i].procname = "lnkfail";
+	ct[i].data = (void*)&(lp->ctl_lnkfail);
+	ct[i].maxlen = sizeof lp->ctl_lnkfail;
+	ct[i].mode = 0444; // Read only
+
+	// FORCOL
+	++i;
+	ct[i].ctl_name = CTL_SMC_FORCOL;
+	ct[i].procname = "forcol";
+	ct[i].data = (void*)&(lp->ctl_forcol);
+	ct[i].maxlen = sizeof lp->ctl_forcol;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// FILTCAR
+	++i;
+	ct[i].ctl_name = CTL_SMC_FILTCAR;
+	ct[i].procname = "filtcar";
+	ct[i].data = (void*)&(lp->ctl_filtcar);
+	ct[i].maxlen = sizeof lp->ctl_filtcar;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// FREEMEM
+	++i;
+	ct[i].ctl_name = CTL_SMC_FREEMEM;
+	ct[i].procname = "freemem";
+	ct[i].data = (void*)&(lp->ctl_freemem);
+	ct[i].maxlen = sizeof lp->ctl_freemem;
+	ct[i].mode = 0444; // Read only
+
+	// TOTMEM
+	++i;
+	ct[i].ctl_name = CTL_SMC_TOTMEM;
+	ct[i].procname = "totmem";
+	ct[i].data = (void*)&(lp->ctl_totmem);
+	ct[i].maxlen = sizeof lp->ctl_totmem;
+	ct[i].mode = 0444; // Read only
+
+	// LEDA
+	++i;
+	ct[i].ctl_name = CTL_SMC_LEDA;
+	ct[i].procname = "leda";
+	ct[i].data = (void*)&(lp->ctl_leda);
+	ct[i].maxlen = sizeof lp->ctl_leda;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// LEDB
+	++i;
+	ct[i].ctl_name = CTL_SMC_LEDB;
+	ct[i].procname = "ledb";
+	ct[i].data = (void*)&(lp->ctl_ledb);
+	ct[i].maxlen = sizeof lp->ctl_ledb;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// CHIPREV
+	++i;
+	ct[i].ctl_name = CTL_SMC_CHIPREV;
+	ct[i].procname = "chiprev";
+	ct[i].data = (void*)&(lp->ctl_chiprev);
+	ct[i].maxlen = sizeof lp->ctl_chiprev;
+	ct[i].mode = 0444; // Read only
+
+#ifdef SMC_DEBUG
+	// REG_BSR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_BSR;
+	ct[i].procname = "reg_bsr";
+	ct[i].data = (void*)&(lp->ctl_reg_bsr);
+	ct[i].maxlen = sizeof lp->ctl_reg_bsr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_TCR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_TCR;
+	ct[i].procname = "reg_tcr";
+	ct[i].data = (void*)&(lp->ctl_reg_tcr);
+	ct[i].maxlen = sizeof lp->ctl_reg_tcr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_ESR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_ESR;
+	ct[i].procname = "reg_esr";
+	ct[i].data = (void*)&(lp->ctl_reg_esr);
+	ct[i].maxlen = sizeof lp->ctl_reg_esr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_RCR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_RCR;
+	ct[i].procname = "reg_rcr";
+	ct[i].data = (void*)&(lp->ctl_reg_rcr);
+	ct[i].maxlen = sizeof lp->ctl_reg_rcr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_CTRR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_CTRR;
+	ct[i].procname = "reg_ctrr";
+	ct[i].data = (void*)&(lp->ctl_reg_ctrr);
+	ct[i].maxlen = sizeof lp->ctl_reg_ctrr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_MIR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_MIR;
+	ct[i].procname = "reg_mir";
+	ct[i].data = (void*)&(lp->ctl_reg_mir);
+	ct[i].maxlen = sizeof lp->ctl_reg_mir;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_RPCR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_RPCR;
+	ct[i].procname = "reg_rpcr";
+	ct[i].data = (void*)&(lp->ctl_reg_rpcr);
+	ct[i].maxlen = sizeof lp->ctl_reg_rpcr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_CFGR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_CFGR;
+	ct[i].procname = "reg_cfgr";
+	ct[i].data = (void*)&(lp->ctl_reg_cfgr);
+	ct[i].maxlen = sizeof lp->ctl_reg_cfgr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_BAR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_BAR;
+	ct[i].procname = "reg_bar";
+	ct[i].data = (void*)&(lp->ctl_reg_bar);
+	ct[i].maxlen = sizeof lp->ctl_reg_bar;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_IAR0
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_IAR0;
+	ct[i].procname = "reg_iar0";
+	ct[i].data = (void*)&(lp->ctl_reg_iar0);
+	ct[i].maxlen = sizeof lp->ctl_reg_iar0;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_IAR1
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_IAR1;
+	ct[i].procname = "reg_iar1";
+	ct[i].data = (void*)&(lp->ctl_reg_iar1);
+	ct[i].maxlen = sizeof lp->ctl_reg_iar1;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_IAR2
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_IAR2;
+	ct[i].procname = "reg_iar2";
+	ct[i].data = (void*)&(lp->ctl_reg_iar2);
+	ct[i].maxlen = sizeof lp->ctl_reg_iar2;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_GPR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_GPR;
+	ct[i].procname = "reg_gpr";
+	ct[i].data = (void*)&(lp->ctl_reg_gpr);
+	ct[i].maxlen = sizeof lp->ctl_reg_gpr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_CTLR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_CTLR;
+	ct[i].procname = "reg_ctlr";
+	ct[i].data = (void*)&(lp->ctl_reg_ctlr);
+	ct[i].maxlen = sizeof lp->ctl_reg_ctlr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_MCR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_MCR;
+	ct[i].procname = "reg_mcr";
+	ct[i].data = (void*)&(lp->ctl_reg_mcr);
+	ct[i].maxlen = sizeof lp->ctl_reg_mcr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_PNR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_PNR;
+	ct[i].procname = "reg_pnr";
+	ct[i].data = (void*)&(lp->ctl_reg_pnr);
+	ct[i].maxlen = sizeof lp->ctl_reg_pnr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_FPR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_FPR;
+	ct[i].procname = "reg_fpr";
+	ct[i].data = (void*)&(lp->ctl_reg_fpr);
+	ct[i].maxlen = sizeof lp->ctl_reg_fpr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_PTR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_PTR;
+	ct[i].procname = "reg_ptr";
+	ct[i].data = (void*)&(lp->ctl_reg_ptr);
+	ct[i].maxlen = sizeof lp->ctl_reg_ptr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_DR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_DR;
+	ct[i].procname = "reg_dr";
+	ct[i].data = (void*)&(lp->ctl_reg_dr);
+	ct[i].maxlen = sizeof lp->ctl_reg_dr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_ISR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_ISR;
+	ct[i].procname = "reg_isr";
+	ct[i].data = (void*)&(lp->ctl_reg_isr);
+	ct[i].maxlen = sizeof lp->ctl_reg_isr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_MTR1
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_MTR1;
+	ct[i].procname = "reg_mtr1";
+	ct[i].data = (void*)&(lp->ctl_reg_mtr1);
+	ct[i].maxlen = sizeof lp->ctl_reg_mtr1;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_MTR2
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_MTR2;
+	ct[i].procname = "reg_mtr2";
+	ct[i].data = (void*)&(lp->ctl_reg_mtr2);
+	ct[i].maxlen = sizeof lp->ctl_reg_mtr2;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_MTR3
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_MTR3;
+	ct[i].procname = "reg_mtr3";
+	ct[i].data = (void*)&(lp->ctl_reg_mtr3);
+	ct[i].maxlen = sizeof lp->ctl_reg_mtr3;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_MTR4
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_MTR4;
+	ct[i].procname = "reg_mtr4";
+	ct[i].data = (void*)&(lp->ctl_reg_mtr4);
+	ct[i].maxlen = sizeof lp->ctl_reg_mtr4;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_MIIR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_MIIR;
+	ct[i].procname = "reg_miir";
+	ct[i].data = (void*)&(lp->ctl_reg_miir);
+	ct[i].maxlen = sizeof lp->ctl_reg_miir;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_REVR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_REVR;
+	ct[i].procname = "reg_revr";
+	ct[i].data = (void*)&(lp->ctl_reg_revr);
+	ct[i].maxlen = sizeof lp->ctl_reg_revr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_ERCVR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_ERCVR;
+	ct[i].procname = "reg_ercvr";
+	ct[i].data = (void*)&(lp->ctl_reg_ercvr);
+	ct[i].maxlen = sizeof lp->ctl_reg_ercvr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// REG_EXTR
+	++i;
+	ct[i].ctl_name = CTL_SMC_REG_EXTR;
+	ct[i].procname = "reg_extr";
+	ct[i].data = (void*)&(lp->ctl_reg_extr);
+	ct[i].maxlen = sizeof lp->ctl_reg_extr;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// PHY Control
+	++i;
+	ct[i].ctl_name = CTL_SMC_PHY_CTRL;
+	ct[i].procname = "phy_ctrl";
+	ct[i].data = (void*)&(lp->ctl_phy_ctrl);
+	ct[i].maxlen = sizeof lp->ctl_phy_ctrl;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// PHY Status
+	++i;
+	ct[i].ctl_name = CTL_SMC_PHY_STAT;
+	ct[i].procname = "phy_stat";
+	ct[i].data = (void*)&(lp->ctl_phy_stat);
+	ct[i].maxlen = sizeof lp->ctl_phy_stat;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// PHY ID1
+	++i;
+	ct[i].ctl_name = CTL_SMC_PHY_ID1;
+	ct[i].procname = "phy_id1";
+	ct[i].data = (void*)&(lp->ctl_phy_id1);
+	ct[i].maxlen = sizeof lp->ctl_phy_id1;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// PHY ID2
+	++i;
+	ct[i].ctl_name = CTL_SMC_PHY_ID2;
+	ct[i].procname = "phy_id2";
+	ct[i].data = (void*)&(lp->ctl_phy_id2);
+	ct[i].maxlen = sizeof lp->ctl_phy_id2;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// PHY Advertise Capabilities
+	++i;
+	ct[i].ctl_name = CTL_SMC_PHY_ADC;
+	ct[i].procname = "phy_adc";
+	ct[i].data = (void*)&(lp->ctl_phy_adc);
+	ct[i].maxlen = sizeof lp->ctl_phy_adc;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// PHY Remote Capabilities
+	++i;
+	ct[i].ctl_name = CTL_SMC_PHY_REMC;
+	ct[i].procname = "phy_remc";
+	ct[i].data = (void*)&(lp->ctl_phy_remc);
+	ct[i].maxlen = sizeof lp->ctl_phy_remc;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// PHY Configuration 1
+	++i;
+	ct[i].ctl_name = CTL_SMC_PHY_CFG1;
+	ct[i].procname = "phy_cfg1";
+	ct[i].data = (void*)&(lp->ctl_phy_cfg1);
+	ct[i].maxlen = sizeof lp->ctl_phy_cfg1;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// PHY Configuration 2
+	++i;
+	ct[i].ctl_name = CTL_SMC_PHY_CFG2;
+	ct[i].procname = "phy_cfg2";
+	ct[i].data = (void*)&(lp->ctl_phy_cfg2);
+	ct[i].maxlen = sizeof lp->ctl_phy_cfg2;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// PHY Interrupt/Status Output
+	++i;
+	ct[i].ctl_name = CTL_SMC_PHY_INT;
+	ct[i].procname = "phy_int";
+	ct[i].data = (void*)&(lp->ctl_phy_int);
+	ct[i].maxlen = sizeof lp->ctl_phy_int;
+	ct[i].mode = 0644; // Read by all, write by root
+
+	// PHY Interrupt/Status Mask
+	++i;
+	ct[i].ctl_name = CTL_SMC_PHY_MASK;
+	ct[i].procname = "phy_mask";
+	ct[i].data = (void*)&(lp->ctl_phy_mask);
+	ct[i].maxlen = sizeof lp->ctl_phy_mask;
+	ct[i].mode = 0644; // Read by all, write by root
+
+#endif // SMC_DEBUG
+
+	// Register /proc/sys/dev/ethX
+	lp->sysctl_header = register_sysctl_table(lp->root_table, 1);
+}
+
+
+/*------------------------------------------------------------
+ . Sysctl unregistration when driver is closed
+ .-------------------------------------------------------------*/
+static void smc_sysctl_unregister(struct net_device *dev)
+{
+	struct smc_local *lp = (struct smc_local *)dev->priv;
+
+	unregister_sysctl_table(lp->sysctl_header);
+}
+
+#endif /* endif CONFIG_SYSCTL */
+
+
+//---PHY CONTROL AND CONFIGURATION-----------------------------------------
+
+#if (SMC_DEBUG > 2 )
+
+/*------------------------------------------------------------
+ . Debugging function for viewing MII Management serial bitstream
+ .-------------------------------------------------------------*/
+static void smc_dump_mii_stream(byte* bits, int size)
+{
+	int i;
+
+	printk("BIT#:");
+	for (i = 0; i < size; ++i)
+		{
+		printk("%d", i%10);
+		}
+
+	printk("\nMDOE:");
+	for (i = 0; i < size; ++i)
+		{
+		if (bits[i] & MII_MDOE)
+			printk("1");
+		else
+			printk("0");
+		}
+
+	printk("\nMDO :");
+	for (i = 0; i < size; ++i)
+		{
+		if (bits[i] & MII_MDO)
+			printk("1");
+		else
+			printk("0");
+		}
+
+	printk("\nMDI :");
+	for (i = 0; i < size; ++i)
+		{
+		if (bits[i] & MII_MDI)
+			printk("1");
+		else
+			printk("0");
+		}
+
+	printk("\n");
+}
+#endif
+
+/*------------------------------------------------------------
+ . Reads a register from the MII Management serial interface
+ .-------------------------------------------------------------*/
+static word smc_read_phy_register(int ioaddr, byte phyaddr, byte phyreg)
+{
+	int oldBank;
+	int i;
+	byte mask;
+	word mii_reg;
+	byte bits[64];
+	int clk_idx = 0;
+	int input_idx;
+	word phydata;
+
+	// 32 consecutive ones on MDO to establish sync
+	for (i = 0; i < 32; ++i)
+		bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+	// Start code <01>
+	bits[clk_idx++] = MII_MDOE;
+	bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+	// Read command <10>
+	bits[clk_idx++] = MII_MDOE | MII_MDO;
+	bits[clk_idx++] = MII_MDOE;
+
+	// Output the PHY address, msb first
+	mask = (byte)0x10;
+	for (i = 0; i < 5; ++i)
+		{
+		if (phyaddr & mask)
+			bits[clk_idx++] = MII_MDOE | MII_MDO;
+		else
+			bits[clk_idx++] = MII_MDOE;
+
+		// Shift to next lowest bit
+		mask >>= 1;
+		}
+
+	// Output the phy register number, msb first
+	mask = (byte)0x10;
+	for (i = 0; i < 5; ++i)
+		{
+		if (phyreg & mask)
+			bits[clk_idx++] = MII_MDOE | MII_MDO;
+		else
+			bits[clk_idx++] = MII_MDOE;
+
+		// Shift to next lowest bit
+		mask >>= 1;
+		}
+
+	// Tristate and turnaround (2 bit times)
+	bits[clk_idx++] = 0;
+	//bits[clk_idx++] = 0;
+
+	// Input starts at this bit time
+	input_idx = clk_idx;
+
+	// Will input 16 bits
+	for (i = 0; i < 16; ++i)
+		bits[clk_idx++] = 0;
+
+	// Final clock bit
+	bits[clk_idx++] = 0;
+
+	// Save the current bank
+	oldBank = inw( ioaddr+BANK_SELECT );
+
+	// Select bank 3
+	SMC_SELECT_BANK( 3 );
+
+	// Get the current MII register value
+	mii_reg = inw( ioaddr+MII_REG );
+
+	// Turn off all MII Interface bits
+	mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO);
+
+	// Clock all 64 cycles
+	for (i = 0; i < sizeof bits; ++i)
+		{
+		// Clock Low - output data
+		outw( mii_reg | bits[i], ioaddr+MII_REG );
+		udelay(50);
+
+
+		// Clock Hi - input data
+		outw( mii_reg | bits[i] | MII_MCLK, ioaddr+MII_REG );
+		udelay(50);
+		bits[i] |= inw( ioaddr+MII_REG ) & MII_MDI;
+		}
+
+	// Return to idle state
+	// Set clock to low, data to low, and output tristated
+	outw( mii_reg, ioaddr+MII_REG );
+	udelay(50);
+
+	// Restore original bank select
+	SMC_SELECT_BANK( oldBank );
+
+	// Recover input data
+	phydata = 0;
+	for (i = 0; i < 16; ++i)
+		{
+		phydata <<= 1;
+
+		if (bits[input_idx++] & MII_MDI)
+			phydata |= 0x0001;
+		}
+
+#if (SMC_DEBUG > 2 )
+	printk("smc_read_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n",
+		phyaddr, phyreg, phydata);
+	smc_dump_mii_stream(bits, sizeof bits);
+#endif
+
+	return(phydata);	
+}
+
+
+/*------------------------------------------------------------
+ . Writes a register to the MII Management serial interface
+ .-------------------------------------------------------------*/
+static void smc_write_phy_register(int ioaddr,
+	byte phyaddr, byte phyreg, word phydata)
+{
+	int oldBank;
+	int i;
+	word mask;
+	word mii_reg;
+	byte bits[65];
+	int clk_idx = 0;
+
+	// 32 consecutive ones on MDO to establish sync
+	for (i = 0; i < 32; ++i)
+		bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+	// Start code <01>
+	bits[clk_idx++] = MII_MDOE;
+	bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+	// Write command <01>
+	bits[clk_idx++] = MII_MDOE;
+	bits[clk_idx++] = MII_MDOE | MII_MDO;
+
+	// Output the PHY address, msb first
+	mask = (byte)0x10;
+	for (i = 0; i < 5; ++i)
+		{
+		if (phyaddr & mask)
+			bits[clk_idx++] = MII_MDOE | MII_MDO;
+		else
+			bits[clk_idx++] = MII_MDOE;
+
+		// Shift to next lowest bit
+		mask >>= 1;
+		}
+
+	// Output the phy register number, msb first
+	mask = (byte)0x10;
+	for (i = 0; i < 5; ++i)
+		{
+		if (phyreg & mask)
+			bits[clk_idx++] = MII_MDOE | MII_MDO;
+		else
+			bits[clk_idx++] = MII_MDOE;
+
+		// Shift to next lowest bit
+		mask >>= 1;
+		}
+
+	// Tristate and turnaround (2 bit times)
+	bits[clk_idx++] = 0;
+	bits[clk_idx++] = 0;
+
+	// Write out 16 bits of data, msb first
+	mask = 0x8000;
+	for (i = 0; i < 16; ++i)
+		{
+		if (phydata & mask)
+			bits[clk_idx++] = MII_MDOE | MII_MDO;
+		else
+			bits[clk_idx++] = MII_MDOE;
+
+		// Shift to next lowest bit
+		mask >>= 1;
+		}
+
+	// Final clock bit (tristate)
+	bits[clk_idx++] = 0;
+
+	// Save the current bank
+	oldBank = inw( ioaddr+BANK_SELECT );
+
+	// Select bank 3
+	SMC_SELECT_BANK( 3 );
+
+	// Get the current MII register value
+	mii_reg = inw( ioaddr+MII_REG );
+
+	// Turn off all MII Interface bits
+	mii_reg &= ~(MII_MDOE|MII_MCLK|MII_MDI|MII_MDO);
+
+	// Clock all cycles
+	for (i = 0; i < sizeof bits; ++i)
+		{
+		// Clock Low - output data
+		outw( mii_reg | bits[i], ioaddr+MII_REG );
+		udelay(50);
+
+
+		// Clock Hi - input data
+		outw( mii_reg | bits[i] | MII_MCLK, ioaddr+MII_REG );
+		udelay(50);
+		bits[i] |= inw( ioaddr+MII_REG ) & MII_MDI;
+		}
+
+	// Return to idle state
+	// Set clock to low, data to low, and output tristated
+	outw( mii_reg, ioaddr+MII_REG );
+	udelay(50);
+
+	// Restore original bank select
+	SMC_SELECT_BANK( oldBank );
+
+#if (SMC_DEBUG > 2 )
+	printk("smc_write_phy_register(): phyaddr=%x,phyreg=%x,phydata=%x\n",
+		phyaddr, phyreg, phydata);
+	smc_dump_mii_stream(bits, sizeof bits);
+#endif
+}
+
+
+/*------------------------------------------------------------
+ . Finds and reports the PHY address
+ .-------------------------------------------------------------*/
+static int smc_detect_phy(struct net_device* dev)
+{
+	struct smc_local *lp = (struct smc_local *)dev->priv;
+	int ioaddr = dev->base_addr;
+	word phy_id1;
+	word phy_id2;
+	int phyaddr;
+	int found = 0;
+
+	PRINTK3("%s:smc_detect_phy()\n", dev->name);
+
+	// Scan all 32 PHY addresses if necessary
+	for (phyaddr = 0; phyaddr < 32; ++phyaddr)
+		{
+		// Read the PHY identifiers
+		phy_id1  = smc_read_phy_register(ioaddr, phyaddr, PHY_ID1_REG);
+		phy_id2  = smc_read_phy_register(ioaddr, phyaddr, PHY_ID2_REG);
+
+		PRINTK3("%s: phy_id1=%x, phy_id2=%x\n",
+			dev->name, phy_id1, phy_id2);
+
+		// Make sure it is a valid identifier	
+		if ((phy_id2 > 0x0000) && (phy_id2 < 0xffff) &&
+		    (phy_id1 > 0x0000) && (phy_id1 < 0xffff))
+			{
+			if ((phy_id1 != 0x8000) && (phy_id2 != 0x8000))
+				{
+				// Save the PHY's address
+				lp->phyaddr = phyaddr;
+				found = 1;
+				break;
+				}
+			}
+		}
+
+	if (!found)
+		{
+		PRINTK("%s: No PHY found\n", dev->name);
+		return(0);
+		}
+
+	// Set the PHY type
+	if ( (phy_id1 == 0x0016) && ((phy_id2 & 0xFFF0) == 0xF840 ) )
+		{
+		lp->phytype = PHY_LAN83C183;
+		PRINTK("%s: PHY=LAN83C183 (LAN91C111 Internal)\n", dev->name);
+		}
+
+	if ( (phy_id1 == 0x0282) && ((phy_id2 & 0xFFF0) == 0x1C50) )
+		{
+		lp->phytype = PHY_LAN83C180;
+		PRINTK("%s: PHY=LAN83C180\n", dev->name);
+		}
+
+	return(1);
+}
+
+/*------------------------------------------------------------
+ . Waits the specified number of milliseconds - kernel friendly
+ .-------------------------------------------------------------*/
+static void smc_wait_ms(unsigned int ms)
+{
+
+	if (!in_interrupt())
+		{
+		current->state = TASK_UNINTERRUPTIBLE;
+		schedule_timeout(1 + ms * HZ / 1000);
+		}
+	else
+		{
+		current->state = TASK_INTERRUPTIBLE;
+		schedule_timeout(1 + ms * HZ / 1000);
+		current->state = TASK_RUNNING;
+		}
+}
+
+/*------------------------------------------------------------
+ . Sets the PHY to a configuration as determined by the user
+ .-------------------------------------------------------------*/
+static int smc_phy_fixed(struct net_device* dev)
+{
+	int ioaddr = dev->base_addr;
+	struct smc_local *lp = (struct smc_local *)dev->priv;
+	byte phyaddr = lp->phyaddr;
+	word my_fixed_caps;
+	word cfg1;
+
+	PRINTK3("%s:smc_phy_fixed()\n", dev->name);
+
+	// Enter Link Disable state
+	cfg1 = smc_read_phy_register(ioaddr, phyaddr, PHY_CFG1_REG);
+	cfg1 |= PHY_CFG1_LNKDIS;
+	smc_write_phy_register(ioaddr, phyaddr, PHY_CFG1_REG, cfg1);
+
+	// Set our fixed capabilities
+	// Disable auto-negotiation
+	my_fixed_caps = 0;
+
+	if (lp->ctl_rfduplx)
+		my_fixed_caps |= PHY_CNTL_DPLX;
+
+	if (lp->ctl_rspeed == 100)
+		my_fixed_caps |= PHY_CNTL_SPEED;
+
+	// Write our capabilities to the phy control register
+	smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, my_fixed_caps);
+
+	// Re-Configure the Receive/Phy Control register
+	outw( lp->rpc_cur_mode, ioaddr + RPC_REG );
+
+	// Success
+	return(1);
+}
+
+
+/*------------------------------------------------------------
+ . Configures the specified PHY using Autonegotiation. Calls
+ . smc_phy_fixed() if the user has requested a certain config.
+ .-------------------------------------------------------------*/
+static void smc_phy_configure(struct net_device* dev)
+{
+	int ioaddr = dev->base_addr;
+	struct smc_local *lp = (struct smc_local *)dev->priv;
+	int timeout;
+	byte phyaddr;
+	word my_phy_caps; // My PHY capabilities
+	word my_ad_caps; // My Advertised capabilities
+	word status;
+	int failed = 0;
+
+	PRINTK3("%s:smc_program_phy()\n", dev->name);
+
+	// Set the blocking flag
+	lp->autoneg_active = 1;
+
+	// Find the address and type of our phy
+	if (!smc_detect_phy(dev))
+		{
+		goto smc_phy_configure_exit;
+		}
+
+	// Get the detected phy address
+	phyaddr = lp->phyaddr;
+
+	// Reset the PHY, setting all other bits to zero
+	smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, PHY_CNTL_RST);
+
+	// Wait for the reset to complete, or time out
+	timeout = 6; // Wait up to 3 seconds
+	while (timeout--)
+		{
+		if (!(smc_read_phy_register(ioaddr, phyaddr, PHY_CNTL_REG)
+		    & PHY_CNTL_RST))
+			{
+			// reset complete
+			break;
+			}
+
+		smc_wait_ms(500); // wait 500 millisecs
+		if (signal_pending(current)) // Exit anyway if signaled
+			{
+			PRINTK2("%s:PHY reset interrupted by signal\n",
+				dev->name);
+			timeout = 0;
+			break;
+			}
+		}
+
+	if (timeout < 1)
+		{
+		PRINTK2("%s:PHY reset timed out\n", dev->name);
+		goto smc_phy_configure_exit;
+		}
+
+	// Read PHY Register 18, Status Output
+	lp->lastPhy18 = smc_read_phy_register(ioaddr, phyaddr, PHY_INT_REG);
+
+	// Enable PHY Interrupts (for register 18)
+	// Interrupts listed here are disabled
+	smc_write_phy_register(ioaddr, phyaddr, PHY_MASK_REG, 
+		PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD |
+		PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB |
+		PHY_INT_SPDDET | PHY_INT_DPLXDET);
+
+	/* Configure the Receive/Phy Control register */
+	SMC_SELECT_BANK( 0 );
+	outw( lp->rpc_cur_mode, ioaddr + RPC_REG );
+
+	// Copy our capabilities from PHY_STAT_REG to PHY_AD_REG
+	my_phy_caps = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG);
+	my_ad_caps  = PHY_AD_CSMA; // I am CSMA capable
+
+	if (my_phy_caps & PHY_STAT_CAP_T4)
+		my_ad_caps |= PHY_AD_T4;
+
+	if (my_phy_caps & PHY_STAT_CAP_TXF)
+		my_ad_caps |= PHY_AD_TX_FDX;
+
+	if (my_phy_caps & PHY_STAT_CAP_TXH)
+		my_ad_caps |= PHY_AD_TX_HDX;
+
+	if (my_phy_caps & PHY_STAT_CAP_TF)
+		my_ad_caps |= PHY_AD_10_FDX;
+
+	if (my_phy_caps & PHY_STAT_CAP_TH)
+		my_ad_caps |= PHY_AD_10_HDX;
+
+	// Disable capabilities not selected by our user
+	if (lp->ctl_rspeed != 100)
+		{
+		my_ad_caps &= ~(PHY_AD_T4|PHY_AD_TX_FDX|PHY_AD_TX_HDX);
+		}
+
+	if (!lp->ctl_rfduplx)
+		{
+		my_ad_caps &= ~(PHY_AD_TX_FDX|PHY_AD_10_FDX);
+		}
+
+	// Update our Auto-Neg Advertisement Register
+	smc_write_phy_register(ioaddr, phyaddr, PHY_AD_REG, my_ad_caps);
+
+	PRINTK2("%s:phy caps=%x\n", dev->name, my_phy_caps);
+	PRINTK2("%s:phy advertised caps=%x\n", dev->name, my_ad_caps);
+
+	// If the user requested no auto neg, then go set his request
+	if (!(lp->ctl_autoneg))
+		{
+		smc_phy_fixed(dev);
+		goto smc_phy_configure_exit;
+		}
+
+	// Restart auto-negotiation process in order to advertise my caps
+	smc_write_phy_register( ioaddr, phyaddr, PHY_CNTL_REG,
+		PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST );
+
+	// Wait for the auto-negotiation to complete.  This may take from
+	// 2 to 3 seconds.
+	// Wait for the reset to complete, or time out
+	timeout = 20; // Wait up to 10 seconds
+	while (timeout--)
+		{
+		status = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG);
+		if (status & PHY_STAT_ANEG_ACK)
+			{
+			// auto-negotiate complete
+			break;
+			}
+
+		smc_wait_ms(500); // wait 500 millisecs
+		if (signal_pending(current)) // Exit anyway if signaled
+			{
+			printk(KERN_DEBUG
+				"%s:PHY auto-negotiate interrupted by signal\n",
+				dev->name);
+			timeout = 0;
+			break;
+			}
+
+		// Restart auto-negotiation if remote fault
+		if (status & PHY_STAT_REM_FLT)
+			{
+			PRINTK2("%s:PHY remote fault detected\n", dev->name);
+
+			// Restart auto-negotiation
+			PRINTK2("%s:PHY restarting auto-negotiation\n",
+				dev->name);
+			smc_write_phy_register( ioaddr, phyaddr, PHY_CNTL_REG,
+				PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST |
+				PHY_CNTL_SPEED | PHY_CNTL_DPLX);
+			}
+		}
+
+	if (timeout < 1)
+		{
+		printk(KERN_DEBUG "%s:PHY auto-negotiate timed out\n",
+			dev->name);
+		PRINTK2("%s:PHY auto-negotiate timed out\n", dev->name);
+		failed = 1;
+		}
+
+	// Fail if we detected an auto-negotiate remote fault
+	if (status & PHY_STAT_REM_FLT)
+		{
+		printk(KERN_DEBUG "%s:PHY remote fault detected\n", dev->name);
+		PRINTK2("%s:PHY remote fault detected\n", dev->name);
+		failed = 1;
+		}
+
+	// The smc_phy_interrupt() routine will be called to update lastPhy18
+
+	// Set our sysctl parameters to match auto-negotiation results
+	if ( lp->lastPhy18 & PHY_INT_SPDDET )
+		{
+		PRINTK2("%s:PHY 100BaseT\n", dev->name);
+		lp->rpc_cur_mode |= RPC_SPEED;
+		}
+	else
+		{
+		PRINTK2("%s:PHY 10BaseT\n", dev->name);
+		lp->rpc_cur_mode &= ~RPC_SPEED;
+		}
+
+	if ( lp->lastPhy18 & PHY_INT_DPLXDET )
+		{
+		PRINTK2("%s:PHY Full Duplex\n", dev->name);
+		lp->rpc_cur_mode |= RPC_DPLX;
+		}
+	else
+		{
+		PRINTK2("%s:PHY Half Duplex\n", dev->name);
+		lp->rpc_cur_mode &= ~RPC_DPLX;
+		}
+
+	// Re-Configure the Receive/Phy Control register
+	outw( lp->rpc_cur_mode, ioaddr + RPC_REG );
+
+  smc_phy_configure_exit:
+
+	// Exit auto-negotiation
+	lp->autoneg_active = 0;
+}
+
+
+
+/*************************************************************************
+ . smc_phy_interrupt
+ .
+ . Purpose:  Handle interrupts relating to PHY register 18. This is
+ .  called from the "hard" interrupt handler.
+ .
+ ************************************************************************/
+static void smc_phy_interrupt(struct net_device* dev)
+{
+	int ioaddr 		= dev->base_addr;
+	struct smc_local *lp 	= (struct smc_local *)dev->priv;
+	byte phyaddr = lp->phyaddr;
+	word phy18;
+
+	PRINTK2("%s: smc_phy_interrupt\n", dev->name);
+
+  while (1)
+	{ 
+	// Read PHY Register 18, Status Output
+	phy18 = smc_read_phy_register(ioaddr, phyaddr, PHY_INT_REG);
+
+	// Exit if not more changes
+	if (phy18 == lp->lastPhy18)
+		break;
+
+#if (SMC_DEBUG > 1 )
+
+	PRINTK2("%s:     phy18=0x%x\n", dev->name, phy18);
+	PRINTK2("%s: lastPhy18=0x%x\n", dev->name, lp->lastPhy18);
+
+	// Handle events
+	if ((phy18 & PHY_INT_LNKFAIL) != (lp->lastPhy18 & PHY_INT_LNKFAIL))
+		{
+		PRINTK2("%s: PHY Link Fail=%x\n", dev->name,
+			phy18 & PHY_INT_LNKFAIL);
+		}
+
+	if ((phy18 & PHY_INT_LOSSSYNC) != (lp->lastPhy18 & PHY_INT_LOSSSYNC))
+		{
+		PRINTK2("%s: PHY LOSS SYNC=%x\n", dev->name,
+			phy18 & PHY_INT_LOSSSYNC);
+		}
+
+	if ((phy18 & PHY_INT_CWRD) != (lp->lastPhy18 & PHY_INT_CWRD))
+		{
+		PRINTK2("%s: PHY INVALID 4B5B code=%x\n", dev->name,
+			phy18 & PHY_INT_CWRD);
+		}
+
+	if ((phy18 & PHY_INT_SSD) != (lp->lastPhy18 & PHY_INT_SSD))
+		{
+		PRINTK2("%s: PHY No Start Of Stream=%x\n", dev->name,
+			phy18 & PHY_INT_SSD);
+		}
+
+	if ((phy18 & PHY_INT_ESD) != (lp->lastPhy18 & PHY_INT_ESD))
+		{
+		PRINTK2("%s: PHY No End Of Stream=%x\n", dev->name,
+			phy18 & PHY_INT_ESD);
+		}
+
+	if ((phy18 & PHY_INT_RPOL) != (lp->lastPhy18 & PHY_INT_RPOL))
+		{
+		PRINTK2("%s: PHY Reverse Polarity Detected=%x\n", dev->name,
+			phy18 & PHY_INT_RPOL);
+		}
+
+	if ((phy18 & PHY_INT_JAB) != (lp->lastPhy18 & PHY_INT_JAB))
+		{
+		PRINTK2("%s: PHY Jabber Detected=%x\n", dev->name,
+			phy18 & PHY_INT_JAB);
+		}
+
+	if ((phy18 & PHY_INT_SPDDET) != (lp->lastPhy18 & PHY_INT_SPDDET))
+		{
+		PRINTK2("%s: PHY Speed Detect=%x\n", dev->name,
+			phy18 & PHY_INT_SPDDET);
+		}
+
+	if ((phy18 & PHY_INT_DPLXDET) != (lp->lastPhy18 & PHY_INT_DPLXDET))
+		{
+		PRINTK2("%s: PHY Duplex Detect=%x\n", dev->name,
+			phy18 & PHY_INT_DPLXDET);
+		}
+#endif
+
+	// Update the last phy 18 variable
+	lp->lastPhy18 = phy18;
+
+	// On a link down event, always reset the PHY. This puts the device
+	// in a known state.
+	if ((phy18 & (PHY_INT_INT | PHY_INT_LNKFAIL)) ==
+	    (PHY_INT_INT | PHY_INT_LNKFAIL))
+	        // Link down detected
+		{
+		smc_phy_reset(dev);  // reset PHY 
+		PRINTK2("%s: PHY reset on Link Down\n", dev->name);
+		}
+		  
+	} // end while
+}
+
+
+/*------------------------------------------------------------
+ . Resets and configures the specified PHY using Autonegotiation.
+ . Calls smc_phy_fixed() if the user has requested a certain config.
+ . Called from smc_phy_interrupt() whenever an invalid state in
+ . in the PHY is detected.
+ .-------------------------------------------------------------*/
+static void smc_phy_reset(struct net_device* dev)
+{
+	int ioaddr = dev->base_addr;
+	struct smc_local *lp = (struct smc_local *)dev->priv;
+	byte phyaddr;
+	word my_phy_caps; // My PHY capabilities
+	word my_ad_caps; // My Advertised capabilities
+
+	// Set the blocking flag
+	lp->autoneg_active = 1;
+
+	// Get the detected phy address
+	phyaddr = lp->phyaddr;
+
+	// Reset the PHY, setting all other bits to zero
+	smc_write_phy_register(ioaddr, phyaddr, PHY_CNTL_REG, PHY_CNTL_RST);
+
+	// Wait for the reset to complete
+	mdelay(50); // wait 50 millisecs - bad in ISR, but I have no choice
+
+	// Read PHY Register 18, Status Output
+	lp->lastPhy18 = smc_read_phy_register(ioaddr, phyaddr, PHY_INT_REG);
+
+	// Enable PHY Interrupts (for register 18)
+	// Interrupts listed here are disabled
+	smc_write_phy_register(ioaddr, phyaddr, PHY_MASK_REG, 
+		PHY_INT_LOSSSYNC | PHY_INT_CWRD | PHY_INT_SSD |
+		PHY_INT_ESD | PHY_INT_RPOL | PHY_INT_JAB |
+		PHY_INT_SPDDET | PHY_INT_DPLXDET);
+
+	/* Configure the Receive/Phy Control register */
+	SMC_SELECT_BANK( 0 );
+	outw( lp->rpc_cur_mode, ioaddr + RPC_REG );
+
+	// Copy our capabilities from PHY_STAT_REG to PHY_AD_REG
+	my_phy_caps = smc_read_phy_register(ioaddr, phyaddr, PHY_STAT_REG);
+	my_ad_caps  = PHY_AD_CSMA; // I am CSMA capable
+
+	if (my_phy_caps & PHY_STAT_CAP_T4)
+		my_ad_caps |= PHY_AD_T4;
+
+	if (my_phy_caps & PHY_STAT_CAP_TXF)
+		my_ad_caps |= PHY_AD_TX_FDX;
+
+	if (my_phy_caps & PHY_STAT_CAP_TXH)
+		my_ad_caps |= PHY_AD_TX_HDX;
+
+	if (my_phy_caps & PHY_STAT_CAP_TF)
+		my_ad_caps |= PHY_AD_10_FDX;
+
+	if (my_phy_caps & PHY_STAT_CAP_TH)
+		my_ad_caps |= PHY_AD_10_HDX;
+
+	// Disable capabilities not selected by our user
+	if (lp->ctl_rspeed != 100)
+		{
+		my_ad_caps &= ~(PHY_AD_T4|PHY_AD_TX_FDX|PHY_AD_TX_HDX);
+		}
+
+	if (!lp->ctl_rfduplx)
+		{
+		my_ad_caps &= ~(PHY_AD_TX_FDX|PHY_AD_10_FDX);
+		}
+
+	// Update our Auto-Neg Advertisement Register
+	smc_write_phy_register(ioaddr, phyaddr, PHY_AD_REG, my_ad_caps);
+
+	// If the user requested no auto neg, then go set his request
+	if (!(lp->ctl_autoneg))
+		{
+		smc_phy_fixed(dev);
+		}
+	else
+		{
+		// Restart auto-negotiation process in order to advertise my caps
+		smc_write_phy_register( ioaddr, phyaddr, PHY_CNTL_REG,
+								PHY_CNTL_ANEG_EN | PHY_CNTL_ANEG_RST );
+
+		// Re-Configure the Receive/Phy Control register
+		outw( lp->rpc_cur_mode, ioaddr + RPC_REG );
+		}
+
+	// Exit auto-negotiation
+	lp->autoneg_active = 0;
+}
+
diff -Nur linux-2.4.30/drivers/net/smc91111.h linux-2.4.30-linux-mips.org/drivers/net/smc91111.h
--- linux-2.4.30/drivers/net/smc91111.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/net/smc91111.h	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,568 @@
+/*------------------------------------------------------------------------
+ . smc91111.h - macros for the LAN91C111 Ethernet Driver
+ .
+ . Copyright (C) 2001 Standard Microsystems Corporation (SMSC)
+ .       Developed by Simple Network Magic Corporation (SNMC)
+ . Copyright (C) 1996 by Erik Stahlman (ES)
+ .
+ . This program is free software; you can redistribute it and/or modify
+ . it under the terms of the GNU General Public License as published by
+ . the Free Software Foundation; either version 2 of the License, or
+ . (at your option) any later version.
+ .
+ . This program is distributed in the hope that it will be useful,
+ . but WITHOUT ANY WARRANTY; without even the implied warranty of
+ . MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ . GNU General Public License for more details.
+ .
+ . You should have received a copy of the GNU General Public License
+ . along with this program; if not, write to the Free Software
+ . Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ .
+ . This file contains register information and access macros for 
+ . the LAN91C111 single chip ethernet controller.  It is a modified
+ . version of the smc9194.h file.
+ . 
+ . Information contained in this file was obtained from the LAN91C111
+ . manual from SMC.  To get a copy, if you really want one, you can find 
+ . information under www.smsc.com.
+ . 
+ . Authors
+ . 	Erik Stahlman				( erik@vt.edu )
+ .	Daris A Nevil				( dnevil@snmc.com )
+ .
+ . History
+ . 03/16/01		Daris A Nevil	Modified for use with LAN91C111 device
+ .
+ ---------------------------------------------------------------------------*/
+#ifndef _SMC91111_H_
+#define _SMC91111_H_
+
+/* I want some simple types */
+
+typedef unsigned char			byte;
+typedef unsigned short			word;
+typedef unsigned long int 		dword;
+
+
+/* Because of bank switching, the LAN91xxx uses only 16 I/O ports */
+
+#define SMC_IO_EXTENT	16
+
+
+/*---------------------------------------------------------------
+ .  
+ . A description of the SMSC registers is probably in order here,
+ . although for details, the SMC datasheet is invaluable.  
+ . 
+ . Basically, the chip has 4 banks of registers ( 0 to 3 ), which
+ . are accessed by writing a number into the BANK_SELECT register
+ . ( I also use a SMC_SELECT_BANK macro for this ).
+ . 
+ . The banks are configured so that for most purposes, bank 2 is all
+ . that is needed for simple run time tasks.  
+ -----------------------------------------------------------------------*/
+
+/*
+ . Bank Select Register: 
+ .
+ .		yyyy yyyy 0000 00xx  
+ .		xx 		= bank number
+ .		yyyy yyyy	= 0x33, for identification purposes.
+*/
+#define	BANK_SELECT		14
+
+// Transmit Control Register
+/* BANK 0  */
+#define	TCR_REG 	0x0000 	// transmit control register 
+#define TCR_ENABLE	0x0001	// When 1 we can transmit
+#define TCR_LOOP	0x0002	// Controls output pin LBK
+#define TCR_FORCOL	0x0004	// When 1 will force a collision
+#define TCR_PAD_EN	0x0080	// When 1 will pad tx frames < 64 bytes w/0
+#define TCR_NOCRC	0x0100	// When 1 will not append CRC to tx frames
+#define TCR_MON_CSN	0x0400	// When 1 tx monitors carrier
+#define TCR_FDUPLX    	0x0800  // When 1 enables full duplex operation
+#define TCR_STP_SQET	0x1000	// When 1 stops tx if Signal Quality Error
+#define	TCR_EPH_LOOP	0x2000	// When 1 enables EPH block loopback
+#define	TCR_SWFDUP	0x8000	// When 1 enables Switched Full Duplex mode
+
+#define	TCR_CLEAR	0	/* do NOTHING */
+/* the default settings for the TCR register : */ 
+/* QUESTION: do I want to enable padding of short packets ? */
+#define	TCR_DEFAULT  	(TCR_ENABLE | TCR_SWFDUP)
+
+
+// EPH Status Register
+/* BANK 0  */
+#define EPH_STATUS_REG	0x0002
+#define ES_TX_SUC	0x0001	// Last TX was successful
+#define ES_SNGL_COL	0x0002	// Single collision detected for last tx
+#define ES_MUL_COL	0x0004	// Multiple collisions detected for last tx
+#define ES_LTX_MULT	0x0008	// Last tx was a multicast
+#define ES_16COL	0x0010	// 16 Collisions Reached
+#define ES_SQET		0x0020	// Signal Quality Error Test
+#define ES_LTXBRD	0x0040	// Last tx was a broadcast
+#define ES_TXDEFR	0x0080	// Transmit Deferred
+#define ES_LATCOL	0x0200	// Late collision detected on last tx
+#define ES_LOSTCARR	0x0400	// Lost Carrier Sense
+#define ES_EXC_DEF	0x0800	// Excessive Deferral
+#define ES_CTR_ROL	0x1000	// Counter Roll Over indication
+#define ES_LINK_OK	0x4000	// Driven by inverted value of nLNK pin
+#define ES_TXUNRN	0x8000	// Tx Underrun
+
+
+// Receive Control Register
+/* BANK 0  */
+#define	RCR_REG		0x0004
+#define	RCR_RX_ABORT	0x0001	// Set if a rx frame was aborted
+#define	RCR_PRMS	0x0002	// Enable promiscuous mode
+#define	RCR_ALMUL	0x0004	// When set accepts all multicast frames
+#define RCR_RXEN	0x0100	// IFF this is set, we can receive packets
+#define	RCR_STRIP_CRC	0x0200	// When set strips CRC from rx packets
+#define	RCR_ABORT_ENB	0x0200	// When set will abort rx on collision 
+#define	RCR_FILT_CAR	0x0400	// When set filters leading 12 bit s of carrier
+#define RCR_SOFTRST	0x8000 	// resets the chip
+
+/* the normal settings for the RCR register : */
+#define	RCR_DEFAULT	(RCR_STRIP_CRC | RCR_RXEN)
+#define RCR_CLEAR	0x0	// set it to a base state
+
+// Counter Register
+/* BANK 0  */
+#define	COUNTER_REG	0x0006
+
+// Memory Information Register
+/* BANK 0  */
+#define	MIR_REG		0x0008
+
+// Receive/Phy Control Register
+/* BANK 0  */
+#define	RPC_REG		0x000A
+#define	RPC_SPEED	0x2000	// When 1 PHY is in 100Mbps mode.
+#define	RPC_DPLX	0x1000	// When 1 PHY is in Full-Duplex Mode
+#define	RPC_ANEG	0x0800	// When 1 PHY is in Auto-Negotiate Mode
+#define	RPC_LSXA_SHFT	5	// Bits to shift LS2A,LS1A,LS0A to lsb
+#define	RPC_LSXB_SHFT	2	// Bits to get LS2B,LS1B,LS0B to lsb
+#define RPC_LED_100_10	(0x00)	// LED = 100Mbps OR's with 10Mbps link detect
+#define RPC_LED_RES	(0x01)	// LED = Reserved
+#define RPC_LED_10	(0x02)	// LED = 10Mbps link detect
+#define RPC_LED_FD	(0x03)	// LED = Full Duplex Mode
+#define RPC_LED_TX_RX	(0x04)	// LED = TX or RX packet occurred
+#define RPC_LED_100	(0x05)	// LED = 100Mbps link dectect
+#define RPC_LED_TX	(0x06)	// LED = TX packet occurred
+#define RPC_LED_RX	(0x07)	// LED = RX packet occurred
+#ifndef RPC_DEFAULT
+#define RPC_DEFAULT (RPC_ANEG | (RPC_LED_100 << RPC_LSXA_SHFT) | (RPC_LED_FD << RPC_LSXB_SHFT) | RPC_SPEED | RPC_DPLX)
+#endif
+
+/* Bank 0 0x000C is reserved */
+
+// Bank Select Register
+/* All Banks */
+#define BSR_REG	0x000E
+
+
+// Configuration Reg
+/* BANK 1 */
+#define CONFIG_REG	0x0000
+#define CONFIG_EXT_PHY	0x0200	// 1=external MII, 0=internal Phy
+#define CONFIG_GPCNTRL	0x0400	// Inverse value drives pin nCNTRL
+#define CONFIG_NO_WAIT	0x1000	// When 1 no extra wait states on ISA bus
+#define CONFIG_EPH_POWER_EN 0x8000 // When 0 EPH is placed into low power mode.
+
+// Default is powered-up, Internal Phy, Wait States, and pin nCNTRL=low
+#define CONFIG_DEFAULT	(CONFIG_EPH_POWER_EN)
+
+
+// Base Address Register
+/* BANK 1 */
+#define	BASE_REG	0x0002
+
+
+// Individual Address Registers
+/* BANK 1 */
+#define	ADDR0_REG	0x0004
+#define	ADDR1_REG	0x0006
+#define	ADDR2_REG	0x0008
+
+
+// General Purpose Register
+/* BANK 1 */
+#define	GP_REG		0x000A
+
+
+// Control Register
+/* BANK 1 */
+#define	CTL_REG		0x000C
+#define CTL_RCV_BAD	0x4000 // When 1 bad CRC packets are received
+#define CTL_AUTO_RELEASE 0x0800 // When 1 tx pages are released automatically
+#define	CTL_LE_ENABLE	0x0080 // When 1 enables Link Error interrupt
+#define	CTL_CR_ENABLE	0x0040 // When 1 enables Counter Rollover interrupt
+#define	CTL_TE_ENABLE	0x0020 // When 1 enables Transmit Error interrupt
+#define	CTL_EEPROM_SELECT 0x0004 // Controls EEPROM reload & store
+#define	CTL_RELOAD	0x0002 // When set reads EEPROM into registers
+#define	CTL_STORE	0x0001 // When set stores registers into EEPROM
+
+
+// MMU Command Register
+/* BANK 2 */
+#define MMU_CMD_REG	0x0000
+#define MC_BUSY		1	// When 1 the last release has not completed
+#define MC_NOP		(0<<5)	// No Op
+#define	MC_ALLOC	(1<<5) 	// OR with number of 256 byte packets
+#define	MC_RESET	(2<<5)	// Reset MMU to initial state
+#define	MC_REMOVE	(3<<5) 	// Remove the current rx packet
+#define MC_RELEASE  	(4<<5) 	// Remove and release the current rx packet
+#define MC_FREEPKT  	(5<<5) 	// Release packet in PNR register
+#define MC_ENQUEUE	(6<<5)	// Enqueue the packet for transmit
+#define MC_RSTTXFIFO	(7<<5)	// Reset the TX FIFOs
+
+
+// Packet Number Register
+/* BANK 2 */
+#define	PN_REG		0x0002
+
+
+// Allocation Result Register
+/* BANK 2 */
+#define	AR_REG		0x0003
+#define AR_FAILED	0x80	// Alocation Failed
+
+
+// RX FIFO Ports Register
+/* BANK 2 */
+#define RXFIFO_REG	0x0004	// Must be read as a word
+#define RXFIFO_REMPTY	0x8000	// RX FIFO Empty
+
+
+// TX FIFO Ports Register
+/* BANK 2 */
+#define TXFIFO_REG	RXFIFO_REG	// Must be read as a word
+#define TXFIFO_TEMPTY	0x80	// TX FIFO Empty
+
+
+// Pointer Register
+/* BANK 2 */
+#define PTR_REG		0x0006
+#define	PTR_RCV		0x8000 // 1=Receive area, 0=Transmit area
+#define	PTR_AUTOINC 	0x4000 // Auto increment the pointer on each access
+#define PTR_READ	0x2000 // When 1 the operation is a read
+
+
+// Data Register
+/* BANK 2 */
+#define	DATA_REG	0x0008
+
+
+// Interrupt Status/Acknowledge Register
+/* BANK 2 */
+#define	INT_REG		0x000C
+
+
+// Interrupt Mask Register
+/* BANK 2 */
+#define IM_REG		0x000D
+#define	IM_MDINT	0x80 // PHY MI Register 18 Interrupt
+#define	IM_ERCV_INT	0x40 // Early Receive Interrupt
+#define	IM_EPH_INT	0x20 // Set by Etheret Protocol Handler section
+#define	IM_RX_OVRN_INT	0x10 // Set by Receiver Overruns
+#define	IM_ALLOC_INT	0x08 // Set when allocation request is completed
+#define	IM_TX_EMPTY_INT	0x04 // Set if the TX FIFO goes empty
+#define	IM_TX_INT	0x02 // Transmit Interrrupt
+#define IM_RCV_INT	0x01 // Receive Interrupt
+
+
+// Multicast Table Registers
+/* BANK 3 */
+#define	MCAST_REG1	0x0000
+#define	MCAST_REG2	0x0002
+#define	MCAST_REG3	0x0004
+#define	MCAST_REG4	0x0006
+
+
+// Management Interface Register (MII)
+/* BANK 3 */
+#define	MII_REG		0x0008
+#define MII_MSK_CRS100	0x4000 // Disables CRS100 detection during tx half dup
+#define MII_MDOE	0x0008 // MII Output Enable
+#define MII_MCLK	0x0004 // MII Clock, pin MDCLK
+#define MII_MDI		0x0002 // MII Input, pin MDI
+#define MII_MDO		0x0001 // MII Output, pin MDO
+
+
+// Revision Register
+/* BANK 3 */
+#define	REV_REG		0x000A /* ( hi: chip id   low: rev # ) */
+
+
+// Early RCV Register
+/* BANK 3 */
+/* this is NOT on SMC9192 */
+#define	ERCV_REG	0x000C
+#define ERCV_RCV_DISCRD	0x0080 // When 1 discards a packet being received
+#define ERCV_THRESHOLD	0x001F // ERCV Threshold Mask
+
+// External Register
+/* BANK 7 */
+#define	EXT_REG		0x0000
+
+
+#define CHIP_9192	3
+#define CHIP_9194	4
+#define CHIP_9195	5
+#define CHIP_9196	6
+#define CHIP_91100	7
+#define CHIP_91100FD	8
+#define CHIP_91111FD	9
+
+static const char * chip_ids[ 15 ] =  { 
+	NULL, NULL, NULL, 
+	/* 3 */ "SMC91C90/91C92",
+	/* 4 */ "SMC91C94",
+	/* 5 */ "SMC91C95",
+	/* 6 */ "SMC91C96",
+	/* 7 */ "SMC91C100", 
+	/* 8 */ "SMC91C100FD", 
+	/* 9 */ "SMC91C11xFD", 
+	NULL, NULL, 
+	NULL, NULL, NULL};  
+
+/* 
+ . Transmit status bits 
+*/
+#define TS_SUCCESS 0x0001
+#define TS_LOSTCAR 0x0400
+#define TS_LATCOL  0x0200
+#define TS_16COL   0x0010
+
+/*
+ . Receive status bits
+*/
+#define RS_ALGNERR	0x8000
+#define RS_BRODCAST	0x4000
+#define RS_BADCRC	0x2000
+#define RS_ODDFRAME	0x1000	// bug: the LAN91C111 never sets this on receive
+#define RS_TOOLONG	0x0800
+#define RS_TOOSHORT	0x0400
+#define RS_MULTICAST	0x0001
+#define RS_ERRORS	(RS_ALGNERR | RS_BADCRC | RS_TOOLONG | RS_TOOSHORT) 
+
+
+// PHY Types
+enum {
+	PHY_LAN83C183 = 1,	// LAN91C111 Internal PHY
+	PHY_LAN83C180
+};
+
+
+// PHY Register Addresses (LAN91C111 Internal PHY)
+
+// PHY Control Register
+#define PHY_CNTL_REG		0x00
+#define PHY_CNTL_RST		0x8000	// 1=PHY Reset
+#define PHY_CNTL_LPBK		0x4000	// 1=PHY Loopback
+#define PHY_CNTL_SPEED		0x2000	// 1=100Mbps, 0=10Mpbs
+#define PHY_CNTL_ANEG_EN	0x1000 // 1=Enable Auto negotiation
+#define PHY_CNTL_PDN		0x0800	// 1=PHY Power Down mode
+#define PHY_CNTL_MII_DIS	0x0400	// 1=MII 4 bit interface disabled
+#define PHY_CNTL_ANEG_RST	0x0200 // 1=Reset Auto negotiate
+#define PHY_CNTL_DPLX		0x0100	// 1=Full Duplex, 0=Half Duplex
+#define PHY_CNTL_COLTST		0x0080	// 1= MII Colision Test
+
+// PHY Status Register
+#define PHY_STAT_REG		0x01
+#define PHY_STAT_CAP_T4		0x8000	// 1=100Base-T4 capable
+#define PHY_STAT_CAP_TXF	0x4000	// 1=100Base-X full duplex capable
+#define PHY_STAT_CAP_TXH	0x2000	// 1=100Base-X half duplex capable
+#define PHY_STAT_CAP_TF		0x1000	// 1=10Mbps full duplex capable
+#define PHY_STAT_CAP_TH		0x0800	// 1=10Mbps half duplex capable
+#define PHY_STAT_CAP_SUPR	0x0040	// 1=recv mgmt frames with not preamble
+#define PHY_STAT_ANEG_ACK	0x0020	// 1=ANEG has completed
+#define PHY_STAT_REM_FLT	0x0010	// 1=Remote Fault detected
+#define PHY_STAT_CAP_ANEG	0x0008	// 1=Auto negotiate capable
+#define PHY_STAT_LINK		0x0004	// 1=valid link
+#define PHY_STAT_JAB		0x0002	// 1=10Mbps jabber condition
+#define PHY_STAT_EXREG		0x0001	// 1=extended registers implemented
+
+// PHY Identifier Registers
+#define PHY_ID1_REG		0x02	// PHY Identifier 1
+#define PHY_ID2_REG		0x03	// PHY Identifier 2
+
+// PHY Auto-Negotiation Advertisement Register
+#define PHY_AD_REG		0x04
+#define PHY_AD_NP		0x8000	// 1=PHY requests exchange of Next Page
+#define PHY_AD_ACK		0x4000	// 1=got link code word from remote
+#define PHY_AD_RF		0x2000	// 1=advertise remote fault
+#define PHY_AD_T4		0x0200	// 1=PHY is capable of 100Base-T4
+#define PHY_AD_TX_FDX		0x0100	// 1=PHY is capable of 100Base-TX FDPLX
+#define PHY_AD_TX_HDX		0x0080	// 1=PHY is capable of 100Base-TX HDPLX
+#define PHY_AD_10_FDX		0x0040	// 1=PHY is capable of 10Base-T FDPLX
+#define PHY_AD_10_HDX		0x0020	// 1=PHY is capable of 10Base-T HDPLX
+#define PHY_AD_CSMA		0x0001	// 1=PHY is capable of 802.3 CMSA
+
+// PHY Auto-negotiation Remote End Capability Register
+#define PHY_RMT_REG		0x05
+// Uses same bit definitions as PHY_AD_REG
+
+// PHY Configuration Register 1
+#define PHY_CFG1_REG		0x10
+#define PHY_CFG1_LNKDIS		0x8000	// 1=Rx Link Detect Function disabled
+#define PHY_CFG1_XMTDIS		0x4000	// 1=TP Transmitter Disabled
+#define PHY_CFG1_XMTPDN		0x2000	// 1=TP Transmitter Powered Down
+#define PHY_CFG1_BYPSCR		0x0400	// 1=Bypass scrambler/descrambler
+#define PHY_CFG1_UNSCDS		0x0200	// 1=Unscramble Idle Reception Disable
+#define PHY_CFG1_EQLZR		0x0100	// 1=Rx Equalizer Disabled
+#define PHY_CFG1_CABLE		0x0080	// 1=STP(150ohm), 0=UTP(100ohm)
+#define PHY_CFG1_RLVL0		0x0040	// 1=Rx Squelch level reduced by 4.5db
+#define PHY_CFG1_TLVL_SHIFT	2	// Transmit Output Level Adjust
+#define PHY_CFG1_TLVL_MASK	0x003C
+#define PHY_CFG1_TRF_MASK	0x0003	// Transmitter Rise/Fall time
+
+
+// PHY Configuration Register 2
+#define PHY_CFG2_REG		0x11
+#define PHY_CFG2_APOLDIS	0x0020	// 1=Auto Polarity Correction disabled
+#define PHY_CFG2_JABDIS		0x0010	// 1=Jabber disabled
+#define PHY_CFG2_MREG		0x0008	// 1=Multiple register access (MII mgt)
+#define PHY_CFG2_INTMDIO	0x0004	// 1=Interrupt signaled with MDIO pulseo
+
+// PHY Status Output (and Interrupt status) Register
+#define PHY_INT_REG		0x12	// Status Output (Interrupt Status)
+#define PHY_INT_INT		0x8000	// 1=bits have changed since last read
+#define	PHY_INT_LNKFAIL		0x4000	// 1=Link Not detected
+#define PHY_INT_LOSSSYNC	0x2000	// 1=Descrambler has lost sync
+#define PHY_INT_CWRD		0x1000	// 1=Invalid 4B5B code detected on rx
+#define PHY_INT_SSD		0x0800	// 1=No Start Of Stream detected on rx
+#define PHY_INT_ESD		0x0400	// 1=No End Of Stream detected on rx
+#define PHY_INT_RPOL		0x0200	// 1=Reverse Polarity detected
+#define PHY_INT_JAB		0x0100	// 1=Jabber detected
+#define PHY_INT_SPDDET		0x0080	// 1=100Base-TX mode, 0=10Base-T mode
+#define PHY_INT_DPLXDET		0x0040	// 1=Device in Full Duplex
+
+// PHY Interrupt/Status Mask Register
+#define PHY_MASK_REG		0x13	// Interrupt Mask
+// Uses the same bit definitions as PHY_INT_REG
+
+
+
+/*-------------------------------------------------------------------------
+ .  I define some macros to make it easier to do somewhat common
+ . or slightly complicated, repeated tasks. 
+ --------------------------------------------------------------------------*/
+
+/* select a register bank, 0 to 3  */
+
+#define SMC_SELECT_BANK(x)  { outw( x, ioaddr + BANK_SELECT ); } 
+
+/* this enables an interrupt in the interrupt mask register */
+#define SMC_ENABLE_INT(x) {\
+		unsigned char mask;\
+		SMC_SELECT_BANK(2);\
+		mask = inb( ioaddr + IM_REG );\
+		mask |= (x);\
+		outb( mask, ioaddr + IM_REG ); \
+}
+
+/* this disables an interrupt from the interrupt mask register */
+
+#define SMC_DISABLE_INT(x) {\
+		unsigned char mask;\
+		SMC_SELECT_BANK(2);\
+		mask = inb( ioaddr + IM_REG );\
+		mask &= ~(x);\
+		outb( mask, ioaddr + IM_REG ); \
+}
+
+/*----------------------------------------------------------------------
+ . Define the interrupts that I want to receive from the card
+ . 
+ . I want: 
+ .  IM_EPH_INT, for nasty errors
+ .  IM_RCV_INT, for happy received packets
+ .  IM_RX_OVRN_INT, because I have to kick the receiver
+ .  IM_MDINT, for PHY Register 18 Status Changes
+ --------------------------------------------------------------------------*/
+#define SMC_INTERRUPT_MASK   (IM_EPH_INT | IM_RX_OVRN_INT | IM_RCV_INT | \
+	IM_MDINT) 
+
+
+#ifdef CONFIG_SYSCTL
+
+
+/*
+ * Declarations for the sysctl interface, which allows users the ability to
+ * control the finer aspects of the LAN91C111 chip.  Since the smc
+ * module currently registers its sysctl table dynamically, the sysctl path
+ * for module FOO is /proc/sys/dev/ethX/FOO
+ */
+#define CTL_SMC         (CTL_BUS+1389)      // arbitrary and hopefully unused
+
+enum {
+	CTL_SMC_INFO = 1,	// Sysctl files information
+	CTL_SMC_SWVER,		// Driver Software Version Info
+	CTL_SMC_SWFDUP,		// Switched Full Duplex Mode
+	CTL_SMC_EPHLOOP,	// EPH Block Internal Loopback
+	CTL_SMC_MIIOP,		// MII Operation
+	CTL_SMC_AUTONEG,	// Auto-negotiate Mode
+	CTL_SMC_RFDUPLX,	// Request Full Duplex Mode
+	CTL_SMC_RSPEED,		// Request Speed Selection
+	CTL_SMC_AFDUPLX,	// Actual Full Duplex Mode
+	CTL_SMC_ASPEED,		// Actual Speed Selection
+	CTL_SMC_LNKFAIL,	// Link Failed
+	CTL_SMC_FORCOL,		// Force a Collision
+	CTL_SMC_FILTCAR,	// Filter Carrier
+	CTL_SMC_FREEMEM,	// Free Buffer Memory
+	CTL_SMC_TOTMEM,		// Total Buffer Memory
+	CTL_SMC_LEDA,		// Output of LED-A
+	CTL_SMC_LEDB,		// Output of LED-B
+	CTL_SMC_CHIPREV,	// LAN91C111 Chip Revision ID
+#ifdef SMC_DEBUG
+	// Register access for debugging
+	CTL_SMC_REG_BSR,	// Bank Select
+	CTL_SMC_REG_TCR,	// Transmit Control
+	CTL_SMC_REG_ESR,	// EPH Status
+	CTL_SMC_REG_RCR,	// Receive Control
+	CTL_SMC_REG_CTRR,	// Counter
+	CTL_SMC_REG_MIR,	// Memory Information
+	CTL_SMC_REG_RPCR,	// Receive/Phy Control
+	CTL_SMC_REG_CFGR,	// Configuration
+	CTL_SMC_REG_BAR,	// Base Address
+	CTL_SMC_REG_IAR0,	// Individual Address 0
+	CTL_SMC_REG_IAR1,	// Individual Address 1 
+	CTL_SMC_REG_IAR2,	// Individual Address 2
+	CTL_SMC_REG_GPR,	// General Purpose
+	CTL_SMC_REG_CTLR,	// Control
+	CTL_SMC_REG_MCR,	// MMU Command
+	CTL_SMC_REG_PNR,	// Packet Number
+	CTL_SMC_REG_FPR,	// FIFO Ports
+	CTL_SMC_REG_PTR,	// Pointer
+	CTL_SMC_REG_DR,		// Data 
+	CTL_SMC_REG_ISR,	// Interrupt Status
+	CTL_SMC_REG_MTR1,	// Multicast Table Entry 1
+	CTL_SMC_REG_MTR2,	// Multicast Table Entry 2
+	CTL_SMC_REG_MTR3,	// Multicast Table Entry 3
+	CTL_SMC_REG_MTR4,	// Multicast Table Entry 4
+	CTL_SMC_REG_MIIR,	// Management Interface
+	CTL_SMC_REG_REVR,	// Revision
+	CTL_SMC_REG_ERCVR,	// Early RCV
+	CTL_SMC_REG_EXTR,	// External
+	CTL_SMC_PHY_CTRL,	// PHY Control
+	CTL_SMC_PHY_STAT,	// PHY Status
+	CTL_SMC_PHY_ID1,	// PHY ID1
+	CTL_SMC_PHY_ID2,	// PHY ID2
+	CTL_SMC_PHY_ADC,	// PHY Advertise Capability
+	CTL_SMC_PHY_REMC,	// PHY Advertise Capability
+	CTL_SMC_PHY_CFG1,	// PHY Configuration 1
+	CTL_SMC_PHY_CFG2,	// PHY Configuration 2
+	CTL_SMC_PHY_INT,	// PHY Interrupt/Status Output
+	CTL_SMC_PHY_MASK,	// PHY Interrupt/Status Mask
+#endif
+	// ---------------------------------------------------
+	CTL_SMC_LAST_ENTRY	// Add new entries above the line
+};
+
+#endif // CONFIG_SYSCTL
+ 
+#endif  /* _SMC_91111_H_ */
+
+
diff -Nur linux-2.4.30/drivers/pci/pci.c linux-2.4.30-linux-mips.org/drivers/pci/pci.c
--- linux-2.4.30/drivers/pci/pci.c	2004-11-17 12:54:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/pci/pci.c	2005-05-12 17:57:29.000000000 +0200
@@ -1281,11 +1281,17 @@
 {
 	unsigned int buses;
 	unsigned short cr;
+	unsigned short bctl;
 	struct pci_bus *child;
 	int is_cardbus = (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS);
 
 	pci_read_config_dword(dev, PCI_PRIMARY_BUS, &buses);
 	DBG("Scanning behind PCI bridge %s, config %06x, pass %d\n", dev->slot_name, buses & 0xffffff, pass);
+	/* Disable MasterAbortMode during probing to avoid reporting
+           of bus errors (in some architectures) */
+	pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &bctl);
+	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, 
+			      bctl & ~PCI_BRIDGE_CTL_MASTER_ABORT);
 	if ((buses & 0xffff00) && !pcibios_assign_all_busses()) {
 		/*
 		 * Bus already configured by firmware, process it in the first
@@ -1351,6 +1357,7 @@
 		pci_write_config_byte(dev, PCI_SUBORDINATE_BUS, max);
 		pci_write_config_word(dev, PCI_COMMAND, cr);
 	}
+	pci_write_config_word(dev, PCI_BRIDGE_CONTROL, bctl);
 	sprintf(child->name, (is_cardbus ? "PCI CardBus #%02x" : "PCI Bus #%02x"), child->number);
 	return max;
 }
diff -Nur linux-2.4.30/drivers/pcmcia/au1000_db1x00.c linux-2.4.30-linux-mips.org/drivers/pcmcia/au1000_db1x00.c
--- linux-2.4.30/drivers/pcmcia/au1000_db1x00.c	2005-01-19 15:09:57.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/pcmcia/au1000_db1x00.c	2005-05-12 17:57:29.000000000 +0200
@@ -1,6 +1,6 @@
 /*
  *
- * Alchemy Semi Db1x00 boards specific pcmcia routines.
+ * AMD Alchemy DUAL-SLOT Db1x00 boards' specific pcmcia routines.
  *
  * Copyright 2002 MontaVista Software Inc.
  * Author: MontaVista Software, Inc.
@@ -54,9 +54,20 @@
 #include <asm/au1000.h>
 #include <asm/au1000_pcmcia.h>
 
+#if defined(CONFIG_MIPS_PB1200)
+#include <asm/pb1200.h>
+#elif defined(CONFIG_MIPS_DB1200)
+#include <asm/db1200.h>
+#else
 #include <asm/db1x00.h>
+#endif
 
-static BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+#define PCMCIA_MAX_SOCK 1
+#define PCMCIA_NUM_SOCKS (PCMCIA_MAX_SOCK+1)
+
+/* VPP/VCC */
+#define SET_VCC_VPP(VCC, VPP, SLOT)\
+    ((((VCC)<<2) | ((VPP)<<0)) << ((SLOT)*8))
 
 static int db1x00_pcmcia_init(struct pcmcia_init *init)
 {
@@ -76,7 +87,7 @@
 db1x00_pcmcia_socket_state(unsigned sock, struct pcmcia_state *state)
 {
 	u32 inserted;
-	unsigned char vs;
+	u16 vs;
 
 	if(sock > PCMCIA_MAX_SOCK) return -1;
 
@@ -87,11 +98,11 @@
 
 	if (sock == 0) {
 		vs = bcsr->status & 0x3;
-		inserted = !(bcsr->status & (1<<4));
+		inserted = BOARD_CARD_INSERTED(0);
 	}
 	else {
 		vs = (bcsr->status & 0xC)>>2;
-		inserted = !(bcsr->status & (1<<5));
+		inserted = BOARD_CARD_INSERTED(1);
 	}
 
 	DEBUG(KERN_DEBUG "db1x00 socket %d: inserted %d, vs %d\n", 
@@ -144,16 +155,9 @@
 	if(info->sock > PCMCIA_MAX_SOCK) return -1;
 
 	if(info->sock == 0)
-#ifdef CONFIG_MIPS_DB1550
-		info->irq = AU1000_GPIO_3;
+		info->irq = BOARD_PC0_INT;
 	else 
-		info->irq = AU1000_GPIO_5;
-#else
-		info->irq = AU1000_GPIO_2;
-	else 
-		info->irq = AU1000_GPIO_5;
-#endif
-
+		info->irq = BOARD_PC1_INT;
 	return 0;
 }
 
diff -Nur linux-2.4.30/drivers/pcmcia/Config.in linux-2.4.30-linux-mips.org/drivers/pcmcia/Config.in
--- linux-2.4.30/drivers/pcmcia/Config.in	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/pcmcia/Config.in	2005-05-12 17:57:29.000000000 +0200
@@ -30,16 +30,14 @@
       dep_tristate '  M8xx support' CONFIG_PCMCIA_M8XX $CONFIG_PCMCIA
    fi
    if [ "$CONFIG_SOC_AU1X00" = "y" ]; then
-      dep_tristate '  Au1x00 PCMCIA support' CONFIG_PCMCIA_AU1X00 $CONFIG_PCMCIA 
-      if [ "$CONFIG_PCMCIA_AU1X00" != "n" ]; then
-        bool '  Pb1x00 board support' CONFIG_PCMCIA_PB1X00
-        bool '  Db1x00 board support' CONFIG_PCMCIA_DB1X00
-        bool '  XXS1500 board support' CONFIG_PCMCIA_XXS1500
-      fi
+      dep_tristate '  Au1x00 PCMCIA support' CONFIG_PCMCIA_AU1X00 $CONFIG_PCMCIA
    fi
    if [ "$CONFIG_SIBYTE_SB1xxx_SOC" = "y" ]; then
       dep_bool '  SiByte PCMCIA support' CONFIG_PCMCIA_SIBYTE $CONFIG_PCMCIA $CONFIG_BLK_DEV_IDE_SIBYTE
    fi
+   if [ "$CONFIG_VRC4171" = "y" -o "$CONFIG_VRC4171" = "m" ]; then
+      dep_tristate '  NEC VRC4171 Card Controllers support' CONFIG_PCMCIA_VRC4171 $CONFIG_PCMCIA
+   fi
    if [ "$CONFIG_VRC4173" = "y" -o "$CONFIG_VRC4173" = "m" ]; then
       dep_tristate '  NEC VRC4173 CARDU support' CONFIG_PCMCIA_VRC4173 $CONFIG_PCMCIA
    fi
diff -Nur linux-2.4.30/drivers/pcmcia/Makefile linux-2.4.30-linux-mips.org/drivers/pcmcia/Makefile
--- linux-2.4.30/drivers/pcmcia/Makefile	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/pcmcia/Makefile	2005-05-12 17:57:29.000000000 +0200
@@ -61,9 +61,18 @@
 
 obj-$(CONFIG_PCMCIA_AU1X00)			+= au1x00_ss.o
 au1000_ss-objs-y				:= au1000_generic.o
-au1000_ss-objs-$(CONFIG_PCMCIA_PB1X00)		+= au1000_pb1x00.o
-au1000_ss-objs-$(CONFIG_PCMCIA_DB1X00)		+= au1000_db1x00.o
-au1000_ss-objs-$(CONFIG_PCMCIA_XXS1500) 	+= au1000_xxs1500.o
+au1000_ss-objs-$(CONFIG_MIPS_PB1000)		+= au1000_pb1x00.o
+au1000_ss-objs-$(CONFIG_MIPS_PB1100)		+= au1000_pb1x00.o
+au1000_ss-objs-$(CONFIG_MIPS_PB1500)		+= au1000_pb1x00.o
+au1000_ss-objs-$(CONFIG_MIPS_PB1550)		+= au1000_pb1550.o
+au1000_ss-objs-$(CONFIG_MIPS_PB1200)		+= au1000_db1x00.o
+au1000_ss-objs-$(CONFIG_MIPS_DB1000)		+= au1000_db1x00.o
+au1000_ss-objs-$(CONFIG_MIPS_DB1100)		+= au1000_db1x00.o
+au1000_ss-objs-$(CONFIG_MIPS_DB1500)		+= au1000_db1x00.o
+au1000_ss-objs-$(CONFIG_MIPS_DB1550)		+= au1000_db1x00.o
+au1000_ss-objs-$(CONFIG_MIPS_DB1200)		+= au1000_db1x00.o
+au1000_ss-objs-$(CONFIG_MIPS_HYDROGEN3)		+= au1000_hydrogen3.o
+au1000_ss-objs-$(CONFIG_MIPS_XXS1500) 		+= au1000_xxs1500.o
 
 obj-$(CONFIG_PCMCIA_SA1100)	+= sa1100_cs.o
 obj-$(CONFIG_PCMCIA_M8XX)	+= m8xx_pcmcia.o
@@ -89,6 +98,7 @@
 sa1100_cs-objs-$(CONFIG_SA1100_XP860)		+= sa1100_xp860.o sa1111_generic.o
 sa1100_cs-objs-$(CONFIG_SA1100_YOPY)		+= sa1100_yopy.o
 
+obj-$(CONFIG_PCMCIA_VRC4171)	+= vrc4171_card.o
 obj-$(CONFIG_PCMCIA_VRC4173)	+= vrc4173_cardu.o
 
 include $(TOPDIR)/Rules.make
diff -Nur linux-2.4.30/drivers/pcmcia/vrc4171_card.c linux-2.4.30-linux-mips.org/drivers/pcmcia/vrc4171_card.c
--- linux-2.4.30/drivers/pcmcia/vrc4171_card.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/pcmcia/vrc4171_card.c	2005-05-12 17:57:29.000000000 +0200
@@ -0,0 +1,886 @@
+/*
+ * vrc4171_card.c, NEC VRC4171 Card Controller driver for Socket Services.
+ *
+ * Copyright (C) 2003  Yoichi Yuasa <yuasa@hh.iij4u.or.jp>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License
+ *  along with this program; if not, write to the Free Software
+ *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+
+#include <asm/io.h>
+#include <asm/vr41xx/vrc4171.h>
+
+#include <pcmcia/ss.h>
+
+#include "i82365.h"
+
+MODULE_DESCRIPTION("NEC VRC4171 Card Controllers driver for Socket Services");
+MODULE_AUTHOR("Yoichi Yuasa <yuasa@hh.iij4u.or.jp>");
+MODULE_LICENSE("GPL");
+
+#define CARD_MAX_SLOTS		2
+#define CARD_SLOTA		0
+#define CARD_SLOTB		1
+#define CARD_SLOTB_OFFSET	0x40
+
+#define CARD_MEM_START		0x10000000
+#define CARD_MEM_END		0x13ffffff
+#define CARD_MAX_MEM_OFFSET	0x3ffffff
+#define CARD_MAX_MEM_SPEED	1000
+
+#define CARD_CONTROLLER_INDEX	0x03e0
+#define CARD_CONTROLLER_DATA	0x03e1
+#define CARD_CONTROLLER_SIZE	2
+ /* Power register */
+  #define VPP_GET_VCC		0x01
+  #define POWER_ENABLE		0x10
+ #define CARD_VOLTAGE_SENSE	0x1f
+  #define VCC_3VORXV_CAPABLE	0x00
+  #define VCC_XV_ONLY		0x01
+  #define VCC_3V_CAPABLE	0x02
+  #define VCC_5V_ONLY		0x03
+ #define CARD_VOLTAGE_SELECT	0x2f
+  #define VCC_3V		0x01
+  #define VCC_5V		0x00
+  #define VCC_XV		0x02
+  #define VCC_STATUS_3V		0x02
+  #define VCC_STATUS_5V		0x01
+  #define VCC_STATUS_XV		0x03
+ #define GLOBAL_CONTROL		0x1e
+  #define EXWRBK		0x04
+  #define IRQPM_EN		0x08
+  #define CLRPMIRQ		0x10
+
+#define IO_MAX_MAPS	2
+#define MEM_MAX_MAPS	5
+
+enum {
+	SLOTB_PROBE = 0,
+	SLOTB_NOPROBE_IO,
+	SLOTB_NOPROBE_MEM,
+	SLOTB_NOPROBE_ALL
+};
+
+typedef struct vrc4171_socket {
+	int noprobe;
+	void (*handler)(void *, unsigned int);
+	void *info;
+	socket_cap_t cap;
+	spinlock_t event_lock;
+	uint16_t events;
+	struct socket_info_t *pcmcia_socket;
+	struct tq_struct tq_task;
+	char name[24];
+	int csc_irq;
+	int io_irq;
+} vrc4171_socket_t;
+
+static vrc4171_socket_t vrc4171_sockets[CARD_MAX_SLOTS];
+static int vrc4171_slotb = SLOTB_IS_NONE;
+static unsigned int vrc4171_irq;
+static uint16_t vrc4171_irq_mask = 0xdeb8;
+
+extern struct socket_info_t *pcmcia_register_socket(int slot,
+                                                    struct pccard_operations *vtable,
+                                                    int use_bus_pm);
+extern void pcmcia_unregister_socket(struct socket_info_t *s);
+
+static inline uint8_t exca_read_byte(int slot, uint8_t index)
+{
+	if (slot == CARD_SLOTB)
+		index += CARD_SLOTB_OFFSET;
+
+	outb(index, CARD_CONTROLLER_INDEX);
+	return inb(CARD_CONTROLLER_DATA);
+}
+
+static inline uint16_t exca_read_word(int slot, uint8_t index)
+{
+	uint16_t data;
+
+	if (slot == CARD_SLOTB)
+		index += CARD_SLOTB_OFFSET;
+
+	outb(index++, CARD_CONTROLLER_INDEX);
+	data = inb(CARD_CONTROLLER_DATA);
+
+	outb(index, CARD_CONTROLLER_INDEX);
+	data |= ((uint16_t)inb(CARD_CONTROLLER_DATA)) << 8;
+
+	return data;
+}
+
+static inline uint8_t exca_write_byte(int slot, uint8_t index, uint8_t data)
+{
+	if (slot == CARD_SLOTB)
+		index += CARD_SLOTB_OFFSET;
+
+	outb(index, CARD_CONTROLLER_INDEX);
+	outb(data, CARD_CONTROLLER_DATA);
+
+	return data;
+}
+
+static inline uint16_t exca_write_word(int slot, uint8_t index, uint16_t data)
+{
+	if (slot == CARD_SLOTB)
+		index += CARD_SLOTB_OFFSET;
+
+	outb(index++, CARD_CONTROLLER_INDEX);
+	outb(data, CARD_CONTROLLER_DATA);
+
+	outb(index, CARD_CONTROLLER_INDEX);
+	outb((uint8_t)(data >> 8), CARD_CONTROLLER_DATA);
+
+	return data;
+}
+
+static inline int search_nonuse_irq(void)
+{
+	int i;
+
+	for (i = 0; i < 16; i++) {
+		if (vrc4171_irq_mask & (1 << i)) {
+			vrc4171_irq_mask &= ~(1 << i);
+			return i;
+		}
+	}
+
+	return -1;
+}
+
+static int pccard_init(unsigned int slot)
+{
+	vrc4171_socket_t *socket = &vrc4171_sockets[slot];
+
+	socket->cap.features |= SS_CAP_PCCARD | SS_CAP_PAGE_REGS;
+	socket->cap.irq_mask = 0;
+	socket->cap.pci_irq = vrc4171_irq;
+	socket->cap.map_size = 0x1000;
+	socket->events = 0;
+	spin_lock_init(socket->event_lock);
+	socket->csc_irq = search_nonuse_irq();
+	socket->io_irq = search_nonuse_irq();
+
+	return 0;
+}
+
+static int pccard_suspend(unsigned int slot)
+{
+	return -EINVAL;
+}
+
+static int pccard_register_callback(unsigned int slot,
+                                    void (*handler)(void *, unsigned int),
+                                    void *info)
+{
+	vrc4171_socket_t *socket;
+
+	if (slot >= CARD_MAX_SLOTS)
+		return -EINVAL;
+
+	socket = &vrc4171_sockets[slot];
+
+	socket->handler = handler;
+	socket->info = info;
+
+	if (handler)
+		MOD_INC_USE_COUNT;
+	else
+		MOD_DEC_USE_COUNT;
+
+	return 0;
+}
+
+static int pccard_inquire_socket(unsigned int slot, socket_cap_t *cap)
+{
+	vrc4171_socket_t *socket;
+
+	if (slot >= CARD_MAX_SLOTS || cap == NULL)
+		return -EINVAL;
+
+	socket = &vrc4171_sockets[slot];
+
+	*cap = socket->cap;
+
+	return 0;
+}
+
+static int pccard_get_status(unsigned int slot, u_int *value)
+{
+	uint8_t status, sense;
+	u_int val = 0;
+
+	if (slot >= CARD_MAX_SLOTS || value == NULL)
+		return -EINVAL;
+
+	status = exca_read_byte(slot, I365_STATUS);
+	if (exca_read_byte(slot, I365_INTCTL) & I365_PC_IOCARD) {
+		if (status & I365_CS_STSCHG)
+			val |= SS_STSCHG;
+	} else {
+		if (!(status & I365_CS_BVD1))
+			val |= SS_BATDEAD;
+		else if ((status & (I365_CS_BVD1 | I365_CS_BVD2)) == I365_CS_BVD1)
+			val |= SS_BATWARN;
+	}
+	if ((status & I365_CS_DETECT) == I365_CS_DETECT)
+		val |= SS_DETECT;
+	if (status & I365_CS_WRPROT)
+		val |= SS_WRPROT;
+	if (status & I365_CS_READY)
+		val |= SS_READY;
+	if (status & I365_CS_POWERON)
+		val |= SS_POWERON;
+
+	sense = exca_read_byte(slot, CARD_VOLTAGE_SENSE);
+	switch (sense) {
+	case VCC_3VORXV_CAPABLE:
+		val |= SS_3VCARD | SS_XVCARD;
+		break;
+	case VCC_XV_ONLY:
+		val |= SS_XVCARD;
+		break;
+	case VCC_3V_CAPABLE:
+		val |= SS_3VCARD;
+		break;
+	default:
+		/* 5V only */
+		break;
+	}
+
+	*value = val;
+
+	return 0;
+}
+
+static inline u_char get_Vcc_value(uint8_t voltage)
+{
+	switch (voltage) {
+	case VCC_STATUS_3V:
+		return 33;
+	case VCC_STATUS_5V:
+		return 50;
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static inline u_char get_Vpp_value(uint8_t power, u_char Vcc)
+{
+	if ((power & 0x03) == 0x01 || (power & 0x03) == 0x02)
+		return Vcc;
+
+	return 0;
+}
+
+static int pccard_get_socket(unsigned int slot, socket_state_t *state)
+{
+	vrc4171_socket_t *socket;
+	uint8_t power, voltage, control, cscint;
+
+	if (slot >= CARD_MAX_SLOTS || state == NULL)
+		return -EINVAL;
+
+	socket = &vrc4171_sockets[slot];
+
+	power = exca_read_byte(slot, I365_POWER);
+	voltage = exca_read_byte(slot, CARD_VOLTAGE_SELECT);
+
+	state->Vcc = get_Vcc_value(voltage);
+	state->Vpp = get_Vpp_value(power, state->Vcc);
+
+	state->flags = 0;
+	if (power & POWER_ENABLE)
+		state->flags |= SS_PWR_AUTO;
+	if (power & I365_PWR_OUT)
+		state->flags |= SS_OUTPUT_ENA;
+
+	control = exca_read_byte(slot, I365_INTCTL);
+	if (control & I365_PC_IOCARD)
+		state->flags |= SS_IOCARD;
+	if (!(control & I365_PC_RESET))
+		state->flags |= SS_RESET;
+
+        cscint = exca_read_byte(slot, I365_CSCINT);
+	state->csc_mask = 0;
+	if (state->flags & SS_IOCARD) {
+		if (cscint & I365_CSC_STSCHG)
+			state->flags |= SS_STSCHG;
+	} else {
+		if (cscint & I365_CSC_BVD1)  
+			state->csc_mask |= SS_BATDEAD;
+		if (cscint & I365_CSC_BVD2)  
+			state->csc_mask |= SS_BATWARN;
+	}
+	if (cscint & I365_CSC_READY)
+		state->csc_mask |= SS_READY;
+	if (cscint & I365_CSC_DETECT)
+		state->csc_mask |= SS_DETECT;
+
+	return 0;
+}
+
+static inline uint8_t set_Vcc_value(u_char Vcc)
+{
+	switch (Vcc) {
+	case 33:
+		return VCC_3V;
+	case 50:
+		return VCC_5V;
+	}
+
+	/* Small voltage is chosen for safety. */
+	return VCC_3V;
+}
+
+static int pccard_set_socket(unsigned int slot, socket_state_t *state)
+{
+	vrc4171_socket_t *socket;
+	uint8_t voltage, power, control, cscint;
+
+	if (slot >= CARD_MAX_SLOTS ||
+	    (state->Vpp != state->Vcc && state->Vpp != 0) ||
+	    (state->Vcc != 50 && state->Vcc != 33 && state->Vcc != 0))
+		return -EINVAL;
+
+	socket = &vrc4171_sockets[slot];
+
+	spin_lock_irq(&socket->event_lock);
+
+	voltage = set_Vcc_value(state->Vcc);
+	exca_write_byte(slot, CARD_VOLTAGE_SELECT, voltage);
+
+	power = POWER_ENABLE;
+	if (state->Vpp == state->Vcc)
+		power |= VPP_GET_VCC;
+	if (state->flags & SS_OUTPUT_ENA)
+		power |= I365_PWR_OUT;
+	exca_write_byte(slot, I365_POWER, power);
+
+	control = 0;
+	if (state->io_irq != 0)
+		control |= socket->io_irq;
+	if (state->flags & SS_IOCARD)
+		control |= I365_PC_IOCARD;
+	if (state->flags & SS_RESET)
+		control	&= ~I365_PC_RESET;
+	else
+		control |= I365_PC_RESET;
+	exca_write_byte(slot, I365_INTCTL, control);
+
+        cscint = 0;
+        exca_write_byte(slot, I365_CSCINT, cscint);
+	exca_read_byte(slot, I365_CSC);	/* clear CardStatus change */
+	if (state->csc_mask != 0)
+		cscint |= socket->csc_irq << 8;
+	if (state->flags & SS_IOCARD) {
+		if (state->csc_mask & SS_STSCHG)
+			cscint |= I365_CSC_STSCHG;
+	} else {
+		if (state->csc_mask & SS_BATDEAD)
+			cscint |= I365_CSC_BVD1;
+		if (state->csc_mask & SS_BATWARN)
+			cscint |= I365_CSC_BVD2;
+	}
+	if (state->csc_mask & SS_READY)
+		cscint |= I365_CSC_READY;
+	if (state->csc_mask & SS_DETECT)
+		cscint |= I365_CSC_DETECT;
+        exca_write_byte(slot, I365_CSCINT, cscint);
+
+	spin_unlock_irq(&socket->event_lock);
+
+	return 0;
+}
+
+static int pccard_get_io_map(unsigned int slot, struct pccard_io_map *io)
+{
+	vrc4171_socket_t *socket;
+	uint8_t ioctl, addrwin;
+	u_char map;
+
+	if (slot >= CARD_MAX_SLOTS || io == NULL ||
+	    io->map >= IO_MAX_MAPS)
+		return -EINVAL;
+
+	socket = &vrc4171_sockets[slot];
+	map = io->map;
+
+	io->start = exca_read_word(slot, I365_IO(map)+I365_W_START);
+	io->stop = exca_read_word(slot, I365_IO(map)+I365_W_STOP);
+
+	ioctl = exca_read_byte(slot, I365_IOCTL);
+	if (io->flags & I365_IOCTL_WAIT(map))
+		io->speed = 1;
+	else
+		io->speed = 0;
+
+	io->flags = 0;
+	if (ioctl & I365_IOCTL_16BIT(map))
+		io->flags |= MAP_16BIT;
+	if (ioctl & I365_IOCTL_IOCS16(map))
+		io->flags |= MAP_AUTOSZ;
+	if (ioctl & I365_IOCTL_0WS(map))
+		io->flags |= MAP_0WS;
+
+	addrwin = exca_read_byte(slot, I365_ADDRWIN);
+	if (addrwin & I365_ENA_IO(map))
+		io->flags |= MAP_ACTIVE;
+
+	return 0;
+}
+
+static int pccard_set_io_map(unsigned int slot, struct pccard_io_map *io)
+{
+	vrc4171_socket_t *socket;
+	uint8_t ioctl, addrwin;
+	u_char map;
+
+	if (slot >= CARD_MAX_SLOTS ||
+	    io == NULL || io->map >= IO_MAX_MAPS ||
+	    io->start > 0xffff || io->stop > 0xffff || io->start > io->stop)
+		return -EINVAL;
+
+	socket = &vrc4171_sockets[slot];
+	map = io->map;
+
+	addrwin = exca_read_byte(slot, I365_ADDRWIN);
+	if (addrwin & I365_ENA_IO(map)) {
+		addrwin &= ~I365_ENA_IO(map);
+		exca_write_byte(slot, I365_ADDRWIN, addrwin);
+	}
+
+	exca_write_word(slot, I365_IO(map)+I365_W_START, io->start);
+	exca_write_word(slot, I365_IO(map)+I365_W_STOP, io->stop);
+
+	ioctl = 0;
+	if (io->speed > 0)
+		ioctl |= I365_IOCTL_WAIT(map);
+	if (io->flags & MAP_16BIT)
+		ioctl |= I365_IOCTL_16BIT(map);
+	if (io->flags & MAP_AUTOSZ)
+		ioctl |= I365_IOCTL_IOCS16(map);
+	if (io->flags & MAP_0WS)
+		ioctl |= I365_IOCTL_0WS(map);
+	exca_write_byte(slot, I365_IOCTL, ioctl);
+
+	if (io->flags & MAP_ACTIVE) {
+		addrwin |= I365_ENA_IO(map);
+		exca_write_byte(slot, I365_ADDRWIN, addrwin);
+	}
+
+	return 0;
+}
+
+static int pccard_get_mem_map(unsigned int slot, struct pccard_mem_map *mem)
+{
+	vrc4171_socket_t *socket;
+	uint8_t addrwin;
+	u_long start, stop;
+	u_int offset;
+	u_char map;
+
+	if (slot >= CARD_MAX_SLOTS || mem == NULL || mem->map >= MEM_MAX_MAPS)
+		return -EINVAL;
+
+	socket = &vrc4171_sockets[slot];
+	map = mem->map;
+
+	mem->flags = 0;
+	mem->speed = 0;
+
+	addrwin = exca_read_byte(slot, I365_ADDRWIN);
+	if (addrwin & I365_ENA_MEM(map))
+		mem->flags |= MAP_ACTIVE;
+
+	start = exca_read_word(slot, I365_MEM(map)+I365_W_START);
+	if (start & I365_MEM_16BIT)
+		mem->flags |= MAP_16BIT;
+	mem->sys_start = (start & 0x3fffUL) << 12;
+
+	stop = exca_read_word(slot, I365_MEM(map)+I365_W_STOP);
+	if (start & I365_MEM_WS0)
+		mem->speed += 1;
+	if (start & I365_MEM_WS1)
+		mem->speed += 2;
+	mem->sys_stop = ((stop & 0x3fffUL) << 12) + 0xfffUL;
+
+	offset = exca_read_word(slot, I365_MEM(map)+I365_W_OFF);
+	if (offset & I365_MEM_REG)
+		mem->flags |= MAP_ATTRIB;
+	if (offset & I365_MEM_WRPROT)
+		mem->flags |= MAP_WRPROT;
+	mem->card_start = (offset & 0x3fffUL) << 12;
+
+	mem->sys_start += CARD_MEM_START;
+	mem->sys_stop += CARD_MEM_START;
+
+	return 0;
+}
+
+static int pccard_set_mem_map(unsigned int slot, struct pccard_mem_map *mem)
+{
+	vrc4171_socket_t *socket;
+	uint16_t start, stop, offset;
+	uint8_t addrwin;
+	u_char map;
+
+	if (slot >= CARD_MAX_SLOTS ||
+	    mem == NULL || mem->map >= MEM_MAX_MAPS ||
+	    mem->sys_start < CARD_MEM_START || mem->sys_start > CARD_MEM_END ||
+	    mem->sys_stop < CARD_MEM_START || mem->sys_stop > CARD_MEM_END ||
+	    mem->sys_start > mem->sys_stop ||
+	    mem->card_start > CARD_MAX_MEM_OFFSET ||
+	    mem->speed > CARD_MAX_MEM_SPEED)
+		return -EINVAL;
+
+	socket = &vrc4171_sockets[slot];
+	map = mem->map;
+
+	addrwin = exca_read_byte(slot, I365_ADDRWIN);
+	if (addrwin & I365_ENA_MEM(map)) {
+		addrwin &= ~I365_ENA_MEM(map);
+		exca_write_byte(slot, I365_ADDRWIN, addrwin);
+	}
+
+	start = (mem->sys_start >> 12) & 0x3fff;
+	if (mem->flags & MAP_16BIT)
+		start |= I365_MEM_16BIT;
+	exca_write_word(slot, I365_MEM(map)+I365_W_START, start);
+
+	stop = (mem->sys_stop >> 12) & 0x3fff;
+	switch (mem->speed) {
+	case 0:
+		break;
+	case 1:
+		stop |= I365_MEM_WS0;
+		break;
+	case 2:
+		stop |= I365_MEM_WS1;
+		break;
+	default:
+		stop |= I365_MEM_WS0 | I365_MEM_WS1;
+		break;
+	}
+	exca_write_word(slot, I365_MEM(map)+I365_W_STOP, stop);
+
+	offset = (mem->card_start >> 12) & 0x3fff;
+	if (mem->flags & MAP_ATTRIB)
+		offset |= I365_MEM_REG;
+	if (mem->flags & MAP_WRPROT)
+		offset |= I365_MEM_WRPROT;
+	exca_write_word(slot, I365_MEM(map)+I365_W_OFF, offset);
+
+	if (mem->flags & MAP_ACTIVE) {
+		addrwin |= I365_ENA_MEM(map);
+		exca_write_byte(slot, I365_ADDRWIN, addrwin);
+	}
+
+	return 0;
+}
+
+static void pccard_proc_setup(unsigned int slot, struct proc_dir_entry *base)
+{          
+}
+
+static struct pccard_operations vrc4171_pccard_operations = {
+	.init			= pccard_init,
+	.suspend		= pccard_suspend,
+	.register_callback	= pccard_register_callback,
+	.inquire_socket		= pccard_inquire_socket,
+	.get_status		= pccard_get_status,
+	.get_socket		= pccard_get_socket,
+	.set_socket		= pccard_set_socket,
+	.get_io_map		= pccard_get_io_map,
+	.set_io_map		= pccard_set_io_map,
+	.get_mem_map		= pccard_get_mem_map,
+	.set_mem_map		= pccard_set_mem_map,
+	.proc_setup		= pccard_proc_setup,
+};
+
+static void pccard_bh(void *data)
+{
+	vrc4171_socket_t *socket = (vrc4171_socket_t *)data;
+	uint16_t events;
+
+	spin_lock_irq(&socket->event_lock);
+	events = socket->events;
+	socket->events = 0;
+	spin_unlock_irq(&socket->event_lock);
+ 
+	if (socket->handler)
+		socket->handler(socket->info, events);
+}
+
+static inline uint16_t get_events(int slot)
+{
+	uint16_t events = 0;
+	uint8_t status, csc;
+
+	status = exca_read_byte(slot, I365_STATUS);
+	csc = exca_read_byte(slot, I365_CSC);
+
+	if (exca_read_byte(slot, I365_INTCTL) & I365_PC_IOCARD) {
+		if ((csc & I365_CSC_STSCHG) && (status & I365_CS_STSCHG))
+			events |= SS_STSCHG;
+	} else {
+		if (csc & (I365_CSC_BVD1 | I365_CSC_BVD2)) {
+			if (!(status & I365_CS_BVD1))
+				events |= SS_BATDEAD;
+			else if ((status & (I365_CS_BVD1 | I365_CS_BVD2)) == I365_CS_BVD1)
+				events |= SS_BATWARN;
+		}
+	}
+	if ((csc & I365_CSC_READY) && (status & I365_CS_READY))
+		events |= SS_READY;
+	if ((csc & I365_CSC_DETECT) && ((status & I365_CS_DETECT) == I365_CS_DETECT))
+		events |= SS_DETECT;
+
+	return events;
+}
+
+static void pccard_status_change(int slot, vrc4171_socket_t *socket)
+{
+	uint16_t events;
+
+	socket->tq_task.routine = pccard_bh;
+	socket->tq_task.data = socket;
+
+	events = get_events(slot);
+	if (events) {
+		spin_lock(&socket->event_lock);
+		socket->events |= events;
+		spin_unlock(&socket->event_lock);
+		schedule_task(&socket->tq_task);
+	}
+}
+
+static void pccard_interrupt(int irq, void *dev_id, struct pt_regs *regs)
+{
+	vrc4171_socket_t *socket;
+	uint16_t status;
+
+	status = vrc4171_get_irq_status();
+	if (status & IRQ_A) {
+		socket = &vrc4171_sockets[CARD_SLOTA];
+		if (socket->noprobe == SLOTB_PROBE) {
+			if (status & (1 << socket->csc_irq))
+				pccard_status_change(CARD_SLOTA, socket);
+		}
+	}
+
+	if (status & IRQ_B) {
+		socket = &vrc4171_sockets[CARD_SLOTB];
+		if (socket->noprobe == SLOTB_PROBE) {
+			if (status & (1 << socket->csc_irq))
+				pccard_status_change(CARD_SLOTB, socket);
+		}
+	}
+}
+
+static inline void reserve_using_irq(int slot)
+{
+	unsigned int irq;
+
+	irq = exca_read_byte(slot, I365_INTCTL);
+	irq &= 0x0f;
+	vrc4171_irq_mask &= ~(1 << irq);
+
+	irq = exca_read_byte(slot, I365_CSCINT);
+	irq = (irq & 0xf0) >> 4;
+	vrc4171_irq_mask &= ~(1 << irq);
+}
+
+static int __devinit vrc4171_add_socket(int slot)
+{
+	vrc4171_socket_t *socket;
+
+	if (slot >= CARD_MAX_SLOTS)
+		return -EINVAL;
+
+	socket = &vrc4171_sockets[slot];
+	if (socket->noprobe != SLOTB_PROBE) {
+		uint8_t addrwin;
+
+		switch (socket->noprobe) {
+		case SLOTB_NOPROBE_MEM:
+			addrwin = exca_read_byte(slot, I365_ADDRWIN);
+			addrwin &= 0x1f;
+			exca_write_byte(slot, I365_ADDRWIN, addrwin);
+			break;
+		case SLOTB_NOPROBE_IO:
+			addrwin = exca_read_byte(slot, I365_ADDRWIN);
+			addrwin &= 0xc0;
+			exca_write_byte(slot, I365_ADDRWIN, addrwin);
+			break;
+		default:
+			break;
+		}
+
+		reserve_using_irq(slot);
+
+		return 0;
+	}
+
+	sprintf(socket->name, "NEC VRC4171 Card Slot %1c", 'A' + slot);
+
+	socket->pcmcia_socket = pcmcia_register_socket(slot, &vrc4171_pccard_operations, 1);
+	if (socket->pcmcia_socket == NULL)
+		return -ENOMEM;
+
+	exca_write_byte(slot, I365_ADDRWIN, 0);
+
+	exca_write_byte(slot, GLOBAL_CONTROL, 0);
+
+	return 0;
+}
+
+static void vrc4171_remove_socket(int slot)
+{
+	vrc4171_socket_t *socket;
+
+	if (slot >= CARD_MAX_SLOTS)
+		return;
+
+	socket = &vrc4171_sockets[slot];
+
+	if (socket->pcmcia_socket != NULL) {
+		pcmcia_unregister_socket(socket->pcmcia_socket);
+		socket->pcmcia_socket = NULL;
+	}
+}
+
+static int __devinit vrc4171_card_setup(char *options)
+{
+	if (options == NULL || *options == '\0')
+		return 0;
+
+	if (strncmp(options, "irq:", 4) == 0) {
+		int irq;
+		options += 4;
+		irq = simple_strtoul(options, &options, 0);
+		if (irq >= 0 && irq < NR_IRQS)
+			vrc4171_irq = irq;
+
+		if (*options != ',')
+			return 0;
+		options++;
+	}
+
+	if (strncmp(options, "slota:", 6) == 0) {
+		options += 6;
+		if (*options != '\0') {
+			if (strncmp(options, "noprobe", 7) == 0) {
+				vrc4171_sockets[CARD_SLOTA].noprobe = 1;
+				options += 7;
+			}
+
+			if (*options != ',')
+				return 0;
+			options++;
+		} else
+			return 0;
+
+	}
+
+	if (strncmp(options, "slotb:", 6) == 0) {
+		options += 6;
+		if (*options != '\0') {
+			if (strncmp(options, "pccard", 6) == 0) {
+				vrc4171_slotb = SLOTB_IS_PCCARD;
+				options += 6;
+			} else if (strncmp(options, "cf", 2) == 0) {
+				vrc4171_slotb = SLOTB_IS_CF;
+				options += 2;
+			} else if (strncmp(options, "flashrom", 8) == 0) {
+				vrc4171_slotb = SLOTB_IS_FLASHROM;
+				options += 8;
+			} else if (strncmp(options, "none", 4) == 0) {
+				vrc4171_slotb = SLOTB_IS_NONE;
+				options += 4;
+			}
+
+			if (*options != ',')
+				return 0;
+			options++;
+
+			if ( strncmp(options, "memnoprobe", 10) == 0)
+				vrc4171_sockets[CARD_SLOTB].noprobe = SLOTB_NOPROBE_MEM;
+			if ( strncmp(options, "ionoprobe", 9) == 0)
+				vrc4171_sockets[CARD_SLOTB].noprobe = SLOTB_NOPROBE_IO;
+			if ( strncmp(options, "noprobe", 7) == 0)
+				vrc4171_sockets[CARD_SLOTB].noprobe = SLOTB_NOPROBE_ALL;
+		}
+	}
+
+	return 0;
+}
+
+__setup("vrc4171_card=", vrc4171_card_setup);
+
+static int __devinit vrc4171_card_init(void)
+{
+	int retval, slot;
+
+	vrc4171_set_multifunction_pin(vrc4171_slotb);
+
+	if (request_region(CARD_CONTROLLER_INDEX, CARD_CONTROLLER_SIZE,
+	                       "NEC VRC4171 Card Controller") == NULL)
+		return -EBUSY;
+
+	for (slot = 0; slot < CARD_MAX_SLOTS; slot++) {
+		if (slot == CARD_SLOTB && vrc4171_slotb == SLOTB_IS_NONE)
+			break;
+
+		retval = vrc4171_add_socket(slot);
+		if (retval != 0)
+			return retval;
+	}
+
+	retval = request_irq(vrc4171_irq, pccard_interrupt, SA_SHIRQ,
+	                     "NEC VRC4171 Card Controller", vrc4171_sockets);
+	if (retval < 0) {
+		for (slot = 0; slot < CARD_MAX_SLOTS; slot++)
+			vrc4171_remove_socket(slot);
+
+		return retval;
+	}
+
+	printk(KERN_INFO "NEC VRC4171 Card Controller, connected to IRQ %d\n", vrc4171_irq);
+
+	return 0;
+}
+
+static void __devexit vrc4171_card_exit(void)
+{
+	int slot;
+
+	for (slot = 0; slot < CARD_MAX_SLOTS; slot++)
+		vrc4171_remove_socket(slot);
+
+	release_region(CARD_CONTROLLER_INDEX, CARD_CONTROLLER_SIZE);
+}
+
+module_init(vrc4171_card_init);
+module_exit(vrc4171_card_exit);
diff -Nur linux-2.4.30/drivers/scsi/NCR53C9x.h linux-2.4.30-linux-mips.org/drivers/scsi/NCR53C9x.h
--- linux-2.4.30/drivers/scsi/NCR53C9x.h	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/scsi/NCR53C9x.h	2005-05-12 17:57:30.000000000 +0200
@@ -144,12 +144,7 @@
 
 #ifndef MULTIPLE_PAD_SIZES
 
-#ifdef CONFIG_CPU_HAS_WB
-#include <asm/wbflush.h>
-#define esp_write(__reg, __val) do{(__reg) = (__val); wbflush();} while(0)
-#else
-#define esp_write(__reg, __val) ((__reg) = (__val))
-#endif
+#define esp_write(__reg, __val) do{(__reg) = (__val); iob();} while(0)
 #define esp_read(__reg) (__reg)
 
 struct ESP_regs {
diff -Nur linux-2.4.30/drivers/sound/au1550_i2s.c linux-2.4.30-linux-mips.org/drivers/sound/au1550_i2s.c
--- linux-2.4.30/drivers/sound/au1550_i2s.c	2005-01-19 15:10:04.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/sound/au1550_i2s.c	2005-05-12 18:01:29.000000000 +0200
@@ -5,20 +5,34 @@
  * Copyright 2004 Embedded Edge, LLC
  *	dan@embeddededge.com
  *
- * So, I was stupid.....Although this is an I2S interface, it still uses
- * the PSC for communication.  The existing au1550_psc.c should have been
- * called au1550_ac97.c or something.  Not going to rename anything now.
+ * Modified for platform independence -- 5/2/2005
+ *  Chris Gray (chris.gray@amd.com)
  *
- * Mostly copied from the au1550_psc.c driver and some from the
- * PowerMac dbdma driver.
- * We assume the processor can do memory coherent DMA.
+ * This module was reworked to support some of the various hw
+ * platforms that use Au1550/Au1200 and I2S codecs. Most of
+ * the Wolfson CODECs I have encountred do not implement readable
+ * registers. Because of this, I implemented local copy of hw
+ * mixer settings. Another drawback is that I2S will often mux
+ * inputs to ADC. 
+ *
+ * Anyway, because of the limitations and "funkiness" of I2S, I
+ * created another file for platform specific implementation. I 
+ * I gave it the name au1550_i2s_hw.c, for the lack of creativity.
+ *
+ * Mostly copied from the au1550_psc.c.
  *
  * The SMBus (I2C) is required for the control of the codec.  It
  * appears at I2C address 0x36 (I2C binary 0011011).  The Pb1550
  * uses the Wolfson WM8731 codec, which is controlled over the I2C.
+ * 
+ * Pb/Db1550 Pb/Db1200 Notes:
  * It's connected to a 12MHz clock, so we can only reliably support
  * 96KHz, 48KHz, 32KHz, and 8KHz data rates.  The framework for variable
- * rate audio is in place, but we currently force it to 48KHz.
+ * rate audio is in place, but we currently force it to 48KHz. This
+ * is only true of the PSC is running as the I2S Master.
+ * In the au1550_i2s_hw.c file I configure the CODEC to run as 
+ * I2S Master while the Au1xxx device is slave running in slave mode
+ * (SERCLK). We can now support any sample rate the CODEC supports.
  *
  *  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
@@ -40,7 +54,9 @@
  *  with this program; if not, write  to the Free Software Foundation, Inc.,
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  *
+ *
  */
+
 #include <linux/version.h>
 #include <linux/module.h>
 #include <linux/string.h>
@@ -62,49 +78,32 @@
 #include <asm/uaccess.h>
 #include <asm/hardirq.h>
 #include <asm/au1000.h>
-#include <asm/pb1550.h>
+
 #include <asm/au1xxx_psc.h>
 #include <asm/au1xxx_dbdma.h>
 
-#undef OSS_DOCUMENTED_MIXER_SEMANTICS
-
-#define AU1550_MODULE_NAME "Au1550 i2s audio"
-#define PFX AU1550_MODULE_NAME
+#include "au1550_i2s.h"
 
-/* Define this if you want to try running at the 44.1 KHz rate.
- * It's just a little off, I think it's actually 44117 or something.
- * I did this for debugging, since many programs, including this
- * driver, will try to upsample from 44.1 to 48 KHz.
- * Seems to work well, we'll just leave it this way.
- */
-#define TRY_441KHz
+#undef OSS_DOCUMENTED_MIXER_SEMANTICS
 
-#ifdef TRY_441KHz
-#define SAMP_RATE	44100
-#else
-#define SAMP_RATE	48000
-#endif
+static int      vra = 0;
 
 /* The number of DBDMA ring descriptors to allocate.  No sense making
  * this too large....if you can't keep up with a few you aren't likely
  * to be able to with lots of them, either.
+ *
+ * CTG -- by cranking these up I was able to play 88200 correctly. 4/26/05
  */
-#define NUM_DBDMA_DESCRIPTORS 4
-
-#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)
-#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)
+#define NUM_DBDMA_DESCRIPTORS 8
+#define MIN_Q_COUNT		4
 
-/* Boot options
- * 0 = no VRA, 1 = use VRA if codec supports it
- * The framework is here, but we currently force no VRA.
- */
-static int      vra = 0;
 MODULE_PARM(vra, "i");
 MODULE_PARM_DESC(vra, "if 1 use VRA if codec supports it");
 
 static struct au1550_state {
 	/* soundcore stuff */
 	int             dev_audio;
+	int				dev_mixer;
 
 	spinlock_t		lock;
 	struct semaphore	open_sem;
@@ -112,7 +111,12 @@
 	mode_t			open_mode;
 	wait_queue_head_t	open_wait;
 	int			no_vra;
-	volatile psc_i2s_t	*psc_addr;
+	struct i2s_hw	* i2s;
+
+	int level_line;
+	int level_mic;
+	int level_left;
+	int level_right;
 
 	struct dmabuf {
 		u32		dmanr;
@@ -195,88 +199,181 @@
 	}
 }
 
-/* Just a place holder.  The Wolfson codec is a write only device,
- * so we would have to keep a local copy of the data.
- */
-#if 0
-static u8
-rdcodec(u8 addr)
+static int
+au1550_open_mixdev(struct inode *inode, struct file *file)
 {
-	return 0  /* data */;
+	file->private_data = &au1550_state;
+	return 0;
 }
-#endif
 
+static int
+au1550_release_mixdev(struct inode *inode, struct file *file)
+{
+	return 0;
+}
 
-static void
-wrcodec(u8 ctlreg, u8 val)
+static int
+au1550_ioctl_mixdev(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
 {
-	int	rcnt;
-	extern int pb1550_wm_codec_write(u8 addr, u8 reg, u8 val);
+	struct au1550_state *s = (struct au1550_state *)file->private_data;
 
-	/* The codec is a write only device, with a 16-bit control/data
-	 * word.  Although it is written as two bytes on the I2C, the
-	 * format is actually 7 bits of register and 9 bits of data.
-	 * The ls bit of the first byte is the ms bit of the data.
-	 */
-	rcnt = 0;
-	while ((pb1550_wm_codec_write((0x36 >> 1), ctlreg, val) != 1) 
-							&& (rcnt < 50)) {
-		rcnt++;
-#if 0
-		printk("Codec write retry %02x %02x\n", ctlreg, val);
-#endif
+	int i, val = 0;
+
+	if (cmd == SOUND_MIXER_INFO) {
+		mixer_info info;
+		strncpy(info.id, s->i2s->name, sizeof(info.id));
+		strncpy(info.name, s->i2s->name, sizeof(info.name));
+		info.modify_counter = 0;
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
 	}
+	if (cmd == SOUND_OLD_MIXER_INFO) {
+		_old_mixer_info info;
+		strncpy(info.id, s->i2s->name, sizeof(info.id));
+		strncpy(info.name, s->i2s->name, sizeof(info.name));
+		if (copy_to_user((void *)arg, &info, sizeof(info)))
+			return -EFAULT;
+		return 0;
+	}
+
+	if (_IOC_TYPE(cmd) != 'M' || _SIOC_SIZE(cmd) != sizeof(int))
+		return -EINVAL;
+
+	if (cmd == OSS_GETVERSION)
+		return put_user(SOUND_VERSION, (int *)arg);
+
+	if (_SIOC_DIR(cmd) == _SIOC_READ) {
+		switch (_IOC_NR(cmd)) {
+		case SOUND_MIXER_RECSRC: /* give them the current record src */
+			val = 0;
+			/*
+			if (!codec->recmask_io) {
+				val = 0;
+			} else {
+				val = codec->recmask_io(codec, 1, 0);
+			}*/
+			break;
+
+		case SOUND_MIXER_DEVMASK: /* give them the supported mixers */
+			val = s->i2s->output_mask | s->i2s->input_mask;
+			break;
+
+		case SOUND_MIXER_RECMASK: 
+			/* Arg contains a bit for each supported recording 
+			 * source */
+			val = s->i2s->input_mask;
+			break;
+
+		case SOUND_MIXER_STEREODEVS: 
+			/* Mixer channels supporting stereo */
+			val = s->i2s->output_mask;
+			break;
+
+		case SOUND_MIXER_CAPS:
+			val = SOUND_CAP_EXCL_INPUT;
+			break;
+
+		default: /* read a specific mixer */
+			i = _IOC_NR(cmd);
+
+			if (!supported_mixer(i))
+				return -EINVAL;
+
+			val = s->i2s->get_mixer(s->i2s, i);
+ 			break;
+		}
+		return put_user(val, (int *)arg);
+	}
+
+	if (_SIOC_DIR(cmd) == (_SIOC_WRITE|_SIOC_READ)) {
+		if (get_user(val, (int *)arg))
+			return -EFAULT;
+		switch (_IOC_NR(cmd)) {
+		case SOUND_MIXER_RECSRC: 
+			/* Arg contains a bit for each recording source */
+			if (!s->i2s->input_mask)
+				return -EINVAL;
+			if (!val)
+				return 0;
+
+			if (!(val &= s->i2s->input_mask))
+				return -EINVAL;
+			else 
+				s->i2s->set_recsrc(s->i2s, val);
+
+			return 0;
+		default: /* write a specific mixer */
+			i = _IOC_NR(cmd);
+
+			if (!supported_mixer(i))
+				return -EINVAL;
+
+			s->i2s->set_mixer(s->i2s, i, val);
+
+			return 0;
+	}
+}
+	return -EINVAL;
 }
 
-void
-codec_init(void)
+static loff_t
+au1550_llseek(struct file *file, loff_t offset, int origin)
 {
-	wrcodec(0x1e, 0x00);	/* Reset */
-	au1550_delay(200);
-	wrcodec(0x0c, 0x00);	/* Power up everything */
-	au1550_delay(10);
-	wrcodec(0x12, 0x00);	/* Deactivate codec */
-	au1550_delay(10);
-	wrcodec(0x08, 0x10);	/* Select DAC outputs to line out */
-	au1550_delay(10);
-	wrcodec(0x0a, 0x00);	/* Disable output mute */
-	au1550_delay(10);
-	wrcodec(0x05, 0x70);	/* lower output volume on headphone */
-	au1550_delay(10);
-	wrcodec(0x0e, 0x02);	/* Set slave, 16-bit, I2S modes */
-	au1550_delay(10);
-	wrcodec(0x10, 0x01);	/* 12MHz (USB), 250fs */
-	au1550_delay(10);
-	wrcodec(0x12, 0x01);	/* Activate codec */
-	au1550_delay(10);
+	return -ESPIPE;
 }
 
+static /*const */ struct file_operations au1550_mixer_fops = {
+	owner:THIS_MODULE,
+	llseek:au1550_llseek,
+	ioctl:au1550_ioctl_mixdev,
+	open:au1550_open_mixdev,
+	release:au1550_release_mixdev,
+};
+
+
+
 /* stop the ADC before calling */
 static void
 set_adc_rate(struct au1550_state *s, unsigned rate)
 {
-	struct dmabuf  *adc = &s->dma_adc;
 	struct dmabuf  *dac = &s->dma_dac;
+	struct dmabuf  *adc = &s->dma_adc;
+	unsigned adcr=rate, dacr=dac->sample_rate;
+
 
-	if (s->no_vra) {
+#ifndef try_srcfacor
+	adcr = dacr = rate;
+#endif
+	s->i2s->set_rates(s->i2s, &adcr, &dacr);
+	
+#ifdef try_srcfacor
+	if ( adcr != rate ) {
 		/* calc SRC factor
 		*/
-		adc->src_factor = (((SAMP_RATE*2) / rate) + 1) >> 1;
-		adc->sample_rate = SAMP_RATE / adc->src_factor;
-		return;
+		adc->src_factor = ((96000 / rate) + 1) >> 1;
+		adc->sample_rate = adcr / adc->src_factor;
+	} else {
+		adc->src_factor = 1;
+		adc->sample_rate = adcr;
 	}
 
-	adc->src_factor = 1;
-
-
-#if 0
-	rate = rate > SAMP_RATE ? SAMP_RATE : rate;
-
-	wrcodec(0, 0);	/* I don't yet know what to write here if we vra */
+	/* Check to see if changing ADC rate had effect on DAC rate */
+	if ( dacr != dac->sample_rate ) {
+		/* calc SRC factor
+		*/
+		dac->src_factor = ((96000 / dac->sample_rate) + 1) >> 1;
+		dac->sample_rate = dacr / dac->src_factor;
+	}
+	printk("-----------------------------------------\n");
+	printk("dacr %d Hz adcr %d Hz\n", dacr, adcr);
+	printk("dac_srcfactor %d adc_srcfactor %d\n", dac->src_factor, adc->src_factor );
+	printk("-----------------------------------------\n");
 
-	adc->sample_rate = rate;
-	dac->sample_rate = rate;
 #endif
+	adc->sample_rate = adcr;
+	dac->sample_rate = dacr;
+	dac->src_factor = adc->src_factor = 1;
 }
 
 /* stop the DAC before calling */
@@ -285,25 +382,43 @@
 {
 	struct dmabuf  *dac = &s->dma_dac;
 	struct dmabuf  *adc = &s->dma_adc;
+	unsigned adcr=adc->sample_rate, dacr=rate;
 
-	if (s->no_vra) {
+#ifndef try_srcfacor
+	adcr = dacr = rate;
+#endif
+	s->i2s->set_rates(s->i2s, &adcr, &dacr);
+
+#ifdef try_srcfacor
+	if ( dacr != rate ) {
 		/* calc SRC factor
 		*/
-		dac->src_factor = (((SAMP_RATE*2) / rate) + 1) >> 1;
-		dac->sample_rate = SAMP_RATE / dac->src_factor;
-		return;
+		dac->src_factor = ((96000 / rate) + 1) >> 1;
+		dac->sample_rate = dacr / dac->src_factor;
+	} else {
+		dac->src_factor = 1;
+		dac->sample_rate = dacr;
 	}
 
-	dac->src_factor = 1;
-
-#if 0
-	rate = rate > SAMP_RATE ? SAMP_RATE : rate;
+	/* Check to see if changing DAC rate had effect on ADC rate */
+	if ( adcr != adc->sample_rate ) {
+		/* calc SRC factor
+		*/
+		adc->src_factor = ((96000 / adc->sample_rate) + 1) >> 1;
+		adc->sample_rate = adcr / adc->src_factor;
+	}
 
-	wrcodec(0, 0);	/* I don't yet know what to write here if we vra */
+	printk("-----------------------------------------\n");
+	printk("dacr %d Hz adcr %d Hz\n", dacr, adcr);
+	printk("dac_srcfactor %d adc_srcfactor %d\n", dac->src_factor, adc->src_factor );
+	printk("-----------------------------------------\n");
 
-	adc->sample_rate = rate;
-	dac->sample_rate = rate;
 #endif
+
+	adc->sample_rate = adcr;
+	dac->sample_rate = dacr;
+	dac->src_factor = adc->src_factor = 1;
+
 }
 
 static void
@@ -311,29 +426,16 @@
 {
 	struct dmabuf  *db = &s->dma_dac;
 	unsigned long   flags;
-	uint	stat;
-	volatile psc_i2s_t *ip;
 
 	if (db->stopped)
 		return;
 
-	ip = s->psc_addr;
 	spin_lock_irqsave(&s->lock, flags);
 
-	ip->psc_i2spcr = PSC_I2SPCR_TP;
-	au_sync();
-
-	/* Wait for Transmit Busy to show disabled.
-	*/
-	do {
-		stat = ip->psc_i2sstat;
-		au_sync();
-	} while ((stat & PSC_I2SSTAT_TB) != 0);
-
+	i2s_stop_xmit(s->i2s);
 	au1xxx_dbdma_reset(db->dmanr);
 
 	db->stopped = 1;
-
 	spin_unlock_irqrestore(&s->lock, flags);
 }
 
@@ -342,29 +444,16 @@
 {
 	struct dmabuf  *db = &s->dma_adc;
 	unsigned long   flags;
-	uint	stat;
-	volatile psc_i2s_t *ip;
 
 	if (db->stopped)
 		return;
 
-	ip = s->psc_addr;
 	spin_lock_irqsave(&s->lock, flags);
 
-	ip->psc_i2spcr = PSC_I2SPCR_RP;
-	au_sync();
-
-	/* Wait for Receive Busy to show disabled.
-	*/
-	do {
-		stat = ip->psc_i2sstat;
-		au_sync();
-	} while ((stat & PSC_I2SSTAT_RB) != 0);
-
+	i2s_stop_recv(s->i2s);
 	au1xxx_dbdma_reset(db->dmanr);
 
 	db->stopped = 1;
-
 	spin_unlock_irqrestore(&s->lock, flags);
 }
 
@@ -390,19 +479,14 @@
 {
 	struct dmabuf  *db = &s->dma_dac;
 	unsigned long   flags;
-	volatile psc_i2s_t *ip;
 
 	if (!db->stopped)
 		return;
 
 	spin_lock_irqsave(&s->lock, flags);
 
-	ip = s->psc_addr;
 	set_xmit_slots(db->num_channels);
-	ip->psc_i2spcr = PSC_I2SPCR_TC;
-	au_sync();
-	ip->psc_i2spcr = PSC_I2SPCR_TS;
-	au_sync();
+	i2s_start_xmit(s->i2s);
 
 	au1xxx_dbdma_start(db->dmanr);
 
@@ -416,7 +500,6 @@
 {
 	struct dmabuf  *db = &s->dma_adc;
 	int	i;
-	volatile psc_i2s_t *ip;
 
 	if (!db->stopped)
 		return;
@@ -431,14 +514,9 @@
 			db->nextIn -= db->dmasize;
 	}
 
-	ip = s->psc_addr;
 	set_recv_slots(db->num_channels);
 	au1xxx_dbdma_start(db->dmanr);
-	ip->psc_i2spcr = PSC_I2SPCR_RC;
-	au_sync();
-	ip->psc_i2spcr = PSC_I2SPCR_RS;
-	au_sync();
-
+	i2s_start_recv(s->i2s);
 	db->stopped = 0;
 }
 
@@ -463,7 +541,6 @@
 	if (db->num_channels == 1)
 		db->cnt_factor *= 2;
 	db->cnt_factor *= db->src_factor;
-
 	db->count = 0;
 	db->dma_qcount = 0;
 	db->nextIn = db->nextOut = db->rawbuf;
@@ -502,15 +579,6 @@
 
 	db->dmasize = db->dma_fragsize * db->numfrag;
 	memset(db->rawbuf, 0, bufs);
-
-#ifdef AU1000_VERBOSE_DEBUG
-	dbg("rate=%d, samplesize=%d, channels=%d",
-	    rate, db->sample_size, db->num_channels);
-	dbg("fragsize=%d, cnt_factor=%d, dma_fragsize=%d",
-	    db->fragsize, db->cnt_factor, db->dma_fragsize);
-	dbg("numfrag=%d, dmasize=%d", db->numfrag, db->dmasize);
-#endif
-
 	db->ready = 1;
 	return 0;
 }
@@ -537,21 +605,14 @@
 {
 	struct au1550_state *s = (struct au1550_state *) dev_id;
 	struct dmabuf  *db = &s->dma_dac;
-	u32	i2s_stat;
-	volatile psc_i2s_t *ip;
 
-	ip = s->psc_addr;
-	i2s_stat = ip->psc_i2sstat;
-#ifdef AU1000_VERBOSE_DEBUG
-	if (i2s_stat & (PSC_I2SSTAT_TF | PSC_I2SSTAT_TR | PSC_I2SSTAT_TF))
-		dbg("I2S status = 0x%08x", i2s_stat);
-#endif
+//	i2s_status(s->i2s);
 	db->dma_qcount--;
 
 	if (db->count >= db->fragsize) {
-		if (au1xxx_dbdma_put_source(db->dmanr, db->nextOut,
-							db->fragsize) == 0) {
-			err("qcount < 2 and no ring room!");
+		if (au1xxx_dbdma_put_source(db->dmanr, db->nextOut, db->fragsize) == 0)
+		{
+			err("qcount < MIN_Q_COUNT and no ring room!");
 		}
 		db->nextOut += db->fragsize;
 		if (db->nextOut >= db->rawbuf + db->dmasize)
@@ -606,65 +667,43 @@
 
 }
 
-static loff_t
-au1550_llseek(struct file *file, loff_t offset, int origin)
-{
-	return -ESPIPE;
-}
-
-
-#if 0
-static int
-au1550_open_mixdev(struct inode *inode, struct file *file)
-{
-	file->private_data = &au1550_state;
-	return 0;
-}
-
-static int
-au1550_release_mixdev(struct inode *inode, struct file *file)
-{
-	return 0;
-}
-
-static int
-mixdev_ioctl(struct ac97_codec *codec, unsigned int cmd,
-                        unsigned long arg)
-{
-	return codec->mixer_ioctl(codec, cmd, arg);
-}
-
-static int
-au1550_ioctl_mixdev(struct inode *inode, struct file *file,
-			       unsigned int cmd, unsigned long arg)
-{
-	struct au1550_state *s = (struct au1550_state *)file->private_data;
-	struct ac97_codec *codec = s->codec;
-
-	return mixdev_ioctl(codec, cmd, arg);
-}
-
-static /*const */ struct file_operations au1550_mixer_fops = {
-	owner:THIS_MODULE,
-	llseek:au1550_llseek,
-	ioctl:au1550_ioctl_mixdev,
-	open:au1550_open_mixdev,
-	release:au1550_release_mixdev,
-};
-#endif
-
 static int
 drain_dac(struct au1550_state *s, int nonblock)
 {
 	unsigned long   flags;
 	int             count, tmo;
 
+	struct dmabuf  *db = &s->dma_dac;
+
+	//DPRINTF();
 	if (s->dma_dac.mapped || !s->dma_dac.ready || s->dma_dac.stopped)
 		return 0;
 
 	for (;;) {
 		spin_lock_irqsave(&s->lock, flags);
-		count = s->dma_dac.count;
+		count = db->count;
+
+		/* Pad the ddma buffer with zeros if the amount remaining 
+		 * is not a multiple of fragsize */
+		if(count % db->fragsize != 0)
+		{
+			int pad = db->fragsize - (count % db->fragsize);
+			char* bufptr = db->nextIn;
+			char* bufend = db->rawbuf + db->dmasize;
+
+			if((bufend - bufptr) < pad)
+				printk("Error!  ddma padding is bigger than available ring space!\n");
+			else
+			{
+				memset((void*)bufptr, 0, pad);
+				count += pad;
+				db->nextIn += pad;
+				db->count += pad;
+				if (db->dma_qcount == 0)
+						start_dac(s);
+				db->dma_qcount++;
+			}
+		}
 		spin_unlock_irqrestore(&s->lock, flags);
 		if (count <= 0)
 			break;
@@ -672,13 +711,15 @@
 			break;
 		if (nonblock)
 			return -EBUSY;
-		tmo = 1000 * count / (s->no_vra ?
-				      SAMP_RATE : s->dma_dac.sample_rate);
+		tmo = 1000 * count / ((s->dma_dac.src_factor!=1) ?
+				      s->i2s->sr : s->dma_dac.sample_rate);
+//		tmo = 1000 * count / s->dma_dac.sample_rate;
 		tmo /= s->dma_dac.dma_bytes_per_sample;
 		au1550_delay(tmo);
 	}
 	if (signal_pending(current))
 		return -ERESTARTSYS;
+
 	return 0;
 }
 
@@ -698,8 +739,7 @@
  *     If interpolating (no VRA), duplicate every audio frame src_factor times.
  */
 static int
-translate_from_user(struct dmabuf *db, char* dmabuf, char* userbuf,
-							       int dmacount)
+translate_from_user(struct dmabuf *db, char* dmabuf, char* userbuf, int dmacount)
 {
 	int             sample, i;
 	int             interp_bytes_per_sample;
@@ -737,11 +777,12 @@
 
 		/* duplicate every audio frame src_factor times
 		*/
-		for (i = 0; i < db->src_factor; i++)
+		for (i = 0; i < db->src_factor; i++) {
 			memcpy(dmabuf, dmasample, db->dma_bytes_per_sample);
+			dmabuf += interp_bytes_per_sample;
+		}
 
 		userbuf += db->user_bytes_per_sample;
-		dmabuf += interp_bytes_per_sample;
 	}
 
 	return num_samples * interp_bytes_per_sample;
@@ -996,15 +1037,14 @@
 		 * 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 < MIN_Q_COUNT) && (db->count >= db->fragsize)) {
 			if (au1xxx_dbdma_put_source(db->dmanr, db->nextOut,
 							db->fragsize) == 0) {
-				err("qcount < 2 and no ring room!");
+				err("qcount < MIN_Q_COUNT and no ring room!");
 			}
 			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;
 			if (db->dma_qcount == 0)
 				start_dac(s);
@@ -1017,7 +1057,6 @@
 		buffer += usercnt;
 		ret += usercnt;
 	}			/* while (count > 0) */
-
 out:
 	up(&s->sem);
 out2:
@@ -1237,6 +1276,7 @@
 				if ((ret = prog_dmabuf_dac(s)))
 					return ret;
 		}
+
 		return put_user((file->f_mode & FMODE_READ) ?
 				s->dma_adc.sample_rate :
 				s->dma_dac.sample_rate,
@@ -1371,9 +1411,6 @@
 			s->dma_dac.cnt_factor;
 		abinfo.fragstotal = s->dma_dac.numfrag;
 		abinfo.fragments = abinfo.bytes >> s->dma_dac.fragshift;
-#ifdef AU1000_VERBOSE_DEBUG
-		dbg("bytes=%d, fragments=%d", abinfo.bytes, abinfo.fragments);
-#endif
 		return copy_to_user((void *) arg, &abinfo,
 				    sizeof(abinfo)) ? -EFAULT : 0;
 
@@ -1536,13 +1573,9 @@
 	case SNDCTL_DSP_SETSYNCRO:
 	case SOUND_PCM_READ_FILTER:
 		return -EINVAL;
+	default: break;
 	}
-
-#if 0
-	return mixdev_ioctl(s->codec, cmd, arg);
-#else
 	return 0;
-#endif
 }
 
 
@@ -1664,54 +1697,11 @@
 MODULE_AUTHOR("Advanced Micro Devices (AMD), dan@embeddededge.com");
 MODULE_DESCRIPTION("Au1550 Audio Driver");
 
-/* Set up an internal clock for the PSC3.  This will then get
- * driven out of the Au1550 as the master.
- */
-static void
-intclk_setup(void)
-{
-	uint	clk, rate, stat;
-
-	/* Wire up Freq4 as a clock for the PSC3.
-	 * We know SMBus uses Freq3.
-	 * By making changes to this rate, plus the word strobe
-	 * size, we can make fine adjustments to the actual data rate.
-	 */
-	rate = get_au1x00_speed();
-#ifdef TRY_441KHz
-	rate /= (11 * 1000000);
-#else
-	rate /= (12 * 1000000);
-#endif
-
-	/* The FRDIV in the frequency control is (FRDIV + 1) * 2
-	*/
-	rate /=2;
-	rate--;
-	clk = au_readl(SYS_FREQCTRL1);
-	au_sync();
-	clk &= ~(SYS_FC_FRDIV4_MASK | SYS_FC_FS4);;
-	clk |= (rate << SYS_FC_FRDIV4_BIT);
-	clk |= SYS_FC_FE4;
-	au_writel(clk, SYS_FREQCTRL1);
-	au_sync();
-
-	/* Set up the clock source routing to get Freq4 to PSC3_intclk.
-	*/
-	clk = au_readl(SYS_CLKSRC);
-	au_sync();
-	clk &= ~0x01f00000;
-	clk |= (6 << 22);
-	au_writel(clk, SYS_CLKSRC);
-	au_sync();
-}
 
 static int __devinit
 au1550_probe(void)
 {
 	struct au1550_state *s = &au1550_state;
-	int             val;
-	volatile psc_i2s_t *ip;
 #ifdef AU1550_DEBUG
 	char            proc_str[80];
 #endif
@@ -1724,14 +1714,7 @@
 	init_MUTEX(&s->open_sem);
 	spin_lock_init(&s->lock);
 
-
-	s->psc_addr = (volatile psc_i2s_t *)I2S_PSC_BASE;
-	ip = s->psc_addr;
-
-	if (!request_region(PHYSADDR(ip),
-			    0x30, AU1550_MODULE_NAME)) {
-		err("I2S Audio ports in use");
-	}
+	s->i2s = i2s_hw_init(); 
 
 	/* Allocate the DMA Channels
 	*/
@@ -1759,17 +1742,15 @@
 		goto err_dma2;
 	}
 
+	info("%s CODEC", s->i2s->name );
 	info("DAC: DMA%d, ADC: DMA%d", DBDMA_I2S_TX_CHAN, DBDMA_I2S_RX_CHAN);
 
 	/* register devices */
-
 	if ((s->dev_audio = register_sound_dsp(&au1550_audio_fops, -1)) < 0)
 		goto err_dev1;
-#if 0
-	if ((s->codec->dev_mixer =
-	     register_sound_mixer(&au1550_mixer_fops, -1)) < 0)
+
+	if ((s->dev_mixer = register_sound_mixer(&au1550_mixer_fops, -1)) < 0)
 		goto err_dev2;
-#endif
 
 #ifdef AU1550_DEBUG
 	/* intialize the debug proc device */
@@ -1777,86 +1758,10 @@
 				       proc_au1550_dump, NULL);
 #endif /* AU1550_DEBUG */
 
-	intclk_setup();
-
-	/* The GPIO for the appropriate PSC was configured by the
-	 * board specific start up.
-	 *
-	 * configure PSC for I2S Audio
-	 */
-	ip->psc_ctrl = PSC_CTRL_DISABLE;	/* Disable PSC */
-	au_sync();
-	ip->psc_sel = (PSC_SEL_CLK_INTCLK | PSC_SEL_PS_I2SMODE);
-	au_sync();
-
-	/* Enable PSC
-	*/
-	ip->psc_ctrl = PSC_CTRL_ENABLE;
-	au_sync();
-
-	/* Wait for PSC ready.
-	*/
-	do {
-		val = ip->psc_i2sstat;
-		au_sync();
-	} while ((val & PSC_I2SSTAT_SR) == 0);
-
-	/* Configure I2S controller.
-	 * Deep FIFO, 16-bit sample, DMA, make sure DMA matches fifo size.
-	 * Actual I2S mode (first bit delayed by one clock).
-	 * Master mode (We provide the clock from the PSC).
-	 */
-	val = PSC_I2SCFG_SET_LEN(16);
-#ifdef TRY_441KHz
-	/* This really should be 250, but it appears that all of the
-	 * PLLs, dividers and so on in the chain shift it.  That's the
-	 * problem with sourceing the clock instead of letting the very
-	 * stable codec provide it.  But, the PSC doesn't appear to want
-	 * to work in slave mode, so this is what we get.  It's  not
-	 * studio quality timing, but it's good enough for listening
-	 * to mp3s.
-	 */
-	val |= PSC_I2SCFG_SET_WS(252);
-#else
-	val |= PSC_I2SCFG_SET_WS(250);
-#endif
-	val |= PSC_I2SCFG_RT_FIFO8 | PSC_I2SCFG_TT_FIFO8 | \
-					PSC_I2SCFG_BI | PSC_I2SCFG_XM;
-
-	ip->psc_i2scfg = val;
-	au_sync();
-	val |= PSC_I2SCFG_DE_ENABLE;
-	ip->psc_i2scfg = val;
-	au_sync();
-
-	/* Wait for Device ready.
-	*/
-	do {
-		val = ip->psc_i2sstat;
-		au_sync();
-	} while ((val & PSC_I2SSTAT_DR) == 0);
-
-	val = ip->psc_i2scfg;
-	au_sync();
-
-	codec_init();
-
-	s->no_vra = 1;
-	if (s->no_vra)
-		info("no VRA, interpolating and decimating");
-
-#if 0
-	/* set mic to be the recording source */
-	val = SOUND_MASK_MIC;
-	mixdev_ioctl(s->codec, SOUND_MIXER_WRITE_RECSRC,
-		     (unsigned long) &val);
-#ifdef AU1550_DEBUG
-	sprintf(proc_str, "driver/%s/%d/ac97", AU1550_MODULE_NAME,
-		s->codec->id);
-	s->ac97_ps = create_proc_read_entry (proc_str, 0, NULL,
-					     ac97_read_proc, &s->codec);
-#endif
-#endif
+	/* Initial settings for sample rates */
+	s->dma_dac.sample_rate = s->dma_adc.sample_rate = 8000;
+	set_adc_rate(s, 8000);  //Set default rate
+	set_dac_rate(s, 8000);  //Set default rate
 
 	return 0;
 
@@ -1866,12 +1771,14 @@
  err_dev2:
 	unregister_sound_dsp(s->dev_audio);
 #endif
+ err_dev2:
+	unregister_sound_dsp(s->dev_audio);
  err_dev1:
 	au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
  err_dma2:
 	au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
  err_dma1:
-	release_region(PHYSADDR(I2S_PSC_BASE), 0x30);
+	i2s_hw_cleanup(s->i2s);
 
 	return -1;
 }
@@ -1890,7 +1797,7 @@
 	synchronize_irq();
 	au1xxx_dbdma_chan_free(s->dma_adc.dmanr);
 	au1xxx_dbdma_chan_free(s->dma_dac.dmanr);
-	release_region(PHYSADDR(I2S_PSC_BASE), 0x30);
+	i2s_hw_cleanup(s->i2s);
 	unregister_sound_dsp(s->dev_audio);
 #if 0
 	unregister_sound_mixer(s->codec->dev_mixer);
diff -Nur linux-2.4.30/drivers/sound/au1550_i2s.h linux-2.4.30-linux-mips.org/drivers/sound/au1550_i2s.h
--- linux-2.4.30/drivers/sound/au1550_i2s.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/sound/au1550_i2s.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,131 @@
+/*
+ *      au1550_i2s_hw.c  --  Sound driver support for AMD Alchemy 
+ *   			Au1550/Au1200 MIPS Processor Cores
+ *
+ */
+
+#ifndef _AU1550_I2S_H
+#define _AU1550_I2S_H
+
+#if defined(CONFIG_MIPS_PB1200)
+	#include <asm/pb1200.h>
+	#define WM8731
+#endif
+
+#if defined(CONFIG_MIPS_DB1200)
+	#include <asm/db1200.h>
+	#define WM8731
+#endif
+
+#if defined(CONFIG_MIPS_PB1550)
+	#include <asm/pb1550.h>
+	#define WM8731
+#endif
+
+#if defined(CONFIG_MIPS_DB1550)
+	#include <asm/db1x00.h>
+	#define WM8731
+#endif
+
+#if defined(CONFIG_MIPS_FICMMP)
+	#include <asm/ficmmp.h>
+	#define WM8721
+#endif
+
+
+#define AU1XXX_MODULE_NAME "Au1xxx i2s audio"
+#define PFX AU1XXX_MODULE_NAME
+
+#define err(format, arg...) printk(KERN_ERR PFX ": " format "\n" , ## arg)
+#define info(format, arg...) printk(KERN_INFO PFX ": " format "\n" , ## arg)
+
+#define supported_mixer(FOO) ((FOO >= 0) && \
+                                    (FOO < SOUND_MIXER_NRDEVICES) && \
+                                    (s->i2s->input_mask | s->i2s->output_mask) & (1<<FOO) )
+
+
+struct i2s_hw {
+	char *name;
+	int	  codec_is_master;
+	int   input_mask;
+	int   output_mask;
+	volatile psc_i2s_t *psc;
+	unsigned int sr;
+	void  (*codec_init)(struct i2s_hw *hw);
+	void  (*wrcodec)(u8 ctlreg, u16 val);
+	int   (*set_rates)(struct i2s_hw *hw, u32 *in, u32 *out);
+	int   (*get_mixer)(struct i2s_hw *hw, u16 oss_channel);
+	void  (*set_mixer)(struct i2s_hw *hw, u16 oss_channel, u16 val);
+	void  (*set_recsrc)(struct i2s_hw *hw, int src);
+};
+
+/* Prototypes */
+struct i2s_hw * i2s_hw_init(void);
+void i2s_hw_cleanup(struct i2s_hw *hw);
+
+/* Helper functions */
+static inline void i2s_stop_xmit(struct i2s_hw *hw)
+{
+	volatile psc_i2s_t *ip = hw->psc;
+	u32 stat;
+
+	ip->psc_i2spcr = PSC_I2SPCR_TP;
+	au_sync();
+
+	/* Wait for Transmit Busy to show disabled.
+	*/
+	do {
+		stat = ip->psc_i2sstat;
+		au_sync();
+	} while ((stat & PSC_I2SSTAT_TB) != 0);
+}	
+	
+static inline void i2s_stop_recv(struct i2s_hw *hw)
+{
+	volatile psc_i2s_t *ip = hw->psc;
+	u32 stat;
+
+	ip->psc_i2spcr = PSC_I2SPCR_RP;
+	au_sync();
+
+	/* Wait for Transmit Busy to show disabled.
+	*/
+	do {
+		stat = ip->psc_i2sstat;
+		au_sync();
+	} while ((stat & PSC_I2SSTAT_RB) != 0);
+}	
+
+static inline void i2s_start_xmit(struct i2s_hw *hw)
+{
+	volatile psc_i2s_t *ip = hw->psc;
+	ip->psc_i2spcr = PSC_I2SPCR_TC;
+	au_sync();
+	ip->psc_i2spcr = PSC_I2SPCR_TS;
+	au_sync();
+}
+
+static inline void i2s_start_recv(struct i2s_hw *hw)
+{
+	volatile psc_i2s_t *ip = hw->psc;
+	ip->psc_i2spcr = PSC_I2SPCR_RC;
+	au_sync();
+	ip->psc_i2spcr = PSC_I2SPCR_RS;
+	au_sync();
+}
+
+static inline unsigned i2s_status(struct i2s_hw *hw)
+{
+	volatile psc_i2s_t *ip = hw->psc;
+	u32	i2s_stat;
+
+	i2s_stat = ip->psc_i2sstat;
+#ifdef AU1000_VERBOSE_DEBUG
+	if (i2s_stat & (PSC_I2SSTAT_TF | PSC_I2SSTAT_TR | PSC_I2SSTAT_TF))
+		dbg("I2S status = 0x%08x", i2s_stat);
+#endif
+
+	return(i2s_stat);
+}
+
+#endif
diff -Nur linux-2.4.30/drivers/sound/au1550_i2s_hw.c linux-2.4.30-linux-mips.org/drivers/sound/au1550_i2s_hw.c
--- linux-2.4.30/drivers/sound/au1550_i2s_hw.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/sound/au1550_i2s_hw.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,664 @@
+/*
+ *      au1550_i2s_hw.c  --  Sound driver support for AMD Alchemy 
+ *   			Au1550/Au1200 MIPS Processor Cores
+ *
+ * The WM8731 CODEC only has one input for ADC recording device. Below
+ * is a snipet of code that illustrates how to swtich between MIC and
+ * LINE for WM8731. This can be used for other CODECs as long as the
+ * set_recsrc() routine is implemented properly.
+ *
+		#include <unistd.h>
+		#include <stdlib.h>
+		#include <stdio.h>
+		#include <fcntl.h>
+		#include <sys/soundcard.h>
+		#include <sys/ioctl.h>
+
+		int main (int argc, char **argv)
+		{
+			char *mixer="/dev/mixer";
+			int fpmixer, insel=SOUND_MASK_LINE;
+			int val;
+
+			fpmixer = open(mixer, O_RDWR );
+			if (-1 == fpmixer) {
+				return -1;
+			}
+			if ( argc > 1 ) insel = SOUND_MASK_MIC;
+			ioctl(fpmixer, SOUND_MIXER_WRITE_RECSRC, &insel);
+			return 0;
+		}
+*/
+
+
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/sound.h>
+#include <linux/slab.h>
+#include <linux/soundcard.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <asm/hardirq.h>
+#include <asm/au1000.h>
+
+#include <asm/au1xxx_psc.h>
+
+#include "au1550_i2s.h"
+
+
+#if !defined(CONFIG_I2C_ALGO_AU1550)
+	#error 
+	#error 
+	#error I2C Support is required for I2S Codec configuration. \
+	Please enable Au1xxx I2C support and rebuild.
+	#error 
+	#error 
+#endif
+
+static void
+au1550_delay(int msec)
+{
+	unsigned long   tmo;
+	signed long     tmo2;
+
+	if (in_interrupt())
+		return;
+
+	tmo = jiffies + (msec * HZ) / 1000;
+	for (;;) {
+		tmo2 = tmo - jiffies;
+		if (tmo2 <= 0)
+			break;
+		schedule_timeout(tmo2);
+	}
+}
+
+#if defined(WM8731) || defined(WM8721)
+
+#define WM_VOLUME_MIN	47
+#define WM_VOLUME_SCALE	80
+
+volatile static struct i2s_ro_mixer {
+	unsigned int value;
+	unsigned int scale;
+	unsigned int hw_min;
+} i2s_ro_mixer[SOUND_MIXER_NRDEVICES]= {
+	[SOUND_MIXER_PCM]	=	{0x5050,79,0x30},
+#if defined(WM8731)
+	[SOUND_MIXER_LINE]	=	{0x4343,31,0},
+	[SOUND_MIXER_MIC]	=	{0x4343,31,0},
+#endif
+};
+
+#define WM_SC_SR_96000		(0x7<<2)
+#define WM_SC_SR_88200		(0xF<<2)
+#define WM_SC_SR_48000		(0x0<<2)
+#define WM_SC_SR_44100		(0x8<<2)
+#define WM_SC_SR_32000		(0x6<<2)
+#define WM_SC_SR_8018		(0x9<<2)
+#define WM_SC_SR_8000		(0x1<<2)
+#define WM_SC_MODE_USB		1
+#define WM_SC_MODE_NORMAL	0
+#define WM_SC_BOSR_250FS	(0<<1)
+#define WM_SC_BOSR_272FS	(1<<1)
+#define WM_SC_BOSR_256FS	(0<<1)
+#define WM_SC_BOSR_128FS	(0<<1)
+#define WM_SC_BOSR_384FS	(1<<1)
+#define WM_SC_BOSR_192FS	(1<<1)
+
+enum wolfson_registers {
+	L_LINE_IN=0,
+	R_LINE_IN,
+	L_HEADPHONE_OUT,			
+	R_HEADPHONE_OUT,			
+	ANALOGUE_AUDIO_PATH_CTRL,
+	DIGITAL_AUDIO_PATH_CTRL,	
+	POWER_DOWN_CTRL,
+	DIGITAL_AUDIO_IF,			
+	SAMPLING_CONTROL, 		
+	ACTIVE_CTRL,
+	RESET=0xF };
+
+static void wm87x1_wrcodec(u8 ctlreg, u16 val)
+{
+	int	rcnt;
+	
+	extern int pb1550_wm_codec_write(u8 addr, u8 reg, u8 val);
+	/* The codec is a write only device, with a 16-bit control/data
+	 * word.  Although it is written as two bytes on the I2C, the
+	 * format is actually 7 bits of register and 9 bits of data.
+	 * The ls bit of the first byte is the ms bit of the data.
+	 */
+	rcnt = 0;
+	while ((pb1550_wm_codec_write((0x36 >> 1), 
+					(ctlreg << 1) | ((val >> 8) & 0x01), 
+					(u8) (val & 0x00FF)) != 1) && 
+			(rcnt < 50)) {
+		rcnt++;
+	}
+	if ( rcnt >= 50 ) printk("timeout wm87x1_wrcodec: reg:%x val:%x\n", ctlreg, val);
+
+	au1550_delay(10);
+}
+
+
+static void wm87x1_set_recsrc(struct i2s_hw *hw, int src)
+{
+	u16 val=0;
+	hw->wrcodec(ACTIVE_CTRL, 0x00);	 	/* Deactivate codec */
+
+#if defined(WM8731)
+	/* if its not MIC, then it must be LINEIN. The check for available mask
+	   should ahve been done at the ioctl level
+	 */
+	if ( src & SOUND_MASK_MIC )	
+		val = 5; 
+	hw->wrcodec(ANALOGUE_AUDIO_PATH_CTRL, 0x18 | val);	/* DAC sel, BYPASS and INSEL(val) */
+#endif
+
+	hw->wrcodec(ACTIVE_CTRL, 0x01);	 	/* Activate codec */
+}
+
+static int wm87x1_read_mixer(struct i2s_hw *hw, u16 oss_channel)
+{
+    int left, right;
+/*	int scale = i2s_ro_mixer[oss_channel].scale; */
+
+	left = (i2s_ro_mixer[oss_channel].value>>8)&0x7f;
+	right = (i2s_ro_mixer[oss_channel].value)&0x7f;
+
+	return (left | (right << 8));
+}
+
+static void wm87x1_write_mixer(struct i2s_hw *hw, u16 oss_channel, u16 val)
+{
+	int scale = i2s_ro_mixer[oss_channel].scale;
+	unsigned int left,right;
+
+	/* cleanse input a little */
+	right = ((val >> 8)  & 0xff) ;
+	left = (val  & 0xff) ;
+
+	if (right > 100) right = 100;
+	if (left > 100) left = 100;
+
+	i2s_ro_mixer[oss_channel].value = left<<8 | right;
+
+	right = (right * scale) / 100;
+	left = (left * scale) / 100;
+
+	right += i2s_ro_mixer[oss_channel].hw_min;
+	left  += i2s_ro_mixer[oss_channel].hw_min;
+
+	printk("write_mixer, chan:%d val:%d\n", oss_channel, val);
+	switch( oss_channel ) {
+	case SOUND_MIXER_PCM:
+		hw->wrcodec(L_HEADPHONE_OUT, left);
+		hw->wrcodec(R_HEADPHONE_OUT, right);
+		break;
+	case SOUND_MIXER_LINE:
+		hw->wrcodec(L_LINE_IN, left);
+		hw->wrcodec(R_LINE_IN, right);
+		break;
+	}
+}
+
+#endif
+
+#if defined(WM8731) || defined(WM8721)
+static void wm87x1_codec_init(struct i2s_hw *hw)
+{
+	hw->wrcodec(RESET, 0x00);				/* Reset */
+	hw->wrcodec(POWER_DOWN_CTRL, 0x00); 	/* Power up everything */
+	hw->wrcodec(ACTIVE_CTRL, 0x00);	 	/* Deactivate codec */
+
+	/* 
+	 *	Setup some default mixer settings :)
+	 */
+	hw->set_mixer(hw, SOUND_MIXER_PCM, (54<<8)|54);
+#if !defined(WM8721)
+	hw->set_mixer(hw, SOUND_MIXER_MIC, (74<<8)|74);
+	hw->set_mixer(hw, SOUND_MIXER_LINE, (74<<8)|74);
+
+	hw->wrcodec(ANALOGUE_AUDIO_PATH_CTRL, 0x18);	/* DAC sel, BYPASS and LINEIN */
+#else
+	hw->wrcodec(ANALOGUE_AUDIO_PATH_CTRL, 0x10);	/* DAC sel */
+#endif
+
+	hw->wrcodec(DIGITAL_AUDIO_PATH_CTRL, 0x00);	/* Disable output mute */
+
+	/* Set slave/master mode in codec */
+	if(hw->codec_is_master)
+		hw->wrcodec(DIGITAL_AUDIO_IF, 0x42);/* Set MASTER, 16-bit, I2S modes */
+	else
+		hw->wrcodec(DIGITAL_AUDIO_IF, 0x02);/* Set SLAVE,  16-bit, I2S modes */
+	
+	hw->wrcodec(SAMPLING_CONTROL, 0x23);	/* Default SR */
+	hw->wrcodec(ACTIVE_CTRL, 0x01);	 	/* Activate codec */
+}
+#endif
+
+#if defined(WM8731)
+
+/*
+ *	The WM8731 I2S Codec cannot use completly different sample
+ *	rate for ADC and DAC. There is small table in a Wolfson
+ *  whitepaper "Master clocks and audio sample rates supported
+ *  by Wolfson's Portable Audio IC's". 
+ * 
+ *  ASSUMES 12MHZ and USB mode settings for CODEC!!
+ *  ASSUMES 12MHZ and USB mode settings for CODEC!!
+ *
+ *  The function below implements the combinations specified in 
+ *  the whitepaper.
+ *
+ */
+static int
+wm8731_set_rates( struct i2s_hw *hw, unsigned int *in, unsigned int *out )
+{
+	volatile psc_i2s_t* ip = (volatile psc_i2s_t *)I2S_PSC_BASE;
+	u32 cfg=0;
+	u16 codec_cfg=0;
+	unsigned int adc=*in, dac=*out;
+  
+    /* Deactivate codec */
+	hw->wrcodec(ACTIVE_CTRL, 0x00);	/* Deactivate codec */
+
+	/*
+	 *	If the PSC is master, then we need to disable PSC before
+	 *  updating registers.
+	 */
+	if ( !(hw->codec_is_master) )	{
+		/* Disable I2S controller */
+		ip->psc_i2scfg &= ~PSC_I2SCFG_DE_ENABLE;
+		au_sync();
+		/* Wait for device disabled */
+		while ((ip->psc_i2sstat & PSC_I2SSTAT_DR) == 1);
+		cfg = ip->psc_i2scfg;
+		/* Clear WS and DIVIDER values */
+		cfg &= ~(PSC_I2SCFG_WS_MASK | PSC_I2SCFG_DIV_MASK);	
+	}
+
+	if ( adc == 8000 && dac == 8000 ) { 
+		// for 8000 USB=1 BOSR=1 fs=1500
+		cfg |= PSC_I2SCFG_SET_WS(94);
+		cfg |= PSC_I2SCFG_DIV16;
+		codec_cfg = (0x03<<2) | WM_SC_MODE_USB;
+	}
+	/* We cannot get 11025 with codec as master --- that I know of */
+	else if ( !hw->codec_is_master && adc == 11025 && dac == 11025 ) { 
+		// for 11025 USB=1 BOSR=1 fs=272
+		cfg |= PSC_I2SCFG_SET_WS(68);
+		cfg |= PSC_I2SCFG_DIV16;
+		codec_cfg = WM_SC_SR_44100 | WM_SC_BOSR_272FS | WM_SC_MODE_USB;
+	}
+	/* We cannot get 22050 with codec as master --- that I know of */
+	else if ( !hw->codec_is_master && adc == 22050 && dac == 22050 ) { 
+		// for 22050 USB=1 BOSR=1 fs=544.22
+		cfg |= PSC_I2SCFG_SET_WS(68);
+		cfg |= PSC_I2SCFG_DIV8;
+        codec_cfg = WM_SC_SR_44100 | WM_SC_BOSR_272FS | WM_SC_MODE_USB;
+	}
+	else if ( adc == 32000 && dac == 32000 ) { 
+		// for 32000 USB=1 BOSR=1 fs=375
+		cfg |= PSC_I2SCFG_SET_WS(94);
+		cfg |= PSC_I2SCFG_DIV4;
+		codec_cfg = WM_SC_SR_32000 | WM_SC_BOSR_272FS | WM_SC_MODE_USB;
+	}
+	else if ( adc == 44100 && dac == 44100 ) { 
+		// for 44100 USB=1 BOSR=1 fs=272
+		cfg |= PSC_I2SCFG_SET_WS(68);
+		cfg |= PSC_I2SCFG_DIV4;
+		codec_cfg = WM_SC_SR_44100 | WM_SC_BOSR_272FS | WM_SC_MODE_USB;
+	}
+	else if ( adc == 48000 && dac == 48000 ) { 
+		// for 48 USB=1 BOSR=0 fs=250
+		cfg |= PSC_I2SCFG_SET_WS(125);
+		cfg |= PSC_I2SCFG_DIV2;
+		codec_cfg = WM_SC_SR_48000 | WM_SC_MODE_USB;
+	}
+	else if ( adc == 88200 && dac == 88200 ) { 
+		// for 88200 USB=1 BOSR=1 fs=136
+		cfg |= PSC_I2SCFG_SET_WS(68);
+		cfg |= PSC_I2SCFG_DIV2;
+		codec_cfg = WM_SC_SR_88200 | WM_SC_BOSR_272FS | WM_SC_MODE_USB;
+	}
+	else if ( adc == 96000 && dac == 96000 ) { 
+		// for 96000 USB=1 BOSR=0 fs=125
+		cfg |= PSC_I2SCFG_SET_WS(63);
+		cfg |= PSC_I2SCFG_DIV2;
+		codec_cfg = WM_SC_SR_96000 | WM_SC_MODE_USB;
+	}
+	/*
+	 *	This is the default sample rate for this platform. 
+	 *  There is a little bit of code duplication here.... but wanted to
+	 *  make it easy to replace defaults. Im sure there is a better way
+	 *  but I'm in a hurry :)
+	 */
+	else  {
+		// for 48 USB=1 BOSR=0 fs=250
+		cfg |= PSC_I2SCFG_SET_WS(125);
+		cfg |= PSC_I2SCFG_DIV2;
+		codec_cfg = WM_SC_SR_48000 | WM_SC_MODE_USB;
+		hw->sr = adc = dac = 48000;
+	}
+
+	if (!(hw->codec_is_master)){
+		/* Reconfigure and enable */
+		ip->psc_i2scfg = cfg;
+		au_sync();
+		ip->psc_i2scfg = cfg  | PSC_I2SCFG_DE_ENABLE;
+		au_sync();
+		/* Wait for device enabled */
+		while ((ip->psc_i2sstat & PSC_I2SSTAT_DR) == 0);
+	}
+
+	hw->wrcodec(SAMPLING_CONTROL, codec_cfg);
+
+	/* Activate codec */
+	hw->wrcodec(ACTIVE_CTRL, 0x01);
+
+	*in = adc;
+	*out = dac;
+	return 0;
+}
+#endif
+
+#if defined(WM8721)
+/*
+ *	The WM8721 I2S Codec does not support ADC.
+ * 
+ *	*** ASSUMES we are operating in NORMAL -- variable clocks
+ *      ASSUMES CODEC is always the master
+ *
+ */
+static int 
+wm8721_set_rates( struct i2s_hw *hw, unsigned int *in, unsigned int *out )
+{
+	u16 codec_cfg=0;
+	unsigned int dac=*out;
+
+#if defined(CONFIG_MIPS_FICMMP)
+	ficmmp_set_i2s_sample_rate(dac);
+#endif
+
+    /* Deactivate codec */
+	hw->wrcodec(ACTIVE_CTRL, 0x00);	/* Deactivate codec */
+
+	switch( dac ) {
+		case 8000:
+			codec_cfg = WM_SC_SR_8000;
+			break;
+		case 8018:			
+			codec_cfg = WM_SC_SR_8018;
+			break;
+		case 32000:
+			codec_cfg = WM_SC_SR_32000;
+			break;
+		case 44100:
+			codec_cfg = WM_SC_SR_44100;
+			break;
+		case 48000:
+			codec_cfg = WM_SC_SR_48000;
+			break;
+		case 88200:
+			codec_cfg = WM_SC_SR_88200;
+			break;
+		case 96000:
+			codec_cfg = WM_SC_SR_96000;
+			break;
+
+	}
+	hw->wrcodec(SAMPLING_CONTROL, codec_cfg);
+
+	/* Activate codec */
+	hw->wrcodec(ACTIVE_CTRL, 0x01);
+
+	*in = 0;
+	*out = dac;
+	return 0;
+}
+#endif
+
+
+
+/*
+ *	The meat of the HW init.
+ */
+#if defined(CONFIG_MIPS_PB1200) || defined(CONFIG_MIPS_DB1200) \
+    || defined(CONFIG_MIPS_PB1550) || defined(CONFIG_MIPS_DB1550)
+
+static struct i2s_hw _i2s =	{ 
+	.name 			= "Wolfson WM8731 i2s", 
+	.input_mask 	= SOUND_MASK_LINE | SOUND_MASK_MIC,
+	.output_mask	= SOUND_MASK_PCM,
+	.codec_init		= wm87x1_codec_init, 
+	.wrcodec		= wm87x1_wrcodec, 
+	.set_rates		= wm8731_set_rates, 
+	.get_mixer		= wm87x1_read_mixer, 
+	.set_mixer		= wm87x1_write_mixer,
+	.set_recsrc		= wm87x1_set_recsrc };
+
+static struct i2s_hw * AMD_PbDb_setup(u32 psc, u32 clock) 
+{
+	volatile psc_i2s_t *ip = (volatile psc_i2s_t *)psc;
+	u32 div, clk,val;
+	struct i2s_hw *i2s=&_i2s;
+
+	i2s->psc = ip;
+	ip->psc_ctrl = PSC_CTRL_DISABLE;	/* Disable PSC */
+	au_sync();
+	
+#if defined(CONFIG_MIPS_PB1200) || defined(CONFIG_MIPS_DB1200)
+	/* CPLD Mux for I2s */
+	bcsr->resets |= BCSR_RESETS_PCS1MUX;
+	au_sync();
+#endif
+
+	/*
+	  	Clocking for a PSC in I2S mode can be complicated due to the
+	    numerous schemes available.
+	 
+	    The Pb/Db1200 and Pb/Db1550 have a 12Mhz clock off the codec
+	    with the codec outputing the clock on CLK_OUT pin. THe PSC uses
+		this clock as its internal clock.
+
+		Pb1200 Option :
+		Another option would be to disconnect the 12Mhz clock,X4, hanging
+		off the Wolfson CODEC and populate resistors R153 and R155
+		and unpopulate R167.
+		The PSC would need to be configured to output its clock on GPIO2.
+		Please refer to the databook for more detailed information.
+		The resistor locations are different on the Pb/Db1550 but the
+		same concept applies.
+	 */
+	switch (clock)	{
+		case PSC_SEL_CLK_INTCLK:
+			i2s->codec_is_master = 0;
+
+			/* The FRDIV in the frequency control is (FRDIV + 1) * 2
+			   Our goal is to have intclk==12MHz
+			*/
+			clk = au_readl(SYS_FREQCTRL1);
+			div = au_readl(SYS_AUXPLL);
+			au_sync();
+			clk &= ~(SYS_FC_FRDIV4_MASK | SYS_FC_FS4);;
+			clk |= (div << SYS_FC_FRDIV4_BIT);
+			clk |= SYS_FC_FE4;
+			au_writel(clk, SYS_FREQCTRL1);
+			au_sync();
+
+			/* Set up the clock source routing to get Freq4 to PSC1_intclk.
+			*/
+			clk = au_readl(SYS_CLKSRC);
+			au_sync();
+			clk &= ~0x3e000000;
+			clk |= (6 << 27); /* 6 == Freq4, refer to databook pg 451 */
+			au_writel(clk, SYS_CLKSRC);
+			au_sync();
+			break;
+
+		case PSC_SEL_CLK_EXTCLK:
+			i2s->codec_is_master = 0;
+			break;
+
+		case PSC_SEL_CLK_SERCLK:
+			i2s->codec_is_master = 1;
+			break;
+
+		default:
+			i2s->codec_is_master = 1;
+			clock = PSC_SEL_CLK_SERCLK;
+			break;
+
+	}
+
+	/* Codec must be initialized here, after codec_is_master is determined. */
+	i2s->codec_init(i2s);
+
+	ip->psc_sel = (clock | PSC_SEL_PS_I2SMODE);
+	au_sync();
+
+	/* Enable PSC
+	*/
+	ip->psc_ctrl = PSC_CTRL_ENABLE;
+	au_sync();
+
+	/* Wait for PSC ready.
+	*/
+	do {
+		val = ip->psc_i2sstat;
+		au_sync();
+	} while ((val & PSC_I2SSTAT_SR) == 0);
+
+	/* Configure I2S controller.
+	 * Deep FIFO, 16-bit sample, DMA, make sure DMA matches fifo size.
+	 * Actual I2S mode (first bit delayed by one clock).
+	 */
+	val = PSC_I2SCFG_SET_LEN(16) | 
+          PSC_I2SCFG_RT_FIFO8 | 
+		  PSC_I2SCFG_TT_FIFO8 |
+		  PSC_I2SCFG_BI | 
+		  PSC_I2SCFG_XM;
+
+	if (i2s->codec_is_master) val |= PSC_I2SCFG_MS;
+
+	ip->psc_i2scfg = val | PSC_I2SCFG_DE_ENABLE;
+	au_sync();
+
+	/* Wait for device enabled */
+	while ((ip->psc_i2sstat & PSC_I2SSTAT_DR) == 0);
+	return i2s;
+}
+#endif
+
+
+/*
+ *	The meat of the HW init.
+ */
+#if defined(CONFIG_MIPS_FICMMP)
+
+static struct i2s_hw _i2s =	{ 
+	.name 			= "Wolfson WM8721 i2s", 
+	.input_mask 	= 0,
+	.output_mask	= SOUND_MASK_PCM,
+	.codec_init		= wm87x1_codec_init,
+	.wrcodec		= wm87x1_wrcodec,
+	.set_rates		= wm8721_set_rates,
+	.get_mixer		= wm87x1_read_mixer,
+	.set_mixer		= wm87x1_write_mixer,
+	.set_recsrc		= wm87x1_set_recsrc };
+
+static struct i2s_hw * ficmmp_setup(u32 psc, u32 clock) 
+{
+	volatile psc_i2s_t *ip = (volatile psc_i2s_t *)psc;
+	struct i2s_hw *i2s=&_i2s;
+	u32 val;
+
+	i2s->psc = ip;
+	ip->psc_ctrl = PSC_CTRL_DISABLE;	/* Disable PSC */
+	au_sync();
+	
+
+	/*
+	  	Clocking for a PSC in I2S mode can be complicated due to the
+	    numerous schemes available.
+
+		We are going to always use SERCLK with PSC in slave mode.
+	 */
+	i2s->codec_is_master = 1;
+	clock = PSC_SEL_CLK_SERCLK;
+
+	/* Codec must be initialized here, after codec_is_master is determined. */
+	i2s->codec_init(i2s);
+
+	ip->psc_sel = (clock | PSC_SEL_PS_I2SMODE);
+	au_sync();
+
+	/* Enable PSC
+	*/
+	ip->psc_ctrl = PSC_CTRL_ENABLE;
+	au_sync();
+
+	/* Wait for PSC ready.
+	*/
+	do {
+		val = ip->psc_i2sstat;
+		au_sync();
+	} while ((val & PSC_I2SSTAT_SR) == 0);
+
+	/* Configure I2S controller.
+	 * Deep FIFO, 16-bit sample, DMA, make sure DMA matches fifo size.
+	 * Actual I2S mode (first bit delayed by one clock).
+	 */
+	val = PSC_I2SCFG_SET_LEN(16) | 
+          PSC_I2SCFG_RT_FIFO8 | 
+		  PSC_I2SCFG_TT_FIFO8 |
+		  PSC_I2SCFG_BI | 
+		  PSC_I2SCFG_XM;
+
+	if (i2s->codec_is_master) val |= PSC_I2SCFG_MS;
+
+	ip->psc_i2scfg = val | PSC_I2SCFG_DE_ENABLE;
+	au_sync();
+
+	/* Wait for device enabled */
+	while ((ip->psc_i2sstat & PSC_I2SSTAT_DR) == 0);
+	return i2s;
+}
+#endif
+
+struct i2s_hw * i2s_hw_init(void)
+{
+	/*
+	 *	Already initialized?
+	 */
+	if (!request_region(PHYSADDR(I2S_PSC_BASE),
+			    0x30, AU1XXX_MODULE_NAME)) {
+		err("I2S Audio ports in use");
+		return NULL;
+	}
+
+#if defined(CONFIG_MIPS_PB1200) || defined(CONFIG_MIPS_DB1200) \
+    || defined(CONFIG_MIPS_PB1550) || defined(CONFIG_MIPS_DB1550)
+	/* The header file defines the I2S_PSC_BASE 
+	   Run the PSC in Slave Mode 
+	 */
+	return(AMD_PbDb_setup(I2S_PSC_BASE, PSC_SEL_CLK_SERCLK));
+#endif
+
+#if defined(CONFIG_MIPS_FICMMP)
+	return(ficmmp_setup(I2S_PSC_BASE, PSC_SEL_CLK_SERCLK));
+#endif
+}
+
+void i2s_hw_cleanup(struct i2s_hw *hw)
+{
+	release_region(PHYSADDR(I2S_PSC_BASE), 0x30);
+}
diff -Nur linux-2.4.30/drivers/sound/au1550_psc.c linux-2.4.30-linux-mips.org/drivers/sound/au1550_psc.c
--- linux-2.4.30/drivers/sound/au1550_psc.c	2005-01-19 15:10:04.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/sound/au1550_psc.c	2005-05-12 17:57:32.000000000 +0200
@@ -30,6 +30,7 @@
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  *
  */
+
 #include <linux/version.h>
 #include <linux/module.h>
 #include <linux/string.h>
@@ -63,6 +64,14 @@
 #include <asm/db1x00.h>
 #endif
 
+#ifdef CONFIG_MIPS_PB1200
+#include <asm/pb1200.h>
+#endif
+
+#ifdef CONFIG_MIPS_DB1200
+#include <asm/db1200.h>
+#endif
+
 #undef OSS_DOCUMENTED_MIXER_SEMANTICS
 
 #define AU1550_MODULE_NAME "Au1550 psc audio"
@@ -521,7 +530,14 @@
 	spin_unlock_irqrestore(&s->lock, flags);
 }
 
-
+/* 
+   NOTE: The xmit slots cannot be changed on the fly when in full-duplex 
+   because the AC'97 block must be stopped/started.  When using this driver 
+   in full-duplex (in & out at the same time), the DMA engine will stop if 
+   you disable the block.
+   TODO: change implementation to properly restart adc/dac after setting 
+   xmit slots.
+*/
 static void
 set_xmit_slots(int num_channels)
 {
@@ -565,6 +581,14 @@
 	} while ((stat & PSC_AC97STAT_DR) == 0);
 }
 
+/* 
+   NOTE: The recv slots cannot be changed on the fly when in full-duplex 
+   because the AC'97 block must be stopped/started.  When using this driver 
+   in full-duplex (in & out at the same time), the DMA engine will stop if 
+   you disable the block.
+   TODO: change implementation to properly restart adc/dac after setting 
+   recv slots.
+*/
 static void
 set_recv_slots(int num_channels)
 {
@@ -608,7 +632,6 @@
 
 	spin_lock_irqsave(&s->lock, flags);
 
-	set_xmit_slots(db->num_channels);
 	au_writel(PSC_AC97PCR_TC, PSC_AC97PCR);
 	au_sync();
 	au_writel(PSC_AC97PCR_TS, PSC_AC97PCR);
@@ -640,7 +663,6 @@
 			db->nextIn -= db->dmasize;
 	}
 
-	set_recv_slots(db->num_channels);
 	au1xxx_dbdma_start(db->dmanr);
 	au_writel(PSC_AC97PCR_RC, PSC_AC97PCR);
 	au_sync();
@@ -752,12 +774,16 @@
 	if (ac97c_stat & (AC97C_XU | AC97C_XO | AC97C_TE))
 		dbg("AC97C status = 0x%08x", ac97c_stat);
 #endif
+	/* There is a possiblity that we are getting 1 interrupt for
+	   multiple descriptors. Use ddma api to find out how many
+	   completed.
+	*/
 	db->dma_qcount--;
 
 	if (db->count >= db->fragsize) {
 		if (au1xxx_dbdma_put_source(db->dmanr, db->nextOut,
 							db->fragsize) == 0) {
-			err("qcount < 2 and no ring room!");
+			err("qcount < 2 and no ring room1!");
 		}
 		db->nextOut += db->fragsize;
 		if (db->nextOut >= db->rawbuf + db->dmasize)
@@ -941,11 +967,12 @@
 
 		/* duplicate every audio frame src_factor times
 		*/
-		for (i = 0; i < db->src_factor; i++)
+		for (i = 0; i < db->src_factor; i++) {
 			memcpy(dmabuf, dmasample, db->dma_bytes_per_sample);
+			dmabuf += interp_bytes_per_sample;
+		}
 
 		userbuf += db->user_bytes_per_sample;
-		dmabuf += interp_bytes_per_sample;
 	}
 
 	return num_samples * interp_bytes_per_sample;
@@ -1203,7 +1230,7 @@
 		while ((db->dma_qcount < 2) && (db->count >= db->fragsize)) {
 			if (au1xxx_dbdma_put_source(db->dmanr, db->nextOut,
 							db->fragsize) == 0) {
-				err("qcount < 2 and no ring room!");
+				err("qcount < 2 and no ring room!0");
 			}
 			db->nextOut += db->fragsize;
 			if (db->nextOut >= db->rawbuf + db->dmasize)
@@ -1481,6 +1508,7 @@
 					return -EINVAL;
 				stop_adc(s);
 				s->dma_adc.num_channels = val;
+				set_recv_slots(val);
 				if ((ret = prog_dmabuf_adc(s)))
 					return ret;
 			}
@@ -1538,6 +1566,7 @@
 				}
 
 				s->dma_dac.num_channels = val;
+				set_xmit_slots(val);
 				if ((ret = prog_dmabuf_dac(s)))
 					return ret;
 			}
@@ -1832,10 +1861,8 @@
 		down(&s->open_sem);
 	}
 
-	stop_dac(s);
-	stop_adc(s);
-
 	if (file->f_mode & FMODE_READ) {
+		stop_adc(s);
 		s->dma_adc.ossfragshift = s->dma_adc.ossmaxfrags =
 			s->dma_adc.subdivision = s->dma_adc.total_bytes = 0;
 		s->dma_adc.num_channels = 1;
@@ -1846,6 +1873,7 @@
 	}
 
 	if (file->f_mode & FMODE_WRITE) {
+		stop_dac(s);
 		s->dma_dac.ossfragshift = s->dma_dac.ossmaxfrags =
 			s->dma_dac.subdivision = s->dma_dac.total_bytes = 0;
 		s->dma_dac.num_channels = 1;
@@ -2091,6 +2119,9 @@
 					     ac97_read_proc, &s->codec);
 #endif
 
+	set_xmit_slots(1);
+	set_recv_slots(1);
+
 	return 0;
 
  err_dev3:
diff -Nur linux-2.4.30/drivers/sound/Config.in linux-2.4.30-linux-mips.org/drivers/sound/Config.in
--- linux-2.4.30/drivers/sound/Config.in	2005-01-19 15:10:04.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/sound/Config.in	2005-05-12 18:01:29.000000000 +0200
@@ -72,10 +72,17 @@
 if [ "$CONFIG_DDB5477" = "y" ]; then
     dep_tristate '  NEC Vrc5477 AC97 sound' CONFIG_SOUND_VRC5477 $CONFIG_SOUND
 fi
-if [ "$CONFIG_SOC_AU1X00" = "y" -o  "$CONFIG_SOC_AU1500" = "y" ]; then
-    dep_tristate '  Au1x00 Sound' CONFIG_SOUND_AU1X00 $CONFIG_SOUND
-    dep_tristate '  Au1550 PSC Sound' CONFIG_SOUND_AU1550_PSC $CONFIG_SOUND
-    dep_tristate '  Au1550 I2S Sound' CONFIG_SOUND_AU1550_I2S $CONFIG_SOUND
+if [ "$CONFIG_SOC_AU1000" = "y" -o \
+     "$CONFIG_SOC_AU1500" = "y" -o \
+     "$CONFIG_SOC_AU1100" = "y" ]; then
+    dep_tristate '  Au1x00 AC97 Sound' CONFIG_SOUND_AU1X00 $CONFIG_SOUND
+fi
+if [ "$CONFIG_SOC_AU1550" = "y" -o \
+     "$CONFIG_SOC_AU1200" = "y" ]; then
+    dep_tristate '  Au1550/Au1200 PSC AC97 Sound' CONFIG_SOUND_AU1550_PSC $CONFIG_SOUND
+    dep_tristate '  Au1550/Au1200 PSC I2S Sound' CONFIG_SOUND_AU1550_I2S $CONFIG_SOUND
+    define_tristate CONFIG_I2C y
+    define_tristate CONFIG_I2C_ALGO_AU1550 y
 fi
 
 dep_tristate '  Trident 4DWave DX/NX, SiS 7018 or ALi 5451 PCI Audio Core' CONFIG_SOUND_TRIDENT $CONFIG_SOUND $CONFIG_PCI
diff -Nur linux-2.4.30/drivers/sound/Makefile linux-2.4.30-linux-mips.org/drivers/sound/Makefile
--- linux-2.4.30/drivers/sound/Makefile	2005-01-19 15:10:04.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/sound/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -70,7 +70,7 @@
 obj-$(CONFIG_SOUND_VRC5477)	+= nec_vrc5477.o ac97_codec.o
 obj-$(CONFIG_SOUND_AU1X00)      += au1000.o ac97_codec.o  
 obj-$(CONFIG_SOUND_AU1550_PSC)  += au1550_psc.o ac97_codec.o  
-obj-$(CONFIG_SOUND_AU1550_I2S)  += au1550_i2s.o
+obj-$(CONFIG_SOUND_AU1550_I2S)  += au1550_i2s.o au1550_i2s_hw.o
 obj-$(CONFIG_SOUND_ESSSOLO1)	+= esssolo1.o
 obj-$(CONFIG_SOUND_FUSION)	+= cs46xx.o ac97_codec.o
 obj-$(CONFIG_SOUND_MAESTRO)	+= maestro.o
diff -Nur linux-2.4.30/drivers/tc/lk201.c linux-2.4.30-linux-mips.org/drivers/tc/lk201.c
--- linux-2.4.30/drivers/tc/lk201.c	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/tc/lk201.c	2005-05-12 17:57:33.000000000 +0200
@@ -5,7 +5,7 @@
  * for more details.
  *
  * Copyright (C) 1999-2002 Harald Koerfgen <hkoerfg@web.de>
- * Copyright (C) 2001, 2002, 2003  Maciej W. Rozycki <macro@ds2.pg.gda.pl>
+ * Copyright (C) 2001, 2002, 2003, 2004  Maciej W. Rozycki
  */
 
 #include <linux/config.h>
@@ -23,8 +23,8 @@
 #include <asm/keyboard.h>
 #include <asm/dec/tc.h>
 #include <asm/dec/machtype.h>
+#include <asm/dec/serial.h>
 
-#include "zs.h"
 #include "lk201.h"
 
 /*
@@ -55,19 +55,20 @@
 unsigned char kbd_sysrq_key = -1;
 #endif
 
-#define KEYB_LINE	3
+#define KEYB_LINE_ZS	3
+#define KEYB_LINE_DZ	0
 
-static int __init lk201_init(struct dec_serial *);
-static void __init lk201_info(struct dec_serial *);
-static void lk201_kbd_rx_char(unsigned char, unsigned char);
+static int __init lk201_init(void *);
+static void __init lk201_info(void *);
+static void lk201_rx_char(unsigned char, unsigned char);
 
-struct zs_hook lk201_kbdhook = {
+static struct dec_serial_hook lk201_hook = {
 	.init_channel	= lk201_init,
 	.init_info	= lk201_info,
 	.rx_char	= NULL,
 	.poll_rx_char	= NULL,
 	.poll_tx_char	= NULL,
-	.cflags		= B4800 | CS8 | CSTOPB | CLOCAL
+	.cflags		= B4800 | CS8 | CSTOPB | CLOCAL,
 };
 
 /*
@@ -93,28 +94,28 @@
 	LK_CMD_ENB_BELL, LK_PARAM_VOLUME(4),
 };
 
-static struct dec_serial* lk201kbd_info;
+static void *lk201_handle;
 
-static int lk201_send(struct dec_serial *info, unsigned char ch)
+static int lk201_send(unsigned char ch)
 {
-	if (info->hook->poll_tx_char(info, ch)) {
+	if (lk201_hook.poll_tx_char(lk201_handle, ch)) {
 		printk(KERN_ERR "lk201: transmit timeout\n");
 		return -EIO;
 	}
 	return 0;
 }
 
-static inline int lk201_get_id(struct dec_serial *info)
+static inline int lk201_get_id(void)
 {
-	return lk201_send(info, LK_CMD_REQ_ID);
+	return lk201_send(LK_CMD_REQ_ID);
 }
 
-static int lk201_reset(struct dec_serial *info)
+static int lk201_reset(void)
 {
 	int i, r;
 
 	for (i = 0; i < sizeof(lk201_reset_string); i++) {
-		r = lk201_send(info, lk201_reset_string[i]);
+		r = lk201_send(lk201_reset_string[i]);
 		if (r < 0)
 			return r;
 	}
@@ -203,24 +204,26 @@
 
 static int write_kbd_rate(struct kbd_repeat *rep)
 {
-	struct dec_serial* info = lk201kbd_info;
 	int delay, rate;
 	int i;
 
 	delay = rep->delay / 5;
 	rate = rep->rate;
 	for (i = 0; i < 4; i++) {
-		if (info->hook->poll_tx_char(info, LK_CMD_RPT_RATE(i)))
+		if (lk201_hook.poll_tx_char(lk201_handle,
+					    LK_CMD_RPT_RATE(i)))
 			return 1;
-		if (info->hook->poll_tx_char(info, LK_PARAM_DELAY(delay)))
+		if (lk201_hook.poll_tx_char(lk201_handle,
+					    LK_PARAM_DELAY(delay)))
 			return 1;
-		if (info->hook->poll_tx_char(info, LK_PARAM_RATE(rate)))
+		if (lk201_hook.poll_tx_char(lk201_handle,
+					    LK_PARAM_RATE(rate)))
 			return 1;
 	}
 	return 0;
 }
 
-static int lk201kbd_rate(struct kbd_repeat *rep)
+static int lk201_kbd_rate(struct kbd_repeat *rep)
 {
 	if (rep == NULL)
 		return -EINVAL;
@@ -237,10 +240,8 @@
 	return 0;
 }
 
-static void lk201kd_mksound(unsigned int hz, unsigned int ticks)
+static void lk201_kd_mksound(unsigned int hz, unsigned int ticks)
 {
-	struct dec_serial* info = lk201kbd_info;
-
 	if (!ticks)
 		return;
 
@@ -253,20 +254,19 @@
 		ticks = 7;
 	ticks = 7 - ticks;
 
-	if (info->hook->poll_tx_char(info, LK_CMD_ENB_BELL))
+	if (lk201_hook.poll_tx_char(lk201_handle, LK_CMD_ENB_BELL))
 		return;
-	if (info->hook->poll_tx_char(info, LK_PARAM_VOLUME(ticks)))
+	if (lk201_hook.poll_tx_char(lk201_handle, LK_PARAM_VOLUME(ticks)))
 		return;
-	if (info->hook->poll_tx_char(info, LK_CMD_BELL))
+	if (lk201_hook.poll_tx_char(lk201_handle, LK_CMD_BELL))
 		return;
 }
 
 void kbd_leds(unsigned char leds)
 {
-	struct dec_serial* info = lk201kbd_info;
 	unsigned char l = 0;
 
-	if (!info)		/* FIXME */
+	if (!lk201_handle)		/* FIXME */
 		return;
 
 	/* FIXME -- Only Hold and Lock LEDs for now. --macro */
@@ -275,13 +275,13 @@
 	if (leds & LED_CAP)
 		l |= LK_LED_LOCK;
 
-	if (info->hook->poll_tx_char(info, LK_CMD_LEDS_ON))
+	if (lk201_hook.poll_tx_char(lk201_handle, LK_CMD_LEDS_ON))
 		return;
-	if (info->hook->poll_tx_char(info, LK_PARAM_LED_MASK(l)))
+	if (lk201_hook.poll_tx_char(lk201_handle, LK_PARAM_LED_MASK(l)))
 		return;
-	if (info->hook->poll_tx_char(info, LK_CMD_LEDS_OFF))
+	if (lk201_hook.poll_tx_char(lk201_handle, LK_CMD_LEDS_OFF))
 		return;
-	if (info->hook->poll_tx_char(info, LK_PARAM_LED_MASK(~l)))
+	if (lk201_hook.poll_tx_char(lk201_handle, LK_PARAM_LED_MASK(~l)))
 		return;
 }
 
@@ -307,7 +307,7 @@
 	return 0x80;
 }
 
-static void lk201_kbd_rx_char(unsigned char ch, unsigned char stat)
+static void lk201_rx_char(unsigned char ch, unsigned char fl)
 {
 	static unsigned char id[6];
 	static int id_i;
@@ -316,9 +316,8 @@
 	static int prev_scancode;
 	unsigned char c = scancodeRemap[ch];
 
-	if (stat && stat != TTY_OVERRUN) {
-		printk(KERN_ERR "lk201: keyboard receive error: 0x%02x\n",
-		       stat);
+	if (fl != TTY_NORMAL && fl != TTY_OVERRUN) {
+		printk(KERN_ERR "lk201: keyboard receive error: 0x%02x\n", fl);
 		return;
 	}
 
@@ -335,7 +334,7 @@
 			/* OK, the power-up concluded. */
 			lk201_report(id);
 			if (id[2] == LK_STAT_PWRUP_OK)
-				lk201_get_id(lk201kbd_info);
+				lk201_get_id();
 			else {
 				id_i = 0;
 				printk(KERN_ERR "lk201: keyboard power-up "
@@ -345,7 +344,7 @@
 			/* We got the ID; report it and start operation. */
 			id_i = 0;
 			lk201_id(id);
-			lk201_reset(lk201kbd_info);
+			lk201_reset();
 		}
 		return;
 	}
@@ -398,29 +397,28 @@
 	tasklet_schedule(&keyboard_tasklet);
 }
 
-static void __init lk201_info(struct dec_serial *info)
+static void __init lk201_info(void *handle)
 {
 }
 
-static int __init lk201_init(struct dec_serial *info)
+static int __init lk201_init(void *handle)
 {
 	/* First install handlers. */
-	lk201kbd_info = info;
-	kbd_rate = lk201kbd_rate;
-	kd_mksound = lk201kd_mksound;
+	lk201_handle = handle;
+	kbd_rate = lk201_kbd_rate;
+	kd_mksound = lk201_kd_mksound;
 
-	info->hook->rx_char = lk201_kbd_rx_char;
+	lk201_hook.rx_char = lk201_rx_char;
 
 	/* Then just issue a reset -- the handlers will do the rest. */
-	lk201_send(info, LK_CMD_POWER_UP);
+	lk201_send(LK_CMD_POWER_UP);
 
 	return 0;
 }
 
 void __init kbd_init_hw(void)
 {
-	extern int register_zs_hook(unsigned int, struct zs_hook *);
-	extern int unregister_zs_hook(unsigned int);
+	int keyb_line;
 
 	/* Maxine uses LK501 at the Access.Bus. */
 	if (!LK_IFACE)
@@ -428,19 +426,15 @@
 
 	printk(KERN_INFO "lk201: DECstation LK keyboard driver v0.05.\n");
 
-	if (LK_IFACE_ZS) {
-		/*
-		 * kbd_init_hw() is being called before
-		 * rs_init() so just register the kbd hook
-		 * and let zs_init do the rest :-)
-		 */
-		if(!register_zs_hook(KEYB_LINE, &lk201_kbdhook))
-			unregister_zs_hook(KEYB_LINE);
-	} else {
-		/*
-		 * TODO: modify dz.c to allow similar hooks
-		 * for LK201 handling on DS2100, DS3100, and DS5000/200
-		 */
-		printk(KERN_ERR "lk201: support for DZ11 not yet ready.\n");
-	}
+	/*
+	 * kbd_init_hw() is being called before
+	 * rs_init() so just register the kbd hook
+	 * and let zs_init do the rest :-)
+	 */
+	if (LK_IFACE_ZS)
+		keyb_line = KEYB_LINE_ZS;
+	else
+		keyb_line = KEYB_LINE_DZ;
+	if (!register_dec_serial_hook(keyb_line, &lk201_hook))
+		unregister_dec_serial_hook(keyb_line);
 }
diff -Nur linux-2.4.30/drivers/tc/zs.c linux-2.4.30-linux-mips.org/drivers/tc/zs.c
--- linux-2.4.30/drivers/tc/zs.c	2005-01-19 15:10:05.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/tc/zs.c	2005-05-12 17:57:33.000000000 +0200
@@ -68,6 +68,8 @@
 #include <asm/bitops.h>
 #include <asm/uaccess.h>
 #include <asm/bootinfo.h>
+#include <asm/dec/serial.h>
+
 #ifdef CONFIG_DECSTATION
 #include <asm/dec/interrupts.h>
 #include <asm/dec/machtype.h>
@@ -160,8 +162,8 @@
 #ifdef CONFIG_SERIAL_DEC_CONSOLE
 static struct console sercons;
 #endif
-#if defined(CONFIG_SERIAL_DEC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) \
-    && !defined(MODULE)
+#if defined(CONFIG_SERIAL_DEC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && \
+   !defined(MODULE)
 static unsigned long break_pressed; /* break, really ... */
 #endif
 
@@ -196,7 +198,6 @@
 /*
  * Debugging.
  */
-#undef SERIAL_DEBUG_INTR
 #undef SERIAL_DEBUG_OPEN
 #undef SERIAL_DEBUG_FLOW
 #undef SERIAL_DEBUG_THROTTLE
@@ -221,10 +222,6 @@
 static struct termios *serial_termios[NUM_CHANNELS];
 static struct termios *serial_termios_locked[NUM_CHANNELS];
 
-#ifndef MIN
-#define MIN(a,b)	((a) < (b) ? (a) : (b))
-#endif
-
 /*
  * tmp_buf is used as a temporary buffer by serial_write.  We need to
  * lock it in case the copy_from_user blocks while swapping in a page,
@@ -386,8 +383,6 @@
  * -----------------------------------------------------------------------
  */
 
-static int tty_break;	/* Set whenever BREAK condition is detected.  */
-
 /*
  * This routine is used by the interrupt handler to schedule
  * processing in the software interrupt portion of the driver.
@@ -414,20 +409,15 @@
 		if (!tty && (!info->hook || !info->hook->rx_char))
 			continue;
 
-		if (tty_break) {
-			tty_break = 0;
-#if defined(CONFIG_SERIAL_DEC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && !defined(MODULE)
-			if (info->line == sercons.index) {
-				if (!break_pressed) {
-					break_pressed = jiffies;
-					goto ignore_char;
-				}
-				break_pressed = 0;
-			}
-#endif
+		flag = TTY_NORMAL;
+		if (info->tty_break) {
+			info->tty_break = 0;
 			flag = TTY_BREAK;
 			if (info->flags & ZILOG_SAK)
 				do_SAK(tty);
+			/* Ignore the null char got when BREAK is removed.  */
+			if (ch == 0)
+				continue;
 		} else {
 			if (stat & Rx_OVR) {
 				flag = TTY_OVERRUN;
@@ -435,20 +425,22 @@
 				flag = TTY_FRAME;
 			} else if (stat & PAR_ERR) {
 				flag = TTY_PARITY;
-			} else
-				flag = 0;
-			if (flag)
+			}
+			if (flag != TTY_NORMAL)
 				/* reset the error indication */
 				write_zsreg(info->zs_channel, R0, ERR_RES);
 		}
 
-#if defined(CONFIG_SERIAL_DEC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && !defined(MODULE)
+#if defined(CONFIG_SERIAL_DEC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && \
+   !defined(MODULE)
 		if (break_pressed && info->line == sercons.index) {
-			if (ch != 0 &&
-			    time_before(jiffies, break_pressed + HZ*5)) {
+			/* Ignore the null char got when BREAK is removed.  */
+			if (ch == 0)
+				continue;
+			if (time_before(jiffies, break_pressed + HZ * 5)) {
 				handle_sysrq(ch, regs, NULL, NULL);
 				break_pressed = 0;
-				goto ignore_char;
+				continue;
 			}
 			break_pressed = 0;
 		}
@@ -459,23 +451,7 @@
 			return;
   		}
 
-		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
-			static int flip_buf_ovf;
-			++flip_buf_ovf;
-			continue;
-		}
-		tty->flip.count++;
-		{
-			static int flip_max_cnt;
-			if (flip_max_cnt < tty->flip.count)
-				flip_max_cnt = tty->flip.count;
-		}
-
-		*tty->flip.flag_buf_ptr++ = flag;
-		*tty->flip.char_buf_ptr++ = ch;
-#if defined(CONFIG_SERIAL_DEC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && !defined(MODULE)
-	ignore_char:
-#endif
+		tty_insert_flip_char(tty, ch, flag);
 	}
 	if (tty)
 		tty_flip_buffer_push(tty);
@@ -517,11 +493,15 @@
 	/* Get status from Read Register 0 */
 	stat = read_zsreg(info->zs_channel, R0);
 
-	if (stat & BRK_ABRT) {
-#ifdef SERIAL_DEBUG_INTR
-		printk("handling break....");
+	if ((stat & BRK_ABRT) && !(info->read_reg_zero & BRK_ABRT)) {
+#if defined(CONFIG_SERIAL_DEC_CONSOLE) && defined(CONFIG_MAGIC_SYSRQ) && \
+   !defined(MODULE)
+		if (info->line == sercons.index) {
+			if (!break_pressed)
+				break_pressed = jiffies;
+		} else
 #endif
-		tty_break = 1;
+			info->tty_break = 1;
 	}
 
 	if (info->zs_channel != info->zs_chan_a) {
@@ -957,7 +937,7 @@
 	save_flags(flags);
 	while (1) {
 		cli();
-		c = MIN(count, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+		c = min(count, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
 				   SERIAL_XMIT_SIZE - info->xmit_head));
 		if (c <= 0)
 			break;
@@ -965,7 +945,7 @@
 		if (from_user) {
 			down(&tmp_buf_sem);
 			copy_from_user(tmp_buf, buf, c);
-			c = MIN(c, MIN(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
+			c = min(c, min(SERIAL_XMIT_SIZE - info->xmit_cnt - 1,
 				       SERIAL_XMIT_SIZE - info->xmit_head));
 			memcpy(info->xmit_buf + info->xmit_head, tmp_buf, c);
 			up(&tmp_buf_sem);
@@ -1282,46 +1262,48 @@
 	}
 
 	switch (cmd) {
-		case TIOCMGET:
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-				sizeof(unsigned int));
-			if (error)
-				return error;
-			return get_modem_info(info, (unsigned int *) arg);
-		case TIOCMBIS:
-		case TIOCMBIC:
-		case TIOCMSET:
-			return set_modem_info(info, cmd, (unsigned int *) arg);
-		case TIOCGSERIAL:
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-						sizeof(struct serial_struct));
-			if (error)
-				return error;
-			return get_serial_info(info,
-					       (struct serial_struct *) arg);
-		case TIOCSSERIAL:
-			return set_serial_info(info,
-					       (struct serial_struct *) arg);
-		case TIOCSERGETLSR: /* Get line status register */
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-				sizeof(unsigned int));
-			if (error)
-				return error;
-			else
-			    return get_lsr_info(info, (unsigned int *) arg);
+	case TIOCMGET:
+		error = verify_area(VERIFY_WRITE, (void *)arg,
+				    sizeof(unsigned int));
+		if (error)
+			return error;
+		return get_modem_info(info, (unsigned int *)arg);
 
-		case TIOCSERGSTRUCT:
-			error = verify_area(VERIFY_WRITE, (void *) arg,
-						sizeof(struct dec_serial));
-			if (error)
-				return error;
-			copy_from_user((struct dec_serial *) arg,
-				       info, sizeof(struct dec_serial));
-			return 0;
+	case TIOCMBIS:
+	case TIOCMBIC:
+	case TIOCMSET:
+		return set_modem_info(info, cmd, (unsigned int *)arg);
 
-		default:
-			return -ENOIOCTLCMD;
-		}
+	case TIOCGSERIAL:
+		error = verify_area(VERIFY_WRITE, (void *)arg,
+				    sizeof(struct serial_struct));
+		if (error)
+			return error;
+		return get_serial_info(info, (struct serial_struct *)arg);
+
+	case TIOCSSERIAL:
+		return set_serial_info(info, (struct serial_struct *)arg);
+
+	case TIOCSERGETLSR:			/* Get line status register */
+		error = verify_area(VERIFY_WRITE, (void *)arg,
+				    sizeof(unsigned int));
+		if (error)
+			return error;
+		else
+			return get_lsr_info(info, (unsigned int *)arg);
+
+	case TIOCSERGSTRUCT:
+		error = verify_area(VERIFY_WRITE, (void *)arg,
+				    sizeof(struct dec_serial));
+		if (error)
+			return error;
+		copy_from_user((struct dec_serial *)arg, info,
+			       sizeof(struct dec_serial));
+		return 0;
+
+	default:
+		return -ENOIOCTLCMD;
+	}
 	return 0;
 }
 
@@ -1446,7 +1428,8 @@
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout)
 {
 	struct dec_serial *info = (struct dec_serial *) tty->driver_data;
-	unsigned long orig_jiffies, char_time;
+	unsigned long orig_jiffies;
+	int char_time;
 
 	if (serial_paranoia_check(info, tty->device, "rs_wait_until_sent"))
 		return;
@@ -1462,7 +1445,7 @@
 	if (char_time == 0)
 		char_time = 1;
 	if (timeout)
-		char_time = MIN(char_time, timeout);
+		char_time = min(char_time, timeout);
 	while ((read_zsreg(info->zs_channel, 1) & Tx_BUF_EMP) == 0) {
 		current->state = TASK_INTERRUPTIBLE;
 		schedule_timeout(char_time);
@@ -1714,7 +1697,7 @@
 
 static void __init show_serial_version(void)
 {
-	printk("DECstation Z8530 serial driver version 0.08\n");
+	printk("DECstation Z8530 serial driver version 0.09\n");
 }
 
 /*  Initialize Z8530s zs_channels
@@ -1994,8 +1977,9 @@
  * polling I/O routines
  */
 static int
-zs_poll_tx_char(struct dec_serial *info, unsigned char ch)
+zs_poll_tx_char(void *handle, unsigned char ch)
 {
+	struct dec_serial *info = handle;
 	struct dec_zschannel *chan = info->zs_channel;
 	int    ret;
 
@@ -2017,8 +2001,9 @@
 }
 
 static int
-zs_poll_rx_char(struct dec_serial *info)
+zs_poll_rx_char(void *handle)
 {
+	struct dec_serial *info = handle;
         struct dec_zschannel *chan = info->zs_channel;
         int    ret;
 
@@ -2038,12 +2023,13 @@
 		return -ENODEV;
 }
 
-unsigned int register_zs_hook(unsigned int channel, struct zs_hook *hook)
+int register_zs_hook(unsigned int channel, struct dec_serial_hook *hook)
 {
 	struct dec_serial *info = &zs_soft[channel];
 
 	if (info->hook) {
-		printk(__FUNCTION__": line %d has already a hook registered\n", channel);
+		printk("%s: line %d has already a hook registered\n",
+		       __FUNCTION__, channel);
 
 		return 0;
 	} else {
@@ -2055,7 +2041,7 @@
 	}
 }
 
-unsigned int unregister_zs_hook(unsigned int channel)
+int unregister_zs_hook(unsigned int channel)
 {
 	struct dec_serial *info = &zs_soft[channel];
 
@@ -2063,8 +2049,8 @@
                 info->hook = NULL;
                 return 1;
         } else {
-                printk(__FUNCTION__": trying to unregister hook on line %d,"
-                       " but none is registered\n", channel);
+                printk("%s: trying to unregister hook on line %d,"
+                       " but none is registered\n", __FUNCTION__, channel);
                 return 0;
         }
 }
@@ -2319,22 +2305,23 @@
 	write_zsreg(chan, 9, nine);
 }
 
-static int kgdbhook_init_channel(struct dec_serial* info)
+static int kgdbhook_init_channel(void *handle)
 {
 	return 0;
 }
 
-static void kgdbhook_init_info(struct dec_serial* info)
+static void kgdbhook_init_info(void *handle)
 {
 }
 
-static void kgdbhook_rx_char(struct dec_serial* info,
-			     unsigned char ch, unsigned char stat)
+static void kgdbhook_rx_char(void *handle, unsigned char ch, unsigned char fl)
 {
+	struct dec_serial *info = handle;
+
+	if (fl != TTY_NORMAL)
+		return;
 	if (ch == 0x03 || ch == '$')
 		breakpoint();
-	if (stat & (Rx_OVR|FRM_ERR|PAR_ERR))
-		write_zsreg(info->zs_channel, 0, ERR_RES);
 }
 
 /* This sets up the serial port we're using, and turns on
@@ -2360,11 +2347,11 @@
  * for /dev/ttyb which is determined in setup_arch() from the
  * boot command line flags.
  */
-struct zs_hook zs_kgdbhook = {
-	init_channel : kgdbhook_init_channel,
-	init_info    : kgdbhook_init_info,
-	cflags       : B38400|CS8|CLOCAL,
-	rx_char      : kgdbhook_rx_char,
+struct dec_serial_hook zs_kgdbhook = {
+	.init_channel	= kgdbhook_init_channel,
+	.init_info	= kgdbhook_init_info,
+	.rx_char	= kgdbhook_rx_char,
+	.cflags		= B38400 | CS8 | CLOCAL,
 }
 
 void __init zs_kgdb_hook(int tty_num)
diff -Nur linux-2.4.30/drivers/tc/zs.h linux-2.4.30-linux-mips.org/drivers/tc/zs.h
--- linux-2.4.30/drivers/tc/zs.h	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/tc/zs.h	2005-05-12 17:57:33.000000000 +0200
@@ -1,14 +1,18 @@
 /*
- * macserial.h: Definitions for the Macintosh Z8530 serial driver.
+ * drivers/tc/zs.h: Definitions for the DECstation Z85C30 serial driver.
  *
  * Adapted from drivers/sbus/char/sunserial.h by Paul Mackerras.
+ * Adapted from drivers/macintosh/macserial.h by Harald Koerfgen.
  *
  * Copyright (C) 1996 Paul Mackerras (Paul.Mackerras@cs.anu.edu.au)
  * Copyright (C) 1995 David S. Miller (davem@caip.rutgers.edu)
+ * Copyright (C) 2004  Maciej W. Rozycki
  */
 #ifndef _DECSERIAL_H
 #define _DECSERIAL_H
 
+#include <asm/dec/serial.h>
+
 #define NUM_ZSREGS    16
 
 struct serial_struct {
@@ -89,63 +93,50 @@
 	unsigned char curregs[NUM_ZSREGS];
 };
 
-struct dec_serial;
-
-struct zs_hook {
-	int (*init_channel)(struct dec_serial* info);
-	void (*init_info)(struct dec_serial* info);
-	void (*rx_char)(unsigned char ch, unsigned char stat);
-	int  (*poll_rx_char)(struct dec_serial* info);
-	int  (*poll_tx_char)(struct dec_serial* info,
-			     unsigned char ch);
-	unsigned cflags;
-};
-
 struct dec_serial {
-	struct dec_serial *zs_next;	/* For IRQ servicing chain */
-	struct dec_zschannel *zs_channel; /* Channel registers */
-	struct dec_zschannel *zs_chan_a;	/* A side registers */
-	unsigned char read_reg_zero;
-
-	char soft_carrier;  /* Use soft carrier on this channel */
-	char break_abort;   /* Is serial console in, so process brk/abrt */
-	struct zs_hook *hook;  /* Hook on this channel */
-	char is_cons;       /* Is this our console. */
-	unsigned char tx_active; /* character is being xmitted */
-	unsigned char tx_stopped; /* output is suspended */
-
-	/* We need to know the current clock divisor
-	 * to read the bps rate the chip has currently
-	 * loaded.
+	struct dec_serial	*zs_next;	/* For IRQ servicing chain.  */
+	struct dec_zschannel	*zs_channel;	/* Channel registers.  */
+	struct dec_zschannel	*zs_chan_a;	/* A side registers.  */
+	unsigned char		read_reg_zero;
+
+	struct dec_serial_hook	*hook;		/* Hook on this channel.  */
+	int			tty_break;	/* Set on BREAK condition.  */
+	int			is_cons;	/* Is this our console.  */
+	int			tx_active;	/* Char is being xmitted.  */
+	int			tx_stopped;	/* Output is suspended.  */
+
+	/*
+	 * We need to know the current clock divisor
+	 * to read the bps rate the chip has currently loaded.
 	 */
-	unsigned char clk_divisor;  /* May be 1, 16, 32, or 64 */
-	int zs_baud;
+	int			clk_divisor;	/* May be 1, 16, 32, or 64.  */
+	int			zs_baud;
 
-	char change_needed;
+	char			change_needed;
 
 	int			magic;
 	int			baud_base;
 	int			port;
 	int			irq;
-	int			flags; 		/* defined in tty.h */
-	int			type; 		/* UART type */
+	int			flags; 		/* Defined in tty.h.  */
+	int			type; 		/* UART type.  */
 	struct tty_struct 	*tty;
 	int			read_status_mask;
 	int			ignore_status_mask;
 	int			timeout;
 	int			xmit_fifo_size;
 	int			custom_divisor;
-	int			x_char;	/* xon/xoff character */
+	int			x_char;		/* XON/XOFF character.  */
 	int			close_delay;
 	unsigned short		closing_wait;
 	unsigned short		closing_wait2;
 	unsigned long		event;
 	unsigned long		last_active;
 	int			line;
-	int			count;	    /* # of fd on device */
-	int			blocked_open; /* # of blocked opens */
-	long			session; /* Session of opening process */
-	long			pgrp; /* pgrp of opening process */
+	int			count;		/* # of fds on device.  */
+	int			blocked_open;	/* # of blocked opens.  */
+	long			session;	/* Sess of opening process.  */
+	long			pgrp;		/* Pgrp of opening process.  */
 	unsigned char 		*xmit_buf;
 	int			xmit_head;
 	int			xmit_tail;
diff -Nur linux-2.4.30/drivers/usb/gadget/amd5536udc.c linux-2.4.30-linux-mips.org/drivers/usb/gadget/amd5536udc.c
--- linux-2.4.30/drivers/usb/gadget/amd5536udc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/usb/gadget/amd5536udc.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,4487 @@
+/*
+ * AMD 5536 UDC high/full speed USB device controller.
+ */
+
+/*
+ * Copyright (C) 2005 AMD (http://www.amd.com)
+ * Author: Thomas Dahlmann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*****************************************************************************
+ * Defines
+ *****************************************************************************/
+
+/* debug control */
+#undef DEBUG
+//DISABLEDamd #define DEBUG     1
+#undef VERBOSE
+//DISABLEDamd #define   VERBOSE         
+//DISABLEDamd #define UDC_REGISTER_DUMP
+/* prefix name for print */
+#define DRIVER_NAME_FOR_PRINT   UDC_DRIVER_NAME
+
+/* Driver name */
+#define DRIVER_DESC             "AMD 5536 UDC USB Peripheral Controller"
+#define DRIVER_VERSION          "01.00.0000 - kernel 2.4.29, $Revision: #115 $"
+
+/* kernel version of new gadget stack generation (for 2.6.x) */
+#define UDC_NEW_GADGET_KERNEL KERNEL_VERSION(2,5,59)
+/*****************************************************************************
+ *  Includes
+ *****************************************************************************/
+
+/* system */
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+#include <linux/ioctl.h>
+#include <linux/fs.h>
+#if     LINUX_VERSION_CODE < UDC_NEW_GADGET_KERNEL
+#include <linux/wrapper.h>
+#else
+#include <linux/moduleparam.h>
+#include <linux/device.h>
+#endif
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+/* MIPS config */
+#ifdef CONFIG_SOC_AU1200
+#include <asm/au1000.h>
+#ifndef CONFIG_USB_NON_PCI_OTGDEVICE
+#define CONFIG_USB_NON_PCI_OTGDEVICE
+#endif
+#endif
+
+/* gadget stack */
+#include <linux/usb_ch9.h>
+#include <linux/usb_gadget.h>
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_OTGMUX_GADGET)
+#include <linux/usb_otg.h>
+#else
+#include "amd5536otg_dummy.h"
+#endif
+
+/* udc specific */
+#include "amd5536udc.h"
+
+/*****************************************************************************
+ *  Static Function Declarations
+ *****************************************************************************/
+
+void udc_tasklet_disconnect(unsigned long);
+static int udc_read_fifo_dwords(struct udc* dev, u32* buf, int dwords);
+static int udc_write_fifo(struct udc* dev, int ep_ix, u32* buf, int bytes);
+static void nuke (struct udc_ep *);
+static int udc_probe (struct pci_dev *pdev, const struct pci_device_id *id);
+static void udc_remove (struct pci_dev *pdev);
+static void usb_reset (struct udc *dev);
+static void usb_reinit (struct udc *dev);
+static void udc_soft_reset(struct udc* dev);
+#ifdef DEBUG
+#ifdef NOT_USED
+static void dump_buffer(u8* buf, u32 bytes);
+#endif
+static int udc_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
+static int udc_open (struct inode *inode, struct file *file);
+static int udc_release (struct inode *inode, struct file *file);
+#endif
+static struct udc_data_dma* udc_get_last_dma_desc(struct udc_request* req);
+static u32 udc_get_ppbdu_rxbytes(struct udc_request* req);
+static int udc_free_dma_chain(struct udc* dev, struct udc_request* req);
+static void udc_create_dma_chain(struct udc_ep* ep, struct udc_request* req, unsigned long buf_len, int gfp_flags);
+static inline int startup_registers(struct udc* dev);
+static inline int udc_control_out_isr(struct udc* dev);
+static inline int udc_control_in_isr(struct udc* dev);
+static inline int udc_data_out_isr(struct udc* dev, int ep_ix);
+static inline int udc_data_in_isr(struct udc* dev, int ep_ix);
+static inline int udc_dev_isr(struct udc* dev, u32 dev_irq);
+#ifdef CONFIG_SOC_AU1200
+void au1200_sync(void);
+#endif
+
+/*****************************************************************************
+ *  Data
+ *****************************************************************************/
+
+/* description */
+static const char driver_name [] = UDC_DRIVER_NAME; 
+static const char driver_desc [] = DRIVER_DESC;
+
+/* structure to hold endpoint function pointers */
+static struct usb_ep_ops udc_ep_ops;
+
+/* received setup data */
+static union udc_setup_data setup_data;
+
+/* pointer to device object */      
+static struct udc       *the_controller;
+
+/* irq spin lock for soft reset */
+spinlock_t udc_irq_spinlock;
+
+/* TODO this is used for dma chaining, global gfp not good */
+static int td_gfp_flags = 0;
+        
+/* slave mode: pending bytes in rx fifo after nyet,
+used if EPIN irq came but no req was available */
+static unsigned int udc_rxfifo_pending = 0;
+
+/* count soft resets after suspend to avoid loop */
+static int soft_reset_occured = 0;
+#ifdef UDC_IPBUG_3958_WORKAROUND_SOFT_RESET_ON_USBRESET
+static int soft_reset_after_usbreset_occured = 0;
+#endif
+/* tasklet for usb disconnect */
+DECLARE_TASKLET(disconnect_tasklet, udc_tasklet_disconnect, (unsigned long) &the_controller);
+
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE
+static struct pci_dev pdev_dummy;
+static struct pci_dev* pdev = &pdev_dummy;
+#endif
+
+#ifdef UDC_IPBUG_3958_WORKAROUND
+/* CNAK pending field: bit0 = ep0in, bit16 = ep0out */
+static u32 cnak_pending = 0;
+#define UDC_QUEUE_CNAK(ep, num) \
+        if (readl(&((ep)->regs->ctl)) & UDC_EPCTL_NAK) { \
+                INFO("NAK could not be cleared for ep%d\n", num); \
+                cnak_pending |= 1 << (num); \
+                (ep)->naking = 1; \
+        }
+#else
+#define UDC_QUEUE_CNAK(ep, num) {} 
+#endif
+#ifdef UDC_IPBUG_3958_WORKAROUND_RXFIFO_FLUSH
+/* rxfifo cleari/trash buffer */
+static u8 udc_rxfifo_trash[UDC_RXFIFO_SIZE];
+#endif
+
+#ifdef UDC_IPBUG_3943_WORKAROUND
+/* otg registering count */
+static u32 otg_reg_count = 0;
+#endif
+/* gadget registering count */
+static u32 gadget_bind_count = 0;
+
+#ifdef DEBUG
+/* driver operations */
+static struct file_operations udc_fops = {
+        owner:          THIS_MODULE,
+
+        read:           NULL,
+        write:          NULL,
+        ioctl:          udc_ioctl,
+        open:           udc_open,
+        release:        udc_release,
+};
+#endif
+
+/* PCI device parameters */
+static struct pci_device_id pci_ids [] = { {
+        .class =        ((PCI_CLASS_SERIAL_USB << 8) | 0xfe),
+        .class_mask =   ~0,
+        .vendor =       UDC_PCI_VENDOR_ID,
+        .device =       UDC_PCI_DEVICE_ID,
+        .subvendor =    PCI_ANY_ID,
+        .subdevice =    PCI_ANY_ID,
+
+}, { /* zero */ }
+};
+
+#ifndef CONFIG_USB_NON_PCI_OTGDEVICE
+/* PCI functions */
+static struct pci_driver udc_pci_driver = {
+        .name =         (char *) driver_name,
+        .id_table =     pci_ids,
+
+        .probe =        udc_probe,
+        .remove =       udc_remove,
+
+        /* TODO power management */
+};
+#endif
+
+/* endpoint names used for print */
+static const char ep0name [] = "ep0in";
+static const char *ep_name [] = {
+        ep0name,
+        "ep1in-int", "ep2in-bulk", "ep3in-bulk", "ep4in-bulk", "ep5in-bulk", "ep6in-bulk", "ep7in-bulk", "ep8in-bulk",
+        "ep9in-bulk", "ep10in-bulk", "ep11in-bulk", "ep12in-bulk", "ep13in-bulk", "ep14in-bulk", "ep15in-bulk", "ep0out",
+        "ep1out-bulk", "ep2out-bulk", "ep3out-bulk", "ep4out-bulk", "ep5out-bulk", "ep6out-bulk", "ep7out-bulk", "ep8out-bulk",
+        "ep9out-bulk", "ep10out-bulk", "ep11out-bulk", "ep12out-bulk", "ep13out-bulk", "ep14out-bulk", "ep15out-bulk" 
+};
+
+#if defined(CONFIG_MIPS)
+/* util's sync only performs sync w/o refilling the pipe
+   => read issued after write might appear before write on bus */
+#define au1200_sync_delay() {\
+  asm(".set noreorder"); \
+  asm("b l1"); \
+  asm("l1:");  \
+  asm("sync");  \
+  asm("nop");   \
+  asm("nop");   \
+  asm("nop");   \
+  asm("nop");   \
+  asm("b l2");   \
+  asm("l2:");   \
+  asm("nop");   \
+  asm("nop");   \
+  asm(".set reorder"); \
+}
+
+//DISABLEDamd #define wb_flush() au1200_sync() 
+#define wb_flush() __asm__ volatile ("sync")
+#else
+#define wb_flush() {} 
+#endif
+
+#ifdef DEBUG
+/* data for debuging only */
+static unsigned long no_pref_req = 0;
+static unsigned long no_req = 0;
+static u32 same_cfg = 0;
+static u32 num_enums = 0;
+#ifdef UDC_REGISTER_DUMP
+#ifdef VERBOSE
+#define writel(d,a) {VDBG("write32: address=%08lx data=%08lx (%s)\n",a,d,#a);\
+                     (*(volatile unsigned int *) __io_virt(a) = (d));}
+#define writeb(d,a) {VDBG("write8 : address=%08lx data=%08lx (%s)\n",a,d,#a);\
+                     (*(volatile unsigned char *) __io_virt(a) = (d));}
+#define readl(a)   read32(a) 
+#define readb(a)   read8(a) 
+#endif
+#endif
+#endif
+
+#if defined(CONFIG_MIPS)
+#undef writel
+#undef writeb
+#define writel(d,a) {((*(volatile unsigned int *)(a)) = (__ioswab32(d))); wb_flush();}
+#define writeb(d,a) {((*(volatile unsigned char *)(a)) = (d)); wb_flush();}
+//DISABLEDamd #define writel(d,a) {((*(volatile unsigned int *)(a)) = (__ioswab32(d))); au1200_sync();}
+//DISABLEDamd #define writeb(d,a) {((*(volatile unsigned char *)(a)) = (d)); au1200_sync();}
+#endif 
+/****** following flags can be set by module parameters */
+/* DMA usage flag */
+static int use_dma = 1;
+/* packet per buffer dma */
+static int use_dma_ppb = 1;
+/* with per descr. update */
+static int use_dma_ppb_du = 0;
+/* buffer fill mode */
+static int use_dma_bufferfill_mode = 0;
+/* full speed only mode */
+static int use_fullspeed = 0;
+/* tx buffer size for high speed */
+static unsigned long hs_tx_buf = UDC_EPIN_BUFF_SIZE;
+
+/* module parameters */
+MODULE_PARM (use_dma, "i");
+MODULE_PARM_DESC (use_dma, "true to use dma");
+MODULE_PARM (use_dma_ppb, "i");
+MODULE_PARM_DESC (use_dma_ppb, "true to use dma in packet per buffer mode");
+MODULE_PARM (use_dma_ppb_du, "i");
+MODULE_PARM_DESC (use_dma_ppb_du, "true to use dma in packet per buffer mode with descriptor update");
+MODULE_PARM (use_fullspeed, "i");
+MODULE_PARM_DESC (use_fullspeed, "true to use fullspeed only");
+MODULE_PARM (hs_tx_buf, "l");
+MODULE_PARM_DESC (hs_tx_buf, "high speed tx buffer size for data endpoints in dwords");
+
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_AUTHOR ("Thomas Dahlmann");
+MODULE_LICENSE("GPL");
+
+#ifndef CONFIG_USB_NON_PCI_OTGDEVICE
+MODULE_DEVICE_TABLE (pci, pci_ids);
+#endif
+
+/*****************************************************************************
+ *  Function Definitions
+ *****************************************************************************/
+
+/* AU specific */
+#ifdef CONFIG_SOC_AU1200
+void au1200_sync(void)
+{
+        au1200_sync_delay();
+}
+#endif
+
+/* Debugging ----------------------------------------------------------------*/
+#if defined(USE_SYSFS_DEBUG_FILES) || defined (DEBUG)
+/**
+ * Gets traffic type string of endpoint
+ * 
+ * \param bmAttributes  endpoint transfer type
+ * \return endpoint transfer type
+ */
+static char *type_string (u8 bmAttributes)
+{
+        switch ((bmAttributes) & USB_ENDPOINT_XFERTYPE_MASK) {
+        case USB_ENDPOINT_XFER_BULK:    return "bulk";
+        case USB_ENDPOINT_XFER_ISOC:    return "iso";
+        case USB_ENDPOINT_XFER_INT:     return "intr";
+        };
+        return "control";
+}
+#endif
+
+/**
+ * Dumps dword read access
+ * 
+ * \param a address pointer
+ * \return read dword
+ */
+#ifdef UDC_REGISTER_DUMP
+#ifdef VERBOSE
+unsigned int read32(void* a)
+{
+        unsigned int  d;
+        d = *(volatile unsigned int *) __io_virt(a);
+        DBG("read32 : address=%08lx data=%08lx\n",a,d);
+        return d;
+
+}
+/**
+ * Dumps byte read access
+ * 
+ * \param a address pointer
+ * \return read byte
+ */
+unsigned char read8(void* a)
+{
+        unsigned char  d;
+        d = *(volatile unsigned char *) __io_virt(a);
+        DBG("read8  : address=%08lx data=%08x\n",a,d);
+        return d;
+
+}
+#endif
+#endif
+
+#ifdef DEBUG
+/**
+ * Dumps byte read access
+ * 
+ * \param a address pointer
+ * \return read byte
+ */
+static void print_td(struct udc_data_dma *p)
+{
+        INFO("td = %08lx: dmastat=%08lx bufptr=%08lx next=%08lx\n", 
+             (unsigned long) p,
+             (unsigned long) p->dmastat,
+             (unsigned long) p->bufptr,
+             (unsigned long) p->next);
+}
+#endif
+
+/* printing registers --------------------------------------------------------*/
+/**
+ * Prints UDC device registers and endpoint irq registers
+ * 
+ * \param dev pointer to device struct 
+ */
+static void print_regs(struct udc* dev)
+{
+#ifndef UDC_IPBUG_3943_WORKAROUND
+        DBG( "------- Device registers -------\n");
+        DBG( "dev config     = %08lx\n", (unsigned long) dev->regs->cfg);
+        DBG( "dev control    = %08lx\n", (unsigned long) dev->regs->ctl);
+        DBG( "dev status     = %08lx\n", (unsigned long) dev->regs->sts);
+        DBG( "\n");
+        DBG( "dev int's      = %08lx\n", (unsigned long) dev->regs->irqsts);
+        DBG( "dev intmask    = %08lx\n", (unsigned long) dev->regs->irqmsk);
+        DBG( "\n");
+        DBG( "dev ep int's   = %08lx\n", (unsigned long) dev->regs->ep_irqsts);
+        DBG( "dev ep intmask = %08lx\n", (unsigned long) dev->regs->ep_irqmsk);
+        DBG( "\n");
+#endif
+        DBG( "USE DMA        = %d\n", use_dma);
+        if (use_dma && use_dma_ppb && !use_dma_ppb_du) {
+                DBG( "DMA mode       = PPBNDU (packet per buffer WITHOUT desc. update)\n");
+#ifndef DEBUG
+                INFO( "DMA mode (PPBNDU)\n");
+#endif
+        } 
+        else if (use_dma && use_dma_ppb_du && use_dma_ppb_du) {
+                DBG( "DMA mode       = PPBDU (packet per buffer WITH desc. update)\n");
+#ifndef DEBUG
+                INFO( "DMA mode (PPBDU)\n");
+#endif
+        } 
+        if (use_dma && use_dma_bufferfill_mode) {
+                DBG( "DMA mode       = BF (buffer fill mode)\n");
+#ifndef DEBUG
+                INFO( "DMA mode (BF)\n");
+#endif
+        } 
+#ifndef DEBUG
+        if (!use_dma) {
+                INFO( "FIFO mode\n");
+        } 
+#endif
+        DBG("-------------------------------------------------------\n");       
+}
+
+/**
+ * Prints snapshot of ep registers
+ * 
+ * \param dev   pointer to device struct
+ */
+#ifdef DEBUG
+static void print_epregs(struct udc* dev, struct udc_epregs* epregs)
+{
+        INFO( "ep control  = %08lx\n", (unsigned long) epregs->ctl);
+        INFO( "ep status   = %08lx\n", (unsigned long) epregs->sts);
+        INFO( "--------------------------------\n");
+}
+#endif
+
+/**
+ * Prints ep registers
+ * 
+ * \param dev           pointer to device struct
+ * \param ep_ix         endpoint index
+ */
+#ifdef NOT_USED
+static void print_current_epregs(struct udc* dev, int ep_ix)
+{
+        INFO( "------- ep %d registers -------\n", ep_ix);
+        print_epregs(dev, dev->ep[ep_ix].regs);
+        INFO( "--------------------------------\n");
+}
+#endif
+
+/* TODO tdahlman: remove */
+/**
+ * Prints misc information, to be removed
+ * 
+ * \param dev           pointer to device struct
+ */
+#ifdef DEBUG
+static void print_misc(struct udc* dev)
+{
+        print_regs(dev);
+ 
+        /******* for DMA IN problem */  
+        if (use_dma) {
+
+                INFO("no_req=%ld no_pref_req=%ld\n", no_req, no_pref_req);
+        }
+}
+#endif
+
+/* driver functions ----------------------------------------------------------*/
+/**
+ * Called by OS for insmod 
+ * 
+ * \param inode         file node
+ * \param               file struct
+ * \return read byte
+ */
+#ifdef DEBUG
+static int udc_open (struct inode *inode, struct file *file)
+{
+        int retval = 0;
+
+#if     LINUX_VERSION_CODE < UDC_NEW_GADGET_KERNEL
+        /* Increment our usage count for the module.*/
+        MOD_INC_USE_COUNT;
+#endif
+
+        return retval;
+}
+
+/**
+ * Called by OS for rmmod 
+ * 
+ * \param inode         file node
+ * \param file          file struct
+ * \return read byte
+ */
+static int udc_release (struct inode *inode, struct file *file)
+{
+        int retval = 0;
+
+#if     LINUX_VERSION_CODE < UDC_NEW_GADGET_KERNEL
+        /* decrement our usage count for the module */
+        MOD_DEC_USE_COUNT;
+#endif
+        return retval;
+}
+
+/**
+ * Called by OS for ioctl() from user space
+ * 
+ * \param inode         file node
+ * \param file          file struct
+ * \param cmd           ioctl command code
+ * \param arg           ioctl argument
+ * \return 0 if success 
+ */
+static int udc_ioctl (struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
+{
+        struct udc* dev = the_controller;
+        
+        if (!dev)
+                return -EINVAL;
+        
+        INFO( "ioctl called\n");
+
+        print_misc(dev);
+        return 0;
+}
+#endif
+
+/**
+ * Masks unused interrupts 
+ * 
+ * \param dev           pointer to device struct
+ * \return 0 if success 
+ */
+static int udc_mask_unused_interrupts(struct udc* dev)
+{
+        u32 tmp;
+
+        /* mask all dev interrupts */
+        tmp =   AMD_BIT(UDC_DEVINT_SVC) |
+                AMD_BIT(UDC_DEVINT_ENUM) |
+                AMD_BIT(UDC_DEVINT_US) |
+                AMD_BIT(UDC_DEVINT_UR) |
+                AMD_BIT(UDC_DEVINT_ES) |
+                AMD_BIT(UDC_DEVINT_SI) |
+                AMD_BIT(UDC_DEVINT_SOF)|
+                AMD_BIT(UDC_DEVINT_SC);
+        writel(tmp, &dev->regs->irqmsk);        
+        
+        /* mask all ep interrupts */
+        writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqmsk);        
+
+        return 0;
+}
+
+/**
+ * Enables endpoint 0 interrupts 
+ * 
+ * \param dev           pointer to device struct
+ * \return 0 if success 
+ */
+static int udc_enable_ep0_interrupts(struct udc* dev)
+{
+        u32 tmp;
+
+        DBG("udc_enable_ep0_interrupts()\n");
+
+        /* read irq mask */
+        tmp = readl(&dev->regs->ep_irqmsk);
+        /* enable ep0 irq's */
+        tmp &= AMD_UNMASK_BIT(UDC_EPINT_IN_EP0)
+               & AMD_UNMASK_BIT(UDC_EPINT_OUT_EP0);  
+        writel(tmp, &dev->regs->ep_irqmsk);        
+
+        return 0;
+}
+
+/**
+ * Enables device interrupts for SET_INTF and SET_CONFIG
+ * 
+ * \param dev           pointer to device struct
+ * \return 0 if success 
+ */
+static int udc_enable_dev_setup_interrupts(struct udc* dev)
+{
+        u32 tmp;
+
+        DBG("enable device interrupts for setup data\n");
+
+        /* read irq mask */
+        tmp = readl(&dev->regs->irqmsk);
+       
+        /* enable SET_INTERFACE, SET_CONFIG and other needed irq's */
+        tmp &= AMD_UNMASK_BIT(UDC_DEVINT_SI)
+               & AMD_UNMASK_BIT(UDC_DEVINT_SC)  
+#ifdef UDC_IPCASE_8000009363_WORKAROUND
+               & AMD_UNMASK_BIT(UDC_DEVINT_SOF)  
+#endif
+               & AMD_UNMASK_BIT(UDC_DEVINT_UR)  
+#ifndef UDC_IPBUG_3943_WORKAROUND
+               & AMD_UNMASK_BIT(UDC_DEVINT_US)  
+#endif
+#ifndef UDC_IPBUG_3950_WORKAROUND
+               & AMD_UNMASK_BIT(UDC_DEVINT_SVC)
+#endif
+               & AMD_UNMASK_BIT(UDC_DEVINT_ENUM);
+        writel(tmp, &dev->regs->irqmsk);        
+
+        return 0;
+}
+
+/**
+ * Calculates fifo start of endpoint based on preceeding endpoints
+ * 
+ * \param ep           pointer to ep struct
+ * \return 0 if success 
+ */
+static int udc_set_txfifo_addr(struct udc_ep *ep)
+{
+        struct udc      *dev;
+        u32 tmp;
+        int i;
+
+        if (!ep || !(ep->is_in))
+                return -EINVAL;
+        
+        dev = ep->dev;
+        ep->txfifo = dev->txfifo;
+
+        /* traverse ep's */
+        for (i = 0; i < ep->num; i++) {
+                if (dev->ep[i].regs) {
+                        /* read fifo size */
+                        tmp = readl(&dev->ep[i].regs->bufin_framenum);
+                        tmp = AMD_GETBITS(tmp, UDC_EPIN_BUFF_SIZE);
+                        ep->txfifo+= tmp;
+                }
+        }
+        return 0;
+}
+
+/**
+ * Enables endpoint, is called by gadget driver
+ * 
+ * \param ep            pointer to ep struct
+ * \param desc          pointer to endpoint descriptor
+ * \return 0 if success 
+ */
+static int
+udc_enable (struct usb_ep *_ep, const struct usb_endpoint_descriptor *desc)
+{
+        struct udc              *dev;
+        struct udc_ep   *ep;
+        u32                     tmp;
+        unsigned long           flags;
+        u8 udc_csr_epix;
+
+        VDBG("udc_enable()\n");        
+        
+        ep = container_of (_ep, struct udc_ep, ep);
+        if (!_ep || !desc || ep->desc || _ep->name == ep0name
+                        || desc->bDescriptorType != USB_DT_ENDPOINT) {
+                ERR("udc_enable: !_ep=%d !desc=%d ep->desc!=NULL=%d _ep->name==ep0name=%d desc->bDescriptorType!=USB_DT_ENDPOINT=%d\n",
+                     !_ep, !desc, ep->desc != NULL, _ep->name == ep0name, desc->bDescriptorType != USB_DT_ENDPOINT);
+                return -EINVAL;
+        }
+        
+        DBG("udc_enable() ep %d\n", ep->num);
+        
+        dev = ep->dev;
+        if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+                return -ESHUTDOWN;
+
+        spin_lock_irqsave (&dev->lock, flags);
+        ep->desc = desc;
+
+        /* ep_reset() has already been called */
+        ep->stopped = 0;
+
+        /* set traffic type */
+        tmp = readl(&dev->ep[ep->num].regs->ctl);
+        tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_EPCTL_ET);
+        writel(tmp, &dev->ep[ep->num].regs->ctl);
+        ep->is_iso = (desc->bmAttributes == USB_ENDPOINT_XFER_ISOC) ? 1 : 0;
+
+        /* set max packet size */
+        tmp = readl(&dev->ep[ep->num].regs->bufout_maxpkt);
+        tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize, UDC_EP_MAX_PKT_SIZE);
+        ep->ep.maxpacket = desc->wMaxPacketSize;
+        writel(tmp, &dev->ep[ep->num].regs->bufout_maxpkt);
+
+        /* IN ep */
+        if (ep->is_in) {
+                
+                /* ep ix in UDC CSR register space */
+                udc_csr_epix = ep->num;
+                
+                /* set buffer size (tx fifo entries) */
+                tmp = readl(&dev->ep[ep->num].regs->bufin_framenum);
+                /* double buffering: fifo size = 2 x max packet size */
+                tmp = AMD_ADDBITS(
+                              tmp,
+                              desc->wMaxPacketSize * UDC_EPIN_BUFF_SIZE_MULT /
+                                                     UDC_DWORD_BYTES,
+                              UDC_EPIN_BUFF_SIZE);                 
+                writel(tmp, &dev->ep[ep->num].regs->bufin_framenum);
+
+                /* calc. tx fifo base addr */
+                udc_set_txfifo_addr(ep); 
+
+                /* flush fifo */
+                tmp = readl(&ep->regs->ctl);
+                tmp |= AMD_BIT(UDC_EPCTL_F); 
+                writel(tmp, &ep->regs->ctl);
+                
+        } /* OUT ep */
+        else {
+                /* ep ix in UDC CSR register space */
+                udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+
+                /* set max packet size UDC CSR  */
+                tmp = readl(&dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]);
+                tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize, UDC_CSR_NE_MAX_PKT);
+                writel(tmp, &dev->csr->ne[ep->num - UDC_CSR_EP_OUT_IX_OFS]);
+        }
+
+        /***** UDC CSR reg ****************************/
+        /* set ep values  */
+        tmp = readl(&dev->csr->ne[udc_csr_epix]);
+        /* max packet */
+        tmp = AMD_ADDBITS(tmp, desc->wMaxPacketSize, UDC_CSR_NE_MAX_PKT);
+        /* ep number */
+        tmp = AMD_ADDBITS(tmp, desc->bEndpointAddress, UDC_CSR_NE_NUM);
+        /* ep direction */
+        tmp = AMD_ADDBITS(tmp, ep->is_in, UDC_CSR_NE_DIR);
+        /* ep type */
+        tmp = AMD_ADDBITS(tmp, desc->bmAttributes, UDC_CSR_NE_TYPE);
+        /* ep config */
+        tmp = AMD_ADDBITS(tmp, ep->dev->cur_config, UDC_CSR_NE_CFG);
+        /* ep interface */
+        tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf, UDC_CSR_NE_INTF);
+        /* ep alt */
+        tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt, UDC_CSR_NE_ALT);
+        /* write reg */
+        writel(tmp, &dev->csr->ne[udc_csr_epix]);
+        
+        /* enable ep irq */
+        tmp = readl(&dev->regs->ep_irqmsk);
+        tmp &= AMD_UNMASK_BIT(ep->num);
+        writel(tmp, &dev->regs->ep_irqmsk);        
+
+        /* clear NAK by writing CNAK */
+        /* avoid BNA for DMA,  dont clear NAK until DMA desc. written */
+        if (!use_dma) {
+                tmp = readl(&ep->regs->ctl);
+                tmp |= AMD_BIT(UDC_EPCTL_CNAK); 
+                writel(tmp, &ep->regs->ctl);
+                ep->naking = 0;
+                UDC_QUEUE_CNAK(ep, ep->num);
+        }
+        tmp = desc->bEndpointAddress;
+        DBG( "enabled %s (ep%d%s-%s) %s\n",
+                _ep->name, tmp & 0x0f, DIR_STRING (tmp),
+                type_string (desc->bmAttributes),
+                ep->dma ? "dma" : "pio");
+
+        /* pci writes may still be posted */
+        spin_unlock_irqrestore (&dev->lock, flags);
+        return 0;
+}
+/**
+ * Resets endpoint
+ * 
+ * \param regs          pointer to device register struct
+ * \param ep            pointer to endpoint
+ */
+static void ep_reset (struct udc_regs *regs, struct udc_ep *ep)
+{
+        u32             tmp;
+
+        VDBG("ep-%d reset\n", ep->num);
+        ep->desc = 0;
+        INIT_LIST_HEAD (&ep->queue);
+
+        ep->ep.maxpacket = ~0;
+        ep->ep.ops = &udc_ep_ops;
+
+        /* set NAK  */
+        tmp = readl(&ep->regs->ctl);
+        tmp |= AMD_BIT(UDC_EPCTL_SNAK); 
+        writel(tmp, &ep->regs->ctl);
+        ep->naking = 1;
+        
+        /* disable interrupt */
+        tmp = readl(&regs->ep_irqmsk);
+        tmp |= AMD_BIT(ep->num);
+        writel(tmp, &regs->ep_irqmsk);        
+
+        if (ep->is_in) {
+                /* unset P and IN bit of potential former DMA */
+                tmp = readl(&ep->regs->ctl);
+                tmp &= AMD_UNMASK_BIT(UDC_EPCTL_P);
+                writel(tmp, &ep->regs->ctl);
+
+                tmp = readl(&ep->regs->sts);
+                tmp |= AMD_BIT(UDC_EPSTS_IN);
+                writel(tmp, &ep->regs->sts);
+
+                /* flush the fifo */
+                tmp = readl(&ep->regs->ctl);
+                tmp |= AMD_BIT(UDC_EPCTL_F); 
+                writel(tmp, &ep->regs->ctl);
+
+        }
+        /* reset desc pointer */
+        writel(0, &ep->regs->desptr);
+
+
+}
+
+/**
+ * Disables endpoint, is called by gadget driver
+ * 
+ * \param ep            pointer to ep struct
+ * \return 0 if success 
+ */
+static int udc_disable (struct usb_ep *_ep)
+{
+        struct udc_ep   *ep = NULL;
+        unsigned long           flags;
+
+        ep = container_of (_ep, struct udc_ep, ep);
+        if (!_ep || !ep->desc || _ep->name == ep0name)
+                return -EINVAL;
+
+        DBG("Disable ep-%d\n", ep->num);
+        spin_lock_irqsave (&ep->dev->lock, flags);
+        nuke (ep);
+        ep_reset (ep->dev->regs, ep);
+
+        VDBG( "disabled %s %s\n",
+                        ep->dma ? "dma" : "pio", _ep->name);
+
+        spin_unlock_irqrestore (&ep->dev->lock, flags);
+        return 0;
+}
+
+/**
+ * Allocates request packet, called by gadget driver
+ * 
+ * \param _ep           pointer to usb ep struct
+ * \param gfp_flags     flags for kmalloc
+ * \return allocated request packet, 0 if error
+ */
+static struct usb_request *
+udc_alloc_request (struct usb_ep *_ep, int gfp_flags)
+{
+        struct udc_ep   *ep;
+        struct udc_request      *req;
+
+        VDBG("udc_alloc_req()\n");
+        if (!_ep)
+                return 0;
+        ep = container_of (_ep, struct udc_ep, ep);
+        /* TODO gfp flags used for dma chaining later - find better way */
+        td_gfp_flags = gfp_flags;
+
+        VDBG("udc_alloc_req(): ep%d\n", ep->num);
+        req = kmalloc (sizeof *req, gfp_flags);
+        if (!req)
+                return 0;
+
+        memset (req, 0, sizeof *req);
+        req->req.dma = DMA_ADDR_INVALID;
+        INIT_LIST_HEAD (&req->queue);
+
+        if (ep->dma) {
+                struct udc_data_dma     *td;
+
+#if defined(CONFIG_MIPS)
+                gfp_flags = GFP_ATOMIC | GFP_DMA;
+#endif
+                /* FIXME ep0 in requests are allocated from data pool here */
+                td = pci_pool_alloc (ep->dev->data_requests, gfp_flags,
+                                &req->td_dma);
+                if (!td) {
+                        kfree (req);
+                        return 0;
+                }
+
+                VDBG("udc_alloc_req: req = %lx td = %lx, req->td_dma = %lx\n",
+                     (unsigned long) req, (unsigned long) td, (unsigned long)req->td_dma);
+                /* prevent from using desc. - set HOST BUSY */
+                td->dmastat = AMD_ADDBITS(td->dmastat,
+                                          UDC_DMA_STP_STS_BS_HOST_BUSY,
+                                          UDC_DMA_STP_STS_BS);  
+                td->bufptr = __constant_cpu_to_le32 (DMA_ADDR_INVALID);
+                req->td_data = td;
+                req->td_data_last = NULL;
+                req->chain_len = 1;
+        }
+        return &req->req;
+}
+
+/**
+ * Frees request packet, called by gadget driver
+ * 
+ * \param _ep   pointer to usb ep struct
+ * \param _req  pointer to request packet to be freed
+ */
+static void
+udc_free_request (struct usb_ep *_ep, struct usb_request *_req)
+{
+        struct udc_ep   *ep;
+        struct udc_request      *req;
+        ep = container_of (_ep, struct udc_ep, ep);
+        if (!_ep || !_req)
+                return;
+
+        req = container_of (_req, struct udc_request, req);
+        VDBG("free_req req=%lx\n", (unsigned long) req);
+        WARN_ON (!list_empty (&req->queue));
+        if (req->td_data) {
+                VDBG("req->td_data=%lx\n", (unsigned long) req->td_data);
+
+#ifdef UDC_IPDEFECT_9000004946_WORKAROUND
+                /* re-link broken chain  */
+                if (req->td_data_last) {
+                        req->td_data_last->next = req->td_data_last_next;
+                }
+#endif
+                /* free dma chain if created */
+                if (req->chain_len > 1) {
+                        udc_free_dma_chain(ep->dev, req);
+                }
+
+                pci_pool_free (ep->dev->data_requests, req->td_data, req->td_dma);
+        }
+        kfree (req);
+}
+
+/*-------------------------------------------------------------------------*/
+#undef USE_KMALLOC
+
+/* many common platforms have dma-coherent caches, which means that it's
+ * safe to use kmalloc() memory for all i/o buffers without using any
+ * cache flushing calls.  (unless you're trying to share cache lines
+ * between dma and non-dma activities, which is a slow idea in any case.)
+ *
+ * other platforms need more care, with 2.5 having a moderately general
+ * solution (which falls down for allocations smaller than one page)
+ * that improves significantly on the 2.4 PCI allocators by removing
+ * the restriction that memory never be freed in_interrupt().
+ */
+#if     defined(CONFIG_X86)
+#define USE_KMALLOC
+
+#elif   defined(CONFIG_PPC) && !defined(CONFIG_NOT_COHERENT_CACHE)
+#define USE_KMALLOC
+
+#elif   defined(CONFIG_MIPS) && !defined(CONFIG_NONCOHERENT_IO)
+#define USE_KMALLOC
+/* FIXME there are other cases, including an x86-64 one ...  */
+#endif
+
+/* allocating buffers this way eliminates dma mapping overhead, which
+ * on some platforms will mean eliminating a per-io buffer copy.  with
+ * some kinds of system caches, further tweaks may still be needed.
+ */
+
+/**
+ * Allocated data buffer for request packet, called by gadget driver
+ * 
+ * \param _ep           pointer to usb ep struct
+ * \param bytes         number bytes to allocate
+ * \param dma           pointer to dma struct
+ * \param gfp_flags     flags for allocate 
+ * \return pointer to allocated buffer, 0 if error
+ */
+static void *
+udc_alloc_buffer (
+        struct usb_ep           *_ep,
+        unsigned                bytes,
+        dma_addr_t              *dma,
+        int                     gfp_flags
+)
+{
+        void                    *retval;
+        struct udc_ep   *ep;
+        VDBG("alloc_buffer(): ep-%s\n", _ep->name);
+        ep = container_of (_ep, struct udc_ep, ep);
+        if (!_ep)
+                return 0;
+        *dma = DMA_ADDR_INVALID;
+
+        if (ep->dma) {
+#if     defined(USE_KMALLOC)
+
+#if defined(CONFIG_MIPS)
+                gfp_flags = GFP_ATOMIC | GFP_DMA;
+#endif
+                retval = kmalloc (bytes, gfp_flags);
+
+                if (retval)
+                        *dma = virt_to_phys (retval);
+
+#elif   LINUX_VERSION_CODE >= UDC_NEW_GADGET_KERNEL
+#warning Using dma_alloc_consistent even with sub-page allocations
+                /* the main problem with this call is that it wastes memory
+                 * on typical 1/N page allocations: it allocates 1-N pages.
+                 */
+                retval = dma_alloc_coherent (&ep->dev->pdev->dev,
+                                bytes, dma, gfp_flags);
+#elif defined (CONFIG_MIPS)
+#warning Using pci_alloc_consistent even with buffers smaller than a page.
+                retval = pci_alloc_consistent(ep->dev->pdev, bytes, dma);
+#else
+#error No dma-coherent memory allocator is available
+                /* pci_alloc_consistent works, but pci_free_consistent()
+                 * isn't safe in_interrupt().  plus, in addition to the
+                 * 1/Nth page weakness, it doesn't understand gfp_flags.
+                 */
+#endif
+        } else
+                retval = kmalloc (bytes, gfp_flags);
+
+        VDBG("alloc_buffer() retval = %lx dma=%lx\n", (unsigned long) retval, (unsigned long) *dma);
+        return retval;
+}
+
+
+/**
+ * Free data buffer for request packet, called by gadget driver
+ * 
+ * \param _ep           pointer to usb ep struct
+ * \param buf           pointer to buffer to be freed
+ * \param dma           pointer to dma struct
+ * \param bytes         number bytes to be freed 
+ * \return pointer to allocated buffer, 0 if error
+ */
+static void
+udc_free_buffer (
+        struct usb_ep *_ep,
+        void *buf,
+        dma_addr_t dma,
+        unsigned bytes
+) {
+        struct udc_ep   *ep;
+
+        VDBG ("ep %s - free buffer %lx\n", _ep->name, (unsigned long) buf);
+
+        /* null pointer ? */
+        if (!buf) {
+                return;
+        }
+
+        ep = container_of(_ep, struct udc_ep, ep);
+        if (!ep)
+                return;
+
+        /* free memory into the right allocator */
+#ifndef USE_KMALLOC
+        if (dma != DMA_ADDR_INVALID) {
+#if defined (CONFIG_MIPS)
+                /* one problem with this call is that some platforms
+                 * don't allow it to be used in_irq().
+                 */
+                pci_free_consistent(ep->dev->pdev, bytes, buf, dma);
+#else
+                dma_free_coherent (ep->dev->pdev, bytes, dma);
+#endif
+        } else
+#endif
+        {
+                if (!(ep->dma))
+                        kfree (buf);
+        }
+
+}
+
+/**
+ * Write data to TX fifo for IN packets 
+ * 
+ * \param ep            pointer to ep struct
+ * \param req           pointer to request packet
+ * \return allocated request packet, 0 if error
+ */
+static void
+write_fifo (struct udc_ep *ep, struct usb_request *req)
+{
+        u8                      *buf;
+        unsigned                count, total;
+
+        VDBG("write_fifo()\n");
+        if (req) {
+                buf = req->buf + req->actual;
+                prefetch (buf);
+                total = req->length - req->actual;
+        } else {
+                total = 0;
+                buf = 0;
+        }
+        
+        /* write just one packet at a time */
+        count = min (ep->ep.maxpacket, total);
+
+        /* TODO tdahlman - get dev pointer from ep */
+        udc_write_fifo(the_controller, ep->num, (u32*) buf, count); 
+}
+
+/**
+ * Read dwords from RX fifo for OUT transfers
+ * 
+ * \param dev           pointer to device struct
+ * \param buf           pointer to buffer to be filled
+ * \param dwords        number of dwords to be read
+ * \return allocated request packet, 0 if error
+ */
+static int udc_read_fifo_dwords(struct udc* dev, u32* buf, int dwords)
+{
+        int i;
+        
+        VDBG("udc_read_dwords(): %d dwords\n", dwords);
+
+        for (i = 0; i < dwords; i++)
+        {
+                *(buf + i) = readl(dev->rxfifo);
+        }        
+        return 0;
+}
+
+/**
+ * Read bytes from RX fifo for OUT transfers
+ * 
+ * \param dev           pointer to device struct
+ * \param buf           pointer to buffer to be filled
+ * \param bytes         number of bytes to be read
+ * \return allocated request packet, 0 if error
+ */
+static int udc_read_fifo_bytes(struct udc* dev, u8* buf, int bytes)
+{
+        int i,j;
+        u32 tmp;
+        
+        VDBG("udc_read_bytes(): %d bytes\n", bytes);
+
+        /* dwords first */
+        for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) {
+               *((u32*) (buf + (i<<2))) = readl(dev->rxfifo); 
+        }
+
+        /* remaining bytes must be read by byte access */
+        if (bytes % UDC_DWORD_BYTES) {
+                tmp = readl(dev->rxfifo);
+                for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) {
+                        *(buf + (i<<2) + j) = (u8) (tmp & UDC_BYTE_MASK);
+                        tmp = tmp >> UDC_BITS_PER_BYTE;
+                }
+        }
+        
+        return 0;
+}
+
+/**
+ * Write bytes to TX fifo for IN packets 
+ * 
+ * \param dev           pointer to device struct
+ * \param ep_ix         endpoint index
+ * \param buf           pointer to buffer to be write 
+ * \param bytes         number of bytes to be write 
+ * \return allocated request packet, 0 if error
+ */
+static int udc_write_fifo(struct udc* dev, int ep_ix, u32* buf, int bytes)
+{
+        int i,j;
+        struct udc_ep *ep;
+
+        VDBG("udc_write_fifo: %d bytes\n", bytes);
+
+        ep = &dev->ep[ep_ix];
+
+        /* dwords first */
+        for (i = 0; i < bytes / UDC_DWORD_BYTES; i++) {
+                writel(*(buf + i), ep->txfifo);
+        }
+
+        /* remaining bytes must be written by byte access */
+        for (j = 0; j < bytes % UDC_DWORD_BYTES; j++) {
+                writeb((u8) (*(buf + i) >> (j << UDC_BITS_PER_BYTE_SHIFT)),
+                       ep->txfifo);
+        }
+
+#ifdef UDC_IPBUG_2253_WORKAROUND
+        {
+                u32 tmp;
+
+                /* NAK if small packet until write confirm completed */
+                if (bytes < UDC_SMALL_PACKET) {
+                        /* set NAK */
+                        tmp = readl(&ep->regs->ctl);
+                        tmp |= AMD_BIT(UDC_EPCTL_SNAK); 
+                        writel(tmp, &ep->regs->ctl);
+                        ep->naking = 1;
+                        wb_flush();
+                }
+
+                /* dummy write confirm */
+                writel(0, &ep->regs->confirm); 
+
+                /* stop NAKing after small packet DMA */
+                if (ep->naking) {
+                        /* clear NAK by writing CNAK */
+                        tmp = readl(&ep->regs->ctl);
+                        tmp |= AMD_BIT(UDC_EPCTL_CNAK); 
+                        writel(tmp, &ep->regs->ctl);
+                        ep->naking = 0;
+                        UDC_QUEUE_CNAK(ep, ep->num);
+                }
+        }
+#else
+        /* dummy write confirm */
+        writel(0, &ep->regs->confirm); 
+#endif
+        
+        return 0;
+}
+
+/**
+ * Read data from RX fifo for OUT transfers
+ * 
+ * \param ep            pointer to ep struct
+ * \param req           pointer to request packet
+ * \return true if request completes for short or max packet, false otherwise   
+ */
+static int
+read_fifo (struct udc_ep *ep, struct udc_request *req)
+{
+        u8                      *buf = req->req.buf + req->req.actual;
+        unsigned                count=0, tmp, is_short;
+        unsigned                cleanup = 0;
+
+
+        /* TODO needed ? */
+        prefetchw (buf);
+        
+        /* received number bytes */
+        count = readl(&ep->regs->sts);
+        count = AMD_GETBITS(count, UDC_EPSTS_RX_PKT_SIZE);
+        
+        tmp = req->req.length - req->req.actual;
+        if (count > tmp) {
+                /* as with DMA, data overflow gets flushed */
+                if ((tmp % ep->ep.maxpacket) != 0) {
+                        ERR( "%s out fifo %d bytes, expected %d\n",
+                                ep->ep.name, count, tmp);
+                        req->req.status = -EOVERFLOW;
+                        cleanup = 1;
+                }
+                count = tmp;
+        }
+        req->req.actual += count;
+        is_short = (count == 0) || ((count % ep->ep.maxpacket) != 0);
+        VDBG( "read %s fifo (OUT) %d bytes%s%s req %p %d/%d\n",
+                        ep->ep.name, count, is_short ? " (short)" : "",
+                        cleanup ? " flush" : "", 
+                        req, req->req.actual, req->req.length);
+        
+        /* read rx fifo bytes */
+        udc_read_fifo_bytes(ep->dev, buf, count); 
+       
+        return is_short || ((req->req.actual == req->req.length)
+                                && !req->req.zero);
+}
+
+/**
+ *  Temporary left in the code for later reusing or removing
+ */
+static inline void spin_stop_dma (u32 *dma)
+{
+}
+
+/**
+ *  Temporary left in the code for later reusing or removing
+ */
+static inline void stop_dma (u32 *dma)
+{
+        spin_stop_dma (dma);
+}
+
+/**
+ * create/re-init a DMA descriptor or a DMA descriptor chain
+ *
+ * \param ep            pointer to endpoint struct 
+ * \param req           pointer to request packet
+ */
+static void prep_dma (struct udc_ep *ep, struct udc_request *req)
+{
+        u32                     tmp;
+        VDBG("prep_dma\n");
+        VDBG("prep_dma ep%d req->td_data=%lx\n",
+              ep->num, (unsigned long) req->td_data);
+
+        /* set buffer pointer */
+        req->td_data->bufptr   = req->req.dma;
+        /* set last bit */
+        req->td_data->dmastat |= AMD_BIT(UDC_DMA_IN_STS_L);
+        
+        /* build/re-init dma chain if maxpkt scatter mode, not for EP0 */
+        if (use_dma_ppb && ep->num != UDC_EP0OUT_IX && ep->num != UDC_EP0IN_IX) {
+
+                udc_create_dma_chain(ep, req, ep->ep.maxpacket, td_gfp_flags);
+                /*TODO better place ? */
+                if (ep->is_in) {
+                        if (req->req.length == ep->ep.maxpacket) {
+                                /* write tx bytes */
+                                req->td_data->dmastat = AMD_ADDBITS(req->td_data->dmastat,
+                                                ep->ep.maxpacket,
+                                                UDC_DMA_IN_STS_TXBYTES); 
+
+                        }
+                }
+                
+        }
+#ifdef UDC_IPDEFECT_9000004946_WORKAROUND
+        else { /* EP0 */
+                /* point to itself */
+                req->td_data->next = req->td_dma;
+        }
+#endif
+        
+        if (ep->is_in) {
+                VDBG("IN: use_dma_ppb=%d req->req.length=%d ep->ep.maxpacket=%d ep%d\n",
+                      use_dma_ppb, req->req.length, ep->ep.maxpacket, ep->num);
+                /* if bytes < max packet then tx bytes must */
+                /* be written in packet per buffer mode */
+                if (!use_dma_ppb || req->req.length < ep->ep.maxpacket
+                    || ep->num == UDC_EP0OUT_IX || ep->num == UDC_EP0IN_IX) {
+                        /* write tx bytes */
+                        req->td_data->dmastat = AMD_ADDBITS(req->td_data->dmastat,
+                                                            req->req.length,
+                                                            UDC_DMA_IN_STS_TXBYTES); 
+                        /* reset frame num */
+                        req->td_data->dmastat = AMD_ADDBITS(req->td_data->dmastat,
+                                                            0,
+                                                            UDC_DMA_IN_STS_FRAMENUM); 
+                }
+                /* set HOST BUSY */
+                req->td_data->dmastat 
+                      = AMD_ADDBITS(req->td_data->dmastat,
+                                    UDC_DMA_STP_STS_BS_HOST_BUSY,
+                                    UDC_DMA_STP_STS_BS);
+        }
+        else {
+                VDBG("OUT set host ready\n");
+                /* set HOST READY */
+                req->td_data->dmastat 
+                      = AMD_ADDBITS(req->td_data->dmastat,
+                                    UDC_DMA_STP_STS_BS_HOST_READY,
+                                    UDC_DMA_STP_STS_BS);
+
+ 
+                        /* clear NAK by writing CNAK */
+                        if (ep->naking) {
+                                tmp = readl(&ep->regs->ctl);
+                                tmp |= AMD_BIT(UDC_EPCTL_CNAK); 
+                                writel(tmp, &ep->regs->ctl);
+                                ep->naking = 0;
+                                UDC_QUEUE_CNAK(ep, ep->num);
+                        }
+
+        }
+}
+
+/**
+ * Completes request packet 
+ * 
+ * \param ep            pointer to ep struct
+ * \param req           pointer to request packet
+ * \param status        status of request
+ */
+static void
+done (struct udc_ep *ep, struct udc_request *req, int status)
+{
+        struct udc              *dev;
+        unsigned                stopped = ep->stopped;
+
+        VDBG("done(): ep%d\n", ep->num);
+
+        list_del_init (&req->queue);
+
+        if (req->req.status == -EINPROGRESS)
+                req->req.status = status;
+        else
+                status = req->req.status;
+
+        dev = ep->dev;
+        if (req->mapped) {
+                pci_unmap_single (dev->pdev, req->req.dma, req->req.length,
+                        ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+                req->req.dma = DMA_ADDR_INVALID;
+                req->mapped = 0;
+        }
+
+        if (status && status != -ESHUTDOWN)
+                VDBG( "complete %s req %p stat %d len %u/%u\n",
+                        ep->ep.name, &req->req, status,
+                        req->req.actual, req->req.length);
+
+        /* don't modify queue heads during completion callback */
+        ep->stopped = 1;
+        VDBG( "complete %s req %p stat %d len %u/%u\n",
+                        ep->ep.name, &req->req, status,
+                        req->req.actual, req->req.length);
+        if (spin_is_locked(&dev->lock)) {
+                spin_unlock (&dev->lock);
+                req->req.complete (&ep->ep, &req->req);
+                spin_lock (&dev->lock);
+        }
+        else {
+                req->req.complete (&ep->ep, &req->req);
+        }
+
+        ep->stopped = stopped;
+}
+
+/**
+ * frees pci pool descriptors of a DMA chain 
+ * 
+ * \param dev           pointer to device struct
+ * \param req           pointer to request packet
+ * \return 0 if success
+ */
+static int udc_free_dma_chain(struct udc* dev, struct udc_request* req)
+{
+
+        int ret_val = 0;
+        struct udc_data_dma     *td;
+        struct udc_data_dma     *td_last = NULL;
+        unsigned int i;
+
+        DBG("free chain req = %lx\n", (unsigned long) req);
+
+#ifdef UDC_IPDEFECT_9000004946_WORKAROUND
+        /* re-link broken chain  */
+        if (req->td_data_last) {
+                req->td_data_last->next = req->td_data_last_next;
+        }
+#endif
+        /* do not free first desc., will be done by free for request */
+        td_last = req->td_data;
+#if defined (CONFIG_MIPS)
+        td = UNCAC_ADDR(phys_to_virt(td_last->next));
+#else
+        td = phys_to_virt(td_last->next);
+#endif
+
+        for (i = 1; i < req->chain_len; i++) {
+
+                pci_pool_free (dev->data_requests, td,
+                                (dma_addr_t) td_last->next);
+                td_last = td;
+#if defined (CONFIG_MIPS)
+                td = UNCAC_ADDR(phys_to_virt(td_last->next));
+#else
+                td = phys_to_virt(td_last->next);
+#endif
+        }
+
+#ifdef UDC_IPDEFECT_9000004946_WORKAROUND
+        req->td_data_last = NULL;
+#endif
+        return ret_val;        
+}
+
+/**
+ * Iterates to the end of a DMA chain and returns last descriptor 
+ * 
+ * \param req           pointer to request packet
+ * \return pointer to last descriptori of chain
+ */
+static struct udc_data_dma* udc_get_last_dma_desc(struct udc_request* req)
+{
+        struct udc_data_dma     *td;
+
+        td = req->td_data;
+        while (td && !(td->dmastat & AMD_BIT(UDC_DMA_IN_STS_L))) {
+#if defined (CONFIG_MIPS)
+                td = UNCAC_ADDR(phys_to_virt(td->next));
+#else
+                td = phys_to_virt(td->next);
+#endif
+        }
+
+        return td;
+
+}
+
+/**
+ * Iterates to the end of a DMA chain and counts bytes received 
+ * 
+ * \param req           pointer to request packet
+ * \return number of received bytes
+ */
+static u32 udc_get_ppbdu_rxbytes(struct udc_request* req)
+{
+        struct udc_data_dma     *td;
+        u32 count;
+
+        td = req->td_data;
+        /* received number bytes */
+        count = AMD_GETBITS(td->dmastat, UDC_DMA_OUT_STS_RXBYTES); 
+        
+        while (td && !(td->dmastat & AMD_BIT(UDC_DMA_IN_STS_L))) {
+#if defined (CONFIG_MIPS)
+                td = UNCAC_ADDR(phys_to_virt(td->next));
+#else
+                td = phys_to_virt(td->next);
+#endif
+                /* received number bytes */
+                if (td) {
+                        count += AMD_GETBITS(td->dmastat, UDC_DMA_OUT_STS_RXBYTES);
+                }
+        }
+
+        return count;
+
+}
+
+/**
+ * Creates or re-inits a DMA chain 
+ *
+ * \param ep            pointer to endpoint struct 
+ * \param req           pointer to request packet
+ * \param buf_len       number of buffer bytes per descriptor (except last short)
+ */
+static void udc_create_dma_chain(struct udc_ep* ep, struct udc_request* req, unsigned long buf_len, int gfp_flags)
+{
+        unsigned long bytes = req->req.length;
+        unsigned int i;
+        dma_addr_t dma_addr;
+        struct udc_data_dma     *td = NULL;
+        struct udc_data_dma     *last = NULL;
+        unsigned long txbytes;
+        unsigned create_new_chain = 0;
+        unsigned len;
+#ifdef UDC_IPDEFECT_9000004946_WORKAROUND
+        dma_addr_t last_next = DMA_ADDR_INVALID;
+#endif
+
+        VDBG("udc_create_dma_chain: bytes=%ld buf_len=%ld\n", bytes, buf_len);
+        dma_addr = DMA_ADDR_INVALID;
+
+        /* unset L bit in first desc for OUT */
+        if (!ep->is_in) {
+                req->td_data->dmastat &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L);
+        }
+        
+        /* alloc only new desc's if not already available */
+        len = req->req.length / ep->ep.maxpacket;
+        if (req->req.length % ep->ep.maxpacket) {
+                len++;
+        }
+
+        if (len > req->chain_len) {
+                /* shorter chain already allocated before */
+                if (req->chain_len > 1) {
+                        udc_free_dma_chain(ep->dev, req);
+                }
+                req->chain_len = len;
+                create_new_chain = 1;
+        }
+
+#ifdef UDC_IPDEFECT_9000004946_WORKAROUND
+        /* re-link broken chain  */
+        if (req->td_data_last) {
+                req->td_data_last->next = req->td_data_last_next;
+        }
+        /* if only one td then last_next is root td */
+        last_next = req->td_dma;
+#endif
+
+        td = req->td_data; 
+        /* gen. required number of descriptors and buffers */
+        for (i = buf_len; i < bytes; i += buf_len)
+        {
+                /* create or determine next desc. */
+                if (create_new_chain) {
+
+#if defined(CONFIG_MIPS)
+                        gfp_flags = GFP_ATOMIC | GFP_DMA;
+#endif
+                        td = pci_pool_alloc (ep->dev->data_requests, gfp_flags,
+                                             &dma_addr);
+
+                        td->dmastat = 0;
+                }
+                else if (i == buf_len)
+                {
+                        /* first td */
+#if defined (CONFIG_MIPS)
+                        td = (struct udc_data_dma*) UNCAC_ADDR(phys_to_virt(req->td_data->next));
+#else
+                        td = (struct udc_data_dma*) phys_to_virt(req->td_data->next);
+#endif
+                        td->dmastat = 0;
+                }
+                else {
+#if defined (CONFIG_MIPS)
+                        td = (struct udc_data_dma*) UNCAC_ADDR(phys_to_virt(last->next));
+#else
+                        td = (struct udc_data_dma*) phys_to_virt(last->next);
+#endif
+                        td->dmastat = 0;
+                }
+                        
+
+                if (td) {
+                        /* assign buffer */
+                       td->bufptr = req->req.dma + i; 
+                }
+                else {
+                        break;
+                }
+
+                /* short packet ? */
+                if ((bytes - i) >= buf_len) {
+                        txbytes = buf_len;
+                }
+                else {
+                        /* short packet */
+                        txbytes = bytes - i;
+                }
+         
+                /* link td and assign tx bytes */
+                if (i == buf_len) {
+                        if (create_new_chain) {
+                                req->td_data->next = dma_addr;
+                        }
+                        else {
+                                //req->td_data->next = virt_to_phys(td);
+                        }
+                        /* write tx bytes */
+                        if (ep->is_in) {
+                                /* first desc */
+                                req->td_data->dmastat = AMD_ADDBITS(req->td_data->dmastat,
+                                                                    ep->ep.maxpacket,
+                                                                    UDC_DMA_IN_STS_TXBYTES);
+                                /* second desc */
+                                td->dmastat = AMD_ADDBITS(td->dmastat,
+                                                          txbytes,
+                                                          UDC_DMA_IN_STS_TXBYTES); 
+                        } 
+
+#ifdef UDC_IPDEFECT_9000004946_WORKAROUND
+                        last_next = req->td_data->next;
+#endif
+                }
+                else {
+                        if (create_new_chain) {
+                                last->next = dma_addr;
+                        }
+                        else { 
+                                //last->next = virt_to_phys(td);
+                        }
+                        if (ep->is_in) {
+                                /* write tx bytes */
+                                td->dmastat = AMD_ADDBITS(td->dmastat,
+                                                          txbytes,
+                                                          UDC_DMA_IN_STS_TXBYTES); 
+                        }
+#ifdef UDC_IPDEFECT_9000004946_WORKAROUND
+                        last_next = last->next;
+#endif
+                }
+                last = td;
+        }
+        /* set last bit */
+        if (td) {
+                td->dmastat |= AMD_BIT(UDC_DMA_IN_STS_L);
+                /* last desc. points to itself */
+#ifdef UDC_IPDEFECT_9000004946_WORKAROUND
+                /* remember broken chain next pointer */
+                req->td_data_last_next = td->next;
+                /* point to itself */
+                td->next = last_next;
+#endif
+                req->td_data_last = td;
+        }
+}
+
+/**
+ * Queues a request packet, called by gadget driver
+ * 
+ * \param _ep           pointer to usb ep struct
+ * \param _req          pointer to request packet to be freed
+ * \param gfp_flags     flags for alloc
+ * \return 0 if success
+ */
+static int
+udc_queue (struct usb_ep *_ep, struct usb_request *_req, int gfp_flags)
+{
+        struct udc_request      *req;
+        struct udc_ep   *ep;
+        struct udc              *dev;
+        unsigned long           flags;
+        u32 tmp;
+        
+
+        VDBG ("udc_queue()\n");
+
+        /* check the inputs */
+        req = container_of (_req, struct udc_request, req);
+        VDBG("!req=%d !compl=%d !buf=%d !empty_list=%d \n",
+                !_req, !_req->complete, !_req->buf, !list_empty(&req->queue));
+
+        if (!_req || !_req->complete || !_req->buf
+                        || !list_empty (&req->queue))
+                return -EINVAL;
+
+        ep = container_of (_ep, struct udc_ep, ep);
+        if (!_ep || (!ep->desc && ep->num != 0))
+                return -EINVAL;
+
+        VDBG("udc_queue(): ep%d-in=%d\n", ep->num, ep->is_in);
+        dev = ep->dev;
+        if (!dev->driver || dev->gadget.speed == USB_SPEED_UNKNOWN)
+                return -ESHUTDOWN;
+
+        /* set up dma mapping in case the caller didn't */
+        if (ep->dma && _req->dma == DMA_ADDR_INVALID) {
+                VDBG("DMA unmapped: mapping req %lx\n", (unsigned long) req);
+                _req->dma = pci_map_single (dev->pdev, _req->buf, _req->length,
+                        ep->is_in ? PCI_DMA_TODEVICE : PCI_DMA_FROMDEVICE);
+                req->mapped = 1;
+        }
+
+        VDBG( "%s queue req %p, len %d req->td_data=%lx buf %p\n",
+                        _ep->name, _req, _req->length, (unsigned long) req->td_data, _req->buf);
+
+        spin_lock_irqsave (&dev->lock, flags);
+
+        _req->status = -EINPROGRESS;
+        _req->actual = 0;
+        req->dma_done = 0;
+
+        /* kickstart this i/o queue? */
+        if (list_empty (&ep->queue) && !ep->stopped) {
+                /* maybe there's no control data, just status ack */
+                if (ep->is_in && _req->length == 0) {
+                        //allow_status (ep);
+                        done (ep, req, 0);
+                        VDBG( "%s status ack\n", ep->ep.name);
+                        goto done;
+                }
+                /* use DMA if the endpoint supports it, else pio */
+                if (ep->dma) {
+
+                        prep_dma(ep, req);
+                        /* write desc pointer to enable DMA */
+                        if (ep->is_in) {
+                                /* set HOST READY */
+                                req->td_data->dmastat 
+                                      = AMD_ADDBITS(req->td_data->dmastat,
+                                                    UDC_DMA_IN_STS_BS_HOST_READY, 
+                                                    UDC_DMA_IN_STS_BS);
+                        }
+
+                        /* write desc pointer */
+                        writel(req->td_dma, &ep->regs->desptr);
+                        /* clear NAK by writing CNAK */
+                        if (ep->naking) {
+                                tmp = readl(&ep->regs->ctl);
+                                tmp |= AMD_BIT(UDC_EPCTL_CNAK); 
+                                writel(tmp, &ep->regs->ctl);
+                                ep->naking = 0;
+                                UDC_QUEUE_CNAK(ep, ep->num);
+                        }
+                
+                        if (!ep->is_in) {
+                                /* enable DMA */
+                                /* set RDE */
+                                tmp = readl(&dev->regs->ctl);
+                                tmp |= AMD_BIT(UDC_DEVCTL_RDE);
+                                writel(tmp, &dev->regs->ctl);                
+                                tmp = readl(&dev->regs->ctl);
+                        }
+#ifdef UDC_DISABLE_IRQ_IF_EMPTY_IN_QUEUE
+                        else {
+                                /* enable ep irq */
+                                tmp = readl(&dev->regs->ep_irqmsk);
+                                tmp &= AMD_UNMASK_BIT(ep->num);
+                                writel(tmp, &dev->regs->ep_irqmsk);        
+                        }
+#endif
+                }
+                else {
+                        /* maybe there's no control data, just status ack */
+                        /*if (ep->num == 0 && _req->length == 0) {
+                                allow_status (ep);
+                                done (ep, req, 0);
+                                VDBG( "%s status ack\n", ep->ep.name);
+                                goto done;
+                        }*/
+                }
+
+        } else if (ep->dma) {
+
+                /* TODO use prep_dma also for OUT ep's, this is not possible
+                   for PPB modes currently, because of chain creation reasons */
+                if (ep->is_in) {
+                        prep_dma(ep, req);
+                }
+
+        } /* else the irq handler advances the queue. */
+        VDBG("list_add\n");
+        if (req) {
+        
+                list_add_tail (&req->queue, &ep->queue);
+
+                /* stop OUT naking */
+                if (!ep->is_in) {
+                        if (!use_dma && udc_rxfifo_pending) {
+                                DBG("udc_queue(): pending bytes in rxfifo after nyet\n");
+                                /* read pending bytes afer nyet: referring to isr */
+                                if (read_fifo(ep, req)) {
+                                        /* finish */
+                                        done (ep, req, 0);
+                                }
+                                udc_rxfifo_pending = 0;
+
+                        }
+                }
+
+        }
+
+
+done:
+        spin_unlock_irqrestore (&dev->lock, flags);
+        return 0;
+}
+
+/**
+ * Completes a request packet in DMA context 
+ * 
+ * \param ep            pointer to ep struct
+ * \param req           pointer to request packet
+ * \param status        request packet status
+ */
+static inline void
+dma_done (
+        struct udc_ep *ep,
+        struct udc_request *req,
+        int status
+)
+{
+        rmb ();
+        done (ep, req, status);
+}
+
+/**
+ * Scan DMA completions 
+ * 
+ * \param ep            pointer to ep struct
+ */
+#ifdef NOT_USED
+static void scan_dma_completions (struct udc_ep *ep)
+{
+        while (!list_empty (&ep->queue)) {
+                struct udc_request      *req;
+
+                req = list_entry (ep->queue.next,
+                                struct udc_request, queue);
+
+                if (!ep->is_in && (req->req.length % ep->ep.maxpacket) != 0) {
+                        req->dma_done = 1;
+                        break;
+                }
+                dma_done (ep, req, 0);
+        }
+}
+#endif
+
+/**
+ * Abort current endpoint DMA 
+ * 
+ * \param ep            pointer to ep struct
+ */
+static inline void abort_dma (struct udc_ep *ep)
+{
+        /* abort the current transfer */
+
+        /* collect completed transfers (except the current one), */
+        /* but we have no real DMA chains, only for max packets */
+        /* for PPB modes */
+        //scan_dma_completions (ep);
+}
+
+/**
+ * Destroy current endpoint DMA 
+ * 
+ * \param ep            pointer to ep struct
+ */
+static void nuke (struct udc_ep *ep)
+{
+        struct udc_request      *req;
+        /* called with spinlock held */
+
+        ep->stopped = 1;
+        if (ep->dma)
+                abort_dma (ep);
+
+        while (!list_empty (&ep->queue)) {
+                req = list_entry (ep->queue.next,
+                                struct udc_request,
+                                queue);
+                done (ep, req, -ESHUTDOWN);
+        }
+}
+
+/**
+ * Dequeues a request packet, called by gadget driver
+ * 
+ * \param _ep           pointer to usb ep struct
+ * \param _req          pointer to request packet to be freed
+ * \return 0 if success
+ */
+static int udc_dequeue (struct usb_ep *_ep, struct usb_request *_req)
+{
+        struct udc_ep   *ep;
+        struct udc_request      *req;
+        unsigned long           flags;
+        int                     stopped;
+
+        ep = container_of (_ep, struct udc_ep, ep);
+        req = container_of (_req, struct udc_request, req);
+        if (!_ep || (!ep->desc && ep->num != 0) || !_req)
+                return -EINVAL;
+
+        spin_lock_irqsave (&ep->dev->lock, flags);
+        stopped = ep->stopped;
+
+        /* pause dma while we scan the queue */
+        ep->stopped = 1;
+        if (ep->dma) {
+                /* disable dma */
+                /* force synch, clean any completed requests */
+                //spin_stop_dma (ep->dma);
+                /* collect completed transfers (except the current one), */
+                /* but we have no real DMA chains, only for max packets */
+                /* for PPB modes */
+                //scan_dma_completions (ep);
+        }
+
+        /* queue head may be partially complete. */
+        if (ep->queue.next == &req->queue) {
+                if (ep->dma) {
+                        DBG( "unlink (%s) dma\n", _ep->name);
+                        _req->status = -ECONNRESET;
+                        abort_dma (ep);
+                        if (likely (ep->queue.next == &req->queue))
+                                dma_done (ep, req,
+                                        -ECONNRESET);
+                } else {
+                        DBG( "unlink (%s) pio\n", _ep->name);
+                        done (ep, req, -ECONNRESET);
+                }
+                req = 0;
+
+        }
+
+        if (req)
+                done (ep, req, -ECONNRESET);
+        ep->stopped = stopped;
+
+        if (ep->dma) {
+                /* turn off dma on inactive queues */
+                if (list_empty (&ep->queue)) {
+                        //stop_dma (ep->dma);
+                }
+                else if (!ep->stopped) {
+                        /* resume current request, or start new one */
+                }
+        }
+
+        spin_unlock_irqrestore (&ep->dev->lock, flags);
+        return req ? 0 : -EOPNOTSUPP;
+}
+
+/**
+ * Halt endpoint 
+ * 
+ * \param ep            pointer to ep struct
+ */
+static void set_halt (struct udc_ep *ep)
+{
+        u32 tmp;
+        struct udc_request* req=NULL;
+        //unsigned dma_pend = 0;
+        ep->stopped = 1;
+        /* check for pending DMA */
+        if (!list_empty (&ep->queue)) {
+                       req = list_entry (ep->queue.next,
+                                        struct udc_request, queue);
+                       if (req) {
+                                /* TODO spin lock ? */
+                                //if (req->dma_pend) {
+                                        /* ep is stopped by ISR after completion of last req */
+                                        //dma_pend = 1;
+                                //}
+                        }
+        }
+        if (!req) {
+                /* set STALL */
+                /* flush the txfifo */
+                tmp = readl(&ep->regs->ctl);
+                tmp |= AMD_BIT(UDC_EPCTL_F);
+                writel(tmp, &ep->regs->ctl);
+
+                tmp = readl(&ep->regs->ctl);
+                tmp |= AMD_BIT(UDC_EPCTL_S);
+                writel(tmp, &ep->regs->ctl);
+                ep->stopped = 0;
+        }
+
+}
+
+/**
+ * clear halt of endpoint 
+ * 
+ * \param ep            pointer to ep struct
+ * \return 0 if success 
+ */
+static int clear_halt (struct udc_ep *ep)
+{
+        u32 tmp;
+        int retval = 0;
+
+        /* ep can be stopped by set_halt() before */
+        if (ep->stopped) {
+                tmp = readl(&ep->regs->ctl);
+                DBG("check STALL for ep%d\n",ep->num);
+                /* STALL cleared ? */
+                if (!(tmp & AMD_BIT(UDC_EPCTL_S))) {
+                        DBG("clear NAK after STALL\n");
+                        /* clear NAK by writing CNAK */
+                        tmp = readl(&ep->regs->ctl);
+                        tmp |= AMD_BIT(UDC_EPCTL_CNAK); 
+                        writel(tmp, &ep->regs->ctl);
+                        ep->stopped = 0;
+                        UDC_QUEUE_CNAK(ep, ep->num);
+                }
+                else {
+                        /* Host did not sent CLEAR_FEATURE HALT yet */
+                        retval = -EINVAL;
+                }
+        }
+
+        return retval;
+}
+
+/**
+ * Halt or clear halt of endpoint 
+ * 
+ * \param ep            pointer to ep struct
+ * \param value         1 - halt, 0 - clear halt
+ * \return 0 if success 
+ */
+static int
+udc_set_halt (struct usb_ep *_ep, int value)
+{
+        struct udc_ep   *ep;
+        int retval = 0;
+
+        ep = container_of (_ep, struct udc_ep, ep);
+        if (!_ep || (!ep->desc && ep->num != 0))
+                return -EINVAL;
+        if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+                return -ESHUTDOWN;
+        if (ep->desc /* not ep0 */ && 
+            (ep->desc->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK)
+                        == USB_ENDPOINT_XFER_ISOC)
+                return -EINVAL;
+
+        DBG( "%s %s halt\n", _ep->name, value ? "set" : "clear");
+
+        /* set/clear, then synch memory views with the device */
+        if (value) {
+                if (ep->num == 0)
+                        ep->dev->protocol_stall = 1;
+                else
+                        set_halt (ep);
+        } else
+                retval = clear_halt (ep);
+
+        return retval;
+}
+
+/**
+ * Return fifo fill state (not used, not implemented)
+ * 
+ * \param _ep            pointer to usb ep struct
+ * \return available bytes in fifo 
+ */
+static int
+udc_fifo_status (struct usb_ep *_ep)
+{
+        struct udc_ep   *ep;
+
+        ep = container_of (_ep, struct udc_ep, ep);
+        if (!_ep || (!ep->desc && ep->num != 0))
+                return -ENODEV;
+        if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+                return -ESHUTDOWN;
+
+        return 0;
+}
+
+/**
+ * Flush the endpoint fifo (not implemented)
+ * 
+ * \param _ep            pointer to usb ep struct
+ */
+static void
+udc_fifo_flush (struct usb_ep *_ep)
+{
+        struct udc_ep   *ep;
+
+        ep = container_of (_ep, struct udc_ep, ep);
+        if (!_ep || (!ep->desc && ep->num != 0))
+                return;
+        if (!ep->dev->driver || ep->dev->gadget.speed == USB_SPEED_UNKNOWN)
+                return;
+}
+
+/* gadget interface */
+static struct usb_ep_ops udc_ep_ops = {
+        .enable         = udc_enable,
+        .disable        = udc_disable,
+
+        .alloc_request  = udc_alloc_request,
+        .free_request   = udc_free_request,
+
+        .alloc_buffer   = udc_alloc_buffer,
+        .free_buffer    = udc_free_buffer,
+
+        .queue          = udc_queue,
+        .dequeue        = udc_dequeue,
+
+        .set_halt       = udc_set_halt,
+        .fifo_status    = udc_fifo_status,
+        .fifo_flush     = udc_fifo_flush,
+};
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * Get frame count fifo (not implemented)
+ * 
+ * \param _gadget            pointer to gadget struct
+ * \return frame count
+ */
+static int udc_get_frame (struct usb_gadget *_gadget)
+{
+        struct udc              *dev;
+        unsigned long           flags;
+        u16                     retval=0;
+
+        if (!_gadget)
+                return -ENODEV;
+        dev = container_of (_gadget, struct udc, gadget);
+        spin_lock_irqsave (&dev->lock, flags);
+        // get frame
+        spin_unlock_irqrestore (&dev->lock, flags);
+        return retval;
+}
+
+/**
+ * Resume UDC (not implemented)
+ * 
+ * \param _gadget            pointer to gadget struct
+ * \return 0 if success
+ */
+static int udc_wakeup (struct usb_gadget *_gadget)
+{
+        struct udc              *dev;
+
+        if (!_gadget)
+                return 0;
+        dev = container_of (_gadget, struct udc, gadget);
+        // resume
+
+        /* pci writes may still be posted */
+        return 0;
+}
+
+/**
+ * gadget ioctl, used for OTG support notification
+ * 
+ * \param _gadget            pointer to gadget struct
+ * \return 1 if OTG supported, else 0
+ */
+static int udc_gadget_ioctl (struct usb_gadget *_gadget, unsigned code, unsigned long param)
+{
+        struct udc              *dev;
+        unsigned long           flags;
+        u16                     retval=0;
+        u32 tmp;
+
+        if (!_gadget)
+                return -ENODEV;
+        dev = container_of (_gadget, struct udc, gadget);
+        spin_lock_irqsave (&dev->lock, flags);
+        tmp = readl(&dev->regs->cfg);
+        
+        if (tmp & AMD_BIT(UDC_DEVCFG_HNPSFEN))
+                retval = 1;
+        else
+                retval = 0;
+        spin_unlock_irqrestore (&dev->lock, flags);
+        return retval;
+}
+
+/* gadget operations */
+static const struct usb_gadget_ops udc_ops = {
+        .get_frame      = udc_get_frame,
+        .wakeup         = udc_wakeup,
+        .ioctl          = udc_gadget_ioctl, 
+        // .set_selfpowered = udc_set_selfpowered,
+};
+
+/*-------------------------------------------------------------------------*/
+
+#undef USE_SYSFS_DEBUG_FILES
+#ifdef  USE_SYSFS_DEBUG_FILES
+
+/* "function" sysfs attribute */
+/**
+ *  Temporary left in the code for later reusing or removing
+ *  dequeue ALL requests
+ */
+static ssize_t
+show_function (struct device *_dev, char *buf)
+{
+        struct udc      *dev = dev_get_drvdata (_dev);
+
+        if (!dev->driver
+                        || !dev->driver->function
+                        || strlen (dev->driver->function) > PAGE_SIZE)
+                return 0;
+        return snprintf (buf, PAGE_SIZE, "%s\n", dev->driver->function);
+}
+static DEVICE_ATTR (function, S_IRUGO, show_function, NULL);
+
+/**
+ *  Temporary left in the code for later reusing or removing
+ *  dequeue ALL requests
+ */
+static ssize_t
+show_registers (struct device *_dev, char *buf)
+{
+        return 0;
+}
+static DEVICE_ATTR (registers, S_IRUGO, show_registers, NULL);
+
+/**
+ *  Temporary left in the code for later reusing or removing
+ *  dequeue ALL requests
+ */
+static ssize_t
+show_queues (struct device *_dev, char *buf)
+{
+        struct udc              *dev;
+        unsigned long           flags;
+
+        dev = dev_get_drvdata (_dev);
+        spin_lock_irqsave (&dev->lock, flags);
+        spin_unlock_irqrestore (&dev->lock, flags);
+        return 0;
+}
+static DEVICE_ATTR (queues, S_IRUGO, show_queues, NULL);
+
+
+#else
+
+#define device_create_file(a,b) do {} while (0)
+#define device_remove_file      device_create_file
+
+#endif
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * Setups endpoint parameters, adds endpoints to linked list
+ * 
+ * \param dev           pointer to dev struct
+ * \param mode          not used
+ */
+static void set_fifo_mode (struct udc *dev, int mode)
+{
+        /* make list */
+        INIT_LIST_HEAD (&dev->gadget.ep_list);
+        list_add_tail (&dev->ep [UDC_EPIN_STATUS_IX].ep.ep_list, &dev->gadget.ep_list);
+        list_add_tail (&dev->ep [UDC_EPIN_IX].ep.ep_list, &dev->gadget.ep_list);
+        list_add_tail (&dev->ep [UDC_EPOUT_IX].ep.ep_list, &dev->gadget.ep_list);
+        
+        /* setup fifo size */
+        dev->ep [UDC_EPIN_STATUS_IX].fifo_size = UDC_EPIN_SMALLINT_BUFF_SIZE;
+        if (dev->gadget.speed == USB_SPEED_FULL)
+                dev->ep [UDC_EPIN_IX].fifo_size = UDC_FS_EPIN_BUFF_SIZE; 
+        else if (dev->gadget.speed == USB_SPEED_HIGH)
+                dev->ep [UDC_EPIN_IX].fifo_size = hs_tx_buf; 
+        dev->ep [UDC_EPOUT_IX].fifo_size = UDC_RXFIFO_SIZE;
+}
+
+/**
+ * Sets fifo mode, called by gadget driver
+ * 
+ * \param gadget        pointer to gadget struct
+ * \param mode          not used
+ * \return 0 if success 
+ */
+int amd5536udc_set_fifo_mode (struct usb_gadget *gadget, int mode)
+{
+        struct udc              *dev;
+        unsigned long           flags;
+
+        if (!gadget)
+                return -ENODEV;
+        dev = container_of (gadget, struct udc, gadget);
+
+        spin_lock_irqsave (&dev->lock, flags);
+        set_fifo_mode (dev, 0);
+        spin_unlock_irqrestore (&dev->lock, flags);
+
+        return 0;
+}
+EXPORT_SYMBOL (amd5536udc_set_fifo_mode);
+
+/**
+ * \brief
+ * init registers at driver load time 
+ *
+ * \param dev        pointer to device struc
+ * \return 0 if success
+ */
+static int startup_registers(struct udc* dev)
+{
+        u32 tmp;
+
+        /* init controller by soft reset */
+        udc_soft_reset(dev);
+
+        /* mask not needed interrupts */
+        udc_mask_unused_interrupts(dev);
+
+        /* put into initial config */
+        usb_reset (dev);
+        /* link up all endpoints */
+        usb_reinit (dev);
+       
+        /* program speed */
+        tmp = readl(&dev->regs->cfg);
+        if (use_fullspeed) {
+                tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_FS, UDC_DEVCFG_SPD);
+        }
+        else {
+                tmp = AMD_ADDBITS(tmp, UDC_DEVCFG_SPD_HS, UDC_DEVCFG_SPD);
+        }
+        writel(tmp, &dev->regs->cfg);
+
+        return 0;
+}
+
+/**
+ * Resets usb context
+ * 
+ * \param dev           pointer to device struct
+ */
+static void usb_reset (struct udc *dev)
+{
+        u32     tmp;
+
+        DBG("usb_reset()\n");        
+        
+        /* force immediate bus disconnect, and synch through pci */
+        // disconnect
+        dev->gadget.speed = USB_SPEED_UNKNOWN;
+
+        /* disable DMA */
+        tmp = readl(&dev->regs->ctl);
+        tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_RDE);
+        tmp &= AMD_UNMASK_BIT(UDC_DEVCTL_TDE);
+        writel(tmp, &dev->regs->ctl);                
+
+        /* enable dynamic CSR programming */
+        tmp =  readl(&dev->regs->cfg);
+        tmp |= AMD_BIT(UDC_DEVCFG_CSR_PRG);
+        writel(tmp, &dev->regs->cfg);                     
+
+        /* set self powered */
+        tmp =  readl(&dev->regs->cfg);
+        tmp |= AMD_BIT(UDC_DEVCFG_SP);
+        writel(tmp, &dev->regs->cfg);                     
+
+        /* clear old dma and irq state */
+        // clear old dma
+
+        /* standard fifo and endpoint allocations */
+        set_fifo_mode (dev, 0);
+}
+
+/**
+ * Inits usb context, sets initial endpoint parameters
+ * 
+ * \param dev           pointer to device struct
+ */
+static void usb_reinit (struct udc *dev)
+{
+        u32     tmp;
+        u32     reg;
+
+        DBG("usb_reinit()\n");
+
+        /* read enum speed */
+        tmp = readl(&dev->regs->sts);
+        tmp = AMD_GETBITS(tmp, UDC_DEVSTS_ENUM_SPEED);        
+        if (tmp ==  UDC_DEVSTS_ENUM_SPEED_HIGH) {
+                dev->gadget.speed = USB_SPEED_HIGH;
+        }
+        else if (tmp ==  UDC_DEVSTS_ENUM_SPEED_FULL) {
+                dev->gadget.speed = USB_SPEED_FULL;
+        }
+        
+        /* basic endpoint init */
+        for (tmp = 0; tmp < UDC_EP_NUM; tmp++) {
+                struct udc_ep   *ep = &dev->ep [tmp];
+
+                ep->ep.name = ep_name [tmp];
+                ep->dev = dev;
+                ep->num = tmp;
+                /* txfifo size is calculated at enable time */
+                ep->txfifo = dev->txfifo;
+
+                /* fifo size */
+                if (tmp < UDC_EPIN_NUM) {
+                        ep->fifo_size = UDC_TXFIFO_SIZE;
+                        ep->is_in = 1;
+                }
+                else {
+                        ep->fifo_size = UDC_RXFIFO_SIZE;
+                        ep->is_in = 0;
+
+                }
+
+                ep->regs = &dev->epregs [tmp];
+                /* TODO ep will be reset only if ep was not enabled before to avoid
+                        disabling ep interrupts when ENUM interrupt occurs but ep is
+                        not enabled by gadget driver 
+                        => to be improved for general ep_reset() */
+                if (!ep->desc) {
+                        ep_reset (dev->regs, ep);
+                }
+
+                if (use_dma) {
+                        /* TODO ep->dma is not really used, just to indicate that */
+                        /* DMA is active: remove this */
+                        /* dma regs = dev control regs */        
+                        ep->dma = (u32*) &dev->regs->ctl;       
+
+                        /* nak OUT endpoints until enable - not for ep0*/
+                        if (tmp != UDC_EP0IN_IX
+                            && tmp != UDC_EP0OUT_IX
+                            && tmp > UDC_EPIN_NUM) {
+                                /* set NAK  */
+                                reg = readl(&dev->ep[tmp].regs->ctl);
+                                reg |= AMD_BIT(UDC_EPCTL_SNAK); 
+                                writel(reg, &dev->ep[tmp].regs->ctl);
+                                dev->ep[tmp].naking = 1;
+                                       
+                        }
+                }
+        }
+        /* EP0 max packet */
+        if (dev->gadget.speed == USB_SPEED_FULL) {
+                dev->ep [UDC_EP0IN_IX].ep.maxpacket = UDC_FS_EP0IN_MAX_PKT_SIZE;
+                dev->ep [UDC_EP0OUT_IX].ep.maxpacket = UDC_FS_EP0OUT_MAX_PKT_SIZE;
+        }
+        else if (dev->gadget.speed == USB_SPEED_HIGH) {
+                dev->ep [UDC_EP0IN_IX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE;
+                dev->ep [UDC_EP0OUT_IX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE;
+        }
+
+        /* with suspend bug workaround, ep0 params for gadget driver
+        are set at gadget driver bind() call */
+        dev->gadget.ep0 = &dev->ep [UDC_EP0IN_IX].ep;
+        dev->ep [UDC_EP0IN_IX].stopped = 0;
+        INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
+
+        /* init cfg/alt/int */
+        dev->cur_config = 0;
+        dev->cur_intf = 0;
+        dev->cur_alt = 0;
+}
+
+/**
+ * Bringup after Connect event,
+ * initial bringup to be ready for ep0 events 
+ * 
+ * \param dev           pointer to device struct
+ */
+static void usb_connect (struct udc *dev) {
+
+                INFO("USB Connect\n");
+
+                /* put into initial config */
+                usb_reset (dev);
+
+                /* enable device setup interrupts */
+                udc_enable_dev_setup_interrupts(dev);
+}
+
+/**
+ * Calls gadget with disconnect event and resets the UDC and makes
+ * initial bringup to be ready for ep0 events 
+ * 
+ * \param dev           pointer to device struct
+ */
+static void usb_disconnect (struct udc *dev) {
+
+                INFO("USB Disconnect\n");
+                /* mask interrupts */
+                udc_mask_unused_interrupts(dev);
+
+                tasklet_schedule(&disconnect_tasklet);
+}
+
+/**
+ * Tasklet for disconnect to be outside of interrupt 
+ * context 
+ * 
+ * \param par   pointer to device struct pointer
+ */
+void udc_tasklet_disconnect(unsigned long par)
+{
+        struct udc* dev = (struct udc*) (*((struct udc**) par));
+
+        DBG("Tasklet disconnect\n");
+
+        /* call gadget to reset configs etc. */
+        if (spin_is_locked(&dev->lock)) {
+                spin_unlock(&dev->lock);
+                dev->driver->disconnect (&dev->gadget);
+                spin_lock(&dev->lock);
+        }
+        else
+                dev->driver->disconnect (&dev->gadget);
+
+        /* disable ep0 to empty req queue */
+        nuke (&dev->ep [UDC_EP0IN_IX]);
+        ep_reset (dev->regs,
+                        &dev->ep [UDC_EP0IN_IX]);
+
+        if (!soft_reset_occured) {
+                /* init controller by soft reset */
+#ifdef UDC_IPCASE_8000018724_WORKAROUND
+                udc_soft_reset(dev);
+#endif
+                soft_reset_occured++;
+
+#ifndef UDC_IPBUG_3950_WORKAROUND
+                /* re-enable dev interrupts */
+                udc_enable_dev_setup_interrupts(dev);
+#endif
+
+                /* TODO for AU1200: disable UDC memory */
+        }
+        
+}
+
+/**
+ * Reset the UDC core 
+ * 
+ * \param dev           pointer to device struct
+ */
+static void udc_soft_reset(struct udc* dev)
+{
+        DBG("Soft reset\n");
+        /* reset possible waiting interrupts, because int.
+           status is lost after soft reset */
+        /* ep int. status reset */ 
+        writel(UDC_EPINT_MSK_DISABLE_ALL, &dev->regs->ep_irqsts);
+        /* device int. status reset */ 
+        writel(UDC_DEV_MSK_DISABLE, &dev->regs->irqsts);
+
+        /* FIXME: reset is the only way to restart DMA now,
+           later if the hw bug is fixed, use a softer way */
+        spin_lock_irq(&udc_irq_spinlock);
+        writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
+        readl(&dev->regs->cfg);
+        spin_unlock_irq(&udc_irq_spinlock);
+        
+}
+
+#if defined(UDC_IPBUG_3950_WORKAROUND) \
+    || defined(UDC_IPCASE_8000018724_WORKAROUND)
+/**
+ * Called by OTG driver to notify us regarding an OTG event
+ * 
+ * \param code           notify code
+ */
+int otg_notify(int code) {
+        VDBG("OTG notify code=%d\n", code);
+        switch (code) {
+                case OTG_GADGET_EVT_SVDROP:
+                        /* disconnect event */
+                        usb_disconnect(the_controller);
+                        break;
+                case OTG_GADGET_EVT_SVALID:
+                        /* connect event */
+                        usb_connect(the_controller);
+                        break;
+        }  
+
+        return 0;
+}
+#endif
+
+/**
+ * Inits endpoint 0 so that SETUP packets are processed
+ * 
+ * \param dev           pointer to device struct
+ */
+static void ep0_start (struct udc *dev)
+{
+        u32 tmp;
+
+        DBG("ep0_start\n");
+
+        /* flush fifo */
+        tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+        tmp |= AMD_BIT(UDC_EPCTL_F); 
+        writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+
+        /* set ep0 directions */
+        dev->ep[UDC_EP0IN_IX].is_in = 1;
+        dev->ep[UDC_EP0OUT_IX].is_in = 0;
+
+        /* set buffer size (tx fifo entries) of EP0_IN */
+        tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufin_framenum);
+        if (dev->gadget.speed == USB_SPEED_FULL)
+                tmp = AMD_ADDBITS(tmp, UDC_FS_EPIN0_BUFF_SIZE, UDC_EPIN_BUFF_SIZE);
+        else if (dev->gadget.speed == USB_SPEED_HIGH)
+                tmp = AMD_ADDBITS(tmp, UDC_EPIN0_BUFF_SIZE, UDC_EPIN_BUFF_SIZE);
+        writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufin_framenum);
+        
+        /* set max packet size of EP0_IN */
+        tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt);
+        if (dev->gadget.speed == USB_SPEED_FULL)
+                tmp = AMD_ADDBITS(tmp, UDC_FS_EP0IN_MAX_PKT_SIZE, UDC_EP_MAX_PKT_SIZE);
+        else if (dev->gadget.speed == USB_SPEED_HIGH)
+                tmp = AMD_ADDBITS(tmp, UDC_EP0IN_MAX_PKT_SIZE, UDC_EP_MAX_PKT_SIZE);
+        writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->bufout_maxpkt);
+
+        /* set max packet size of EP0_OUT */
+        tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt);
+        if (dev->gadget.speed == USB_SPEED_FULL)
+                tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE, UDC_EP_MAX_PKT_SIZE);
+        else if (dev->gadget.speed == USB_SPEED_HIGH)
+                tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE, UDC_EP_MAX_PKT_SIZE);
+        writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->bufout_maxpkt);
+
+        /* set max packet size of EP0 in UDC CSR  */
+        tmp = readl(&dev->csr->ne[0]);
+        if (dev->gadget.speed == USB_SPEED_FULL)
+                tmp = AMD_ADDBITS(tmp, UDC_FS_EP0OUT_MAX_PKT_SIZE, UDC_CSR_NE_MAX_PKT);
+        else if (dev->gadget.speed == USB_SPEED_HIGH)
+                tmp = AMD_ADDBITS(tmp, UDC_EP0OUT_MAX_PKT_SIZE, UDC_CSR_NE_MAX_PKT);
+        writel(tmp, &dev->csr->ne[0]);
+
+        if (use_dma) {
+                dev->ep [UDC_EP0OUT_IX].td->dmastat |= AMD_BIT(UDC_DMA_OUT_STS_L);
+                /* write dma desc address */
+                writel(dev->ep [UDC_EP0OUT_IX].td_stp_dma, &dev->ep[UDC_EP0OUT_IX].regs->subptr);
+                writel(dev->ep [UDC_EP0OUT_IX].td_dma, &dev->ep[UDC_EP0OUT_IX].regs->desptr);
+                /* enable DMA */
+                tmp = readl(&dev->regs->ctl);
+                tmp |= AMD_BIT(UDC_DEVCTL_MODE)
+                                | AMD_BIT(UDC_DEVCTL_RDE)
+                                | AMD_BIT(UDC_DEVCTL_TDE);
+                if (use_dma_bufferfill_mode) {
+                        tmp |= AMD_BIT(UDC_DEVCTL_BF);
+                } 
+                else if (use_dma_ppb_du) {
+                        tmp |= AMD_BIT(UDC_DEVCTL_DU);
+                }
+                writel(tmp, &dev->regs->ctl);                
+        }
+
+        /* clear NAK by writing CNAK for EP0IN */
+        tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+        tmp |= AMD_BIT(UDC_EPCTL_CNAK); 
+        writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+        dev->ep[UDC_EP0IN_IX].naking = 0;
+        UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX);
+        
+        /* clear NAK by writing CNAK for EP0OUT */
+        tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
+        tmp |= AMD_BIT(UDC_EPCTL_CNAK); 
+        writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
+        dev->ep[UDC_EP0OUT_IX].naking = 0;
+        UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX);
+}
+
+/**
+ * \brief
+ * Make endpoint 0 ready for control traffic
+ *
+ * \param dev        pointer to device struc
+ * \return 0 if success
+ */
+static int enable_ep0(struct udc *dev)
+{
+        /* ... then enable host detection and ep0; and we're ready
+         * for set_configuration as well as eventual disconnect.
+         */
+        ep0_start (dev);
+        /* enable ep0 interrupts */
+        udc_enable_ep0_interrupts(dev);
+        /* enable device setup interrupts */
+        udc_enable_dev_setup_interrupts(dev);
+
+        return 0;
+}
+
+/**
+ * \brief
+ * Called by gadget driver to register itself
+ * 
+ * When a driver is successfully registered, it will receive
+ * control requests including set_configuration(), which enables
+ * non-control requests.  then usb traffic follows until a
+ * disconnect is reported.  then a host may connect again, or
+ * the driver might get unbound.
+ *
+ * \param driver        pointer to gadget driver struct
+ * \return 0 if success
+ */
+int usb_gadget_register_driver (struct usb_gadget_driver *driver)
+{
+        struct udc              *dev = the_controller;
+        int                     retval;
+
+        /* insist on high speed support from the driver */
+        if (!driver
+                        || driver->speed != USB_SPEED_HIGH
+                        || !driver->bind
+                        || !driver->unbind
+                        || !driver->setup)
+                return -EINVAL;
+        if (!dev)
+                return -ENODEV;
+        if (dev->driver)
+                return -EBUSY;
+
+        /* hook up the driver ... */
+#if     LINUX_VERSION_CODE >= UDC_NEW_GADGET_KERNEL
+        driver->driver.bus = 0;
+#endif
+        dev->driver = driver;
+#if     LINUX_VERSION_CODE >= UDC_NEW_GADGET_KERNEL
+        dev->gadget.dev.driver = &driver->driver;
+#endif
+
+#if     LINUX_VERSION_CODE >= UDC_NEW_GADGET_KERNEL
+        device_create_file (&dev->pdev->dev, &dev_attr_function);
+        device_create_file (&dev->pdev->dev, &dev_attr_queues);
+#endif
+
+        retval = driver->bind (&dev->gadget);
+        gadget_bind_count++;
+        if (retval) {
+                DBG( "bind to driver %s --> %d\n",
+                                driver->driver.name, retval);
+                dev->driver = 0;
+#if     LINUX_VERSION_CODE >= UDC_NEW_GADGET_KERNEL
+                dev->gadget.dev.driver = 0;
+#endif
+                return retval;
+        }
+
+        /* if otg driver already registered */
+        /* call otg bind() to mux udc to phy */
+        if (dev->otg_driver) {
+                dev->otg_driver->bind();
+        }
+
+        /* pci writes may still be posted */
+        return 0;
+}
+EXPORT_SYMBOL (usb_gadget_register_driver);
+
+/**
+ * Called by OTG driver to register itself
+ * 
+ *
+ * \param driver        pointer to OTG driver struct
+ * \return 0 if success
+ */
+int usb_gadget_register_otg (struct usb_gadget_otg_extension *driver)
+{
+        struct udc              *dev = the_controller;
+        int                     retval;
+        
+        if (!driver || !driver->bind)
+                return -EINVAL;
+        if (!dev)
+                return -ENODEV;
+        if (dev->otg_driver)
+                return -EBUSY;
+
+        /* hook up the driver ... */
+        dev->otg_driver = driver;
+
+#ifdef UDC_IPBUG_3943_WORKAROUND
+        /* init registers here first with suspend bug */
+        if (!otg_reg_count) {
+                startup_registers(dev);
+                otg_reg_count++; 
+        }
+#endif
+
+#ifdef UDC_IPBUG_3950_WORKAROUND
+        /* set notify function */
+        driver->notify = otg_notify;
+#endif
+        /* if gadget driver already registered */
+        /* call gadget bind() to switch to mux udc to phy */
+        /* TODO use SD bit to disconnect/connect later, in */
+        /* AMD5536 RevA0 this does not work ! */
+        if (dev->driver) {
+
+               /* otg driver bind() */
+               retval = driver->bind ();
+               if (retval) {
+                       DBG( "error bind to otg driver\n");
+                       dev->otg_driver = 0;
+                       return retval;
+               }
+                /* get ready for ep0 traffic */
+                enable_ep0(dev);
+        }
+
+        INFO( "registered otg driver\n");
+
+        return 0;
+}
+EXPORT_SYMBOL (usb_gadget_register_otg);
+
+/**
+ * Called by OTG driver to unregister itself
+ * 
+ *
+ * \param driver        pointer to OTG driver struct
+ * \return 0 if success
+ */
+int usb_gadget_unregister_otg (struct usb_gadget_otg_extension *driver)
+{
+        struct udc      *dev = the_controller;
+        unsigned long   flags;
+
+        if (!dev)
+                return -ENODEV;
+        if (!driver || driver != dev->otg_driver)
+                return -EINVAL;
+
+        spin_lock_irqsave (&dev->lock, flags);
+        /* add critical sections here */
+
+        /* mask not needed interrupts */
+        udc_mask_unused_interrupts(dev);
+
+        spin_unlock_irqrestore (&dev->lock, flags);
+
+        driver->unbind ();
+#ifdef UDC_IPBUG_3950_WORKAROUND
+        driver->notify = NULL;
+#endif
+        dev->otg_supported = 0;
+        dev->otg_driver = NULL;
+
+        DBG( "unregistered otg driver\n");
+        return 0;
+}
+EXPORT_SYMBOL (usb_gadget_unregister_otg);
+
+/**
+ *  Temporary left in the code for later reusing or removing
+ *  dequeue ALL requests
+ */
+static void
+stop_activity (struct udc *dev, struct usb_gadget_driver *driver)
+{
+        int tmp;
+
+        /* don't disconnect if it's not connected */
+        if (dev->gadget.speed == USB_SPEED_UNKNOWN)
+                driver = 0;
+
+        /* stop hardware; prevent new request submissions;
+         * and kill any outstanding requests.
+         */
+        usb_reset (dev);
+        for (tmp = 0; tmp < UDC_EP_NUM; tmp++) {
+                nuke (&dev->ep [tmp]);
+        }
+
+        /* report disconnect; the driver is already quiesced */
+        if (driver) {
+                spin_unlock (&dev->lock);
+                driver->disconnect (&dev->gadget);
+                spin_lock (&dev->lock);
+        }
+
+        usb_reinit (dev);
+}
+
+/**
+ * Called by gadget driver to unregister itself
+ *
+ * \param driver        pointer to gadget driver struct
+ * \return 0 if success
+ */
+int usb_gadget_unregister_driver (struct usb_gadget_driver *driver)
+{
+        struct udc      *dev = the_controller;
+        unsigned long   flags;
+
+        if (!dev)
+                return -ENODEV;
+        if (!driver || driver != dev->driver)
+                return -EINVAL;
+        if (gadget_bind_count) {
+                spin_lock_irqsave (&dev->lock, flags);
+                stop_activity (dev, driver);
+                spin_unlock_irqrestore (&dev->lock, flags);
+        }
+
+        /* unbind from otg driver first */
+        if (dev->otg_driver) {
+                dev->otg_driver->unbind();
+        }
+
+        if (gadget_bind_count) {
+                driver->unbind (&dev->gadget);
+        }
+        gadget_bind_count = 0;
+        dev->driver = 0;
+
+        DBG( "unregistered driver '%s'\n", driver->driver.name);
+        return 0;
+}
+EXPORT_SYMBOL (usb_gadget_unregister_driver);
+
+/**
+ * Interrupt Service Routine, see Linux Kernel Doc for parameters  
+ *
+ * \param irq           irq number
+ * \param _dev          pointer to device object
+ * \param r             don't used
+ */
+static irqreturn_t udc_irq (int irq, void *_dev, struct pt_regs * r)
+{
+        struct udc              *dev = _dev;
+        u32 reg;
+        u16 i;
+        u32 ep_irq;
+
+#ifdef UDC_IPBUG_3943_WORKAROUND
+        /* If UDC is suspended, then don't touch any register, otherwise
+           system hangs in endless retry => possibly hang !!! */
+        if (dev->otg_driver && dev->otg_driver->query) {
+                if (dev->otg_driver->query(0) & OTG_FLAGS_UDC_SUSP) {
+                        return IRQ_HANDLED;
+                }     
+        }
+        else
+                return IRQ_HANDLED;
+#endif 
+        
+        spin_lock (&dev->lock);
+
+
+        /* check for ep irq */
+        reg = readl(&dev->regs->ep_irqsts);
+        if (reg)
+        {
+                /* EP0 OUT */
+                if (reg & AMD_BIT(UDC_EPINT_OUT_EP0))
+                {
+                        udc_control_out_isr(dev);
+                } /* EP0 IN */
+                if (reg & AMD_BIT(UDC_EPINT_IN_EP0)) {
+                        udc_control_in_isr(dev);
+                        
+                } 
+
+                /* data endpoint */
+                /* iterate ep's */
+                for (i = 1; i < UDC_EP_NUM; i++) {
+                        ep_irq = 1 << i;
+                        /* irq for out ep ? */
+                        if ((reg & ep_irq) && i > UDC_EPIN_NUM) {      
+                                /* clear irq */
+                                writel(ep_irq, &dev->regs->ep_irqsts);
+                                udc_data_out_isr(dev,i);
+                        } /* irq for in ep ? */
+                        if ((reg & ep_irq) && i < UDC_EPIN_NUM && i > 0) {      
+                                /* clear irq */
+                                writel(ep_irq, &dev->regs->ep_irqsts);
+                                udc_data_in_isr(dev,i);
+                        }
+                        
+                }
+                                          
+        }
+
+
+        /* check for dev irq */
+        reg = readl(&dev->regs->irqsts);
+        if (reg) {
+                /* clear irq */
+                writel(reg, &dev->regs->irqsts);
+                udc_dev_isr(dev, reg);
+        }
+  
+
+        spin_unlock (&dev->lock);
+        return IRQ_HANDLED;
+}
+
+#ifdef UDC_IPBUG_3958_WORKAROUND
+/**
+ * Clear pending NAK bits  
+ *
+ * \param dev           pointer to UDC device object    
+ * \return 0 if success
+ */
+static void udc_process_cnak_queue(struct udc* dev)
+{
+        u32 tmp;
+        u32 reg;
+        /* check epin's */
+        DBG("CNAK pending queue processing\n");
+        for (tmp = 0; tmp < UDC_EPIN_NUM_USED; tmp++) {
+                if (cnak_pending & (1 << tmp)) {
+                        DBG("CNAK pending for ep%d\n", tmp);
+                        /* clear NAK by writing CNAK */
+                        reg = readl(&dev->ep[tmp].regs->ctl);
+                        reg |= AMD_BIT(UDC_EPCTL_CNAK); 
+                        writel(reg, &dev->ep[tmp].regs->ctl);
+                        dev->ep[tmp].naking = 0;
+                        UDC_QUEUE_CNAK(&dev->ep[tmp], dev->ep[tmp].num);
+                } 
+        }
+        /* ...  and ep0out */
+        if (cnak_pending & (1 << UDC_EP0OUT_IX)) {
+                DBG("CNAK pending for ep%d\n", UDC_EP0OUT_IX);
+                /* clear NAK by writing CNAK */
+                reg = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
+                reg |= AMD_BIT(UDC_EPCTL_CNAK); 
+                writel(reg, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
+                dev->ep[UDC_EP0OUT_IX].naking = 0;
+                UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], 
+                                dev->ep[UDC_EP0OUT_IX].num);
+        }
+}
+#endif
+
+/**
+ * Interrupt handler for Control OUT traffic  
+ *
+ * \param dev           pointer to UDC device object    
+ * \return 0 if success
+ */
+static int udc_control_out_isr(struct udc* dev)
+{
+        int ret_val = 0;
+        u32 tmp;
+        
+        /* clear irq */
+        writel(AMD_BIT(UDC_EPINT_OUT_EP0), &dev->regs->ep_irqsts);
+
+        tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->sts);
+        /* check BNA and clear if set */
+        if (tmp & AMD_BIT(UDC_EPSTS_BNA)) {
+                VDBG("BNA set\n");       
+                writel(AMD_BIT(UDC_EPSTS_BNA), &dev->ep[UDC_EP0OUT_IX].regs->sts);
+        }
+        
+        /* type of data: SETUP or DATA 0 bytes */
+        tmp = AMD_GETBITS(tmp, UDC_EPSTS_OUT);
+        VDBG( "data_typ = %lx\n", (unsigned long) tmp);
+        /* setup data */
+        if (tmp == UDC_EPSTS_OUT_SETUP) {
+
+                /* set NAK for EP0_IN */
+                tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+                tmp |= AMD_BIT(UDC_EPCTL_SNAK); 
+                writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+                dev->ep[UDC_EP0IN_IX].naking = 1;
+                /* get setup data */
+                if (use_dma) {
+
+                        /* clear OUT bits in ep status */
+                        writel(UDC_EPSTS_OUT_SETUP_CLEAR, &dev->ep[UDC_EP0OUT_IX].regs->sts);
+
+                        setup_data.raw[0] = dev->ep[UDC_EP0OUT_IX].td_stp->data12;
+                        setup_data.raw[1] = dev->ep[UDC_EP0OUT_IX].td_stp->data34;
+
+                        /* set HOST READY */
+                        writel(UDC_DMA_STP_STS_BS_HOST_READY,
+                               &dev->ep[UDC_EP0OUT_IX].td_stp->dmastat);
+                        /* set RDE */
+                        tmp = readl(&dev->regs->ctl);
+                        tmp |= AMD_BIT(UDC_DEVCTL_RDE);
+                        writel(tmp, &dev->regs->ctl);                
+                        tmp = readl(&dev->regs->ctl);
+                }
+                else {
+                        /* read fifo */
+                        udc_read_fifo_dwords(dev, setup_data.raw, 2);
+                }
+
+                /* call gadget zero with setup data received */
+                spin_unlock (&dev->lock);
+                tmp = dev->driver->setup (&dev->gadget, &setup_data.r);
+                spin_lock (&dev->lock);
+
+                /* clear NAK by writing CNAK in EP0_IN */
+                tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+                tmp |= AMD_BIT(UDC_EPCTL_CNAK); 
+                writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+                dev->ep[UDC_EP0IN_IX].naking = 0;
+                UDC_QUEUE_CNAK(&dev->ep[UDC_EP0IN_IX], UDC_EP0IN_IX);
+                
+
+                /* FIXME tdahlman - which cnak has to be set ep0 in or out ? */
+                /* clear NAK by writing CNAK in EP0_OUT */
+                tmp = readl(&dev->ep[UDC_EP0OUT_IX].regs->ctl);
+                tmp |= AMD_BIT(UDC_EPCTL_CNAK); 
+                writel(tmp, &dev->ep[UDC_EP0OUT_IX].regs->ctl);
+                dev->ep[UDC_EP0OUT_IX].naking = 0;
+                UDC_QUEUE_CNAK(&dev->ep[UDC_EP0OUT_IX], UDC_EP0OUT_IX);
+               
+                if (!use_dma) { 
+                        /* clear OUT bits in ep status */
+                        writel(UDC_EPSTS_OUT_SETUP_CLEAR, &dev->ep[UDC_EP0OUT_IX].regs->sts);
+                }
+
+        } /* data packet 0 bytes */
+        else if (tmp == UDC_EPSTS_OUT_DATA) {
+                /* clear OUT bits in ep status */
+                writel(UDC_EPSTS_OUT_DATA_CLEAR, &dev->ep[UDC_EP0OUT_IX].regs->sts);
+                
+                /* get setup data: only 0 packet */
+                if (use_dma) {
+
+                        /* set HOST READY */
+                        dev->ep[UDC_EP0OUT_IX].td->dmastat =
+                                AMD_ADDBITS(dev->ep[UDC_EP0OUT_IX].td->dmastat,
+                                            UDC_DMA_OUT_STS_BS_HOST_READY,
+                                            UDC_DMA_OUT_STS_BS);
+                        /* set RDE */
+                        tmp = readl(&dev->regs->ctl);
+                        tmp |= AMD_BIT(UDC_DEVCTL_RDE);
+                        writel(tmp, &dev->regs->ctl);                
+                        tmp = readl(&dev->regs->ctl);
+                }
+                else {
+                        /* dummy read confirm */
+                        readl(&dev->ep[UDC_EP0OUT_IX].regs->confirm);
+                }
+        }
+
+#ifdef UDC_IPBUG_3958_WORKAROUND
+        /* check pending CNAKS */
+        if (cnak_pending) {
+                /* CNAk processing when rxfifo empty only */
+                if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) {
+                        udc_process_cnak_queue(dev);
+                }
+        }
+#endif       
+ 
+        return ret_val;                       
+}
+
+/**
+ * Interrupt handler for Control IN traffic  
+ *
+ * \param dev           pointer to UDC device object    
+ * \return 0 if success
+ */
+static int udc_control_in_isr(struct udc* dev)
+{
+        int ret_val = 0;
+        u32 tmp;
+        struct udc_ep   *ep;
+        struct udc_request              *req;
+        unsigned        len;
+
+        ep = &dev->ep [0];
+
+        /* clear irq */
+        writel(AMD_BIT(UDC_EPINT_IN_EP0), &dev->regs->ep_irqsts);
+        
+        tmp= readl(&dev->ep[UDC_EP0IN_IX].regs->sts);
+        /* DMA completion */
+        if (tmp & AMD_BIT(UDC_EPSTS_TDC)) {
+                VDBG("isr: TDC clear \n");
+#ifdef UDC_IPBUG_2253_WORKAROUND
+                /* stop NAKing after small packet DMA */
+                if (ep->naking) {
+                        /* clear NAK by writing CNAK */
+                        tmp = readl(&ep->regs->ctl);
+                        tmp |= AMD_BIT(UDC_EPCTL_CNAK); 
+                        writel(tmp, &ep->regs->ctl);
+                        ep->naking = 0;
+                        UDC_QUEUE_CNAK(ep, ep->num);
+                }
+#endif
+                /* clear TDC bit */
+                writel(AMD_BIT(UDC_EPSTS_TDC),&dev->ep[UDC_EP0IN_IX].regs->sts);
+        } /* status reg has IN bit set ? */
+        else if (tmp & AMD_BIT(UDC_EPSTS_IN)) {
+                if (ep->dma) {
+                        /* clear IN bit */
+                        writel(AMD_BIT(UDC_EPSTS_IN),&dev->ep[UDC_EP0IN_IX].regs->sts);
+                }
+
+                if (!list_empty (&ep->queue))
+                {
+                        /* next request */
+                        req = list_entry (ep->queue.next,
+                                        struct udc_request, queue);
+                        
+                        if (ep->dma) {
+                                /* write desc pointer */
+                                writel(req->td_dma, &ep->regs->desptr);
+                                /* set HOST READY */
+                                req->td_data->dmastat 
+                                      = AMD_ADDBITS(req->td_data->dmastat,
+                                                    UDC_DMA_STP_STS_BS_HOST_READY,
+                                                    UDC_DMA_STP_STS_BS);
+                                wb_flush();
+
+#ifdef UDC_IPBUG_2253_WORKAROUND
+                                /* NAK if small packet until TDC interrupt */
+                                if (req->req.length < UDC_SMALL_PACKET) {
+                                        /* set NAK */
+                                        tmp = readl(&ep->regs->ctl);
+                                        tmp |= AMD_BIT(UDC_EPCTL_SNAK); 
+                                        writel(tmp, &ep->regs->ctl);
+                                        ep->naking = 1;
+                                        wb_flush();
+                                }
+#endif
+                                /* set poll demand bit */
+                                tmp = readl(&dev->ep[UDC_EP0IN_IX].regs->ctl);
+                                tmp |= AMD_BIT(UDC_EPCTL_P);
+                                writel(tmp, &dev->ep[UDC_EP0IN_IX].regs->ctl);
+
+                                /* all bytes will be transferred */
+                                req->req.actual = req->req.length;
+
+                                /* complete req */
+                                done (ep, req, 0);
+                                
+                        }
+                        else {
+                                /* write fifo */
+                                write_fifo(ep, &(req->req));
+
+                                /* lengh bytes transfered */
+                                len = req->req.length - req->req.actual;
+                                len = min (ep->ep.maxpacket, len);
+                                req->req.actual += len;
+                                if (req->req.actual == req->req.length
+                                    || (len != ep->ep.maxpacket)) {
+                                    //&& (!req->req.zero || len != ep->ep.maxpacket)) {
+                                        /* complete req */
+                                        done (ep, req, 0);
+                                }
+                        }
+                        
+                }
+                
+                ep->stopped = 0;
+                dev->protocol_stall = 0;
+                if (!ep->dma) {
+                        /* clear IN bit */
+                        writel(AMD_BIT(UDC_EPSTS_IN),&dev->ep[UDC_EP0IN_IX].regs->sts);
+                }
+        }
+
+        return ret_val;
+}
+
+/**
+ * Interrupt handler for data OUT traffic  
+ *
+ * \param dev           pointer to UDC device object    
+ * \param ep_ix         endpoint index    
+ * \return 0 if success
+ */
+static int udc_data_out_isr(struct udc* dev, int ep_ix)
+{
+        int ret_val = 0;
+        u32 tmp;
+        struct udc_ep   *ep;
+        struct udc_request              *req;
+        unsigned int count;
+        struct udc_data_dma     *td = NULL;
+        unsigned dma_done;
+
+        VDBG("ep%d irq\n", ep_ix); 
+        ep = &dev->ep [ep_ix];
+
+        tmp = readl(&ep->regs->sts);
+        if (use_dma) {
+                /* BNA event ? */
+                if (tmp & AMD_BIT(UDC_EPSTS_BNA)) {
+                        ERR("BNA ep%d occured - DESPTR = %lx \n", ep->num, (unsigned long) readl(&ep->regs->desptr));
+                        
+                        /* clear BNA */
+                        writel(tmp | AMD_BIT(UDC_EPSTS_BNA), &ep->regs->sts); 
+                        /* TODO no early return */
+                        return 1;                       
+                } 
+        }
+        /* HE event ? */
+        if (tmp & AMD_BIT(UDC_EPSTS_HE)) {
+                ERR("HE ep%d occured\n", ep->num);
+                
+                /* clear HE */
+                writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts); 
+                /* TODO no early return */
+                return 1;                       
+        } 
+
+        if (!list_empty (&ep->queue)) {
+
+                /* next request */ 
+                req = list_entry (ep->queue.next,
+                        struct udc_request, queue);
+        }
+        else
+        {
+                req = 0;
+#ifdef DEBUG
+                no_req++;
+#endif
+                udc_rxfifo_pending = 1;
+        }
+        VDBG("req = %lx\n", (unsigned long) req);
+        /* fifo mode ****************/
+        if (!use_dma) {
+
+                /* read fifo */
+                if (req && read_fifo(ep, req)) {
+                        /* finish */
+                        done (ep, req, 0);
+                        /* maybe advance queue to next request */
+                        if (!list_empty (&ep->queue) && !ep->stopped) {
+                                req = list_entry (ep->queue.next,
+                                        struct udc_request, queue);
+                        } 
+                        else
+                                req = 0;
+                }
+
+        } /* DMA ********************/
+        else if (req) {
+
+                /* check for DMA done */
+                if (!use_dma_ppb) {
+                        dma_done = AMD_GETBITS(req->td_data->dmastat, UDC_DMA_OUT_STS_BS); 
+                } /* packet per buffer mode - rx bytes */
+                else {
+                        td = udc_get_last_dma_desc(req);
+                        dma_done = AMD_GETBITS(td->dmastat, UDC_DMA_OUT_STS_BS); 
+                }
+                if (dma_done == UDC_DMA_OUT_STS_BS_DMA_DONE) {
+                        /* buffer fill mode - rx bytes */
+                        if (!use_dma_ppb) {
+                                /* received number bytes */
+                                count = AMD_GETBITS(req->td_data->dmastat, UDC_DMA_OUT_STS_RXBYTES); 
+                                VDBG("rx bytes=%lx\n", (unsigned long) count);
+                        } /* packet per buffer mode - rx bytes */
+                        else {
+                                VDBG("req->td_data=%lx\n", (unsigned long) req->td_data);
+                                VDBG("last desc = %lx\n", (unsigned long) td);
+                                /* received number bytes */
+                                if (use_dma_ppb_du) {
+                                        /* every desc. counts bytes */
+                                        count = udc_get_ppbdu_rxbytes(req);
+                                }
+                                else {
+                                        /* last desc. counts bytes */
+                                        count = AMD_GETBITS(td->dmastat, UDC_DMA_OUT_STS_RXBYTES);
+                                }
+                                VDBG("last desc rx bytes=%lx\n", (unsigned long) count);
+                        }
+
+                        tmp = req->req.length - req->req.actual;
+                        if (count > tmp) {
+                                /* as with DMA, data overflow gets flushed */
+                                if ((tmp % ep->ep.maxpacket) != 0) {
+                                        ERR( "%s out fifo %d bytes, expected %d\n",
+                                                ep->ep.name, count, tmp);
+                                        req->req.status = -EOVERFLOW;
+                                }
+                                count = tmp;
+                        }
+                        req->req.actual += count;
+
+                        /* complete request */ 
+                        done (ep, req, 0);
+                        
+                        /* maybe advance queue to next request */
+                        if (!list_empty (&ep->queue) && !ep->stopped) {
+                                req = list_entry (ep->queue.next,
+                                        struct udc_request, queue);
+
+                                /* next dma */
+                                prep_dma(ep, req);
+                                /* write desc pointer */
+                                writel(req->td_dma, &ep->regs->desptr);
+                                /* set RDE */
+                                tmp = readl(&dev->regs->ctl);
+                                tmp |= AMD_BIT(UDC_DEVCTL_RDE);
+                                writel(tmp, &dev->regs->ctl);                
+                                tmp = readl(&dev->regs->ctl);
+                        }       
+#ifdef DEBUG  
+                        else {
+                                no_pref_req++;
+                                VDBG("OUT queue empty\n");
+                        }
+#endif
+
+               } 
+               else {
+                        /* RX DMA must be reenabled for each desc in PPBDU mode */
+                        if (use_dma_ppb_du) {
+                                /* set RDE */ 
+                                tmp = readl(&dev->regs->ctl);
+                                tmp |= AMD_BIT(UDC_DEVCTL_RDE);
+                                writel(tmp, &dev->regs->ctl);                
+                                tmp = readl(&dev->regs->ctl);
+                        }
+               } 
+                
+        }
+
+#ifdef UDC_IPBUG_3958_WORKAROUND
+        /* check pending CNAKS */
+        if (cnak_pending) {
+                /* CNAk processing when rxfifo empty only */
+                if (readl(&dev->regs->sts) & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) {
+                        udc_process_cnak_queue(dev);
+                }
+        }
+#endif       
+
+        /* clear OUT bits in ep status */
+        writel(UDC_EPSTS_OUT_DATA_CLEAR, &ep->regs->sts);
+
+        return ret_val;
+}
+
+
+/**
+ * Interrupt handler for data IN traffic  
+ *
+ * \param dev           pointer to UDC device object    
+ * \param ep_ix         endpoint index    
+ * \return 0 if success
+ */
+static int udc_data_in_isr(struct udc* dev, int ep_ix)
+{
+        int ret_val = 0;
+        u32 tmp;
+        struct udc_ep   *ep;
+        struct udc_request              *req;
+        struct udc_data_dma     *td;
+        unsigned dma_done;
+        unsigned        len;
+
+        ep = &dev->ep [ep_ix];
+        
+        tmp = readl(&ep->regs->sts);
+        if (use_dma) {
+                /* BNA ? */
+                if (tmp & AMD_BIT(UDC_EPSTS_BNA)) {
+                        ERR("BNA ep%d occured - DESPTR = %08lx \n",
+                            ep->num, (unsigned long) readl(&ep->regs->desptr));
+                        
+                        /* clear BNA */
+                        writel(tmp, &ep->regs->sts); 
+                        /* TODO no early return */
+
+                        return 1;                       
+                } 
+        }
+        /* HE event ? */
+        if (tmp & AMD_BIT(UDC_EPSTS_HE)) {
+                ERR("HE ep%d occured -  DESPTR = %08lx \n",
+                     ep->num, (unsigned long) readl(&ep->regs->desptr));
+                
+                /* clear HE */
+                writel(tmp | AMD_BIT(UDC_EPSTS_HE), &ep->regs->sts); 
+                /* TODO no early return */
+                return 1;                       
+        } 
+
+        tmp= readl(&ep->regs->sts);
+        
+        /* DMA completion */
+        if (tmp & AMD_BIT(UDC_EPSTS_TDC)) {
+                VDBG("TDC set- completion\n");
+                if (!list_empty (&ep->queue)) {
+                       req = list_entry (ep->queue.next,
+                                        struct udc_request, queue);
+                       if (req) { 
+                                /* lengh bytes transfered */
+                                /* check dma done of last desc. in PPBDU mode */
+                                if (use_dma_ppb_du) {
+                                        td = udc_get_last_dma_desc(req);
+                                        if (td) {
+                                                /* RTL bug (Bugzilla #3672): the TDC */
+                                                /* irq is quicker */
+                                                /* than the desc. update in memory */
+                                                /* This must be fixed in RTL !!! */
+                                                 /* TDC is set, but DMA is busy */
+                                                dma_done = 
+                                                   AMD_GETBITS(td->dmastat, UDC_DMA_IN_STS_BS); 
+                                                /* don't care DMA done as long the above mentioned RTL bug is */
+                                                /* present, TODO check dma_done if this is fixed */
+                                                //if (dma_done == UDC_DMA_IN_STS_BS_DMA_DONE) {
+                                                        req->req.actual = req->req.length;
+                                                //}
+                                        }
+                                }
+                                else {
+                                        /* assume all bytes transferred */
+                                        /* TODO check error status */
+                                        req->req.actual = req->req.length;
+                                }
+
+                                if (req->req.actual == req->req.length) { 
+#ifdef UDC_IPBUG_2253_WORKAROUND
+                                        /* stop NAKing after small packet DMA */
+                                        if (ep->naking) {
+                                                /* clear NAK by writing CNAK */
+                                                tmp = readl(&ep->regs->ctl);
+                                                tmp |= AMD_BIT(UDC_EPCTL_CNAK); 
+                                                writel(tmp, &ep->regs->ctl);
+                                                ep->naking = 0;
+                                                UDC_QUEUE_CNAK(ep, ep->num);
+                                        }
+#endif
+                                        /* complete req */
+                                        done (ep, req, 0);
+                                        req->dma_pend = 0;
+#ifdef UDC_DISABLE_IRQ_IF_EMPTY_IN_QUEUE
+                                        /* further request available ? */
+                                        if (list_empty (&ep->queue)) {
+                                                /* disable interrupt */
+                                                tmp = readl(&dev->regs->ep_irqmsk);
+                                                tmp |= AMD_BIT(ep->num);
+                                                writel(tmp, &dev->regs->ep_irqmsk);        
+                                        } 
+#endif
+
+                                        /* ep stopped and marked for stall ? */
+                                        if (ep->stopped) {
+                                                /* flush the txfifo */
+                                                tmp = readl(&ep->regs->ctl);
+                                                tmp |= AMD_BIT(UDC_EPCTL_F);
+                                                writel(tmp, &ep->regs->ctl);
+                                                
+                                                /* set STALL */
+                                                DBG("ep-%d set STALL\n", ep->num);
+                                                tmp = readl(&ep->regs->ctl);
+                                                tmp |= AMD_BIT(UDC_EPCTL_S);
+                                                writel(tmp, &ep->regs->ctl);
+                                                //ep->stopped = 0;
+                                        }
+                                }
+                       }
+               }
+        
+
+                /* clear TDC bit */
+                writel(AMD_BIT(UDC_EPSTS_TDC),&ep->regs->sts);
+
+                 
+        } /* status reg has IN bit set ? */
+        else if (tmp & AMD_BIT(UDC_EPSTS_IN)) {
+                if (!list_empty (&ep->queue))
+                {
+                        /* next request */
+                        req = list_entry (ep->queue.next,
+                                        struct udc_request, queue);
+                        /* FIFO mode ********/ 
+                        if (!use_dma) {
+                                /* write fifo */
+                                write_fifo(ep, &(req->req));
+                                len = req->req.length - req->req.actual;
+                                len = min (ep->ep.maxpacket, len);
+                                req->req.actual += len;
+                                if (req->req.actual == req->req.length 
+                                    || (len != ep->ep.maxpacket)) {
+                                    //&& (!req->req.zero || len != ep->ep.maxpacket)) {
+                                        /* complete req */
+                                        done (ep, req, 0);
+                                }
+                        } /* DMA *****************/
+                        else if (req && !req->dma_pend) { 
+                                VDBG("IN DMA : req=%lx req->td_data=%lx\n",
+                                     (unsigned long) req, (unsigned long) req->td_data);
+                                if (req->td_data) {
+
+                                        req->dma_pend = 1;
+
+                                        /* unset L bit of first desc. for chain */
+                                        if (use_dma_ppb && req->req.length > ep->ep.maxpacket) {
+                                                req->td_data->dmastat &= AMD_CLEAR_BIT(UDC_DMA_IN_STS_L);
+                                        }
+
+                                        /* write desc pointer */
+                                        writel(req->td_dma, &ep->regs->desptr);
+
+                                        wb_flush();                                        
+                                        /* set HOST READY */
+                                        req->td_data->dmastat 
+                                              = AMD_ADDBITS(req->td_data->dmastat,
+                                                            UDC_DMA_IN_STS_BS_HOST_READY,
+                                                            UDC_DMA_IN_STS_BS);
+
+                                        wb_flush();
+
+#ifdef UDC_IPBUG_2253_WORKAROUND
+                                        /* NAK if small packet until TDC interrupt */
+                                        if (req->req.length < UDC_SMALL_PACKET) {
+                                                /* set NAK */
+                                                tmp = readl(&ep->regs->ctl);
+                                                tmp |= AMD_BIT(UDC_EPCTL_SNAK); 
+                                                writel(tmp, &ep->regs->ctl);
+                                                ep->naking = 1;
+                                                wb_flush();
+                                        }
+#endif
+
+        
+                                        /* set poll demand bit */
+                                        tmp = readl(&ep->regs->ctl);
+                                        tmp |= AMD_BIT(UDC_EPCTL_P);
+                                        writel(tmp, &ep->regs->ctl);
+                                }
+                        }
+
+                } 
+
+                /* clear IN bit */
+                writel(AMD_BIT(UDC_EPSTS_IN),&ep->regs->sts);
+        }
+       
+        return ret_val;
+ 
+}
+
+
+
+/**
+ * Interrupt handler for global device events  
+ *
+ * \param dev           pointer to UDC device object    
+ * \param dev_irq       device interrupt bit of DEVINT register    
+ * \return 0 if success
+ */
+static int udc_dev_isr(struct udc* dev, u32 dev_irq)
+{
+        int ret_val = 0;
+        u32 tmp;
+        u32 cfg;
+        struct udc_ep   *ep;
+        u16 i;
+        u8 udc_csr_epix;
+
+#ifdef UDC_IPCASE_8000009363_WORKAROUND
+        /* SOF interrupt for polling STALL */
+        if (dev_irq & AMD_BIT(UDC_DEVINT_SOF)) {
+                /* check if stopped ep after STALL whether
+                   STALL cleared by CLEAR_FEATURE */
+                ep = &dev->ep [UDC_EPIN_IX];
+                if (ep->stopped) {
+                        tmp = readl(&ep->regs->ctl);
+                        DBG("check STALL for ep%d\n",ep->num);
+                        /* STALL cleared ? */
+                        if (!(tmp & AMD_BIT(UDC_EPCTL_S))) {
+                                DBG("clear NAK after STALL\n");
+                                /* clear NAK by writing CNAK */
+                                tmp = readl(&ep->regs->ctl);
+                                tmp |= AMD_BIT(UDC_EPCTL_CNAK); 
+                                writel(tmp, &ep->regs->ctl);
+                                ep->stopped = 0;
+                                UDC_QUEUE_CNAK(ep, ep->num);
+                        }
+                }
+                        
+        }
+#endif
+        
+        /* SET_CONFIG irq ? */
+        if (dev_irq & AMD_BIT(UDC_DEVINT_SC)) {
+
+                /* read config value */
+                tmp = readl(&dev->regs->sts);
+                cfg = AMD_GETBITS(tmp, UDC_DEVSTS_CFG);
+#ifdef DEBUG
+                /* this is needed for debug only */
+                if (cfg == dev->cur_config) {
+                        same_cfg = 1;
+                }
+                else {
+                        same_cfg = 0;
+                }
+                VDBG("same_cfg=%d\n", same_cfg);
+#endif
+                DBG("SET_CONFIG interrupt: config=%d\n", cfg);
+                dev->cur_config = cfg;
+
+                /* make usb request for gadget driver */
+                memset(&setup_data, 0 , sizeof(union udc_setup_data));
+                setup_data.r.bRequest = USB_REQ_SET_CONFIGURATION;
+                setup_data.r.wValue = dev->cur_config;
+
+                /* programm the NE registers */
+                /* TODO - put this to extra function or use usb_reinit() or udc_enable() */
+                for (i = 0; i < UDC_EP_NUM; i++) {
+                        ep = &dev->ep [i];
+                        if (ep->is_in) {
+                                
+                                /* ep ix in UDC CSR register space */
+                                udc_csr_epix = ep->num;
+
+                                
+                        } /* OUT ep */
+                        else {
+                                /* ep ix in UDC CSR register space */
+                                udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+                        }
+
+                        tmp = readl(&dev->csr->ne[udc_csr_epix]);
+                        /* ep cfg */
+                        tmp = AMD_ADDBITS(tmp, ep->dev->cur_config, UDC_CSR_NE_CFG);
+                        /* write reg */
+
+                        writel(tmp, &dev->csr->ne[udc_csr_epix]);
+                }
+                /* call gadget zero with setup data received */
+                spin_unlock (&dev->lock);
+                tmp = dev->driver->setup (&dev->gadget, &setup_data.r);
+                spin_lock (&dev->lock);
+
+                /* set CSR_DONE to complete */
+                tmp = readl(&dev->regs->ctl);
+                tmp |= AMD_BIT(UDC_DEVCTL_CSR_DONE);
+                writel(tmp, &dev->regs->ctl);
+                                      
+        } /* SET_INTERFACE ? */
+        if (dev_irq & AMD_BIT(UDC_DEVINT_SI)) {
+                /* read interface and alt setting values */
+                tmp = readl(&dev->regs->sts);
+                dev->cur_alt = AMD_GETBITS(tmp, UDC_DEVSTS_ALT);
+                dev->cur_intf = AMD_GETBITS(tmp, UDC_DEVSTS_INTF);
+                                      
+                /* make usb request for gadget driver */
+                memset(&setup_data, 0 , sizeof(union udc_setup_data));
+                setup_data.r.bRequest = USB_REQ_SET_INTERFACE;
+                setup_data.r.bRequestType = USB_RECIP_INTERFACE;
+                setup_data.r.wValue = dev->cur_alt;
+                setup_data.r.wIndex = dev->cur_intf;
+                
+                DBG("SET_INTERFACE interrupt: alt=%d intf=%d\n", dev->cur_alt, dev->cur_intf);
+
+                /* programm the NE registers */
+                /* TODO - put this to extra function or use usb_reinit() or udc_enable() */
+                for (i = 0; i < UDC_EP_NUM; i++) {
+                        ep = &dev->ep [i];
+                        if (ep->is_in) {
+                                
+                                /* ep ix in UDC CSR register space */
+                                udc_csr_epix = ep->num;
+
+                                
+                        } /* OUT ep */
+                        else {
+                                /* ep ix in UDC CSR register space */
+                                udc_csr_epix = ep->num - UDC_CSR_EP_OUT_IX_OFS;
+                        }
+
+                        /***** UDC CSR reg ****************************/
+                        /* set ep values  */
+                        tmp = readl(&dev->csr->ne[udc_csr_epix]);
+                        /* ep interface */
+                        tmp = AMD_ADDBITS(tmp, ep->dev->cur_intf, UDC_CSR_NE_INTF);
+                        //tmp = AMD_ADDBITS(tmp, 2, UDC_CSR_NE_INTF);
+                        /* ep alt */
+                        tmp = AMD_ADDBITS(tmp, ep->dev->cur_alt, UDC_CSR_NE_ALT);
+                        //tmp = AMD_ADDBITS(tmp, 1, UDC_CSR_NE_ALT);
+                        /* write reg */
+                        writel(tmp, &dev->csr->ne[udc_csr_epix]);
+                }
+
+                /* call gadget zero with setup data received */
+                spin_unlock (&dev->lock);
+                tmp = dev->driver->setup (&dev->gadget, &setup_data.r);
+                spin_lock (&dev->lock);
+
+                /* set CSR_DONE to complete */
+                tmp = readl(&dev->regs->ctl);
+                tmp |= AMD_BIT(UDC_DEVCTL_CSR_DONE);
+                writel(tmp, &dev->regs->ctl);
+        } /* USB reset */
+        if (dev_irq & AMD_BIT(UDC_DEVINT_UR)) {
+                DBG("USB Reset interrupt\n");
+
+                /* allow soft reset when suspend occurs */
+                soft_reset_occured = 0;
+
+                /* mask not needed interrupts */
+                udc_mask_unused_interrupts(dev);
+
+                /* call gadget to reset configs etc. */
+                spin_unlock (&dev->lock);
+                dev->driver->disconnect (&dev->gadget);
+                spin_lock (&dev->lock);
+
+                /* disable ep0 to empty req queue */
+                nuke (&dev->ep [UDC_EP0IN_IX]);
+                ep_reset (dev->regs,
+                          &dev->ep [UDC_EP0IN_IX]);
+
+#ifdef UDC_IPBUG_3958_WORKAROUND_SOFT_RESET_ON_USBRESET
+                /* soft reset when rxfifo not empty */
+                tmp = readl(&dev->regs->sts);
+                if (!(tmp & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY)) && 
+                    !soft_reset_after_usbreset_occured) {
+                        udc_soft_reset(dev);
+                        soft_reset_after_usbreset_occured++;
+                } 
+#endif
+
+                /* DMA reset to kill potential old DMA hw hang, */
+                /* POLL bit is already reset by ep_reset() through */
+                /* disconnect() */
+                UDC_DMARST(tmp,dev);
+                
+                /* put into initial config */
+                usb_reset (dev);
+
+#ifdef UDC_IPBUG_3958_WORKAROUND_RXFIFO_FLUSH_ON_USBRESET
+                /* TODO this workaround hang sometimes */
+                {
+                        u32 reg;
+                        u32 val;
+                        /* TODO outsource this */
+                        /* take care of PCSBUG#1503, empty rxfifo to allow CNAK */
+                        /* disable DMA */
+                        tmp = readl(&dev->regs->ctl);
+                        writel(tmp & AMD_UNMASK_BIT(UDC_DEVCTL_MODE), &dev->regs->ctl);
+                        /* empty rxfifo */
+                        reg = readl(&dev->regs->sts);
+                        while (!(reg & AMD_BIT(UDC_DEVSTS_RXFIFO_EMPTY))) {
+                                DBG("RXFIFO not empty\n");
+                                /* clear all OUT and BNA bits */
+                                for (i = UDC_EPIN_NUM; i < UDC_EP_NUM; i++) {
+                                        ep = &dev->ep [i];
+                                        reg = readl(&ep->regs->sts);
+                                        /* BNA clear */
+                                        if (reg & AMD_BIT(UDC_EPSTS_BNA)) {
+                                                ERR("BNA ep%d occured - ep sts = %08lx \n", ep->num, (unsigned long) reg);
+                                                /* clear BNA */
+                                                writel(reg | AMD_BIT(UDC_EPSTS_BNA), &ep->regs->sts); 
+                                        }
+                                        if (reg & AMD_BIT(UDC_EPSTS_OUT_DATA) || 
+                                            reg & AMD_BIT(UDC_EPSTS_OUT_SETUP)) { 
+                                                /* received number bytes */
+                                                val = readl(&ep->regs->sts);
+                                                val = AMD_GETBITS(val, UDC_EPSTS_RX_PKT_SIZE);
+                                                /* read rx fifo bytes */
+                                                DBG("Clear ep%d rxfifo bytes count=%d\n", ep->num, val);
+                                                udc_read_fifo_bytes(dev, udc_rxfifo_trash, val); 
+                                                /* clear OUT/SETUP bits in ep status */
+                                                writel(reg | UDC_EPSTS_OUT_SETUP_CLEAR | 
+                                                                UDC_EPSTS_OUT_DATA_CLEAR, &ep->regs->sts);
+                                        }
+                                }
+                                reg = readl(&dev->regs->sts);
+                        }
+                        /* re-enable DMA if was active */
+                        writel(tmp, &dev->regs->ctl);
+                }
+#endif
+
+                /* enable device setup interrupts */
+                udc_enable_dev_setup_interrupts(dev);
+
+        } /* USB suspend */
+#ifndef UDC_IPBUG_3943_WORKAROUND
+        if (dev_irq & AMD_BIT(UDC_DEVINT_US)) {
+                DBG("USB Suspend interrupt\n");
+
+        } /* new speed ? */
+#endif
+        if (dev_irq & AMD_BIT(UDC_DEVINT_ENUM)) {
+                DBG("ENUM interrupt\n");
+#ifdef DEBUG
+                num_enums++;
+                DBG("%d enumerations !\n", num_enums);
+#endif
+                soft_reset_after_usbreset_occured = 0;
+
+                /* disable ep0 to empty req queue */
+                nuke (&dev->ep [UDC_EP0IN_IX]);
+                ep_reset (dev->regs,
+                          &dev->ep [UDC_EP0IN_IX]);
+
+                /* link up all endpoints */
+                usb_reinit (dev);
+                if (dev->gadget.speed == USB_SPEED_HIGH) {
+                        INFO("Connect: Speed = HIGH_SPEED\n");
+                }
+                else if (dev->gadget.speed == USB_SPEED_FULL) {
+                        INFO("Connect: Speed = FULL_SPEED\n");
+                }
+
+                /* init ep 0 */
+                ep0_start(dev);
+
+                /* enable ep0 interrupts */
+                udc_enable_ep0_interrupts(dev);
+        }
+#ifndef UDC_IPBUG_3950_WORKAROUND 
+        /* session valid change interrupt */
+        if (dev_irq & AMD_BIT(UDC_DEVINT_SVC)) {
+                DBG("USB SVC interrupt\n");
+
+                /* check that session is not valid to detect disconnect */
+                tmp = readl(&dev->regs->sts);
+                if (!(tmp & AMD_BIT(UDC_DEVSTS_SESSVLD))) {
+                        DBG("USB Disconnect (session valid low)\n");
+                        /* cleanup on disconnect */
+                        usb_disconnect(the_controller);
+                }
+
+        }
+#endif
+
+        return ret_val;
+}
+
+
+/**
+ * Tears down device 
+ *
+ * \param _dev        pointer to device struct
+ */
+#if     LINUX_VERSION_CODE >= UDC_NEW_GADGET_KERNEL
+static void gadget_release (struct device *_dev)
+{
+        struct amd5536udc *dev = dev_get_drvdata (_dev);
+
+        kfree (dev);
+}
+#endif
+
+/**
+ * Tears down the binding between this driver and the pci device
+ *
+ * \param pdev        pointer to pci device struct
+ */
+static void udc_remove (struct pci_dev *pdev)
+{
+        struct udc              *dev;
+
+#ifdef DEBUG
+        /* debug */
+        print_misc(the_controller);
+#endif
+        dev = pci_get_drvdata (pdev);
+        /* start with the driver above us */
+        if (dev->driver) {
+                /* should have been done already by driver model core */
+                WARN( "pci remove, driver '%s' is still registered\n",
+                                dev->driver->driver.name);
+                usb_gadget_unregister_driver (dev->driver);
+        }
+        /* check for otg driver above us */
+        if (dev->otg_driver) {
+                /* should have been done already by driver model core */
+                WARN( "otg driver is still registered\n");
+                usb_gadget_unregister_otg (dev->otg_driver);
+        }
+        /* then clean up the resources we allocated during probe() */
+        if (dev->data_requests) {
+                pci_pool_destroy (dev->data_requests);
+        }
+        if (dev->stp_requests) {
+                /* cleanup DMA desc's for ep0in */
+                pci_pool_free (dev->stp_requests,
+                               dev->ep [UDC_EP0OUT_IX].td_stp,
+                               dev->ep [UDC_EP0OUT_IX].td_stp_dma);
+                pci_pool_free (dev->stp_requests,
+                               dev->ep [UDC_EP0OUT_IX].td,
+                               dev->ep [UDC_EP0OUT_IX].td_dma);
+
+                pci_pool_destroy (dev->stp_requests);
+        }
+
+        /* init controller by soft reset */
+        writel(AMD_BIT(UDC_DEVCFG_SOFTRESET), &dev->regs->cfg);
+        if (dev->got_irq)
+                free_irq (pdev->irq, dev);
+        if (dev->regs)
+                iounmap (dev->regs);
+        if (dev->region)
+                release_mem_region (pci_resource_start (pdev, 0),
+                                pci_resource_len (pdev, 0));
+#ifndef CONFIG_USB_NON_PCI_OTGDEVICE
+        if (dev->enabled)
+                pci_disable_device (pdev);
+#endif
+
+#if     LINUX_VERSION_CODE >= UDC_NEW_GADGET_KERNEL
+        device_unregister (&dev->gadget.dev);
+        device_remove_file (&pdev->dev, &dev_attr_registers);
+#endif
+
+        pci_set_drvdata (pdev, 0);
+
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE
+        {
+                u32 tmp;
+                /* disable UDC memory and DMA */
+                tmp = readl(USB_MSR_BASE + USB_MSR_MCFG);
+                tmp &= AMD_CLEAR_BIT(USBMSRMCFG_DMEMEN) & AMD_CLEAR_BIT(USBMSRMCFG_DBMEN);
+                writel(tmp, USB_MSR_BASE + USB_MSR_MCFG); 
+        }
+#endif
+
+        INFO( "unbind\n");
+
+        kfree (dev);
+        the_controller = 0;
+}
+
+/**
+ * Called when pci device is recognized and driver is loaded by kernel,
+ * init register access and register interrupt, but wait for gadget
+ * driver registering before any USB traffic  
+ *
+ * \param pdev        pointer to pci device struct
+ * \param id          pointer to pci device id
+ * \return 0 if success
+ */
+static int udc_probe (struct pci_dev *pdev, const struct pci_device_id *id)
+{
+        struct udc              *dev;
+        unsigned long           resource, len;
+        void                    *base = 0;
+        char                    buf [8], *bufp;
+        struct udc_stp_dma      *td_stp;
+        struct udc_data_dma     *td_data;
+        int retval;
+
+        VDBG("udc_probe()\n");
+
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE
+        {
+                u32     tmp;
+                /*
+                 * Fill in the dummy pci_dev. Only those members actually
+                 * dereferenced in this driver are initialized.
+                 */
+                memset(pdev, 0, sizeof(struct pci_dev));
+                strcpy(pdev->name, "non-PCI UDC");
+                strcpy(pdev->slot_name, "builtin");
+                pdev->resource[0].name = "UDC Registers";
+                pdev->resource[0].start = USB_UDC_BASE;
+                pdev->resource[0].end = USB_UDC_BASE + USB_UDC_LEN - 1;
+                pdev->resource[0].flags = 0;
+                pdev->irq = AU1200_USB_INT;
+
+                /* enable UDC memory, DMA and clock */
+                tmp = readl(USB_MSR_BASE + USB_MSR_MCFG);
+                tmp |= AMD_BIT(USBMSRMCFG_DMEMEN) | AMD_BIT(USBMSRMCFG_DBMEN)
+                        | AMD_BIT(USBMSRMCFG_UDCCLKEN)
+                        | AMD_BIT(USBMSRMCFG_PHYPLLEN);
+                        // | AMD_BIT(USBMSRMCFG_RDCOMB) | AMD_BIT(USBMSRMCFG_PFEN);
+                writel(tmp, USB_MSR_BASE + USB_MSR_MCFG); 
+        }
+#else 
+        /* if you want to support more than one controller in a system,
+         * usb_gadget_driver_{register,unregister}() must change.
+         */
+        if (the_controller) {
+                WARN ("ignoring %s\n", pdev->slot_name);
+                return -EBUSY;
+        }
+#endif
+        /* alloc, and start init */
+        dev = kmalloc (sizeof (struct udc), SLAB_KERNEL);
+        if (dev == NULL){
+                retval = -ENOMEM;
+                goto done;
+        }
+
+        memset (dev, 0, sizeof (struct udc));
+        spin_lock_init(&dev->lock);
+        spin_lock_init(&udc_irq_spinlock);
+        dev->pdev = pdev;
+        dev->gadget.ops = &udc_ops;
+
+#if     LINUX_VERSION_CODE < UDC_NEW_GADGET_KERNEL
+        dev->gadget.dev.bus_id = pdev->slot_name;
+#else
+        strcpy (dev->gadget.dev.bus_id, "gadget");
+        dev->gadget.dev.parent = &pdev->dev;
+        dev->gadget.dev.dma_mask = pdev->dev.dma_mask;
+        dev->gadget.dev.release = gadget_release;
+        dev->gadget.name = driver_name;
+
+#endif
+        dev->gadget.name = driver_name;
+
+#ifndef CONFIG_USB_NON_PCI_OTGDEVICE
+        /* now all the pci goodies ... */
+        if (pci_enable_device (pdev) < 0) {
+                retval = -ENODEV;
+                goto done;
+        }
+#endif
+        dev->enabled = 1;
+
+        resource = pci_resource_start (pdev, 0);
+        len = pci_resource_len (pdev, 0);
+
+        if (!request_mem_region (resource, len, driver_name)) {
+                DBG( "controller already in use\n");
+                retval = -EBUSY;
+                goto done;
+        }
+
+        dev->region = 1;
+
+        base = ioremap_nocache (resource, len);
+        if (base == NULL) {
+                DBG( "can't map memory\n");
+                retval = -EFAULT;
+                goto done;
+        }
+
+        /* udc csr registers base */
+        dev->csr = (struct udc_csrs*) (base + UDC_CSR_ADDR);
+        /* dev registers base */
+        dev->regs = (struct udc_regs *) (base + UDC_DEVCFG_ADDR);                
+        /* ep registers base */
+        dev->epregs = (struct udc_epregs *) (base + UDC_EPREGS_ADDR);                
+        /* fifo's base */
+        dev->rxfifo = (u32*) (base + UDC_RXFIFO_ADDR);                
+        dev->txfifo = (u32*) (base + UDC_TXFIFO_ADDR);                
+
+        /* init registers, interrupts, ... */
+#ifndef UDC_IPBUG_3943_WORKAROUND
+        startup_registers(dev);
+#else
+        {
+                u32 tmp;
+
+                /* TODO put this to extra function,
+                 * this all is extracted from usb_init() and
+                 * usb_reset() but without register access */
+                dev->gadget.ep0 = &dev->ep [UDC_EP0IN_IX].ep;
+                dev->ep [UDC_EP0IN_IX].stopped = 0;
+                INIT_LIST_HEAD (&dev->gadget.ep0->ep_list);
+                dev->gadget.speed = USB_SPEED_HIGH;
+                /* standard fifo and endpoint allocations */
+                set_fifo_mode (dev, 0);
+                /* basic endpoint init */
+                for (tmp = 0; tmp < UDC_EP_NUM; tmp++) {
+                        struct udc_ep   *ep = &dev->ep [tmp];
+
+                        ep->ep.name = ep_name [tmp];
+                        ep->dev = dev;
+                        ep->num = tmp;
+                        /* txfifo size is calculated at enable time */
+                        ep->txfifo = dev->txfifo;
+
+                        /* fifo size */
+                        if (tmp < UDC_EPIN_NUM) {
+                                ep->fifo_size = UDC_TXFIFO_SIZE;
+                                ep->is_in = 1;
+                        }
+                        else {
+                                ep->fifo_size = UDC_RXFIFO_SIZE;
+                                ep->is_in = 0;
+
+                        }
+
+                        ep->regs = &dev->epregs [tmp];
+                        if (!ep->desc) {
+                                ep->desc = 0;
+                                INIT_LIST_HEAD (&ep->queue);
+
+                                ep->ep.maxpacket = ~0;
+                                ep->ep.ops = &udc_ep_ops;
+                        }
+                }
+                dev->ep [UDC_EP0IN_IX].ep.maxpacket = UDC_EP0IN_MAX_PKT_SIZE;
+                dev->ep [UDC_EP0OUT_IX].ep.maxpacket = UDC_EP0OUT_MAX_PKT_SIZE;
+        }
+#endif
+
+        /* irq setup after old hardware is cleaned up */
+        if (!pdev->irq) {
+                ERR( "No IRQ.  Check PCI setup!\n");
+                retval = -ENODEV;
+                goto done;
+        }
+#ifndef __sparc__
+        snprintf (buf, sizeof buf, "%d", pdev->irq);
+        bufp = buf;
+#else
+        bufp = __irq_itoa(pdev->irq);
+#endif
+        if (request_irq (pdev->irq, udc_irq, SA_SHIRQ, driver_name, dev)
+                        != 0) {
+                ERR( "request interrupt %s failed\n", bufp);
+                retval = -EBUSY;
+                goto done;
+        }
+        dev->got_irq = 1;
+
+        if (use_dma) {
+                /* consistent DMA mode setting ? */
+                if (use_dma_ppb) {
+                        use_dma_bufferfill_mode = 0;
+                }
+                else {
+                        use_dma_ppb_du = 0;
+                        use_dma_bufferfill_mode = 1;
+                }
+                /* DMA setup */
+#if     LINUX_VERSION_CODE >= UDC_NEW_GADGET_KERNEL
+                dev->data_requests = pci_pool_create ("data_requests", pdev,
+                        sizeof (struct udc_data_dma),
+                        UDC_PCIPOOL_ALIGN,
+                        0 /* or page-crossing issues */ );
+#else
+                dev->data_requests = pci_pool_create ("data_requests", pdev,
+                        sizeof (struct udc_data_dma),
+                        UDC_PCIPOOL_ALIGN,
+                        0 /* or page-crossing issues */,
+                        SLAB_KERNEL /* 2.4 only */ );
+#endif
+                if (!dev->data_requests) {
+                        DBG( "can't get request data pool\n");
+                        retval = -ENOMEM;
+                        goto done;
+                }
+
+                /* EP0 in dma regs = dev control regs */        
+                dev->ep[UDC_EP0IN_IX].dma = &dev->regs->ctl;       
+
+                /* dma desc for setup data */
+#if     LINUX_VERSION_CODE >= UDC_NEW_GADGET_KERNEL
+                dev->stp_requests = pci_pool_create ("setup requests", pdev,
+                        sizeof (struct udc_stp_dma),
+                        UDC_PCIPOOL_ALIGN,
+                        0 /* or page-crossing issues */ );
+#else
+                dev->stp_requests = pci_pool_create ("setup requests", pdev,
+                        sizeof (struct udc_stp_dma),
+                        UDC_PCIPOOL_ALIGN,
+                        0 /* or page-crossing issues */,
+                        SLAB_KERNEL /* 2.4 only */ );
+#endif
+                if (!dev->stp_requests) {
+                        DBG( "can't get stp request pool\n");
+                        retval = -ENOMEM;
+                        goto done;
+                }
+                /* setup */
+                td_stp = pci_pool_alloc (dev->stp_requests, UDC_PCIPOOL_GFP_STP,
+                                        &dev->ep [UDC_EP0OUT_IX].td_stp_dma);
+                dev->ep [UDC_EP0OUT_IX].td_stp = td_stp;
+                /* data: 0 packets !? */
+                td_data = pci_pool_alloc (dev->stp_requests, UDC_PCIPOOL_GFP_STP,
+                                        &dev->ep [UDC_EP0OUT_IX].td_dma);
+                dev->ep [UDC_EP0OUT_IX].td = td_data;
+#ifdef UDC_IPDEFECT_9000004946_WORKAROUND
+                /* point to itself */
+                dev->ep [UDC_EP0OUT_IX].td->next = dev->ep [UDC_EP0OUT_IX].td_dma;
+#endif
+        }
+
+        dev->chiprev = 0;
+
+        /* normally Linux inits PCI_LATENCY_TIMER */
+#ifndef CONFIG_USB_NON_PCI_OTGDEVICE
+        pci_set_master (pdev);
+#ifdef  HAVE_PCI_SET_MWI
+        pci_set_mwi (pdev);
+#endif
+        /* chip rev for Hiddensee */
+        pci_read_config_byte(pdev, PCI_REVISION_ID, (u8*) &dev->chiprev);
+#else
+        /* chip rev for Au1200 */
+        dev->chiprev = (u16) read_c0_prid() & 0xff;
+#endif
+
+        /* done */
+        pci_set_drvdata (pdev, dev);
+
+        INFO( "%s\n", driver_desc);
+        INFO( "irq %s, pci mem %08lx, chip rev %02x\n",
+                        bufp, resource, dev->chiprev);
+        bufp = DRIVER_VERSION;
+#ifdef CONFIG_SOC_AU1200
+#ifdef UDC_AUA1
+        if (dev->chiprev < UDC_AUA1) {
+                ERR("Your chip revision is %d, it must be at least %d\n",
+                    dev->chiprev, UDC_AUA1);
+                retval = -ENODEV;
+                goto done;
+        }
+        INFO("driver version: %s (for AU1200 A1)\n", bufp);
+#else
+        INFO("driver version: %s (for AU1200 A0)\n", bufp);
+#endif
+#else
+#ifdef UDC_HSB0
+        INFO("driver version: %s (for Geode5536 B0)\n", bufp);
+#else
+        INFO("driver version: %s (for Geode5536 A0)\n", bufp);
+#endif
+#endif
+        the_controller = dev;
+
+#if     LINUX_VERSION_CODE >= UDC_NEW_GADGET_KERNEL
+        device_register (&dev->gadget.dev);
+        device_create_file (&pdev->dev, &dev_attr_registers);
+#endif
+
+        /* print dev register info */
+        print_regs(dev);
+
+        return 0;
+
+done:
+        if (dev)
+                udc_remove (pdev);
+        return retval;
+}
+
+/**
+ * Inits driver
+ *
+ * \return 0 if success
+ */
+static int __init init (void)
+{
+        int rc;
+
+#ifdef DEBUG
+        /* register char device */
+        rc = register_chrdev(UDC_MAJOR_NUM, UDC_DEVICE_NAME, &udc_fops);
+        if (rc < 0)
+        {
+                printk("Error registering udc char device");
+        }
+#endif        
+
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE
+        rc = udc_probe(pdev, pci_ids);
+#else
+        rc = pci_module_init (&udc_pci_driver);
+#endif
+
+        return rc;
+}
+module_init (init);
+
+/**
+ * Cleans driver
+ */
+static void __exit cleanup (void)
+{
+#ifdef DEBUG
+        /* unregister char device */
+        unregister_chrdev(UDC_MAJOR_NUM, UDC_DEVICE_NAME);
+#endif
+
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE
+        udc_remove(pdev);
+#else
+        pci_unregister_driver (&udc_pci_driver);
+#endif
+
+}
+module_exit (cleanup);
+
+/**
+ *  Prints a buffers contents 
+ *  \param buf          pointer to buffer
+ *  \param bytes        number bytes to print
+ */
+#ifdef DEBUG
+#ifdef NOT_USED
+static void dump_buffer(u8* buf, u32 bytes)
+{
+        int i;
+
+        printk("\nbuffer %lx = %d bytes:\n", (unsigned long) buf, bytes);
+        for (i = 0; i < bytes; i++) {
+                printk("%02x", *(buf + i));
+                if ((i+1)%16 == 0)
+                        printk("\n");
+        }
+        printk("\n");
+}
+#endif
+#endif
diff -Nur linux-2.4.30/drivers/usb/gadget/amd5536udc.h linux-2.4.30-linux-mips.org/drivers/usb/gadget/amd5536udc.h
--- linux-2.4.30/drivers/usb/gadget/amd5536udc.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/usb/gadget/amd5536udc.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,917 @@
+/*
+ * Header for Linux Driver for AMD 5536 UDC 
+ * high/full speed USB device controller
+ */
+
+/*
+ * Copyright (C) 2005 AMD (http://www.amd.com)
+ * Author: Thomas Dahlmann
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef AMD5536UDC_H
+#define AMD5536UDC_H
+
+/*****************************************************************************
+*  Constants
+*****************************************************************************/
+
+/* Driver  constants -------------------------------------------------------*/
+#define UDC_DRIVER_NAME "amd5536udc"
+
+/* PCI constants -----------------------------------------------------------*/
+#define UDC_PCI_VENDOR_ID 0x1022
+#define UDC_PCI_DEVICE_ID 0x2096
+
+/* Platform specific -------------------------------------------------------*/
+#if defined(CONFIG_MIPS)
+#define UDC_PCIPOOL_ALIGN       4096
+#define UDC_PCIPOOL_GFP_STP     (GFP_ATOMIC | GFP_DMA)
+#else
+#define UDC_PCIPOOL_ALIGN       0 
+#define UDC_PCIPOOL_GFP_STP     (GFP_KERNEL)
+#endif
+
+/* temp define for AU1200, will live in au1000.h normally */
+#ifndef USB_UDC_BASE
+#define USB_UDC_BASE              0x14022000
+#define USB_UDC_LEN               0x2000
+#define USB_MSR_BASE              0xB4020000
+#define USB_MSR_MCFG              4
+#define USBMSRMCFG_DMEMEN         4
+#define USBMSRMCFG_DBMEN          5
+#define USBMSRMCFG_UDCCLKEN       18
+#define AU1200_USB_INT            29
+#endif
+
+/* Special optimization for certain gadgets ------------------------------- */ 
+/* use hardware NAK if IN queue empty, this can be used to 
+* avoid interrupt flood as for the ethernet gadget where host side
+* sends IN tokens permamently */
+#define UDC_DISABLE_IRQ_IF_EMPTY_IN_QUEUE
+/*#undef UDC_DISABLE_IRQ_IF_EMPTY_IN_QUEUE*/                                       
+
+/* IP core defect/bug/case workarounds ---------------------------------------- */
+/* Hiddensee A0 workarounds */
+/* Description: "Last descriptor loop */
+#define UDC_IPDEFECT_9000004946_WORKAROUND
+/* Description: "Resume from STALL/CLEAR_FEATURE HALT */
+/* STALL not required so far by any gadget, so disabled because SOF int. is used
+   in workaround which drops performance */
+#undef  UDC_IPCASE_8000009363_WORKAROUND
+/* Description: "DMA machine not restartable" */
+#define UDC_IPCASE_8000018724_WORKAROUND
+/* Description: "UDC not accessible when phy is suspended */
+#define UDC_IPBUG_3943_WORKAROUND
+/* Description: "USB device needs session-valid status information */
+#define UDC_IPBUG_3950_WORKAROUND
+/* Description: "Clearing endpoint NAK bits not possible if RxFIFO
+   not empty (PCS#1503) */
+#define UDC_IPBUG_3958_WORKAROUND
+#ifdef UDC_IPBUG_3958_WORKAROUND
+/* option 1: flush rxfifo on USB reset */
+#undef UDC_IPBUG_3958_WORKAROUND_RXFIFO_FLUSH_ON_USBRESET
+/* option 2: soft reset on USB reset */
+#ifndef UDC_IPBUG_3958_WORKAROUND_RXFIFO_FLUSH_ON_USBRESET
+#define UDC_IPBUG_3958_WORKAROUND_SOFT_RESET_ON_USBRESET
+#endif
+#endif
+/* UDC data errors during loop test (DMA) 
+   - found only with Travis but used for Hiddensee too, bug entry
+     is in Travis bugzilla
+   - the workaround applies only for small packets */
+#define UDC_IPBUG_2253_WORKAROUND
+#define UDC_SMALL_PACKET        32
+
+/* Disable workarounds which don't apply to Hiddensee B0/ Au1200 A1 */
+/* And enable new features */ 
+/* Hiddensee B0 flag */
+#define UDC_HSB0
+#if defined(CONFIG_MIPS)
+#undef UDC_HSB0
+/* Au1200 A1 flag */
+#define UDC_AUA1 1
+#endif
+
+/* disable workarounds for newer chip revisions */
+#ifdef UDC_HSB0
+#undef UDC_IPBUG_3943_WORKAROUND
+#undef UDC_IPBUG_3950_WORKAROUND
+#define UDC_DMARST_AVAIL
+#endif
+#ifdef UDC_AUA1
+#undef UDC_IPBUG_3943_WORKAROUND
+#endif
+
+/* SETUP usb commands 
+*  needed, because some SETUP's are handled in hw, but must be passed to 
+*  gadget driver above -----------------------------------------------------*/
+/* SET_CONFIG */
+#define UDC_SETCONFIG_DWORD0                    0x00000900
+#define UDC_SETCONFIG_DWORD0_VALUE_MASK         0xffff0000 
+#define UDC_SETCONFIG_DWORD0_VALUE_OFS          16 
+
+#define UDC_SETCONFIG_DWORD1                    0x00000000
+
+/* SET_INTERFACE */
+#define UDC_SETINTF_DWORD0                      0x00000b00
+#define UDC_SETINTF_DWORD0_ALT_MASK             0xffff0000 
+#define UDC_SETINTF_DWORD0_ALT_OFS              16
+
+#define UDC_SETINTF_DWORD1                      0x00000000
+#define UDC_SETINTF_DWORD1_INTF_MASK            0x0000ffff 
+#define UDC_SETINTF_DWORD1_INTF_OFS             0 
+
+/* Global CSR's -------------------------------------------------------------*/
+/* UDC CSR's */
+#define UDC_CSR_ADDR                            0x500 
+
+/* EP NE bits */
+/* EP number */
+#define UDC_CSR_NE_NUM_MASK                     0x0000000f 
+#define UDC_CSR_NE_NUM_OFS                      0 
+/* EP direction */
+#define UDC_CSR_NE_DIR_MASK                     0x00000010 
+#define UDC_CSR_NE_DIR_OFS                      4 
+/* EP type */
+#define UDC_CSR_NE_TYPE_MASK                    0x00000060
+#define UDC_CSR_NE_TYPE_OFS                     5
+/* EP config number */
+#define UDC_CSR_NE_CFG_MASK                     0x00000780
+#define UDC_CSR_NE_CFG_OFS                      7
+/* EP interface number */
+#define UDC_CSR_NE_INTF_MASK                    0x00007800
+#define UDC_CSR_NE_INTF_OFS                     11 
+/* EP alt setting */
+#define UDC_CSR_NE_ALT_MASK                     0x00078000
+#define UDC_CSR_NE_ALT_OFS                      15
+ 
+/* max pkt */
+#define UDC_CSR_NE_MAX_PKT_MASK                 0x3ff80000 
+#define UDC_CSR_NE_MAX_PKT_OFS                  19 
+
+/* Device Config Register ---------------------------------------------------*/
+#define UDC_DEVCFG_ADDR                         0x400 
+
+#define UDC_DEVCFG_SOFTRESET                    31
+#define UDC_DEVCFG_HNPSFEN                      30
+#define UDC_DEVCFG_DMARST                       29
+#define UDC_DEVCFG_SET_DESC                     18
+#define UDC_DEVCFG_CSR_PRG                      17
+#define UDC_DEVCFG_STATUS                       7
+#define UDC_DEVCFG_DIR                          6
+#define UDC_DEVCFG_PI                           5
+#define UDC_DEVCFG_SS                           4
+#define UDC_DEVCFG_SP                           3
+#define UDC_DEVCFG_RWKP                         2
+
+#define UDC_DEVCFG_SPD_MASK                     0x3
+#define UDC_DEVCFG_SPD_OFS                      0
+#define UDC_DEVCFG_SPD_HS                       0x0
+#define UDC_DEVCFG_SPD_FS                       0x1
+#define UDC_DEVCFG_SPD_LS                       0x2
+/*#define UDC_DEVCFG_SPD_FS                     0x3*/
+
+
+/* Device Control Register --------------------------------------------------*/
+#define UDC_DEVCTL_ADDR                         0x404 
+
+#define UDC_DEVCTL_THLEN_MASK                   0xff000000 
+#define UDC_DEVCTL_THLEN_OFS                    24 
+
+#define UDC_DEVCTL_BRLEN_MASK                   0x00ff0000 
+#define UDC_DEVCTL_BRLEN_OFS                    16 
+
+#define UDC_DEVCTL_CSR_DONE                     13
+#define UDC_DEVCTL_DEVNAK                       12
+#define UDC_DEVCTL_SD                           10
+#define UDC_DEVCTL_MODE                         9 
+#define UDC_DEVCTL_BREN                         8
+#define UDC_DEVCTL_THE                          7
+#define UDC_DEVCTL_BF                           6
+#define UDC_DEVCTL_BE                           5
+#define UDC_DEVCTL_DU                           4
+#define UDC_DEVCTL_TDE                          3
+#define UDC_DEVCTL_RDE                          2
+#define UDC_DEVCTL_RES                          0
+
+
+/* Device Status Register ---------------------------------------------------*/
+#define UDC_DEVSTS_ADDR                         0x408
+
+#define UDC_DEVSTS_TS_MASK                      0xfffc0000
+#define UDC_DEVSTS_TS_OFS                       18
+
+#define UDC_DEVSTS_SESSVLD                      17
+#define UDC_DEVSTS_PHY_ERROR                    16
+#define UDC_DEVSTS_RXFIFO_EMPTY                 15
+
+#define UDC_DEVSTS_ENUM_SPEED_MASK              0x00006000
+#define UDC_DEVSTS_ENUM_SPEED_OFS               13
+#define UDC_DEVSTS_ENUM_SPEED_FULL              1
+#define UDC_DEVSTS_ENUM_SPEED_HIGH              0
+
+#define UDC_DEVSTS_SUSP                         12
+
+#define UDC_DEVSTS_ALT_MASK                     0x00000f00
+#define UDC_DEVSTS_ALT_OFS                      8
+
+#define UDC_DEVSTS_INTF_MASK                    0x000000f0
+#define UDC_DEVSTS_INTF_OFS                     4
+
+#define UDC_DEVSTS_CFG_MASK                     0x0000000f
+#define UDC_DEVSTS_CFG_OFS                      0
+
+
+/* Device Interrupt Register ------------------------------------------------*/
+#define UDC_DEVINT_ADDR                         0x40c
+
+#define UDC_DEVINT_SVC                          7
+#define UDC_DEVINT_ENUM                         6
+#define UDC_DEVINT_SOF                          5
+#define UDC_DEVINT_US                           4
+#define UDC_DEVINT_UR                           3
+#define UDC_DEVINT_ES                           2
+#define UDC_DEVINT_SI                           1
+#define UDC_DEVINT_SC                           0
+
+/* Device Interrupt Mask Register -------------------------------------------*/
+#define UDC_DEVINT_MSK_ADDR                     0x410
+
+#define UDC_DEVINT_MSK                          0x7f
+
+/* Endpoint Interrupt Register ----------------------------------------------*/
+#define UDC_EPINT_ADDR                          0x414           
+
+#define UDC_EPINT_OUT_MASK                      0xffff0000           
+#define UDC_EPINT_OUT_OFS                       16           
+#define UDC_EPINT_IN_MASK                       0x0000ffff           
+#define UDC_EPINT_IN_OFS                        0 
+
+#define UDC_EPINT_IN_EP0                        0 
+#define UDC_EPINT_IN_EP1                        1 
+#define UDC_EPINT_IN_EP2                        2 
+#define UDC_EPINT_IN_EP3                        3 
+#define UDC_EPINT_OUT_EP0                       16 
+#define UDC_EPINT_OUT_EP1                       17
+#define UDC_EPINT_OUT_EP2                       18 
+#define UDC_EPINT_OUT_EP3                       19 
+
+#define UDC_EPINT_EP0_ENABLE_MSK                0x001e001e    
+
+/* Endpoint Interrupt Mask Register -----------------------------------------*/
+#define UDC_EPINT_MSK_ADDR                      0x418           
+
+#define UDC_EPINT_OUT_MSK_MASK                  0xffff0000           
+#define UDC_EPINT_OUT_MSK_OFS                   16           
+#define UDC_EPINT_IN_MSK_MASK                   0x0000ffff           
+#define UDC_EPINT_IN_MSK_OFS                    0 
+
+#define UDC_EPINT_MSK_DISABLE_ALL               0xffffffff
+/* mask non-EP0 endpoints */
+#define UDC_EPDATAINT_MSK_DISABLE               0xfffefffe
+/* mask all dev interrupts */
+#define UDC_DEV_MSK_DISABLE                     0x7f
+
+/* Endpoint-specific CSR's --------------------------------------------------*/
+/* Endpoint Control Registers -----------------------------------------------*/
+#define UDC_EPREGS_ADDR                         0x0
+#define UDC_EPIN_REGS_ADDR                      0x0
+#define UDC_EPOUT_REGS_ADDR                     0x200
+
+#define UDC_EPCTL_ADDR                          0x0
+
+#define UDC_EPCTL_RRDY                          9
+#define UDC_EPCTL_CNAK                          8
+#define UDC_EPCTL_SNAK                          7
+#define UDC_EPCTL_NAK                           6
+
+#define UDC_EPCTL_ET_MASK                       0x00000030
+#define UDC_EPCTL_ET_OFS                        4
+#define UDC_EPCTL_ET_CONTROL                    0
+#define UDC_EPCTL_ET_ISO                        1 
+#define UDC_EPCTL_ET_BULK                       2
+#define UDC_EPCTL_ET_INTERRUPT                  3
+
+#define UDC_EPCTL_P                             3
+#define UDC_EPCTL_SN                            2
+#define UDC_EPCTL_F                             1
+#define UDC_EPCTL_S                             0
+
+/* Endpoint Status Registers ------------------------------------------------*/
+#define UDC_EPSTS_ADDR                          0x4
+
+#define UDC_EPSTS_RX_PKT_SIZE_MASK              0x007ff800
+#define UDC_EPSTS_RX_PKT_SIZE_OFS               11
+
+#define UDC_EPSTS_TDC                           10
+#define UDC_EPSTS_HE                            9
+#define UDC_EPSTS_BNA                           7
+#define UDC_EPSTS_IN                            6
+
+#define UDC_EPSTS_OUT_MASK                      0x00000030
+#define UDC_EPSTS_OUT_OFS                       4 
+#define UDC_EPSTS_OUT_DATA                      1
+#define UDC_EPSTS_OUT_DATA_CLEAR                0x10
+#define UDC_EPSTS_OUT_SETUP                     2
+#define UDC_EPSTS_OUT_SETUP_CLEAR               0x20
+
+/* Endpoint Buffer Size IN/ Receive Packet Frame Number OUT  Registers ------*/
+#define UDC_EPIN_BUFF_SIZE_ADDR                 0x8
+#define UDC_EPOUT_FRAME_NUMBER_ADDR             0x8
+
+#define UDC_EPIN_BUFF_SIZE_MASK                 0x0000ffff
+#define UDC_EPIN_BUFF_SIZE_OFS                  0
+/*  EP0in txfifo = 128 bytes*/ 
+#define UDC_EPIN0_BUFF_SIZE                     32
+/*  EP0in fullspeed txfifo = 128 bytes*/ 
+#define UDC_FS_EPIN0_BUFF_SIZE                  32
+
+/* fifo size mult = fifo size / max packet */
+#define UDC_EPIN_BUFF_SIZE_MULT                 2
+
+/* EPin data fifo size = 1024 bytes DOUBLE BUFFERING */
+#define UDC_EPIN_BUFF_SIZE                      256 
+/* EPin small INT data fifo size = 128 bytes */
+#define UDC_EPIN_SMALLINT_BUFF_SIZE             32 
+
+/* EPin fullspeed data fifo size = 128 bytes DOUBLE BUFFERING */
+#define UDC_FS_EPIN_BUFF_SIZE                   32 
+
+#define UDC_EPOUT_FRAME_NUMBER_MASK             0x0000ffff
+#define UDC_EPOUT_FRAME_NUMBER_OFS              0 
+
+/* Endpoint Buffer Size OUT/Max Packet Size Registers -----------------------*/
+#define UDC_EPOUT_BUFF_SIZE_ADDR                0x0c
+#define UDC_EP_MAX_PKT_SIZE_ADDR                0x0c
+
+#define UDC_EPOUT_BUFF_SIZE_MASK                0xffff0000
+#define UDC_EPOUT_BUFF_SIZE_OFS                 16 
+#define UDC_EP_MAX_PKT_SIZE_MASK                0x0000ffff
+#define UDC_EP_MAX_PKT_SIZE_OFS                 0
+/* EP0in max packet size = 64 bytes */
+#define UDC_EP0IN_MAX_PKT_SIZE                  64
+/* EP0out max packet size = 64 bytes */
+#define UDC_EP0OUT_MAX_PKT_SIZE                 64
+/* EP0in fullspeed max packet size = 64 bytes */
+#define UDC_FS_EP0IN_MAX_PKT_SIZE               64   
+/* EP0out fullspeed max packet size = 64 bytes */
+#define UDC_FS_EP0OUT_MAX_PKT_SIZE              64
+
+/* Endpoint dma descriptors ------------------------------------------------*/
+/* Setup data */
+/* Status dword */
+#define UDC_DMA_STP_STS_CFG_MASK                0x0fff0000
+#define UDC_DMA_STP_STS_CFG_OFS                 16
+#define UDC_DMA_STP_STS_CFG_ALT_MASK            0x000f0000     
+#define UDC_DMA_STP_STS_CFG_ALT_OFS             16     
+#define UDC_DMA_STP_STS_CFG_INTF_MASK           0x00f00000     
+#define UDC_DMA_STP_STS_CFG_INTF_OFS            20     
+#define UDC_DMA_STP_STS_CFG_NUM_MASK            0x0f000000     
+#define UDC_DMA_STP_STS_CFG_NUM_OFS             24     
+#define UDC_DMA_STP_STS_RX_MASK                 0x30000000
+#define UDC_DMA_STP_STS_RX_OFS                  28 
+#define UDC_DMA_STP_STS_BS_MASK                 0xc0000000
+#define UDC_DMA_STP_STS_BS_OFS                  30 
+#define UDC_DMA_STP_STS_BS_HOST_READY           0 
+#define UDC_DMA_STP_STS_BS_DMA_BUSY             1 
+#define UDC_DMA_STP_STS_BS_DMA_DONE             2 
+#define UDC_DMA_STP_STS_BS_HOST_BUSY            3 
+/* IN data */
+/* Status dword */
+#define UDC_DMA_IN_STS_TXBYTES_MASK            0x0000ffff
+#define UDC_DMA_IN_STS_TXBYTES_OFS             0
+#define UDC_DMA_IN_STS_FRAMENUM_MASK           0x07ff0000
+#define UDC_DMA_IN_STS_FRAMENUM_OFS            0
+#define UDC_DMA_IN_STS_L                       27
+#define UDC_DMA_IN_STS_TX_MASK                 0x30000000
+#define UDC_DMA_IN_STS_TX_OFS                  28 
+#define UDC_DMA_IN_STS_BS_MASK                 0xc0000000
+#define UDC_DMA_IN_STS_BS_OFS                  30 
+#define UDC_DMA_IN_STS_BS_HOST_READY           0 
+#define UDC_DMA_IN_STS_BS_DMA_BUSY             1 
+#define UDC_DMA_IN_STS_BS_DMA_DONE             2 
+#define UDC_DMA_IN_STS_BS_HOST_BUSY            3 
+/* OUT data */
+/* Status dword */
+#define UDC_DMA_OUT_STS_RXBYTES_MASK            0x0000ffff
+#define UDC_DMA_OUT_STS_RXBYTES_OFS             0
+#define UDC_DMA_OUT_STS_FRAMENUM_MASK           0x07ff0000
+#define UDC_DMA_OUT_STS_FRAMENUM_OFS            0
+#define UDC_DMA_OUT_STS_L                       27
+#define UDC_DMA_OUT_STS_RX_MASK                 0x30000000
+#define UDC_DMA_OUT_STS_RX_OFS                  28 
+#define UDC_DMA_OUT_STS_BS_MASK                 0xc0000000
+#define UDC_DMA_OUT_STS_BS_OFS                  30 
+#define UDC_DMA_OUT_STS_BS_HOST_READY           0 
+#define UDC_DMA_OUT_STS_BS_DMA_BUSY             1 
+#define UDC_DMA_OUT_STS_BS_DMA_DONE             2 
+#define UDC_DMA_OUT_STS_BS_HOST_BUSY            3 
+/* other DMA constants */
+/* max dma packet */
+#define UDC_DMA_MAXPACKET                       4096 
+/* DMA buffer len for temp request, should be the same as the upper
+layer gadget is using => TODO replace follwing constant */
+#define UDC_DMA_TEMP_BUFFER_LEN                16384
+/* invalid DMA pointer */
+#define        DMA_ADDR_INVALID        (~(dma_addr_t)0)
+
+/* other Endpoint register addresses and values-----------------------------*/
+#define UDC_EP_SUBPTR_ADDR                      0x10
+#define UDC_EP_DESPTR_ADDR                      0x14
+#define UDC_EP_WRITE_CONFIRM_ADDR               0x1c
+
+/* EP number as layouted in AHB space */
+#define UDC_EP_NUM                              32
+#define UDC_EPIN_NUM                            16
+#define UDC_EPIN_NUM_USED                       5
+#define UDC_EPOUT_NUM                           16
+/* EP number of EP's really used = EP0 + 8 data EP's */
+#define UDC_USED_EP_NUM                         9
+/* UDC CSR regs are aligned but AHB regs not - offset for OUT EP's */
+#define UDC_CSR_EP_OUT_IX_OFS                   12
+
+#define UDC_EP0OUT_IX                           16
+#define UDC_EP0IN_IX                            0
+
+/* max packet */
+#define UDC_HS_BULK_MAXPKT                      512
+//DISABLEDamd #define UDC_EP0_MAXPKT                          64
+/* max packet fullspeed */
+//DISABLEDamd #define UDC_FS_EP0_MAXPKT                       8
+
+/* Rx fifo address and size = 1k -------------------------------------------*/
+#define UDC_RXFIFO_ADDR                         0x800
+#define UDC_RXFIFO_SIZE                         0x400
+
+/* Tx fifo address and size = 1.5k -----------------------------------------*/
+#define UDC_TXFIFO_ADDR                         0xc00
+#define UDC_TXFIFO_SIZE                         0x600
+
+/* default data endpoints --------------------------------------------------*/
+#define UDC_EPIN_STATUS_IX                      1                            
+#define UDC_EPIN_IX                             2                            
+#define UDC_EPOUT_IX                            18                            
+
+/* general constants -------------------------------------------------------*/ 
+#define UDC_DWORD_BYTES                         4
+#define UDC_BITS_PER_BYTE_SHIFT                 3
+#define UDC_BYTE_MASK                           0xff
+#define UDC_BITS_PER_BYTE                       8
+
+/* char device constants ---------------------------------------------------*/
+/* names */
+#ifdef DEBUG
+#ifdef UDC_DRIVER_NAME
+#define UDC_DEVICE_NAME UDC_DRIVER_NAME
+#else
+#define UDC_DEVICE_NAME "amd5536udc"
+#endif
+#define UDC_DEVICE_FILE_NAME "amd5536udc_dev"
+#define UDC_DEVICE_FILE_INODE "/dev/amd5536udc_dev"
+/* major number */
+#define UDC_MAJOR_NUM   240
+/* ioctl */
+#define UDC_IOCTL_READ_EP0OUT_DATA_FROM_FIFO _IOWR(UDC_MAJOR_NUM, 1, int)
+#endif
+
+#ifdef __KERNEL__
+
+/* 2.5 and 2.4.older portability changes ... */
+
+#ifndef container_of
+#define        container_of        list_entry
+#endif
+
+#ifndef likely
+#define likely(x) (x)
+#define unlikely(x) (x)
+#endif
+
+#ifndef BUG_ON
+#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0)
+#endif
+
+#ifndef WARN_ON
+#define        WARN_ON(x)        do { } while (0)
+#endif
+
+#ifndef        IRQ_NONE
+typedef void irqreturn_t;
+#define IRQ_NONE
+#define IRQ_HANDLED
+#define IRQ_RETVAL(x)
+#endif
+
+#endif /*__KERNEL__*/
+
+/*****************************************************************************
+*  Types
+*****************************************************************************/
+
+/* UDC CSR's */
+struct udc_csrs {
+        
+        /* sca - setup command address */
+        u32 sca;
+
+        /* ep ne's */
+        u32 ne[UDC_USED_EP_NUM];
+} __attribute__ ((packed));
+
+/* AHB subsystem CSR registers */
+struct udc_regs {
+
+        /* device configuration */
+        u32 cfg;
+
+        /* device control */
+        u32 ctl;
+
+        /* device status */
+        u32 sts;
+
+        /* device interrupt */
+        u32 irqsts;
+
+        /* device interrupt mask */
+        u32 irqmsk;
+        
+        /* endpoint interrupt  */
+        u32 ep_irqsts;
+
+        /* endpoint interrupt mask */
+        u32 ep_irqmsk;
+} __attribute__ ((packed));
+
+/* endpoint specific registers */
+struct udc_epregs {
+
+        /* endpoint control */
+        u32 ctl;
+
+        /* endpoint status */
+        u32 sts;
+
+        /* endpoint buffer size in/ receive packet frame number out  */
+        u32 bufin_framenum;
+
+        /* endpoint buffer size out/max packet size */
+        u32 bufout_maxpkt;        
+
+        /* endpoint setup buffer pointer */
+        u32 subptr; 
+        
+        /* endpoint data descriptor pointer */
+        u32 desptr;
+
+        /* reserverd */
+        u32 reserved;
+
+        /* write/read confirmation */
+        u32 confirm;
+
+}  __attribute__ ((packed));
+
+#ifdef        __KERNEL__
+/* control data DMA desc */
+struct udc_stp_dma {
+        /* status quadlet */
+        u32     dmastat;
+        /* reserved */
+        u32     _reserved;
+        /* first setup word */
+        u32     data12;
+        /* second setup word */
+        u32     data34;
+} __attribute__ ((aligned (16)));
+
+/* normal data DMA desc */
+struct udc_data_dma {
+        /* status quadlet */
+        u32     dmastat;
+        /* reserved */
+        u32     _reserved;
+        /* buffer pointer */
+        u32     bufptr;
+        /* next descriptor pointer */
+        u32     next;
+} __attribute__ ((aligned (16)));
+
+/* request packet */
+struct udc_request {
+        struct usb_request                  req;
+
+        /* first dma desc. of chain */
+        struct udc_data_dma                 *td_data;
+        /* last dma desc. of chain */
+        struct udc_data_dma                 *td_data_last;
+#ifdef UDC_IPDEFECT_9000004946_WORKAROUND
+        /* next pointer of broken chain */
+        dma_addr_t                          td_data_last_next; 
+#endif
+        /* chain length */
+        unsigned                            chain_len;
+
+        dma_addr_t                          td_dma;
+        struct list_head                    queue;
+        unsigned                            mapped : 1,
+                                            dma_done : 1,
+                                            dma_pend : 1,
+                                            valid : 1;
+};
+
+/* UDC specific endpoint parameters */
+struct udc_ep {
+        struct usb_ep                       ep;
+        struct udc_epregs                   *regs;
+        u32                                 *txfifo;
+        u32*                                dma;
+        dma_addr_t                          td_dma;
+        dma_addr_t                          td_stp_dma;        
+        struct udc_stp_dma                  *td_stp;
+        struct udc_data_dma                 *td;
+        /* temp request */
+        struct udc_request                  *req;
+        unsigned                            req_used;        
+        unsigned                            req_completed;        
+        /* NAK state */
+        unsigned                            naking;        
+
+
+
+        struct udc                          *dev;
+        unsigned long                       irqs;
+
+        /* analogous to a host-side qh */
+        struct list_head                        queue;
+        const struct usb_endpoint_descriptor    *desc;
+        unsigned                                num : 8,
+                                                fifo_size : 12,
+                                                in_fifo_validate : 1,
+                                                stopped : 1,
+                                                is_in : 1,
+                                                is_iso : 1;
+};
+
+/* device struct */
+struct udc {
+        /* each pci device provides one gadget, several endpoints */
+        struct usb_gadget               gadget;
+        spinlock_t                      lock;
+        struct udc_ep                   ep [UDC_EP_NUM];
+        struct usb_gadget_driver        *driver;
+        struct usb_gadget_otg_extension *otg_driver;
+        unsigned                        enabled : 1,
+                                        protocol_stall : 1,
+                                        got_irq : 1,
+                                        region : 1,
+                                        otg_supported : 1,
+                                        selfpowered : 1;
+        u16                             chiprev;
+
+        /* pci state used to access those endpoints */
+        struct pci_dev                  *pdev;
+        struct udc_csrs                 *csr;
+        struct udc_regs                 *regs;
+        struct udc_epregs               *epregs;
+        u32*                            rxfifo;
+        u32*                            txfifo;
+
+        /* DMA desc pools */
+        struct pci_pool                 *data_requests;
+        struct pci_pool                 *stp_requests;
+
+        /* states */
+        u16                             cur_config;
+        u16                             cur_intf;
+        u16                             cur_alt;
+};
+
+/* setup request data */
+union udc_setup_data {
+        u32                        raw [2];
+        struct usb_ctrlrequest     r;
+};
+#endif /*__KERNEL__*/
+
+/*****************************************************************************
+*  Macros
+*****************************************************************************/
+
+/***************************************
+* SET and GET bitfields in u32 values
+* via constants for mask/offset:
+* <bit_field_stub_name> is the text between
+* UDC_ and _MASK|_OFS of appropiate
+* constant
+****************************************/
+/* set bitfield value in u32 u32Val */
+#define AMD_ADDBITS(u32Val, bitfield_val, bitfield_stub_name)\
+        (((u32Val) & (((u32) ~((u32) bitfield_stub_name##_MASK))))\
+        |(((bitfield_val) << ((u32) bitfield_stub_name##_OFS))\
+        & ((u32) bitfield_stub_name##_MASK)))
+
+/* set bitfield value in zero-initialized u32 u32Val */
+/* => bitfield bits in u32Val are all zero */
+#define AMD_INIT_SETBITS(u32Val, bitfield_val, bitfield_stub_name)\
+        ((u32Val)\
+        |(((bitfield_val) << ((u32) bitfield_stub_name##_OFS))\
+        &((u32) bitfield_stub_name##_MASK)))
+
+/* get bitfield value from u32 u32Val */
+#define AMD_GETBITS(u32Val, bitfield_stub_name)\
+        ((u32Val & ((u32) bitfield_stub_name##_MASK))\
+        >> ((u32) bitfield_stub_name##_OFS))
+
+/* SET and GET bits in u32 values ------------------------------------------*/
+#define AMD_BIT(bit_stub_name) (1 << bit_stub_name)
+#define AMD_UNMASK_BIT(bit_stub_name) (~AMD_BIT(bit_stub_name))
+#define AMD_CLEAR_BIT(bit_stub_name) (~AMD_BIT(bit_stub_name))
+
+/* misc --------------------------------------------------------------------*/
+#define        DIR_STRING(bAddress) (((bAddress) & USB_DIR_IN) ? "in" : "out")
+
+/* UDC specific macros -----------------------------------------------------*/
+#ifdef UDC_DMARST_AVAIL
+#define UDC_DMARST(tmp, dev) \
+        DBG("DMA machine reset\n"); \
+        tmp = readl(&dev->regs->cfg); \
+        writel(tmp | AMD_BIT(UDC_DEVCFG_DMARST), &dev->regs->cfg); \
+        writel(tmp, &dev->regs->cfg);
+#else
+#define UDC_DMARST(tmp, dev) {} 
+#endif
+
+/* print macros ------------------------------------------------------------*/
+
+#ifdef VERBOSE
+#ifndef DEBUG
+#define DEBUG
+#endif
+#endif
+
+/* Driver name for printing messages, this should be defined by driver */
+#ifndef DRIVER_NAME_FOR_PRINT
+//#define DRIVER_NAME_FOR_PRINT "amd5536"
+#endif
+
+/**
+ * \brief
+ * Macro for printing information in drivers
+ * 
+ * This macro is used for printing kernel messages in driver source code.
+ * It should be used for printing useful information about states and called
+ * functions for normal operation (not for errors and warnings).
+ * 
+ * \param fmt is format string for printk
+ * \param args... are arguments given to printk (number depends on <fmt>)
+ * \return code from printk
+ */
+#define INFO(args...) \
+        printk(KERN_INFO DRIVER_NAME_FOR_PRINT ": " args)
+
+/**
+ * \brief
+ * Macro for printing warnings in drivers
+ * 
+ * This macro is used for printing kernel messages in driver source code.
+ * It should be used for printing warnings.
+ * 
+ * \param fmt is format string for printk
+ * \param args... are arguments given to printk (number depends on <fmt>)
+ * \return code from printk
+ */
+#define WARN(args...) \
+        printk(KERN_WARNING DRIVER_NAME_FOR_PRINT " warning: " args)
+
+/**
+ * \brief
+ * Macro for printing errors in drivers
+ * 
+ * This macro is used for printing kernel messages in driver source code.
+ * It should be used for printing errors.
+ * 
+ * \param fmt is format string for printk
+ * \param args... are arguments given to printk (number depends on <fmt>)
+ * \return code from printk
+ */
+#define ERR(args...) \
+        printk(KERN_ERR DRIVER_NAME_FOR_PRINT " error: " args)
+
+/**
+ * \brief
+ * Macro for printing debug messages in drivers
+ * 
+ * This macro is used for printing kernel messages in driver source code
+ * when DEBUG is defined 
+ * It should be used for printing debug messages.
+ * 
+ * \param fmt is format string for printk
+ * \param args... are arguments given to printk (number depends on <fmt>)
+ * \return code from printk
+ */
+#ifdef DEBUG
+#define DBG(args...) \
+        printk(KERN_DEBUG DRIVER_NAME_FOR_PRINT " debug: " args)
+#else
+
+#define DBG(args...) \
+        do {} while (0)
+#endif
+
+/**
+ * \brief
+ * Macro for printing verbose debug messages in drivers
+ * 
+ * This macro is used for printing kernel messages in driver source code
+ * when DEBUG and VERBOSE is defined
+ * It should be used for printing debug messages.
+ * 
+ * \param fmt is format string for printk
+ * \param args... are arguments given to printk (number depends on <fmt>)
+ * \return code from printk
+ */
+#ifdef VERBOSE
+#define VDBG DBG
+#else
+#define VDBG(args...) \
+	do { } while (0)
+#endif
+
+/*****************************************************************************
+*  Data
+*****************************************************************************/
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+
+/*****************************************************************************
+*  Functions
+*****************************************************************************/
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+
+/*****************************************************************************
+*  Inline Functions
+*****************************************************************************/
+
+/**
+ * allow gadget some resting by halting
+ * 
+ * \param ep           pointer to ep struct
+ */
+static inline void allow_status (struct udc_ep *ep)
+{
+        ep->stopped = 1;
+}
+
+#ifdef        __KERNEL__
+/**
+ * stop endpoint NAK 
+ * 
+ * \param ep           pointer to ep struct
+ */
+static inline void stop_out_naking (struct udc_ep *ep)
+{
+        u32        tmp;
+        /* clear NAK by writing CNAK */
+        tmp = readl(&ep->regs->ctl);
+                tmp |= AMD_BIT(UDC_EPCTL_CNAK); 
+                writel(tmp, &ep->regs->ctl);
+                tmp = readl(&ep->regs->ctl);
+}
+#endif        /* __KERNEL__ */
+
+#endif /* #ifdef  AMD5536UDC_H */
diff -Nur linux-2.4.30/drivers/usb/gadget/amd5536uoc.c linux-2.4.30-linux-mips.org/drivers/usb/gadget/amd5536uoc.c
--- linux-2.4.30/drivers/usb/gadget/amd5536uoc.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/usb/gadget/amd5536uoc.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,1394 @@
+/*
+ * AMD 5536 USB OTG controller driver
+ */
+
+/*
+ * Copyright (C) 2005 AMD (http://www.amd.com)
+ * Author: Karsten Boge 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+/*****************************************************************************
+ *  Includes
+ *****************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/version.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/smp_lock.h>
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/list.h>
+#include <linux/interrupt.h>
+
+#include <asm/byteorder.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/unaligned.h>
+
+#ifdef CONFIG_TEST_INTERFACE
+#ifndef CONFIG_USB_OTG
+#define CONFIG_USB_OTG
+#endif
+#endif
+/* #define DEBUG */
+/* #define VERBOSE */
+
+#include <linux/usb_otg.h>
+#include <linux/usb.h>
+
+#define	DRIVER_DESC		"AMD 5536 USB OTG Controller"
+#define DRIVER_VERSION          "01.00.0000 - kernel 2.4.29, $Revision: #115 $"
+#define DRIVER_NAME_FOR_PRINT   OTG_DRIVER_NAME
+#include "amd5536uoc.h"
+
+
+/*****************************************************************************
+ *  Function Declarations
+ *****************************************************************************/
+
+#ifdef CONFIG_TEST_INTERFACE
+static int otg_test_bind(struct otg **);
+static int otg_test_unbind(struct otg **);
+#endif
+#ifdef CONFIG_USB_OTG
+static int otg_bind_host(struct usb_bus *);
+static int otg_unbind_host(struct usb_bus *);
+static int otg_app_request(u32);
+#endif
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET)
+static int otg_bind_gadget(void);
+static int otg_unbind_gadget(void);
+static u32 otg_app_query(int);
+#endif
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_HSA0_GADGET)
+static inline void otg_req_state_chg (struct otg *, u32);
+#endif
+
+
+/*****************************************************************************
+ *  Data
+ *****************************************************************************/
+
+static const char driver_name [] = OTG_DRIVER_NAME;
+static const char driver_desc [] = DRIVER_DESC;
+
+static struct otg *the_controller;
+
+static u32 init_state = 0;
+
+#ifdef CONFIG_TEST_INTERFACE
+static struct usb_test_otg_extension otg_test_driver = {
+	.bind    = otg_test_bind,
+	.unbind  = otg_test_unbind,
+	.request = otg_app_request,
+	.query   = otg_app_query
+};
+#endif
+
+#ifdef CONFIG_USB_OTGMUX_GADGET
+static struct usb_gadget_otg_extension otg_gadget_driver = {
+	.bind    = otg_bind_gadget,
+	.unbind  = otg_unbind_gadget,
+	.request = NULL,
+	.query   = otg_app_query,
+	.notify  = NULL
+};
+#endif
+
+#ifdef CONFIG_USB_OTG
+static struct usbcore_otg_extension otg_host_driver = {
+	.bind    = otg_bind_host,
+	.unbind  = otg_unbind_host,
+	.request = otg_app_request,
+	.query   = otg_app_query
+};
+
+static struct usb_gadget_otg_extension otg_gadget_driver = {
+	.bind    = otg_bind_gadget,
+	.unbind  = otg_unbind_gadget,
+	.request = otg_app_request,
+	.query   = otg_app_query,
+	.notify  = NULL
+};
+#endif /* CONFIG_USB_OTG */
+
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_HSA0_GADGET)
+static struct otg_ctl __otg_ctl = {
+	.act_state  = OTG_NOT_ASSIGNED,
+	.prv_state  = OTG_NOT_ASSIGNED,
+	.params     = 0,
+};
+static struct otg_ctl *otg_ctl = &__otg_ctl;
+
+static u32 state_mask;
+static u32 high_count = 0;
+#endif
+
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE  /* Au1200 */
+static struct pci_dev pdev_dummy;
+
+static au1x00_gpio_bit_ctl init_usb_vbus_switch = {
+	.direction = 1,  /* out */
+	.data      = 1   /* set */
+};
+#endif
+
+
+/*****************************************************************************
+ *  Function Definitions
+ *****************************************************************************/
+
+#if defined(CONFIG_USB_OTGMUX_IDPIN) || defined(CONFIG_USB_HSA0_GADGET)
+/**
+ * \brief
+ * OTG state change
+ *
+ * subset of OTG states to support the gadget only or
+ * ID pin configuration
+ *
+ * \param  dev          OTG controller info
+ * \param  event_code   event that requested a state change
+ * \param  pEvt_mask
+ *
+ * \return              events that were not handled here
+ */
+u32 otg_change_state (struct otg *dev, u32 _event, u32 *pEvt_mask)
+{
+	u32  event_code = _event;
+	u32  uoc_status = get_status (dev);
+
+	if (GOT_EVENT (OTG_GADGET_READY, event_code) &&
+	    ((otg_ctl->act_state & OTG_STATE_MASK) != OTG_NOT_ASSIGNED)) {
+
+		/* Switch from "NO_B_DEVICE" states to normal operation or   */
+		/*       deactivate operations in case gadget was unloaded   */
+
+		CHANGE_STATE (dev, OTG_NOT_ASSIGNED, pEvt_mask);
+	}
+	if ((OTG_INT_TMX & event_code) && high_count) {
+
+		/* a long timer is running : decrement the high part         */
+
+		restart_timer (dev);
+		high_count--;
+		RES_EVENT (OTG_INT_TMX, event_code);
+	}
+
+	do switch (otg_ctl->act_state & OTG_STATE_MASK) {
+
+	/* NOT_ASSIGNED (yet): init state, 1st time after loading            */
+	/* ======================================================            */
+
+	case OTG_NOT_ASSIGNED :
+
+		CHECK_STATE (dev, OTG_NOT_ASSIGNED);
+
+		if (IS_FLAG_RES (OTG_FLAGS_ACTIV)) {
+
+			/* seems to be the first time: let it run !          */
+
+			SET_FLAG (OTG_FLAGS_ACTIV);
+		}
+
+		/*      muxer is still neutral                               */
+
+		RES_EVENT ((OTG_INT_IDC | OTG_INT_TMX), event_code);
+
+		if (IS_FLAG_RES (OTG_GADGET_READY)) {
+
+			/* NOT_READY (substate): gadget drivers not ready    */
+			/* ==============================================    */
+			/*                                                   */
+			/*     check ID pin and enable host as needed        */
+
+			if (IS_BIT_RES (OTG_STS_ID, uoc_status)) {
+
+				/* ID pin connected: A-device (host)         */
+
+				if (OTG_NO_B_DEVICE_A != otg_ctl->act_state) {
+					CHANGE_STATE (dev, OTG_NO_B_DEVICE_A,
+					              pEvt_mask);
+				}
+			}
+			else if (IS_BIT_SET (OTG_STS_ID, uoc_status)) {
+
+				/* ID pin not connected: disable (neutral)   */
+
+				if (OTG_NO_B_DEVICE_B != otg_ctl->act_state) {
+					CHANGE_STATE (dev, OTG_NO_B_DEVICE_B,
+					              pEvt_mask);
+				}
+			}
+		}
+		else if ((OTG_NO_B_DEVICE_A == otg_ctl->act_state) ||
+		         (OTG_NO_B_DEVICE_B == otg_ctl->act_state)) {
+
+			/* Exit "not ready" state                            */
+			/* ----------------------                            */
+
+			RES_EVENT (OTG_GADGET_READY, event_code);
+			CHANGE_STATE (dev, OTG_NOT_ASSIGNED, pEvt_mask);
+		}
+
+		/* ==================================================        */
+
+#ifdef CONFIG_USB_OTGMUX_IDPIN
+
+		else if (IS_BIT_RES (OTG_STS_ID, uoc_status)) {
+
+			/* ID pin connected: A-device (host)                 */
+
+			CHANGE_STATE (dev, OTG_A_HOST, pEvt_mask);
+		}
+#endif
+		else {
+
+			/* ID pin is not connected: B-device (peripheral)    */
+
+			CHANGE_STATE (dev, OTG_B_IDLE, pEvt_mask);
+		}
+		break;
+
+#ifdef CONFIG_USB_OTGMUX_IDPIN
+
+	/* A: ID pin is connected, A-device drives VBus                      */
+	/* ============================================                      */
+
+	case OTG_A_HOST :
+
+		CHECK_STATE (dev, OTG_A_HOST);
+
+		if (IS_BIT_SET (OTG_STS_ID, uoc_status)) {
+
+			/* ID change => B_IDLE                               */
+
+			RES_EVENT ((OTG_INT_IDC | OTG_INT_PCC), event_code);
+			CHANGE_STATE (dev, OTG_B_IDLE, pEvt_mask);
+		}
+		break;
+
+	/* B: ID pin is not connected                                        */
+	/* ==========================                                        */
+#endif
+
+	case OTG_B_IDLE :
+
+		/* B_IDLE: init state for B-devices                          */
+		/*         monitor VBus, no connection, no activity          */
+
+		CHECK_STATE (dev, OTG_B_IDLE);
+
+#ifdef CONFIG_USB_OTGMUX_IDPIN
+
+		if (IS_BIT_RES (OTG_STS_ID, uoc_status)) {
+
+			/* ID pin changed => A_HOST                          */
+
+			RES_EVENT (OTG_INT_IDC, event_code);
+			CHANGE_STATE (dev, OTG_A_HOST, pEvt_mask);
+		}
+		else if (IS_BIT_SET (OTG_STS_SESSVLD, uoc_status))
+#else
+		if (IS_BIT_SET (OTG_STS_SESSVLD, uoc_status))
+#endif
+		{
+			/* Session valid => B_PERIPHERAL                     */
+
+			RES_EVENT (OTG_INT_SVC, event_code);
+			CHANGE_STATE (dev, OTG_B_PERIPHERAL, pEvt_mask);
+		}
+		break;
+
+	case OTG_B_PERIPHERAL :
+
+		/* B_PERIPHERAL: connected to A-host, responding             */
+		/*               VBus driven by A, remote activity           */
+
+		CHECK_STATE (dev, OTG_B_PERIPHERAL);
+
+#ifdef CONFIG_USB_OTGMUX_IDPIN
+
+		if (IS_BIT_RES (OTG_STS_ID, uoc_status) ||
+		    IS_BIT_RES (OTG_STS_SESSVLD, uoc_status))
+#else
+		if (IS_BIT_RES (OTG_STS_SESSVLD, uoc_status))
+#endif
+		{
+			/* ID pin changed | ~Session valid => B_IDLE         */
+
+			RES_EVENT ((OTG_INT_IDC | OTG_INT_SVC), event_code);
+			CHANGE_STATE (dev, OTG_B_IDLE, pEvt_mask);
+		}
+		break;
+
+	/* unlikely */ default :
+
+		/* something went wrong */
+
+		BUG ();
+		break;
+	}
+	while ((otg_ctl->act_state ^ otg_ctl->prv_state) & OTG_STATE_MASK);
+
+	DBG ("OTG-state change done\n");
+
+	return event_code;
+}
+#endif /* CONFIG_USB_OTGMUX_IDPIN || CONFIG_USB_HSA0_GADGET */
+
+#ifdef CONFIG_TEST_INTERFACE
+static int otg_test_bind (struct otg **dev)
+{
+	*dev = the_controller;
+	if (!*dev)
+		return -EINVAL;
+#ifdef CONFIG_TEST_MODE_ONLY
+	INFO ("bind OTG driver to the test driver, enable device\n");
+	SET_FLAG (OTG_HOST_READY | OTG_GADGET_READY);
+	if (IS_FLAG_SET (OTG_FLAGS_ACTIV)) {
+		otg_req_state_chg (the_controller,
+		                   OTG_HOST_READY | OTG_GADGET_READY);
+	}
+	the_controller->gadgetflag = 1;
+#else
+	INFO ("bind OTG driver to the test interface\n");
+#endif
+	return 0;
+}
+
+static int otg_test_unbind (struct otg **dev)
+{
+	*dev = NULL;
+#ifdef CONFIG_TEST_MODE_ONLY
+	INFO ("unbind OTG driver from the test driver, disable device\n");
+	the_controller->gadgetflag = 0;
+	RES_FLAG (OTG_HOST_READY | OTG_GADGET_READY);
+	if (IS_FLAG_SET (OTG_FLAGS_ACTIV)) {
+		otg_req_state_chg (the_controller,
+		                   OTG_HOST_READY | OTG_GADGET_READY);
+	}
+	return (otg_unbind_gadget ());
+#else
+	INFO ("unbind OTG driver from the test interface\n");
+	return 0;
+#endif
+}
+#endif /* CONFIG_TEST_INTERFACE */
+
+#ifdef CONFIG_USB_OTG
+
+/**
+ * \brief
+ * Bind the OTG controller to usbcore
+ *
+ * \param  bus   usb bus the host is assigned to
+ *
+ * \return error code
+ */
+static int otg_bind_host (struct usb_bus *bus)
+{
+	/* This bus supports OTG */
+
+	bus->otghost->otgpriv = (void *) the_controller;
+	the_controller->hostcount++;
+	DBG ("bind OTG driver to USB core: bus %d, port %d (OTG host %d)\n",
+	     bus->busnum, bus->otghost->otgport + 1,
+	     the_controller->hostcount);
+	if (IS_FLAG_RES (OTG_HOST_READY)) {
+		RES_FLAG (OTG_A_BUS_DROP);
+		SET_FLAG (OTG_HOST_READY | OTG_A_BUS_REQ);
+		if (IS_FLAG_SET (OTG_FLAGS_ACTIV)) {
+			otg_req_state_chg (the_controller, OTG_HOST_READY);
+		}
+	}
+	return 0;
+}
+
+/**
+ * \brief
+ * Unbind the OTG controller from usbcore
+ *
+ * \param  bus   usb bus the host is assigned to
+ *
+ * \return error code
+ */
+static int otg_unbind_host (struct usb_bus *bus)
+{
+	DBG ("unbind OTG driver from USB core (actual OTG hosts: %d)\n",
+	     the_controller->hostcount);
+	the_controller->hostcount--;
+	if (!the_controller->hostcount) {
+		RES_FLAG (OTG_HOST_READY | OTG_A_BUS_REQ);
+		SET_FLAG (OTG_A_BUS_DROP);
+		if (IS_FLAG_SET (OTG_FLAGS_ACTIV)) {
+			otg_req_state_chg (the_controller, OTG_HOST_READY);
+		}
+	}
+	bus->otghost->otgpriv = NULL;
+	return 0;
+}
+#endif /* CONFIG_USB_OTG */
+
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET)
+
+/**
+ * \brief
+ * Bind the OTG controller to the gadget
+ *
+ * \param  void
+ *
+ * \return error code
+ */
+static int otg_bind_gadget (void)
+{
+	if (the_controller->gadgetflag) {
+		ERR ("USB gadget: OTG driver already registered\n");
+		return -EBUSY;
+	}
+	DBG ("bind OTG driver to USB gadget\n");
+	the_controller->gadgetflag = 1;
+	SET_FLAG (OTG_GADGET_READY | OTG_B_BUS_REQ);
+	if (IS_FLAG_SET (OTG_FLAGS_ACTIV)) {
+		otg_req_state_chg (the_controller, OTG_GADGET_READY);
+	}
+	return 0;
+}
+
+/**
+ * \brief
+ * Unbind the OTG controller from the gadget
+ *
+ * \param  void
+ *
+ * \return error code
+ */
+static int otg_unbind_gadget (void)
+{
+	DBG ("unbind OTG driver from USB gadget\n");
+	RES_FLAG (OTG_GADGET_READY | OTG_B_BUS_REQ);
+	if (IS_FLAG_SET (OTG_FLAGS_ACTIV)) {
+		otg_req_state_chg (the_controller, OTG_GADGET_READY);
+	}
+	the_controller->gadgetflag = 0;
+	return 0;
+}
+#endif /* CONFIG_USB_OTG || CONFIG_USB_OTGMUX_GADGET */
+
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_HSA0_GADGET)
+
+/**
+ * \brief
+ * OTG state change request
+ *
+ * \param  dev    OTG device info
+ * \param  params
+ *
+ * \return void
+ */
+static inline void otg_req_state_chg (struct otg *dev, u32 params)
+{
+	u32 temp;
+
+	/* disable global OTG interrupt:                                     */
+	temp = ~((u32) OTG_INT_GLOBAL) & readl (&dev->regs->inten);
+	writel (temp, &dev->regs->inten);
+	temp &= readl (&dev->regs->intr);
+	if (temp) {
+		writel (temp, &dev->regs->intr);
+	}
+	/* update OTG state:                                                 */
+	otg_change_state (dev, (params | temp), &temp);
+
+	/* enable global OTG interrupt:                                      */
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high                        ***/
+/*** HS-A0 WA: BUG-3950: gadget needs disconnect notification              ***/
+#ifdef UDC_HSA0_WORKAROUNDS
+	state_mask = ~temp & OTG_INT_ADDS;
+	writel ((OTG_INT_ADDS | temp | OTG_INT_GLOBAL), &dev->regs->inten);
+#else
+	writel ((temp | OTG_INT_GLOBAL), &dev->regs->inten);
+#endif
+}
+#endif /* CONFIG_USB_OTGMUX || CONFIG_USB_OTGMUX_IDPIN || _HSA0_GADGET */
+
+#ifdef CONFIG_USB_OTG
+
+/**
+ * \brief
+ * OTG application request
+ *
+ * \param  params
+ *
+ * \return OTG_APP_REQ_ACK
+ */
+static int otg_app_request (u32 params)
+{
+	u32  flags = params;
+
+	if (OTG_AB_HNP_REQ & flags) {
+
+		/* HNP requested by application                              */
+
+		if (((the_controller->otg_ctl->act_state &
+		      OTG_STATE_MASK) != OTG_A_HOST) &&
+		    ((the_controller->otg_ctl->act_state &
+		      OTG_STATE_MASK) != OTG_B_HOST)) {
+
+			/* this is for host states only, otherwise ignore    */
+
+			flags &= ~((u32) OTG_AB_HNP_REQ);
+		}
+		else if (IS_FLAG_RES (OTG_A_bHNP_EN)) {
+
+			/* HNP still disabled: force enable                  */
+
+			flags |= OTG_A_bHNP_EN;
+		}
+	}
+	if (OTG_A_bHNP_EN & flags) {
+
+		/* HNP enable requested                                      */
+
+		if (IS_FLAG_RES (OTG_A_bHNP_EN) &&
+		    ((the_controller->otg_ctl->act_state &
+		      OTG_STATE_MASK) == OTG_A_HOST)) {
+
+			/* HNP still disabled: do it                         */
+
+			if (otg_enable_hnp (the_controller) < 0) {
+				ERR ("Couldn't enable HNP\n");
+			}
+		}
+	}               /* This is now done (OTG_A_bHNP_EN)                  */
+
+	if (OTG_A_CLR_ERR & flags) {
+
+		/* request to clear error flags                              */
+
+		flags |= OTG_A_VBUS_FAILED;
+	}
+
+	/* create mask of flags to clear                                     */
+
+	flags &= (OTG_B_SRP_ERROR | OTG_A_VBUS_FAILED);
+
+	if (flags) {
+
+		/* reset error flags if requested                            */
+
+		RES_FLAG ((flags));
+	}
+
+	/* prepare for pending requests (no more actions on OTG_A_bHNP_EN)   */
+
+	flags = params & (SW_REQUEST_MASK & ~((u32) OTG_A_bHNP_EN));
+	SET_FLAG ((flags));
+
+	if (OTG_A_BUS_DROP & flags) {
+
+		/* request to drop A-VBus: reset "rise" request if pending   */
+
+		flags &= ~OTG_A_BUS_REQ;
+		RES_FLAG (OTG_A_BUS_REQ);
+	}
+	if (OTG_A_BUS_REQ & flags) {
+
+		/* request to rise A-VBus: reset "drop" request if pending   */
+
+		RES_FLAG (OTG_A_BUS_DROP);
+	}
+	if (OTG_B_BUS_DIS & flags) {
+
+		/* request to disable B-dev: reset "ena" request if pending  */
+
+		flags &= ~OTG_B_BUS_REQ;
+		RES_FLAG (OTG_B_BUS_REQ);
+	}
+	if (OTG_B_BUS_REQ & flags) {
+
+		/* request to enable B-dev: reset "disa" request if pending  */
+
+		RES_FLAG (OTG_B_BUS_DIS);
+	}
+	if (flags) {
+
+		/* request still pending may force a state change            */
+
+		otg_req_state_chg (the_controller, flags);
+	}
+	return OTG_APP_REQ_ACK;
+}
+#endif /* CONFIG_USB_OTG */
+
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_HSA0_GADGET)
+
+/**
+ * \brief
+ * OTG application query
+ *
+ * \param  index   select status info data
+ *
+ * \return status
+ */
+static u32 otg_app_query (int index)
+{
+	u32  temp = 0;
+
+	if (index == 0) {
+		temp = otg_ctl->params | readl (&the_controller->regs->sts);
+
+/*** HS-A0 WA: BUG-3943: gadget phy suspend issue                          ***/
+#ifdef UDC_HSA0_WORKAROUNDS
+		if (((readl (&the_controller->regs->ctl) & OTG_CTL_MUX_MASK) ==
+		     OTG_CTL_ENABLE_UDC) &&
+		    ((temp & OTG_STS_PSUS) || (~temp & OTG_STS_VBUSVLD))) {
+			temp |= OTG_FLAGS_UDC_SUSP;
+		}
+#endif
+	}
+	else if (index == 1) {
+		temp = otg_ctl->act_state;
+	}
+	return (temp);
+}
+
+/**
+ * \brief
+ * OTG state activation
+ *
+ * \param  dev   OTG controller info
+ *
+ * \return void
+ */
+inline void otg_activate_state (struct otg *dev)
+{
+	u32 temp = 0;
+
+	/* Enable timer interrupt, start timer, set state                    */
+
+	SET_OTG_TIMER (dev, IDSNS_WAIT);
+	CHANGE_STATE (dev, OTG_NOT_ASSIGNED, &temp);
+	CHECK_STATE (dev, OTG_NOT_ASSIGNED);
+
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high                        ***/
+/*** HS-A0 WA: BUG-3943: gadget suspend issue                              ***/
+/*** HS-A0 WA: BUG-3950: gadget needs disconnect notification              ***/
+
+	state_mask = ~temp & OTG_INT_ADDS;
+	writel ((OTG_INT_ADDS | temp | OTG_INT_GLOBAL), &dev->regs->inten);
+
+	DBG ("OTG-HW initialized, now checking ID ...\n");
+}
+
+/**
+ * \brief
+ * OTG ISR calling the main state machine
+ *
+ * \param  irq    IRQ number
+ * \param  _dev  
+ * \param  r     
+ *
+ * \return IRQ_HANDLED (system code)
+ */
+static irqreturn_t otg_isr (int irq, void *dev, struct pt_regs *r)
+{
+	u32         interrupts, int_mask;
+
+	int_mask = readl (&((struct otg *) dev)->regs->inten);
+	if ((OTG_INT_GLOBAL & int_mask) &&
+	    (int_mask &= ~((u32) OTG_INT_GLOBAL)) &&
+	    (interrupts = int_mask &
+		          readl (&((struct otg *) dev)->regs->intr))) {
+
+		writel (int_mask, &((struct otg *) dev)->regs->inten);
+		writel (interrupts, &((struct otg *) dev)->regs->intr);
+
+		/* filter out additional WA interrupts, they're done         */
+		/*       don't want to see them in the state machine         */
+
+		if (interrupts & ~state_mask) {
+
+			/* events pending for the state machine              */
+
+			otg_change_state ((struct otg *) dev,
+			                  (interrupts & ~state_mask),
+			                  &int_mask);
+		}
+
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high                        ***/
+/*** HS-A0 WA: BUG-3943: gadget suspend issue                              ***/
+/*** HS-A0 WA: BUG-3950: gadget needs disconnect notification              ***/
+#ifdef UDC_HSA0_WORKAROUNDS
+#ifndef CONFIG_TEST_MODE_ONLY
+
+		if (GOT_EVENT (OTG_INT_SVC, interrupts) &&
+		    IS_FLAG_SET (OTG_GADGET_READY) &&
+		    otg_gadget_driver.notify) {
+
+			if (IS_BIT_RES (OTG_STS_SESSVLD, readl
+			        (&((struct otg *) dev)->regs->sts))) {
+
+				if ((otg_ctl->act_state & OTG_STATE_MASK) ==
+				    OTG_B_IDLE) {
+					VDBG ("calling gadget: disconnect\n");
+					otg_gadget_driver.notify
+					    (OTG_GADGET_EVT_SVDROP);
+				}
+			}
+			else {
+
+				if ((otg_ctl->act_state & OTG_STATE_MASK) ==
+				    OTG_B_PERIPHERAL) {
+					VDBG ("calling gadget: connect\n");
+					otg_gadget_driver.notify
+					    (OTG_GADGET_EVT_SVALID);
+				}
+			}
+		}
+#endif
+#endif /* UDC_HSA0_WORKAROUNDS */
+
+		/* enable interrupts and keep information about WA ints:     */
+
+		state_mask = OTG_INT_ADDS & ~int_mask;
+		writel ((OTG_INT_ADDS | int_mask | OTG_INT_GLOBAL),
+		        &((struct otg *) dev)->regs->inten);
+
+	}
+	return IRQ_HANDLED;
+}
+#endif /* CONFIG_USB_OTG || CONFIG_USB_OTGMUX_IDPIN || _HSA0_GADGET */
+
+/**
+ * \brief
+ * OTG state init
+ *
+ * \param  dev   OTG controller info
+ *
+ * \return void
+ */
+inline void otg_init_state (struct otg *dev)
+{
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_HSA0_GADGET)
+	int i;
+#endif
+
+	VDBG ("OTG init ...\n");
+
+#ifdef VERBOSE
+	/* print regs */
+	print_regs (dev);
+#endif
+	/* Make sure we'll remember the initial state                        */
+	init_state = readl (&dev->regs->ctl);
+	VDBG ("  OTG init state was %08x\n", init_state);
+
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_HSA0_GADGET)
+
+	/* turn on the OTG controller                                        */
+
+	writel ((init_state | OTG_CTL_PADEN), &dev->regs->ctl);
+
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high                        ***/
+/*** HS-A0 WA: BUG-3943: gadget suspend issue                              ***/
+#ifndef UDC_HSA0_WORKAROUNDS
+
+	/* Enable OTG hardware (incl. auto pull-up enable)                   */
+
+	writel ((readl (&dev->regs->cap) | OTG_CAP_APU), &dev->regs->cap);
+#endif
+
+	/* initialize flags                                                  */
+	otg_ctl->params = 0;
+#endif
+
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high                        ***/
+/*** HS-A0 WA: BUG-3943: gadget suspend issue                              ***/
+/*** HS-A0 WA: BUG-3950: gadget disconnect notification                    ***/
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_HSA0_GADGET)
+
+	/* make sure all interrupts are disabled                             */
+
+	writel (OTG_INT_DISALL, &dev->regs->inten);
+	writel (OTG_INT_ENALL, &dev->regs->intr);
+
+	/* Set multiplexer to neutral, get power control, drop VBus          */
+
+	if (((init_state & OTG_CTL_MUX_MASK) == OTG_CTL_ENABLE_UHC) &&
+	    ((((init_state & OTG_CTL_PPO) && (init_state & OTG_CTL_PPWR))) ||
+	     ((~init_state & OTG_CTL_PPO) &&
+	      (readl (&dev->regs->sts) & OTG_STS_SESSVLD)))) {
+
+		/* VBus still powered try to discharge VBus and set timer    */
+
+		DBG ("Setting init state, trying to discharge VBus ...\n");
+
+		for (i = 0; i < 4; i++) {
+			writel (TIMER_PERIOD, &dev->regs->tmr);
+			writel ((OTG_CTL_PADEN | OTG_CTL_IDSNSEN |
+			         OTG_CTL_PPO | OTG_CTL_DISCHRG |
+			         OTG_CTL_TMR_UNCOND), &dev->regs->ctl);
+			do { }
+			while (!(readl (&dev->regs->sts) & OTG_STS_TMH));
+		}
+		writel ((OTG_CTL_PADEN | OTG_CTL_IDSNSEN | OTG_CTL_PPO),
+		        &dev->regs->ctl);
+		writel (OTG_INT_ENALL, &dev->regs->intr);
+#ifdef DEBUG
+		if (readl (&dev->regs->sts) & OTG_STS_SESSVLD) {
+
+			DBG ("  VBus still high, external host connected");
+		}
+		else {
+
+			DBG ("  VBus discharged\n");
+		}
+#endif
+	}
+	else {
+		DBG ("Setting init state\n");
+
+		writel ((OTG_CTL_PADEN | OTG_CTL_IDSNSEN | OTG_CTL_PPO),
+		        &dev->regs->ctl);
+	}
+#else  /* !_OTG && !_IDPIN && !_HSA0 */
+#ifdef CONFIG_USB_OTGMUX_GADGET
+
+	/* Set multiplexer to gadget                                         */
+	writel ((readl (&dev->regs->cap) & ~((u32) OTG_CAP_APU)),
+	        &dev->regs->cap);
+	writel ((OTG_CTL_ENABLE_UDC | OTG_CTL_PADEN),
+	        &dev->regs->ctl);
+	INFO ("OTG functionality is disabled, port is assigned to device\n");
+#endif
+#endif /* CONFIG_USB_OTG || CONFIG_USB_OTGMUX_IDPIN || _HSA0_GADGET */
+
+#ifdef CONFIG_USB_OTGMUX_HOST
+
+	/* Set multiplexer to host                                           */
+	writel ((readl (&dev->regs->cap) & ~((u32) OTG_CAP_APU)),
+	        &dev->regs->cap);
+
+#ifndef CONFIG_USB_NON_PCI_OTGDEVICE
+
+	writel ((OTG_CTL_ENABLE_UHC | OTG_CTL_PADEN),
+	        &dev->regs->ctl);
+
+#else  /* Au1200 */
+	writel ((OTG_CTL_ENABLE_UHC | OTG_CTL_PPO | OTG_CTL_PPWR |
+	         OTG_CTL_PADEN), &dev->regs->ctl);
+
+	/* Turn on VBus                                                      */
+	au1000gpio_bit_clear (USB_VBUS_GPIO);
+#endif
+
+	INFO ("OTG functionality is disabled, port is assigned to host\n");
+#endif
+	VDBG ("OTG init done\n");
+}
+
+/**
+ * \brief
+ * OTG state exit
+ *
+ * \param  dev   OTG controller info
+ *
+ * \return       muliplexer state
+ */
+inline int otg_exit_state (struct otg *dev)
+{
+	int muxer;
+
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_HSA0_GADGET)
+
+	/* Disable all interrupts                                            */
+	writel (OTG_INT_DISALL, &dev->regs->inten);
+	writel (OTG_INT_ENALL, &dev->regs->intr);
+
+	/* reset state, terminate all connections                            */
+	CHANGE_STATE (dev, OTG_NOT_ASSIGNED, &state_mask);
+	CHECK_STATE (dev, OTG_NOT_ASSIGNED);
+	otg_ctl->params = 0;
+#endif
+
+	muxer = init_state & (OTG_CTL_ENABLE_UHC | OTG_CTL_ENABLE_UDC);
+
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high                        ***/
+/*** HS-A0 WA: BUG-3943: gadget suspend issue                              ***/
+#ifdef UDC_HSA0_WORKAROUNDS
+
+	/* Don't assign the port to the device controller                    */
+
+	if (!(muxer ^ OTG_CTL_ENABLE_UDC)) {
+
+		init_state &= ~((u32) (OTG_CTL_MUX_MASK | OTG_CTL_PUEN));
+		muxer = OTG_CTL_DISABLE_ALL;
+	}
+	VDBG ("OTG writing back corrected init state: %08x\n", init_state);
+#else
+	/* Reset APU                                                         */
+
+	writel ((readl (&dev->regs->cap) & ~((u32) OTG_CAP_APU)),
+	        &dev->regs->cap);
+
+	VDBG ("OTG writing back init state: %08x\n", init_state);
+#endif
+	/* Now, that's the moment to remember                                */
+	/* Set dev muxer and pull up bits, turn off the OTG controller       */
+
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE  /* Au1200 */
+
+	/* Turn off VBus                                                     */
+	au1000gpio_bit_set (USB_VBUS_GPIO);
+#endif
+	writel (init_state, &dev->regs->ctl);
+
+	if (!(muxer ^ OTG_CTL_ENABLE_UHC)) {
+		INFO ("disabling OTG-HW, port is assigned to host\n");
+	}
+	else if (!(muxer ^ OTG_CTL_ENABLE_UDC)) {
+		INFO ("disabling OTG-HW, port is assigned to device\n");
+	}
+	else {
+		INFO ("disabling OTG-HW, port is not assigned\n");
+	}
+
+	VDBG ("OTG exit: OTG-HW disabled\n");
+
+	return muxer;
+}
+
+/**
+ * \brief
+ * OTG remove: clean up resources, remove driver
+ *
+ * \param  pdev  PCI device info
+ *
+ * \return void
+ */
+static void otg_remove (struct pci_dev *pdev)
+{
+	struct otg *dev = pci_get_drvdata (pdev);
+
+#ifndef CONFIG_TEST_MODE_ONLY
+#ifdef CONFIG_USB_OTG
+
+	/* unregistering from the usb core */
+	usb_bus_deregister_otg (&otg_host_driver);
+#endif
+
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET)
+
+	/* unregistering from the usb gadget */
+	usb_gadget_unregister_otg (&otg_gadget_driver);
+#endif
+#endif
+
+#ifdef CONFIG_TEST_INTERFACE
+
+	/* unregistering from the test driver */
+	usb_otg_test_unregister_driver (&otg_test_driver);
+#endif
+
+	/* clean up the OTG controller */
+	if (!(otg_exit_state (dev))) {
+		INFO ("OTG HW disabled, port is not assigned\n");
+	}
+
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high                        ***/
+/*** HS-A0 WA: BUG-3943: gadget suspend issue                              ***/
+/*** HS-A0 WA: BUG-3950: gadget disconnect notification                    ***/
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_HSA0_GADGET)
+
+	/* then clean up the resources we allocated during probe() */
+	if (dev->got_irq)
+		free_irq (pdev->irq, dev);
+#endif
+
+	if (dev->regs)
+		iounmap (dev->regs);
+
+	if (dev->region)
+		release_mem_region (pci_resource_start (pdev, 0),
+                                    pci_resource_len (pdev, 0));
+
+#ifndef CONFIG_USB_NON_PCI_OTGDEVICE
+
+	if (dev->enabled)
+		pci_disable_device (pdev);
+#else
+	/* Note: assuming this is for Au1200 so au_... macros are used       */
+
+	/* Release power switch GPIO (Pb1200)                                */
+	au1000gpio_bit_term (USB_VBUS_GPIO, &init_usb_vbus_switch);
+
+	if (dev->enabled) {
+		au_writel ((au_readl (USB_MSR_BASE + USB_MSR_MCFG) &
+		            ~((u32) (1 << USBMSRMCFG_GMEMEN))),
+		           (USB_MSR_BASE + USB_MSR_MCFG));
+/* 		au_sync (); */
+		udelay (1000);
+	}
+#endif
+	pci_set_drvdata (pdev, 0);
+	the_controller = 0;
+
+	INFO ("unbind from pci %s\n", pdev->slot_name);
+
+	DBG ("kfree: OTG driver: %p\n", dev);
+	kfree (dev);
+}
+
+/**
+ * \brief
+ * OTG probe: init driver, assign resources
+ *
+ * \param  pdev  PCI device info
+ *
+ * \return  void
+ */
+static int otg_probe (struct pci_dev *pdev, const struct pci_device_id *id)
+{
+	struct otg *dev;
+	u32         resource, len;
+	void       *base = 0;
+	int         retval;
+	char        buf [8] = {0,0,0,0,0,0,0,0}, *bufp;
+
+	/* alloc, and start init */
+	dev = (struct otg *) kmalloc (sizeof(struct otg), SLAB_KERNEL);
+	if (!dev) {
+		ERR ("couldn't allocate memory for OTG driver\n");
+		retval = -ENOMEM;
+		goto done;
+	}
+	DBG ( "kmalloc: OTG driver: %p\n", dev);
+
+	/* hold global device pointer */
+	the_controller = dev;
+
+	memset (dev, 0, sizeof(struct otg));
+	spin_lock_init (&dev->lock);
+
+#ifndef CONFIG_USB_NON_PCI_OTGDEVICE
+
+	dev->pdev = pdev;
+
+	/* now all the pci goodies ...                                       */
+	if (pci_enable_device (pdev) < 0) {
+		ERR ("couldn't enable PCI device\n");
+		retval = -ENODEV;
+		goto done;
+	}
+	dev->enabled = 1;
+
+	/* BAR 0 holds all the registers                                     */
+
+	resource = pci_resource_start (pdev, 0);
+	len = pci_resource_len (pdev, 0);
+
+#else /* CONFIG_USB_NON_PCI_OTGDEVICE */
+
+	resource = USB_UOC_BASE;
+	len = USB_UOC_LEN;
+
+	/*
+	* Fill in the dummy pci_dev. Only those members actually
+	* dereferenced in this driver are initialized.
+	*/
+	memset(pdev, 0, sizeof(struct pci_dev));
+	strcpy(pdev->name, "non-PCI UOC");
+	strcpy(pdev->slot_name, "builtin");
+	pdev->resource[0].name = "UOC Registers";
+	pdev->resource[0].start = resource;
+	pdev->resource[0].end = resource + len - 1;
+	pdev->resource[0].flags = 0;
+	pdev->irq = AU1200_USB_INT;
+
+	dev->pdev = pdev;
+
+	/* enable UOC memory (Au1200)                                        */
+	/* Note: assuming this is for Au1200 so au_... macros are used       */
+	au_writel ((au_readl (USB_MSR_BASE + USB_MSR_MCFG) |
+	            (1 << USBMSRMCFG_GMEMEN)), (USB_MSR_BASE + USB_MSR_MCFG));
+	au_sync ();
+
+	/* Init power switch GPIO (Pb1200)                                   */
+
+	if (au1000gpio_bit_init (USB_VBUS_GPIO, &init_usb_vbus_switch)) {
+
+		ERR ("Couldn't initialize power switch GPIO\n");
+	}
+
+	dev->enabled = 1;
+
+#endif /* CONFIG_USB_NON_PCI_OTGDEVICE */
+
+	if (!request_mem_region (resource, len, driver_name)) {
+		ERR ("controller already in use\n");
+		retval = -EBUSY;
+		goto done;
+	}
+	dev->region = 1;
+
+	base = ioremap_nocache (resource, len);
+	if (!base) {
+		ERR ("couldn't map memory\n");
+		retval = -EFAULT;
+		goto done;
+	}
+
+	/* register base */
+	dev->regs = (struct otg_regs *) base;
+	bufp = buf;
+
+	dev->device = NULL;
+
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high                        ***/
+/*** HS-A0 WA: BUG-3943: gadget suspend issue                              ***/
+/*** HS-A0 WA: BUG-3950: gadget disconnect notification                    ***/
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_HSA0_GADGET)
+
+	/* OTG state variables */
+	dev->otg_ctl = otg_ctl;
+
+	/* make sure all interrupts are disabled */
+	writel (OTG_INT_DISALL, &dev->regs->inten);
+	writel (OTG_INT_ENALL, &dev->regs->intr);
+
+	/* irq setup after old hardware is cleaned up */
+	if (!pdev->irq) {
+		ERR ("No IRQ. Check PCI setup!\n");
+		retval = -ENODEV;
+		goto done;
+	}
+#ifndef __sparc__
+
+	snprintf (buf, sizeof buf, "%d", pdev->irq);
+	bufp = buf;
+#else
+	bufp = __irq_itoa(pdev->irq);
+#endif
+	if (request_irq (pdev->irq, otg_isr, SA_SHIRQ, driver_name, dev) != 0)
+	{
+		ERR ("request interrupt %s failed\n", bufp);
+		retval = -EBUSY;
+		goto done;
+	}
+	dev->got_irq = 1;
+
+#endif /* CONFIG_USB_OTG || CONFIG_USB_OTGMUX_IDPIN || _HSA0_GADGET */
+
+	dev->chiprev = 0;
+
+#ifndef CONFIG_USB_NON_PCI_OTGDEVICE
+	pci_read_config_byte (pdev, PCI_REVISION_ID, (u8*) &dev->chiprev);
+#else
+        /* chip rev for Au1200 */
+        dev->chiprev = (u16) read_c0_prid() & 0xff;
+#endif
+
+	/* done */
+	pci_set_drvdata (pdev, dev);
+	INFO ("%s\n", driver_desc);
+
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high                        ***/
+/*** HS-A0 WA: BUG-3943: gadget suspend issue                              ***/
+/*** HS-A0 WA: BUG-3950: gadget disconnect notification                    ***/
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_HSA0_GADGET)
+
+	INFO ("irq %s, pci mem %08x, chip rev %02x\n",
+	      bufp, resource, dev->chiprev);
+#else
+	INFO ("pci mem %08x, chip rev %02x\n",
+	      resource, dev->chiprev);
+#endif
+	bufp = DRIVER_VERSION;
+	INFO ("driver version: %s\n", bufp);
+
+	/* initialize the OTG controller */
+	otg_init_state (dev);
+
+#ifdef CONFIG_TEST_INTERFACE
+
+	/* registering to the test driver */
+	usb_otg_test_register_driver (&otg_test_driver);
+#endif
+
+#ifndef CONFIG_TEST_MODE_ONLY
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_GADGET)
+
+	/* registering to the device driver */
+	if (usb_gadget_register_otg (&otg_gadget_driver)) {
+		ERR ("gadget driver registration failed\n");
+		retval = -ENODEV;
+		goto done;
+	}
+#endif
+
+#ifdef CONFIG_USB_OTG
+
+	/* registering to the usbcore */
+	if (usb_bus_register_otg (&otg_host_driver)) {
+		usb_gadget_unregister_otg (&otg_gadget_driver);
+		ERR ("usbcore registration failed\n");
+		retval = -ENODEV;
+		goto done;
+	}
+#endif
+#endif
+
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_HSA0_GADGET)
+
+	/* finally activate OTG functionality */
+	otg_activate_state (dev);
+#endif
+	return 0;
+done:
+	if (dev) {
+
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high                        ***/
+/*** HS-A0 WA: BUG-3943: gadget suspend issue                              ***/
+/*** HS-A0 WA: BUG-3950: gadget disconnect notification                    ***/
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_HSA0_GADGET)
+
+		if (dev->got_irq)
+			free_irq (pdev->irq, dev);
+#endif
+		if (dev->regs)
+			iounmap (dev->regs);
+
+		if (dev->region)
+			release_mem_region (pci_resource_start (pdev, 0),
+			                    pci_resource_len (pdev, 0));
+
+#ifndef CONFIG_USB_NON_PCI_OTGDEVICE
+
+		if (dev->enabled)
+			pci_disable_device (pdev);
+#else
+	/* Note: assuming this is for Au1200 so au_... macros are used       */
+		if (dev->enabled) {
+			au_writel ((au_readl (USB_MSR_BASE + USB_MSR_MCFG) &
+			            ~((u32) (1 << USBMSRMCFG_GMEMEN))),
+		               (USB_MSR_BASE + USB_MSR_MCFG));
+/* 			au_sync (); */
+			udelay (1000);
+		}
+#endif
+		pci_set_drvdata (pdev, 0);
+		the_controller = 0;
+
+		INFO ("unbind from pci %s\n", pdev->slot_name);
+
+		DBG ("kfree: OTG driver: %p\n", dev);
+		kfree (dev);
+	}
+	return retval;
+}
+
+
+/*****************************************************************************
+ *  More data
+ *****************************************************************************/
+
+/**
+ * \brief
+ * PCI ID table
+ *
+ */
+static struct pci_device_id pci_ids [] = { {
+	.vendor =	PCI_VENDOR_ID_AMD,
+	.device =	PCI_DEVICE_ID_AMD_CS5536_UOC,
+	.subvendor =	PCI_ANY_ID,
+	.subdevice =	PCI_ANY_ID,
+	.class = 	0,
+	.class_mask = 	0,
+
+}, { /* end: all zeroes */ }
+};
+
+#ifndef CONFIG_USB_NON_PCI_OTGDEVICE
+/**
+ * \brief
+ * PCI driver struct to be used for driver registration
+ * ( this is a "new style" PCI driver module )
+ *
+ */
+static struct pci_driver otg_pci_driver = {
+	.name =		(char *) driver_name,
+	.id_table =	pci_ids,
+	.probe =	otg_probe,
+	.remove =	otg_remove,
+
+	/* FIXME add power management support */
+};
+
+MODULE_DEVICE_TABLE (pci, pci_ids);
+#endif
+
+/* This comment closes the module definition from above. There can be multiple 
+   definitions of this kind in a file. See the doxygen documentation for more
+   information. */
+/** \}*/
+
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_AUTHOR ("Karsten Boge");
+MODULE_LICENSE ("GPL");
+
+static int __init init (void)
+{
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE
+
+	return (otg_probe (&pdev_dummy, pci_ids));
+#else
+        return (pci_module_init (&otg_pci_driver));
+#endif
+}
+module_init (init);
+
+static void __exit cleanup (void)
+{
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE
+
+	otg_remove (&pdev_dummy);
+#else
+	pci_unregister_driver (&otg_pci_driver);
+#endif
+}
+module_exit (cleanup);
diff -Nur linux-2.4.30/drivers/usb/gadget/amd5536uoc.h linux-2.4.30-linux-mips.org/drivers/usb/gadget/amd5536uoc.h
--- linux-2.4.30/drivers/usb/gadget/amd5536uoc.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/usb/gadget/amd5536uoc.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,1242 @@
+/*
+ * Header for AMD 5536 USB OTG controller driver
+ */
+
+/*
+ * Copyright (C) 2005 AMD (http://www.amd.com)
+ * Author: Karsten Boge 
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+#ifndef AMD5536UOC_H
+#define AMD5536UOC_H
+
+/*****************************************************************************
+*  Config options
+**************************************************************************** */
+
+#ifndef UDC_HSA0_WORKAROUNDS
+#define UDC_HSA0_WORKAROUNDS
+#endif
+
+#ifdef CONFIG_USB_OTG
+#undef CONFIG_USB_OTGMUX_HOST
+#undef CONFIG_USB_OTGMUX_GADGET
+#undef CONFIG_USB_OTGMUX_IDPIN
+#endif
+
+#ifdef CONFIG_USB_OTGMUX_IDPIN
+#undef CONFIG_USB_OTGMUX_HOST
+#ifndef CONFIG_USB_OTGMUX_GADGET
+#define CONFIG_USB_OTGMUX_GADGET
+#endif
+#endif
+
+#ifdef UDC_HSA0_WORKAROUNDS
+#ifdef CONFIG_USB_OTGMUX_GADGET
+#ifndef CONFIG_USB_HSA0_GADGET
+#define CONFIG_USB_HSA0_GADGET
+#endif
+#endif
+#endif
+
+#ifndef OTG_HSA0_HNP_WA
+#define OTG_HSA0_HNP_WA
+#endif
+
+#ifdef CONFIG_SOC_AU1200  /* _MIPS_PB1200 */
+#ifndef CONFIG_USB_NON_PCI_OTGDEVICE
+#define CONFIG_USB_NON_PCI_OTGDEVICE
+#endif
+#include <asm/au1000.h>
+#include <asm/au1000_gpio.h>
+#endif
+
+#ifdef VERBOSE
+#ifndef DEBUG
+#define DEBUG
+#endif
+#endif
+
+
+/*****************************************************************************
+*  Constants
+*****************************************************************************/
+
+#define OTG_DRIVER_NAME        "amd5536otg"
+
+#define OTG_CHIPREV            0
+
+#define OTG_FLAGS_ACTIV        (1<<19)   /* full OTG functionality is activ  */
+
+
+#ifndef PCI_VENDOR_ID_AMD
+#define PCI_VENDOR_ID_AMD             0x1022
+#endif
+#ifndef PCI_DEVICE_ID_AMD_CS5536_UOC
+#define PCI_DEVICE_ID_AMD_CS5536_UOC  0x2097
+#endif
+
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE  /* Pb1200 */
+#define USB_VBUS_GPIO          3  /* AU1000_GPIO_3 */
+#endif
+
+
+/**********************************
+*  CS5536UOC Register definitions
+**********************************/
+
+/* capabilities */
+#define OTG_CAP_APU            (1<<15)   /* automatic pull-up enable         */
+
+/* multiplexer */
+#define OTG_MUX_DISABLE_ALL    0         /* not assigned                     */
+#define OTG_MUX_ENABLE_UHC     (2<<0)    /* assigned to host                 */
+#define OTG_MUX_ENABLE_UDC     (3<<0)    /* assigned to device               */
+#define OTG_MUX_PUEN           (1<<2)    /* pull-up enable                   */
+#define OTG_MUX_VBUSVLD        (1<<8)    /* VBus valid                       */
+
+/* status */
+#define OTG_STS_ID             (1<<0)    /* ID pin status                    */
+#define OTG_STS_VBUSVLD        (1<<1)    /* VBus valid                       */
+#define OTG_STS_SESSVLD        (1<<2)    /* Session valid                    */
+#define OTG_STS_SESSEND        (1<<3)    /* Session end                      */
+#define OTG_STS_LST            (3<<4)    /* Line state                       */
+#define OTG_STS_LST_J          (1<<4)    /* Line state                       */
+#define OTG_STS_LST_K          (2<<4)    /* Line state                       */
+#define OTG_STS_PSPD           (3<<6)    /* Port speed                       */
+#define OTG_STS_PSPD_LS        (1<<7)    /* Port speed                       */
+#define OTG_STS_PSPD_FS        (2<<7)    /* Port speed                       */
+#define OTG_STS_FSOE           (1<<8)    /* FS output enable (OHC)           */
+#define OTG_STS_PCON           (1<<9)    /* Port connected                   */
+#define OTG_STS_PSUS           (1<<10)   /* Port suspended                   */
+#define OTG_STS_TMH            (1<<11)   /* Timer halted                     */
+#define OTG_STS_HNP_EN         (1<<12)   /* HNP enabled for B-dev            */
+#define OTG_STS_HNP_SUPP       (1<<13)   /* A-host supports HNP              */
+#define OTG_STS_HNP_ALTSUPP    (1<<14)   /* A-host supports alt. HNP         */
+#define OTG_STS_HNPSTS         (OTG_STS_HNP_EN | OTG_STS_HNP_SUPP | \
+                                OTG_STS_HNP_ALTSUPP)
+#define OTG_STS_OC             (1<<15)   /* over-current                     */
+#define OTG_STS_DPR            (1<<16)   /* Downstream port reset            */
+
+/* control */
+#define OTG_CTL_DISABLE_ALL    0         /* not assigned                     */
+#define OTG_CTL_ENABLE_UHC     (2<<0)    /* assigned to host                 */
+#define OTG_CTL_ENABLE_UDC     (3<<0)    /* assigned to device               */
+#define OTG_CTL_MUX_MASK       (3<<0)    /* port mux mask                    */
+#define OTG_CTL_PPWR           (1<<2)    /* port power switch                */
+#define OTG_CTL_PPO            (1<<3)    /* port power override              */
+#define OTG_CTL_CHRG           (1<<4)    /* charge VBus                      */
+#define OTG_CTL_DISCHRG        (1<<5)    /* discharge VBus                   */
+#define OTG_CTL_IDSNSEN        (1<<6)    /* ID sense enable, ID-PU           */
+#define OTG_CTL_PADEN          (1<<7)
+#define OTG_CTL_PUEN           (1<<8)    /* pull-up enable                   */
+#define OTG_CTL_DMPDEN         (1<<9)    /* pull-down enable                 */
+#define OTG_CTL_HNPSFEN        (1<<10)   /* HNP SET_FEATURE enable           */
+#define OTG_CTL_WPCS_DEAS      (2<<16)   /* deassert port connect            */
+#define OTG_CTL_WPCS_ASRT      (3<<16)   /* assert port connect              */
+#define OTG_CTL_WPSS_DEAS      (2<<18)   /* deassert port suspend            */
+#define OTG_CTL_WPSS_ASRT      (3<<18)   /* assert port suspend              */
+/* timer conditions */
+#define OTG_CTL_TMR_RLP        (1<<28)   /* timer reload policy              */
+#define OTG_CTL_TMR_ALL        (0xf<<24) /* stop timer                       */
+#define OTG_CTL_TMR_STOP       0         /* timer disabled                   */
+#define OTG_CTL_TMR_UNCOND     (1<<24)   /* count unconditionally            */
+#define OTG_CTL_TMR_SE0        (2<<24)   /* count if LSt = FS-SE0            */
+#define OTG_CTL_TMR_FSJ        (3<<24)   /* count if LSt = FS-J              */
+#define OTG_CTL_TMR_FSK        (4<<24)   /* count if LSt = FS-K              */
+#define OTG_CTL_TMR_NOSE0      (5<<24)   /* count if LSt <> FS-SE0           */
+#define OTG_CTL_TMR_NORX       (6<<24)   /* count if Rx inactiv              */
+#define OTG_CTL_TMR_ID         (7<<24)   /* count if ID = 0                  */
+
+/* interrupts */
+#define OTG_INT_GLOBAL         (1<<31)   /* global interrupt enable          */
+#define OTG_INT_ENALL          0x7fff    /* enable all                       */
+#define OTG_INT_DISALL         0         /* disable all                      */
+#define OTG_INT_IDC            (1<<0)    /* ID pin change                    */
+#define OTG_INT_VBVC           (1<<1)    /* VBUS valid change                */
+#define OTG_INT_SVC            (1<<2)    /* Session valid change             */
+#define OTG_INT_SEC            (1<<3)    /* Session end change               */
+#define OTG_INT_LSTC           (1<<4)    /* Line state change                */
+#define OTG_INT_PSPDC          (1<<5)    /* Port speed change                */
+#define OTG_INT_FSOEC          (1<<6)    /* FS/LS OE change                  */
+#define OTG_INT_HSDD           (1<<7)    /* HS disconnect detected           */
+#define OTG_INT_RXACT          (1<<8)    /* Rx activity detected             */
+#define OTG_INT_PCC            (1<<9)    /* Port connect change              */
+#define OTG_INT_PSC            (1<<10)   /* Port suspend change              */
+#define OTG_INT_TMX            (1<<11)   /* Timer expired                    */
+#define OTG_INT_HNPFC          (1<<12)   /* HNP feature change               */
+#define OTG_INT_OCD            (1<<13)   /* over current detected            */
+#define OTG_INT_DPRC           (1<<14)   /* Downstream port reset change     */
+
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high                        ***/
+/*** HS-A0 WA: BUG-3950: gatget SV notification                            ***/
+#ifdef UDC_HSA0_WORKAROUNDS
+#define OTG_INT_ADDS   OTG_INT_SVC
+#endif
+
+/**********************************
+*  typical timer values
+**********************************/
+
+#define OTG_TMR_SYSTEM_LATENCY 0      /* platform specific                   */
+
+#define OTG_TMR_IDSNS_WAIT     10     /* (ms) ID sense wait                  */
+
+/*********************************/
+
+#define TIMER_PERIOD    1000          /* 10 ms, for periods longer than 10ms */
+
+/**********************************
+*  OTG state dependend data
+**********************************/
+
+/*
+ * generic
+ */
+#define OTG_CTL_DEFAULT                      (OTG_CTL_PADEN | \
+                                              OTG_CTL_IDSNSEN)
+#define OTG_CTL_HOST_DEFAULT                 (OTG_CTL_DEFAULT | \
+                                              OTG_CTL_ENABLE_UHC)
+#ifdef UDC_HSA0_WORKAROUNDS
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high                        ***/
+/*** HS-A0 WA: BUG-3943: gadget suspend issue                              ***/
+#ifdef CONFIG_USB_OTG
+#define OTG_CTL_PERIPHERAL_DEFAULT           (OTG_CTL_DEFAULT | \
+                                              OTG_CTL_HNPSFEN | \
+                                              OTG_CTL_ENABLE_UDC | \
+                                              OTG_CTL_PPO | OTG_CTL_PUEN)
+#else
+#define OTG_CTL_PERIPHERAL_DEFAULT           (OTG_CTL_DEFAULT | \
+                                              OTG_CTL_ENABLE_UDC | \
+                                              OTG_CTL_PPO | OTG_CTL_PUEN)
+#endif
+#else
+#ifdef CONFIG_USB_OTG
+#define OTG_CTL_PERIPHERAL_DEFAULT           (OTG_CTL_DEFAULT | \
+                                              OTG_CTL_HNPSFEN | \
+                                              OTG_CTL_ENABLE_UDC | OTG_CTL_PPO)
+#else
+#define OTG_CTL_PERIPHERAL_DEFAULT           (OTG_CTL_DEFAULT | \
+                                              OTG_CTL_ENABLE_UDC | OTG_CTL_PPO)
+#endif
+#endif
+
+#define OTG_INT_DEFAULT                      OTG_INT_IDC
+
+/*
+ * OTG_NOT_ASSIGNED
+ */
+#define OTG_NOT_ASSIGNED_CONTROL             (OTG_CTL_DEFAULT | OTG_CTL_PPO | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_NOT_ASSIGNED_STATUS              0
+#define OTG_NOT_ASSIGNED_STATUS_MASK         0
+#define OTG_NOT_ASSIGNED_INTERRUPTS          OTG_INT_TMX
+
+/*
+ * OTG_NO_B_DEVICE_A
+ */
+#define OTG_NO_B_DEVICE_A_CONTROL            OTG_CTL_HOST_DEFAULT
+#define OTG_NO_B_DEVICE_A_STATUS             0
+#define OTG_NO_B_DEVICE_A_STATUS_MASK        0
+#define OTG_NO_B_DEVICE_A_INTERRUPTS         OTG_INT_DEFAULT
+
+/*
+ * OTG_NO_B_DEVICE_B
+ */
+#define OTG_NO_B_DEVICE_B_CONTROL            (OTG_CTL_DEFAULT | OTG_CTL_PPO)
+#define OTG_NO_B_DEVICE_B_STATUS             0
+#define OTG_NO_B_DEVICE_B_STATUS_MASK        0
+#define OTG_NO_B_DEVICE_B_INTERRUPTS         OTG_INT_DEFAULT
+
+/*
+ * OTG_A_IDLE
+ */
+#define OTG_A_IDLE_CONTROL                   (OTG_CTL_DEFAULT | OTG_CTL_PPO)
+#define OTG_A_IDLE_STATUS                    0
+#define OTG_A_IDLE_STATUS_MASK               0
+#define OTG_A_IDLE_INTERRUPTS                (OTG_INT_DEFAULT | OTG_INT_SVC | \
+                                              OTG_INT_LSTC)
+
+/*
+ * OTG_A_IDLE_WAIT_DP
+ */
+#define OTG_A_IDLE_WAIT_DP_CONTROL           (OTG_A_IDLE_CONTROL | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_A_IDLE_WAIT_DP_STATUS            0
+#define OTG_A_IDLE_WAIT_DP_STATUS_MASK       0
+#define OTG_A_IDLE_WAIT_DP_INTERRUPTS        (OTG_A_IDLE_INTERRUPTS | \
+                                              OTG_INT_TMX)
+
+/*
+ * OTG_A_IDLE_WAIT_VP
+ */
+#define OTG_A_IDLE_WAIT_VP_CONTROL           (OTG_A_IDLE_CONTROL | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_A_IDLE_WAIT_VP_STATUS            0
+#define OTG_A_IDLE_WAIT_VP_STATUS_MASK       0
+#define OTG_A_IDLE_WAIT_VP_INTERRUPTS        (OTG_A_IDLE_INTERRUPTS | \
+                                              OTG_INT_TMX)
+
+/*
+ * OTG_A_IDLE_WAIT_MP
+ */
+#define OTG_A_IDLE_WAIT_MP_CONTROL           (OTG_A_IDLE_CONTROL | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_A_IDLE_WAIT_MP_STATUS            0
+#define OTG_A_IDLE_WAIT_MP_STATUS_MASK       0
+#define OTG_A_IDLE_WAIT_MP_INTERRUPTS        (OTG_A_IDLE_INTERRUPTS | \
+                                              OTG_INT_TMX)
+
+/*
+ * OTG_A_IDLE_WAIT_DV
+ */
+#define OTG_A_IDLE_WAIT_DV_CONTROL           (OTG_A_IDLE_CONTROL | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_A_IDLE_WAIT_DV_STATUS            0
+#define OTG_A_IDLE_WAIT_DV_STATUS_MASK       0
+#define OTG_A_IDLE_WAIT_DV_INTERRUPTS        (OTG_A_IDLE_INTERRUPTS | \
+                                              OTG_INT_TMX)
+
+/*
+ * OTG_A_WAIT_VRISE
+ */
+#define OTG_A_WAIT_VRISE_CONTROL             (OTG_CTL_HOST_DEFAULT | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_A_WAIT_VRISE_STATUS              0
+#define OTG_A_WAIT_VRISE_STATUS_MASK         0
+#define OTG_A_WAIT_VRISE_INTERRUPTS          (OTG_INT_DEFAULT | OTG_INT_TMX | \
+                                              OTG_INT_VBVC)
+
+/*
+ * OTG_A_WAIT_BCON
+ */
+#define OTG_A_WAIT_BCON_CONTROL              OTG_CTL_HOST_DEFAULT
+#define OTG_A_WAIT_BCON_STATUS               0
+#define OTG_A_WAIT_BCON_STATUS_MASK          0
+#define OTG_A_WAIT_BCON_INTERRUPTS           (OTG_INT_DEFAULT | OTG_INT_TMX | \
+                                              OTG_INT_VBVC | OTG_INT_PCC)
+
+/*
+ * OTG_A_WAIT_BCON_VB
+ */
+#define OTG_A_WAIT_BCON_VB_CONTROL           (OTG_A_WAIT_BCON_CONTROL | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_A_WAIT_BCON_VB_STATUS            0
+#define OTG_A_WAIT_BCON_VB_STATUS_MASK       0
+#define OTG_A_WAIT_BCON_VB_INTERRUPTS        OTG_A_WAIT_BCON_INTERRUPTS
+
+/*
+ * OTG_A_HOST
+ */
+#define OTG_A_HOST_CONTROL                    OTG_CTL_HOST_DEFAULT
+#define OTG_A_HOST_STATUS                     0
+#define OTG_A_HOST_STATUS_MASK                0
+#ifdef CONFIG_USB_OTG
+#ifndef VERBOSE
+#define OTG_A_HOST_INTERRUPTS                (OTG_INT_DEFAULT | \
+                                              OTG_INT_VBVC | OTG_INT_DPRC | \
+                                              OTG_INT_PCC | OTG_INT_PSC)
+#else
+#define OTG_A_HOST_INTERRUPTS                (OTG_INT_DEFAULT | \
+                                              OTG_INT_VBVC | OTG_INT_DPRC | \
+                                              OTG_INT_PCC | OTG_INT_PSC | \
+                                              OTG_INT_PSPDC)
+                                              /* OTG_INT_LSTC */
+#endif
+#else
+        /* IDPIN mode only                                                   */
+#define OTG_A_HOST_INTERRUPTS                OTG_INT_IDC
+#endif
+
+/*
+ * OTG_A_SUSPEND
+ */
+#define OTG_A_SUSPEND_CONTROL                (OTG_CTL_HOST_DEFAULT | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_A_SUSPEND_STATUS                 0
+#define OTG_A_SUSPEND_STATUS_MASK            0
+#define OTG_A_SUSPEND_INTERRUPTS             (OTG_INT_DEFAULT | OTG_INT_TMX | \
+                                              OTG_INT_VBVC |  OTG_INT_DPRC | \
+                                              OTG_INT_PCC | OTG_INT_PSC)
+
+/*
+ * OTG_A_PERIPHERAL
+ */
+#define OTG_A_PERIPHERAL_CONTROL             (OTG_CTL_PERIPHERAL_DEFAULT | \
+                                              OTG_CTL_PPWR | OTG_CTL_DMPDEN)
+#define OTG_A_PERIPHERAL_STATUS              0
+#define OTG_A_PERIPHERAL_STATUS_MASK         0
+#ifndef VERBOSE
+#define OTG_A_PERIPHERAL_INTERRUPTS          (OTG_INT_DEFAULT | \
+                                              OTG_INT_VBVC | OTG_INT_OCD | \
+                                              OTG_INT_PCC | OTG_INT_PSC)
+#else
+#define OTG_A_PERIPHERAL_INTERRUPTS          (OTG_INT_DEFAULT | \
+                                              OTG_INT_VBVC | OTG_INT_OCD | \
+                                              OTG_INT_PCC | OTG_INT_PSC | \
+                                              OTG_INT_PSPDC)
+                                              /* OTG_INT_LSTC */
+#endif
+
+#ifdef OTG_HSA0_HNP_WA
+/*
+ * OTG_A_PERIPHERAL_HNPFIX
+ */
+#define OTG_A_PERIPHERAL_HNPFIX              (1 | OTG_A_PERIPHERAL)
+#define OTG_TMR_HNP_A_WAIT_ASUSP             2000
+
+#define OTG_A_PERIPHERAL_HNPFIX_CONTROL      (OTG_A_PERIPHERAL_CONTROL | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_A_PERIPHERAL_HNPFIX_STATUS       0
+#define OTG_A_PERIPHERAL_HNPFIX_STATUS_MASK  0
+#define OTG_A_PERIPHERAL_HNPFIX_INTERRUPTS   (OTG_A_PERIPHERAL_INTERRUPTS | \
+                                              OTG_INT_TMX)
+#endif
+
+/*
+ * OTG_A_VBUS_ERROR
+ */
+#define OTG_A_VBUS_ERROR_CONTROL             (OTG_CTL_HOST_DEFAULT | \
+                                              OTG_CTL_PPO | OTG_CTL_DISCHRG)
+#define OTG_A_VBUS_ERROR_STATUS              0
+#define OTG_A_VBUS_ERROR_STATUS_MASK         0
+#define OTG_A_VBUS_ERROR_INTERRUPTS          OTG_INT_DEFAULT
+
+/*
+ * OTG_A_WAIT_VFALL
+ */
+#define OTG_A_WAIT_VFALL_CONTROL             (OTG_CTL_HOST_DEFAULT | \
+                                              OTG_CTL_PPO)
+#define OTG_A_WAIT_VFALL_STATUS              0
+#define OTG_A_WAIT_VFALL_STATUS_MASK         0
+#define OTG_A_WAIT_VFALL_INTERRUPTS          (OTG_INT_DEFAULT | OTG_INT_SEC)
+
+/*
+ * OTG_A_WAIT_VFALL_DN
+ */
+#define OTG_A_WAIT_VFALL_DN_CONTROL          (OTG_A_WAIT_VFALL_CONTROL | \
+                                              OTG_CTL_DISCHRG)
+#define OTG_A_WAIT_VFALL_DN_STATUS           0
+#define OTG_A_WAIT_VFALL_DN_STATUS_MASK      0
+#define OTG_A_WAIT_VFALL_DN_INTERRUPTS       OTG_A_WAIT_VFALL_INTERRUPTS
+
+#ifdef CONFIG_USB_OTG_FORCE_SUSPEND
+/*
+ * OTG_A_WAIT_BSUSPEND
+ */
+#define OTG_A_WAIT_BSUSPEND_CONTROL          (OTG_CTL_DEFAULT | \
+                                              OTG_CTL_PPO | OTG_CTL_PPWR | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_A_WAIT_BSUSPEND_STATUS           0
+#define OTG_A_WAIT_BSUSPEND_STATUS_MASK      0
+#define OTG_A_WAIT_BSUSPEND_INTERRUPTS       (OTG_INT_DEFAULT | OTG_INT_TMX | \
+                                              OTG_INT_VBVC | OTG_INT_OCD | \
+                                              OTG_INT_PSPDC | OTG_INT_LSTC)
+
+/*
+ * OTG_A_WAIT_BDISCON
+ */
+#ifdef OTG_HSA0_HNP_WA
+#define OTG_TMR_HNP_WA(time)  2 * OTG_TMR_HNP_A_WAIT_ASUSP + (OTG_TMR_##time)
+#endif
+#define OTG_A_WAIT_BDISCON_CONTROL           (OTG_CTL_DEFAULT | \
+                                              OTG_CTL_PPO | OTG_CTL_PPWR | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_A_WAIT_BDISCON_STATUS            0
+#define OTG_A_WAIT_BDISCON_STATUS_MASK       0
+#define OTG_A_WAIT_BDISCON_INTERRUPTS        (OTG_INT_DEFAULT | OTG_INT_TMX | \
+                                              OTG_INT_VBVC | OTG_INT_OCD | \
+                                              OTG_INT_PSPDC | OTG_INT_LSTC)
+#endif  /* CONFIG_USB_OTG_FORCE_SUSPEND */
+
+/*
+ * OTG_B_IDLE
+ */
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high                        ***/
+/*** HS-A0 WA: BUG-3943: gadget suspend issue                              ***/
+#ifdef UDC_HSA0_WORKAROUNDS
+#define OTG_B_IDLE_CONTROL                   (OTG_CTL_PERIPHERAL_DEFAULT & \
+                                              ~((u32) (OTG_CTL_PUEN | \
+                                                       OTG_CTL_ENABLE_UDC)))
+#else
+#define OTG_B_IDLE_CONTROL                   OTG_CTL_PERIPHERAL_DEFAULT
+#endif
+#define OTG_B_IDLE_STATUS                    0
+#define OTG_B_IDLE_STATUS_MASK               0
+#ifdef CONFIG_USB_OTG
+#define OTG_B_IDLE_INTERRUPTS                (OTG_INT_DEFAULT | OTG_INT_SVC)
+#else
+#ifdef CONFIG_USB_OTGMUX_IDPIN
+        /* IDPIN mode                                                        */
+#define OTG_B_IDLE_INTERRUPTS                (OTG_INT_IDC | OTG_INT_SVC)
+#else
+        /* gadget mode                                                       */
+#define OTG_B_IDLE_INTERRUPTS                OTG_INT_SVC
+#endif
+#endif
+
+/*
+ * OTG_B_PERIPHERAL
+ */
+#define OTG_B_PERIPHERAL_CONTROL             (OTG_CTL_PERIPHERAL_DEFAULT | \
+                                              OTG_CTL_DMPDEN)
+#define OTG_B_PERIPHERAL_STATUS              0
+#define OTG_B_PERIPHERAL_STATUS_MASK         0
+#ifdef CONFIG_USB_OTG
+#ifndef VERBOSE
+#define OTG_B_PERIPHERAL_INTERRUPTS          (OTG_INT_DEFAULT | OTG_INT_SVC | \
+                                              OTG_INT_PCC | OTG_INT_PSC | \
+                                              OTG_INT_HNPFC)
+#else
+#define OTG_B_PERIPHERAL_INTERRUPTS          (OTG_INT_DEFAULT | OTG_INT_SVC | \
+                                              OTG_INT_PCC | OTG_INT_PSC | \
+                                              OTG_INT_HNPFC | OTG_INT_PSPDC)
+                                              /* OTG_INT_LSTC */
+#endif
+#else
+#ifdef CONFIG_USB_OTGMUX_IDPIN
+        /* IDPIN mode                                                        */
+#define OTG_B_PERIPHERAL_INTERRUPTS          (OTG_INT_IDC | OTG_INT_SVC)
+#else
+        /* gadget mode                                                       */
+#define OTG_B_PERIPHERAL_INTERRUPTS          OTG_INT_SVC
+#endif
+#endif
+
+/*
+ * OTG_B_PERIPHERAL_WT
+ */
+#define OTG_B_PERIPHERAL_WT_CONTROL          (OTG_B_PERIPHERAL_CONTROL | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_B_PERIPHERAL_WT_STATUS           0
+#define OTG_B_PERIPHERAL_WT_STATUS_MASK      0
+#define OTG_B_PERIPHERAL_WT_INTERRUPTS       (OTG_B_PERIPHERAL_INTERRUPTS | \
+                                              OTG_INT_TMX)
+
+/*
+ * OTG_B_PERIPHERAL_DC
+ */
+#define OTG_B_PERIPHERAL_DC_CONTROL          (OTG_CTL_HOST_DEFAULT | \
+                                              OTG_CTL_PPO | OTG_CTL_TMR_UNCOND)
+#define OTG_B_PERIPHERAL_DC_STATUS           0
+#define OTG_B_PERIPHERAL_DC_STATUS_MASK      0
+#define OTG_B_PERIPHERAL_DC_INTERRUPTS       (OTG_B_PERIPHERAL_INTERRUPTS | \
+                                              OTG_INT_TMX | OTG_INT_LSTC)
+
+/*
+ * OTG_B_WAIT_ACON
+ */
+#define OTG_B_WAIT_ACON_CONTROL              (OTG_CTL_HOST_DEFAULT | \
+                                              OTG_CTL_PPO | OTG_CTL_TMR_UNCOND)
+#define OTG_B_WAIT_ACON_STATUS               0
+#define OTG_B_WAIT_ACON_STATUS_MASK          0
+#define OTG_B_WAIT_ACON_INTERRUPTS           (OTG_INT_DEFAULT | OTG_INT_SVC | \
+                                              OTG_INT_PCC | OTG_INT_PSC | \
+                                              OTG_INT_HNPFC | OTG_INT_TMX)
+
+/*
+ * OTG_B_HOST
+ */
+#define OTG_B_HOST_CONTROL                   (OTG_CTL_HOST_DEFAULT | \
+                                              OTG_CTL_PPO)
+#define OTG_B_HOST_STATUS                    0
+#define OTG_B_HOST_STATUS_MASK               0
+#ifndef VERBOSE
+#define OTG_B_HOST_INTERRUPTS                (OTG_INT_DEFAULT | OTG_INT_SVC | \
+                                              OTG_INT_PCC)
+#else
+#define OTG_B_HOST_INTERRUPTS                (OTG_INT_DEFAULT | OTG_INT_SVC | \
+                                              OTG_INT_PCC | OTG_INT_PSPDC)
+                                              /* OTG_INT_LSTC */
+#endif
+
+/*
+ * OTG_B_SRP_INIT
+ */
+#define OTG_B_SRP_INIT_CONTROL               OTG_B_IDLE_CONTROL
+#define OTG_B_SRP_INIT_STATUS                0
+#define OTG_B_SRP_INIT_STATUS_MASK           0
+#define OTG_B_SRP_INIT_INTERRUPTS            OTG_INT_DEFAULT 
+
+/*
+ * OTG_B_SRP_INIT_WAIT_SE0
+ */
+#define OTG_B_SRP_INIT_WAIT_SE0_CONTROL      (OTG_B_SRP_INIT_CONTROL | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_B_SRP_INIT_WAIT_SE0_STATUS       0
+#define OTG_B_SRP_INIT_WAIT_SE0_STATUS_MASK  0
+#define OTG_B_SRP_INIT_WAIT_SE0_INTERRUPTS   (OTG_B_SRP_INIT_INTERRUPTS | \
+                                              OTG_INT_TMX | OTG_INT_LSTC)
+
+/*
+ * OTG_B_SRP_INIT_D_PULSE
+ *
+ * note: changing to this state requires an additional call:
+ *       set_srp_conditions (dev);
+ *       reset_srp_conditions (dev) is required for the next state
+ */
+/*** HS-A0 WA: BUG-3885: VB_SESS_VLD value too high                        ***/
+/*** HS-A0 WA: BUG-3943: gadget suspend issue                              ***/
+/* note: set_/reset_srp_conditions is not needed if HS-A0 WAs are active
+ */
+#ifdef UDC_HSA0_WORKAROUNDS
+#define OTG_B_SRP_INIT_D_PULSE_CONTROL       (OTG_CTL_PERIPHERAL_DEFAULT | \
+                                              OTG_CTL_PUEN | \
+                                              OTG_CTL_TMR_UNCOND)
+#else
+#define OTG_B_SRP_INIT_D_PULSE_CONTROL       (OTG_B_SRP_INIT_CONTROL | \
+                                              OTG_CTL_PUEN | \
+                                              OTG_CTL_TMR_UNCOND)
+#endif
+#define OTG_B_SRP_INIT_D_PULSE_STATUS        0
+#define OTG_B_SRP_INIT_D_PULSE_STATUS_MASK   0
+#define OTG_B_SRP_INIT_D_PULSE_INTERRUPTS    (OTG_B_SRP_INIT_INTERRUPTS | \
+                                              OTG_INT_SEC | OTG_INT_TMX)
+
+/*
+ * OTG_B_SRP_INIT_V_PULSE
+ */
+#define OTG_B_SRP_INIT_V_PULSE_CONTROL       (OTG_B_SRP_INIT_CONTROL | \
+                                              OTG_CTL_CHRG | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_B_SRP_INIT_V_PULSE_STATUS        0
+#define OTG_B_SRP_INIT_V_PULSE_STATUS_MASK   0
+#define OTG_B_SRP_INIT_V_PULSE_INTERRUPTS    (OTG_B_SRP_INIT_INTERRUPTS | \
+                                              OTG_INT_TMX)
+
+/*
+ * OTG_B_SRP_INIT_V_DCHRG
+ */
+#define OTG_B_SRP_INIT_V_DCHRG_CONTROL       (OTG_B_SRP_INIT_CONTROL | \
+                                              OTG_CTL_DISCHRG | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_B_SRP_INIT_V_DCHRG_STATUS        0
+#define OTG_B_SRP_INIT_V_DCHRG_STATUS_MASK   0
+#define OTG_B_SRP_INIT_V_DCHRG_INTERRUPTS    (OTG_B_SRP_INIT_INTERRUPTS | \
+                                              OTG_INT_TMX)
+
+/*
+ * OTG_B_SRP_INIT_WAIT_VBUS
+ */
+#define OTG_B_SRP_INIT_WAIT_VBUS_CONTROL     (OTG_B_SRP_INIT_CONTROL | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_B_SRP_INIT_WAIT_VBUS_STATUS      0
+#define OTG_B_SRP_INIT_WAIT_VBUS_STATUS_MASK 0
+#define OTG_B_SRP_INIT_WAIT_VBUS_INTERRUPTS  (OTG_B_SRP_INIT_INTERRUPTS | \
+                                              OTG_INT_SVC | OTG_INT_TMX)
+
+#ifdef CONFIG_USB_OTG_FORCE_SUSPEND
+/*
+ * OTG_B_WAIT_ADISCON
+ */
+#define OTG_B_WAIT_ADISCON_CONTROL           (OTG_CTL_DEFAULT | OTG_CTL_PPO | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_B_WAIT_ADISCON_STATUS            0
+#define OTG_B_WAIT_ADISCON_STATUS_MASK       0
+#define OTG_B_WAIT_ADISCON_INTERRUPTS        (OTG_INT_DEFAULT | OTG_INT_SVC | \
+                                              OTG_INT_PSPDC | OTG_INT_LSTC | \
+                                              OTG_INT_TMX)
+
+/*
+ * OTG_B_WAIT_ASUSPEND
+ */
+#define OTG_B_WAIT_ASUSPEND_CONTROL          (OTG_CTL_DEFAULT | OTG_CTL_PPO | \
+                                              OTG_CTL_TMR_UNCOND)
+#define OTG_B_WAIT_ASUSPEND_STATUS           0
+#define OTG_B_WAIT_ASUSPEND_STATUS_MASK      0
+#define OTG_B_WAIT_ASUSPEND_INTERRUPTS       (OTG_INT_DEFAULT | OTG_INT_SVC | \
+                                              OTG_INT_PSPDC | OTG_INT_LSTC | \
+                                              OTG_INT_TMX)
+#endif  /* CONFIG_USB_OTG_FORCE_SUSPEND */
+
+/*********************************/
+
+/* other */
+
+#define OTG_APP_REQ_ACK        0
+
+
+/*****************************************************************************
+*  Types
+*****************************************************************************/
+
+
+/*****************************************************************************
+*  Macros
+*****************************************************************************/
+
+/* printing messages */
+
+#define INFO(args...) \
+        printk(KERN_INFO DRIVER_NAME_FOR_PRINT ": " args)
+
+#define WARN(args...) \
+        printk(KERN_WARNING DRIVER_NAME_FOR_PRINT " warning: " args)
+
+#define ERR(args...) \
+        printk(KERN_ERR DRIVER_NAME_FOR_PRINT " error: " args)
+
+#ifdef DEBUG
+#define DBG(args...) \
+        printk(KERN_DEBUG DRIVER_NAME_FOR_PRINT " debug: " args)
+#else
+#define DBG(args...) \
+        do {} while (0)
+#endif
+
+#ifdef VERBOSE
+#define VDBG DBG
+#else
+#define VDBG(args...) \
+	do { } while (0)
+#endif
+
+/****************************************************************************/
+
+/* this should always return "1" and print something in verbose mode */
+#ifdef VERBOSE
+#define VDBG_SPC(fmt,args...) \
+	(VDBG (fmt, args) ? 1 : 1)
+#else
+#define VDBG_SPC(fmt,args...) 1
+#endif
+
+/* query bit(s) (long: 32-bit access) */
+#define IS_BIT_RES(data, code) \
+	(!((data) & (code)) ? \
+	 (VDBG_SPC ("  OTG HW status: %s is reset\n", #data)) : 0)
+
+#define IS_BIT_SET(data, code) \
+	(((data) & (code)) ? \
+	 (VDBG_SPC ("  OTG HW status: %s is set\n", #data)) : 0)
+
+/* query SW flag(s) */
+#define IS_FLAG_RES(data) \
+	(!((data) & otg_ctl->params) ? \
+	 (VDBG_SPC ("  OTG SW status: %s is reset\n", #data)) : 0)
+
+#define IS_FLAG_SET(data) \
+	(((data) & otg_ctl->params) ? \
+	 (VDBG_SPC ("  OTG SW status: %s is set\n", #data)) : 0)
+
+/* query event bit(s) */
+#define GOT_EVENT(data, code) \
+	(((data) & (code)) ? \
+	 (VDBG_SPC ("  OTG event: %s\n", #data)) : 0)
+
+/* set SW flag */
+#ifdef VERBOSE
+#define SET_FLAG(data) \
+	if (!((data) & otg_ctl->params)) \
+		DBG ("  OTG SW status change: set flag %s\n", #data); \
+	otg_ctl->params |= (data)
+#else
+#define SET_FLAG(data) \
+	otg_ctl->params |= (data)
+#endif
+
+/* reset SW flag */
+#ifdef VERBOSE
+#define RES_FLAG(data) \
+	if ((data) & otg_ctl->params) \
+		DBG ("  OTG SW status change: reset flag %s\n", #data); \
+	otg_ctl->params &= ~((u32) (data))
+#else
+#define RES_FLAG(data) \
+	otg_ctl->params &= ~((u32) (data))
+#endif
+
+/* reset event bit */
+#define RES_EVENT(data, code) \
+	(code) &= ~((u32) (data))
+/* NOTE: this is not really needed so far, might be replaced with */
+/* #define RES_EVENT(data, code) \                                */
+/* 	do {} while (0)                                           */
+
+/* change OTG state */
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE  /* Au1200 */
+#define CHANGE_STATE(dev, state, pMask) \
+	if (((state##_CONTROL) & OTG_CTL_PPO) && \
+	    (~(state##_CONTROL) & OTG_CTL_PPWR)) \
+		au1000gpio_bit_set (USB_VBUS_GPIO); \
+	else \
+		au1000gpio_bit_clear (USB_VBUS_GPIO); \
+	writel ((state##_CONTROL), &(dev)->regs->ctl); \
+	*(pMask) = (state##_INTERRUPTS); \
+	(dev)->otg_ctl->act_state = (state); \
+	DBG ("OTG new state: %s\n", #state)
+#else
+#define CHANGE_STATE(dev, state, pMask) \
+	writel ((state##_CONTROL), &(dev)->regs->ctl); \
+	*(pMask) = (state##_INTERRUPTS); \
+	(dev)->otg_ctl->act_state = (state); \
+	DBG ("OTG new state: %s\n", #state)
+#endif
+
+/* verify OTG state */
+#ifndef CONFIG_OTG_TEST_MODE
+
+#define CHECK_STATE(dev, state) \
+	(dev)->otg_ctl->prv_state = (state); \
+	VDBG ("OTG state: %s\n", #state)
+#else
+#define CHECK_STATE(dev, state) \
+	(dev)->otg_ctl->prv_state = (state); \
+	if (((readl (&(dev)->regs->sts) ^ (state##_STATUS))) & \
+	    state##_STATUS_MASK) \
+		WARN ("OTG warning: incorrect status\n"); \
+	VDBG ("OTG state: %s\n", #state)
+#endif
+
+/* set timer */
+#define SET_OTG_TIMER(dev, val) \
+	set_timer ((dev), ((OTG_TMR_##val) * 100))
+
+/* set timer ( >10ms ) */
+#define SET_OTG_TIMER_LONG(dev, val) \
+	set_timer_long ((dev), ((OTG_TMR_##val) / 10))
+
+#ifdef VERBOSE
+#define HS_DISCON_WARNING() \
+	if (!(OTG_CTL_ENABLE_UHC ^ \
+	      (OTG_CTL_MUX_MASK & readl (&dev->regs->ctl))) && \
+	    !(OTG_STS_PSPD & readl (&dev->regs->sts))) \
+		DBG ("  OTG warning: disable UHC from HS-mode\n")
+#else
+#define HS_DISCON_WARNING() \
+	do { } while (0)
+#endif
+
+
+/*****************************************************************************
+*  Data
+*****************************************************************************/
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+struct otg_regs {
+	u32 cap;          /* capabilities */
+	u32 mux;          /* mux */
+	u32 sts;          /* status */
+	u32 ctl;          /* control */
+	u32 tmr;          /* timer */
+	u32 intr;         /* interrupt request */
+	u32 inten;        /* interrupt enable */
+} __attribute__ ((packed));
+
+
+#ifdef CONFIG_TEST_INTERFACE
+struct usb_test_otg_extension {
+	int (*bind) (struct otg **);
+	int (*unbind) (struct otg **);
+	int (*request) (u32);	/* function call for state change requests */
+	u32 (*query) (int);	/* function call to query state */
+};
+#endif
+
+#ifdef	__KERNEL__
+
+/* 2.5 and 2.4.older portability changes ... */
+
+#ifndef container_of
+#define	container_of	list_entry
+#endif
+
+#ifndef likely
+#define likely(x) (x)
+#define unlikely(x) (x)
+#endif
+
+#ifndef BUG_ON
+#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0)
+#endif
+
+#ifndef WARN_ON
+#define	WARN_ON(x)	do { } while (0)
+#endif
+
+#endif	/* __KERNEL__ */
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+/*****************************************************************************
+*  Functions
+*****************************************************************************/
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+void otg_init_state (struct otg *);
+
+int otg_exit_state (struct otg *);
+
+#ifdef CONFIG_USB_OTG
+u32 otg_change_state (struct otg *, u32, u32 *);
+#endif
+
+#ifdef DEBUG
+static void print_regs (struct otg *);
+#endif  /* DEBUG */
+
+#ifdef CONFIG_TEST_INTERFACE
+int usb_otg_test_register_driver (struct usb_test_otg_extension *);
+int usb_otg_test_unregister_driver (struct usb_test_otg_extension *);
+#endif
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+/*****************************************************************************
+*  Inline Functions
+*****************************************************************************/
+
+#if defined(CONFIG_USB_OTG) || defined(CONFIG_USB_OTGMUX_IDPIN) || \
+    defined(CONFIG_USB_HSA0_GADGET)
+
+extern u32 high_count;
+extern struct otg_ctl *otg_ctl;
+
+/**
+ * \brief
+ * Read the status register
+ *
+ * \param  dev   OTG controller info
+ *
+ * \return status
+ */
+static inline u32 get_status (struct otg *dev)
+{
+	return (readl (&dev->regs->sts));
+}
+
+/**
+ * \brief
+ * Load and start the timer for an unconditional run
+ *
+ * \param  dev   OTG controller info
+ * \param  val   Value to load
+ *
+ * \return void
+ */
+static inline void set_timer (struct otg *dev, u32 val)
+{
+	high_count = 0;
+
+	writel ((val - OTG_TMR_SYSTEM_LATENCY), &dev->regs->tmr);
+	VDBG ("  OTG action: start timer: %d0 us\n", val);
+}
+
+/**
+ * \brief
+ * Load and start the timer for an unconditional run
+ *
+ * \param  dev   OTG controller info
+ * \param  val   Value to load
+ *
+ * \return void
+ */
+static inline void set_timer_long (struct otg *dev, u32 val)
+{
+	high_count = val - 1;
+
+	writel ((TIMER_PERIOD - OTG_TMR_SYSTEM_LATENCY), &dev->regs->tmr);
+	VDBG ("  OTG action: start timer: %d0 ms\n", val);
+}
+
+/**
+ * \brief
+ * Re-start the timer (value already loaded)
+ *
+ * \param  dev   OTG controller info
+ *
+ * \return void
+ */
+static inline void restart_timer (struct otg *dev)
+{
+	writel ((readl (&dev->regs->ctl) | OTG_CTL_TMR_UNCOND),
+	        &dev->regs->ctl);
+}
+
+/**
+ * \brief
+ * Reset the timer while running (value already loaded)
+ *
+ * \param  dev   OTG controller info
+ *
+ * \return void
+ */
+static inline void reset_timer (struct otg *dev)
+{
+	u32 temp;
+
+	temp = readl (&dev->regs->ctl);
+	writel ((temp & ~((u32) OTG_CTL_TMR_ALL)), &dev->regs->ctl);
+	writel (temp, &dev->regs->ctl);
+	VDBG ("  OTG action: re-start timer\n");
+}
+
+/**
+ * \brief
+ * Prepare the D-pulse
+ *
+ * \param  dev   OTG controller info
+ *
+ * \return void
+ */
+static inline void set_srp_conditions (struct otg *dev)
+{
+#ifdef UDC_HSA0_WORKAROUNDS
+	VDBG ("  OTG action: SRP init: no action needed due to A0 WAs\n");
+#else
+	writel ((readl (&dev->regs->cap) & ~((u32) OTG_CAP_APU)),
+	        &dev->regs->cap);
+	VDBG ("  OTG action: set SRP conditions: disable APU\n");
+#endif
+}
+
+/**
+ * \brief
+ * Reset conditions after SRP
+ *
+ * activates the auto-pull-up feature so after SRP the host
+ * will detect a device connect after calling this function
+ *
+ * \param  dev   OTG controller info
+ *
+ * \return void
+ */
+static inline void reset_srp_conditions (struct otg *dev)
+{
+#ifdef UDC_HSA0_WORKAROUNDS
+	VDBG ("  OTG action: SRP done: no action needed due to A0 WAs\n");
+#else
+	writel ((readl (&dev->regs->cap) | OTG_CAP_APU),
+	        &dev->regs->cap);
+	VDBG ("  OTG action: reset SRP conditions: enable APU\n");
+#endif
+}
+
+/**
+ * \brief
+ * enable HNP for both devices
+ *
+ * \param  dev   OTG controller info
+ *
+ * \return success
+ */
+static inline int otg_enable_hnp (struct otg *dev)
+{
+	int  retVal;
+
+	if (!dev->device) {
+		ERR ("Tried to send OTG request, device pointer not valid\n");
+		retVal = -ENODEV;
+	}
+	else {
+		retVal = usb_otg_enable_b_hnp (dev->device);
+		DBG ("  OTG action: enabled HNP in B-device\n");
+	}
+	if (retVal < 0) {
+		RES_FLAG (OTG_A_bHNP_EN);
+	}
+	else {
+		SET_FLAG (OTG_A_bHNP_EN);
+	}
+	return retVal;
+}
+
+#endif   /* CONFIG_USB_OTGMUX */
+#ifdef DEBUG
+/**
+ * \brief
+ * Print OTG controller registers (debug mode only)
+ *
+ * \param dev    OTG controller info
+ *
+ * \return void
+ */
+static inline void print_regs (struct otg *dev)
+{
+	DBG( "-- UOC registers ---\n");
+	DBG( "otg cap   = %08x\n", readl (&dev->regs->cap));
+	DBG( "otg mux   = %08x\n", readl (&dev->regs->mux));
+	DBG( "otg sts   = %08x\n", readl (&dev->regs->sts));
+	DBG( "otg ctl   = %08x\n", readl (&dev->regs->ctl));
+	DBG( "otg tmr   = %08x\n", readl (&dev->regs->tmr));
+	DBG( "otg intr  = %08x\n", readl (&dev->regs->intr));
+	DBG( "otg inten = %08x\n", readl (&dev->regs->inten));
+	DBG( "--------------------\n");
+}
+#endif  /* DEBUG */
+
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE  /* Au1200 */
+
+/**
+ * \brief
+ * GPIO functions (Au1200 only)
+ *
+ * \param  data  pin info
+ *
+ * \return success
+ */
+static inline int au1000gpio_bit_get (u32 data)
+{
+	return (au_readl ((SYS_PINSTATERD & (1 << data)) ? 1 : 0));
+}
+
+/* #if defined(CONFIG_AU1X00_GPIO) || defined(CONFIG_AU1X00_GPIO_MODULE) */
+#ifdef CONFIG_AU1X00_GPIO
+
+	/* GPIO functions are defined in drivers/char/au1000_gpio.c          */
+
+#else
+
+static inline int au1000gpio_bit_tristate (u32 data)
+{
+	au_writel ((1 << data), SYS_TRIOUTCLR);
+	return 0;
+}
+
+static inline int au1000gpio_bit_set (u32 data)
+{
+	au_writel ((1 << data), SYS_OUTPUTSET);
+	return 0;
+}
+
+static inline int au1000gpio_bit_clear (u32 data)
+{
+	au_writel ((1 << data), SYS_OUTPUTCLR);
+	return 0;
+}
+
+static inline int au1000gpio_bit_init (u32 data, au1x00_gpio_bit_ctl *opt)
+{
+	if (opt->direction) {
+
+		if (opt->data) {
+			return (au1000gpio_bit_set (data));
+		}
+		else {
+			return (au1000gpio_bit_clear (data));
+		}
+	}
+	else {
+		au_writel ((~au_readl (SYS_TRIOUTRD) | (1 << data)),
+		           SYS_TRIOUTCLR);
+	}
+	return 0;
+}
+
+static inline int au1000gpio_bit_term (u32 data, au1x00_gpio_bit_ctl *opt)
+{
+	if (opt->direction) {
+
+		if (opt->data < 0) {
+			return (au1000gpio_bit_tristate (data));
+		}
+		else {
+			if (opt->data) {
+				return (au1000gpio_bit_set (data));
+			}
+			else {
+				return (au1000gpio_bit_clear (data));
+			}
+		}
+	}
+	return 0;
+}
+
+#endif
+#endif  /* CONFIG_USB_NON_PCI_OTGDEVICE (Au1200) */
+
+#ifndef CONFIG_USB_OTGMUX
+/**
+ * \brief
+ * Set muxer to neutral and drop VBus
+ *
+ * \param  dev   OTG controller info
+ *
+ * \return void
+ */
+static inline void disable_both (struct otg *dev)
+{
+	writel ((OTG_CTL_PPO | OTG_CTL_PADEN), &dev->regs->ctl);
+
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE  /* Au1200 */
+	if (au1000gpio_bit_set (USB_VBUS_GPIO))
+		ERR ("VBus error: couln't set GPIO bit\n");
+#endif
+
+	VDBG ("  OTG action: disable both, get power control, drop VBus\n");
+}
+
+/**
+ * \brief
+ * Set muxer and power control to support standard host operation
+ *
+ * \param  dev   OTG controller info
+ *
+ * \return void
+ */
+static inline void enable_uhc (struct otg *dev)
+{
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE  /* Au1200 */
+
+	writel ((OTG_CTL_ENABLE_UHC | OTG_CTL_PPO | OTG_CTL_PPWR |
+	         OTG_CTL_PADEN), &dev->regs->ctl);
+	if (au1000gpio_bit_clear (USB_VBUS_GPIO))
+		ERR ("VBus error: couln't clear GPIO bit\n");
+	VDBG ("  OTG action: enable UHC, get power control, rise VBus\n");
+#else
+
+	writel ((OTG_CTL_ENABLE_UHC | OTG_CTL_PADEN), &dev->regs->ctl);
+	VDBG ("  OTG action: enable UHC, release power control\n");
+#endif
+}
+
+/**
+ * \brief
+ * Set muxer and power to support standard gadget operation
+ *
+ * \param  dev   OTG controller info
+ *
+ * \return void
+ */
+static inline void enable_udc (struct otg *dev)
+{
+	writel ((OTG_CTL_ENABLE_UDC | OTG_CTL_PPO | OTG_CTL_PUEN |
+	         OTG_CTL_PADEN), &dev->regs->ctl);
+
+#ifdef CONFIG_USB_NON_PCI_OTGDEVICE  /* Au1200 */
+	if (au1000gpio_bit_set (USB_VBUS_GPIO))
+		ERR ("VBus error: couln't set GPIO bit\n");
+#endif
+
+	VDBG ("  OTG action: enable UDC, get power control, drop VBus\n");
+}
+#endif  /* ! CONFIG_USB_OTGMUX */
+
+#endif /* AMD5536UOC_H */
+
diff -Nur linux-2.4.30/drivers/usb/gadget/Config.in linux-2.4.30-linux-mips.org/drivers/usb/gadget/Config.in
--- linux-2.4.30/drivers/usb/gadget/Config.in	2005-01-19 15:10:06.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/usb/gadget/Config.in	2005-05-12 18:01:29.000000000 +0200
@@ -18,6 +18,7 @@
   choice 'USB Peripheral Controller Driver' "\
 	NetChip-2280			CONFIG_USB_GADGET_NET2280 \
 	Toshiba-TC86C001(Goku-S)	CONFIG_USB_GADGET_GOKU \
+	AMD5536-UDC	                CONFIG_USB_GADGET_AMD5536UDC \
 	" NetChip-2280
 
   define_tristate CONFIG_USB_GADGET_CONTROLLER n
@@ -30,9 +31,14 @@
     define_tristate CONFIG_USB_GOKU $CONFIG_USB_GADGET
     define_tristate CONFIG_USB_GADGET_CONTROLLER $CONFIG_USB_GOKU
   fi
+  if [ "$CONFIG_PCI" = "y" -a "$CONFIG_USB_GADGET_AMD5536UDC" = "y" ] ; then
+    define_tristate CONFIG_USB_AMD5536UDC $CONFIG_USB_GADGET
+    define_tristate CONFIG_USB_GADGET_CONTROLLER $CONFIG_USB_AMD5536UDC
+  fi
 
   # or any other controller that supports high speed transfers ...
   define_bool CONFIG_USB_GADGET_DUALSPEED $CONFIG_USB_GADGET_NET2280
+  define_bool CONFIG_USB_GADGET_DUALSPEED $CONFIG_USB_GADGET_AMD5536UDC
 
   if [ "$CONFIG_USB_GADGET_CONTROLLER" = "y" -o "$CONFIG_USB_GADGET_CONTROLLER" = "m" ] ; then
 
@@ -81,4 +87,45 @@
     fi
   fi
 fi
+
+#
+# USB OTG configuration
+#
+  comment ''
+
+if [ "$CONFIG_PCI" = "y" -a "$CONFIG_USB_GADGET_AMD5536UDC" = "y" ] ; then
+ tristate 'Support for USB On-The-Go' CONFIG_USB_OTG_CONTROLLER
+ if [ "$CONFIG_USB_OTG_CONTROLLER" = "y" -o "$CONFIG_USB_OTG_CONTROLLER" = "m" ]; then
+   bool '  AMD 5536 On-The-Go support' CONFIG_USB_AMD5536OTG $CONFIG_USB_OTG_CONTROLLER
+   if [ "$CONFIG_USB_AMD5536OTG" = "y" -o  "$CONFIG_USB_AMD5536OTG" = "m" ]; then
+     comment '    OTG port functionality'
+        comment '      Full On-The-Go support not supported yet'
+     if [ "$CONFIG_USB_OTG" != "y" -a "$CONFIG_USB_OTGMUX_GADGET" != "y" -a "$CONFIG_USB_OTGMUX_IDPIN" != "y" ]; then
+       bool '      Host only' CONFIG_USB_OTGMUX_HOST
+       if [ "$CONFIG_USB_OTGMUX_HOST" = "y" ]; then
+         define_bool CONFIG_USB_OTGMUX_IDPIN n
+         define_bool CONFIG_USB_OTGMUX_GADGET n
+         define_bool CONFIG_USB_OTG n
+       fi
+     fi
+     if [ "$CONFIG_USB_OTG" != "y" -a "$CONFIG_USB_OTGMUX_HOST" != "y" -a "$CONFIG_USB_OTGMUX_IDPIN" != "y" ]; then
+       bool '      Gadget only' CONFIG_USB_OTGMUX_GADGET
+       if [ "$CONFIG_USB_OTGMUX_GADGET" = "y" ]; then
+         define_bool CONFIG_USB_OTGMUX_IDPIN n
+         define_bool CONFIG_USB_OTGMUX_HOST n
+         define_bool CONFIG_USB_OTG n
+       fi
+     fi
+     if [ "$CONFIG_USB_OTG" != "y" -a "$CONFIG_USB_OTGMUX_HOST" != "y" -a "$CONFIG_USB_OTGMUX_GADGET" != "y" ]; then
+       bool '      ID pin dependend' CONFIG_USB_OTGMUX_IDPIN
+       if [ "$CONFIG_USB_OTGMUX_IDPIN" = "y" ]; then
+         define_bool CONFIG_USB_OTGMUX_HOST n
+         define_bool CONFIG_USB_OTGMUX_GADGET n
+         define_bool CONFIG_USB_OTG n
+       fi
+     fi
+   fi
+ fi
+fi
+
 endmenu
diff -Nur linux-2.4.30/drivers/usb/gadget/ether.c linux-2.4.30-linux-mips.org/drivers/usb/gadget/ether.c
--- linux-2.4.30/drivers/usb/gadget/ether.c	2005-01-19 15:10:06.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/usb/gadget/ether.c	2005-05-12 18:01:29.000000000 +0200
@@ -162,8 +162,15 @@
 /* Thanks to NetChip Technologies for donating this product ID.
  * It's for devices with only CDC Ethernet configurations.
  */
+#ifdef CONFIG_USB_GADGET_AMD5536UDC
+#define CDC_VENDOR_NUM	0x0438		/* AMD */
+#define CDC_PRODUCT_NUM	0xdc01		/* Linux-USB Ethernet Gadget */
+/* TODO better place for define */
+#define UDC_MAX_BUFLEN 2048
+#else
 #define CDC_VENDOR_NUM	0x0525		/* NetChip */
 #define CDC_PRODUCT_NUM	0xa4a1		/* Linux-USB Ethernet Gadget */
+#endif
 
 /* For hardware that can't talk CDC, we use the same vendor ID that
  * ARM Linux has used for ethernet-over-usb, both with sa1100 and
@@ -260,6 +267,9 @@
 #define DEV_CONFIG_CDC
 #endif
 
+#ifdef CONFIG_USB_GADGET_AMD5536UDC
+#define	DEV_CONFIG_CDC
+#endif
 
 /* For CDC-incapable hardware, choose the simple cdc subset.
  * Anything that talks bulk (without notable bugs) can do this.
@@ -1162,6 +1172,10 @@
 			req = container_of (dev->tx_reqs.next,
 						struct usb_request, list);
 			list_del (&req->list);
+#ifdef CONFIG_USB_GADGET_AMD5536UDC
+                        if (req->buf)
+                                usb_ep_free_buffer (dev->in_ep, req->buf, req->dma, UDC_MAX_BUFLEN);
+#endif
 			usb_ep_free_request (dev->in_ep, req);
 		}
 		dev->in_ep = NULL;
@@ -1172,6 +1186,10 @@
 			req = container_of (dev->rx_reqs.next,
 						struct usb_request, list);
 			list_del (&req->list);
+#ifdef CONFIG_USB_GADGET_AMD5536UDC
+                        if (req->buf)
+                                usb_ep_free_buffer (dev->out_ep, req->buf, req->dma, UDC_MAX_BUFLEN);
+#endif
 			usb_ep_free_request (dev->out_ep, req);
 		}
 		dev->out_ep = NULL;
@@ -1341,7 +1359,7 @@
 		return;
 	}
 	req->buf = usb_ep_alloc_buffer (dev->status_ep, 16,
-				&dev->req->dma, GFP_ATOMIC);
+				&req->dma, GFP_ATOMIC);
 	if (req->buf == 0) {
 		DEBUG (dev, "status buf ENOMEM\n");
 free_req:
@@ -1785,7 +1803,9 @@
 		goto enomem;
 	}
 
+#ifndef CONFIG_USB_GADGET_AMD5536UDC
 	req->buf = skb->data;
+#endif
 	req->length = size;
 	req->complete = rx_complete;
 	req->context = skb;
@@ -1827,6 +1847,16 @@
 			break;
 		}
 
+#ifdef CONFIG_USB_GADGET_AMD5536UDC
+                /* TODO TDB we need to copy here because UDC needs dword aligned buffers */
+                VDEBUG(dev, "memcpy( skb->data %lx,req->buf %lx,req->actual %d)\n",  skb->data,req->buf,req->actual);
+                if (req->actual <= UDC_MAX_BUFLEN) {
+                        memcpy(skb->data, req->buf, req->actual);
+                }
+                else {
+                        ERROR(dev, "memcpy of %d bytes bigger than max %d\n", req->actual, UDC_MAX_BUFLEN);
+                }
+#endif
 		skb->dev = dev->net;
 		skb->protocol = eth_type_trans (skb, dev->net);
 		dev->stats.rx_packets++;
@@ -1895,6 +1925,15 @@
 		req = usb_ep_alloc_request (ep, gfp_flags);
 		if (!req)
 			return list_empty (list) ? -ENOMEM : 0;
+
+
+#ifdef CONFIG_USB_GADGET_AMD5536UDC
+                /* TODO TDB allocate buffer which must be dword aligned for UDC */
+                req->buf = usb_ep_alloc_buffer (ep, UDC_MAX_BUFLEN, 
+                                &req->dma, GFP_ATOMIC);
+#endif
+
+
 		list_add (&req->list, list);
 	}
 	return 0;
@@ -1906,6 +1945,10 @@
 
 		next = req->list.next;
 		list_del (&req->list);
+#ifdef CONFIG_USB_GADGET_AMD5536UDC
+                if (req->buf)
+                        usb_ep_free_buffer (ep, req->buf, req->dma, UDC_MAX_BUFLEN);
+#endif
 		usb_ep_free_request (ep, req);
 
 		if (next == list)
@@ -2039,7 +2082,19 @@
 		length = skb->len;
 	}
 #endif
+#ifdef CONFIG_USB_GADGET_AMD5536UDC
+                /* TODO TDB we need to copy here because UDC needs dword aligned buffers */
+        VDEBUG(dev, "memcpy(req->buf %lx, skb->data %lx, length %d)", req->buf, skb->data, length);
+        if (length <= UDC_MAX_BUFLEN) {
+                memcpy(req->buf, skb->data, length);
+        }
+        else {
+                ERROR(dev, "memcpy of %d bytes bigger than max %d\n", length, UDC_MAX_BUFLEN);
+        }
+        VDEBUG(dev, "past memcpy(req->buf %lx, skb->data %lx, length %d)", req->buf, skb->data, length);
+#else
 	req->buf = skb->data;
+#endif
 	req->context = skb;
 	req->complete = tx_complete;
 
@@ -2345,6 +2400,8 @@
 		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0210);
 	} else if (gadget_is_pxa27x(gadget)) {
 		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0211);
+	} else if (gadget_is_amd5536udc (gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0212);
 	} else {
 		/* can't assume CDC works.  don't want to default to
 		 * anything less functional on CDC-capable hardware,
diff -Nur linux-2.4.30/drivers/usb/gadget/file_storage.c linux-2.4.30-linux-mips.org/drivers/usb/gadget/file_storage.c
--- linux-2.4.30/drivers/usb/gadget/file_storage.c	2004-08-08 01:26:05.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/usb/gadget/file_storage.c	2005-05-12 18:01:29.000000000 +0200
@@ -249,9 +249,13 @@
  *
  * DO NOT REUSE THESE IDs with any other driver!!  Ever!!
  * Instead:  allocate your own, using normal USB-IF procedures. */
-#define DRIVER_VENDOR_ID	0x0525	// NetChip
-#define DRIVER_PRODUCT_ID	0xa4a5	// Linux-USB File-backed Storage Gadget
-
+#ifdef CONFIG_USB_GADGET_AMD5536UDC
+#define DRIVER_VENDOR_ID	0x0438		/* AMD */
+#define DRIVER_PRODUCT_ID	0xdc02		/* Linux-USB File-backed Storage Gadget */
+#else
+#define DRIVER_VENDOR_ID	0x0525		/* NetChip */
+#define DRIVER_PRODUCT_ID	0xa4a5		/* Linux-USB File-backed Storage Gadget */
+#endif
 
 /*
  * This driver assumes self-powered hardware and has no way for users to
@@ -1330,6 +1334,7 @@
 		case USB_DT_DEVICE:
 			VDBG(fsg, "get device descriptor\n");
 			value = min(ctrl->wLength, (u16) sizeof device_desc);
+                        device_desc.bMaxPacketSize0 = fsg->ep0->maxpacket;
 			memcpy(req->buf, &device_desc, value);
 			break;
 #ifdef CONFIG_USB_GADGET_DUALSPEED
@@ -1670,8 +1675,11 @@
 			curlun->sense_data = SS_INVALID_FIELD_IN_CDB;
 			return -EINVAL;
 		}
-		if (fsg->cmnd[1] & 0x08)	// FUA
-			curlun->filp->f_flags |= O_SYNC;
+                /* FUA avoids cache -> slow */
+                /* FIXME make sure that device is self powered otherwise
+                 * cached data may be lost on disconnect */
+		/*if (fsg->cmnd[1] & 0x08)	// FUA
+			curlun->filp->f_flags |= O_SYNC;*/
 	}
 	if (lba >= curlun->num_sectors) {
 		curlun->sense_data = SS_LOGICAL_BLOCK_ADDRESS_OUT_OF_RANGE;
@@ -3582,6 +3590,10 @@
 			mod_data.release = __constant_cpu_to_le16(0x0227);
 		else if (gadget_is_omap(fsg->gadget))
 			mod_data.release = __constant_cpu_to_le16(0x0228);
+		else if (gadget_is_amd5536udc(fsg->gadget)) {
+			mod_data.release = __constant_cpu_to_le16(0x0229);
+		        mod_data.can_stall = 0;
+                }
 		else {
 			WARN(fsg, "controller '%s' not recognized\n",
 				fsg->gadget->name);
diff -Nur linux-2.4.30/drivers/usb/gadget/gadget_chips.h linux-2.4.30-linux-mips.org/drivers/usb/gadget/gadget_chips.h
--- linux-2.4.30/drivers/usb/gadget/gadget_chips.h	2005-01-19 15:10:06.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/usb/gadget/gadget_chips.h	2005-05-12 18:01:29.000000000 +0200
@@ -74,6 +74,13 @@
 #define	gadget_is_pxa27x(g)	0
 #endif
 
+#ifdef CONFIG_USB_GADGET_AMD5536UDC
+#define	gadget_is_amd5536udc(g)	!strcmp("amd5536udc", (g)->name)
+#else
+#define	gadget_is_amd5536udc(g)	0
+#endif
+
+#
 // CONFIG_USB_GADGET_AT91RM9200
 // CONFIG_USB_GADGET_SX2
 // CONFIG_USB_GADGET_AU1X00
diff -Nur linux-2.4.30/drivers/usb/gadget/Makefile linux-2.4.30-linux-mips.org/drivers/usb/gadget/Makefile
--- linux-2.4.30/drivers/usb/gadget/Makefile	2004-08-08 01:26:05.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/usb/gadget/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -10,10 +10,21 @@
 
 obj-$(CONFIG_USB_NET2280)	+= net2280.o
 obj-$(CONFIG_USB_GOKU)		+= goku_udc.o
+obj-$(CONFIG_USB_AMD5536UDC)	+= amd5536udc.o
+
+ifeq ($(CONFIG_USB_AMD5536OTG),y)
+obj-$(CONFIG_USB_OTG_CONTROLLER)	+= amd5536otg.o
+endif
 
 # only one of these may be statically linked ...
 controller-$(CONFIG_USB_NET2280)	+= net2280.o
 controller-$(CONFIG_USB_GOKU)		+= goku_udc.o
+controller-$(CONFIG_USB_AMD5536UDC)	+= amd5536udc.o
+
+# OTG ...
+ifeq ($(CONFIG_USB_AMD5536OTG),y)
+controller-$(CONFIG_USB_OTG_CONTROLLER)	+= amd5536uoc.o
+endif
 
 # ... and only one of these, too; kbuild/kconfig don't help though.
 g_zero-objs			:= zero.o usbstring.o config.o epautoconf.o
@@ -29,6 +40,12 @@
 						epautoconf.o
 obj-$(CONFIG_USB_FILE_STORAGE)	+= g_file_storage.o
 
+# OTG ...
+otg-objs			:= amd5536uoc.o
+ifeq ($(CONFIG_USB_OTG),y)
+otg-objs			+= otg-fsm.o
+endif
+
 export-objs :=			$(controller-y) $(controller-m)
 
 include $(TOPDIR)/Rules.make
@@ -39,3 +56,6 @@
 	$(LD) -r -o $@ $(g_ether-objs)
 g_file_storage.o: $(g_file_storage-objs)
 	$(LD) -r -o $@ $(g_file_storage-objs)
+
+amd5536otg.o: $(otg-objs)
+	$(LD) -r -o $@ $(otg-objs)
diff -Nur linux-2.4.30/drivers/usb/gadget/zero.c linux-2.4.30-linux-mips.org/drivers/usb/gadget/zero.c
--- linux-2.4.30/drivers/usb/gadget/zero.c	2005-01-19 15:10:07.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/usb/gadget/zero.c	2005-05-12 18:01:29.000000000 +0200
@@ -201,8 +201,13 @@
  * Instead:  allocate your own, using normal USB-IF procedures.
  */
 #ifndef	CONFIG_USB_ZERO_HNPTEST
+#ifdef CONFIG_USB_GADGET_AMD5536UDC
+#define DRIVER_VENDOR_NUM	0x0438		/* AMD */
+#define DRIVER_PRODUCT_NUM	0xdc00		/* Linux-USB "Gadget Zero" */
+#else
 #define DRIVER_VENDOR_NUM	0x0525		/* NetChip */
 #define DRIVER_PRODUCT_NUM	0xa4a0		/* Linux-USB "Gadget Zero" */
+#endif
 #else
 #define DRIVER_VENDOR_NUM	0x1a0a		/* OTG test device IDs */
 #define DRIVER_PRODUCT_NUM	0xbadd
@@ -1198,6 +1203,9 @@
 		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0210);
 	} else if (gadget_is_pxa27x(gadget)) {
 		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0211);
+	} else if (gadget_is_amd5536udc(gadget)) {
+		device_desc.bcdDevice = __constant_cpu_to_le16 (0x0212);
+                loopdefault = 1;
 	} else {
 		/* gadget zero is so simple (for now, no altsettings) that
 		 * it SHOULD NOT have problems with bulk-capable hardware.
@@ -1227,6 +1235,7 @@
 		goto enomem;
 	dev->req->buf = usb_ep_alloc_buffer (gadget->ep0, USB_BUFSIZ,
 				&dev->req->dma, GFP_KERNEL);
+        dev->req->length = USB_BUFSIZ;
 	if (!dev->req->buf)
 		goto enomem;
 
diff -Nur linux-2.4.30/drivers/usb/hcd.c linux-2.4.30-linux-mips.org/drivers/usb/hcd.c
--- linux-2.4.30/drivers/usb/hcd.c	2004-04-14 15:05:32.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/usb/hcd.c	2005-05-12 18:01:29.000000000 +0200
@@ -1505,3 +1505,166 @@
 	urb->complete (urb);
 }
 EXPORT_SYMBOL (usb_hcd_giveback_urb);
+
+#ifdef CONFIG_USB_NON_PCI_EHCI
+
+/*-------------------------------------------------------------------------*/
+
+/**
+ * usb_ehci_nonpci_remove/probe --
+ *
+ *
+ */
+
+// bogus pci_dev
+static struct pci_dev bogus_pcidev;
+static struct usb_hcd *nonpci_hcd;
+
+void usb_ehci_nonpci_remove(int base, int len)
+{
+	struct usb_device	*hub;
+	struct usb_hcd		*hcd;
+	struct pci_dev 		*dev;
+
+
+	dev = &bogus_pcidev;
+	hcd = nonpci_hcd;
+	if (!hcd)
+		return;
+	printk (KERN_INFO "%s %s: remove state %x\n",
+		hcd->description,  dev->slot_name, hcd->state);
+
+	if (in_interrupt ()) BUG ();
+
+	hub = hcd->bus->root_hub;
+	hcd->state = USB_STATE_QUIESCING;
+
+	dbg ("%s: roothub graceful disconnect", hcd->bus->bus_name);
+	usb_disconnect (&hub);
+	// usb_disconnect (&hcd->bus->root_hub);
+
+	hcd->driver->stop (hcd);
+	hcd->state = USB_STATE_HALT;
+
+	free_irq (hcd->irq, hcd);
+	iounmap (hcd->regs);
+	release_mem_region (base,len );
+
+	down (&hcd_list_lock);
+	list_del (&hcd->hcd_list);
+	up (&hcd_list_lock);
+
+	usb_deregister_bus (hcd->bus);
+	usb_free_bus (hcd->bus);
+	hcd->bus = NULL;
+
+	hcd->driver->hcd_free (hcd);
+}
+EXPORT_SYMBOL (usb_ehci_nonpci_remove);
+
+
+int usb_ehci_nonpci_probe (int base, int len, int irq,
+							const struct hc_driver *nonpci_ehci_driver)
+{
+	void *mem_base;
+	struct usb_bus		*bus;
+	struct usb_hcd		*hcd;
+	struct pci_dev 		*dev;
+	int					retval;
+	struct hc_driver	*driver = (struct hc_driver	*) nonpci_ehci_driver;
+
+	/*
+	* Fill in the bogus pci_dev. Only those members actually
+	* dereferenced in this driver are initialized.
+	*/
+	memset(&bogus_pcidev, 0, sizeof(struct pci_dev));
+	strcpy(bogus_pcidev.name, "non-PCI EHCI");
+	strcpy(bogus_pcidev.slot_name, "builtin.EHC");
+	bogus_pcidev.resource[0].name = "EHCI Operational Registers";
+	bogus_pcidev.resource[0].start = base;
+	bogus_pcidev.resource[0].end = base + len - 1;
+	bogus_pcidev.resource[0].flags = 0;
+	bogus_pcidev.irq = irq;
+	dev = &bogus_pcidev;
+
+	if (!base || !len || (irq < 0))
+		return -ENODEV;
+
+	if (!request_mem_region (base, len, "usb-ehci")) {
+		dbg ("controller already in use");
+		return -EBUSY;
+	}
+
+	mem_base = ioremap_nocache (base, len);
+	if (!mem_base) {
+		err("Error mapping EHCI memory");
+		retval = -EFAULT;
+clean_1:
+		release_mem_region (base, len);
+		err ("init %s fail, %d", dev->slot_name, retval);
+		return retval;
+	}
+
+
+	hcd = driver->hcd_alloc ();
+	if (hcd == NULL){
+		retval = -ENOMEM;
+clean_2:
+		iounmap ((void*)base);
+		dbg ("hcd alloc fail");
+		goto clean_1;
+	}
+
+	pci_set_drvdata(dev, hcd);
+	hcd->driver = driver;
+	hcd->description = driver->description;
+	hcd->pdev = dev;
+	printk (KERN_INFO "%s %s: %s\n",
+			hcd->description,  dev->slot_name, dev->name);
+
+	if (request_irq (dev->irq, hcd_irq, SA_SHIRQ, hcd->description, hcd)
+			!= 0) {
+		err ("request interrupt %d failed", dev->irq);
+		retval = -EBUSY;
+clean_3:
+		driver->hcd_free (hcd);
+		goto clean_2;
+	}
+	hcd->irq = dev->irq;
+	hcd->regs = (void*)mem_base;
+	hcd->region = 0;
+	printk (KERN_INFO "%s %s: irq %d, io base %p\n",
+		hcd->description,  dev->slot_name, dev->irq, (void*)mem_base);
+
+// FIXME simpler: make "bus" be that data, not pointer to it.
+// (fixed in 2.5)
+	bus = usb_alloc_bus (&hcd_operations);
+	if (bus == NULL) {
+		dbg ("usb_alloc_bus fail");
+		retval = -ENOMEM;
+		free_irq (dev->irq, hcd);
+		goto clean_3;
+	}
+	hcd->bus = bus;
+	bus->bus_name = dev->slot_name;
+	hcd->product_desc = dev->name;
+	bus->hcpriv = (void *) hcd;
+
+	INIT_LIST_HEAD (&hcd->dev_list);
+	INIT_LIST_HEAD (&hcd->hcd_list);
+
+	down (&hcd_list_lock);
+	list_add (&hcd->hcd_list, &hcd_list);
+	up (&hcd_list_lock);
+
+	usb_register_bus (bus);
+
+	if ((retval = driver->start (hcd)) < 0)
+		usb_ehci_nonpci_remove(base,len);
+
+	nonpci_hcd = hcd;
+	return retval;
+}
+EXPORT_SYMBOL (usb_ehci_nonpci_probe);
+
+#endif
diff -Nur linux-2.4.30/drivers/usb/hcd.h linux-2.4.30-linux-mips.org/drivers/usb/hcd.h
--- linux-2.4.30/drivers/usb/hcd.h	2003-06-13 16:51:36.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/usb/hcd.h	2005-05-12 18:01:29.000000000 +0200
@@ -169,6 +169,12 @@
 
 #endif /* CONFIG_PCI */
 
+#ifdef CONFIG_USB_NON_PCI_EHCI
+extern int usb_ehci_nonpci_probe (int base, int len, int irq,
+			const struct hc_driver *nonpci_ehci_driver);
+extern void usb_ehci_nonpci_remove (int base, int len);
+#endif
+
 /*-------------------------------------------------------------------------*/
 
 /*
diff -Nur linux-2.4.30/drivers/usb/host/Config.in linux-2.4.30-linux-mips.org/drivers/usb/host/Config.in
--- linux-2.4.30/drivers/usb/host/Config.in	2003-11-28 19:26:20.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/usb/host/Config.in	2005-05-12 18:01:29.000000000 +0200
@@ -3,6 +3,7 @@
 #
 comment 'USB Host Controller Drivers'
 dep_tristate '  EHCI HCD (USB 2.0) support (EXPERIMENTAL)' CONFIG_USB_EHCI_HCD $CONFIG_USB $CONFIG_EXPERIMENTAL
+dep_tristate '    Non-PCI EHCI support' CONFIG_USB_NON_PCI_EHCI $CONFIG_USB_EHCI_HCD
 if [ "$CONFIG_USB_UHCI_ALT" != "y" ]; then
    dep_tristate '  UHCI (Intel PIIX4, VIA, ...) support' CONFIG_USB_UHCI $CONFIG_USB
 fi
@@ -17,3 +18,4 @@
    dep_tristate '  SL811HS Alternate (x86, StrongARM, isosynchronous mode)' CONFIG_USB_SL811HS_ALT $CONFIG_USB $CONFIG_EXPERIMENTAL
    dep_tristate '  SL811HS (x86, StrongARM) support, old driver' CONFIG_USB_SL811HS $CONFIG_USB $CONFIG_EXPERIMENTAL
 fi
+dep_tristate '    Non-PCI OHCI support' CONFIG_USB_NON_PCI_OHCI $CONFIG_USB_OHCI
diff -Nur linux-2.4.30/drivers/usb/host/ehci-hcd.c linux-2.4.30-linux-mips.org/drivers/usb/host/ehci-hcd.c
--- linux-2.4.30/drivers/usb/host/ehci-hcd.c	2004-11-17 12:54:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/usb/host/ehci-hcd.c	2005-05-12 18:01:29.000000000 +0200
@@ -350,8 +350,11 @@
 	temp = HCC_EXT_CAPS (hcc_params);
 	while (temp) {
 		u32		cap;
-
+#ifndef CONFIG_USB_NON_PCI_EHCI
 		pci_read_config_dword (ehci->hcd.pdev, temp, &cap);
+#else
+		cap = 2;
+#endif
 		ehci_dbg (ehci, "capability %04x at %02x\n", cap, temp);
 		switch (cap & 0xff) {
 		case 1:			/* BIOS/SMM/... handoff */
@@ -436,8 +439,10 @@
 			ehci_info (ehci, "enabled 64bit PCI DMA\n");
 	}
 
+#ifndef CONFIG_USB_NON_PCI_EHCI
 	/* help hc dma work well with cachelines */
 	pci_set_mwi (ehci->hcd.pdev);
+#endif
 
 	/* clear interrupt enables, set irq latency */
 	temp = readl (&ehci->regs->command) & 0x0fff;
@@ -491,7 +496,11 @@
 	readl (&ehci->regs->command);	/* unblock posted write */
 
         /* PCI Serial Bus Release Number is at 0x60 offset */
+#ifndef CONFIG_USB_NON_PCI_EHCI
 	pci_read_config_byte (hcd->pdev, 0x60, &tempbyte);
+#else
+	tempbyte=0x20;
+#endif
 	temp = HC_VERSION(readl (&ehci->caps->hc_capbase));
 	ehci_info (ehci,
 		"USB %x.%x enabled, EHCI %x.%02x, driver %s\n",
@@ -1013,8 +1022,9 @@
 	.hub_control =		ehci_hub_control,
 };
 
-/*-------------------------------------------------------------------------*/
+#ifndef CONFIG_USB_NON_PCI_EHCI
 
+/*-------------------------------------------------------------------------*/
 /* EHCI spec says PCI is required. */
 
 /* PCI driver selection metadata; PCI hotplugging uses this */
@@ -1036,6 +1046,7 @@
 };
 MODULE_DEVICE_TABLE (pci, pci_ids);
 
+
 /* pci driver glue; this is a "new style" PCI driver module */
 static struct pci_driver ehci_pci_driver = {
 	.name =		(char *) hcd_name,
@@ -1050,6 +1061,47 @@
 #endif
 };
 
+#else /* CONFIG_USB_NON_PCI_EHCI */
+
+// Boot options
+static int ehci_base=0, ehci_len=0;
+static int ehci_IRQ=-1;
+
+MODULE_PARM(ehci_base, "i");
+MODULE_PARM(ehci_len, "i");
+MODULE_PARM(ehci_IRQ, "i");
+MODULE_PARM_DESC(ehci_base, "IO Base address of EHCI Oper. registers");
+MODULE_PARM_DESC(ehci_len, "IO length of EHCI Oper. registers");
+MODULE_PARM_DESC(ehci_IRQ, "IRQ for EHCI interrupts");
+
+#ifndef MODULE
+
+static int __init
+ehci_setup (char* options)
+{
+	char* this_opt;
+
+	if (!options || !*options)
+		return 0;
+
+	for(this_opt=strtok(options,",");this_opt;this_opt=strtok(NULL,",")) {
+		if (!strncmp(this_opt, "base:", 5)) {
+			ehci_base = simple_strtoul(this_opt+5, NULL, 0);
+		} else if (!strncmp(this_opt, "len:", 4)) {
+			ehci_len = simple_strtoul(this_opt+4, NULL, 0);
+		} else if (!strncmp(this_opt, "irq:", 4)) {
+			ehci_IRQ = simple_strtoul(this_opt+4, NULL, 0);
+		}
+	}
+	return 0;
+}
+
+__setup("usb_ehci=", ehci_setup);
+
+#endif  /* !MODULE */
+
+#endif /* CONFIG_USB_NON_PCI_EHCI */
+
 #define DRIVER_INFO DRIVER_VERSION " " DRIVER_DESC
 
 MODULE_DESCRIPTION (DRIVER_INFO);
@@ -1063,12 +1115,20 @@
 		sizeof (struct ehci_qh), sizeof (struct ehci_qtd),
 		sizeof (struct ehci_itd), sizeof (struct ehci_sitd));
 
+#ifndef CONFIG_USB_NON_PCI_EHCI
 	return pci_module_init (&ehci_pci_driver);
+#else
+	return usb_ehci_nonpci_probe(ehci_base,ehci_len,ehci_IRQ,&ehci_driver);
+#endif
 }
 module_init (init);
 
 static void __exit cleanup (void) 
 {	
+#ifndef CONFIG_USB_NON_PCI_EHCI
 	pci_unregister_driver (&ehci_pci_driver);
+#else
+	usb_ehci_nonpci_remove(ehci_base,ehci_len);
+#endif
 }
 module_exit (cleanup);
diff -Nur linux-2.4.30/drivers/usb/host/usb-ohci.c linux-2.4.30-linux-mips.org/drivers/usb/host/usb-ohci.c
--- linux-2.4.30/drivers/usb/host/usb-ohci.c	2005-01-19 15:10:07.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/usb/host/usb-ohci.c	2005-05-12 18:01:29.000000000 +0200
@@ -2564,6 +2564,7 @@
 		hc_release_ohci (ohci);
 		return ret;
 	}
+#ifndef CONFIG_USB_NON_PCI_OHCI
 	ohci->flags = id->driver_data;
 	
 	/* Check for NSC87560. We have to look at the bridge (fn1) to identify
@@ -2582,6 +2583,7 @@
 		printk (KERN_INFO __FILE__ ": Using NSC SuperIO setup\n");
 	if (ohci->flags & OHCI_QUIRK_AMD756)
 		printk (KERN_INFO __FILE__ ": AMD756 erratum 4 workaround\n");
+#endif
 
 	if (hc_reset (ohci) < 0) {
 		hc_release_ohci (ohci);
@@ -2627,8 +2629,10 @@
 	int temp;
 	int i;
 
+#ifndef CONFIG_USB_NON_PCI_OHCI
 	if (ohci->pci_latency)
 		pci_write_config_byte (ohci->ohci_dev, PCI_LATENCY_TIMER, ohci->pci_latency);
+#endif
 
 	ohci->disabled = 1;
 	ohci->sleeping = 0;
@@ -2658,6 +2662,7 @@
 
 /*-------------------------------------------------------------------------*/
 
+#ifndef CONFIG_USB_NON_PCI_OHCI
 /* configured so that an OHCI device is always provided */
 /* always called with process context; sleeping is OK */
 
@@ -2705,6 +2710,88 @@
 	}
 	return status;
 } 
+#else  /* CONFIG_USB_NON_PCI_OHCI */
+
+// Boot options
+static int ohci_base=0, ohci_len=0;
+static int ohci_irq=-1;
+
+MODULE_PARM(ohci_base, "i");
+MODULE_PARM(ohci_len, "i");
+MODULE_PARM(ohci_irq, "i");
+MODULE_PARM_DESC(ohci_base, "IO Base address of OHCI Oper. registers");
+MODULE_PARM_DESC(ohci_len, "IO length of OHCI Oper. registers");
+MODULE_PARM_DESC(ohci_irq, "IRQ for OHCI interrupts");
+
+// bogus pci_dev
+static struct pci_dev bogus_pcidev;
+
+static struct pci_driver ohci_pci_driver = {
+	name:		"usb-ohci",
+};
+
+static int __devinit
+ohci_non_pci_init (void)
+{
+	void *mem_base;
+
+	if (!ohci_base || !ohci_len || (ohci_irq < 0)) 
+		return -ENODEV;
+
+	if (!request_mem_region (ohci_base, ohci_len, ohci_pci_driver.name)) {
+		dbg ("controller already in use");
+		return -EBUSY;
+	}
+
+	mem_base = ioremap_nocache (ohci_base, ohci_len);
+	if (!mem_base) {
+		err("Error mapping OHCI memory");
+		return -EFAULT;
+	}
+
+	/*
+	* Fill in the bogus pci_dev. Only those members actually
+	* dereferenced in this driver are initialized.
+	*/
+	memset(&bogus_pcidev, 0, sizeof(struct pci_dev));
+	strcpy(bogus_pcidev.name, "non-PCI OHCI");
+	strcpy(bogus_pcidev.slot_name, "builtin.OHC");
+	bogus_pcidev.resource[0].name = "OHCI Operational Registers";
+	bogus_pcidev.resource[0].start = ohci_base;
+	bogus_pcidev.resource[0].end = ohci_base + ohci_len - 1;
+	bogus_pcidev.resource[0].flags = 0;
+	bogus_pcidev.irq = ohci_irq;
+
+	return hc_found_ohci (&bogus_pcidev, bogus_pcidev.irq, mem_base, NULL);
+} 
+
+#ifndef MODULE
+
+static int __init
+ohci_setup (char* options)
+{
+	char* this_opt;
+
+	if (!options || !*options)
+		return 0;
+
+	for(this_opt=strtok(options,",");this_opt;this_opt=strtok(NULL,",")) {
+		if (!strncmp(this_opt, "base:", 5)) { 
+			ohci_base = simple_strtoul(this_opt+5, NULL, 0);
+		} else if (!strncmp(this_opt, "len:", 4)) { 
+			ohci_len = simple_strtoul(this_opt+4, NULL, 0);
+		} else if (!strncmp(this_opt, "irq:", 4)) { 
+			ohci_irq = simple_strtoul(this_opt+4, NULL, 0);
+		}
+	}
+	return 0;
+}
+
+__setup("usb_ohci=", ohci_setup);
+
+#endif  /* !MODULE */
+
+#endif  /* CONFIG_USB_NON_PCI_OHCI */
 
 /*-------------------------------------------------------------------------*/
 
@@ -2740,11 +2827,16 @@
 
 	hc_release_ohci (ohci);
 
+#ifndef CONFIG_USB_NON_PCI_OHCI
 	release_mem_region (pci_resource_start (dev, 0), pci_resource_len (dev, 0));
 	pci_disable_device (dev);
+#else
+	release_mem_region (ohci_base, ohci_len);
+#endif
 }
 
 
+#ifndef CONFIG_USB_NON_PCI_OHCI
 #ifdef	CONFIG_PM
 
 /*-------------------------------------------------------------------------*/
@@ -2983,20 +3075,29 @@
 	resume:		ohci_pci_resume,
 #endif	/* PM */
 };
+#endif /* CONFIG_USB_NON_PCI_OHCI */
 
  
 /*-------------------------------------------------------------------------*/
 
 static int __init ohci_hcd_init (void) 
 {
+#ifndef CONFIG_USB_NON_PCI_OHCI
 	return pci_module_init (&ohci_pci_driver);
+#else
+	return ohci_non_pci_init();
+#endif
 }
 
 /*-------------------------------------------------------------------------*/
 
 static void __exit ohci_hcd_cleanup (void) 
 {	
+#ifndef CONFIG_USB_NON_PCI_OHCI
 	pci_unregister_driver (&ohci_pci_driver);
+#else
+	ohci_pci_remove(&bogus_pcidev);
+#endif
 }
 
 module_init (ohci_hcd_init);
diff -Nur linux-2.4.30/drivers/video/au1200fb.c linux-2.4.30-linux-mips.org/drivers/video/au1200fb.c
--- linux-2.4.30/drivers/video/au1200fb.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/video/au1200fb.c	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,1891 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *	Au1200 LCD Driver.
+ *
+ * Copyright 2004 AMD
+ * Author: AMD
+ *
+ * Based on:
+ * linux/drivers/video/skeletonfb.c -- Skeleton for a frame buffer device
+ *  Created 28 Dec 1997 by Geert Uytterhoeven
+ *
+ *  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.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/init.h>
+#include <asm/uaccess.h>
+
+#include <asm/au1000.h>
+#include <asm/au1xxx_gpio.h>
+#include "au1200fb.h"
+
+#include <video/fbcon.h>
+#include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb32.h>
+#define CMAPSIZE 16
+
+// #define ORIGINAL 1 /* BIG CHANGES HERE */
+
+#ifdef ORIGINAL
+#define AU1200_LCD_GET_WINENABLE	1
+#define AU1200_LCD_SET_WINENABLE	2
+#define AU1200_LCD_GET_WINLOCATION	3
+#define AU1200_LCD_SET_WINLOCATION	4
+#define AU1200_LCD_GET_WINSIZE		5
+#define AU1200_LCD_SET_WINSIZE		6
+#define AU1200_LCD_GET_BACKCOLOR	7
+#define AU1200_LCD_SET_BACKCOLOR	8
+#define AU1200_LCD_GET_COLORKEY	9
+#define AU1200_LCD_SET_COLORKEY	10
+#define AU1200_LCD_GET_PANEL		11
+#define AU1200_LCD_SET_PANEL		12
+
+typedef struct au1200_lcd_getset_t
+{
+	unsigned int subcmd;
+	union {
+		struct {
+			int enable;
+		} winenable;
+		struct {
+			int x, y;
+		} winlocation;
+		struct {
+			int hsz, vsz;
+		} winsize;
+		struct {
+			unsigned int color;
+		} backcolor;
+		struct {
+			unsigned int key;
+			unsigned int mask;
+		} colorkey;
+		struct {
+			int panel;
+			char desc[80];
+		} panel;
+	};
+} au1200_lcd_getset_t;
+
+#else
+
+#define AU1200_LCD_FB_IOCTL 0x46FF
+
+#define AU1200_LCD_SET_SCREEN 1
+#define AU1200_LCD_GET_SCREEN 2
+#define AU1200_LCD_SET_WINDOW 3
+#define AU1200_LCD_GET_WINDOW 4
+#define AU1200_LCD_SET_PANEL  5
+#define AU1200_LCD_GET_PANEL  6
+
+#define SCREEN_SIZE		    (1<< 1)
+#define SCREEN_BACKCOLOR    (1<< 2)
+#define SCREEN_BRIGHTNESS   (1<< 3)
+#define SCREEN_COLORKEY     (1<< 4)
+#define SCREEN_MASK         (1<< 5)
+typedef struct au1200_lcd_global_regs_t
+{
+    unsigned int flags;
+    unsigned int xsize;
+    unsigned int ysize;
+    unsigned int backcolor;
+    unsigned int brightness;
+	unsigned int colorkey;
+	unsigned int mask;
+    unsigned int panel_choice;
+    char panel_desc[80];
+
+} au1200_lcd_global_regs_t;
+
+#define WIN_POSITION            (1<< 0)
+#define WIN_ALPHA_COLOR         (1<< 1)
+#define WIN_ALPHA_MODE          (1<< 2)
+#define WIN_PRIORITY            (1<< 3)
+#define WIN_CHANNEL             (1<< 4)
+#define WIN_BUFFER_FORMAT       (1<< 5)
+#define WIN_COLOR_ORDER         (1<< 6)
+#define WIN_PIXEL_ORDER         (1<< 7)
+#define WIN_SIZE                (1<< 8)
+#define WIN_COLORKEY_MODE       (1<< 9)
+#define WIN_DOUBLE_BUFFER_MODE  (1<< 10)
+#define WIN_RAM_ARRAY_MODE      (1<< 11)
+#define WIN_BUFFER_SCALE        (1<< 12)
+#define WIN_ENABLE	            (1<< 13)
+
+typedef struct au1200_lcd_window_regs_t
+{
+    unsigned int flags;
+    unsigned int xpos;
+    unsigned int ypos;
+    unsigned int alpha_color;
+    unsigned int alpha_mode;
+    unsigned int priority;
+    unsigned int channel;
+    unsigned int buffer_format;
+    unsigned int color_order;
+    unsigned int pixel_order;
+    unsigned int xsize;
+    unsigned int ysize;
+    unsigned int colorkey_mode;
+    unsigned int double_buffer_mode;
+    unsigned int ram_array_mode;
+    unsigned int xscale;
+    unsigned int yscale;
+    unsigned int enable;
+} au1200_lcd_window_regs_t;
+
+
+typedef struct au1200_lcd_iodata_t
+{
+
+    unsigned int subcmd;
+    au1200_lcd_global_regs_t global;
+    au1200_lcd_window_regs_t window;
+
+} au1200_lcd_iodata_t;
+
+#endif
+
+AU1200_LCD *lcd = (AU1200_LCD *)AU1200_LCD_ADDR;
+static int window_index = 0; /* default is zero */
+static int panel_index = -1; /* default is call board_au1200fb_panel */
+
+struct window_settings
+{
+	unsigned char name[64];
+	uint32 mode_backcolor;
+	uint32 mode_colorkey;
+	uint32 mode_colorkeymsk;
+	struct
+	{
+		int xres;
+		int yres;
+		int xpos;
+		int ypos;
+		uint32 mode_winctrl1; /* winctrl1[FRM,CCO,PO,PIPE] */
+		uint32 mode_winenable;
+	} w[4];
+};
+
+struct panel_settings
+{
+	unsigned char name[64];
+	/* panel physical dimensions */
+	uint32 Xres;
+	uint32 Yres;
+	/* panel timings */
+	uint32 mode_screen;
+	uint32 mode_horztiming;
+	uint32 mode_verttiming;
+	uint32 mode_clkcontrol;
+	uint32 mode_pwmdiv;
+	uint32 mode_pwmhi;
+	uint32 mode_outmask;
+	uint32 mode_fifoctrl;
+	uint32 mode_toyclksrc;
+	uint32 mode_backlight;
+	uint32 mode_auxpll;
+	int (*device_init)(void);
+	int (*device_shutdown)(void);
+};
+
+#if defined(__BIG_ENDIAN)
+#define LCD_WINCTRL1_PO_16BPP LCD_WINCTRL1_PO_00
+#else
+#define LCD_WINCTRL1_PO_16BPP LCD_WINCTRL1_PO_01
+#endif
+
+extern int board_au1200fb_panel (void);
+extern int board_au1200fb_panel_init (void);
+extern int board_au1200fb_panel_shutdown (void);
+
+#if defined(CONFIG_FOCUS_ENHANCEMENTS)
+extern int board_au1200fb_focus_init_hdtv(void);
+extern int board_au1200fb_focus_init_component(void);
+extern int board_au1200fb_focus_init_cvsv(void);
+extern int board_au1200fb_focus_shutdown(void);
+#endif
+
+/*
+ * Default window configurations
+ */
+static struct window_settings windows[] =
+{
+	{ /* Index 0 */
+		"0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx",
+		/* mode_backcolor	*/ 0x006600ff,
+		/* mode_colorkey,msk*/ 0, 0,
+		{
+			{
+			/* xres, yres, xpos, ypos */ 0, 0, 0, 0,
+			/* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565|LCD_WINCTRL1_PO_16BPP,
+			/* mode_winenable*/ LCD_WINENABLE_WEN0,
+			},
+			{
+			/* xres, yres, xpos, ypos */ 0, 0, 0, 0,
+			/* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565|LCD_WINCTRL1_PO_16BPP,
+			/* mode_winenable*/ 0,
+			},
+			{
+			/* xres, yres, xpos, ypos */ 0, 0, 0, 0,
+			/* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565|LCD_WINCTRL1_PO_16BPP|LCD_WINCTRL1_PIPE,
+			/* mode_winenable*/ 0,
+			},
+			{
+			/* xres, yres, xpos, ypos */ 0, 0, 0, 0,
+			/* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565|LCD_WINCTRL1_PO_16BPP|LCD_WINCTRL1_PIPE,
+			/* mode_winenable*/ 0,
+			},
+		},
+	},
+
+	{ /* Index 1 */
+		"0-FS gfx, 1-video, 2-ovly gfx, 3-ovly gfx",
+		/* mode_backcolor	*/ 0x006600ff,
+		/* mode_colorkey,msk*/ 0, 0,
+		{
+			{
+			/* xres, yres, xpos, ypos */ 320, 240, 5, 5,
+#if 0
+			/* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565|LCD_WINCTRL1_PO_16BPP,
+#endif
+			/* mode_winctrl1 */ LCD_WINCTRL1_FRM_24BPP|LCD_WINCTRL1_PO_00,
+			/* mode_winenable*/ LCD_WINENABLE_WEN0,
+			},
+			{
+			/* xres, yres, xpos, ypos */ 0, 0, 0, 0,
+			/* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565|LCD_WINCTRL1_PO_16BPP,
+			/* mode_winenable*/ 0,
+			},
+			{
+			/* xres, yres, xpos, ypos */ 100, 100, 0, 0,
+			/* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565|LCD_WINCTRL1_PO_16BPP|LCD_WINCTRL1_PIPE,
+			/* mode_winenable*/ 0/*LCD_WINENABLE_WEN2*/,
+			},
+			{
+			/* xres, yres, xpos, ypos */ 200, 25, 0, 0,
+			/* mode_winctrl1 */ LCD_WINCTRL1_FRM_16BPP565|LCD_WINCTRL1_PO_16BPP|LCD_WINCTRL1_PIPE,
+			/* mode_winenable*/ 0,
+			},
+		},
+	},
+	/* Need VGA 640 @ 24bpp, @ 32bpp */
+	/* Need VGA 800 @ 24bpp, @ 32bpp */
+	/* Need VGA 1024 @ 24bpp, @ 32bpp */
+} ;
+
+/*
+ * Controller configurations for various panels.
+ */
+static struct panel_settings panels[] =
+{
+	{ /* Index 0: QVGA 320x240 H:33.3kHz V:110Hz */
+		"VGA_320x240",
+		320, 240,
+		/* mode_screen 		*/ LCD_SCREEN_SX_N(320) | LCD_SCREEN_SY_N(240),
+		/* mode_horztiming	*/ 0x00c4623b,
+		/* mode_verttiming	*/ 0x00502814,
+		/* mode_clkcontrol	*/ 0x00020002, /* /4=24Mhz */
+		/* mode_pwmdiv		*/ 0x00000000,
+		/* mode_pwmhi		*/ 0x00000000,
+		/* mode_outmask		*/ 0x00FFFFFF,
+		/* mode_fifoctrl	*/ 0x2f2f2f2f,
+		/* mode_toyclksrc   */ 0x00000004, /* 96MHz AUXPLL directly */
+		/* mode_backlight   */ 0x00000000,
+		/* mode_auxpll		*/ 8, /* 96MHz AUXPLL */
+		/* device_init		*/ NULL,
+		/* device_shutdown	*/ NULL,
+	},
+
+	{ /* Index 1: VGA 640x480 H:30.3kHz V:58Hz */
+		"VGA_640x480",
+		640, 480,
+		/* mode_screen 		*/ 0x13f9df80,
+		/* mode_horztiming	*/ 0x003c5859,
+		/* mode_verttiming	*/ 0x00741201,
+		/* mode_clkcontrol	*/ 0x00020001, /* /4=24Mhz */
+		/* mode_pwmdiv		*/ 0x00000000,
+		/* mode_pwmhi		*/ 0x00000000,
+		/* mode_outmask		*/ 0x00FFFFFF,
+		/* mode_fifoctrl	*/ 0x2f2f2f2f,
+		/* mode_toyclksrc   */ 0x00000004, /* AUXPLL directly */
+		/* mode_backlight   */ 0x00000000,
+		/* mode_auxpll		*/ 8, /* 96MHz AUXPLL */
+		/* device_init		*/ NULL,
+		/* device_shutdown	*/ NULL,
+	},
+
+	{ /* Index 2: SVGA 800x600 H:46.1kHz V:69Hz */
+		"SVGA_800x600",
+		800, 600,
+		/* mode_screen 		*/ 0x18fa5780,
+		/* mode_horztiming	*/ 0x00dc7e77,
+		/* mode_verttiming	*/ 0x00584805,
+		/* mode_clkcontrol	*/ 0x00020000, /* /2=48Mhz */
+		/* mode_pwmdiv		*/ 0x00000000,
+		/* mode_pwmhi		*/ 0x00000000,
+		/* mode_outmask		*/ 0x00FFFFFF,
+		/* mode_fifoctrl	*/ 0x2f2f2f2f,
+		/* mode_toyclksrc   */ 0x00000004, /* AUXPLL directly */
+		/* mode_backlight   */ 0x00000000,
+		/* mode_auxpll		*/ 8, /* 96MHz AUXPLL */
+		/* device_init		*/ NULL,
+		/* device_shutdown	*/ NULL,
+	},
+
+	{ /* Index 3: XVGA 1024x768 H:56.2kHz V:70Hz */
+		"XVGA_1024x768",
+		1024, 768,
+		/* mode_screen 		*/ 0x1ffaff80,
+		/* mode_horztiming	*/ 0x007d0e57,
+		/* mode_verttiming	*/ 0x00740a01,
+		/* mode_clkcontrol	*/ 0x000A0000, /* /1 */
+		/* mode_pwmdiv		*/ 0x00000000,
+		/* mode_pwmhi		*/ 0x00000000,
+		/* mode_outmask		*/ 0x00FFFFFF,
+		/* mode_fifoctrl	*/ 0x2f2f2f2f,
+		/* mode_toyclksrc   */ 0x00000004, /* AUXPLL directly */
+		/* mode_backlight   */ 0x00000000,
+		/* mode_auxpll		*/ 6, /* 72MHz AUXPLL */
+		/* device_init		*/ NULL,
+		/* device_shutdown	*/ NULL,
+	},
+
+	{ /* Index 4: XVGA 1280x1024 H:68.5kHz V:65Hz */
+		"XVGA_1280x1024",
+		1280, 1024,
+		/* mode_screen 		*/ 0x27fbff80,
+		/* mode_horztiming	*/ 0x00cdb2c7,
+		/* mode_verttiming	*/ 0x00600002,
+		/* mode_clkcontrol	*/ 0x000A0000, /* /1 */
+		/* mode_pwmdiv		*/ 0x00000000,
+		/* mode_pwmhi		*/ 0x00000000,
+		/* mode_outmask		*/ 0x00FFFFFF,
+		/* mode_fifoctrl	*/ 0x2f2f2f2f,
+		/* mode_toyclksrc   */ 0x00000004, /* AUXPLL directly */
+		/* mode_backlight   */ 0x00000000,
+		/* mode_auxpll		*/ 10, /* 120MHz AUXPLL */
+		/* device_init		*/ NULL,
+		/* device_shutdown	*/ NULL,
+	},
+
+	{ /* Index 5: Samsung 1024x768 TFT */
+		"Samsung_1024x768_TFT",
+		1024, 768,
+		/* mode_screen 		*/ 0x1ffaff80,
+		/* mode_horztiming	*/ 0x018cc677,
+		/* mode_verttiming	*/ 0x00241217,
+		/* mode_clkcontrol	*/ 0x00000000, /* SCB 0x1 /4=24Mhz */
+		/* mode_pwmdiv		*/ 0x8000063f, /* SCB 0x0 */
+		/* mode_pwmhi		*/ 0x03400000, /* SCB 0x0 */
+		/* mode_outmask		*/ 0x00fcfcfc,
+		/* mode_fifoctrl	*/ 0x2f2f2f2f,
+		/* mode_toyclksrc   */ 0x00000004, /* 96MHz AUXPLL directly */
+		/* mode_backlight   */ 0x00000000,
+		/* mode_auxpll		*/ 8, /* 96MHz AUXPLL */
+		/* device_init		*/ board_au1200fb_panel_init,
+		/* device_shutdown	*/ board_au1200fb_panel_shutdown,
+	},
+
+	{ /* Index 6: Toshiba 640x480 TFT */
+		"Toshiba_640x480_TFT",
+		640, 480,
+		/* mode_screen 		*/ LCD_SCREEN_SX_N(640) | LCD_SCREEN_SY_N(480),
+		/* mode_horztiming	*/ LCD_HORZTIMING_HPW_N(96) | LCD_HORZTIMING_HND1_N(13) | LCD_HORZTIMING_HND2_N(51),
+		/* mode_verttiming	*/ LCD_VERTTIMING_VPW_N(2) | LCD_VERTTIMING_VND1_N(11) | LCD_VERTTIMING_VND2_N(32) ,
+		/* mode_clkcontrol	*/ 0x00000000, /* /4=24Mhz */
+		/* mode_pwmdiv		*/ 0x8000063f,
+		/* mode_pwmhi		*/ 0x03400000,
+		/* mode_outmask		*/ 0x00fcfcfc,
+		/* mode_fifoctrl	*/ 0x2f2f2f2f,
+		/* mode_toyclksrc   */ 0x00000004, /* 96MHz AUXPLL directly */
+		/* mode_backlight   */ 0x00000000,
+		/* mode_auxpll		*/ 8, /* 96MHz AUXPLL */
+		/* device_init		*/ board_au1200fb_panel_init,
+		/* device_shutdown	*/ board_au1200fb_panel_shutdown,
+	},
+
+	{ /* Index 7: Sharp 320x240 TFT */
+		"Sharp_320x240_TFT",
+		320, 240,
+		/* mode_screen 		*/ LCD_SCREEN_SX_N(320) | LCD_SCREEN_SY_N(240),
+		/* mode_horztiming	*/ LCD_HORZTIMING_HPW_N(60) | LCD_HORZTIMING_HND1_N(13) | LCD_HORZTIMING_HND2_N(2),
+		/* mode_verttiming	*/ LCD_VERTTIMING_VPW_N(2) | LCD_VERTTIMING_VND1_N(2) | LCD_VERTTIMING_VND2_N(5) ,
+		/* mode_clkcontrol	*/ LCD_CLKCONTROL_PCD_N(7), /* /16=6Mhz */
+		/* mode_pwmdiv		*/ 0x8000063f,
+		/* mode_pwmhi		*/ 0x03400000,
+		/* mode_outmask		*/ 0x00fcfcfc,
+		/* mode_fifoctrl	*/ 0x2f2f2f2f,
+		/* mode_toyclksrc   */ 0x00000004, /* 96MHz AUXPLL directly */
+		/* mode_backlight   */ 0x00000000,
+		/* mode_auxpll		*/ 8, /* 96MHz AUXPLL */
+		/* device_init		*/ board_au1200fb_panel_init,
+		/* device_shutdown	*/ board_au1200fb_panel_shutdown,
+	},
+	{ /* Index 8: Toppoly TD070WGCB2 7" 854x480 TFT */
+		"Toppoly_TD070WGCB2",
+		854, 480,
+		/* mode_screen 		*/ LCD_SCREEN_SX_N(854) | LCD_SCREEN_SY_N(480),
+		/* mode_horztiming	*/ LCD_HORZTIMING_HND2_N(44) | LCD_HORZTIMING_HND1_N(44) | LCD_HORZTIMING_HPW_N(114),
+		/* mode_verttiming	*/ LCD_VERTTIMING_VND2_N(20) | LCD_VERTTIMING_VND1_N(21) | LCD_VERTTIMING_VPW_N(4),
+		/* mode_clkcontrol	*/ 0x00020001, /* /4=24Mhz */
+		/* mode_pwmdiv		*/ 0x8000063f,
+		/* mode_pwmhi		*/ 0x03400000,
+		/* mode_outmask		*/ 0x00FCFCFC,
+		/* mode_fifoctrl	*/ 0x2f2f2f2f,
+		/* mode_toyclksrc   */ 0x00000004, /* AUXPLL directly */
+		/* mode_backlight   */ 0x00000000,
+		/* mode_auxpll		*/ 8, /* 96MHz AUXPLL */
+		/* device_init		*/ board_au1200fb_panel_init,
+		/* device_shutdown	*/ board_au1200fb_panel_shutdown,
+	},
+#if defined(CONFIG_FOCUS_ENHANCEMENTS)
+	{ /* Index 9: Focus FS453 TV-Out 640x480 */
+		"FS453_640x480 (Composite/S-Video)",
+		640, 480,
+		/* mode_screen 		*/ LCD_SCREEN_SX_N(640) | LCD_SCREEN_SY_N(480),
+		/* mode_horztiming	*/ LCD_HORZTIMING_HND2_N(143) | LCD_HORZTIMING_HND1_N(143) | LCD_HORZTIMING_HPW_N(10),
+		/* mode_verttiming	*/ LCD_VERTTIMING_VND2_N(30) | LCD_VERTTIMING_VND1_N(30) | LCD_VERTTIMING_VPW_N(5),
+		/* mode_clkcontrol	*/ 0x00480000 | (1<<17) | (1<<18), /* External Clock, 1:1 clock ratio */
+		/* mode_pwmdiv		*/ 0x00000000,
+		/* mode_pwmhi		*/ 0x00000000,
+		/* mode_outmask		*/ 0x00FFFFFF,
+		/* mode_fifoctrl	*/ 0x2f2f2f2f,
+		/* mode_toyclksrc   */ 0x00000000,
+		/* mode_backlight   */ 0x00000000,
+		/* mode_auxpll		*/ 8, /* 96MHz AUXPLL */
+		/* device_init		*/ board_au1200fb_focus_init_cvsv,
+		/* device_shutdown	*/ board_au1200fb_focus_shutdown,
+	},
+	
+	{ /* Index 10: Focus FS453 TV-Out 640x480 */
+		"FS453_640x480 (Component Video)",
+		640, 480,
+		/* mode_screen 		*/ LCD_SCREEN_SX_N(640) | LCD_SCREEN_SY_N(480),
+		/* mode_horztiming	*/ LCD_HORZTIMING_HND2_N(143) | LCD_HORZTIMING_HND1_N(143) | LCD_HORZTIMING_HPW_N(10),
+		/* mode_verttiming	*/ LCD_VERTTIMING_VND2_N(30) | LCD_VERTTIMING_VND1_N(30) | LCD_VERTTIMING_VPW_N(5),
+		/* mode_clkcontrol	*/ 0x00480000 | (1<<17) | (1<<18), /* External Clock, 1:1 clock ratio */
+		/* mode_pwmdiv		*/ 0x00000000,
+		/* mode_pwmhi		*/ 0x00000000,
+		/* mode_outmask		*/ 0x00FFFFFF,
+		/* mode_fifoctrl	*/ 0x2f2f2f2f,
+		/* mode_toyclksrc   */ 0x00000000,
+		/* mode_backlight   */ 0x00000000,
+		/* mode_auxpll		*/ 8, /* 96MHz AUXPLL */
+		/* device_init		*/ board_au1200fb_focus_init_component,
+		/* device_shutdown	*/ board_au1200fb_focus_shutdown,
+	},
+	
+	{ /* Index 11: Focus FS453 TV-Out 640x480 */
+		"FS453_640x480 (HDTV)",
+		720, 480,
+		/* mode_screen 		*/ LCD_SCREEN_SX_N(720) | LCD_SCREEN_SY_N(480),
+		/* mode_horztiming	*/ LCD_HORZTIMING_HND2_N(28) | LCD_HORZTIMING_HND1_N(46) | LCD_HORZTIMING_HPW_N(64),
+		/* mode_verttiming	*/ LCD_VERTTIMING_VND2_N(7) | LCD_VERTTIMING_VND1_N(31) | LCD_VERTTIMING_VPW_N(7),
+		/* mode_clkcontrol	*/ 0x00480000 | (1<<17) | (1<<18), /* External Clock, 1:1 clock ratio */
+		/* mode_pwmdiv		*/ 0x00000000,
+		/* mode_pwmhi		*/ 0x00000000,
+		/* mode_outmask		*/ 0x00FFFFFF,
+		/* mode_fifoctrl	*/ 0x2f2f2f2f,
+		/* mode_toyclksrc   */ 0x00000000,
+		/* mode_backlight   */ 0x00000000,
+		/* mode_auxpll		*/ 8, /* 96MHz AUXPLL */
+		/* device_init		*/ board_au1200fb_focus_init_hdtv,
+		/* device_shutdown	*/ board_au1200fb_focus_shutdown,
+	},
+#endif
+};
+
+#define NUM_PANELS (sizeof(panels) / sizeof(struct panel_settings))
+
+static struct window_settings *win;
+static struct panel_settings *panel;
+
+struct au1200fb_info {
+	struct fb_info_gen gen;
+	unsigned long fb_virt_start;
+	unsigned long fb_size;
+	unsigned long fb_phys;
+	int mmaped;
+	int nohwcursor;
+	int noblanking;
+
+	struct { unsigned red, green, blue, pad; } palette[256];
+
+#if defined(FBCON_HAS_CFB16)
+	u16 fbcon_cmap16[16];
+#endif
+#if defined(FBCON_HAS_CFB32)
+	u32 fbcon_cmap32[16];
+#endif
+};
+
+
+struct au1200fb_par {
+    struct fb_var_screeninfo var;
+	
+	int line_length;  /* in bytes */
+	int cmap_len;     /* color-map length */
+};
+
+#ifndef CONFIG_FB_AU1200_DEVS
+#define CONFIG_FB_AU1200_DEVS 1
+#endif
+
+static struct au1200fb_info fb_infos[CONFIG_FB_AU1200_DEVS];
+static struct au1200fb_par fb_pars[CONFIG_FB_AU1200_DEVS];
+static struct display disps[CONFIG_FB_AU1200_DEVS];
+
+int au1200fb_init(void);
+void au1200fb_setup(char *options, int *ints);
+static int au1200fb_mmap(struct fb_info *fb, struct file *file, 
+		struct vm_area_struct *vma);
+static int au1200_blank(int blank_mode, struct fb_info_gen *info);
+static int au1200fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+			  u_long arg, int con, struct fb_info *info);
+
+void au1200_nocursor(struct display *p, int mode, int xx, int yy){};
+
+static int au1200_setlocation (int plane, int xpos, int ypos);
+static int au1200_setsize (int plane, int xres, int yres);
+static void au1200_setmode(int plane);
+static void au1200_setpanel (struct panel_settings *newpanel);
+
+static struct fb_ops au1200fb_ops = {
+	owner:		THIS_MODULE,
+	fb_get_fix:	fbgen_get_fix,
+	fb_get_var:	fbgen_get_var,
+	fb_set_var:	fbgen_set_var,
+	fb_get_cmap:	fbgen_get_cmap,
+	fb_set_cmap:	fbgen_set_cmap,
+	fb_pan_display: fbgen_pan_display,
+        fb_ioctl:       au1200fb_ioctl,
+	fb_mmap:        au1200fb_mmap,
+};
+
+
+static int
+winbpp (unsigned int winctrl1)
+{
+	/* how many bytes of memory are needed for each pixel format */
+	switch (winctrl1 & LCD_WINCTRL1_FRM)
+	{
+		case LCD_WINCTRL1_FRM_1BPP: return 1; break;
+		case LCD_WINCTRL1_FRM_2BPP: return 2; break;
+		case LCD_WINCTRL1_FRM_4BPP: return 4; break;
+		case LCD_WINCTRL1_FRM_8BPP: return 8; break;
+		case LCD_WINCTRL1_FRM_12BPP: return 16; break;
+		case LCD_WINCTRL1_FRM_16BPP655: return 16; break;
+		case LCD_WINCTRL1_FRM_16BPP565: return 16; break;
+		case LCD_WINCTRL1_FRM_16BPP556: return 16; break;
+		case LCD_WINCTRL1_FRM_16BPPI1555: return 16; break;
+		case LCD_WINCTRL1_FRM_16BPPI5551: return 16; break;
+		case LCD_WINCTRL1_FRM_16BPPA1555: return 16; break;
+		case LCD_WINCTRL1_FRM_16BPPA5551: return 16; break;
+		case LCD_WINCTRL1_FRM_24BPP: return 32; break;
+		case LCD_WINCTRL1_FRM_32BPP: return 32; break;
+		default: return 0; break;
+	}
+}
+
+static int
+fbinfo2index (struct fb_info *fb_info)
+{
+	int i;
+	for (i = 0; i < CONFIG_FB_AU1200_DEVS; ++i)
+	{
+		if (fb_info == (struct fb_info *)(&fb_infos[i]))
+			return i;
+	}
+	printk("au1200fb: ERROR: fbinfo2index failed!\n");
+	return -1;
+}
+
+static void au1200_detect(void)
+{
+	/*
+	 *  This function should detect the current video mode settings 
+	 *  and store it as the default video mode
+	 * Yeh, well, we're not going to change any settings so we're
+	 * always stuck with the default ...
+	 */
+}
+
+static int au1200_encode_fix(struct fb_fix_screeninfo *fix, 
+		const void *_par, struct fb_info_gen *_info)
+{
+    struct au1200fb_info *info = (struct au1200fb_info *) _info;
+    struct au1200fb_par *par = (struct au1200fb_par *) _par;
+	int plane;
+
+	plane = fbinfo2index(info);
+
+	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+
+	fix->smem_start = info->fb_phys;
+	fix->smem_len = info->fb_size;
+	fix->type = FB_TYPE_PACKED_PIXELS;
+	fix->type_aux = 0;
+        fix->visual = (par->var.bits_per_pixel == 8) ?
+	       	FB_VISUAL_PSEUDOCOLOR	: FB_VISUAL_TRUECOLOR;
+	fix->ywrapstep = 0;
+	fix->xpanstep = 1;
+	fix->ypanstep = 1;
+	/* FIX!!!! why doesn't par->line_length work???? it does for au1100 */
+	fix->line_length = fb_pars[plane].line_length; /*par->line_length;*/
+	return 0;
+}
+
+static void set_color_bitfields(struct fb_var_screeninfo *var, int plane)
+{
+	if (var->bits_per_pixel == 8)
+	{
+		var->red.offset = 0;
+		var->red.length = 8;
+		var->green.offset = 0;
+		var->green.length = 8;
+		var->blue.offset = 0;
+		var->blue.length = 8;
+		var->transp.offset = 0;
+		var->transp.length = 0;
+	}
+	else
+	
+	if (var->bits_per_pixel == 16)
+	{
+		/* FIX!!! How does CCO affect this ? */
+		/* FIX!!! Not exactly sure how many of these work with FB */
+		switch (win->w[plane].mode_winctrl1 & LCD_WINCTRL1_FRM)
+		{
+			case LCD_WINCTRL1_FRM_16BPP655:
+				var->red.offset = 10;
+				var->red.length = 6;
+				var->green.offset = 5;
+				var->green.length = 5;
+				var->blue.offset = 0;
+				var->blue.length = 5;
+				var->transp.offset = 0;
+				var->transp.length = 0;
+				break;
+
+			case LCD_WINCTRL1_FRM_16BPP565:
+				var->red.offset = 11;
+				var->red.length = 5;
+				var->green.offset = 5;
+				var->green.length = 6;
+				var->blue.offset = 0;
+				var->blue.length = 5;
+				var->transp.offset = 0;
+				var->transp.length = 0;
+				break;
+
+			case LCD_WINCTRL1_FRM_16BPP556:
+				var->red.offset = 11;
+				var->red.length = 5;
+				var->green.offset = 6;
+				var->green.length = 5;
+				var->blue.offset = 0;
+				var->blue.length = 6;
+				var->transp.offset = 0;
+				var->transp.length = 0;
+				break;
+
+			case LCD_WINCTRL1_FRM_16BPPI1555:
+				var->red.offset = 10;
+				var->red.length = 5;
+				var->green.offset = 5;
+				var->green.length = 5;
+				var->blue.offset = 0;
+				var->blue.length = 5;
+				var->transp.offset = 0;
+				var->transp.length = 0;
+				break;
+
+			case LCD_WINCTRL1_FRM_16BPPI5551:
+				var->red.offset = 11;
+				var->red.length = 5;
+				var->green.offset = 6;
+				var->green.length = 5;
+				var->blue.offset = 1;
+				var->blue.length = 5;
+				var->transp.offset = 0;
+				var->transp.length = 0;
+				break;
+
+			case LCD_WINCTRL1_FRM_16BPPA1555:
+				var->red.offset = 10;
+				var->red.length = 5;
+				var->green.offset = 5;
+				var->green.length = 5;
+				var->blue.offset = 0;
+				var->blue.length = 5;
+				var->transp.offset = 15;
+				var->transp.length = 1;
+				break;
+
+			case LCD_WINCTRL1_FRM_16BPPA5551:
+				var->red.offset = 11;
+				var->red.length = 5;
+				var->green.offset = 6;
+				var->green.length = 5;
+				var->blue.offset = 1;
+				var->blue.length = 5;
+				var->transp.offset = 0;
+				var->transp.length = 1;
+				break;
+
+			default:
+				printk("ERROR: Invalid PIXEL FORMAT!!!\n"); break;
+		}
+	}
+	else
+
+	if (var->bits_per_pixel == 32)
+	{
+		switch (win->w[plane].mode_winctrl1 & LCD_WINCTRL1_FRM)
+		{
+		case LCD_WINCTRL1_FRM_24BPP:
+			var->red.offset = 16;
+			var->red.length = 8;
+			var->green.offset = 8;
+			var->green.length = 8;
+			var->blue.offset = 0;
+			var->blue.length = 8;
+			var->transp.offset = 0;
+			var->transp.length = 0;
+			break;
+
+		case LCD_WINCTRL1_FRM_32BPP:
+			var->red.offset = 16;
+			var->red.length = 8;
+			var->green.offset = 8;
+			var->green.length = 8;
+			var->blue.offset = 0;
+			var->blue.length = 8;
+			var->transp.offset = 24;
+			var->transp.length = 8;
+			break;
+		}
+	}
+	var->red.msb_right = 0;
+	var->green.msb_right = 0;
+	var->blue.msb_right = 0;
+	var->transp.msb_right = 0;
+#if 0
+printk("set_color_bitfields(a=%d, r=%d..%d, g=%d..%d, b=%d..%d)\n",
+	var->transp.offset,
+	var->red.offset+var->red.length-1, var->red.offset,
+	var->green.offset+var->green.length-1, var->green.offset,
+	var->blue.offset+var->blue.length-1, var->blue.offset);
+#endif
+}
+
+static int au1200_decode_var(const struct fb_var_screeninfo *var, 
+		void *_par, struct fb_info_gen *_info)
+{
+	struct au1200fb_par *par = (struct au1200fb_par *)_par;
+	int plane, bpp;
+
+	plane = fbinfo2index((struct fb_info *)_info);
+
+	/*
+	 * Don't allow setting any of these yet: xres and yres don't
+	 * make sense for LCD panels.
+	 */
+	if (var->xres != win->w[plane].xres ||
+	    var->yres != win->w[plane].yres ||
+	    var->xres != win->w[plane].xres ||
+	    var->yres != win->w[plane].yres) {
+		return -EINVAL;
+	}
+
+	bpp = winbpp(win->w[plane].mode_winctrl1);
+	if(var->bits_per_pixel != bpp) {
+		/* on au1200, window pixel format is independent of panel pixel */
+		printk("WARNING: bits_per_pizel != panel->bpp\n");
+	}
+
+	memset(par, 0, sizeof(struct au1200fb_par));
+	par->var = *var;
+	
+	/* FIX!!! */
+	switch (var->bits_per_pixel) {
+		case 8:
+			par->var.bits_per_pixel = 8;
+			break;
+		case 16:
+			par->var.bits_per_pixel = 16;
+			break;
+		case 24:
+		case 32:
+			par->var.bits_per_pixel = 32;
+			break;
+		default:
+			printk("color depth %d bpp not supported\n",
+					var->bits_per_pixel);
+			return -EINVAL;
+
+	}
+	set_color_bitfields(&par->var, plane);
+	/* FIX!!! what is this for 24/32bpp? */
+	par->cmap_len = (par->var.bits_per_pixel == 8) ? 256 : 16;
+	return 0;
+}
+
+static int au1200_encode_var(struct fb_var_screeninfo *var, 
+		const void *par, struct fb_info_gen *_info)
+{
+	*var = ((struct au1200fb_par *)par)->var;
+	return 0;
+}
+
+static void 
+au1200_get_par(void *_par, struct fb_info_gen *_info)
+{
+	int index;
+
+	index = fbinfo2index((struct fb_info *)_info);
+	*(struct au1200fb_par *)_par = fb_pars[index];
+}
+
+static void au1200_set_par(const void *par, struct fb_info_gen *info)
+{
+	/* nothing to do: we don't change any settings */
+}
+
+static int au1200_getcolreg(unsigned regno, unsigned *red, unsigned *green,
+			 unsigned *blue, unsigned *transp,
+			 struct fb_info *info)
+{
+	struct au1200fb_info* i = (struct au1200fb_info*)info;
+
+	if (regno > 255)
+		return 1;
+   
+	*red    = i->palette[regno].red; 
+	*green  = i->palette[regno].green; 
+	*blue   = i->palette[regno].blue; 
+	*transp = 0;
+
+	return 0;
+}
+
+static int au1200_setcolreg(unsigned regno, unsigned red, unsigned green,
+			 unsigned blue, unsigned transp,
+			 struct fb_info *info)
+{
+	struct au1200fb_info* i = (struct au1200fb_info *)info;
+	int plane, bpp;
+
+	plane = fbinfo2index((struct fb_info *)info);
+	bpp = winbpp(win->w[plane].mode_winctrl1);
+
+	if (regno > 255)
+		return 1;
+
+	i->palette[regno].red    = red;
+	i->palette[regno].green  = green;
+	i->palette[regno].blue   = blue;
+   
+	switch(bpp) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		red >>= 10;
+		green >>= 10;
+		blue >>= 10;
+		panel_reg->lcd_pallettebase[regno] = (blue&0x1f) | 
+			((green&0x3f)<<5) | ((red&0x1f)<<11);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+/* FIX!!!! depends upon pixel format */
+	case 16:
+		i->fbcon_cmap16[regno] =
+			((red & 0xf800) >> 0) |
+			((green & 0xfc00) >> 5) |
+			((blue & 0xf800) >> 11);
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		i->fbcon_cmap32[regno] =
+            (((u32 )transp & 0xff00) << 16) |
+            (((u32 )red & 0xff00) << 8) |
+            (((u32 )green & 0xff00)) |
+            (((u32 )blue & 0xff00) >> 8);
+		break;
+#endif
+	default:
+	printk("unsupported au1200_setcolreg(%d)\n", bpp);
+		break;
+	}
+
+	return 0;
+}
+
+
+static int  au1200_blank(int blank_mode, struct fb_info_gen *_info)
+{
+	struct au1200fb_info *fb_info = (struct au1200fb_info *)_info;
+	int plane;
+
+	/* Short-circuit screen blanking */
+	if (fb_info->noblanking)
+		return 0;
+
+	plane = fbinfo2index((struct fb_info *)_info);
+
+	switch (blank_mode) {
+	case VESA_NO_BLANKING:
+		/* printk("turn on panel\n"); */
+		au1200_setpanel(panel);
+		break;
+
+	case VESA_VSYNC_SUSPEND:
+	case VESA_HSYNC_SUSPEND:
+	case VESA_POWERDOWN:
+		/* printk("turn off panel\n"); */
+		au1200_setpanel(NULL);
+        break;
+	default: 
+		break;
+
+	}
+	return 0;
+}
+
+static void au1200_set_disp(const void *unused, struct display *disp,
+			 struct fb_info_gen *info)
+{
+	struct au1200fb_info *fb_info;
+
+	fb_info = (struct au1200fb_info *)info;
+
+	disp->screen_base = (char *)fb_info->fb_virt_start;
+
+	switch (disp->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		disp->dispsw = &fbcon_cfb8;
+		if (fb_info->nohwcursor)
+			fbcon_cfb8.cursor = au1200_nocursor;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 16:
+		disp->dispsw = &fbcon_cfb16;
+		disp->dispsw_data = fb_info->fbcon_cmap16;
+		if (fb_info->nohwcursor)
+			fbcon_cfb16.cursor = au1200_nocursor;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB32
+	case 32:
+		disp->dispsw = &fbcon_cfb32;
+		disp->dispsw_data = fb_info->fbcon_cmap32;
+		if (fb_info->nohwcursor)
+			fbcon_cfb32.cursor = au1200_nocursor;
+		break;
+#endif
+	default:
+		disp->dispsw = &fbcon_dummy;
+		disp->dispsw_data = NULL;
+		break;
+	}
+}
+
+static int
+au1200fb_mmap(struct fb_info *_fb,
+	     struct file *file,
+	     struct vm_area_struct *vma)
+{
+	unsigned int len;
+	unsigned long start=0, off;
+
+	struct au1200fb_info *fb_info = (struct au1200fb_info *)_fb;
+
+	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
+		return -EINVAL;
+	}
+    
+	start = fb_info->fb_phys & PAGE_MASK;
+	len = PAGE_ALIGN((start & ~PAGE_MASK) + fb_info->fb_size);
+
+	off = vma->vm_pgoff << PAGE_SHIFT;
+
+	if ((vma->vm_end - vma->vm_start + off) > len) {
+		return -EINVAL;
+	}
+
+	off += start;
+	vma->vm_pgoff = off >> PAGE_SHIFT;
+
+	pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK;
+	pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED;
+
+	/* This is an IO map - tell maydump to skip this VMA */
+	vma->vm_flags |= VM_IO;
+    
+	if (io_remap_page_range(vma->vm_start, off,
+				vma->vm_end - vma->vm_start,
+				vma->vm_page_prot)) {
+		return -EAGAIN;
+	}
+
+	fb_info->mmaped = 1;
+	return 0;
+}
+
+int au1200_pan_display(const struct fb_var_screeninfo *var,
+		       struct fb_info_gen *info)
+{
+	return 0;
+}
+
+#ifndef ORIGINAL
+
+void set_global(u_int cmd, au1200_lcd_global_regs_t *pdata) {
+
+    unsigned int hi0, hi1, divider;
+ 
+    /* SCREEN_SIZE: user cannot reset size, must switch panel choice */
+
+    if (pdata->flags & SCREEN_BACKCOLOR)
+        lcd->backcolor = pdata->backcolor;
+
+    if (pdata->flags & SCREEN_BRIGHTNESS) {
+
+        // limit brightness pwm duty to >= 30/1600
+        if (pdata->brightness < 30) {
+            pdata->brightness = 30;
+        }
+        divider = (lcd->pwmdiv & 0x3FFFF) + 1;
+        hi1 = (lcd->pwmhi >> 16) + 1;
+        hi1 = (((pdata->brightness & 0xFF)+1) * divider >> 8);
+        lcd->pwmhi &= 0xFFFF;
+        lcd->pwmhi |= (hi1 << 16);
+    }
+
+    if (pdata->flags & SCREEN_COLORKEY)
+		lcd->colorkey = pdata->colorkey;
+
+    if (pdata->flags & SCREEN_MASK)
+        lcd->colorkeymsk = pdata->mask;
+}
+
+void get_global(u_int cmd, au1200_lcd_global_regs_t *pdata) {
+
+    unsigned int hi1, divider;
+
+    pdata->xsize = ((lcd->screen & LCD_SCREEN_SX) >> 19) + 1;
+    pdata->ysize = ((lcd->screen & LCD_SCREEN_SY) >> 8) + 1;
+
+    pdata->backcolor = lcd->backcolor;
+	pdata->colorkey = lcd->colorkey;
+    pdata->mask = lcd->colorkeymsk;
+
+    // brightness
+    hi1 = (lcd->pwmhi >> 16) + 1;
+    divider = (lcd->pwmdiv & 0x3FFFF) + 1;
+    pdata->brightness = ((hi1 << 8) / divider) - 1;
+
+}
+
+void print_global(au1200_lcd_global_regs_t *pdata) {
+
+    printk("pdata->xsize %x\n", pdata->xsize);
+    printk("pdata->ysize %x\n", pdata->ysize);
+    printk("pdata->backcolor %x\n", pdata->backcolor);
+    printk("pdata->brightness %x\n", pdata->brightness);
+	printk("pdata->colorkey %x\n", pdata->colorkey);
+    printk("pdata->mask %x\n", pdata->mask);
+}
+
+void set_window(unsigned int plane, au1200_lcd_window_regs_t *pdata) {
+
+    unsigned int val, bpp;
+
+    /* Window control register 0 */
+    if (pdata->flags & WIN_POSITION) {
+        val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_OX | LCD_WINCTRL0_OY);
+        val |= ((pdata->xpos << 21) & LCD_WINCTRL0_OX);
+        val |= ((pdata->ypos << 10) & LCD_WINCTRL0_OY);
+        lcd->window[plane].winctrl0 = val;
+    }
+    if (pdata->flags & WIN_ALPHA_COLOR) {
+        val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_A);
+        val |= ((pdata->alpha_color << 2) & LCD_WINCTRL0_A);
+        lcd->window[plane].winctrl0 = val;
+    }
+    if (pdata->flags & WIN_ALPHA_MODE) {
+        val = lcd->window[plane].winctrl0 & ~(LCD_WINCTRL0_AEN);
+        val |= ((pdata->alpha_mode << 1) & LCD_WINCTRL0_AEN);
+        lcd->window[plane].winctrl0 = val;
+    }
+
+    /* Window control register 1 */
+    if (pdata->flags & WIN_PRIORITY) {
+        val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PRI);
+        val |= ((pdata->priority << 30) & LCD_WINCTRL1_PRI);
+        lcd->window[plane].winctrl1 = val;
+    }
+    if (pdata->flags & WIN_CHANNEL) {
+        val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PIPE);
+        val |= ((pdata->channel << 29) & LCD_WINCTRL1_PIPE);
+        lcd->window[plane].winctrl1 = val;
+    }
+    if (pdata->flags & WIN_BUFFER_FORMAT) {
+        val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_FRM);
+        val |= ((pdata->buffer_format << 25) & LCD_WINCTRL1_FRM);
+        lcd->window[plane].winctrl1 = val;
+    }
+    if (pdata->flags & WIN_COLOR_ORDER) {
+        val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_CCO);
+        val |= ((pdata->color_order << 24) & LCD_WINCTRL1_CCO);
+        lcd->window[plane].winctrl1 = val;
+    }
+    if (pdata->flags & WIN_PIXEL_ORDER) {
+        val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_PO);
+        val |= ((pdata->pixel_order << 22) & LCD_WINCTRL1_PO);
+        lcd->window[plane].winctrl1 = val;
+    }
+    if (pdata->flags & WIN_SIZE) {
+        val = lcd->window[plane].winctrl1 & ~(LCD_WINCTRL1_SZX | LCD_WINCTRL1_SZY);
+        val |= (((pdata->xsize << 11) - 1) & LCD_WINCTRL1_SZX);
+        val |= (((pdata->ysize) - 1) & LCD_WINCTRL1_SZY);
+        lcd->window[plane].winctrl1 = val;
+        /* program buffer line width */
+        bpp = winbpp(val) / 8;
+        val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_BX);
+        val |= (((pdata->xsize * bpp) << 8) & LCD_WINCTRL2_BX);
+        lcd->window[plane].winctrl2 = val;
+    }
+
+    /* Window control register 2 */
+    if (pdata->flags & WIN_COLORKEY_MODE) {
+        val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_CKMODE);
+        val |= ((pdata->colorkey_mode << 24) & LCD_WINCTRL2_CKMODE);
+        lcd->window[plane].winctrl2 = val;
+    }
+    if (pdata->flags & WIN_DOUBLE_BUFFER_MODE) {
+        val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_DBM);
+        val |= ((pdata->double_buffer_mode << 23) & LCD_WINCTRL2_DBM);
+        lcd->window[plane].winctrl2 = val;
+    }
+    if (pdata->flags & WIN_RAM_ARRAY_MODE) {
+        val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_RAM);
+        val |= ((pdata->colorkey_mode << 21) & LCD_WINCTRL2_RAM);
+        lcd->window[plane].winctrl2 = val;
+    }
+
+    /* Buffer line width programmed with WIN_SIZE */
+
+    if (pdata->flags & WIN_BUFFER_SCALE) {
+        val = lcd->window[plane].winctrl2 & ~(LCD_WINCTRL2_SCX | LCD_WINCTRL2_SCY);
+        val |= ((pdata->xsize << 11) & LCD_WINCTRL2_SCX);
+        val |= ((pdata->ysize) & LCD_WINCTRL2_SCY);
+        lcd->window[plane].winctrl2 = val;
+    }
+
+    if (pdata->flags & WIN_ENABLE) {
+        val = lcd->winenable;
+        val &= ~(1<<plane);
+        val |= (pdata->enable & 1) << plane;
+        lcd->winenable = val;
+    }
+
+}
+
+void get_window(unsigned int plane, au1200_lcd_window_regs_t *pdata) {
+
+    /* Window control register 0 */
+    pdata->xpos = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_OX) >> 21;
+    pdata->ypos = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_OY) >> 10;
+    pdata->alpha_color = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_A) >> 2;
+    pdata->alpha_mode = (lcd->window[plane].winctrl0 & LCD_WINCTRL0_AEN) >> 1;
+
+    /* Window control register 1 */
+    pdata->priority = (lcd->window[plane].winctrl1& LCD_WINCTRL1_PRI) >> 30;
+    pdata->channel = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_PIPE) >> 29;
+    pdata->buffer_format = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_FRM) >> 25;
+    pdata->color_order = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_CCO) >> 24;
+    pdata->pixel_order = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_PO) >> 22;
+    pdata->xsize = ((lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZX) >> 11) + 1;
+    pdata->ysize = (lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZY) + 1;
+
+    /* Window control register 2 */
+    pdata->colorkey_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_CKMODE) >> 24;
+    pdata->double_buffer_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_DBM) >> 23;
+    pdata->colorkey_mode = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_RAM) >> 21;
+    pdata->xsize = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_SCX) >> 11;
+    pdata->ysize = (lcd->window[plane].winctrl2 & LCD_WINCTRL2_SCY);
+
+    pdata->enable = (lcd->winenable >> plane) & 1;
+}
+
+#endif //not ORIGINAL
+
+static int au1200fb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+			  u_long arg, int con, struct fb_info *info)
+{
+	int plane;
+#ifdef ORIGINAL
+    au1200_lcd_getset_t iodata;
+#endif
+
+	plane = fbinfo2index(info);
+    printk("au1200fb: ioctl %d on plane %d\n", cmd, plane);
+
+#ifdef ORIGINAL
+	if (cmd == 0x46FF)
+	{
+		au1200_lcd_getset_t iodata;
+
+		if (copy_from_user(&iodata, (void *) arg, sizeof(au1200_lcd_getset_t)))
+			return -EFAULT;
+
+		switch (iodata.subcmd)
+		{
+			case AU1200_LCD_GET_WINENABLE:
+				iodata.winenable.enable = (lcd->winenable & (1<<plane)) ? 1 : 0;
+				break;
+			case AU1200_LCD_SET_WINENABLE:
+				{
+				u32 winenable;
+				winenable = lcd->winenable;
+				winenable &= ~(1<<plane);
+				winenable |= (iodata.winenable.enable) ? (1<<plane) : 0;
+				lcd->winenable = winenable;
+				}
+				break;
+			case AU1200_LCD_GET_WINLOCATION:
+				iodata.winlocation.x =
+					(lcd->window[plane].winctrl0 & LCD_WINCTRL0_OX) >> 21;
+				iodata.winlocation.y =
+					(lcd->window[plane].winctrl0 & LCD_WINCTRL0_OY) >> 10;
+				break;
+			case AU1200_LCD_SET_WINLOCATION:
+				au1200_setlocation(plane, iodata.winlocation.x, iodata.winlocation.y);
+				break;
+			case AU1200_LCD_GET_WINSIZE:
+				iodata.winsize.hsz =
+                    ((lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZX) >> 11) + 1;
+				iodata.winsize.vsz =
+                    ((lcd->window[plane].winctrl1 & LCD_WINCTRL1_SZY) >> 0) + 1;
+				break;
+			case AU1200_LCD_SET_WINSIZE:
+				au1200_setsize(plane, iodata.winsize.hsz, iodata.winsize.vsz);
+				break;
+			case AU1200_LCD_GET_BACKCOLOR:
+				iodata.backcolor.color = lcd->backcolor;
+				break;
+			case AU1200_LCD_SET_BACKCOLOR:
+				 lcd->backcolor = iodata.backcolor.color;
+				break;
+			case AU1200_LCD_GET_COLORKEY:
+				iodata.colorkey.key = lcd->colorkey;
+				iodata.colorkey.mask = lcd->colorkeymsk;
+				break;
+			case AU1200_LCD_SET_COLORKEY:
+				lcd->colorkey = iodata.colorkey.key;
+				lcd->colorkeymsk = iodata.colorkey.mask;
+				break;
+			case AU1200_LCD_GET_PANEL:
+				iodata.panel.panel = panel_index;
+				break;
+			case AU1200_LCD_SET_PANEL:
+				if ((iodata.panel.panel >= 0) && (iodata.panel.panel < NUM_PANELS))
+				{
+					struct panel_settings *newpanel;
+					panel_index = iodata.panel.panel;
+					newpanel = &panels[panel_index];
+					au1200_setpanel(newpanel);
+				}
+				break;
+		}
+
+		return copy_to_user((void *) arg, &iodata, sizeof(au1200_lcd_getset_t)) ? -EFAULT : 0;
+	}
+
+	return -EINVAL;
+#else
+
+    printk("IOCTL called\n");
+    if (cmd == AU1200_LCD_FB_IOCTL) {
+
+        au1200_lcd_iodata_t iodata;
+
+        if (copy_from_user(&iodata, (void *) arg, sizeof(au1200_lcd_iodata_t)))
+            return -EFAULT;
+
+        printk("FB IOCTL called\n");
+
+        switch (iodata.subcmd)
+        {
+
+        case AU1200_LCD_SET_SCREEN:
+            printk("AU1200_LCD_SET_SCREEN\n");
+            set_global(cmd, &iodata.global);
+            break;
+
+        case AU1200_LCD_GET_SCREEN:
+            printk("AU1200_LCD_GET_SCREEN\n");
+            get_global(cmd, &iodata.global);
+            break;
+
+        case AU1200_LCD_SET_WINDOW:
+            printk("AU1200_LCD_SET_WINDOW\n");
+            set_window(plane, &iodata.window);
+            break;
+
+        case AU1200_LCD_GET_WINDOW:
+            printk("AU1200_LCD_GET_WINDOW\n");
+            get_window(plane, &iodata.window);
+            break;
+
+        case AU1200_LCD_SET_PANEL:
+            printk("AU1200_LCD_SET_PANEL\n");
+            if ((iodata.global.panel_choice >= 0) && (iodata.global.panel_choice < NUM_PANELS))
+            {
+                struct panel_settings *newpanel;
+                panel_index = iodata.global.panel_choice;
+                newpanel = &panels[panel_index];
+                au1200_setpanel(newpanel);
+            }
+            break;
+
+        case AU1200_LCD_GET_PANEL:
+            printk("AU1200_LCD_GET_PANEL\n");
+            iodata.global.panel_choice = panel_index;
+            break;
+
+        default:
+            return -EINVAL;
+        }
+
+        if (copy_to_user((void *) arg, &iodata, sizeof(au1200_lcd_iodata_t))) 
+            return -EFAULT;
+
+    }
+#endif
+
+	return 0;
+}
+
+static struct fbgen_hwswitch au1200_switch = {
+	au1200_detect, 
+	au1200_encode_fix, 
+	au1200_decode_var, 
+	au1200_encode_var, 
+	au1200_get_par, 
+	au1200_set_par, 
+	au1200_getcolreg, 
+	au1200_setcolreg, 
+	au1200_pan_display, 
+	au1200_blank, 
+	au1200_set_disp
+};
+
+static void au1200_setpanel (struct panel_settings *newpanel)
+{
+	/*
+	 * Perform global setup/init of LCD controller
+	 */
+	uint32 winenable;
+
+	/* Make sure all windows disabled */
+	winenable = lcd->winenable;
+	lcd->winenable = 0;
+
+	/*
+	 * Ensure everything is disabled before reconfiguring
+	 */
+	if (lcd->screen & LCD_SCREEN_SEN)
+	{
+		/* Wait for vertical sync period */
+		lcd->intstatus = LCD_INT_SS;
+		while ((lcd->intstatus & LCD_INT_SS) == 0)
+			;		
+		
+		lcd->screen &= ~LCD_SCREEN_SEN;	/*disable the controller*/
+		
+		do
+		{
+			lcd->intstatus = lcd->intstatus; /*clear interrupts*/
+		}
+		/*wait for controller to shut down*/
+		while ((lcd->intstatus & LCD_INT_SD) == 0);
+		
+		/* Call shutdown of current panel (if up) */
+		/* this must occur last, because if an external clock is driving
+		    the controller, the clock cannot be turned off before first
+			shutting down the controller.
+		 */
+		if (panel->device_shutdown != NULL) panel->device_shutdown();
+	}
+
+	/* Check if only needing to turn off panel */
+	if (panel == NULL) return;
+
+	panel = newpanel;
+	
+	printk("Panel(%s), %dx%d\n", panel->name, panel->Xres, panel->Yres);
+
+	/*
+	 * Setup clocking if internal LCD clock source (assumes sys_auxpll valid)
+	 */
+	if (!(panel->mode_clkcontrol & LCD_CLKCONTROL_EXT))
+	{
+		uint32 sys_clksrc;
+		/* WARNING! This should really be a check since other peripherals can 
+		   be affected by changins sys_auxpll  */
+		au_writel(panel->mode_auxpll, SYS_AUXPLL);
+		sys_clksrc = au_readl(SYS_CLKSRC) & ~0x0000001f; 
+		sys_clksrc |= panel->mode_toyclksrc;
+		au_writel(sys_clksrc, SYS_CLKSRC);
+	}
+
+	/*
+	 * Configure panel timings
+	 */
+	lcd->screen = panel->mode_screen;
+	lcd->horztiming = panel->mode_horztiming;
+	lcd->verttiming = panel->mode_verttiming;
+	lcd->clkcontrol = panel->mode_clkcontrol;
+	lcd->pwmdiv = panel->mode_pwmdiv;
+	lcd->pwmhi = panel->mode_pwmhi;
+	lcd->outmask = panel->mode_outmask;
+	lcd->fifoctrl = panel->mode_fifoctrl;
+	au_sync();
+
+	/* FIX!!! Check window settings to make sure still valid for new geometry */
+	au1200_setlocation(0, win->w[0].xpos, win->w[0].ypos);
+	au1200_setlocation(1, win->w[1].xpos, win->w[1].ypos);
+	au1200_setlocation(2, win->w[2].xpos, win->w[2].ypos);
+	au1200_setlocation(3, win->w[3].xpos, win->w[3].ypos);
+	lcd->winenable = winenable;
+
+	/*
+	 * Re-enable screen now that it is configured
+	 */
+	lcd->screen |= LCD_SCREEN_SEN;
+	au_sync();
+
+	/* Call init of panel */
+	if (panel->device_init != NULL) panel->device_init();
+
+#if 0
+#define D(X) printk("%25s: %08X\n", #X, X)
+	D(lcd->screen);
+	D(lcd->horztiming);
+	D(lcd->verttiming);
+	D(lcd->clkcontrol);
+	D(lcd->pwmdiv);
+	D(lcd->pwmhi);
+	D(lcd->outmask);
+	D(lcd->fifoctrl);
+	D(lcd->window[0].winctrl0);
+	D(lcd->window[0].winctrl1);
+	D(lcd->window[0].winctrl2);
+	D(lcd->window[0].winbuf0);
+	D(lcd->window[0].winbuf1);
+	D(lcd->window[0].winbufctrl);
+	D(lcd->window[1].winctrl0);
+	D(lcd->window[1].winctrl1);
+	D(lcd->window[1].winctrl2);
+	D(lcd->window[1].winbuf0);
+	D(lcd->window[1].winbuf1);
+	D(lcd->window[1].winbufctrl);
+	D(lcd->window[2].winctrl0);
+	D(lcd->window[2].winctrl1);
+	D(lcd->window[2].winctrl2);
+	D(lcd->window[2].winbuf0);
+	D(lcd->window[2].winbuf1);
+	D(lcd->window[2].winbufctrl);
+	D(lcd->window[3].winctrl0);
+	D(lcd->window[3].winctrl1);
+	D(lcd->window[3].winctrl2);
+	D(lcd->window[3].winbuf0);
+	D(lcd->window[3].winbuf1);
+	D(lcd->window[3].winbufctrl);
+	D(lcd->winenable);
+	D(lcd->intenable);
+	D(lcd->intstatus);
+	D(lcd->backcolor);
+	D(lcd->winenable);
+	D(lcd->colorkey);
+    D(lcd->colorkeymsk);
+	D(lcd->hwc.cursorctrl);
+	D(lcd->hwc.cursorpos);
+	D(lcd->hwc.cursorcolor0);
+	D(lcd->hwc.cursorcolor1);
+	D(lcd->hwc.cursorcolor2);
+	D(lcd->hwc.cursorcolor3);
+#endif
+}
+
+static int au1200_setsize (int plane, int xres, int yres)
+{
+	uint32 winctrl1, winctrl2;
+	int Bpp;
+
+	/* FIX!!! X*Y can not surpass allocated memory */
+
+	printk("au1200_setsize: plane %d size %dx%d\n", plane, xres, yres);
+
+	winctrl1 = lcd->window[plane].winctrl1;
+	printk("org winctrl1 %08X\n", winctrl1);
+	winctrl1 &= ~(LCD_WINCTRL1_SZX | LCD_WINCTRL1_SZY);
+	winctrl1 |= ((xres-1) << 11);
+	winctrl1 |= (yres-1);
+	printk("new winctrl1 %08X\n", winctrl1);
+   	lcd->window[plane].winctrl1 = winctrl1;
+
+    Bpp = winbpp(winctrl1) / 8;
+
+    winctrl2 = lcd->window[plane].winctrl2;
+    winctrl2 &= ~(LCD_WINCTRL2_BX);
+    winctrl2 |= ((xres*Bpp) << 8);
+
+    printk("au1200_setsize: done\n");
+
+	return 0;
+}
+
+static int au1200_setlocation (int plane, int xpos, int ypos)
+{
+	uint32 winctrl0, winctrl1, winenable, fb_offset = 0;
+	int xsz, ysz;
+
+	/* FIX!!! NOT CHECKING FOR COMPLETE OFFSCREEN YET */
+
+	winctrl0 = lcd->window[plane].winctrl0;
+	winctrl1 = lcd->window[plane].winctrl1;
+	winctrl0 &= (LCD_WINCTRL0_A | LCD_WINCTRL0_AEN);
+	winctrl1 &= ~(LCD_WINCTRL1_SZX | LCD_WINCTRL1_SZY);
+
+	/* Check for off-screen adjustments */
+	xsz = win->w[plane].xres;
+	ysz = win->w[plane].yres;
+	if ((xpos + win->w[plane].xres) > panel->Xres)
+	{
+		/* Off-screen to the right */
+		xsz = panel->Xres - xpos; /* off by 1 ??? */
+		/*printk("off screen right\n");*/
+	}
+
+	if ((ypos + win->w[plane].yres) > panel->Yres)
+	{
+		/* Off-screen to the bottom */
+		ysz = panel->Yres - ypos; /* off by 1 ??? */
+		/*printk("off screen bottom\n");*/
+	}
+
+	if (xpos < 0)
+	{
+		/* Off-screen to the left */
+		xsz = win->w[plane].xres + xpos;
+		fb_offset += (((0 - xpos) * winbpp(lcd->window[plane].winctrl1))/8);
+		xpos = 0;
+		/*printk("off screen left\n");*/
+	}
+
+	if (ypos < 0)
+	{
+		/* Off-screen to the top */
+		ysz = win->w[plane].yres + ypos;
+		fb_offset += ((0 - ypos) *	fb_pars[plane].line_length);
+		ypos = 0;
+		/*printk("off screen top\n");*/
+	}
+
+	/* record settings */
+	win->w[plane].xpos = xpos;
+	win->w[plane].ypos = ypos;
+
+	xsz -= 1;
+	ysz -= 1;
+	winctrl0 |= (xpos << 21);
+	winctrl0 |= (ypos << 10);
+	winctrl1 |= (xsz << 11);
+	winctrl1 |= (ysz << 0);
+
+	/* Disable the window while making changes, then restore WINEN */
+	winenable = lcd->winenable & (1 << plane);
+	lcd->winenable &= ~(1 << plane);
+	lcd->window[plane].winctrl0 = winctrl0;
+	lcd->window[plane].winctrl1 = winctrl1;
+	lcd->window[plane].winbuf0 =
+	lcd->window[plane].winbuf1 = fb_infos[plane].fb_phys + fb_offset;
+	lcd->window[plane].winbufctrl = 0; /* select winbuf0 */
+	lcd->winenable |= winenable;
+
+	return 0;
+}
+
+static void au1200_setmode(int plane)
+{
+	/* Window/plane setup */
+	lcd->window[plane].winctrl1 = ( 0
+		| LCD_WINCTRL1_PRI_N(plane)
+		| win->w[plane].mode_winctrl1 /* FRM,CCO,PO,PIPE */
+		) ;
+
+	au1200_setlocation(plane, win->w[plane].xpos, win->w[plane].ypos);
+
+	lcd->window[plane].winctrl2 = ( 0
+		| LCD_WINCTRL2_CKMODE_00
+		| LCD_WINCTRL2_DBM
+/*			| LCD_WINCTRL2_RAM */
+		| LCD_WINCTRL2_BX_N(fb_pars[plane].line_length)
+		| LCD_WINCTRL2_SCX_1
+		| LCD_WINCTRL2_SCY_1
+		) ;
+	lcd->winenable |= win->w[plane].mode_winenable;
+	au_sync();
+
+}
+
+static unsigned long
+au1200fb_alloc_fbmem (unsigned long size)
+{
+	/* __get_free_pages() fulfills a max request of 2MB */
+	/* do multiple requests to obtain large contigous mem */
+#define MAX_GFP 0x00200000
+
+	unsigned long mem, amem, alloced = 0, allocsize;
+
+	size += 0x1000;
+	allocsize = (size < MAX_GFP) ? size : MAX_GFP;
+
+	/* Get first chunk */
+	mem = (unsigned long )
+		__get_free_pages(GFP_ATOMIC | GFP_DMA, get_order(allocsize));
+	if (mem != 0) alloced = allocsize;
+
+	/* Get remaining, contiguous chunks */
+	while (alloced < size)
+	{
+		amem = (unsigned long )
+			__get_free_pages(GFP_ATOMIC | GFP_DMA, get_order(allocsize));
+		if (amem != 0)
+			alloced += allocsize;
+
+		/* check for contiguous mem alloced */
+		if ((amem == 0) || (amem + allocsize) != mem)
+			break;
+		else
+			mem = amem;
+	}
+	return mem;
+}
+
+int __init au1200fb_init(void)
+{
+	int num_panels = sizeof(panels)/sizeof(struct panel_settings);
+	struct au1200fb_info *fb_info;
+	struct display *disp;
+	struct au1200fb_par *par;
+	unsigned long page;
+	int plane, bpp;
+
+    /*
+	* Get the panel information/display mode
+	*/
+	if (panel_index < 0)
+		panel_index = board_au1200fb_panel();
+	if ((panel_index < 0) || (panel_index >= num_panels)) {
+		printk("ERROR: INVALID PANEL %d\n", panel_index);
+		return -EINVAL;
+	}
+	panel = &panels[panel_index];
+	win = &windows[window_index];
+
+	printk("au1200fb: Panel %d %s\n", panel_index, panel->name);
+	printk("au1200fb: Win %d %s\n", window_index, win->name);
+
+	/* Global setup/init */
+	au1200_setpanel(panel);
+	lcd->intenable = 0;
+	lcd->intstatus = ~0;
+	lcd->backcolor = win->mode_backcolor;
+	lcd->winenable = 0;
+
+	/* Setup Color Key - FIX!!! */
+	lcd->colorkey = win->mode_colorkey;
+	lcd->colorkeymsk = win->mode_colorkeymsk;
+
+	/* Setup HWCursor - FIX!!! Need to support this eventually */
+	lcd->hwc.cursorctrl = 0;
+	lcd->hwc.cursorpos = 0;
+	lcd->hwc.cursorcolor0 = 0;
+	lcd->hwc.cursorcolor1 = 0;
+	lcd->hwc.cursorcolor2 = 0;
+	lcd->hwc.cursorcolor3 = 0;
+
+	/* Register each plane as a frame buffer device */
+	for (plane = 0; plane < CONFIG_FB_AU1200_DEVS; ++plane)
+	{
+		fb_info = &fb_infos[plane];
+		disp = &disps[plane];
+		par = &fb_pars[plane];
+
+		bpp = winbpp(win->w[plane].mode_winctrl1);
+		if (win->w[plane].xres == 0)
+			win->w[plane].xres = panel->Xres;
+		if (win->w[plane].yres == 0)
+			win->w[plane].yres = panel->Yres;
+
+		par->var.xres =
+		par->var.xres_virtual = win->w[plane].xres;
+		par->var.yres =
+		par->var.yres_virtual = win->w[plane].yres;
+		par->var.bits_per_pixel = bpp;
+		par->line_length = win->w[plane].xres * bpp / 8; /* in bytes */
+		/*
+		 * Allocate LCD framebuffer from system memory
+		 * Set page reserved so that mmap will work. This is necessary
+		 * since we'll be remapping normal memory.
+		 */
+		fb_info->fb_size = (win->w[plane].xres * win->w[plane].yres * bpp) / 8;
+		fb_info->fb_virt_start = au1200fb_alloc_fbmem(fb_info->fb_size);
+		if (!fb_info->fb_virt_start) {
+			printk("Unable to allocate fb memory\n");
+			return -ENOMEM;
+		}
+		fb_info->fb_phys = virt_to_bus((void *)fb_info->fb_virt_start);
+		for (page = fb_info->fb_virt_start;
+		     page < PAGE_ALIGN(fb_info->fb_virt_start + fb_info->fb_size); 
+		     page += PAGE_SIZE) {
+			SetPageReserved(virt_to_page(page));
+		}
+		/* Convert to kseg1 */
+		fb_info->fb_virt_start =
+			(void *)((u32)fb_info->fb_virt_start | 0xA0000000);
+		/* FIX!!! may wish to avoid this to save startup time??? */
+		memset((void *)fb_info->fb_virt_start, 0, fb_info->fb_size);
+
+		fb_info->gen.parsize = sizeof(struct au1200fb_par);
+		fb_info->gen.fbhw = &au1200_switch;
+		strcpy(fb_info->gen.info.modename, "Au1200 LCD");
+		fb_info->gen.info.changevar = NULL;
+		fb_info->gen.info.node = -1;
+
+		fb_info->gen.info.fbops = &au1200fb_ops;
+		fb_info->gen.info.disp = disp;
+		fb_info->gen.info.switch_con = &fbgen_switch;
+		fb_info->gen.info.updatevar = &fbgen_update_var;
+		fb_info->gen.info.blank = &fbgen_blank;
+		fb_info->gen.info.flags = FBINFO_FLAG_DEFAULT;
+
+		fb_info->nohwcursor = 1;
+		fb_info->noblanking = 1;
+
+		/* This should give a reasonable default video mode */
+		fbgen_get_var(&disp->var, -1, &fb_info->gen.info);
+		fbgen_do_set_var(&disp->var, 1, &fb_info->gen);
+		fbgen_set_disp(-1, &fb_info->gen);
+		fbgen_install_cmap(0, &fb_info->gen);
+
+		/* Turn on plane */
+		au1200_setmode(plane);
+
+		if (register_framebuffer(&fb_info->gen.info) < 0)
+			return -EINVAL;
+
+		printk(KERN_INFO "fb%d: %s plane %d @ %08X (%d x %d x %d)\n", 
+				GET_FB_IDX(fb_info->gen.info.node), 
+				fb_info->gen.info.modename, plane, fb_info->fb_phys,
+				win->w[plane].xres, win->w[plane].yres, bpp);
+	}
+	/* uncomment this if your driver cannot be unloaded */
+	/* MOD_INC_USE_COUNT; */
+	return 0;
+}
+
+void au1200fb_setup(char *options, int *ints)
+{
+	char* this_opt;
+	int i;
+	int num_panels = sizeof(panels)/sizeof(struct panel_settings);
+
+	if (!options || !*options)
+		return;
+
+	for(this_opt=strtok(options, ","); this_opt;
+	    this_opt=strtok(NULL, ",")) {
+		if (!strncmp(this_opt, "panel:", 6)) {
+			int i;
+			long int li;
+			char *endptr;
+			this_opt += 6;
+
+			/* Panel name can be name, "bs" for board-switch, or number/index */
+			li = simple_strtol(this_opt, &endptr, 0);
+			if (*endptr == '\0') {
+				panel_index = (int)li;
+			}
+			else if (strcmp(this_opt, "bs") == 0) {
+				panel_index = board_au1200fb_panel();
+			}
+			else
+			for (i=0; i<num_panels; i++) {
+				if (!strcmp(this_opt, panels[i].name)) {
+					panel_index = i;
+					break;
+				}
+			}
+		}
+		else if (!strncmp(this_opt, "nohwcursor", 10)) {
+			printk("nohwcursor\n");
+			fb_infos[0].nohwcursor = 1;
+		}
+	}
+
+	printk("au1200fb: Panel %d %s\n", panel_index,
+		panels[panel_index].name);
+}
+
+
+
+#ifdef MODULE
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Au1200 LCD framebuffer driver");
+
+void au1200fb_cleanup(struct fb_info *info)
+{
+	unregister_framebuffer(info);
+}
+
+module_init(au1200fb_init);
+module_exit(au1200fb_cleanup);
+#endif /* MODULE */
+
+
diff -Nur linux-2.4.30/drivers/video/au1200fb.h linux-2.4.30-linux-mips.org/drivers/video/au1200fb.h
--- linux-2.4.30/drivers/video/au1200fb.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/video/au1200fb.h	2005-05-12 17:57:34.000000000 +0200
@@ -0,0 +1,288 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *	Hardware definitions for the Au1200 LCD controller
+ *
+ * Copyright 2004 AMD
+ * Author:	AMD
+ *
+ *  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.
+ */
+
+#ifndef _AU1200LCD_H
+#define _AU1200LCD_H
+
+/********************************************************************/
+#define AU1200_LCD_ADDR		0xB5000000
+
+#define uint8 unsigned char
+#define uint32 unsigned int
+
+typedef volatile struct
+{
+	uint32	reserved0;
+	uint32	screen;
+	uint32	backcolor;
+	uint32	horztiming;
+	uint32	verttiming;
+	uint32	clkcontrol;
+	uint32	pwmdiv;
+	uint32	pwmhi;
+	uint32	reserved1;
+	uint32	winenable;
+	uint32	colorkey;
+	uint32	colorkeymsk;
+	struct
+	{
+		uint32	cursorctrl;
+		uint32	cursorpos;
+		uint32	cursorcolor0;
+		uint32	cursorcolor1;
+		uint32	cursorcolor2;
+		uint32	cursorcolor3;
+	} hwc;
+	uint32	intstatus;
+	uint32	intenable;
+	uint32	outmask;
+	uint32	fifoctrl;
+	uint32	reserved2[(0x0100-0x0058)/4];
+	struct
+	{
+		uint32	winctrl0;
+		uint32	winctrl1;
+		uint32	winctrl2;
+		uint32	winbuf0;
+		uint32	winbuf1;
+		uint32	winbufctrl;
+		uint32	winreserved0;
+		uint32	winreserved1;
+	} window[4];
+
+	uint32	reserved3[(0x0400-0x0180)/4];
+
+	uint32	palette[(0x0800-0x0400)/4];
+
+	uint8	cursorpattern[256];
+
+} AU1200_LCD;
+
+/* lcd_screen */
+#define LCD_SCREEN_SEN		(1<<31)
+#define LCD_SCREEN_SX		(0x07FF<<19)
+#define LCD_SCREEN_SY		(0x07FF<< 8)
+#define LCD_SCREEN_SWP		(1<<7)
+#define LCD_SCREEN_SWD		(1<<6)
+#define LCD_SCREEN_ST		(7<<0)
+#define LCD_SCREEN_ST_TFT	(0<<0)
+#define LCD_SCREEN_SX_N(WIDTH)	((WIDTH-1)<<19)
+#define LCD_SCREEN_SY_N(HEIGHT)	((HEIGHT-1)<<8)
+#define LCD_SCREEN_ST_CSTN	(1<<0)
+#define LCD_SCREEN_ST_CDSTN	(2<<0)
+#define LCD_SCREEN_ST_M8STN	(3<<0)
+#define LCD_SCREEN_ST_M4STN	(4<<0)
+
+/* lcd_backcolor */
+#define LCD_BACKCOLOR_SBGR		(0xFF<<16)
+#define LCD_BACKCOLOR_SBGG		(0xFF<<8)
+#define LCD_BACKCOLOR_SBGB		(0xFF<<0)
+#define LCD_BACKCOLOR_SBGR_N(N)	((N)<<16)
+#define LCD_BACKCOLOR_SBGG_N(N)	((N)<<8)
+#define LCD_BACKCOLOR_SBGB_N(N)	((N)<<0)
+
+/* lcd_winenable */
+#define LCD_WINENABLE_WEN3		(1<<3)
+#define LCD_WINENABLE_WEN2		(1<<2)
+#define LCD_WINENABLE_WEN1		(1<<1)
+#define LCD_WINENABLE_WEN0		(1<<0)
+
+/* lcd_colorkey */
+#define LCD_COLORKEY_CKR		(0xFF<<16)
+#define LCD_COLORKEY_CKG		(0xFF<<8)
+#define LCD_COLORKEY_CKB		(0xFF<<0)
+#define LCD_COLORKEY_CKR_N(N)	((N)<<16)
+#define LCD_COLORKEY_CKG_N(N)	((N)<<8)
+#define LCD_COLORKEY_CKB_N(N)	((N)<<0)
+
+/* lcd_colorkeymsk */
+#define LCD_COLORKEYMSK_CKMR		(0xFF<<16)
+#define LCD_COLORKEYMSK_CKMG		(0xFF<<8)
+#define LCD_COLORKEYMSK_CKMB		(0xFF<<0)
+#define LCD_COLORKEYMSK_CKMR_N(N)	((N)<<16)
+#define LCD_COLORKEYMSK_CKMG_N(N)	((N)<<8)
+#define LCD_COLORKEYMSK_CKMB_N(N)	((N)<<0)
+
+/* lcd windows control 0 */
+#define LCD_WINCTRL0_OX		(0x07FF<<21)
+#define LCD_WINCTRL0_OY		(0x07FF<<10)
+#define LCD_WINCTRL0_A		(0x00FF<<2)
+#define LCD_WINCTRL0_AEN	(1<<1)
+#define LCD_WINCTRL0_OX_N(N) ((N)<<21)
+#define LCD_WINCTRL0_OY_N(N) ((N)<<10)
+#define LCD_WINCTRL0_A_N(N) ((N)<<2)
+
+/* lcd windows control 1 */
+#define LCD_WINCTRL1_PRI	(3<<30)
+#define LCD_WINCTRL1_PIPE	(1<<29)
+#define LCD_WINCTRL1_FRM	(0xF<<25)
+#define LCD_WINCTRL1_CCO	(1<<24)
+#define LCD_WINCTRL1_PO		(3<<22)
+#define LCD_WINCTRL1_SZX	(0x07FF<<11)
+#define LCD_WINCTRL1_SZY	(0x07FF<<0)
+#define LCD_WINCTRL1_FRM_1BPP	(0<<25)
+#define LCD_WINCTRL1_FRM_2BPP	(1<<25)
+#define LCD_WINCTRL1_FRM_4BPP	(2<<25)
+#define LCD_WINCTRL1_FRM_8BPP	(3<<25)
+#define LCD_WINCTRL1_FRM_12BPP	(4<<25)
+#define LCD_WINCTRL1_FRM_16BPP655	(5<<25)
+#define LCD_WINCTRL1_FRM_16BPP565	(6<<25)
+#define LCD_WINCTRL1_FRM_16BPP556	(7<<25)
+#define LCD_WINCTRL1_FRM_16BPPI1555	(8<<25)
+#define LCD_WINCTRL1_FRM_16BPPI5551	(9<<25)
+#define LCD_WINCTRL1_FRM_16BPPA1555	(10<<25)
+#define LCD_WINCTRL1_FRM_16BPPA5551	(11<<25)
+#define LCD_WINCTRL1_FRM_24BPP		(12<<25)
+#define LCD_WINCTRL1_FRM_32BPP		(13<<25)
+#define LCD_WINCTRL1_PRI_N(N)	((N)<<30)
+#define LCD_WINCTRL1_PO_00		(0<<22)
+#define LCD_WINCTRL1_PO_01		(1<<22)
+#define LCD_WINCTRL1_PO_10		(2<<22)
+#define LCD_WINCTRL1_PO_11		(3<<22)
+#define LCD_WINCTRL1_SZX_N(N)	((N-1)<<11)
+#define LCD_WINCTRL1_SZY_N(N)	((N-1)<<0)
+
+/* lcd windows control 2 */
+#define LCD_WINCTRL2_CKMODE		(3<<24)
+#define LCD_WINCTRL2_DBM		(1<<23)
+#define LCD_WINCTRL2_RAM		(3<<21)
+#define LCD_WINCTRL2_BX			(0x1FFF<<8)
+#define LCD_WINCTRL2_SCX		(0xF<<4)
+#define LCD_WINCTRL2_SCY		(0xF<<0)
+#define LCD_WINCTRL2_CKMODE_00		(0<<24)
+#define LCD_WINCTRL2_CKMODE_01		(1<<24)
+#define LCD_WINCTRL2_CKMODE_10		(2<<24)
+#define LCD_WINCTRL2_CKMODE_11		(3<<24)
+#define LCD_WINCTRL2_RAM_NONE		(0<<21)
+#define LCD_WINCTRL2_RAM_PALETTE	(1<<21)
+#define LCD_WINCTRL2_RAM_GAMMA		(2<<21)
+#define LCD_WINCTRL2_RAM_BUFFER		(3<<21)
+#define LCD_WINCTRL2_BX_N(N)	((N)<<8)
+#define LCD_WINCTRL2_SCX_1		(0<<4)
+#define LCD_WINCTRL2_SCX_2		(1<<4)
+#define LCD_WINCTRL2_SCX_4		(2<<4)
+#define LCD_WINCTRL2_SCY_1		(0<<0)
+#define LCD_WINCTRL2_SCY_2		(1<<0)
+#define LCD_WINCTRL2_SCY_4		(2<<0)
+
+/* lcd windows buffer control */
+#define LCD_WINBUFCTRL_DB		(1<<1)
+#define LCD_WINBUFCTRL_DBN		(1<<0)
+
+/* lcd_intstatus, lcd_intenable */
+#define LCD_INT_IFO				(0xF<<14)
+#define LCD_INT_IFU				(0xF<<10)
+#define LCD_INT_OFO				(1<<9)
+#define LCD_INT_OFU				(1<<8)
+#define LCD_INT_WAIT			(1<<3)
+#define LCD_INT_SD				(1<<2)
+#define LCD_INT_SA				(1<<1)
+#define LCD_INT_SS				(1<<0)
+
+/* lcd_horztiming */
+#define LCD_HORZTIMING_HND2		(0x1FF<<18)
+#define LCD_HORZTIMING_HND1		(0x1FF<<9)
+#define LCD_HORZTIMING_HPW		(0x1FF<<0)
+#define LCD_HORZTIMING_HND2_N(N)(((N)-1)<<18)
+#define LCD_HORZTIMING_HND1_N(N)(((N)-1)<<9)
+#define LCD_HORZTIMING_HPW_N(N)	(((N)-1)<<0)
+
+/* lcd_verttiming */
+#define LCD_VERTTIMING_VND2		(0x1FF<<18)
+#define LCD_VERTTIMING_VND1		(0x1FF<<9)
+#define LCD_VERTTIMING_VPW		(0x1FF<<0)
+#define LCD_VERTTIMING_VND2_N(N)(((N)-1)<<18)
+#define LCD_VERTTIMING_VND1_N(N)(((N)-1)<<9)
+#define LCD_VERTTIMING_VPW_N(N)	(((N)-1)<<0)
+
+/* lcd_clkcontrol */
+#define LCD_CLKCONTROL_EXT		(1<<22)
+#define LCD_CLKCONTROL_DELAY	(3<<20)
+#define LCD_CLKCONTROL_CDD		(1<<19)
+#define LCD_CLKCONTROL_IB		(1<<18)
+#define LCD_CLKCONTROL_IC		(1<<17)
+#define LCD_CLKCONTROL_IH		(1<<16)
+#define LCD_CLKCONTROL_IV		(1<<15)
+#define LCD_CLKCONTROL_BF		(0x1F<<10)
+#define LCD_CLKCONTROL_PCD		(0x3FF<<0)
+#define LCD_CLKCONTROL_BF_N(N)	(((N)-1)<<10)
+#define LCD_CLKCONTROL_PCD_N(N)	((N)<<0)
+
+/* lcd_pwmdiv */
+#define LCD_PWMDIV_EN			(1<<31)
+#define LCD_PWMDIV_PWMDIV		(0x1FFFF<<0)
+#define LCD_PWMDIV_PWMDIV_N(N)	((N)<<0)
+
+/* lcd_pwmhi */
+#define LCD_PWMHI_PWMHI1		(0xFFFF<<16)
+#define LCD_PWMHI_PWMHI0		(0xFFFF<<0)
+#define LCD_PWMHI_PWMHI1_N(N)	((N)<<16)
+#define LCD_PWMHI_PWMHI0_N(N)	((N)<<0)
+
+/* lcd_hwccon */
+#define LCD_HWCCON_EN			(1<<0)
+
+/* lcd_cursorpos */
+#define LCD_CURSORPOS_HWCXOFF		(0x1F<<27)
+#define LCD_CURSORPOS_HWCXPOS		(0x07FF<<16)
+#define LCD_CURSORPOS_HWCYOFF		(0x1F<<11)
+#define LCD_CURSORPOS_HWCYPOS		(0x07FF<<0)
+#define LCD_CURSORPOS_HWCXOFF_N(N)	((N)<<27)
+#define LCD_CURSORPOS_HWCXPOS_N(N)	((N)<<16)
+#define LCD_CURSORPOS_HWCYOFF_N(N)	((N)<<11)
+#define LCD_CURSORPOS_HWCYPOS_N(N)	((N)<<0)
+
+/* lcd_cursorcolor */
+#define LCD_CURSORCOLOR_HWCA		(0xFF<<24)
+#define LCD_CURSORCOLOR_HWCR		(0xFF<<16)
+#define LCD_CURSORCOLOR_HWCG		(0xFF<<8)
+#define LCD_CURSORCOLOR_HWCB		(0xFF<<0)
+#define LCD_CURSORCOLOR_HWCA_N(N)	((N)<<24)
+#define LCD_CURSORCOLOR_HWCR_N(N)	((N)<<16)
+#define LCD_CURSORCOLOR_HWCG_N(N)	((N)<<8)
+#define LCD_CURSORCOLOR_HWCB_N(N)	((N)<<0)
+
+/* lcd_fifoctrl */
+#define LCD_FIFOCTRL_F3IF		(1<<29)
+#define LCD_FIFOCTRL_F3REQ		(0x1F<<24)
+#define LCD_FIFOCTRL_F2IF		(1<<29)
+#define LCD_FIFOCTRL_F2REQ		(0x1F<<16)
+#define LCD_FIFOCTRL_F1IF		(1<<29)
+#define LCD_FIFOCTRL_F1REQ		(0x1F<<8)
+#define LCD_FIFOCTRL_F0IF		(1<<29)
+#define LCD_FIFOCTRL_F0REQ		(0x1F<<0)
+#define LCD_FIFOCTRL_F3REQ_N(N)	((N-1)<<24)
+#define LCD_FIFOCTRL_F2REQ_N(N)	((N-1)<<16)
+#define LCD_FIFOCTRL_F1REQ_N(N)	((N-1)<<8)
+#define LCD_FIFOCTRL_F0REQ_N(N)	((N-1)<<0)
+
+/* lcd_outmask */
+#define LCD_OUTMASK_MASK		(0x00FFFFFF)
+
+/********************************************************************/
+#endif /* _AU1200LCD_H */
diff -Nur linux-2.4.30/drivers/video/Config.in linux-2.4.30-linux-mips.org/drivers/video/Config.in
--- linux-2.4.30/drivers/video/Config.in	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/video/Config.in	2005-05-12 18:01:30.000000000 +0200
@@ -87,8 +87,8 @@
    if [ "$CONFIG_HP300" = "y" ]; then
       define_bool CONFIG_FB_HP300 y
    fi
-   if [ "$ARCH" = "alpha" ]; then
-      tristate '  TGA framebuffer support' CONFIG_FB_TGA
+   if [ "$ARCH" = "alpha" -o "$CONFIG_TC" = "y" ]; then
+      tristate '  TGA/SFB+ framebuffer support' CONFIG_FB_TGA
    fi
    if [ "$CONFIG_X86" = "y" ]; then
       bool '  VESA VGA graphics console' CONFIG_FB_VESA
@@ -121,6 +121,17 @@
          hex '    Framebuffer Base Address' CONFIG_E1355_FB_BASE a8200000
       fi
    fi
+   if [ "$CONFIG_SOC_AU1100" = "y" ]; then
+      bool '  Au1100 LCD Driver' CONFIG_FB_AU1100
+   fi
+   
+   if [ "$CONFIG_SOC_AU1200" = "y" ]; then
+      bool '  Au1200 LCD Driver' CONFIG_FB_AU1200
+      if [ "$CONFIG_FB_AU1200" = "y" ]; then
+      int  '    Number of planes (1 to 4)' CONFIG_FB_AU1200_DEVS 1
+      fi
+   fi
+   
    if [ "$CONFIG_EXPERIMENTAL" = "y" ]; then
       if [ "$CONFIG_PCI" != "n" ]; then
 	 tristate '  Matrox acceleration (EXPERIMENTAL)' CONFIG_FB_MATROX
@@ -159,6 +170,7 @@
 	    bool '    SIS 300 series support' CONFIG_FB_SIS_300
 	    bool '    SIS 315/330 series support' CONFIG_FB_SIS_315
 	 fi
+	 bool '  SiliconMotion 501 Driver' CONFIG_FB_SMI501
 	 tristate '  NeoMagic display support (EXPERIMENTAL)' CONFIG_FB_NEOMAGIC
 	 tristate '  3Dfx Banshee/Voodoo3 display support (EXPERIMENTAL)' CONFIG_FB_3DFX
 	 tristate '  3Dfx Voodoo Graphics (sst1) support (EXPERIMENTAL)' CONFIG_FB_VOODOO1
@@ -178,9 +190,6 @@
 	     bool '    Use CRT on Pb1100 ' CONFIG_PB1500_CRT
 	     bool '    Use TFT Panel on Pb1100 ' CONFIG_PB1500_TFT
            fi
-           if [ "$CONFIG_SOC_AU1100" = "y" ]; then
-	     bool '    Au1100 LCD Driver' CONFIG_FB_AU1100
-           fi
 	 fi
       fi
    fi
diff -Nur linux-2.4.30/drivers/video/fbmem.c linux-2.4.30-linux-mips.org/drivers/video/fbmem.c
--- linux-2.4.30/drivers/video/fbmem.c	2005-01-19 15:10:09.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/video/fbmem.c	2005-05-12 18:01:30.000000000 +0200
@@ -139,12 +139,16 @@
 extern int e1356fb_setup(char*);
 extern int au1100fb_init(void);
 extern int au1100fb_setup(char*);
+extern int au1200fb_init(void);
+extern int au1200fb_setup(char*);
 extern int pvr2fb_init(void);
 extern int pvr2fb_setup(char*);
 extern int sstfb_init(void);
 extern int sstfb_setup(char*);
 extern int it8181fb_init(void);
 extern int it8181fb_setup(char*);
+extern int smi501fb_init(void);
+extern int smi501fb_setup(char*);
 
 static struct {
 	const char *name;
@@ -254,6 +258,9 @@
 #ifdef CONFIG_FB_VESA
 	{ "vesa", vesafb_init, vesafb_setup },
 #endif 
+#ifdef CONFIG_FB_SMI501
+	{ "smi501fb", smi501fb_init, smi501fb_setup },
+#endif
 
 	/*
 	 * Chipset specific drivers that don't use resource management (yet)
@@ -331,6 +338,9 @@
 #ifdef CONFIG_FB_AU1100
 	{ "au1100fb", au1100fb_init, au1100fb_setup },
 #endif 
+#ifdef CONFIG_FB_AU1200
+	{ "au1200fb", au1200fb_init, au1200fb_setup },
+#endif 
 #ifdef CONFIG_FB_IT8181
 	{ "it8181fb", it8181fb_init, it8181fb_setup },
 #endif
diff -Nur linux-2.4.30/drivers/video/ims332.h linux-2.4.30-linux-mips.org/drivers/video/ims332.h
--- linux-2.4.30/drivers/video/ims332.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/video/ims332.h	2005-05-12 17:57:34.000000000 +0200
@@ -0,0 +1,275 @@
+/*
+ *	linux/drivers/video/ims332.h
+ *
+ *	Copyright 2003  Thiemo Seufer <seufer@csv.ica.uni-stuttgart.de>
+ *
+ *	This file is subject to the terms and conditions of the GNU General
+ *	Public License. See the file COPYING in the main directory of this
+ *	archive for more details.
+ */
+#include <linux/types.h>
+
+/*
+ * IMS332 16-bit wide, 128-bit aligned registers.
+ */
+struct _ims332_reg {
+	volatile u16 r;
+	u16 pad[7];
+};
+
+struct _ims332_regs {
+#define IMS332_BOOT_PLL_MUTLIPLIER	0x00001f
+#define IMS332_BOOT_CLOCK_SOURCE_SEL	0x000020
+#define IMS332_BOOT_ADDRESS_ALIGNMENT	0x000040
+#define IMS332_BOOT_WRITE_ZERO		0xffff80
+	struct _ims332_reg boot;
+	struct _ims332_reg pad0[0x020 - 0x000];
+	struct _ims332_reg half_sync;
+	struct _ims332_reg back_porch;
+	struct _ims332_reg display;
+	struct _ims332_reg short_display;
+	struct _ims332_reg broad_pulse;
+	struct _ims332_reg vsync;
+	struct _ims332_reg vpre_equalise;
+	struct _ims332_reg vpost_equalise;
+	struct _ims332_reg vblank;
+	struct _ims332_reg vdisplay;
+	struct _ims332_reg line_time;
+	struct _ims332_reg line_start;
+	struct _ims332_reg mem_init;
+	struct _ims332_reg transfer_delay;
+	struct _ims332_reg pad1[0x03f - 0x02e];
+	struct _ims332_reg pixel_address_mask;
+	struct _ims332_reg pad2[0x05f - 0x040];
+
+#define IMS332_CTRL_A_BOOT_ENABLE_VTG		0x000001
+#define IMS332_CTRL_A_SCREEN_FORMAT		0x000002
+#define IMS332_CTRL_A_INTERLACED_STANDARD	0x000004
+#define IMS332_CTRL_A_OPERATING_MODE		0x000008
+#define IMS332_CTRL_A_FRAME_FLYBACK_PATTERN	0x000010
+#define IMS332_CTRL_A_DIGITAL_SYNC_FORMAT	0x000020
+#define IMS332_CTRL_A_ANALOGUE_VIDEO_FORMAT	0x000040
+#define IMS332_CTRL_A_BLANK_LEVEL		0x000080
+#define IMS332_CTRL_A_BLANK_IO			0x000100
+#define IMS332_CTRL_A_BLANK_FUNCTION_SWITCH	0x000200
+#define IMS332_CTRL_A_FORCE_BLANKING		0x000400
+#define IMS332_CTRL_A_TURN_OFF_BLANKING		0x000800
+#define IMS332_CTRL_A_VRAM_ADDRESS_INCREMENT	0x003000
+#define IMS332_CTRL_A_TURN_OFF_DMA		0x004000
+#define IMS332_CTRL_A_SYNC_DELAY		0x038000
+#define IMS332_CTRL_A_PIXEL_PORT_INTERLEAVING	0x040000
+#define IMS332_CTRL_A_DELAYED_SAMPLING		0x080000
+#define IMS332_CTRL_A_BITS_PER_PIXEL		0x700000
+#define IMS332_CTRL_A_CURSOR_DISABLE		0x800000
+	struct _ims332_reg config_control_a;
+	struct _ims332_reg pad3[0x06f - 0x060];
+
+#define IMS332_CTRL_B_WRITE_ZERO	0xffffff
+	struct _ims332_reg config_control_b;
+	struct _ims332_reg pad4[0x07f - 0x070];
+	struct _ims332_reg screen_top;
+	struct _ims332_reg pad5[0x0a0 - 0x080];
+	/* cursor color palette, 3 entries, reg no. 0xa1 - 0xa3 */
+	struct _ims332_reg cursor_color_palette0;
+	struct _ims332_reg cursor_color_palette1;
+	struct _ims332_reg cursor_color_palette2;
+	struct _ims332_reg pad6[0x0bf - 0x0a3];
+	struct _ims332_reg rgb_frame_checksum0;
+	struct _ims332_reg rgb_frame_checksum1;
+	struct _ims332_reg rgb_frame_checksum2;
+	struct _ims332_reg pad7[0x0c6 - 0x0c2];
+	struct _ims332_reg cursor_start;
+	struct _ims332_reg pad8[0x0ff - 0x0c7];
+	/* color palette, 256 entries of form 0x00BBGGRR, reg no. 0x100 - 0x1ff */
+	struct _ims332_reg color_palette[0x1ff - 0x0ff];
+	/* hardware cursor bitmap, reg no. 0x200 - 0x3ff */
+	struct _ims332_reg cursor_ram[0x3ff - 0x1ff];
+};
+
+/*
+ * In the functions below we use some weird looking helper variables to
+ * access most members of this struct, otherwise the compiler splits
+ * the read/write in two byte accesses.
+ */
+struct ims332_regs {
+	struct _ims332_regs rw;
+	char pad0[0x80000 - sizeof (struct _ims332_regs)];
+	struct _ims332_regs r;
+	char pad1[0xa0000 - (sizeof (struct _ims332_regs) + 0x80000)];
+	struct _ims332_regs w;
+} __attribute__((packed));
+
+static inline void ims332_control_reg_bits(struct ims332_regs *regs, u32 mask,
+					   u32 val)
+{
+	volatile u16 *ctr = &(regs->r.config_control_a.r);
+	volatile u16 *ctw = &(regs->w.config_control_a.r);
+	u32 ctrl;
+
+	mb();
+	ctrl = *ctr;
+	rmb();
+	ctrl |= ((regs->rw.boot.r << 8) & 0x00ff0000);
+	ctrl |= val & mask;
+	ctrl &= ~(~val & mask);
+	wmb();
+	regs->rw.boot.r = (ctrl >> 8) & 0xff00;
+	wmb();
+	*ctw = ctrl & 0xffff;
+}
+
+/* FIXME: This is maxinefb specific. */
+static inline void ims332_bootstrap(struct ims332_regs *regs)
+{
+	volatile u16 *ctw = &(regs->w.config_control_a.r);
+	u32 ctrl = IMS332_CTRL_A_BOOT_ENABLE_VTG | IMS332_CTRL_A_TURN_OFF_DMA;
+
+	/* bootstrap sequence */
+	mb();
+	regs->rw.boot.r = 0;
+	wmb();
+	*ctw = 0;
+
+	/* init control A register */
+	wmb();
+	regs->rw.boot.r = (ctrl >> 8) & 0xff00;
+	wmb();
+	*ctw = ctrl & 0xffff;
+}
+
+static inline void ims332_blank_screen(struct ims332_regs *regs, int blank)
+{
+	ims332_control_reg_bits(regs, IMS332_CTRL_A_FORCE_BLANKING,
+				blank ? IMS332_CTRL_A_FORCE_BLANKING : 0);
+}
+
+static inline void ims332_set_color_depth(struct ims332_regs *regs, u32 depth)
+{
+	u32 dp;
+	u32 mask = (IMS332_CTRL_A_PIXEL_PORT_INTERLEAVING
+		    | IMS332_CTRL_A_DELAYED_SAMPLING
+		    | IMS332_CTRL_A_BITS_PER_PIXEL);
+
+	switch (depth) {
+	case 1: dp = 0 << 20; break;
+	case 2: dp = 1 << 20; break;
+	case 4: dp = 2 << 20; break;
+	case 8: dp = 3 << 20; break;
+	case 15: dp = (4 << 20) | IMS332_CTRL_A_PIXEL_PORT_INTERLEAVING; break;
+	case 16: dp = (5 << 20) | IMS332_CTRL_A_PIXEL_PORT_INTERLEAVING; break;
+	default: return;
+	}
+	ims332_control_reg_bits(regs, mask, dp);
+
+	if (depth <= 8) {
+		volatile u16 *pmask = &(regs->w.pixel_address_mask.r);
+		u32 dm = (1 << depth) - 1;
+
+		wmb();
+		regs->rw.boot.r = dm << 8;
+		wmb();
+		*pmask = dm << 8 | dm;
+	}
+}
+
+static inline void ims332_set_screen_top(struct ims332_regs *regs, u16 top)
+{
+	volatile u16 *st = &(regs->w.screen_top.r);
+
+	mb();
+	*st = top & 0xffff;
+}
+
+static inline void ims332_enable_cursor(struct ims332_regs *regs, int on)
+{
+	ims332_control_reg_bits(regs, IMS332_CTRL_A_CURSOR_DISABLE,
+				on ? 0 : IMS332_CTRL_A_CURSOR_DISABLE);
+}
+
+static inline void ims332_position_cursor(struct ims332_regs *regs,
+					  u16 x, u16 y)
+{
+	volatile u16 *cp = &(regs->w.cursor_start.r);
+	u32 val = ((x & 0xfff) << 12) | (y & 0xfff);
+
+	if (x > 2303 || y > 2303)
+		return;
+
+	mb();
+	regs->rw.boot.r = (val >> 8) & 0xff00;
+	wmb();
+	*cp = val & 0xffff;
+}
+
+static inline void ims332_set_font(struct ims332_regs *regs, u8 fgc,
+				   u16 width, u16 height)
+{
+	volatile u16 *cp0 = &(regs->w.cursor_color_palette0.r);
+	int i;
+
+	mb();
+	for (i = 0; i < 0x200; i++) {
+		volatile u16 *cram = &(regs->w.cursor_ram[i].r);
+
+		if (height << 6 <= i << 3)
+			*cram = 0x0000;
+		else if (width <= i % 8 << 3)
+			*cram = 0x0000;
+		else if (((width >> 3) & 0xffff) > i % 8)
+			*cram = 0x5555;
+		else
+			*cram = 0x5555 & ~(0xffff << (width % 8 << 1));
+		wmb();
+	}
+	regs->rw.boot.r = fgc << 8;
+	wmb();
+	*cp0 = fgc << 8 | fgc;
+}
+
+static inline void ims332_read_cmap(struct ims332_regs *regs, u8 reg,
+				    u8* red, u8* green, u8* blue)
+{
+	volatile u16 *rptr = &(regs->r.color_palette[reg].r);
+	u16 val;
+
+	mb();
+	val = *rptr;
+	*red = val & 0xff;
+	*green = (val >> 8) & 0xff;
+	rmb();
+	*blue = (regs->rw.boot.r >> 8) & 0xff;
+}
+
+static inline void ims332_write_cmap(struct ims332_regs *regs, u8 reg,
+				     u8 red, u8 green, u8 blue)
+{
+	volatile u16 *wptr = &(regs->w.color_palette[reg].r);
+
+	mb();
+	regs->rw.boot.r = blue << 8;
+	wmb();
+	*wptr = (green << 8) + red;
+}
+
+static inline void ims332_dump_regs(struct ims332_regs *regs)
+{
+	int i;
+
+	printk(__FUNCTION__);
+	ims332_control_reg_bits(regs, IMS332_CTRL_A_BOOT_ENABLE_VTG, 0);
+	for (i = 0; i < 0x100; i++) {
+		volatile u16 *cpad = (u16 *)((char *)(&regs->r) + sizeof(struct _ims332_reg) * i);
+		u32 val;
+
+		val = *cpad;
+		rmb();
+		val |= regs->rw.boot.r << 8;
+		rmb();
+		if (! (i % 8))
+			printk("\n%02x:", i);
+		printk(" %06x", val);
+	}
+	ims332_control_reg_bits(regs, IMS332_CTRL_A_BOOT_ENABLE_VTG,
+				IMS332_CTRL_A_BOOT_ENABLE_VTG);
+	printk("\n");
+}
diff -Nur linux-2.4.30/drivers/video/Makefile linux-2.4.30-linux-mips.org/drivers/video/Makefile
--- linux-2.4.30/drivers/video/Makefile	2004-02-18 14:36:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/video/Makefile	2005-05-12 18:01:30.000000000 +0200
@@ -87,7 +87,9 @@
 obj-$(CONFIG_FB_MAXINE)           += maxinefb.o
 obj-$(CONFIG_FB_TX3912)           += tx3912fb.o
 obj-$(CONFIG_FB_AU1100)		  += au1100fb.o fbgen.o
+obj-$(CONFIG_FB_AU1200)		  += au1200fb.o fbgen.o
 obj-$(CONFIG_FB_IT8181)		  += it8181fb.o fbgen.o
+obj-$(CONFIG_FB_SMI501)           += smi501fb.o fbgen.o
 
 subdir-$(CONFIG_STI_CONSOLE)      += sti
 ifeq ($(CONFIG_STI_CONSOLE),y)
diff -Nur linux-2.4.30/drivers/video/maxinefb.h linux-2.4.30-linux-mips.org/drivers/video/maxinefb.h
--- linux-2.4.30/drivers/video/maxinefb.h	2003-08-25 13:44:42.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/video/maxinefb.h	1970-01-01 01:00:00.000000000 +0100
@@ -1,38 +0,0 @@
-/*
- *      linux/drivers/video/maxinefb.h
- *
- *      DECstation 5000/xx onboard framebuffer support, Copyright (C) 1999 by
- *      Michael Engel <engel@unix-ag.org> and Karsten Merker <merker@guug.de>
- *      This file is subject to the terms and conditions of the GNU General
- *      Public License.  See the file COPYING in the main directory of this
- *      archive for more details.
- */
-
-#include <asm/addrspace.h>
-
-/*
- * IMS332 video controller register base address
- */
-#define MAXINEFB_IMS332_ADDRESS		KSEG1ADDR(0x1c140000)
-
-/*
- * Begin of DECstation 5000/xx onboard framebuffer memory, default resolution
- * is 1024x768x8
- */
-#define DS5000_xx_ONBOARD_FBMEM_START	KSEG1ADDR(0x0a000000)
-
-/*
- *      The IMS 332 video controller used in the DECstation 5000/xx series
- *      uses 32 bits wide registers; the following defines declare the
- *      register numbers, to get the real offset, these have to be multiplied
- *      by four.
- */
-
-#define IMS332_REG_CURSOR_RAM           0x200	/* hardware cursor bitmap */
-
-/*
- * The color palette entries have the form 0x00BBGGRR
- */
-#define IMS332_REG_COLOR_PALETTE        0x100	/* color palette, 256 entries */
-#define IMS332_REG_CURSOR_COLOR_PALETTE	0x0a1	/* cursor color palette, */
-						/* 3 entries             */
diff -Nur linux-2.4.30/drivers/video/newport_con.c linux-2.4.30-linux-mips.org/drivers/video/newport_con.c
--- linux-2.4.30/drivers/video/newport_con.c	2003-08-25 13:44:42.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/video/newport_con.c	2005-05-12 17:57:34.000000000 +0200
@@ -22,6 +22,7 @@
 #include <linux/module.h>
 #include <linux/slab.h>
 
+#include <asm/io.h>
 #include <asm/uaccess.h>
 #include <asm/system.h>
 #include <asm/page.h>
@@ -77,7 +78,7 @@
 static inline void newport_render_background(int xstart, int ystart,
 					     int xend, int yend, int ci)
 {
-	newport_wait();
+	newport_wait(npregs);
 	npregs->set.wrmask = 0xffffffff;
 	npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
 				 NPORT_DMODE0_DOSETUP | NPORT_DMODE0_STOPX
@@ -94,7 +95,7 @@
 	unsigned short i;
 
 	for (i = 0; i < 16; i++) {
-		newport_bfwait();
+		newport_bfwait(npregs);
 		newport_cmap_setaddr(npregs, color_table[i]);
 		newport_cmap_setrgb(npregs,
 				    default_red[i],
@@ -107,7 +108,7 @@
 	unsigned long i;
 
 	for (i = 0; i < LINUX_LOGO_COLORS; i++) {
-		newport_bfwait();
+		newport_bfwait(npregs);
 		newport_cmap_setaddr(npregs, i + 0x20);
 		newport_cmap_setrgb(npregs,
 				    linux_logo_red[i],
@@ -115,13 +116,13 @@
 				    linux_logo_blue[i]);
 	}
 
-	newport_wait();
+	newport_wait(npregs);
 	npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
 				 NPORT_DMODE0_CHOST);
 
 	npregs->set.xystarti = ((newport_xsize - LOGO_W) << 16) | (0);
 	npregs->set.xyendi = ((newport_xsize - 1) << 16);
-	newport_wait();
+	newport_wait(npregs);
 
 	for (i = 0; i < LOGO_W * LOGO_H; i++)
 		npregs->go.hostrw0 = linux_logo[i] << 24;
@@ -133,7 +134,7 @@
 	if (logo_active)
 		return;
 
-	newport_wait();
+	newport_wait(npregs);
 	npregs->set.wrmask = 0xffffffff;
 	npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
 				 NPORT_DMODE0_DOSETUP | NPORT_DMODE0_STOPX
@@ -155,7 +156,7 @@
 	unsigned short treg;
 	int i;
 
-	newport_wait();
+	newport_wait(npregs);
 	treg = newport_vc2_get(npregs, VC2_IREG_CONTROL);
 	newport_vc2_set(npregs, VC2_IREG_CONTROL,
 			(treg | VC2_CTRL_EVIDEO));
@@ -165,7 +166,7 @@
 	npregs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_RAM |
 			       NPORT_DMODE_W2 | VC2_PROTOCOL);
 	for (i = 0; i < 128; i++) {
-		newport_bfwait();
+		newport_bfwait(npregs);
 		if (i == 92 || i == 94)
 			npregs->set.dcbdata0.byshort.s1 = 0xff00;
 		else
@@ -205,7 +206,7 @@
 	npregs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_RAM |
 			       NPORT_DMODE_W2 | VC2_PROTOCOL);
 	for (i = 0; i < 128; i++) {
-		newport_bfwait();
+		newport_bfwait(npregs);
 		linetable[i] = npregs->set.dcbdata0.byshort.s1;
 	}
 
@@ -216,12 +217,12 @@
 		npregs->set.dcbmode = (NPORT_DMODE_AVC2 | VC2_REGADDR_RAM |
 				       NPORT_DMODE_W2 | VC2_PROTOCOL);
 		do {
-			newport_bfwait();
+			newport_bfwait(npregs);
 			treg = npregs->set.dcbdata0.byshort.s1;
 			if ((treg & 1) == 0)
 				cols += (treg >> 7) & 0xfe;
 			if ((treg & 0x80) == 0) {
-				newport_bfwait();
+				newport_bfwait(npregs);
 				treg = npregs->set.dcbdata0.byshort.s1;
 			}
 		} while ((treg & 0x8000) == 0);
@@ -291,16 +292,16 @@
 
 	if (!sgi_gfxaddr)
 		return NULL;
-	npregs = (struct newport_regs *) (KSEG1 + sgi_gfxaddr);
+	npregs = (struct newport_regs *)	/* ioremap cannot fail */
+		 ioremap(sgi_gfxaddr, sizeof(struct newport_regs));
 	npregs->cset.config = NPORT_CFG_GD0;
 
-	if (newport_wait()) {
-		return NULL;
-	}
+	if (newport_wait(npregs))
+		goto out_unmap;
 
 	npregs->set.xstarti = TESTVAL;
 	if (npregs->set._xstart.word != XSTI_TO_FXSTART(TESTVAL))
-		return NULL;
+		goto out_unmap;
 
 	for (i = 0; i < MAX_NR_CONSOLES; i++)
 		font_data[i] = FONT_DATA;
@@ -310,6 +311,10 @@
 	newport_get_screensize();
 
 	return "SGI Newport";
+
+out_unmap:
+	iounmap((void *)npregs);
+	return NULL;
 }
 
 static void newport_init(struct vc_data *vc, int init)
@@ -363,7 +368,7 @@
 				  (charattr & 0xf0) >> 4);
 
 	/* Set the color and drawing mode. */
-	newport_wait();
+	newport_wait(npregs);
 	npregs->set.colori = charattr & 0xf;
 	npregs->set.drawmode0 = (NPORT_DMODE0_DRAW | NPORT_DMODE0_BLOCK |
 				 NPORT_DMODE0_STOPX | NPORT_DMODE0_ZPENAB |
@@ -372,7 +377,7 @@
 	/* Set coordinates for bitmap operation. */
 	npregs->set.xystarti = (xpos << 16) | ((ypos + topscan) & 0x3ff);
 	npregs->set.xyendi = ((xpos + 7) << 16);
-	newport_wait();
+	newport_wait(npregs);
 
 	/* Go, baby, go... */
 	RENDER(npregs, p);
@@ -396,7 +401,7 @@
 					  xpos + ((count - 1) << 3), ypos,
 					  (charattr & 0xf0) >> 4);
 
-	newport_wait();
+	newport_wait(npregs);
 
 	/* Set the color and drawing mode. */
 	npregs->set.colori = charattr & 0xf;
@@ -407,7 +412,7 @@
 	for (i = 0; i < count; i++, xpos += 8) {
 		p = &font_data[vc->vc_num][(scr_readw(s++) & 0xff) << 4];
 
-		newport_wait();
+		newport_wait(npregs);
 
 		/* Set coordinates for bitmap operation. */
 		npregs->set.xystarti =
@@ -689,7 +694,7 @@
 		xe = xs;
 		xs = tmp;
 	}
-	newport_wait();
+	newport_wait(npregs);
 	npregs->set.drawmode0 = (NPORT_DMODE0_S2S | NPORT_DMODE0_BLOCK |
 				 NPORT_DMODE0_DOSETUP | NPORT_DMODE0_STOPX
 				 | NPORT_DMODE0_STOPY);
@@ -706,35 +711,35 @@
 #define DUMMY (void *) newport_dummy
 
 const struct consw newport_con = {
-    con_startup:	newport_startup,
-    con_init:		newport_init,
-    con_deinit:		newport_deinit,
-    con_clear:		newport_clear,
-    con_putc:		newport_putc,
-    con_putcs:		newport_putcs,
-    con_cursor:		newport_cursor,
-    con_scroll:		newport_scroll,
-    con_bmove:		newport_bmove,
-    con_switch:		newport_switch,
-    con_blank:		newport_blank,
-    con_font_op:	newport_font_op,
-    con_set_palette:	newport_set_palette,
-    con_scrolldelta:	newport_scrolldelta,
-    con_set_origin:	DUMMY,
-    con_save_screen:	DUMMY
+	.con_startup	  = newport_startup,
+	.con_init	  = newport_init,
+	.con_deinit	  = newport_deinit,
+	.con_clear	  = newport_clear,
+	.con_putc	  = newport_putc,
+	.con_putcs	  = newport_putcs,
+	.con_cursor	  = newport_cursor,
+	.con_scroll	  = newport_scroll,
+	.con_bmove 	  = newport_bmove,
+	.con_switch	  = newport_switch,
+	.con_blank	  = newport_blank,
+	.con_font_op	  = newport_font_op,
+	.con_set_palette  = newport_set_palette,
+	.con_scrolldelta  = newport_scrolldelta,
+	.con_set_origin	  = DUMMY,
+	.con_save_screen  = DUMMY
 };
 
 #ifdef MODULE
 static int __init newport_console_init(void)
 {
 	take_over_console(&newport_con, 0, MAX_NR_CONSOLES - 1, 1);
-
 	return 0;
 }
 
 static void __exit newport_console_exit(void)
 {
 	give_up_console(&newport_con);
+	iounmap((void *)npregs);
 }
 
 module_init(newport_console_init);
diff -Nur linux-2.4.30/drivers/video/smi501fb.c linux-2.4.30-linux-mips.org/drivers/video/smi501fb.c
--- linux-2.4.30/drivers/video/smi501fb.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/video/smi501fb.c	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,827 @@
+/***************************************************************************
+        smifb.c  -  Silicon Motion, Inc. LynxEM+ frame buffer device
+                             -------------------
+    begin                : Thu Aug 9 2001
+    copyright            : (C) 2001 by Szu-Tao Huang
+    email                : johuang@siliconmotion.com
+    
+    Updated to SM501 by Eric.Devolder@amd.com and dan@embeddededge.com
+    for the AMD Mirage Portable Tablet.  20 Oct 2003
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/tty.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/fb.h>
+#include <linux/pci.h>
+#include <linux/init.h>
+
+#include <asm/io.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/system.h>
+#include <asm/uaccess.h>
+
+#include <video/fbcon.h>
+#include <video/fbcon-cfb8.h>
+#include <video/fbcon-cfb16.h>
+#include <video/fbcon-cfb24.h>
+
+static char *SMIRegs;		// point to virtual Memory Map IO starting address
+static char *SMILFB;		// point to virtual video memory starting address
+static struct smifb_info fb_info;
+static struct display disp;
+static struct smifb_par current_par;	// used to record hardware information
+
+#include "smi501fb.h"
+
+static int initdone = 0;
+static int crt_out = 1;
+
+static int
+smifb_ioctl(struct inode *inode, struct file *file, u_int cmd,
+              u_long arg, int con, struct fb_info *info)
+{
+	unsigned char	bcmd, status;
+	unsigned int	bval;
+	struct	smireg_op	sreg;
+	int	retval;
+
+	retval = 0;
+	if (cmd == 1) {
+		/* Send command to battery charger.
+		*/
+		bcmd = (unsigned char)arg;
+
+		/* 0x22 is the slave address.
+		*/
+		smi_mmiowb(0x22, I2C_SLAVE_ADDRESS);
+		wmb();
+		smi_mmiowb(bcmd, I2C_DATA);
+		wmb();
+		smi_mmiowb(0, I2C_BYTE_COUNT);	/* byte count - 1 */
+		wmb();
+		smi_mmiowb(0x05, I2C_CONTROL);	/* enable, 100kHz, start */
+		wmb();
+		do {
+			status = smi_mmiorb(I2C_STATUS_RESET);
+			wmb();
+		} while (status == 0);
+		smi_mmiowb(0x00, I2C_CONTROL);	/* enable, 100kHz, stop */
+		if (status & 0x40)
+			smi_mmiowb(0x00, I2C_STATUS_RESET);
+	}
+	else if (cmd == 2) {
+		/* Read results of command.
+		*/
+		smi_mmiowb(0x23, I2C_SLAVE_ADDRESS);
+		wmb();
+		smi_mmiowb(1, I2C_BYTE_COUNT);	/* byte count - 1 */
+		wmb();
+		smi_mmiowb(0x05, I2C_CONTROL);	/* enable, 100kHz, start */
+		wmb();
+		do {
+			status = smi_mmiorb(I2C_STATUS_RESET);
+			wmb();
+		} while (status == 0);
+		bval = smi_mmiorl(I2C_DATA);
+		wmb();
+		smi_mmiowb(0x00, I2C_CONTROL);	/* enable, 100kHz, stop */
+		wmb();
+		if (copy_to_user((char *)arg, &bval, 4))
+			retval = -EFAULT;
+		if (status & 0x40)
+			smi_mmiowb(0x00, I2C_STATUS_RESET);
+	}
+	else if (cmd == 3) {	/* Debug test */
+		bval = smi_mmiorl(GPIO_DATA_HI);
+		bval ^= 0x0000c000;
+		smi_mmiowl(bval, GPIO_DATA_HI);
+
+	}
+	else if (cmd == 4) {	/* Register operation */
+		if (copy_from_user(&sreg, (void *)arg, sizeof(struct smireg_op))) {
+			return -EFAULT;
+		}
+
+		/* Just 32-bit access for now.
+		*/
+		if (sreg.sr_op == SMI_LOAD_REG) {
+			sreg.sr_val = smi_mmiorl(sreg.sr_reg);
+		}
+		else if (sreg.sr_op == SMI_STORE_REG) {
+			smi_mmiowl(sreg.sr_val, sreg.sr_reg);
+		}
+		else if (sreg.sr_op == SMI_AND_REG) {
+			bval = smi_mmiorl(sreg.sr_reg);
+			smi_mmiowl((bval & sreg.sr_val), sreg.sr_reg);
+			sreg.sr_val = bval;
+		}
+		else if (sreg.sr_op == SMI_OR_REG) {
+			bval = smi_mmiorl(sreg.sr_reg);
+			smi_mmiowl((bval | sreg.sr_val), sreg.sr_reg);
+			sreg.sr_val = bval;
+		}
+		else {
+			retval = -EINVAL;
+		}
+		if (copy_to_user((void *)arg, &sreg, sizeof(struct smireg_op))) {
+			retval = -EFAULT;
+		}
+	}
+	else {
+		retval = -EINVAL;
+	}
+
+	return retval;
+}
+
+static int
+smifb_mmap(struct fb_info *_fb,
+         struct file *file,
+         struct vm_area_struct *vma)
+{
+	unsigned int len;
+	unsigned long start=0, off;
+	struct smifb_info *fb = (struct smifb_info *)_fb;
+
+	if (vma->vm_pgoff > (~0UL >> PAGE_SHIFT)) {
+		return -EINVAL;
+	}
+
+	start = fb->fb_phys & PAGE_MASK;
+	len = PAGE_ALIGN((start & ~PAGE_MASK) + fb->fb_size);
+
+	off = vma->vm_pgoff << PAGE_SHIFT;
+
+	if ((vma->vm_end - vma->vm_start + off) > len) {
+		return -EINVAL;
+	}
+	off += start;
+	vma->vm_pgoff = off >> PAGE_SHIFT;
+
+	pgprot_val(vma->vm_page_prot) &= ~_CACHE_MASK;
+	pgprot_val(vma->vm_page_prot) |= _CACHE_UNCACHED;
+
+	/* This is an IO map - tell maydump to skip this VMA */
+	vma->vm_flags |= VM_IO;
+
+	if (io_remap_page_range(vma->vm_start, off,
+                vma->vm_end - vma->vm_start,
+                vma->vm_page_prot)) {
+		return -EAGAIN;
+	}
+
+	fb->mmaped = 1;
+	return 0;
+}
+
+static struct fb_ops smifb_ops = {
+	owner:			THIS_MODULE,
+	fb_get_fix:		fbgen_get_fix,
+	fb_get_var:		fbgen_get_var,
+	fb_set_var:		fbgen_set_var,
+	fb_get_cmap:		fbgen_get_cmap,
+	fb_set_cmap:		fbgen_set_cmap,
+	fb_pan_display:		fbgen_pan_display,
+	fb_ioctl:		smifb_ioctl,
+	fb_mmap:		smifb_mmap,
+};
+
+static void smi_nocursor(struct display *p, int mode, int xx, int yy){};
+
+static void
+smi_detect (void)
+{
+	/*
+	 *  This function should detect the current video mode settings
+	 *  and store it as the default video mode
+	 */
+
+	/*
+	 * Yeh, well, we're not going to change any settings so we're
+	 * always stuck with the default ...
+	 */
+}
+
+static int
+smi_encode_fix(struct fb_fix_screeninfo *fix,
+        const void *_par, struct fb_info_gen *_info)
+{
+	struct smifb_info *info = (struct smifb_info *) _info;
+	struct smifb_par *par = (struct smifb_par *) _par;
+	struct fb_var_screeninfo *var = &par->var;
+
+	memset(fix, 0, sizeof(struct fb_fix_screeninfo));
+
+	fix->smem_start = info->fb_phys;
+	fix->smem_len = info->fb_size;
+	fix->type = FB_TYPE_PACKED_PIXELS;
+	fix->type_aux = 0;
+	fix->visual = (var->bits_per_pixel == 8) ?
+            FB_VISUAL_PSEUDOCOLOR   : FB_VISUAL_TRUECOLOR;
+	fix->ywrapstep = 0;
+	fix->xpanstep = 1;
+	fix->ypanstep = 1;
+	fix->line_length = current_par.line_length;
+	return 0;
+}
+
+static void
+set_color_bitfields(struct fb_var_screeninfo *var)
+{
+	switch (var->bits_per_pixel) {
+	case 8:
+		var->red.offset = 0;
+		var->red.length = 8;
+		var->green.offset = 0;
+		var->green.length = 8;
+		var->blue.offset = 0;
+		var->blue.length = 8;
+		var->transp.offset = 0;
+		var->transp.length = 0;
+		break;
+	case 16:    /* RGB 565 */
+		var->red.offset = 11;
+		var->red.length = 5;
+		var->green.offset = 5;
+		var->green.length = 6;
+		var->blue.offset = 0;
+		var->blue.length = 5;
+		var->transp.offset = 0;
+		var->transp.length = 0;
+		break;
+	}
+
+	var->red.msb_right = 0;
+	var->green.msb_right = 0;
+	var->blue.msb_right = 0;
+	var->transp.msb_right = 0;
+}
+
+static int
+smi_decode_var(const struct fb_var_screeninfo *var,
+        void *_par, struct fb_info_gen *_info)
+{
+
+	struct smifb_par *par = (struct smifb_par *)_par;
+
+	/*
+	 * Don't allow setting any of these yet: xres and yres don't
+	 * make sense for LCD panels.
+	 */
+/*
+	if (var->xres != p_lcd->xres || var->yres != p_lcd->yres ||
+			var->xres != p_lcd->xres || var->yres != p_lcd->yres) {
+		return -EINVAL;
+	}
+	if(var->bits_per_pixel != p_lcd->bpp) {
+		return -EINVAL;
+	} */
+
+	memset(par, 0, sizeof(struct smifb_par));
+	par->var = *var;
+
+	/* FIXME */
+	switch (var->bits_per_pixel) {
+	case 8:
+		par->var.bits_per_pixel = 8;
+		break;
+	case 16:
+		par->var.bits_per_pixel = 16;
+		break;
+	default:
+		printk("color depth %d bpp not supported\n",
+						var->bits_per_pixel);
+		return -EINVAL;
+
+	}
+	set_color_bitfields(&par->var);
+	par->cmap_len = (par->var.bits_per_pixel == 8) ? 256 : 16;
+	return 0;
+}
+
+static int
+smi_encode_var(struct fb_var_screeninfo *var,
+        const void *par, struct fb_info_gen *_info)
+{
+	*var = ((struct smifb_par *)par)->var;
+	return 0;
+}
+
+static void
+smi_get_par(void *_par, struct fb_info_gen *_info)
+{
+	*(struct smifb_par *)_par = current_par;
+}
+
+static void
+smi_set_par(const void *par, struct fb_info_gen *info)
+{
+	/* nothing to do: we don't change any settings */
+}
+
+static int
+smi_getcolreg(unsigned regno, unsigned *red, unsigned *green,
+             unsigned *blue, unsigned *transp,
+             struct fb_info *info)
+{
+	struct smifb_info* i = (struct smifb_info*)info;
+
+	if (regno > 255)
+		return 1;
+
+	*red    = i->palette[regno].red;
+	*green  = i->palette[regno].green;
+	*blue   = i->palette[regno].blue;
+	*transp = 0;
+
+	return 0;
+}
+
+static int
+smi_setcolreg(unsigned regno, unsigned red, unsigned green,
+	unsigned blue, unsigned transp,
+	struct fb_info *info)
+{
+	struct smifb_info* i = (struct smifb_info *)info;
+
+	if (regno > 255)
+		return 1;
+
+	i->palette[regno].red    = red;
+	i->palette[regno].green  = green;
+	i->palette[regno].blue   = blue;
+
+	switch(current_par.var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		red >>= 10;
+		green >>= 10;
+		blue >>= 10;
+			// FIX!!! fill palette
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 16:
+		i->fbcon_cmap16[regno] =
+		    ((red & 0xf800) >> 0) |
+		    ((green & 0xfc00) >> 5) |
+		    ((blue & 0xf800) >> 11);
+		break;
+#endif
+	default:
+		break;
+	}
+
+	return 0;
+}
+
+static int
+smi_blank(int blank_mode, struct fb_info_gen *_info)
+{
+	switch (blank_mode) {
+	case VESA_NO_BLANKING:
+		/* turn on panel */
+		break;
+	case VESA_VSYNC_SUSPEND:
+	case VESA_HSYNC_SUSPEND:
+	case VESA_POWERDOWN:
+		/* turn off panel */
+		break;
+	default:
+		break;
+	}
+	return 0;
+}
+
+static void
+smi_set_disp(const void *unused, struct display *disp,
+             struct fb_info_gen *info)
+{
+	disp->screen_base = (char *)fb_info.fb_virt_start;
+
+	switch (disp->var.bits_per_pixel) {
+#ifdef FBCON_HAS_CFB8
+	case 8:
+		disp->dispsw = &fbcon_cfb8;
+		//fbcon_cfb8.cursor = smi_nocursor;
+		break;
+#endif
+#ifdef FBCON_HAS_CFB16
+	case 16:
+		disp->dispsw = &fbcon_cfb16;
+		disp->dispsw_data = fb_info.fbcon_cmap16;
+		//fbcon_cfb16.cursor = smi_nocursor;
+		break;
+#endif
+	default:
+		disp->dispsw = &fbcon_dummy;
+		disp->dispsw_data = NULL;
+		break;
+    }
+}
+
+static int
+smi_pan_display(const struct fb_var_screeninfo *var,
+               struct fb_info_gen *info)
+{
+	return 0;
+}
+
+static struct fbgen_hwswitch smi_switch = {
+	detect:		smi_detect,
+	encode_fix:	smi_encode_fix,
+	decode_var:	smi_decode_var,
+	encode_var:	smi_encode_var,
+	get_par:	smi_get_par,
+	set_par:	smi_set_par,
+	getcolreg:	smi_getcolreg,
+	setcolreg:	smi_setcolreg,
+	pan_display:	smi_pan_display,
+	blank:		smi_blank,
+	set_disp:	smi_set_disp
+};
+
+/* GPIO functions to set/clear bits and direction for the bit-bang
+ * I2C algorithm.  Someday, these will need to be protected with
+ * spinlocks/irq.  Right now, I2C is the only one to modify GPIOs.
+ */
+void
+smi501_set_gpio_hi_data(unsigned int val)
+{
+	unsigned int reg;
+
+	reg = smi_mmiorl(GPIO_DATA_HI);
+	wmb();
+	reg |= val;
+	smi_mmiowl(reg, GPIO_DATA_HI);
+	wmb();
+}
+
+void
+smi501_clr_gpio_hi_data(unsigned int val)
+{
+	unsigned int new;
+
+	new = smi_mmiorl(GPIO_DATA_HI);
+	wmb();
+	new &= ~val;
+	smi_mmiowl(new, GPIO_DATA_HI);
+	wmb();
+}
+
+unsigned int
+smi501_get_gpio_hi_data(unsigned int mask)
+{
+	unsigned int new;
+
+	new = smi_mmiorl(GPIO_DATA_HI);
+	wmb();
+
+	return (new & mask);
+}
+
+void
+smi501_set_gpio_hi_direction(unsigned int mask, unsigned int val)
+{
+	unsigned int new;
+
+	new = smi_mmiorl(GPIO_DATA_DIR_HI);
+	wmb();
+	new &= ~mask;
+	new |= val;
+	smi_mmiowl(new, GPIO_DATA_DIR_HI);
+	wmb();
+}
+
+
+/* This function still needs lots of work to generically support
+ * different output devices (CRT or LCD) and resolutions.
+ * Currently hard-coded for Mirage 1024x768 LCD panel.
+ */
+static void
+smi_setmode(struct smifb_info *sfb,struct smifb_par *hw)
+{
+	uint	reg;
+
+	if (initdone)
+		return;
+
+	initdone = 1;
+
+	/* Just blast in some control values based upon the chip
+	 * documentation.  We use the internal memory, I don't know
+	 * how to determine the amount available yet.
+	 */
+	smi_mmiowl(0x07F127C2, DRAM_CTRL);
+	smi_mmiowl(0x02000020, PANEL_HWC_ADDRESS);
+	smi_mmiowl(0x007FF800, PANEL_HWC_ADDRESS);
+	smi_mmiowl(0x00021827, POWER_MODE1_GATE);
+	smi_mmiowl(0x011A0A09, POWER_MODE1_CLOCK);
+	smi_mmiowl(0x00000001, POWER_MODE_CTRL);
+	smi_mmiowl(0x80000000, PANEL_FB_ADDRESS);
+	smi_mmiowl(0x08000800, PANEL_FB_WIDTH);
+	smi_mmiowl(0x04000000, PANEL_WINDOW_WIDTH);
+	smi_mmiowl(0x03000000, PANEL_WINDOW_HEIGHT);
+	smi_mmiowl(0x00000000, PANEL_PLANE_TL);
+	smi_mmiowl(0x02FF03FF, PANEL_PLANE_BR);
+	smi_mmiowl(0x05D003FF, PANEL_HORIZONTAL_TOTAL);
+	smi_mmiowl(0x00C80424, PANEL_HORIZONTAL_SYNC);
+	smi_mmiowl(0x032502FF, PANEL_VERTICAL_TOTAL);
+	smi_mmiowl(0x00060302, PANEL_VERTICAL_SYNC);
+	smi_mmiowl(0x00013905, PANEL_DISPLAY_CTRL);
+	smi_mmiowl(0x01013105, PANEL_DISPLAY_CTRL);
+	waitforvsync();
+	smi_mmiowl(0x03013905, PANEL_DISPLAY_CTRL);
+	waitforvsync();
+	smi_mmiowl(0x07013905, PANEL_DISPLAY_CTRL);
+	waitforvsync();
+	smi_mmiowl(0x0F013905, PANEL_DISPLAY_CTRL);
+	smi_mmiowl(0x0002187F, POWER_MODE1_GATE);
+	smi_mmiowl(0x01011801, POWER_MODE1_CLOCK);
+	smi_mmiowl(0x00000001, POWER_MODE_CTRL);
+
+	smi_mmiowl(0x80000000, PANEL_FB_ADDRESS);
+	smi_mmiowl(0x00000000, PANEL_PAN_CTRL);
+	smi_mmiowl(0x00000000, PANEL_COLOR_KEY);
+
+	if (crt_out) {
+		/* Just sent the panel out to the CRT for now.
+		*/
+		smi_mmiowl(0x80000000, CRT_FB_ADDRESS);
+		smi_mmiowl(0x08000800, CRT_FB_WIDTH);
+		smi_mmiowl(0x05D003FF, CRT_HORIZONTAL_TOTAL);
+		smi_mmiowl(0x00C80424, CRT_HORIZONTAL_SYNC);
+		smi_mmiowl(0x032502FF, CRT_VERTICAL_TOTAL);
+		smi_mmiowl(0x00060302, CRT_VERTICAL_SYNC);
+		smi_mmiowl(0x007FF800, CRT_HWC_ADDRESS);
+		smi_mmiowl(0x00010305, CRT_DISPLAY_CTRL);
+		smi_mmiowl(0x00000001, MISC_CTRL);
+	}
+
+	/* Enable I2C in GPIO.
+	*/
+#if 0
+	reg = smi_mmiorl(GPIO_HI_CTRL);
+	wmb();
+	reg |= 0x0000c000;		/* Enable bits 46, 47 */
+	smi_mmiowl(reg, GPIO_HI_CTRL);
+	wmb();
+	smi_mmiowb(0x00, I2C_CONTROL);	/* enable, 100kHz, stop */
+	wmb();
+	smi_mmiowb(0x00, I2C_STATUS_RESET);
+#else
+	/* Enable GPIO pins and make them inputs.
+	*/
+	reg = smi_mmiorl(GPIO_DATA_DIR_HI);
+	wmb();
+	reg &= ~0x0000c000;		/* Enable bits 46, 47 */
+	smi_mmiowl(reg, GPIO_DATA_DIR_HI);
+	reg = smi_mmiorl(GPIO_HI_CTRL);
+	wmb();
+	reg &= ~0x0000c000;		/* Enable bits 46, 47 */
+	smi_mmiowl(reg, GPIO_HI_CTRL);
+	wmb();
+	reg = smi_mmiorl(GPIO_DATA_HI);
+	wmb();
+	reg |= 0x0000c000;		/* Let 'em float */
+	smi_mmiowl(reg, GPIO_DATA_HI);
+#endif
+
+	/* Enable 8-bit ZV Port.
+	*/
+	reg = smi_mmiorl(GPIO_LO_CTRL);
+	wmb();
+	reg |= 0x00ff0000;		/* Enable bits 16-23 */
+	smi_mmiowl(reg, GPIO_LO_CTRL);
+	wmb();
+}
+
+/* Set up the zv port to be displayed on the screen.  Currently
+ * coded to expect CCIR 656, YUV 4:2:2 cosited from the PNX1302.
+ */
+static void
+set_videoport(int loc_x, int loc_y, int size_x, int size_y)
+{
+	int nbytes;
+	int top, left, bot, right;
+
+	/* Initialize registers, most power up undefined.
+	*/
+	smi_mmiowl(0x0014008a, ZV_CAPTURE_CLIP);
+	smi_mmiowl(((size_y << 16) | size_x), ZV_CAPTURE_SIZE);
+
+	/* Magic buffer addresses.  Just ensure they don't
+	 * collide with something else in memory.
+	 */
+	smi_mmiowl(0x00200000, ZV_CAPTURE_BUF0);
+	smi_mmiowl(0x00400000, ZV_CAPTURE_BUF1);
+	smi_mmiowl(0x00000000, ZV_CAPTURE_OFFSET);
+	smi_mmiowl(0x00000004, ZV_FIFO_CTRL);
+
+#if 0
+	smi_mmiowl(0x00016007, VIDEO_DISPLAY_CTRL);
+#else
+	smi_mmiowl(0x00010005, VIDEO_DISPLAY_CTRL);
+#endif
+	smi_mmiowl(0x00400000, VIDEO_DISPLAY_FB0);
+	smi_mmiowl(0x00400000, VIDEO_DISPLAY_FB1);
+	smi_mmiowl(((size_x * 2) << 16), VIDEO_DISPLAY_FBWIDTH);
+	nbytes = (size_x * 2) * size_y;
+	nbytes += 127;
+	nbytes &= ~127;
+	smi_mmiowl(0x00400000 + nbytes, VIDEO_DISPLAY_FB0LAST);
+	smi_mmiowl(0x00400000 + nbytes, VIDEO_DISPLAY_FB1LAST);
+
+
+	top = loc_y;
+	left = loc_x;
+	smi_mmiowl(((top << 16) | left), VIDEO_DISPLAY_TL);
+
+	/* Use max ntsc.
+	*/
+	bot = top + size_y;
+	right = left + size_x;
+	smi_mmiowl(((bot << 16) | right), VIDEO_DISPLAY_BR);
+
+	smi_mmiowl(0x00000000, VIDEO_SCALE);
+	smi_mmiowl(0x00000000, VIDEO_INITIAL_SCALE);
+
+	smi_mmiowl(0x00ededed, VIDEO_YUV_CONSTANTS);
+
+	smi_mmiowl(0x17010000, VIDEO_ALPHA_CTRL);
+
+	/* Enable
+	*/
+#if 0
+	smi_mmiowl(0x00000211, ZV_CAPTURE_CTRL);
+#endif
+
+}
+
+/*
+ * Unmap in the memory mapped IO registers
+ *
+ */
+
+static void __devinit
+smi_unmap_mmio(struct smifb_info *sfb)
+{
+	if (sfb && SMIRegs) {
+		iounmap(SMIRegs);
+		SMIRegs = NULL;
+	}
+}
+
+
+/*
+ * Unmap in the screen memory
+ *
+ */
+static void __devinit
+smi_unmap_smem(struct smifb_info *sfb)
+{
+	if (sfb && sfb->fb_virt_start) {
+		iounmap(sfb->fb_virt_start);
+		sfb->fb_virt_start = NULL;
+	}
+}
+
+void
+smi501fb_setup (char *options, int *ints)
+{
+	char* this_opt;
+    
+	if (!options || !*options)
+		return;
+
+	/* The only thing I'm looking for right now is to enable a
+	 * CRT output that mirrors the panel display.
+	 */
+	if (strcmp(options, "crt") == 0)
+		crt_out = 1;
+
+	return;
+}
+
+int __init smi501fb_init(void)
+{
+	struct smifb_info *sfb;
+	char name[16];
+	int err;
+
+	struct pci_dev *pdev = NULL;
+	int i = 0;
+
+	/* Find and enable Voyager
+	 * Rev. AA is 0x501, Rev. B is 0x510.
+	 */
+	pdev = pci_find_device(0x126f,0x510, pdev);
+	if (pdev == NULL)
+		pdev = pci_find_device(0x126f,0x501, pdev);
+	if (pdev == NULL)
+		return -ENODEV;
+	
+	/* Enable the chip.
+	*/
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+	current_par.chipID = 0x510;
+
+	err = -ENOMEM;
+	sprintf(name, "smifb");
+	sfb = &fb_info;
+	memset(sfb, 0, sizeof(struct smifb_info));
+	memset(&disp, 0, sizeof(struct display));
+
+	sfb->currcon = -1;
+	sfb->dev = pdev;
+
+
+	/* Set up MMIO space.
+	*/
+	sfb->mmio_phys = pci_resource_start(pdev,1);
+	sfb->mmio_size = 0x00200000;
+	sfb->mmio_virt_start = ioremap(sfb->mmio_phys, sfb->mmio_size);
+	SMIRegs = sfb->mmio_virt_start;
+
+	/* Set up framebuffer.  It's a 64M space, various amount of
+	 * internal memory.  I don't know how to determine the real
+	 * amount of memory (yet).
+	 */
+	sfb->fb_phys = pci_resource_start(pdev,0);
+	sfb->fb_size = 0x00800000;
+	sfb->fb_virt_start = ioremap(sfb->fb_phys, sfb->fb_size);
+	SMILFB = sfb->fb_virt_start;
+
+	 memset((void *)fb_info.fb_virt_start, 0, fb_info.fb_size);
+
+	current_par.var.xres =
+   	current_par.var.xres_virtual = 1024; //sfb->fb.var.xres;
+	current_par.var.yres =
+	current_par.var.yres_virtual = 768; //sfb->fb.var.yres;
+	current_par.var.bits_per_pixel = 16; //sfb->fb.var.bits_per_pixel;
+	current_par.line_length = (current_par.var.bits_per_pixel * current_par.var.xres) / 8;
+
+	smi_setmode(sfb, &current_par);
+
+	fb_info.gen.parsize = sizeof(struct smifb_par);
+	fb_info.gen.fbhw = &smi_switch;
+
+	strcpy(fb_info.gen.info.modename, "SMI Voyager");
+	fb_info.gen.info.changevar = NULL;
+	fb_info.gen.info.node = -1;
+
+	fb_info.gen.info.fbops = &smifb_ops;
+	fb_info.gen.info.disp = &disp;
+	fb_info.gen.info.switch_con = &fbgen_switch;
+	fb_info.gen.info.updatevar = &fbgen_update_var;
+	fb_info.gen.info.blank = &fbgen_blank;
+	fb_info.gen.info.flags = FBINFO_FLAG_DEFAULT;
+
+	/* This should give a reasonable default video mode
+	*/
+	fbgen_get_var(&disp.var, -1, &fb_info.gen.info);
+	fbgen_do_set_var(&disp.var, 1, &fb_info.gen);
+	fbgen_set_disp(-1, &fb_info.gen);
+	fbgen_install_cmap(0, &fb_info.gen);
+
+	if (register_framebuffer(&fb_info.gen.info) < 0)
+		goto failed;
+
+#if 0
+	/* This is for videoport testing.  The Mirage uses an 8-bit
+	 * video port, which is now known to not work properly
+	 * on the SMI501 part.
+	 */
+	set_videoport(150, 150, 768, 576);
+#endif
+
+	MOD_INC_USE_COUNT;
+	printk("Silicon Motion Inc. VOYAGER Init complete.\n");
+
+	return 0;
+
+failed:
+	smi_unmap_smem(sfb);
+	smi_unmap_mmio(sfb);
+
+	return err;
+}
diff -Nur linux-2.4.30/drivers/video/smi501fb.h linux-2.4.30-linux-mips.org/drivers/video/smi501fb.h
--- linux-2.4.30/drivers/video/smi501fb.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/video/smi501fb.h	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,179 @@
+/***************************************************************************
+			smifb.h  -  SiliconMotion LynxEM+ frame buffer device
+                             -------------------
+    begin                : Thu Aug 9 2001
+    copyright            : (C) 2001 by Szu-Tao Huang
+    email                : johuang@siliconmotion.com
+    
+    Updated to SM501 by Eric.Devolder@amd.com and dan@embeddededge.com
+    for the AMD Mirage Portable Tablet.  20 Oct 2003
+ ***************************************************************************/
+
+/***************************************************************************
+ *                                                                         *
+ *   This program is free software; you can redistribute it and/or modify  *
+ *   it under the terms of the GNU General Public License as published by  *
+ *   the Free Software Foundation; either version 2 of the License, or     *
+ *   (at your option) any later version.                                   *
+ *                                                                         *
+ ***************************************************************************/
+#include <linux/config.h>
+
+#define smi_mmiowb(dat,reg)	writeb(dat, (unsigned int)(SMIRegs + reg))
+#define smi_mmioww(dat,reg)	writew(dat, (unsigned int)(SMIRegs + reg))
+#define smi_mmiowl(dat,reg)	writel(dat, (unsigned int)(SMIRegs + reg))
+
+#define smi_mmiorb(reg)	        readb(SMIRegs + reg)
+#define smi_mmiorw(reg)	        readw(SMIRegs + reg)
+#define smi_mmiorl(reg)	        readl(SMIRegs + reg)
+
+#define NR_PALETTE      256
+
+/* Address space offsets for various control/status registers.
+*/
+#define MISC_CTRL			0x000004
+#define GPIO_LO_CTRL			0x000008
+#define GPIO_HI_CTRL			0x00000c
+#define DRAM_CTRL			0x000010
+#define CURRENT_POWER_GATE		0x000038
+#define CURRENT_POWER_CLOCK		0x00003C
+#define POWER_MODE1_GATE                0x000048
+#define POWER_MODE1_CLOCK               0x00004C
+#define POWER_MODE_CTRL			0x000054
+
+#define GPIO_DATA_LO			0x010000
+#define GPIO_DATA_HI			0x010004
+#define GPIO_DATA_DIR_LO		0x010008
+#define GPIO_DATA_DIR_HI		0x01000c
+#define I2C_BYTE_COUNT			0x010040
+#define I2C_CONTROL			0x010041
+#define I2C_STATUS_RESET		0x010042
+#define I2C_SLAVE_ADDRESS		0x010043
+#define I2C_DATA			0x010044
+
+#define DE_COLOR_COMPARE		0x100020
+#define DE_COLOR_COMPARE_MASK		0x100024
+#define DE_MASKS			0x100028
+#define DE_WRAP				0x10004C
+
+#define PANEL_DISPLAY_CTRL              0x080000
+#define PANEL_PAN_CTRL                  0x080004
+#define PANEL_COLOR_KEY                 0x080008
+#define PANEL_FB_ADDRESS                0x08000C
+#define PANEL_FB_WIDTH                  0x080010
+#define PANEL_WINDOW_WIDTH              0x080014
+#define PANEL_WINDOW_HEIGHT             0x080018
+#define PANEL_PLANE_TL                  0x08001C
+#define PANEL_PLANE_BR                  0x080020
+#define PANEL_HORIZONTAL_TOTAL          0x080024
+#define PANEL_HORIZONTAL_SYNC           0x080028
+#define PANEL_VERTICAL_TOTAL            0x08002C
+#define PANEL_VERTICAL_SYNC             0x080030
+#define PANEL_CURRENT_LINE              0x080034
+#define VIDEO_DISPLAY_CTRL		0x080040
+#define VIDEO_DISPLAY_FB0		0x080044
+#define VIDEO_DISPLAY_FBWIDTH		0x080048
+#define VIDEO_DISPLAY_FB0LAST		0x08004C
+#define VIDEO_DISPLAY_TL		0x080050
+#define VIDEO_DISPLAY_BR		0x080054
+#define VIDEO_SCALE			0x080058
+#define VIDEO_INITIAL_SCALE		0x08005C
+#define VIDEO_YUV_CONSTANTS		0x080060
+#define VIDEO_DISPLAY_FB1		0x080064
+#define VIDEO_DISPLAY_FB1LAST		0x080068
+#define VIDEO_ALPHA_CTRL		0x080080
+#define PANEL_HWC_ADDRESS		0x0800F0
+#define CRT_DISPLAY_CTRL		0x080200
+#define CRT_FB_ADDRESS			0x080204
+#define CRT_FB_WIDTH			0x080208
+#define CRT_HORIZONTAL_TOTAL		0x08020c
+#define CRT_HORIZONTAL_SYNC		0x080210
+#define CRT_VERTICAL_TOTAL		0x080214
+#define CRT_VERTICAL_SYNC		0x080218
+#define CRT_HWC_ADDRESS			0x080230
+#define CRT_HWC_LOCATION		0x080234
+
+#define ZV_CAPTURE_CTRL			0x090000
+#define ZV_CAPTURE_CLIP			0x090004
+#define ZV_CAPTURE_SIZE			0x090008
+#define ZV_CAPTURE_BUF0			0x09000c
+#define ZV_CAPTURE_BUF1			0x090010
+#define ZV_CAPTURE_OFFSET		0x090014
+#define ZV_FIFO_CTRL			0x090018
+
+#define waitforvsync() udelay(100); udelay(100); udelay(100); udelay(100);
+
+/*
+ * Minimum X and Y resolutions
+ */
+#define MIN_XRES	640
+#define MIN_YRES	480
+
+/*
+* Private structure
+*/
+struct smifb_info
+{
+        /*
+        * The following is a pointer to be passed into the
+        * functions below.  The modules outside the main
+        * smifb.c driver have no knowledge as to what
+        * is within this structure.
+        */
+        struct fb_info_gen          gen;
+        struct display_switch   *dispsw;
+        struct pci_dev	        *dev;
+        signed int              currcon;
+
+        struct {
+                u8 red, green, blue;
+        } palette[NR_PALETTE];
+
+        u_int                   palette_size;
+
+		int mmaped;
+		u32 mmio_phys;
+		u32 mmio_size;
+		char *mmio_virt_start;
+
+		u32 fb_phys;
+		u32 fb_size;
+		char *fb_virt_start;
+
+#if defined(FBCON_HAS_CFB16)
+		u16 fbcon_cmap16[16];
+#endif
+
+		/* smi_alloc_fb_info has struct display + 16*sizeof(u32)
+		*/
+		u32 yyy[16];
+};
+
+struct smifb_par
+{
+	struct fb_var_screeninfo var;
+	int line_length;	/* in bytes */
+	int cmap_len;		/* color-map length */
+
+	/*
+	 * Hardware
+	 */
+	u16		chipID;
+
+	u_int	width;
+	u_int	height;
+	u_int	hz;
+};
+
+/* User access to registers for debug and development.
+*/
+struct smireg_op {
+	uint	sr_op;
+	uint	sr_reg;
+	uint	sr_val;
+};
+
+#define SMI_LOAD_REG	1
+#define SMI_STORE_REG	2
+#define SMI_AND_REG	3
+#define SMI_OR_REG	4
diff -Nur linux-2.4.30/drivers/video/tgafb.c linux-2.4.30-linux-mips.org/drivers/video/tgafb.c
--- linux-2.4.30/drivers/video/tgafb.c	2001-11-14 23:52:20.000000000 +0100
+++ linux-2.4.30-linux-mips.org/drivers/video/tgafb.c	2005-05-12 17:57:35.000000000 +0200
@@ -45,6 +45,15 @@
 #include <linux/console.h>
 #include <asm/io.h>
 
+#ifdef CONFIG_TC
+#include <asm/dec/tc.h>
+#else
+static int search_tc_card(const char *) { return -1; }
+static void claim_tc_card(int) { }
+static void release_tc_card(int) { }
+static unsigned long get_tc_base_addr(int) { return 0; }
+#endif
+
 #include <video/fbcon.h>
 #include <video/fbcon-cfb8.h>
 #include <video/fbcon-cfb32.h>
@@ -84,10 +93,10 @@
 };
 
 static unsigned int deep_presets[4] = {
-  0x00014000,
-  0x0001440d,
+  0x00004000,
+  0x0000440d,
   0xffffffff,
-  0x0001441d
+  0x0000441d
 };
 
 static unsigned int rasterop_presets[4] = {
@@ -131,6 +140,13 @@
 	0,
 	FB_VMODE_NONINTERLACED
     }},
+    { "1280x1024-72", {			/* mode #0 of PMAGD boards */
+	1280, 1024, 1280, 1024, 0, 0, 0, 0,
+	{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
+	0, 0, -1, -1, FB_ACCELF_TEXT, 7692, 232, 32, 34, 3, 160, 3,
+	FB_SYNC_ON_GREEN,
+	FB_VMODE_NONINTERLACED
+    }},
     { "800x600-56", {
 	800, 600, 800, 600, 0, 0, 0, 0,
 	{0, 8, 0}, {0, 8, 0}, {0, 8, 0}, {0, 0, 0},
@@ -488,7 +504,8 @@
       continue;
 
     mb();
-    TGA_WRITE_REG(deep_presets[fb_info.tga_type], TGA_DEEP_REG);
+    TGA_WRITE_REG(deep_presets[fb_info.tga_type] |
+		  (par->sync_on_green ? 0x0 : 0x00010000), TGA_DEEP_REG);
     while (TGA_READ_REG(TGA_CMD_STAT_REG) & 1) /* wait for not busy */
 	continue;
     mb();
@@ -548,7 +565,7 @@
 	BT463_WRITE(BT463_REG_ACC, BT463_CMD_REG_0, 0x40);
 	BT463_WRITE(BT463_REG_ACC, BT463_CMD_REG_1, 0x08);
 	BT463_WRITE(BT463_REG_ACC, BT463_CMD_REG_2, 
-		(par->sync_on_green ? 0x80 : 0x40));
+		(par->sync_on_green ? 0xc0 : 0x40));
 
 	BT463_WRITE(BT463_REG_ACC, BT463_READ_MASK_0, 0xff);
 	BT463_WRITE(BT463_REG_ACC, BT463_READ_MASK_1, 0xff);
@@ -921,19 +938,34 @@
 int __init tgafb_init(void)
 {
     struct pci_dev *pdev;
+    int slot;
 
     pdev = pci_find_device(PCI_VENDOR_ID_DEC, PCI_DEVICE_ID_DEC_TGA, NULL);
     if (!pdev)
+	slot = search_tc_card("PMAGD");
+    if (!pdev && slot < 0)
 	return -ENXIO;
 
     /* divine board type */
 
-    fb_info.tga_mem_base = (unsigned long)ioremap(pdev->resource[0].start, 0);
-    fb_info.tga_type = (readl(fb_info.tga_mem_base) >> 12) & 0x0f;
-    fb_info.tga_regs_base = fb_info.tga_mem_base + TGA_REGS_OFFSET;
-    fb_info.tga_fb_base = (fb_info.tga_mem_base
+    if (pdev) {
+	fb_info.tga_mem_base = (unsigned long)ioremap(pdev->resource[0].start,
+						      0);
+	fb_info.tga_type = (readl(fb_info.tga_mem_base) >> 12) & 0x0f;
+	fb_info.tga_regs_base = fb_info.tga_mem_base + TGA_REGS_OFFSET;
+	fb_info.tga_fb_base = (fb_info.tga_mem_base
 			   + fb_offset_presets[fb_info.tga_type]);
-    pci_read_config_byte(pdev, PCI_REVISION_ID, &fb_info.tga_chip_rev);
+	pci_read_config_byte(pdev, PCI_REVISION_ID, &fb_info.tga_chip_rev);
+
+    } else {
+	claim_tc_card(slot);
+	fb_info.tga_mem_base = get_tc_base_addr(slot);
+	fb_info.tga_type = (readl(fb_info.tga_mem_base) >> 12) & 0x0f;	/* ? */
+	fb_info.tga_regs_base = fb_info.tga_mem_base + TGA_REGS_OFFSET;
+	fb_info.tga_fb_base = (fb_info.tga_mem_base
+			   + fb_offset_presets[fb_info.tga_type]);
+	fb_info.tga_chip_rev = TGA_READ_REG(TGA_START_REG) & 0xff;
+    }
 
     /* setup framebuffer */
 
@@ -950,40 +982,62 @@
     fb_info.gen.fbhw = &tgafb_hwswitch;
     fb_info.gen.fbhw->detect();
 
-    printk (KERN_INFO "tgafb: DC21030 [TGA] detected, rev=0x%02x\n", fb_info.tga_chip_rev);
-    printk (KERN_INFO "tgafb: at PCI bus %d, device %d, function %d\n", 
-	    pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+    if (pdev) {
+	printk (KERN_INFO "tgafb: DC21030 [TGA] detected, rev=0x%02x\n",
+		fb_info.tga_chip_rev);
+	printk (KERN_INFO "tgafb: at PCI bus %d, device %d, function %d\n", 
+		pdev->bus->number,
+		PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
+    } else {
+	printk (KERN_INFO "tgafb: SFB+ detected, rev=0x%02x\n",
+		fb_info.tga_chip_rev);
+    }
 	    
     switch (fb_info.tga_type) 
     { 
 	case TGA_TYPE_8PLANE:
-	    strcpy (fb_info.gen.info.modename,"Digital ZLXp-E1"); 
+	    if (pdev)
+		strcpy (fb_info.gen.info.modename,"Digital ZLXp-E1"); 
+	    else
+		strcpy (fb_info.gen.info.modename,"Digital ZLX-E1"); 
 	    break;
 
 	case TGA_TYPE_24PLANE:
-	    strcpy (fb_info.gen.info.modename,"Digital ZLXp-E2"); 
+	    if (pdev)
+		strcpy (fb_info.gen.info.modename,"Digital ZLXp-E2"); 
+	    else
+		strcpy (fb_info.gen.info.modename,"Digital ZLX-E2"); 
 	    break;
 
 	case TGA_TYPE_24PLUSZ:
-	    strcpy (fb_info.gen.info.modename,"Digital ZLXp-E3"); 
+	    if (pdev)
+		strcpy (fb_info.gen.info.modename,"Digital ZLXp-E3"); 
+	    else
+		strcpy (fb_info.gen.info.modename,"Digital ZLX-E3"); 
 	    break;
     }
 
     /* This should give a reasonable default video mode */
 
     if (!default_var_valid) {
-	default_var = tgafb_predefined[0].var;
+	if (pdev)
+	    default_var = tgafb_predefined[0].var;
+	else
+	    default_var = tgafb_predefined[1].var;
     }
     fbgen_get_var(&disp.var, -1, &fb_info.gen.info);
     disp.var.activate = FB_ACTIVATE_NOW;
     fbgen_do_set_var(&disp.var, 1, &fb_info.gen);
     fbgen_set_disp(-1, &fb_info.gen);
     fbgen_install_cmap(0, &fb_info.gen);
-    if (register_framebuffer(&fb_info.gen.info) < 0)
+    if (register_framebuffer(&fb_info.gen.info) < 0) {
+	if (slot >= 0)
+	    release_tc_card(slot);
 	return -EINVAL;
-    printk(KERN_INFO "fb%d: %s frame buffer device at 0x%lx\n", 
+    }
+    printk(KERN_INFO "fb%d: %s frame buffer device at 0x%llx\n", 
 	    GET_FB_IDX(fb_info.gen.info.node), fb_info.gen.info.modename, 
-	    pdev->resource[0].start);
+	    fb_info.tga_mem_base);
     return 0;
 }
 
diff -Nur linux-2.4.30/drivers/video/tgafb.h linux-2.4.30-linux-mips.org/drivers/video/tgafb.h
--- linux-2.4.30/drivers/video/tgafb.h	2000-04-12 18:47:28.000000000 +0200
+++ linux-2.4.30-linux-mips.org/drivers/video/tgafb.h	2005-05-12 17:57:35.000000000 +0200
@@ -36,6 +36,7 @@
 #define	TGA_RASTEROP_REG		0x0034
 #define	TGA_PIXELSHIFT_REG		0x0038
 #define	TGA_DEEP_REG			0x0050
+#define	TGA_START_REG			0x0054
 #define	TGA_PIXELMASK_REG		0x005c
 #define	TGA_CURSOR_BASE_REG		0x0060
 #define	TGA_HORIZ_REG			0x0064
diff -Nur linux-2.4.30/fs/binfmt_elf.c linux-2.4.30-linux-mips.org/fs/binfmt_elf.c
--- linux-2.4.30/fs/binfmt_elf.c	2005-04-04 03:42:20.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/binfmt_elf.c	2005-05-12 17:57:35.000000000 +0200
@@ -660,6 +660,9 @@
 		    bprm->argc++;
 		  }
 		}
+	} else {
+		/* Executables without an interpreter also need a personality  */
+		SET_PERSONALITY(elf_ex, ibcs2_interpreter);
 	}
 
 	/* Flush all traces of the currently running executable */
@@ -1211,7 +1214,11 @@
 	elf.e_entry = 0;
 	elf.e_phoff = sizeof(elf);
 	elf.e_shoff = 0;
+#ifdef ELF_CORE_EFLAGS
+	elf.e_flags = ELF_CORE_EFLAGS;
+#else
 	elf.e_flags = 0;
+#endif
 	elf.e_ehsize = sizeof(elf);
 	elf.e_phentsize = sizeof(struct elf_phdr);
 	elf.e_phnum = segs+1;		/* Include notes */
diff -Nur linux-2.4.30/fs/Config.in linux-2.4.30-linux-mips.org/fs/Config.in
--- linux-2.4.30/fs/Config.in	2004-11-17 12:54:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/Config.in	2005-05-12 18:01:30.000000000 +0200
@@ -49,6 +49,7 @@
 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
 fi
 tristate 'Compressed ROM file system support' CONFIG_CRAMFS
 bool 'Virtual memory file system support (former shm fs)' CONFIG_TMPFS
diff -Nur linux-2.4.30/fs/jffs2/background.c linux-2.4.30-linux-mips.org/fs/jffs2/background.c
--- linux-2.4.30/fs/jffs2/background.c	2001-10-25 09:07:09.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/jffs2/background.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,61 +1,36 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: background.c,v 1.16 2001/10/08 09:22:38 dwmw2 Exp $
+ * $Id: background.c,v 1.48 2004/02/23 17:17:23 dwmw2 Exp $
  *
  */
 
 #define __KERNEL_SYSCALLS__
 
 #include <linux/kernel.h>
-#include <linux/sched.h>
-#include <linux/unistd.h>
 #include <linux/jffs2.h>
 #include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
 #include <linux/completion.h>
+#include <linux/sched.h>
+#include <linux/unistd.h>
+#include <linux/suspend.h>
 #include "nodelist.h"
 
 
 static int jffs2_garbage_collect_thread(void *);
-static int thread_should_wake(struct jffs2_sb_info *c);
 
 void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c)
 {
-	spin_lock_bh(&c->erase_completion_lock);
-        if (c->gc_task && thread_should_wake(c))
+	spin_lock(&c->erase_completion_lock);
+        if (c->gc_task && jffs2_thread_should_wake(c))
                 send_sig(SIGHUP, c->gc_task, 1);
-	spin_unlock_bh(&c->erase_completion_lock);
+	spin_unlock(&c->erase_completion_lock);
 }
 
 /* This must only ever be called when no GC thread is currently running */
@@ -86,12 +61,12 @@
 
 void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c)
 {
-	spin_lock_bh(&c->erase_completion_lock);
+	spin_lock(&c->erase_completion_lock);
 	if (c->gc_task) {
 		D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid));
 		send_sig(SIGKILL, c->gc_task, 1);
 	}
-	spin_unlock_bh(&c->erase_completion_lock);
+	spin_unlock(&c->erase_completion_lock);
 	wait_for_completion(&c->gc_thread_exit);
 }
 
@@ -99,85 +74,76 @@
 {
 	struct jffs2_sb_info *c = _c;
 
-	daemonize();
-	current->tty = NULL;
+	daemonize("jffs2_gcd_mtd%d", c->mtd->index);
+	allow_signal(SIGKILL);
+	allow_signal(SIGSTOP);
+	allow_signal(SIGCONT);
+
 	c->gc_task = current;
 	up(&c->gc_thread_start);
 
-        sprintf(current->comm, "jffs2_gcd_mtd%d", c->mtd->index);
-
-	/* FIXME in the 2.2 backport */
-	current->nice = 10;
+	set_user_nice(current, 10);
 
 	for (;;) {
-		spin_lock_irq(&current->sigmask_lock);
-		siginitsetinv (&current->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT));
-		recalc_sigpending(current);
-		spin_unlock_irq(&current->sigmask_lock);
+		allow_signal(SIGHUP);
 
-		if (!thread_should_wake(c)) {
-                        set_current_state (TASK_INTERRUPTIBLE);
+		if (!jffs2_thread_should_wake(c)) {
+			set_current_state (TASK_INTERRUPTIBLE);
 			D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n"));
-			/* Yes, there's a race here; we checked thread_should_wake() before
-			   setting current->state to TASK_INTERRUPTIBLE. But it doesn't
+			/* Yes, there's a race here; we checked jffs2_thread_should_wake()
+			   before setting current->state to TASK_INTERRUPTIBLE. But it doesn't
 			   matter - We don't care if we miss a wakeup, because the GC thread
 			   is only an optimisation anyway. */
 			schedule();
 		}
-                
-		if (current->need_resched)
-			schedule();
 
-                /* Put_super will send a SIGKILL and then wait on the sem. 
-                 */
-                while (signal_pending(current)) {
-                        siginfo_t info;
-                        unsigned long signr;
-
-                        spin_lock_irq(&current->sigmask_lock);
-                        signr = dequeue_signal(&current->blocked, &info);
-                        spin_unlock_irq(&current->sigmask_lock);
-
-                        switch(signr) {
-                        case SIGSTOP:
-                                D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGSTOP received.\n"));
-                                set_current_state(TASK_STOPPED);
-                                schedule();
-                                break;
-
-                        case SIGKILL:
-                                D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n"));
-				spin_lock_bh(&c->erase_completion_lock);
-                                c->gc_task = NULL;
-				spin_unlock_bh(&c->erase_completion_lock);
-				complete_and_exit(&c->gc_thread_exit, 0);
+		if (current->flags & PF_FREEZE) {
+			refrigerator(0);
+			/* refrigerator() should recalc sigpending for us
+			   but doesn't. No matter - allow_signal() will. */
+			continue;
+		}
+
+		cond_resched();
+
+		/* Put_super will send a SIGKILL and then wait on the sem. 
+		 */
+		while (signal_pending(current)) {
+			siginfo_t info;
+			unsigned long signr;
+
+			signr = dequeue_signal_lock(current, &current->blocked, &info);
+
+			switch(signr) {
+			case SIGSTOP:
+				D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGSTOP received.\n"));
+				set_current_state(TASK_STOPPED);
+				schedule();
+				break;
+
+			case SIGKILL:
+				D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n"));
+				goto die;
 
 			case SIGHUP:
 				D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGHUP received.\n"));
 				break;
 			default:
 				D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr));
-
-                        }
-                }
+			}
+		}
 		/* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */
-		spin_lock_irq(&current->sigmask_lock);
-		siginitsetinv (&current->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT));
-		recalc_sigpending(current);
-		spin_unlock_irq(&current->sigmask_lock);
+		disallow_signal(SIGHUP);
 
 		D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n"));
-		jffs2_garbage_collect_pass(c);
+		if (jffs2_garbage_collect_pass(c) == -ENOSPC) {
+			printk(KERN_NOTICE "No space for garbage collection. Aborting GC thread\n");
+			goto die;
+		}
 	}
-}
-
-static int thread_should_wake(struct jffs2_sb_info *c)
-{
-	D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x\n", 
-		  c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size));
-	if (c->nr_free_blocks + c->nr_erasing_blocks < JFFS2_RESERVED_BLOCKS_GCTRIGGER &&
-	    c->dirty_size > c->sector_size)
-		return 1;
-	else 
-		return 0;
+ die:
+	spin_lock(&c->erase_completion_lock);
+	c->gc_task = NULL;
+	spin_unlock(&c->erase_completion_lock);
+	complete_and_exit(&c->gc_thread_exit, 0);
 }
diff -Nur linux-2.4.30/fs/jffs2/build.c linux-2.4.30-linux-mips.org/fs/jffs2/build.c
--- linux-2.4.30/fs/jffs2/build.c	2003-06-13 16:51:37.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/jffs2/build.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,47 +1,22 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: build.c,v 1.16.2.3 2003/04/30 09:43:32 dwmw2 Exp $
+ * $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $
  *
  */
 
 #include <linux/kernel.h>
-#include <linux/jffs2.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
 #include "nodelist.h"
 
-int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *);
-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *);
+static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **);
 
 static inline struct jffs2_inode_cache *
 first_inode_chain(int *i, struct jffs2_sb_info *c)
@@ -68,16 +43,52 @@
 	     ic;					\
 	     ic = next_inode(&i, ic, (c)))
 
+
+static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+	struct jffs2_full_dirent *fd;
+
+	D1(printk(KERN_DEBUG "jffs2_build_inode building directory inode #%u\n", ic->ino));
+
+	/* For each child, increase nlink */
+	for(fd = ic->scan_dents; fd; fd = fd->next) {
+		struct jffs2_inode_cache *child_ic;
+		if (!fd->ino)
+			continue;
+
+		/* XXX: Can get high latency here with huge directories */
+
+		child_ic = jffs2_get_ino_cache(c, fd->ino);
+		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);
+			continue;
+		}
+
+		if (child_ic->nlink++ && fd->type == DT_DIR) {
+			printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
+			if (fd->ino == 1 && ic->ino == 1) {
+				printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
+				printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
+			}
+			/* What do we do about it? */
+		}
+		D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
+		/* Can't free them. We might need them in pass 2 */
+	}
+}
+
 /* Scan plan:
  - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go
  - Scan directory tree from top down, setting nlink in inocaches
  - Scan inocaches for inodes with nlink==0
 */
-int jffs2_build_filesystem(struct jffs2_sb_info *c)
+static int jffs2_build_filesystem(struct jffs2_sb_info *c)
 {
 	int ret;
 	int i;
 	struct jffs2_inode_cache *ic;
+	struct jffs2_full_dirent *dead_fds = NULL;
 
 	/* First, scan the medium and build all the inode caches with
 	   lists of physical nodes */
@@ -90,14 +101,17 @@
 		return ret;
 
 	D1(printk(KERN_DEBUG "Scanned flash completely\n"));
-	/* Now build the data map for each inode, marking obsoleted nodes
-	   as such, and also increase nlink of any children. */
+	D1(jffs2_dump_block_lists(c));
+
+	/* 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));
-		ret = jffs2_build_inode_pass1(c, ic);
-		if (ret) {
-			D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret));
-			return ret;
+
+		D1(BUG_ON(ic->ino > c->highest_ino));
+
+		if (ic->scan_dents) {
+			jffs2_build_inode_pass1(c, ic);
+			cond_resched();
 		}
 	}
 	D1(printk(KERN_DEBUG "Pass 1 complete\n"));
@@ -107,164 +121,89 @@
 	   children too, and repeat the scan. As that's going to be
 	   a fairly uncommon occurrence, it's not so evil to do it this
 	   way. Recursion bad. */
-	do { 
-		D1(printk(KERN_DEBUG "Pass 2 (re)starting\n"));
-		ret = 0;
-		for_each_inode(i, c, ic) {
-			D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes));
-			if (ic->nlink)
-				continue;
+	D1(printk(KERN_DEBUG "Pass 2 starting\n"));
+
+	for_each_inode(i, c, ic) {
+		D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes));
+		if (ic->nlink)
+			continue;
 			
-			ret = jffs2_build_remove_unlinked_inode(c, ic);
-			if (ret)
-				break;
-		/* -EAGAIN means the inode's nlink was zero, so we deleted it,
-		   and furthermore that it had children and their nlink has now
-		   gone to zero too. So we have to restart the scan. */
-		} 
-	} while(ret == -EAGAIN);
-	
+		jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
+		cond_resched();
+	} 
+
+	D1(printk(KERN_DEBUG "Pass 2a starting\n"));
+
+	while (dead_fds) {
+		struct jffs2_inode_cache *ic;
+		struct jffs2_full_dirent *fd = dead_fds;
+
+		dead_fds = fd->next;
+
+		ic = jffs2_get_ino_cache(c, fd->ino);
+		D1(printk(KERN_DEBUG "Removing dead_fd ino #%u (\"%s\"), ic at %p\n", fd->ino, fd->name, ic));
+
+		if (ic)
+			jffs2_build_remove_unlinked_inode(c, ic, &dead_fds);
+		jffs2_free_full_dirent(fd);
+	}
+
 	D1(printk(KERN_DEBUG "Pass 2 complete\n"));
 	
-	/* Finally, we can scan again and free the dirent nodes and scan_info structs */
+	/* Finally, we can scan again and free the dirent structs */
 	for_each_inode(i, c, ic) {
-		struct jffs2_scan_info *scan = ic->scan;
 		struct jffs2_full_dirent *fd;
 		D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes));
-		if (!scan) {
-			if (ic->nlink) {
-				D1(printk(KERN_WARNING "Why no scan struct for ino #%u which has nlink %d?\n", ic->ino, ic->nlink));
-			}
-			continue;
-		}
-		ic->scan = NULL;
-		while(scan->dents) {
-			fd = scan->dents;
-			scan->dents = fd->next;
+
+		while(ic->scan_dents) {
+			fd = ic->scan_dents;
+			ic->scan_dents = fd->next;
 			jffs2_free_full_dirent(fd);
 		}
-		kfree(scan);
+		ic->scan_dents = NULL;
+		cond_resched();
 	}
 	D1(printk(KERN_DEBUG "Pass 3 complete\n"));
+	D1(jffs2_dump_block_lists(c));
 
-	return ret;
-}
-	
-int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
-{
-	struct jffs2_tmp_dnode_info *tn;
-	struct jffs2_full_dirent *fd;
-	struct jffs2_node_frag *fraglist = NULL;
-	struct jffs2_tmp_dnode_info *metadata = NULL;
-
-	D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino));
-	if (ic->ino > c->highest_ino)
-		c->highest_ino = ic->ino;
-
-	if (!ic->scan->tmpnodes && ic->ino != 1) {
-		D1(printk(KERN_DEBUG "jffs2_build_inode: ino #%u has no data nodes!\n", ic->ino));
-	}
-	/* Build the list to make sure any obsolete nodes are marked as such */
-	while(ic->scan->tmpnodes) {
-		tn = ic->scan->tmpnodes;
-		ic->scan->tmpnodes = tn->next;
-		
-		if (metadata && tn->version > metadata->version) {
-			D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring old metadata at 0x%08x\n",
-				  metadata->fn->raw->flash_offset &~3));
-			
-			jffs2_free_full_dnode(metadata->fn);
-			jffs2_free_tmp_dnode_info(metadata);
-			metadata = NULL;
-		}
-			
-		if (tn->fn->size) {
-			jffs2_add_full_dnode_to_fraglist (c, &fraglist, tn->fn);
-			jffs2_free_tmp_dnode_info(tn);
-		} else {
-			if (!metadata) {
-				metadata = tn;
-			} else {
-				D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring new metadata at 0x%08x\n",
-					  tn->fn->raw->flash_offset &~3));
-				
-				jffs2_free_full_dnode(tn->fn);
-				jffs2_free_tmp_dnode_info(tn);
-			}
-		}
-	}
-		
-	/* OK. Now clear up */
-	if (metadata) {
-		jffs2_free_full_dnode(metadata->fn);
-		jffs2_free_tmp_dnode_info(metadata);
-	}
-	metadata = NULL;
-	
-	while (fraglist) {
-		struct jffs2_node_frag *frag;
-		frag = fraglist;
-		fraglist = fraglist->next;
-		
-		if (frag->node && !(--frag->node->frags)) {
-			jffs2_free_full_dnode(frag->node);
-		}
-		jffs2_free_node_frag(frag);
-	}
-
-	/* Now for each child, increase nlink */
-	for(fd=ic->scan->dents; fd; fd = fd->next) {
-		struct jffs2_inode_cache *child_ic;
-		if (!fd->ino)
-			continue;
+	/* Rotate the lists by some number to ensure wear levelling */
+	jffs2_rotate_lists(c);
 
-		child_ic = jffs2_get_ino_cache(c, fd->ino);
-		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);
-			continue;
-		}
-
-		if (child_ic->nlink++ && fd->type == DT_DIR) {
-			printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino);
-			if (fd->ino == 1 && ic->ino == 1) {
-				printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n");
-				printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n");
-			}
-			/* What do we do about it? */
-		}
-		D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino));
-		/* Can't free them. We might need them in pass 2 */
-	}
-	return 0;
+	return ret;
 }
 
-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds)
 {
 	struct jffs2_raw_node_ref *raw;
 	struct jffs2_full_dirent *fd;
-	int ret = 0;
-
-	if(!ic->scan) {
-		D1(printk(KERN_DEBUG "ino #%u was already removed\n", ic->ino));
-		return 0;
-	}
 
 	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) {
-		D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", raw->flash_offset&~3));
+		D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw)));
 		jffs2_mark_node_obsolete(c, raw);
 	}
 
-	if (ic->scan->dents) {
-		printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
-	
-		while(ic->scan->dents) {
+	if (ic->scan_dents) {
+		int whinged = 0;
+		D1(printk(KERN_DEBUG "Inode #%u was a directory which may have children...\n", ic->ino));
+
+		while(ic->scan_dents) {
 			struct jffs2_inode_cache *child_ic;
 
-			fd = ic->scan->dents;
-			ic->scan->dents = fd->next;
+			fd = ic->scan_dents;
+			ic->scan_dents = fd->next;
+
+			if (!fd->ino) {
+				/* It's a deletion dirent. Ignore it */
+				D1(printk(KERN_DEBUG "Child \"%s\" is a deletion dirent, skipping...\n", fd->name));
+				jffs2_free_full_dirent(fd);
+				continue;
+			}
+			if (!whinged) {
+				whinged = 1;
+				printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
+			}
 
 			D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n",
 				  fd->name, fd->ino));
@@ -272,16 +211,136 @@
 			child_ic = jffs2_get_ino_cache(c, fd->ino);
 			if (!child_ic) {
 				printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino);
+				jffs2_free_full_dirent(fd);
 				continue;
 			}
-			jffs2_free_full_dirent(fd);
+
+			/* Reduce nlink of the child. If it's now zero, stick it on the 
+			   dead_fds list to be cleaned up later. Else just free the fd */
+
 			child_ic->nlink--;
+			
+			if (!child_ic->nlink) {
+				D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got zero nlink. Adding to dead_fds list.\n",
+					  fd->ino, fd->name));
+				fd->next = *dead_fds;
+				*dead_fds = fd;
+			} else {
+				D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got nlink %d. Ignoring.\n",
+					  fd->ino, fd->name, child_ic->nlink));
+				jffs2_free_full_dirent(fd);
+			}
 		}
-		ret = -EAGAIN;
 	}
-	kfree(ic->scan);
-	ic->scan = NULL;
-	//	jffs2_del_ino_cache(c, ic);
-	//	jffs2_free_inode_cache(ic);
-	return ret;
+
+	/*
+	   We don't delete the inocache from the hash list and free it yet. 
+	   The erase code will do that, when all the nodes are completely gone.
+	*/
+}
+
+static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
+{
+	uint32_t size;
+
+	/* Deletion should almost _always_ be allowed. We're fairly
+	   buggered once we stop allowing people to delete stuff
+	   because there's not enough free space... */
+	c->resv_blocks_deletion = 2;
+
+	/* Be conservative about how much space we need before we allow writes. 
+	   On top of that which is required for deletia, require an extra 2%
+	   of the medium to be available, for overhead caused by nodes being
+	   split across blocks, etc. */
+
+	size = c->flash_size / 50; /* 2% of flash size */
+	size += c->nr_blocks * 100; /* And 100 bytes per eraseblock */
+	size += c->sector_size - 1; /* ... and round up */
+
+	c->resv_blocks_write = c->resv_blocks_deletion + (size / c->sector_size);
+
+	/* When do we let the GC thread run in the background */
+
+	c->resv_blocks_gctrigger = c->resv_blocks_write + 1;
+
+	/* When do we allow garbage collection to merge nodes to make 
+	   long-term progress at the expense of short-term space exhaustion? */
+	c->resv_blocks_gcmerge = c->resv_blocks_deletion + 1;
+
+	/* When do we allow garbage collection to eat from bad blocks rather
+	   than actually making progress? */
+	c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2;
+
+	/* If there's less than this amount of dirty space, don't bother
+	   trying to GC to make more space. It'll be a fruitless task */
+	c->nospc_dirty_size = c->sector_size + (c->flash_size / 100);
+
+	D1(printk(KERN_DEBUG "JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n",
+		  c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks));
+	D1(printk(KERN_DEBUG "Blocks required to allow deletion:    %d (%d KiB)\n",
+		  c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024));
+	D1(printk(KERN_DEBUG "Blocks required to allow writes:      %d (%d KiB)\n",
+		  c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024));
+	D1(printk(KERN_DEBUG "Blocks required to quiesce GC thread: %d (%d KiB)\n",
+		  c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024));
+	D1(printk(KERN_DEBUG "Blocks required to allow GC merges:   %d (%d KiB)\n",
+		  c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024));
+	D1(printk(KERN_DEBUG "Blocks required to GC bad blocks:     %d (%d KiB)\n",
+		  c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024));
+	D1(printk(KERN_DEBUG "Amount of dirty space required to GC: %d bytes\n",
+		  c->nospc_dirty_size));
+} 
+
+int jffs2_do_mount_fs(struct jffs2_sb_info *c)
+{
+	int i;
+
+	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->blocks)
+		return -ENOMEM;
+	for (i=0; i<c->nr_blocks; i++) {
+		INIT_LIST_HEAD(&c->blocks[i].list);
+		c->blocks[i].offset = i * c->sector_size;
+		c->blocks[i].free_size = c->sector_size;
+		c->blocks[i].dirty_size = 0;
+		c->blocks[i].wasted_size = 0;
+		c->blocks[i].unchecked_size = 0;
+		c->blocks[i].used_size = 0;
+		c->blocks[i].first_node = NULL;
+		c->blocks[i].last_node = NULL;
+	}
+
+	init_MUTEX(&c->alloc_sem);
+	init_MUTEX(&c->erase_free_sem);
+	init_waitqueue_head(&c->erase_wait);
+	init_waitqueue_head(&c->inocache_wq);
+	spin_lock_init(&c->erase_completion_lock);
+	spin_lock_init(&c->inocache_lock);
+
+	INIT_LIST_HEAD(&c->clean_list);
+	INIT_LIST_HEAD(&c->very_dirty_list);
+	INIT_LIST_HEAD(&c->dirty_list);
+	INIT_LIST_HEAD(&c->erasable_list);
+	INIT_LIST_HEAD(&c->erasing_list);
+	INIT_LIST_HEAD(&c->erase_pending_list);
+	INIT_LIST_HEAD(&c->erasable_pending_wbuf_list);
+	INIT_LIST_HEAD(&c->erase_complete_list);
+	INIT_LIST_HEAD(&c->free_list);
+	INIT_LIST_HEAD(&c->bad_list);
+	INIT_LIST_HEAD(&c->bad_used_list);
+	c->highest_ino = 1;
+
+	if (jffs2_build_filesystem(c)) {
+		D1(printk(KERN_DEBUG "build_fs failed\n"));
+		jffs2_free_ino_caches(c);
+		jffs2_free_raw_node_refs(c);
+		kfree(c->blocks);
+		return -EIO;
+	}
+
+	jffs2_calc_trigger_levels(c);
+
+	return 0;
 }
diff -Nur linux-2.4.30/fs/jffs2/compr.c linux-2.4.30-linux-mips.org/fs/jffs2/compr.c
--- linux-2.4.30/fs/jffs2/compr.c	2001-10-05 00:13:18.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/jffs2/compr.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,59 +1,37 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
  * Created by Arjan van de Ven <arjanv@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: compr.c,v 1.17 2001/09/23 09:56:46 dwmw2 Exp $
+ * $Id: compr.c,v 1.34 2004/03/08 15:29:09 dwmw2 Exp $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/string.h>
-#include <linux/types.h>
 #include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/slab.h>
 #include <linux/jffs2.h>
+#include "nodelist.h"
 
-int zlib_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
-void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
-int rtime_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
-int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
-int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen);
-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen);
+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);
 
 
 /* jffs2_compress:
  * @data: Pointer to uncompressed data
- * @cdata: Pointer to buffer for compressed data
+ * @cdata: Pointer to returned pointer to buffer for compressed data
  * @datalen: On entry, holds the amount of data available for compression.
  *	On exit, expected to hold the amount of data actually compressed.
  * @cdatalen: On entry, holds the amount of space available for compressed
@@ -68,47 +46,61 @@
  * 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(unsigned char *data_in, unsigned char *cpage_out, 
-		    __u32 *datalen, __u32 *cdatalen)
+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)
 {
+#ifdef JFFS2_COMPRESSION
 	int ret;
 
-	ret = zlib_compress(data_in, cpage_out, datalen, cdatalen);
+	*cpage_out = kmalloc(*cdatalen, GFP_KERNEL);
+	if (!*cpage_out) {
+		printk(KERN_WARNING "No memory for compressor allocation. Compression failed\n");
+		goto out;
+	}
+
+#ifdef JFFS2_USE_ZLIB
+	ret = jffs2_zlib_compress(data_in, *cpage_out, datalen, cdatalen);
 	if (!ret) {
 		return JFFS2_COMPR_ZLIB;
 	}
-#if 0 /* Disabled 23/9/1. With zlib it hardly ever gets a look in */
-	ret = dynrubin_compress(data_in, cpage_out, datalen, cdatalen);
+#endif
+#ifdef JFFS2_USE_DYNRUBIN
+	ret = jffs2_dynrubin_compress(data_in, *cpage_out, datalen, cdatalen);
 	if (!ret) {
 		return JFFS2_COMPR_DYNRUBIN;
 	}
 #endif
-#if 0 /* Disabled 26/2/1. Obsoleted by dynrubin */
-	ret = rubinmips_compress(data_in, cpage_out, datalen, cdatalen);
+#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 = rtime_compress(data_in, cpage_out, datalen, cdatalen);
+	ret = jffs2_rtime_compress(data_in, *cpage_out, datalen, cdatalen);
 	if (!ret) {
 		return JFFS2_COMPR_RTIME;
 	}
-#if 0
-	/* We don't need to copy. Let the caller special-case the COMPR_NONE case. */
-	/* If we get here, no compression is going to work */
-	/* But we might want to use the fragmentation part -- Arjan */
-	memcpy(cpage_out,data_in,min(*datalen,*cdatalen));
-	if (*datalen > *cdatalen)
-		*datalen = *cdatalen;
-#endif		
+#endif
+	kfree(*cpage_out);
+#endif /* Compression */
+ 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);
+}
 
-int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, 
-		     unsigned char *data_out, __u32 cdatalen, __u32 datalen)
+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)
 {
 	switch (comprtype) {
 	case JFFS2_COMPR_NONE:
@@ -119,30 +111,27 @@
 	case JFFS2_COMPR_ZERO:
 		memset(data_out, 0, datalen);
 		break;
-
+#ifdef JFFS2_USE_ZLIB
 	case JFFS2_COMPR_ZLIB:
-		zlib_decompress(cdata_in, data_out, cdatalen, datalen);
+		jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen);
 		break;
-
+#endif
+#ifdef JFFS2_USE_RTIME
 	case JFFS2_COMPR_RTIME:
-		rtime_decompress(cdata_in, data_out, cdatalen, datalen);
+		jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen);
 		break;
-
-	case JFFS2_COMPR_RUBINMIPS:
-#if 0 /* Disabled 23/9/1 */
-		rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
-#else
-		printk(KERN_WARNING "JFFS2: Rubinmips compression encountered but support not compiled in!\n");
 #endif
+#ifdef JFFS2_USE_RUBINMIPS
+	case JFFS2_COMPR_RUBINMIPS:
+		jffs2_rubinmips_decompress(cdata_in, data_out, cdatalen, datalen);
 		break;
-	case JFFS2_COMPR_DYNRUBIN:
-#if 1 /* Phase this one out */
-		dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
-#else
-		printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n");
 #endif
-		break;
+#ifdef JFFS2_USE_DYNRUBIN
+	case JFFS2_COMPR_DYNRUBIN:
 
+		jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
+		break;
+#endif
 	default:
 		printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype);
 		return -EIO;
diff -Nur linux-2.4.30/fs/jffs2/compr_rtime.c linux-2.4.30-linux-mips.org/fs/jffs2/compr_rtime.c
--- linux-2.4.30/fs/jffs2/compr_rtime.c	2001-09-14 23:04:07.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/jffs2/compr_rtime.c	2005-05-12 18:01:29.000000000 +0200
@@ -1,43 +1,19 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
  * Created by Arjan van de Ven <arjanv@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: compr_rtime.c,v 1.5 2001/03/15 15:38:23 dwmw2 Exp $
+ * $Id: compr_rtime.c,v 1.11 2003/10/04 08:33:06 dwmw2 Exp $
  *
  *
  * Very simple lz77-ish encoder.
  *
  * Theory of operation: Both encoder and decoder have a list of "last
- * occurances" for every possible source-value; after sending the
+ * occurrences" for every possible source-value; after sending the
  * first source-byte, the second byte indicated the "run" length of
  * matches
  *
@@ -51,10 +27,10 @@
 #include <linux/string.h> 
 
 /* _compress returns the compressed size, -1 if bigger */
-int rtime_compress(unsigned char *data_in, unsigned char *cpage_out, 
-		   __u32 *sourcelen, __u32 *dstlen)
+int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, 
+		   uint32_t *sourcelen, uint32_t *dstlen)
 {
-	int positions[256];
+	short positions[256];
 	int outpos = 0;
 	int pos=0;
 
@@ -91,10 +67,10 @@
 }		   
 
 
-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
-		      __u32 srclen, __u32 destlen)
+void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
+		      uint32_t srclen, uint32_t destlen)
 {
-	int positions[256];
+	short positions[256];
 	int outpos = 0;
 	int pos=0;
 	
diff -Nur linux-2.4.30/fs/jffs2/compr_rubin.c linux-2.4.30-linux-mips.org/fs/jffs2/compr_rubin.c
--- linux-2.4.30/fs/jffs2/compr_rubin.c	2001-10-05 00:13:18.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/jffs2/compr_rubin.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,37 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001, 2002 Red Hat, Inc.
  *
  * Created by Arjan van de Ven <arjanv@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: compr_rubin.c,v 1.13 2001/09/23 10:06:05 rmk Exp $
+ * $Id: compr_rubin.c,v 1.17 2002/05/20 14:56:37 dwmw2 Exp $
  *
  */
 
@@ -43,7 +19,7 @@
 
 
 
-void init_rubin(struct rubin_state *rs, int div, int *bits)
+static void init_rubin(struct rubin_state *rs, int div, int *bits)
 {	
 	int c;
 
@@ -56,7 +32,7 @@
 }
 
 
-int encode(struct rubin_state *rs, long A, long B, int symbol)
+static int encode(struct rubin_state *rs, long A, long B, int symbol)
 {
 
 	long i0, i1;
@@ -91,7 +67,7 @@
 }
 
 
-void end_rubin(struct rubin_state *rs)
+static void end_rubin(struct rubin_state *rs)
 {				
 
 	int i;
@@ -104,7 +80,7 @@
 }
 
 
-void init_decode(struct rubin_state *rs, int div, int *bits)
+static void init_decode(struct rubin_state *rs, int div, int *bits)
 {
 	init_rubin(rs, div, bits);		
 
@@ -151,7 +127,7 @@
 	rs->rec_q = rec_q;
 }
 
-int decode(struct rubin_state *rs, long A, long B)
+static int decode(struct rubin_state *rs, long A, long B)
 {
 	unsigned long p = rs->p, q = rs->q;
 	long i0, threshold;
@@ -212,8 +188,8 @@
 
 
 
-int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, 
-		      unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen)
+static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, 
+		      unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen)
 	{
 	int outpos = 0;
 	int pos=0;
@@ -246,20 +222,20 @@
 }		   
 #if 0
 /* _compress returns the compressed size, -1 if bigger */
-int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, 
-		   __u32 *sourcelen, __u32 *dstlen)
+int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, 
+		   uint32_t *sourcelen, uint32_t *dstlen)
 {
 	return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
 }
 #endif
-int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, 
-		   __u32 *sourcelen, __u32 *dstlen)
+int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, 
+		   uint32_t *sourcelen, uint32_t *dstlen)
 {
 	int bits[8];
 	unsigned char histo[256];
 	int i;
 	int ret;
-	__u32 mysrclen, mydstlen;
+	uint32_t mysrclen, mydstlen;
 
 	mysrclen = *sourcelen;
 	mydstlen = *dstlen - 8;
@@ -315,8 +291,8 @@
 	return 0;
 }
 
-void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in, 
-			 unsigned char *page_out, __u32 srclen, __u32 destlen)
+static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in, 
+			 unsigned char *page_out, uint32_t srclen, uint32_t destlen)
 {
 	int outpos = 0;
 	struct rubin_state rs;
@@ -330,14 +306,14 @@
 }		   
 
 
-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, 
-		   __u32 sourcelen, __u32 dstlen)
+void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, 
+		   uint32_t sourcelen, uint32_t dstlen)
 {
 	rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
 }
 
-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, 
-		   __u32 sourcelen, __u32 dstlen)
+void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, 
+		   uint32_t sourcelen, uint32_t dstlen)
 {
 	int bits[8];
 	int c;
diff -Nur linux-2.4.30/fs/jffs2/compr_rubin.h linux-2.4.30-linux-mips.org/fs/jffs2/compr_rubin.h
--- linux-2.4.30/fs/jffs2/compr_rubin.h	2001-09-14 23:04:07.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/jffs2/compr_rubin.h	2005-05-12 18:01:30.000000000 +0200
@@ -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.5 2001/02/26 13:50:01 dwmw2 Exp $ */
+/* $Id: compr_rubin.h,v 1.6 2002/01/25 01:49:26 dwmw2 Exp $ */
 
 #include "pushpull.h"
 
@@ -19,10 +19,3 @@
 	int bit_divider;
 	int bits[8];
 };
-
-
-void init_rubin (struct rubin_state *rs, int div, int *bits);
-int encode (struct rubin_state *, long, long, int);
-void end_rubin (struct rubin_state *);
-void init_decode (struct rubin_state *, int div, int *bits);
-int decode (struct rubin_state *, long, long);
diff -Nur linux-2.4.30/fs/jffs2/comprtest.c linux-2.4.30-linux-mips.org/fs/jffs2/comprtest.c
--- linux-2.4.30/fs/jffs2/comprtest.c	2001-09-14 23:04:07.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/jffs2/comprtest.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,4 +1,4 @@
-/* $Id: comprtest.c,v 1.4 2001/02/21 14:03:20 dwmw2 Exp $ */
+/* $Id: comprtest.c,v 1.5 2002/01/03 15:20:44 dwmw2 Exp $ */
 
 #include <linux/kernel.h>
 #include <linux/string.h>
@@ -266,13 +266,13 @@
 static unsigned char decomprbuf[TESTDATA_LEN];
 
 int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, 
-		     unsigned char *data_out, __u32 cdatalen, __u32 datalen);
+		     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen);
 unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, 
-			     __u32 *datalen, __u32 *cdatalen);
+			     uint32_t *datalen, uint32_t *cdatalen);
 
 int init_module(void ) {
 	unsigned char comprtype;
-	__u32 c, d;
+	uint32_t c, d;
 	int ret;
 
 	printk("Original data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
diff -Nur linux-2.4.30/fs/jffs2/compr_zlib.c linux-2.4.30-linux-mips.org/fs/jffs2/compr_zlib.c
--- linux-2.4.30/fs/jffs2/compr_zlib.c	2002-11-29 00:53:15.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/compr_zlib.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,50 +1,26 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: compr_zlib.c,v 1.8.2.1 2002/10/11 09:04:44 dwmw2 Exp $
+ * $Id: compr_zlib.c,v 1.25 2003/12/03 09:25:43 dwmw2 Exp $
  *
  */
 
-#ifndef __KERNEL__
+#if !defined(__KERNEL__) && !defined(__ECOS)
 #error "The userspace support got too messy and was removed. Update your mkfs.jffs2"
 #endif
 
 #include <linux/config.h>
 #include <linux/kernel.h>
-#include <linux/mtd/compatmac.h> /* for min() */
 #include <linux/slab.h>
-#include <linux/jffs2.h>
 #include <linux/zlib.h>
+#include <linux/zutil.h>
+#include <asm/semaphore.h>
 #include "nodelist.h"
 
 	/* Plan: call deflate() with avail_in == *sourcelen, 
@@ -58,21 +34,24 @@
 
 static DECLARE_MUTEX(deflate_sem);
 static DECLARE_MUTEX(inflate_sem);
-static void *deflate_workspace;
-static void *inflate_workspace;
+static z_stream inf_strm, def_strm;
+
+#ifdef __KERNEL__ /* Linux-only */
+#include <linux/vmalloc.h>
+#include <linux/init.h>
 
 int __init jffs2_zlib_init(void)
 {
-	deflate_workspace = vmalloc(zlib_deflate_workspacesize());
-	if (!deflate_workspace) {
+	def_strm.workspace = vmalloc(zlib_deflate_workspacesize());
+	if (!def_strm.workspace) {
 		printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize());
 		return -ENOMEM;
 	}
 	D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize()));
-	inflate_workspace = vmalloc(zlib_inflate_workspacesize());
-	if (!inflate_workspace) {
+	inf_strm.workspace = vmalloc(zlib_inflate_workspacesize());
+	if (!inf_strm.workspace) {
 		printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize());
-		vfree(deflate_workspace);
+		vfree(def_strm.workspace);
 		return -ENOMEM;
 	}
 	D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize()));
@@ -81,97 +60,120 @@
 
 void jffs2_zlib_exit(void)
 {
-	vfree(deflate_workspace);
-	vfree(inflate_workspace);
+	vfree(def_strm.workspace);
+	vfree(inf_strm.workspace);
 }
+#endif /* __KERNEL__ */
 
-int zlib_compress(unsigned char *data_in, unsigned char *cpage_out, 
-		   __u32 *sourcelen, __u32 *dstlen)
+int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, 
+		   uint32_t *sourcelen, uint32_t *dstlen)
 {
-	z_stream strm;
 	int ret;
 
 	if (*dstlen <= STREAM_END_SPACE)
 		return -1;
 
 	down(&deflate_sem);
-	strm.workspace = deflate_workspace;
 
-	if (Z_OK != zlib_deflateInit(&strm, 3)) {
+	if (Z_OK != zlib_deflateInit(&def_strm, 3)) {
 		printk(KERN_WARNING "deflateInit failed\n");
 		up(&deflate_sem);
 		return -1;
 	}
 
-	strm.next_in = data_in;
-	strm.total_in = 0;
+	def_strm.next_in = data_in;
+	def_strm.total_in = 0;
 	
-	strm.next_out = cpage_out;
-	strm.total_out = 0;
+	def_strm.next_out = cpage_out;
+	def_strm.total_out = 0;
 
-	while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) {
-		strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE);
-		strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out);
+	while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) {
+		def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE);
+		def_strm.avail_in = min((unsigned)(*sourcelen-def_strm.total_in), def_strm.avail_out);
 		D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n",
-			  strm.avail_in, strm.avail_out));
-		ret = zlib_deflate(&strm, Z_PARTIAL_FLUSH);
+			  def_strm.avail_in, def_strm.avail_out));
+		ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH);
 		D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n", 
-			  strm.avail_in, strm.avail_out, strm.total_in, strm.total_out));
+			  def_strm.avail_in, def_strm.avail_out, def_strm.total_in, def_strm.total_out));
 		if (ret != Z_OK) {
 			D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret));
-			zlib_deflateEnd(&strm);
+			zlib_deflateEnd(&def_strm);
 			up(&deflate_sem);
 			return -1;
 		}
 	}
-	strm.avail_out += STREAM_END_SPACE;
-	strm.avail_in = 0;
-	ret = zlib_deflate(&strm, Z_FINISH);
-	zlib_deflateEnd(&strm);
-	up(&deflate_sem);
+	def_strm.avail_out += STREAM_END_SPACE;
+	def_strm.avail_in = 0;
+	ret = zlib_deflate(&def_strm, Z_FINISH);
+	zlib_deflateEnd(&def_strm);
+
 	if (ret != Z_STREAM_END) {
 		D1(printk(KERN_DEBUG "final deflate returned %d\n", ret));
-		return -1;
+		ret = -1;
+		goto out;
 	}
 
-	D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
-		  strm.total_in, strm.total_out));
+	if (def_strm.total_out >= def_strm.total_in) {
+		D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld; failing\n",
+			  def_strm.total_in, def_strm.total_out));
+		ret = -1;
+		goto out;
+	}
 
-	if (strm.total_out >= strm.total_in)
-		return -1;
+	D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n",
+		  def_strm.total_in, def_strm.total_out));
 
-	*dstlen = strm.total_out;
-	*sourcelen = strm.total_in;
-	return 0;
+	*dstlen = def_strm.total_out;
+	*sourcelen = def_strm.total_in;
+	ret = 0;
+ out:
+	up(&deflate_sem);
+	return ret;
 }
 
-void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
-		      __u32 srclen, __u32 destlen)
+void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
+		      uint32_t srclen, uint32_t destlen)
 {
-	z_stream strm;
 	int ret;
+	int wbits = MAX_WBITS;
 
 	down(&inflate_sem);
-	strm.workspace = inflate_workspace;
 
-	if (Z_OK != zlib_inflateInit(&strm)) {
+	inf_strm.next_in = data_in;
+	inf_strm.avail_in = srclen;
+	inf_strm.total_in = 0;
+	
+	inf_strm.next_out = cpage_out;
+	inf_strm.avail_out = destlen;
+	inf_strm.total_out = 0;
+
+	/* If it's deflate, and it's got no preset dictionary, then
+	   we can tell zlib to skip the adler32 check. */
+	if (srclen > 2 && !(data_in[1] & PRESET_DICT) &&
+	    ((data_in[0] & 0x0f) == Z_DEFLATED) &&
+	    !(((data_in[0]<<8) + data_in[1]) % 31)) {
+
+		D2(printk(KERN_DEBUG "inflate skipping adler32\n"));
+		wbits = -((data_in[0] >> 4) + 8);
+		inf_strm.next_in += 2;
+		inf_strm.avail_in -= 2;
+	} else {
+		/* Let this remain D1 for now -- it should never happen */
+		D1(printk(KERN_DEBUG "inflate not skipping adler32\n"));
+	}
+
+
+	if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) {
 		printk(KERN_WARNING "inflateInit failed\n");
 		up(&inflate_sem);
 		return;
 	}
-	strm.next_in = data_in;
-	strm.avail_in = srclen;
-	strm.total_in = 0;
-	
-	strm.next_out = cpage_out;
-	strm.avail_out = destlen;
-	strm.total_out = 0;
 
-	while((ret = zlib_inflate(&strm, Z_FINISH)) == Z_OK)
+	while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK)
 		;
 	if (ret != Z_STREAM_END) {
 		printk(KERN_NOTICE "inflate returned %d\n", ret);
 	}
-	zlib_inflateEnd(&strm);
+	zlib_inflateEnd(&inf_strm);
 	up(&inflate_sem);
 }
diff -Nur linux-2.4.30/fs/jffs2/crc32.c linux-2.4.30-linux-mips.org/fs/jffs2/crc32.c
--- linux-2.4.30/fs/jffs2/crc32.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/crc32.c	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,97 @@
+/*
+ *  COPYRIGHT (C) 1986 Gary S. Brown.  You may use this program, or
+ *  code or tables extracted from it, as desired without restriction.
+ *
+ *  First, the polynomial itself and its table of feedback terms.  The
+ *  polynomial is
+ *  X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0
+ *
+ *  Note that we take it "backwards" and put the highest-order term in
+ *  the lowest-order bit.  The X^32 term is "implied"; the LSB is the
+ *  X^31 term, etc.  The X^0 term (usually shown as "+1") results in
+ *  the MSB being 1
+ *
+ *  Note that the usual hardware shift register implementation, which
+ *  is what we're using (we're merely optimizing it by doing eight-bit
+ *  chunks at a time) shifts bits into the lowest-order term.  In our
+ *  implementation, that means shifting towards the right.  Why do we
+ *  do it this way?  Because the calculated CRC must be transmitted in
+ *  order from highest-order term to lowest-order term.  UARTs transmit
+ *  characters in order from LSB to MSB.  By storing the CRC this way
+ *  we hand it to the UART in the order low-byte to high-byte; the UART
+ *  sends each low-bit to hight-bit; and the result is transmission bit
+ *  by bit from highest- to lowest-order term without requiring any bit
+ *  shuffling on our part.  Reception works similarly
+ *
+ *  The feedback terms table consists of 256, 32-bit entries.  Notes
+ *
+ *      The table can be generated at runtime if desired; code to do so
+ *      is shown later.  It might not be obvious, but the feedback
+ *      terms simply represent the results of eight shift/xor opera
+ *      tions for all combinations of data and CRC register values
+ *
+ *      The values must be right-shifted by eight bits by the "updcrc
+ *      logic; the shift must be unsigned (bring in zeroes).  On some
+ *      hardware you could probably optimize the shift in assembler by
+ *      using byte-swap instructions
+ *      polynomial $edb88320
+ */
+
+/* $Id: crc32.c,v 1.4 2002/01/03 15:20:44 dwmw2 Exp $ */
+
+#include "crc32.h"
+
+const uint32_t crc32_table[256] = {
+	0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L,
+	0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L,
+	0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L,
+	0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL,
+	0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L,
+	0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L,
+	0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L,
+	0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL,
+	0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L,
+	0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL,
+	0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L,
+	0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L,
+	0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L,
+	0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL,
+	0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL,
+	0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L,
+	0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL,
+	0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L,
+	0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L,
+	0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L,
+	0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL,
+	0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L,
+	0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L,
+	0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL,
+	0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L,
+	0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L,
+	0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L,
+	0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L,
+	0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L,
+	0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL,
+	0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL,
+	0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L,
+	0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L,
+	0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL,
+	0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL,
+	0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L,
+	0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL,
+	0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L,
+	0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL,
+	0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L,
+	0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL,
+	0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L,
+	0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L,
+	0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL,
+	0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L,
+	0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L,
+	0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L,
+	0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L,
+	0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L,
+	0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L,
+	0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL,
+	0x2d02ef8dL
+};
diff -Nur linux-2.4.30/fs/jffs2/crc32.h linux-2.4.30-linux-mips.org/fs/jffs2/crc32.h
--- linux-2.4.30/fs/jffs2/crc32.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/crc32.h	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,21 @@
+#ifndef CRC32_H
+#define CRC32_H
+
+/* $Id: crc32.h,v 1.4 2002/01/03 15:20:44 dwmw2 Exp $ */
+
+#include <linux/types.h>
+
+extern const uint32_t crc32_table[256];
+
+/* Return a 32-bit CRC of the contents of the buffer. */
+
+static inline uint32_t 
+crc32(uint32_t val, const void *ss, int len)
+{
+	const unsigned char *s = ss;
+        while (--len >= 0)
+                val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8);
+        return val;
+}
+
+#endif
diff -Nur linux-2.4.30/fs/jffs2/dir.c linux-2.4.30-linux-mips.org/fs/jffs2/dir.c
--- linux-2.4.30/fs/jffs2/dir.c	2003-11-28 19:26:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/dir.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,84 +1,73 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: dir.c,v 1.45.2.8 2003/11/02 13:51:17 dwmw2 Exp $
+ * $Id: dir.c,v 1.82 2003/10/11 11:47:23 dwmw2 Exp $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
+#include <linux/sched.h>
 #include <linux/fs.h>
-#include <linux/mtd/compatmac.h> /* For completion */
+#include <linux/crc32.h>
 #include <linux/jffs2.h>
 #include <linux/jffs2_fs_i.h>
 #include <linux/jffs2_fs_sb.h>
+#include <linux/time.h>
 #include "nodelist.h"
-#include <linux/crc32.h>
+
+/* Urgh. Please tell me there's a nicer way of doing these. */
+#include <linux/version.h>
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
+typedef int mknod_arg_t;
+#define NAMEI_COMPAT(x) ((void *)x)
+#else
+typedef dev_t mknod_arg_t;
+#define NAMEI_COMPAT(x) (x)
+#endif
 
 static int jffs2_readdir (struct file *, void *, filldir_t);
 
-static int jffs2_create (struct inode *,struct dentry *,int);
-static struct dentry *jffs2_lookup (struct inode *,struct dentry *);
+static int jffs2_create (struct inode *,struct dentry *,int,
+			 struct nameidata *);
+static struct dentry *jffs2_lookup (struct inode *,struct dentry *,
+				    struct nameidata *);
 static int jffs2_link (struct dentry *,struct inode *,struct dentry *);
 static int jffs2_unlink (struct inode *,struct dentry *);
 static int jffs2_symlink (struct inode *,struct dentry *,const char *);
 static int jffs2_mkdir (struct inode *,struct dentry *,int);
 static int jffs2_rmdir (struct inode *,struct dentry *);
-static int jffs2_mknod (struct inode *,struct dentry *,int,int);
+static int jffs2_mknod (struct inode *,struct dentry *,int,mknod_arg_t);
 static int jffs2_rename (struct inode *, struct dentry *,
                         struct inode *, struct dentry *);
 
 struct file_operations jffs2_dir_operations =
 {
-	read:		generic_read_dir,
-	readdir:	jffs2_readdir,
-	ioctl:		jffs2_ioctl,
-	fsync:		jffs2_null_fsync
+	.read =		generic_read_dir,
+	.readdir =	jffs2_readdir,
+	.ioctl =	jffs2_ioctl,
+	.fsync =	jffs2_fsync
 };
 
 
 struct inode_operations jffs2_dir_inode_operations =
 {
-	create:		jffs2_create,
-	lookup:		jffs2_lookup,
-	link:		jffs2_link,
-	unlink:		jffs2_unlink,
-	symlink:	jffs2_symlink,
-	mkdir:		jffs2_mkdir,
-	rmdir:		jffs2_rmdir,
-	mknod:		jffs2_mknod,
-	rename:		jffs2_rename,
-	setattr:	jffs2_setattr,
+	.create =	NAMEI_COMPAT(jffs2_create),
+	.lookup =	NAMEI_COMPAT(jffs2_lookup),
+	.link =		jffs2_link,
+	.unlink =	jffs2_unlink,
+	.symlink =	jffs2_symlink,
+	.mkdir =	jffs2_mkdir,
+	.rmdir =	jffs2_rmdir,
+	.mknod =	jffs2_mknod,
+	.rename =	jffs2_rename,
+	.setattr =	jffs2_setattr,
 };
 
 /***********************************************************************/
@@ -88,12 +77,13 @@
    and we use the same hash function as the dentries. Makes this 
    nice and simple
 */
-static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target)
+static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target,
+				   struct nameidata *nd)
 {
 	struct jffs2_inode_info *dir_f;
 	struct jffs2_sb_info *c;
 	struct jffs2_full_dirent *fd = NULL, *fd_list;
-	__u32 ino = 0;
+	uint32_t ino = 0;
 	struct inode *inode = NULL;
 
 	D1(printk(KERN_DEBUG "jffs2_lookup()\n"));
@@ -153,8 +143,9 @@
 		offset++;
 	}
 	if (offset == 1) {
-		D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", filp->f_dentry->d_parent->d_inode->i_ino));
-		if (filldir(dirent, "..", 2, 1, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0)
+		unsigned long pino = parent_ino(filp->f_dentry);
+		D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", pino));
+		if (filldir(dirent, "..", 2, 1, pino, DT_DIR) < 0)
 			goto out;
 		offset++;
 	}
@@ -188,18 +179,14 @@
 
 /***********************************************************************/
 
-static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode)
+
+static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode,
+			struct nameidata *nd)
 {
+	struct jffs2_raw_inode *ri;
 	struct jffs2_inode_info *f, *dir_f;
 	struct jffs2_sb_info *c;
 	struct inode *inode;
-	struct jffs2_raw_inode *ri;
-	struct jffs2_raw_dirent *rd;
-	struct jffs2_full_dnode *fn;
-	struct jffs2_full_dirent *fd;
-	int namelen;
-	__u32 alloclen, phys_ofs;
-	__u32 writtenlen;
 	int ret;
 
 	ri = jffs2_alloc_raw_inode();
@@ -210,23 +197,11 @@
 
 	D1(printk(KERN_DEBUG "jffs2_create()\n"));
 
-	/* Try to reserve enough space for both node and dirent. 
-	 * Just the node will do for now, though 
-	 */
-	namelen = dentry->d_name.len;
-	ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
-	D1(printk(KERN_DEBUG "jffs2_create(): reserved 0x%x bytes\n", alloclen));
-	if (ret) {
-		jffs2_free_raw_inode(ri);
-		return ret;
-	}
-
 	inode = jffs2_new_inode(dir_i, mode, ri);
 
 	if (IS_ERR(inode)) {
 		D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n"));
 		jffs2_free_raw_inode(ri);
-		jffs2_complete_reservation(c);
 		return PTR_ERR(inode);
 	}
 
@@ -236,93 +211,22 @@
 	inode->i_mapping->nrpages = 0;
 
 	f = JFFS2_INODE_INFO(inode);
-
-	ri->data_crc = 0;
-	ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
-
-	fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen);
-	D1(printk(KERN_DEBUG "jffs2_create created file with mode 0x%x\n", ri->mode));
-	jffs2_free_raw_inode(ri);
-
-	if (IS_ERR(fn)) {
-		D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
-		/* Eeek. Wave bye bye */
-		up(&f->sem);
-		jffs2_complete_reservation(c);
-		jffs2_clear_inode(inode);
-		return PTR_ERR(fn);
-	}
-	/* No data here. Only a metadata node, which will be 
-	   obsoleted by the first data write
-	*/
-	f->metadata = fn;
-
-	/* Work out where to put the dirent node now. */
-	writtenlen = PAD(writtenlen);
-	phys_ofs += writtenlen;
-	alloclen -= writtenlen;
-	up(&f->sem);
-
-	if (alloclen < sizeof(*rd)+namelen) {
-		/* Not enough space left in this chunk. Get some more */
-		jffs2_complete_reservation(c);
-		ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
-		
-		if (ret) {
-			/* Eep. */
-			D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
-			jffs2_clear_inode(inode);
-			return ret;
-		}
-	}
-
-	rd = jffs2_alloc_raw_dirent();
-	if (!rd) {
-		/* Argh. Now we treat it like a normal delete */
-		jffs2_complete_reservation(c);
-		jffs2_clear_inode(inode);
-		return -ENOMEM;
-	}
-
 	dir_f = JFFS2_INODE_INFO(dir_i);
-	down(&dir_f->sem);
-
-	rd->magic = JFFS2_MAGIC_BITMASK;
-	rd->nodetype = JFFS2_NODETYPE_DIRENT;
-	rd->totlen = sizeof(*rd) + namelen;
-	rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
-
-	rd->pino = dir_i->i_ino;
-	rd->version = ++dir_f->highest_version;
-	rd->ino = inode->i_ino;
-	rd->mctime = CURRENT_TIME;
-	rd->nsize = namelen;
-	rd->type = DT_REG;
-	rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
-	rd->name_crc = crc32(0, dentry->d_name.name, namelen);
 
-	fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
+	ret = jffs2_do_create(c, dir_f, f, ri, 
+			      dentry->d_name.name, dentry->d_name.len);
 
-	jffs2_complete_reservation(c);
-	
-	if (IS_ERR(fd)) {
-		/* dirent failed to write. Delete the inode normally 
-		   as if it were the final unlink() */
-		jffs2_free_raw_dirent(rd);
-		up(&dir_f->sem);
+	if (ret) {
 		jffs2_clear_inode(inode);
-		return PTR_ERR(fd);
+		make_bad_inode(inode);
+		iput(inode);
+		jffs2_free_raw_inode(ri);
+		return ret;
 	}
 
-	dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
-
-	jffs2_free_raw_dirent(rd);
-
-	/* Link the fd into the inode's list, obsoleting an old
-	   one if necessary. */
-	jffs2_add_fd_to_list(c, fd, &dir_f->dents);
-	up(&dir_f->sem);
+	dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime));
 
+	jffs2_free_raw_inode(ri);
 	d_instantiate(dentry, inode);
 
 	D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n",
@@ -332,173 +236,48 @@
 
 /***********************************************************************/
 
-static int jffs2_do_unlink(struct inode *dir_i, struct dentry *dentry, int rename)
-{
-	struct jffs2_inode_info *dir_f, *f;
-	struct jffs2_sb_info *c;
-	struct jffs2_raw_dirent *rd;
-	struct jffs2_full_dirent *fd;
-	__u32 alloclen, phys_ofs;
-	int ret;
-
-	c = JFFS2_SB_INFO(dir_i->i_sb);
-
-	rd = jffs2_alloc_raw_dirent();
-	if (!rd)
-		return -ENOMEM;
-
-	ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_DELETION);
-	if (ret) {
-		jffs2_free_raw_dirent(rd);
-		return ret;
-	}
-
-	dir_f = JFFS2_INODE_INFO(dir_i);
-	down(&dir_f->sem);
-
-	/* Build a deletion node */
-	rd->magic = JFFS2_MAGIC_BITMASK;
-	rd->nodetype = JFFS2_NODETYPE_DIRENT;
-	rd->totlen = sizeof(*rd) + dentry->d_name.len;
-	rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
-
-	rd->pino = dir_i->i_ino;
-	rd->version = ++dir_f->highest_version;
-	rd->ino = 0;
-	rd->mctime = CURRENT_TIME;
-	rd->nsize = dentry->d_name.len;
-	rd->type = DT_UNKNOWN;
-	rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
-	rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len);
-
-	fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL);
-	
-	jffs2_complete_reservation(c);
-	jffs2_free_raw_dirent(rd);
-
-	if (IS_ERR(fd)) {
-		up(&dir_f->sem);
-		return PTR_ERR(fd);
-	}
-
-	/* File it. This will mark the old one obsolete. */
-	jffs2_add_fd_to_list(c, fd, &dir_f->dents);
-	up(&dir_f->sem);
-	
-	if (!rename) {
-		f = JFFS2_INODE_INFO(dentry->d_inode);
-		down(&f->sem);
-
-		while (f->dents) {
-			/* There can be only deleted ones */
-			fd = f->dents;
-			
-			f->dents = fd->next;
-			
-			if (fd->ino) {
-				printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n",
-				       f->inocache->ino, fd->name, fd->ino);
-			} else {
-				D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, f->inocache->ino));
-			}
-			jffs2_mark_node_obsolete(c, fd->raw);
-			jffs2_free_full_dirent(fd);
-		}
-		/* Don't oops on unlinking a bad inode */
-		if (f->inocache)
-			f->inocache->nlink--;
-		dentry->d_inode->i_nlink--;
-		up(&f->sem);
-	}
-
-	return 0;
-}
 
 static int jffs2_unlink(struct inode *dir_i, struct dentry *dentry)
 {
-	return jffs2_do_unlink(dir_i, dentry, 0);
-}
-/***********************************************************************/
-
-static int jffs2_do_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry, int rename)
-{
-	struct jffs2_inode_info *dir_f, *f;
-	struct jffs2_sb_info *c;
-	struct jffs2_raw_dirent *rd;
-	struct jffs2_full_dirent *fd;
-	__u32 alloclen, phys_ofs;
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(dir_i->i_sb);
+	struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
+	struct jffs2_inode_info *dead_f = JFFS2_INODE_INFO(dentry->d_inode);
 	int ret;
 
-	c = JFFS2_SB_INFO(dir_i->i_sb);
-
-	rd = jffs2_alloc_raw_dirent();
-	if (!rd)
-		return -ENOMEM;
-
-	ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_NORMAL);
-	if (ret) {
-		jffs2_free_raw_dirent(rd);
-		return ret;
-	}
-	
-	dir_f = JFFS2_INODE_INFO(dir_i);
-	down(&dir_f->sem);
-
-	/* Build a deletion node */
-	rd->magic = JFFS2_MAGIC_BITMASK;
-	rd->nodetype = JFFS2_NODETYPE_DIRENT;
-	rd->totlen = sizeof(*rd) + dentry->d_name.len;
-	rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
-
-	rd->pino = dir_i->i_ino;
-	rd->version = ++dir_f->highest_version;
-	rd->ino = old_dentry->d_inode->i_ino;
-	rd->mctime = CURRENT_TIME;
-	rd->nsize = dentry->d_name.len;
-
-	/* XXX: This is ugly. */
-	rd->type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
-	if (!rd->type) rd->type = DT_REG;
-
-	rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
-	rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len);
-
-	fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL);
-	
-	jffs2_complete_reservation(c);
-	jffs2_free_raw_dirent(rd);
-
-	if (IS_ERR(fd)) {
-		up(&dir_f->sem);
-		return PTR_ERR(fd);
-	}
-
-	/* File it. This will mark the old one obsolete. */
-	jffs2_add_fd_to_list(c, fd, &dir_f->dents);
-	up(&dir_f->sem);
-
-	if (!rename) {
-		f = JFFS2_INODE_INFO(old_dentry->d_inode);
-		down(&f->sem);
-		old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
-		up(&f->sem);
-	}
-	return 0;
+	ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name, 
+			       dentry->d_name.len, dead_f);
+	if (dead_f->inocache)
+		dentry->d_inode->i_nlink = dead_f->inocache->nlink;
+	return ret;
 }
+/***********************************************************************/
+
 
 static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry)
 {
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dentry->d_inode->i_sb);
+	struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
+	struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i);
 	int ret;
+	uint8_t type;
 
-	/* Can't link a bad inode. */
-	if (!JFFS2_INODE_INFO(old_dentry->d_inode)->inocache)
+	/* Don't let people make hard links to bad inodes. */
+	if (!f->inocache)
 		return -EIO;
 
 	if (S_ISDIR(old_dentry->d_inode->i_mode))
 		return -EPERM;
 
-	ret = jffs2_do_link(old_dentry, dir_i, dentry, 0);
+	/* XXX: This is ugly */
+	type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
+	if (!type) type = DT_REG;
+
+	ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len);
+
 	if (!ret) {
+		down(&f->sem);
+		old_dentry->d_inode->i_nlink = ++f->inocache->nlink;
+		up(&f->sem);
 		d_instantiate(dentry, old_dentry->d_inode);
 		atomic_inc(&old_dentry->d_inode->i_count);
 	}
@@ -517,8 +296,7 @@
 	struct jffs2_full_dnode *fn;
 	struct jffs2_full_dirent *fd;
 	int namelen;
-	__u32 alloclen, phys_ofs;
-	__u32 writtenlen;
+	uint32_t alloclen, phys_ofs;
 	int ret;
 
 	/* FIXME: If you care. We'd need to use frags for the target
@@ -556,15 +334,16 @@
 
 	f = JFFS2_INODE_INFO(inode);
 
-	inode->i_size = ri->isize = ri->dsize = ri->csize = strlen(target);
-	ri->totlen = sizeof(*ri) + ri->dsize;
-	ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+	inode->i_size = strlen(target);
+	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 = crc32(0, target, strlen(target));
-	ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+	ri->data_crc = cpu_to_je32(crc32(0, target, strlen(target)));
+	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 	
-	fn = jffs2_write_dnode(inode, ri, target, strlen(target), phys_ofs, &writtenlen);
+	fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, ALLOC_NORMAL);
 
 	jffs2_free_raw_inode(ri);
 
@@ -581,20 +360,12 @@
 	f->metadata = fn;
 	up(&f->sem);
 
-	/* Work out where to put the dirent node now. */
-	writtenlen = (writtenlen+3)&~3;
-	phys_ofs += writtenlen;
-	alloclen -= writtenlen;
-
-	if (alloclen < sizeof(*rd)+namelen) {
-		/* Not enough space left in this chunk. Get some more */
-		jffs2_complete_reservation(c);
-		ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
-		if (ret) {
-			/* Eep. */
-			jffs2_clear_inode(inode);
-			return ret;
-		}
+	jffs2_complete_reservation(c);
+	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+	if (ret) {
+		/* Eep. */
+		jffs2_clear_inode(inode);
+		return ret;
 	}
 
 	rd = jffs2_alloc_raw_dirent();
@@ -608,41 +379,42 @@
 	dir_f = JFFS2_INODE_INFO(dir_i);
 	down(&dir_f->sem);
 
-	rd->magic = JFFS2_MAGIC_BITMASK;
-	rd->nodetype = JFFS2_NODETYPE_DIRENT;
-	rd->totlen = sizeof(*rd) + namelen;
-	rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
-
-	rd->pino = dir_i->i_ino;
-	rd->version = ++dir_f->highest_version;
-	rd->ino = inode->i_ino;
-	rd->mctime = CURRENT_TIME;
+	rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+	rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
+	rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
+
+	rd->pino = cpu_to_je32(dir_i->i_ino);
+	rd->version = cpu_to_je32(++dir_f->highest_version);
+	rd->ino = cpu_to_je32(inode->i_ino);
+	rd->mctime = cpu_to_je32(get_seconds());
 	rd->nsize = namelen;
 	rd->type = DT_LNK;
-	rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
-	rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+	rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
+
+	fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
 
-	fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
-	
-	jffs2_complete_reservation(c);
-	
 	if (IS_ERR(fd)) {
 		/* dirent failed to write. Delete the inode normally 
 		   as if it were the final unlink() */
+		jffs2_complete_reservation(c);
 		jffs2_free_raw_dirent(rd);
 		up(&dir_f->sem);
 		jffs2_clear_inode(inode);
 		return PTR_ERR(fd);
 	}
 
-	dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
+	dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
 
 	jffs2_free_raw_dirent(rd);
 
 	/* Link the fd into the inode's list, obsoleting an old
 	   one if necessary. */
 	jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+
 	up(&dir_f->sem);
+	jffs2_complete_reservation(c);
 
 	d_instantiate(dentry, inode);
 	return 0;
@@ -659,8 +431,7 @@
 	struct jffs2_full_dnode *fn;
 	struct jffs2_full_dirent *fd;
 	int namelen;
-	__u32 alloclen, phys_ofs;
-	__u32 writtenlen;
+	uint32_t alloclen, phys_ofs;
 	int ret;
 
 	mode |= S_IFDIR;
@@ -692,13 +463,15 @@
 
 	inode->i_op = &jffs2_dir_inode_operations;
 	inode->i_fop = &jffs2_dir_operations;
+	/* Directories get nlink 2 at start */
+	inode->i_nlink = 2;
 
 	f = JFFS2_INODE_INFO(inode);
 
-	ri->data_crc = 0;
-	ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+	ri->data_crc = cpu_to_je32(0);
+	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 	
-	fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen);
+	fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
 
 	jffs2_free_raw_inode(ri);
 
@@ -715,20 +488,12 @@
 	f->metadata = fn;
 	up(&f->sem);
 
-	/* Work out where to put the dirent node now. */
-	writtenlen = PAD(writtenlen);
-	phys_ofs += writtenlen;
-	alloclen -= writtenlen;
-
-	if (alloclen < sizeof(*rd)+namelen) {
-		/* Not enough space left in this chunk. Get some more */
-		jffs2_complete_reservation(c);
-		ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
-		if (ret) {
-			/* Eep. */
-			jffs2_clear_inode(inode);
-			return ret;
-		}
+	jffs2_complete_reservation(c);
+	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+	if (ret) {
+		/* Eep. */
+		jffs2_clear_inode(inode);
+		return ret;
 	}
 	
 	rd = jffs2_alloc_raw_dirent();
@@ -742,41 +507,43 @@
 	dir_f = JFFS2_INODE_INFO(dir_i);
 	down(&dir_f->sem);
 
-	rd->magic = JFFS2_MAGIC_BITMASK;
-	rd->nodetype = JFFS2_NODETYPE_DIRENT;
-	rd->totlen = sizeof(*rd) + namelen;
-	rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
-
-	rd->pino = dir_i->i_ino;
-	rd->version = ++dir_f->highest_version;
-	rd->ino = inode->i_ino;
-	rd->mctime = CURRENT_TIME;
+	rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+	rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
+	rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
+
+	rd->pino = cpu_to_je32(dir_i->i_ino);
+	rd->version = cpu_to_je32(++dir_f->highest_version);
+	rd->ino = cpu_to_je32(inode->i_ino);
+	rd->mctime = cpu_to_je32(get_seconds());
 	rd->nsize = namelen;
 	rd->type = DT_DIR;
-	rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
-	rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+	rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
 
-	fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
-	
-	jffs2_complete_reservation(c);
+	fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
 	
 	if (IS_ERR(fd)) {
 		/* dirent failed to write. Delete the inode normally 
 		   as if it were the final unlink() */
+		jffs2_complete_reservation(c);
 		jffs2_free_raw_dirent(rd);
 		up(&dir_f->sem);
 		jffs2_clear_inode(inode);
 		return PTR_ERR(fd);
 	}
 
-	dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
+	dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
+	dir_i->i_nlink++;
 
 	jffs2_free_raw_dirent(rd);
 
 	/* Link the fd into the inode's list, obsoleting an old
 	   one if necessary. */
 	jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+
 	up(&dir_f->sem);
+	jffs2_complete_reservation(c);
 
 	d_instantiate(dentry, inode);
 	return 0;
@@ -786,15 +553,19 @@
 {
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
 	struct jffs2_full_dirent *fd;
+	int ret;
 
 	for (fd = f->dents ; fd; fd = fd->next) {
 		if (fd->ino)
 			return -ENOTEMPTY;
 	}
-	return jffs2_unlink(dir_i, dentry);
+	ret = jffs2_unlink(dir_i, dentry);
+	if (!ret)
+		dir_i->i_nlink--;
+	return ret;
 }
 
-static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, int rdev)
+static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mknod_arg_t rdev)
 {
 	struct jffs2_inode_info *f, *dir_f;
 	struct jffs2_sb_info *c;
@@ -804,12 +575,14 @@
 	struct jffs2_full_dnode *fn;
 	struct jffs2_full_dirent *fd;
 	int namelen;
-	unsigned short dev;
+	jint16_t dev;
 	int devlen = 0;
-	__u32 alloclen, phys_ofs;
-	__u32 writtenlen;
+	uint32_t alloclen, phys_ofs;
 	int ret;
 
+	if (!old_valid_dev(rdev))
+		return -EINVAL;
+
 	ri = jffs2_alloc_raw_inode();
 	if (!ri)
 		return -ENOMEM;
@@ -817,7 +590,7 @@
 	c = JFFS2_SB_INFO(dir_i->i_sb);
 	
 	if (S_ISBLK(mode) || S_ISCHR(mode)) {
-		dev = (MAJOR(to_kdev_t(rdev)) << 8) | MINOR(to_kdev_t(rdev));
+		dev = cpu_to_je16(old_encode_dev(rdev));
 		devlen = sizeof(dev);
 	}
 	
@@ -844,15 +617,15 @@
 
 	f = JFFS2_INODE_INFO(inode);
 
-	ri->dsize = ri->csize = devlen;
-	ri->totlen = sizeof(*ri) + ri->csize;
-	ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
+	ri->dsize = ri->csize = cpu_to_je32(devlen);
+	ri->totlen = cpu_to_je32(sizeof(*ri) + devlen);
+	ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
 
 	ri->compr = JFFS2_COMPR_NONE;
-	ri->data_crc = crc32(0, &dev, devlen);
-	ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
+	ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen));
+	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
 	
-	fn = jffs2_write_dnode(inode, ri, (char *)&dev, devlen, phys_ofs, &writtenlen);
+	fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, ALLOC_NORMAL);
 
 	jffs2_free_raw_inode(ri);
 
@@ -869,20 +642,12 @@
 	f->metadata = fn;
 	up(&f->sem);
 
-	/* Work out where to put the dirent node now. */
-	writtenlen = (writtenlen+3)&~3;
-	phys_ofs += writtenlen;
-	alloclen -= writtenlen;
-
-	if (alloclen < sizeof(*rd)+namelen) {
-		/* Not enough space left in this chunk. Get some more */
-		jffs2_complete_reservation(c);
-		ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
-		if (ret) {
-			/* Eep. */
-			jffs2_clear_inode(inode);
-			return ret;
-		}
+	jffs2_complete_reservation(c);
+	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+	if (ret) {
+		/* Eep. */
+		jffs2_clear_inode(inode);
+		return ret;
 	}
 
 	rd = jffs2_alloc_raw_dirent();
@@ -896,44 +661,45 @@
 	dir_f = JFFS2_INODE_INFO(dir_i);
 	down(&dir_f->sem);
 
-	rd->magic = JFFS2_MAGIC_BITMASK;
-	rd->nodetype = JFFS2_NODETYPE_DIRENT;
-	rd->totlen = sizeof(*rd) + namelen;
-	rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4);
-
-	rd->pino = dir_i->i_ino;
-	rd->version = ++dir_f->highest_version;
-	rd->ino = inode->i_ino;
-	rd->mctime = CURRENT_TIME;
+	rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+	rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
+	rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
+
+	rd->pino = cpu_to_je32(dir_i->i_ino);
+	rd->version = cpu_to_je32(++dir_f->highest_version);
+	rd->ino = cpu_to_je32(inode->i_ino);
+	rd->mctime = cpu_to_je32(get_seconds());
 	rd->nsize = namelen;
 
 	/* XXX: This is ugly. */
 	rd->type = (mode & S_IFMT) >> 12;
 
-	rd->node_crc = crc32(0, rd, sizeof(*rd)-8);
-	rd->name_crc = crc32(0, dentry->d_name.name, namelen);
+	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+	rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen));
 
-	fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
-	
-	jffs2_complete_reservation(c);
+	fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL);
 	
 	if (IS_ERR(fd)) {
 		/* dirent failed to write. Delete the inode normally 
 		   as if it were the final unlink() */
+		jffs2_complete_reservation(c);
 		jffs2_free_raw_dirent(rd);
 		up(&dir_f->sem);
 		jffs2_clear_inode(inode);
 		return PTR_ERR(fd);
 	}
 
-	dir_i->i_mtime = dir_i->i_ctime = rd->mctime;
+	dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime));
 
 	jffs2_free_raw_dirent(rd);
 
 	/* Link the fd into the inode's list, obsoleting an old
 	   one if necessary. */
 	jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+
 	up(&dir_f->sem);
+	jffs2_complete_reservation(c);
 
 	d_instantiate(dentry, inode);
 
@@ -944,7 +710,9 @@
                         struct inode *new_dir_i, struct dentry *new_dentry)
 {
 	int ret;
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb);
 	struct jffs2_inode_info *victim_f = NULL;
+	uint8_t type;
 
 	/* The VFS will check for us and prevent trying to rename a 
 	 * file over a directory and vice versa, but if it's a directory,
@@ -973,7 +741,15 @@
 	*/
 
 	/* Make a hard link */
-	ret = jffs2_do_link(old_dentry, new_dir_i, new_dentry, 1);
+	
+	/* XXX: This is ugly */
+	type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12;
+	if (!type) type = DT_REG;
+
+	ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i), 
+			    old_dentry->d_inode->i_ino, type,
+			    new_dentry->d_name.name, new_dentry->d_name.len);
+
 	if (ret)
 		return ret;
 
@@ -989,22 +765,36 @@
 		}
 	}
 
+	/* If it was a directory we moved, and there was no victim, 
+	   increase i_nlink on its new parent */
+	if (S_ISDIR(old_dentry->d_inode->i_mode) && !victim_f)
+		new_dir_i->i_nlink++;
+
 	/* Unlink the original */
-	ret = jffs2_do_unlink(old_dir_i, old_dentry, 1);
-	
+	ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), 
+		      old_dentry->d_name.name, old_dentry->d_name.len, NULL);
+
+	/* We don't touch inode->i_nlink */
+
 	if (ret) {
 		/* Oh shit. We really ought to make a single node which can do both atomically */
 		struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode);
 		down(&f->sem);
+		old_dentry->d_inode->i_nlink++;
 		if (f->inocache)
-			old_dentry->d_inode->i_nlink = f->inocache->nlink++;
+			f->inocache->nlink++;
 		up(&f->sem);
-		       
+
 		printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret);
 		/* Might as well let the VFS know */
 		d_instantiate(new_dentry, old_dentry->d_inode);
 		atomic_inc(&old_dentry->d_inode->i_count);
+		return ret;
 	}
-	return ret;
+
+	if (S_ISDIR(old_dentry->d_inode->i_mode))
+		old_dir_i->i_nlink--;
+
+	return 0;
 }
 
diff -Nur linux-2.4.30/fs/jffs2/erase.c linux-2.4.30-linux-mips.org/fs/jffs2/erase.c
--- linux-2.4.30/fs/jffs2/erase.c	2003-11-28 19:26:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/erase.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,68 +1,60 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: erase.c,v 1.24.2.1 2003/11/02 13:51:17 dwmw2 Exp $
+ * $Id: erase.c,v 1.58 2003/11/26 13:02:46 dwmw2 Exp $
  *
  */
+
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/mtd/mtd.h>
-#include <linux/jffs2.h>
-#include <linux/interrupt.h>
-#include "nodelist.h"
+#include <linux/compiler.h>
 #include <linux/crc32.h>
+#include <linux/sched.h>
+#include <linux/pagemap.h>
+#include "nodelist.h"
 
 struct erase_priv_struct {
 	struct jffs2_eraseblock *jeb;
 	struct jffs2_sb_info *c;
 };
       
+#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_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);
 
 void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
-	struct erase_info *instr;
 	int ret;
+#ifdef __ECOS
+       ret = jffs2_flash_erase(c, jeb);
+       if (!ret) {
+               jffs2_erase_succeeded(c, jeb);
+               return;
+       }
+#else /* Linux */
+	struct erase_info *instr;
 
 	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");
-		spin_lock_bh(&c->erase_completion_lock);
+		spin_lock(&c->erase_completion_lock);
 		list_del(&jeb->list);
 		list_add(&jeb->list, &c->erase_pending_list);
 		c->erasing_size -= c->sector_size;
-		spin_unlock_bh(&c->erase_completion_lock);
+		c->dirty_size += c->sector_size;
+		jeb->dirty_size = c->sector_size;
+		spin_unlock(&c->erase_completion_lock);
 		return;
 	}
 
@@ -77,19 +69,27 @@
 	((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) {
+	if (!ret)
 		return;
-	}
+
+	kfree(instr);
+#endif /* __ECOS */
+
 	if (ret == -ENOMEM || ret == -EAGAIN) {
 		/* Erase failed immediately. Refile it on the list */
 		D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret));
-		spin_lock_bh(&c->erase_completion_lock);
+		spin_lock(&c->erase_completion_lock);
 		list_del(&jeb->list);
 		list_add(&jeb->list, &c->erase_pending_list);
 		c->erasing_size -= c->sector_size;
-		spin_unlock_bh(&c->erase_completion_lock);
-		kfree(instr);
+		c->dirty_size += c->sector_size;
+		jeb->dirty_size = c->sector_size;
+		spin_unlock(&c->erase_completion_lock);
 		return;
 	}
 
@@ -97,74 +97,101 @@
 		printk(KERN_WARNING "Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n", jeb->offset);
 	else
 		printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret);
-	spin_lock_bh(&c->erase_completion_lock);
-	list_del(&jeb->list);
-	list_add(&jeb->list, &c->bad_list);
-	c->nr_erasing_blocks--;
-	c->bad_size += c->sector_size;
-	c->erasing_size -= c->sector_size;
-	spin_unlock_bh(&c->erase_completion_lock);
-	wake_up(&c->erase_wait);
-	kfree(instr);
+
+	jffs2_erase_failed(c, jeb);
 }
 
-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c)
+void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count)
 {
 	struct jffs2_eraseblock *jeb;
 
-	spin_lock_bh(&c->erase_completion_lock);
-	while (!list_empty(&c->erase_pending_list)) {
+	down(&c->erase_free_sem);
 
-		jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
+	spin_lock(&c->erase_completion_lock);
 
-		D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
+	while (!list_empty(&c->erase_complete_list) ||
+	       !list_empty(&c->erase_pending_list)) {
+
+		if (!list_empty(&c->erase_complete_list)) {
+			jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
+			list_del(&jeb->list);
+			spin_unlock(&c->erase_completion_lock);
+			jffs2_mark_erased_block(c, jeb);
+
+			if (!--count) {
+				D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n"));
+				goto done;
+			}
+
+		} else if (!list_empty(&c->erase_pending_list)) {
+			jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list);
+			D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset));
+			list_del(&jeb->list);
+			c->erasing_size += c->sector_size;
+			c->wasted_size -= jeb->wasted_size;
+			c->free_size -= jeb->free_size;
+			c->used_size -= jeb->used_size;
+			c->dirty_size -= jeb->dirty_size;
+			jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
+			jffs2_free_all_node_refs(c, jeb);
+			list_add(&jeb->list, &c->erasing_list);
+			spin_unlock(&c->erase_completion_lock);
+
+			jffs2_erase_block(c, jeb);
+
+		} else {
+			BUG();
+		}
 
-		list_del(&jeb->list);
-		c->erasing_size += c->sector_size;
-		c->free_size -= jeb->free_size;
-		c->used_size -= jeb->used_size;
-		c->dirty_size -= jeb->dirty_size;
-		jeb->used_size = jeb->dirty_size = jeb->free_size = 0;
-		jffs2_free_all_node_refs(c, jeb);
-		list_add(&jeb->list, &c->erasing_list);
-		spin_unlock_bh(&c->erase_completion_lock);
-		
-		jffs2_erase_block(c, jeb);
 		/* Be nice */
-		if (current->need_resched)
-			schedule();
-		spin_lock_bh(&c->erase_completion_lock);
+		cond_resched();
+		spin_lock(&c->erase_completion_lock);
 	}
-	spin_unlock_bh(&c->erase_completion_lock);
+
+	spin_unlock(&c->erase_completion_lock);
+ done:
 	D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n"));
+
+	up(&c->erase_free_sem);
+}
+
+static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset));
+	spin_lock(&c->erase_completion_lock);
+	list_del(&jeb->list);
+	list_add_tail(&jeb->list, &c->erase_complete_list);
+	spin_unlock(&c->erase_completion_lock);
+	/* Ensure that kupdated calls us again to mark them clean */
+	jffs2_erase_pending_trigger(c);
 }
 
+static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	 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
 static void jffs2_erase_callback(struct erase_info *instr)
 {
 	struct erase_priv_struct *priv = (void *)instr->priv;
 
 	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);
-		spin_lock(&priv->c->erase_completion_lock);
-		priv->c->erasing_size -= priv->c->sector_size;
-		priv->c->bad_size += priv->c->sector_size;
-		list_del(&priv->jeb->list);
-		list_add(&priv->jeb->list, &priv->c->bad_list);
-		priv->c->nr_erasing_blocks--;
-		spin_unlock(&priv->c->erase_completion_lock);
-		wake_up(&priv->c->erase_wait);
+		jffs2_erase_failed(priv->c, priv->jeb);
 	} else {
-		D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", instr->addr));
-		spin_lock(&priv->c->erase_completion_lock);
-		list_del(&priv->jeb->list);
-		list_add_tail(&priv->jeb->list, &priv->c->erase_complete_list);
-		spin_unlock(&priv->c->erase_completion_lock);
+		jffs2_erase_succeeded(priv->c, priv->jeb);
 	}	
-	/* Make sure someone picks up the block off the erase_complete list */
-	OFNI_BS_2SFFJ(priv->c)->s_dirt = 1;
 	kfree(instr);
 }
+#endif /* !__ECOS */
 
 /* Hmmm. Maybe we should accept the extra space it takes and make
    this a standard doubly-linked list? */
@@ -221,7 +248,7 @@
 		this = ic->nodes;
 	   
 		while(this) {
-			printk( "0x%08x(%d)->", this->flash_offset & ~3, this->flash_offset &3);
+			printk( "0x%08x(%d)->", ref_offset(this), ref_flags(this));
 			if (++i == 5) {
 				printk("\n" KERN_DEBUG);
 				i=0;
@@ -256,118 +283,134 @@
 	jeb->last_node = NULL;
 }
 
-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
-{
-	OFNI_BS_2SFFJ(c)->s_dirt = 1;
-}
-
-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c)
+static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
-	static struct jffs2_unknown_node marker = {JFFS2_MAGIC_BITMASK, JFFS2_NODETYPE_CLEANMARKER, sizeof(struct jffs2_unknown_node)};
-	struct jffs2_eraseblock *jeb;
-	struct jffs2_raw_node_ref *marker_ref;
+	struct jffs2_raw_node_ref *marker_ref = NULL;
 	unsigned char *ebuf;
-	ssize_t retlen;
+	size_t retlen;
 	int ret;
 
-	marker.hdr_crc = crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4);
-
-	spin_lock_bh(&c->erase_completion_lock);
-	while (!list_empty(&c->erase_complete_list)) {
-		jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list);
-		list_del(&jeb->list);
-		spin_unlock_bh(&c->erase_completion_lock);
-
+	if (!jffs2_cleanmarker_oob(c)) {
 		marker_ref = jffs2_alloc_raw_node_ref();
 		if (!marker_ref) {
 			printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n");
-			/* Come back later */
+			/* Stick it back on the list from whence it came and come back later */
 			jffs2_erase_pending_trigger(c);
+			spin_lock(&c->erase_completion_lock);
+			list_add(&jeb->list, &c->erase_complete_list);
+			spin_unlock(&c->erase_completion_lock);
 			return;
 		}
+	}
+	ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
+	if (!ebuf) {
+		printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset);
+	} else {
+		uint32_t ofs = jeb->offset;
 
-		ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL);
-		if (!ebuf) {
-			printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset);
-		} else {
-			__u32 ofs = jeb->offset;
-
-			D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
-			while(ofs < jeb->offset + c->sector_size) {
-				__u32 readlen = min((__u32)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
-				int i;
-
-				ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf);
-				if (ret < 0) {
-					printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
-					goto bad;
-				}
-				if (retlen != readlen) {
-					printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %d\n", ofs, readlen, retlen);
-					goto bad;
-				}
-				for (i=0; i<readlen; i += sizeof(unsigned long)) {
-					/* 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: 
+		D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset));
+		while(ofs < jeb->offset + c->sector_size) {
+			uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
+			int i;
+
+			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);
+				goto bad;
+			}
+			if (retlen != readlen) {
+				printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen);
+				goto bad;
+			}
+			for (i=0; i<readlen; i += sizeof(unsigned long)) {
+				/* 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: 
+					if (!jffs2_cleanmarker_oob(c))
 						jffs2_free_raw_node_ref(marker_ref);
-						kfree(ebuf);
-					bad2:
-						spin_lock_bh(&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--;
-						spin_unlock_bh(&c->erase_completion_lock);
-						wake_up(&c->erase_wait);
-						return;
-					}
+					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--;
+					spin_unlock(&c->erase_completion_lock);
+					wake_up(&c->erase_wait);
+					return;
 				}
-				ofs += readlen;
 			}
-			kfree(ebuf);
+			ofs += readlen;
+			cond_resched();
 		}
+		kfree(ebuf);
+	}
 					
-		/* Write the erase complete marker */	
-		D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
-		ret = c->mtd->write(c->mtd, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
+	/* 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)) {
+
+		if (jffs2_write_nand_cleanmarker(c, jeb))
+			goto bad2;
+			
+		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 jffs2_unknown_node marker = {
+			.magic =	cpu_to_je16(JFFS2_MAGIC_BITMASK),
+			.nodetype =	cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER),
+			.totlen =	cpu_to_je32(c->cleanmarker_size)
+		};
+
+		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);
 		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 %d\n",
+			printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n",
 			       jeb->offset, sizeof(marker), retlen);
 			goto bad2;
 		}
 
 		marker_ref->next_in_ino = NULL;
 		marker_ref->next_phys = NULL;
-		marker_ref->flash_offset = jeb->offset;
-		marker_ref->totlen = PAD(sizeof(marker));
-
+		marker_ref->flash_offset = jeb->offset | REF_NORMAL;
+		marker_ref->__totlen = c->cleanmarker_size;
+			
 		jeb->first_node = jeb->last_node = marker_ref;
-
-		jeb->free_size = c->sector_size - marker_ref->totlen;
-		jeb->used_size = marker_ref->totlen;
+			
+		jeb->free_size = c->sector_size - c->cleanmarker_size;
+		jeb->used_size = c->cleanmarker_size;
 		jeb->dirty_size = 0;
+		jeb->wasted_size = 0;
+	}
 
-		spin_lock_bh(&c->erase_completion_lock);
-		c->erasing_size -= c->sector_size;
-		c->free_size += jeb->free_size;
-		c->used_size += jeb->used_size;
+	spin_lock(&c->erase_completion_lock);
+	c->erasing_size -= c->sector_size;
+	c->free_size += jeb->free_size;
+	c->used_size += jeb->used_size;
 
-		ACCT_SANITY_CHECK(c,jeb);
-		ACCT_PARANOIA_CHECK(jeb);
+	ACCT_SANITY_CHECK(c,jeb);
+	D1(ACCT_PARANOIA_CHECK(jeb));
 
-		list_add_tail(&jeb->list, &c->free_list);
-		c->nr_erasing_blocks--;
-		c->nr_free_blocks++;
-		wake_up(&c->erase_wait);
-	}
-	spin_unlock_bh(&c->erase_completion_lock);
+	list_add_tail(&jeb->list, &c->free_list);
+	c->nr_erasing_blocks--;
+	c->nr_free_blocks++;
+	spin_unlock(&c->erase_completion_lock);
+	wake_up(&c->erase_wait);
 }
+
diff -Nur linux-2.4.30/fs/jffs2/file.c linux-2.4.30-linux-mips.org/fs/jffs2/file.c
--- linux-2.4.30/fs/jffs2/file.c	2003-11-28 19:26:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/file.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,319 +1,106 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: file.c,v 1.58.2.7 2003/11/02 13:51:17 dwmw2 Exp $
+ * $Id: file.c,v 1.98 2004/03/19 16:41:09 dwmw2 Exp $
  *
  */
 
+#include <linux/version.h>
 #include <linux/kernel.h>
-#include <linux/mtd/compatmac.h> /* for min() */
 #include <linux/slab.h>
 #include <linux/fs.h>
+#include <linux/time.h>
 #include <linux/pagemap.h>
+#include <linux/highmem.h>
+#include <linux/crc32.h>
 #include <linux/jffs2.h>
 #include "nodelist.h"
-#include <linux/crc32.h>
 
 extern int generic_file_open(struct inode *, struct file *) __attribute__((weak));
 extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) __attribute__((weak));
 
 
-int jffs2_null_fsync(struct file *filp, struct dentry *dentry, int datasync)
+int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync)
 {
-	/* Move along. Nothing to see here */
-	return 0;
+	struct inode *inode = dentry->d_inode;
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+
+	/* Trigger GC to flush any pending writes for this inode */
+	jffs2_flush_wbuf_gc(c, inode->i_ino);
+			
+	return 0;	
 }
 
 struct file_operations jffs2_file_operations =
 {
-	llseek:		generic_file_llseek,
-	open:		generic_file_open,
-	read:		generic_file_read,
-	write:		generic_file_write,
-	ioctl:		jffs2_ioctl,
-	mmap:		generic_file_mmap,
-	fsync:		jffs2_null_fsync
+	.llseek =	generic_file_llseek,
+	.open =		generic_file_open,
+	.read =		generic_file_read,
+	.write =	generic_file_write,
+	.ioctl =	jffs2_ioctl,
+	.mmap =		generic_file_readonly_mmap,
+	.fsync =	jffs2_fsync,
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,29)
+	.sendfile =	generic_file_sendfile
+#endif
 };
 
 /* jffs2_file_inode_operations */
 
 struct inode_operations jffs2_file_inode_operations =
 {
-	setattr:	jffs2_setattr
+	.setattr =	jffs2_setattr
 };
 
 struct address_space_operations jffs2_file_address_operations =
 {
-	readpage:	jffs2_readpage,
-	prepare_write:	jffs2_prepare_write,
-	commit_write:	jffs2_commit_write
+	.readpage =	jffs2_readpage,
+	.prepare_write =jffs2_prepare_write,
+	.commit_write =	jffs2_commit_write
 };
 
-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr)
-{
-	struct jffs2_full_dnode *old_metadata, *new_metadata;
-	struct inode *inode = dentry->d_inode;
-	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
-	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
-	struct jffs2_raw_inode *ri;
-	unsigned short dev;
-	unsigned char *mdata = NULL;
-	int mdatalen = 0;
-	unsigned int ivalid;
-	__u32 phys_ofs, alloclen;
-	int ret;
-	D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
-	ret = inode_change_ok(inode, iattr);
-	if (ret) 
-		return ret;
-
-	/* Special cases - we don't want more than one data node
-	   for these types on the medium at any time. So setattr
-	   must read the original data associated with the node
-	   (i.e. the device numbers or the target name) and write
-	   it out again with the appropriate data attached */
-	if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
-		/* For these, we don't actually need to read the old node */
-		dev =  (MAJOR(to_kdev_t(dentry->d_inode->i_rdev)) << 8) | 
-			MINOR(to_kdev_t(dentry->d_inode->i_rdev));
-		mdata = (char *)&dev;
-		mdatalen = sizeof(dev);
-		D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
-	} else if (S_ISLNK(inode->i_mode)) {
-		mdatalen = f->metadata->size;
-		mdata = kmalloc(f->metadata->size, GFP_USER);
-		if (!mdata)
-			return -ENOMEM;
-		ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen);
-		if (ret) {
-			kfree(mdata);
-			return ret;
-		}
-		D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
-	}
-
-	ri = jffs2_alloc_raw_inode();
-	if (!ri) {
-		if (S_ISLNK(inode->i_mode))
-			kfree(mdata);
-		return -ENOMEM;
-	}
-		
-	ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
-	if (ret) {
-		jffs2_free_raw_inode(ri);
-		if (S_ISLNK(inode->i_mode))
-			 kfree(mdata);
-		return ret;
-	}
-	down(&f->sem);
-        ivalid = iattr->ia_valid;
-	
-	ri->magic = JFFS2_MAGIC_BITMASK;
-	ri->nodetype = JFFS2_NODETYPE_INODE;
-	ri->totlen = sizeof(*ri) + mdatalen;
-	ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
-
-	ri->ino = inode->i_ino;
-	ri->version = ++f->highest_version;
-
-	ri->mode = (ivalid & ATTR_MODE)?iattr->ia_mode:inode->i_mode;
-	ri->uid = (ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid;
-	ri->gid = (ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid;
-
-	if (ivalid & ATTR_MODE && ri->mode & S_ISGID &&
-	    !in_group_p(ri->gid) && !capable(CAP_FSETID))
-		ri->mode &= ~S_ISGID;
-
-	ri->isize = (ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size;
-	ri->atime = (ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime;
-	ri->mtime = (ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime;
-	ri->ctime = (ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime;
-
-	ri->offset = 0;
-	ri->csize = ri->dsize = mdatalen;
-	ri->compr = JFFS2_COMPR_NONE;
-	if (inode->i_size < ri->isize) {
-		/* It's an extension. Make it a hole node */
-		ri->compr = JFFS2_COMPR_ZERO;
-		ri->dsize = ri->isize - inode->i_size;
-		ri->offset = inode->i_size;
-	}
-	ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
-	if (mdatalen)
-		ri->data_crc = crc32(0, mdata, mdatalen);
-	else
-		ri->data_crc = 0;
-
-	new_metadata = jffs2_write_dnode(inode, ri, mdata, mdatalen, phys_ofs, NULL);
-	if (S_ISLNK(inode->i_mode))
-		kfree(mdata);
-
-	jffs2_complete_reservation(c);
-	
-	if (IS_ERR(new_metadata)) {
-		jffs2_free_raw_inode(ri);
-		up(&f->sem);
-		return PTR_ERR(new_metadata);
-	}
-	/* It worked. Update the inode */
-	inode->i_atime = ri->atime;
-	inode->i_ctime = ri->ctime;
-	inode->i_mtime = ri->mtime;
-	inode->i_mode = ri->mode;
-	inode->i_uid = ri->uid;
-	inode->i_gid = ri->gid;
-
-
-	old_metadata = f->metadata;
-
-	if (inode->i_size > ri->isize) {
-		vmtruncate(inode, ri->isize);
-		jffs2_truncate_fraglist (c, &f->fraglist, ri->isize);
-	}
-
-	if (inode->i_size < ri->isize) {
-		jffs2_add_full_dnode_to_inode(c, f, new_metadata);
-		inode->i_size = ri->isize;
-		f->metadata = NULL;
-	} else {
-		f->metadata = new_metadata;
-	}
-	if (old_metadata) {
-		jffs2_mark_node_obsolete(c, old_metadata->raw);
-		jffs2_free_full_dnode(old_metadata);
-	}
-	jffs2_free_raw_inode(ri);
-	up(&f->sem);
-	return 0;
-}
-
 int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg)
 {
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
-	struct jffs2_node_frag *frag = f->fraglist;
-	__u32 offset = pg->index << PAGE_CACHE_SHIFT;
-	__u32 end = offset + PAGE_CACHE_SIZE;
 	unsigned char *pg_buf;
 	int ret;
 
-	D1(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%x\n", inode->i_ino, offset));
+	D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT));
 
 	if (!PageLocked(pg))
                 PAGE_BUG(pg);
 
-	while(frag && frag->ofs + frag->size  <= offset) {
-		//		D1(printk(KERN_DEBUG "skipping frag %d-%d; before the region we care about\n", frag->ofs, frag->ofs + frag->size));
-		frag = frag->next;
-	}
-
 	pg_buf = kmap(pg);
+	/* FIXME: Can kmap fail? */
 
-	/* XXX FIXME: Where a single physical node actually shows up in two
-	   frags, we read it twice. Don't do that. */
-	/* Now we're pointing at the first frag which overlaps our page */
-	while(offset < end) {
-		D2(printk(KERN_DEBUG "jffs2_readpage: offset %d, end %d\n", offset, end));
-		if (!frag || frag->ofs > offset) {
-			__u32 holesize = end - offset;
-			if (frag) {
-				D1(printk(KERN_NOTICE "Eep. Hole in ino %ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", inode->i_ino, frag->ofs, offset));
-				holesize = min(holesize, frag->ofs - offset);
-				D1(jffs2_print_frag_list(f));
-			}
-			D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
-			memset(pg_buf, 0, holesize);
-			pg_buf += holesize;
-			offset += holesize;
-			continue;
-		} else if (frag->ofs < offset && (offset & (PAGE_CACHE_SIZE-1)) != 0) {
-			D1(printk(KERN_NOTICE "Eep. Overlap in ino #%ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n",
-				  inode->i_ino, frag->ofs, offset));
-			D1(jffs2_print_frag_list(f));
-			memset(pg_buf, 0, end - offset);
-			ClearPageUptodate(pg);
-			SetPageError(pg);
-			kunmap(pg);
-			return -EIO;
-		} else if (!frag->node) {
-			__u32 holeend = min(end, frag->ofs + frag->size);
-			D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size));
-			memset(pg_buf, 0, holeend - offset);
-			pg_buf += holeend - offset;
-			offset = holeend;
-			frag = frag->next;
-			continue;
-		} else {
-			__u32 readlen;
-			__u32 fragofs; /* offset within the frag to start reading */
-
-			fragofs = offset - frag->ofs;
-			readlen = min(frag->size - fragofs, end - offset);
-			D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%x\n", frag->ofs+fragofs, 
-				  fragofs+frag->ofs+readlen, frag->node->raw->flash_offset & ~3));
-			ret = jffs2_read_dnode(c, frag->node, pg_buf, fragofs + frag->ofs - frag->node->ofs, readlen);
-			D2(printk(KERN_DEBUG "node read done\n"));
-			if (ret) {
-				D1(printk(KERN_DEBUG"jffs2_readpage error %d\n",ret));
-				memset(pg_buf, 0, readlen);
-				ClearPageUptodate(pg);
-				SetPageError(pg);
-				kunmap(pg);
-				return ret;
-			}
-		
-			pg_buf += readlen;
-			offset += readlen;
-			frag = frag->next;
-			D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
-		}
+	ret = jffs2_read_inode_range(c, f, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
+
+	if (ret) {
+		ClearPageUptodate(pg);
+		SetPageError(pg);
+	} else {
+		SetPageUptodate(pg);
+		ClearPageError(pg);
 	}
-	D2(printk(KERN_DEBUG "readpage finishing\n"));
-	SetPageUptodate(pg);
-	ClearPageError(pg);
 
 	flush_dcache_page(pg);
-
 	kunmap(pg);
-	D1(printk(KERN_DEBUG "readpage finished\n"));
+
+	D2(printk(KERN_DEBUG "readpage finished\n"));
 	return 0;
 }
 
 int jffs2_do_readpage_unlock(struct inode *inode, struct page *pg)
 {
 	int ret = jffs2_do_readpage_nolock(inode, pg);
-	UnlockPage(pg);
+	unlock_page(pg);
 	return ret;
 }
 
@@ -333,17 +120,17 @@
 {
 	struct inode *inode = pg->mapping->host;
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
-	__u32 pageofs = pg->index << PAGE_CACHE_SHIFT;
+	uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT;
 	int ret = 0;
 
-	D1(printk(KERN_DEBUG "jffs2_prepare_write() nrpages %ld\n", inode->i_mapping->nrpages));
+	D1(printk(KERN_DEBUG "jffs2_prepare_write()\n"));
 
 	if (pageofs > inode->i_size) {
 		/* Make new hole frag from old EOF to new page */
 		struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
 		struct jffs2_raw_inode ri;
 		struct jffs2_full_dnode *fn;
-		__u32 phys_ofs, alloc_len;
+		uint32_t phys_ofs, alloc_len;
 		
 		D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n",
 			  (unsigned int)inode->i_size, pageofs));
@@ -355,29 +142,30 @@
 		down(&f->sem);
 		memset(&ri, 0, sizeof(ri));
 
-		ri.magic = JFFS2_MAGIC_BITMASK;
-		ri.nodetype = JFFS2_NODETYPE_INODE;
-		ri.totlen = sizeof(ri);
-		ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
-
-		ri.ino = f->inocache->ino;
-		ri.version = ++f->highest_version;
-		ri.mode = inode->i_mode;
-		ri.uid = inode->i_uid;
-		ri.gid = inode->i_gid;
-		ri.isize = max((__u32)inode->i_size, pageofs);
-		ri.atime = ri.ctime = ri.mtime = CURRENT_TIME;
-		ri.offset = inode->i_size;
-		ri.dsize = pageofs - inode->i_size;
-		ri.csize = 0;
+		ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+		ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+		ri.totlen = cpu_to_je32(sizeof(ri));
+		ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
+
+		ri.ino = cpu_to_je32(f->inocache->ino);
+		ri.version = cpu_to_je32(++f->highest_version);
+		ri.mode = cpu_to_jemode(inode->i_mode);
+		ri.uid = cpu_to_je16(inode->i_uid);
+		ri.gid = cpu_to_je16(inode->i_gid);
+		ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs));
+		ri.atime = ri.ctime = ri.mtime = cpu_to_je32(get_seconds());
+		ri.offset = cpu_to_je32(inode->i_size);
+		ri.dsize = cpu_to_je32(pageofs - inode->i_size);
+		ri.csize = cpu_to_je32(0);
 		ri.compr = JFFS2_COMPR_ZERO;
-		ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
-		ri.data_crc = 0;
+		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
+		ri.data_crc = cpu_to_je32(0);
 		
-		fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL);
-		jffs2_complete_reservation(c);
+		fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
+
 		if (IS_ERR(fn)) {
 			ret = PTR_ERR(fn);
+			jffs2_complete_reservation(c);
 			up(&f->sem);
 			return ret;
 		}
@@ -391,16 +179,17 @@
 			D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret));
 			jffs2_mark_node_obsolete(c, fn->raw);
 			jffs2_free_full_dnode(fn);
+			jffs2_complete_reservation(c);
 			up(&f->sem);
 			return ret;
 		}
+		jffs2_complete_reservation(c);
 		inode->i_size = pageofs;
 		up(&f->sem);
 	}
 	
-
 	/* Read in the page if it wasn't already present, unless it's a whole page */
-	if (!Page_Uptodate(pg) && (start || end < PAGE_CACHE_SIZE)) {
+	if (!PageUptodate(pg) && (start || end < PAGE_CACHE_SIZE)) {
 		down(&f->sem);
 		ret = jffs2_do_readpage_nolock(inode, pg);
 		up(&f->sem);
@@ -417,14 +206,13 @@
 	struct inode *inode = pg->mapping->host;
 	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
-	__u32 newsize = max_t(__u32, filp->f_dentry->d_inode->i_size, (pg->index << PAGE_CACHE_SHIFT) + end);
-	__u32 file_ofs = (pg->index << PAGE_CACHE_SHIFT);
-	__u32 writelen = min((__u32)PAGE_CACHE_SIZE, newsize - file_ofs);
 	struct jffs2_raw_inode *ri;
+	unsigned aligned_start = start & ~3;
 	int ret = 0;
-	ssize_t writtenlen = 0;
+	uint32_t writtenlen = 0;
 
-	D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
+	D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n",
+		  inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags));
 
 	if (!start && end == PAGE_CACHE_SIZE) {
 		/* We need to avoid deadlock with page_cache_read() in
@@ -435,109 +223,53 @@
 	}
 
 	ri = jffs2_alloc_raw_inode();
-	if (!ri)
-		return -ENOMEM;
-
-	while(writelen) {
-		struct jffs2_full_dnode *fn;
-		unsigned char *comprbuf = NULL;
-		unsigned char comprtype = JFFS2_COMPR_NONE;
-		__u32 phys_ofs, alloclen;
-		__u32 datalen, cdatalen;
-
-		D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, file_ofs));
-
-		ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
-		if (ret) {
-			SetPageError(pg);
-			D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
-			break;
-		}
-		down(&f->sem);
-		datalen = writelen;
-		cdatalen = min(alloclen - sizeof(*ri), writelen);
 
-		comprbuf = kmalloc(cdatalen, GFP_KERNEL);
-		if (comprbuf) {
-			comprtype = jffs2_compress(page_address(pg)+ (file_ofs & (PAGE_CACHE_SIZE-1)), comprbuf, &datalen, &cdatalen);
-		}
-		if (comprtype == JFFS2_COMPR_NONE) {
-			/* Either compression failed, or the allocation of comprbuf failed */
-			if (comprbuf)
-				kfree(comprbuf);
-			comprbuf = page_address(pg) + (file_ofs & (PAGE_CACHE_SIZE -1));
-			datalen = cdatalen;
-		}
-		/* Now comprbuf points to the data to be written, be it compressed or not.
-		   comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means
-		   that the comprbuf doesn't need to be kfree()d. 
-		*/
-
-		ri->magic = JFFS2_MAGIC_BITMASK;
-		ri->nodetype = JFFS2_NODETYPE_INODE;
-		ri->totlen = sizeof(*ri) + cdatalen;
-		ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
-
-		ri->ino = inode->i_ino;
-		ri->version = ++f->highest_version;
-		ri->mode = inode->i_mode;
-		ri->uid = inode->i_uid;
-		ri->gid = inode->i_gid;
-		ri->isize = max((__u32)inode->i_size, file_ofs + datalen);
-		ri->atime = ri->ctime = ri->mtime = CURRENT_TIME;
-		ri->offset = file_ofs;
-		ri->csize = cdatalen;
-		ri->dsize = datalen;
-		ri->compr = comprtype;
-		ri->node_crc = crc32(0, ri, sizeof(*ri)-8);
-		ri->data_crc = crc32(0, comprbuf, cdatalen);
+	if (!ri) {
+		D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n"));
+		return -ENOMEM;
+	}
 
-		fn = jffs2_write_dnode(inode, ri, comprbuf, cdatalen, phys_ofs, NULL);
+	/* Set the fields that the generic jffs2_write_inode_range() code can't find */
+	ri->ino = cpu_to_je32(inode->i_ino);
+	ri->mode = cpu_to_jemode(inode->i_mode);
+	ri->uid = cpu_to_je16(inode->i_uid);
+	ri->gid = cpu_to_je16(inode->i_gid);
+	ri->isize = cpu_to_je32((uint32_t)inode->i_size);
+	ri->atime = ri->ctime = ri->mtime = cpu_to_je32(get_seconds());
+
+	/* In 2.4, it was already kmapped by generic_file_write(). Doesn't
+	   hurt to do it again. The alternative is ifdefs, which are ugly. */
+	kmap(pg);
+
+	ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + aligned_start,
+				      (pg->index << PAGE_CACHE_SHIFT) + aligned_start,
+				      end - aligned_start, &writtenlen);
 
-		jffs2_complete_reservation(c);
+	kunmap(pg);
 
-		if (comprtype != JFFS2_COMPR_NONE)
-			kfree(comprbuf);
+	if (ret) {
+		/* There was an error writing. */
+		SetPageError(pg);
+	}
+	
+	/* Adjust writtenlen for the padding we did, so we don't confuse our caller */
+	if (writtenlen < (start&3))
+		writtenlen = 0;
+	else
+		writtenlen -= (start&3);
 
-		if (IS_ERR(fn)) {
-			ret = PTR_ERR(fn);
-			up(&f->sem);
-			SetPageError(pg);
-			break;
+	if (writtenlen) {
+		if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) {
+			inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen;
+			inode->i_blocks = (inode->i_size + 511) >> 9;
+			
+			inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime));
 		}
-		ret = jffs2_add_full_dnode_to_inode(c, f, fn);
-		if (f->metadata) {
-			jffs2_mark_node_obsolete(c, f->metadata->raw);
-			jffs2_free_full_dnode(f->metadata);
-			f->metadata = NULL;
-		}
-		up(&f->sem);
-		if (ret) {
-			/* Eep */
-			D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret));
-			jffs2_mark_node_obsolete(c, fn->raw);
-			jffs2_free_full_dnode(fn);
-			SetPageError(pg);
-			break;
-		}
-		inode->i_size = ri->isize;
-		inode->i_blocks = (inode->i_size + 511) >> 9;
-		inode->i_ctime = inode->i_mtime = ri->ctime;
-		if (!datalen) {
-			printk(KERN_WARNING "Eep. We didn't actually write any bloody data\n");
-			ret = -EIO;
-			SetPageError(pg);
-			break;
-		}
-		D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
-		writtenlen += datalen;
-		file_ofs += datalen;
-		writelen -= datalen;
 	}
 
 	jffs2_free_raw_inode(ri);
 
-	if (writtenlen < end) {
+	if (start+writtenlen < end) {
 		/* generic_file_write has written more to the page cache than we've
 		   actually written to the medium. Mark the page !Uptodate so that 
 		   it gets reread */
@@ -545,13 +277,7 @@
 		SetPageError(pg);
 		ClearPageUptodate(pg);
 	}
-	if (writtenlen <= start) {
-		/* We didn't even get to the start of the affected part */
-		ret = ret?ret:-ENOSPC;
-		D1(printk(KERN_DEBUG "jffs2_commit_write(): Only %x bytes written to page. start (%x) not reached, returning %d\n", writtenlen, start, ret));
-	}
-	writtenlen = min(end-start, writtenlen-start);
 
-	D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d. nrpages is %ld\n",writtenlen?writtenlen:ret, inode->i_mapping->nrpages));
+	D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",writtenlen?writtenlen:ret));
 	return writtenlen?writtenlen:ret;
 }
diff -Nur linux-2.4.30/fs/jffs2/fs.c linux-2.4.30-linux-mips.org/fs/jffs2/fs.c
--- linux-2.4.30/fs/jffs2/fs.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/fs.c	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,652 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ * $Id: fs.c,v 1.43 2004/05/07 16:00:31 gleixner Exp $
+ *
+ */
+
+#include <linux/version.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/list.h>
+#include <linux/mtd/mtd.h>
+#include <linux/pagemap.h>
+#include <linux/slab.h>
+#include <linux/vfs.h>
+#include <linux/crc32.h>
+#include "nodelist.h"
+
+
+static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr)
+{
+	struct jffs2_full_dnode *old_metadata, *new_metadata;
+	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+	struct jffs2_raw_inode *ri;
+	unsigned short dev;
+	unsigned char *mdata = NULL;
+	int mdatalen = 0;
+	unsigned int ivalid;
+	uint32_t phys_ofs, alloclen;
+	int ret;
+	D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino));
+	ret = inode_change_ok(inode, iattr);
+	if (ret) 
+		return ret;
+
+	/* Special cases - we don't want more than one data node
+	   for these types on the medium at any time. So setattr
+	   must read the original data associated with the node
+	   (i.e. the device numbers or the target name) and write
+	   it out again with the appropriate data attached */
+	if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
+		/* For these, we don't actually need to read the old node */
+		dev = old_encode_dev(inode->i_rdev);
+		mdata = (char *)&dev;
+		mdatalen = sizeof(dev);
+		D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen));
+	} else if (S_ISLNK(inode->i_mode)) {
+		mdatalen = f->metadata->size;
+		mdata = kmalloc(f->metadata->size, GFP_USER);
+		if (!mdata)
+			return -ENOMEM;
+		ret = jffs2_read_dnode(c, f, f->metadata, mdata, 0, mdatalen);
+		if (ret) {
+			kfree(mdata);
+			return ret;
+		}
+		D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen));
+	}
+
+	ri = jffs2_alloc_raw_inode();
+	if (!ri) {
+		if (S_ISLNK(inode->i_mode))
+			kfree(mdata);
+		return -ENOMEM;
+	}
+		
+	ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+	if (ret) {
+		jffs2_free_raw_inode(ri);
+		if (S_ISLNK(inode->i_mode & S_IFMT))
+			 kfree(mdata);
+		return ret;
+	}
+	down(&f->sem);
+	ivalid = iattr->ia_valid;
+	
+	ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+	ri->totlen = cpu_to_je32(sizeof(*ri) + mdatalen);
+	ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
+
+	ri->ino = cpu_to_je32(inode->i_ino);
+	ri->version = cpu_to_je32(++f->highest_version);
+
+	ri->uid = cpu_to_je16((ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid);
+	ri->gid = cpu_to_je16((ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid);
+
+	if (ivalid & ATTR_MODE)
+		if (iattr->ia_mode & S_ISGID &&
+		    !in_group_p(je16_to_cpu(ri->gid)) && !capable(CAP_FSETID))
+			ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID);
+		else 
+			ri->mode = cpu_to_jemode(iattr->ia_mode);
+	else
+		ri->mode = cpu_to_jemode(inode->i_mode);
+
+
+	ri->isize = cpu_to_je32((ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size);
+	ri->atime = cpu_to_je32(I_SEC((ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime));
+	ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime));
+	ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime));
+
+	ri->offset = cpu_to_je32(0);
+	ri->csize = ri->dsize = cpu_to_je32(mdatalen);
+	ri->compr = JFFS2_COMPR_NONE;
+	if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
+		/* It's an extension. Make it a hole node */
+		ri->compr = JFFS2_COMPR_ZERO;
+		ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size);
+		ri->offset = cpu_to_je32(inode->i_size);
+	}
+	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+	if (mdatalen)
+		ri->data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
+	else
+		ri->data_crc = cpu_to_je32(0);
+
+	new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, ALLOC_NORMAL);
+	if (S_ISLNK(inode->i_mode))
+		kfree(mdata);
+	
+	if (IS_ERR(new_metadata)) {
+		jffs2_complete_reservation(c);
+		jffs2_free_raw_inode(ri);
+		up(&f->sem);
+		return PTR_ERR(new_metadata);
+	}
+	/* It worked. Update the inode */
+	inode->i_atime = ITIME(je32_to_cpu(ri->atime));
+	inode->i_ctime = ITIME(je32_to_cpu(ri->ctime));
+	inode->i_mtime = ITIME(je32_to_cpu(ri->mtime));
+	inode->i_mode = jemode_to_cpu(ri->mode);
+	inode->i_uid = je16_to_cpu(ri->uid);
+	inode->i_gid = je16_to_cpu(ri->gid);
+
+
+	old_metadata = f->metadata;
+
+	if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size)
+		jffs2_truncate_fraglist (c, &f->fragtree, iattr->ia_size);
+
+	if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) {
+		jffs2_add_full_dnode_to_inode(c, f, new_metadata);
+		inode->i_size = iattr->ia_size;
+		f->metadata = NULL;
+	} else {
+		f->metadata = new_metadata;
+	}
+	if (old_metadata) {
+		jffs2_mark_node_obsolete(c, old_metadata->raw);
+		jffs2_free_full_dnode(old_metadata);
+	}
+	jffs2_free_raw_inode(ri);
+
+	up(&f->sem);
+	jffs2_complete_reservation(c);
+
+	/* We have to do the vmtruncate() without f->sem held, since
+	   some pages may be locked and waiting for it in readpage(). 
+	   We are protected from a simultaneous write() extending i_size
+	   back past iattr->ia_size, because do_truncate() holds the
+	   generic inode semaphore. */
+	if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size)
+		vmtruncate(inode, iattr->ia_size);
+
+	return 0;
+}
+
+int jffs2_setattr(struct dentry *dentry, struct iattr *iattr)
+{
+	return jffs2_do_setattr(dentry->d_inode, iattr);
+}
+
+int jffs2_statfs(struct super_block *sb, struct kstatfs *buf)
+{
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+	unsigned long avail;
+
+	buf->f_type = JFFS2_SUPER_MAGIC;
+	buf->f_bsize = 1 << PAGE_SHIFT;
+	buf->f_blocks = c->flash_size >> PAGE_SHIFT;
+	buf->f_files = 0;
+	buf->f_ffree = 0;
+	buf->f_namelen = JFFS2_MAX_NAME_LEN;
+
+	spin_lock(&c->erase_completion_lock);
+
+	avail = c->dirty_size + c->free_size;
+	if (avail > c->sector_size * c->resv_blocks_write)
+		avail -= c->sector_size * c->resv_blocks_write;
+	else
+		avail = 0;
+
+	buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
+
+	D1(jffs2_dump_block_lists(c));
+
+	spin_unlock(&c->erase_completion_lock);
+
+	return 0;
+}
+
+
+void jffs2_clear_inode (struct inode *inode)
+{
+	/* We can forget about this inode for now - drop all 
+	 *  the nodelists associated with it, etc.
+	 */
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
+	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
+	
+	D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
+
+	jffs2_do_clear_inode(c, f);
+}
+
+void jffs2_read_inode (struct inode *inode)
+{
+	struct jffs2_inode_info *f;
+	struct jffs2_sb_info *c;
+	struct jffs2_raw_inode latest_node;
+	int ret;
+
+	D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
+
+	f = JFFS2_INODE_INFO(inode);
+	c = JFFS2_SB_INFO(inode->i_sb);
+
+	jffs2_init_inode_info(f);
+	
+	ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node);
+
+	if (ret) {
+		make_bad_inode(inode);
+		up(&f->sem);
+		return;
+	}
+	inode->i_mode = jemode_to_cpu(latest_node.mode);
+	inode->i_uid = je16_to_cpu(latest_node.uid);
+	inode->i_gid = je16_to_cpu(latest_node.gid);
+	inode->i_size = je32_to_cpu(latest_node.isize);
+	inode->i_atime = ITIME(je32_to_cpu(latest_node.atime));
+	inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime));
+	inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime));
+
+	inode->i_nlink = f->inocache->nlink;
+
+	inode->i_blksize = PAGE_SIZE;
+	inode->i_blocks = (inode->i_size + 511) >> 9;
+	
+	switch (inode->i_mode & S_IFMT) {
+		jint16_t rdev;
+
+	case S_IFLNK:
+		inode->i_op = &jffs2_symlink_inode_operations;
+		break;
+		
+	case S_IFDIR:
+	{
+		struct jffs2_full_dirent *fd;
+
+		for (fd=f->dents; fd; fd = fd->next) {
+			if (fd->type == DT_DIR && fd->ino)
+				inode->i_nlink++;
+		}
+		/* and '..' */
+		inode->i_nlink++;
+		/* Root dir gets i_nlink 3 for some reason */
+		if (inode->i_ino == 1)
+			inode->i_nlink++;
+
+		inode->i_op = &jffs2_dir_inode_operations;
+		inode->i_fop = &jffs2_dir_operations;
+		break;
+	}
+	case S_IFREG:
+		inode->i_op = &jffs2_file_inode_operations;
+		inode->i_fop = &jffs2_file_operations;
+		inode->i_mapping->a_ops = &jffs2_file_address_operations;
+		inode->i_mapping->nrpages = 0;
+		break;
+
+	case S_IFBLK:
+	case S_IFCHR:
+		/* Read the device numbers from the media */
+		D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
+		if (jffs2_read_dnode(c, f, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
+			/* Eep */
+			printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
+			up(&f->sem);
+			jffs2_do_clear_inode(c, f);
+			make_bad_inode(inode);
+			return;
+		}			
+
+	case S_IFSOCK:
+	case S_IFIFO:
+		inode->i_op = &jffs2_file_inode_operations;
+		init_special_inode(inode, inode->i_mode,
+				   old_decode_dev((je16_to_cpu(rdev))));
+		break;
+
+	default:
+		printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino);
+	}
+
+	up(&f->sem);
+
+	D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
+}
+
+void jffs2_dirty_inode(struct inode *inode)
+{
+	struct iattr iattr;
+
+	if (!(inode->i_state & I_DIRTY_DATASYNC)) {
+		D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino));
+		return;
+	}
+
+	D1(printk(KERN_DEBUG "jffs2_dirty_inode() calling setattr() for ino #%lu\n", inode->i_ino));
+
+	iattr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_MTIME|ATTR_CTIME;
+	iattr.ia_mode = inode->i_mode;
+	iattr.ia_uid = inode->i_uid;
+	iattr.ia_gid = inode->i_gid;
+	iattr.ia_atime = inode->i_atime;
+	iattr.ia_mtime = inode->i_mtime;
+	iattr.ia_ctime = inode->i_ctime;
+
+	jffs2_do_setattr(inode, &iattr);
+}
+
+int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
+{
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+
+	if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY))
+		return -EROFS;
+
+	/* We stop if it was running, then restart if it needs to.
+	   This also catches the case where it was stopped and this
+	   is just a remount to restart it.
+	   Flush the writebuffer, if neccecary, else we loose it */
+	if (!(sb->s_flags & MS_RDONLY)) {
+		jffs2_stop_garbage_collect_thread(c);
+		down(&c->alloc_sem);
+		jffs2_flush_wbuf_pad(c);
+		up(&c->alloc_sem);
+	}	
+
+	if (!(*flags & MS_RDONLY))
+		jffs2_start_garbage_collect_thread(c);
+	
+	sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY);
+
+	return 0;
+}
+
+void jffs2_write_super (struct super_block *sb)
+{
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+	sb->s_dirt = 0;
+
+	if (sb->s_flags & MS_RDONLY)
+		return;
+
+	D1(printk(KERN_DEBUG "jffs2_write_super()\n"));
+	jffs2_garbage_collect_trigger(c);
+	jffs2_erase_pending_blocks(c, 0);
+	jffs2_flush_wbuf_gc(c, 0);
+}
+
+
+/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
+   fill in the raw_inode while you're at it. */
+struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
+{
+	struct inode *inode;
+	struct super_block *sb = dir_i->i_sb;
+	struct jffs2_sb_info *c;
+	struct jffs2_inode_info *f;
+	int ret;
+
+	D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
+
+	c = JFFS2_SB_INFO(sb);
+	
+	inode = new_inode(sb);
+	
+	if (!inode)
+		return ERR_PTR(-ENOMEM);
+
+	f = JFFS2_INODE_INFO(inode);
+	jffs2_init_inode_info(f);
+
+	memset(ri, 0, sizeof(*ri));
+	/* Set OS-specific defaults for new inodes */
+	ri->uid = cpu_to_je16(current->fsuid);
+
+	if (dir_i->i_mode & S_ISGID) {
+		ri->gid = cpu_to_je16(dir_i->i_gid);
+		if (S_ISDIR(mode))
+			mode |= S_ISGID;
+	} else {
+		ri->gid = cpu_to_je16(current->fsgid);
+	}
+	ri->mode =  cpu_to_jemode(mode);
+	ret = jffs2_do_new_inode (c, f, mode, ri);
+	if (ret) {
+		make_bad_inode(inode);
+		iput(inode);
+		return ERR_PTR(ret);
+	}
+	inode->i_nlink = 1;
+	inode->i_ino = je32_to_cpu(ri->ino);
+	inode->i_mode = jemode_to_cpu(ri->mode);
+	inode->i_gid = je16_to_cpu(ri->gid);
+	inode->i_uid = je16_to_cpu(ri->uid);
+	inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
+	ri->atime = ri->mtime = ri->ctime = cpu_to_je32(I_SEC(inode->i_mtime));
+
+	inode->i_blksize = PAGE_SIZE;
+	inode->i_blocks = 0;
+	inode->i_size = 0;
+
+	insert_inode_hash(inode);
+
+	return inode;
+}
+
+
+int jffs2_do_fill_super(struct super_block *sb, void *data, int silent)
+{
+	struct jffs2_sb_info *c;
+	struct inode *root_i;
+	int ret;
+	size_t blocks;
+
+	c = JFFS2_SB_INFO(sb);
+
+	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;
+	/*
+	 * Size alignment check
+	 */
+	if ((c->sector_size * blocks) != c->flash_size) {
+		printk(KERN_ERR "jffs2: Flash size not aligned to erasesize\n");
+		return -EINVAL;
+	}
+
+	if (c->sector_size != c->mtd->erasesize)
+		printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using virtual blocks size (%dKiB) instead\n", 
+			c->mtd->erasesize / 1024, c->sector_size / 1024);
+
+	if (c->flash_size < 5*c->sector_size) {
+		printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n", c->flash_size / c->sector_size);
+		return -EINVAL;
+	}
+
+	c->cleanmarker_size = sizeof(struct jffs2_unknown_node);
+	/* Joern -- stick alignment for weird 8-byte-page flash here */
+
+	/* NAND (or other bizarre) flash... do setup accordingly */
+	ret = jffs2_flash_setup(c);
+	if (ret)
+		return ret;
+
+	c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL);
+	if (!c->inocache_list) {
+		ret = -ENOMEM;
+		goto out_wbuf;
+	}
+	memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *));
+
+	if ((ret = jffs2_do_mount_fs(c)))
+		goto out_inohash;
+
+	ret = -EINVAL;
+
+	D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n"));
+	root_i = iget(sb, 1);
+	if (is_bad_inode(root_i)) {
+		D1(printk(KERN_WARNING "get root inode failed\n"));
+		goto out_nodes;
+	}
+
+	D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n"));
+	sb->s_root = d_alloc_root(root_i);
+	if (!sb->s_root)
+		goto out_root_i;
+
+#if LINUX_VERSION_CODE >= 0x20403
+	sb->s_maxbytes = 0xFFFFFFFF;
+#endif
+	sb->s_blocksize = PAGE_CACHE_SIZE;
+	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+	sb->s_magic = JFFS2_SUPER_MAGIC;
+	if (!(sb->s_flags & MS_RDONLY))
+		jffs2_start_garbage_collect_thread(c);
+	return 0;
+
+ out_root_i:
+	iput(root_i);
+ out_nodes:
+	jffs2_free_ino_caches(c);
+	jffs2_free_raw_node_refs(c);
+	kfree(c->blocks);
+ out_inohash:
+	kfree(c->inocache_list);
+ out_wbuf:
+	jffs2_flash_cleanup(c);
+
+	return ret;
+}
+
+void jffs2_gc_release_inode(struct jffs2_sb_info *c,
+				   struct jffs2_inode_info *f)
+{
+	iput(OFNI_EDONI_2SFFJ(f));
+}
+
+struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
+						     int inum, int nlink)
+{
+	struct inode *inode;
+	struct jffs2_inode_cache *ic;
+	if (!nlink) {
+		/* The inode has zero nlink but its nodes weren't yet marked
+		   obsolete. This has to be because we're still waiting for 
+		   the final (close() and) iput() to happen.
+
+		   There's a possibility that the final iput() could have 
+		   happened while we were contemplating. In order to ensure
+		   that we don't cause a new read_inode() (which would fail)
+		   for the inode in question, we use ilookup() in this case
+		   instead of iget().
+
+		   The nlink can't _become_ zero at this point because we're 
+		   holding the alloc_sem, and jffs2_do_unlink() would also
+		   need that while decrementing nlink on any inode.
+		*/
+		inode = ilookup(OFNI_BS_2SFFJ(c), inum);
+		if (!inode) {
+			D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
+				  inum));
+
+			spin_lock(&c->inocache_lock);
+			ic = jffs2_get_ino_cache(c, inum);
+			if (!ic) {
+				D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum));
+				spin_unlock(&c->inocache_lock);
+				return NULL;
+			}
+			if (ic->state != INO_STATE_CHECKEDABSENT) {
+				/* Wait for progress. Don't just loop */
+				D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n",
+					  ic->ino, ic->state));
+				sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+			} else {
+				spin_unlock(&c->inocache_lock);
+			}
+
+			return NULL;
+		}
+	} else {
+		/* Inode has links to it still; they're not going away because
+		   jffs2_do_unlink() would need the alloc_sem and we have it.
+		   Just iget() it, and if read_inode() is necessary that's OK.
+		*/
+		inode = iget(OFNI_BS_2SFFJ(c), inum);
+		if (!inode)
+			return ERR_PTR(-ENOMEM);
+	}
+	if (is_bad_inode(inode)) {
+		printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n",
+		       inum, nlink);
+		/* NB. This will happen again. We need to do something appropriate here. */
+		iput(inode);
+		return ERR_PTR(-EIO);
+	}
+
+	return JFFS2_INODE_INFO(inode);
+}
+
+unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, 
+				   struct jffs2_inode_info *f, 
+				   unsigned long offset,
+				   unsigned long *priv)
+{
+	struct inode *inode = OFNI_EDONI_2SFFJ(f);
+	struct page *pg;
+
+	pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT, 
+			     (void *)jffs2_do_readpage_unlock, inode);
+	if (IS_ERR(pg))
+		return (void *)pg;
+	
+	*priv = (unsigned long)pg;
+	return kmap(pg);
+}
+
+void jffs2_gc_release_page(struct jffs2_sb_info *c,
+			   unsigned char *ptr,
+			   unsigned long *priv)
+{
+	struct page *pg = (void *)*priv;
+
+	kunmap(pg);
+	page_cache_release(pg);
+}
+
+int jffs2_flash_setup(struct jffs2_sb_info *c) {
+	int ret = 0;
+	
+	if (jffs2_cleanmarker_oob(c)) {
+		/* NAND flash... do setup accordingly */
+		ret = jffs2_nand_flash_setup(c);
+		if (ret)
+			return ret;
+	}
+
+	/* add setups for other bizarre flashes here... */
+	return ret;
+}
+
+void jffs2_flash_cleanup(struct jffs2_sb_info *c) {
+
+	if (jffs2_cleanmarker_oob(c)) {
+		jffs2_nand_flash_cleanup(c);
+	}
+
+	/* add cleanups for other bizarre flashes here... */
+}
diff -Nur linux-2.4.30/fs/jffs2/gc.c linux-2.4.30-linux-mips.org/fs/jffs2/gc.c
--- linux-2.4.30/fs/jffs2/gc.c	2003-11-28 19:26:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/gc.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,76 +1,67 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: gc.c,v 1.52.2.7 2003/11/02 13:54:20 dwmw2 Exp $
+ * $Id: gc.c,v 1.133 2004/03/08 15:29:09 dwmw2 Exp $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/mtd/mtd.h>
 #include <linux/slab.h>
-#include <linux/jffs2.h>
-#include <linux/sched.h>
-#include <linux/interrupt.h>
 #include <linux/pagemap.h>
-#include "nodelist.h"
 #include <linux/crc32.h>
+#include <linux/compiler.h>
+#include <linux/stat.h>
+#include "nodelist.h"
 
+static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, 
+					  struct jffs2_inode_cache *ic,
+					  struct jffs2_raw_node_ref *raw);
 static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
-					struct inode *inode, struct jffs2_full_dnode *fd);
+					struct jffs2_inode_info *f, struct jffs2_full_dnode *fd);
 static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
-					struct inode *inode, struct jffs2_full_dirent *fd);
+					struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);
 static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
-					struct inode *inode, struct jffs2_full_dirent *fd);
+					struct jffs2_inode_info *f, struct jffs2_full_dirent *fd);
 static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
-				      struct inode *indeo, struct jffs2_full_dnode *fn,
-				      __u32 start, __u32 end);
+				      struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
+				      uint32_t start, uint32_t end);
 static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
-				       struct inode *inode, struct jffs2_full_dnode *fn,
-				       __u32 start, __u32 end);
+				       struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
+				       uint32_t start, uint32_t end);
+static int jffs2_garbage_collect_live(struct jffs2_sb_info *c,  struct jffs2_eraseblock *jeb,
+			       struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f);
 
 /* Called with erase_completion_lock held */
 static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c)
 {
 	struct jffs2_eraseblock *ret;
 	struct list_head *nextlist = NULL;
+	int n = jiffies % 128;
 
 	/* Pick an eraseblock to garbage collect next. This is where we'll
 	   put the clever wear-levelling algorithms. Eventually.  */
-	if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > JFFS2_RESERVED_BLOCKS_GCBAD) {
+	/* We possibly want to favour the dirtier blocks more when the
+	   number of free blocks is low. */
+	if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) {
 		D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n"));
 		nextlist = &c->bad_used_list;
-	} else if (jiffies % 100 && !list_empty(&c->dirty_list)) {
-		/* Most of the time, pick one off the dirty list */
+	} else if (n < 50 && !list_empty(&c->erasable_list)) {
+		/* Note that most of them will have gone directly to be erased. 
+		   So don't favour the erasable_list _too_ much. */
+		D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n"));
+		nextlist = &c->erasable_list;
+	} else if (n < 110 && !list_empty(&c->very_dirty_list)) {
+		/* Most of the time, pick one off the very_dirty list */
+		D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next\n"));
+		nextlist = &c->very_dirty_list;
+	} else if (n < 126 && !list_empty(&c->dirty_list)) {
 		D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n"));
 		nextlist = &c->dirty_list;
 	} else if (!list_empty(&c->clean_list)) {
@@ -80,9 +71,16 @@
 		D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n"));
 
 		nextlist = &c->dirty_list;
+	} else if (!list_empty(&c->very_dirty_list)) {
+		D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n"));
+		nextlist = &c->very_dirty_list;
+	} else if (!list_empty(&c->erasable_list)) {
+		D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n"));
+
+		nextlist = &c->erasable_list;
 	} else {
-		/* Eep. Both were empty */
-		printk(KERN_NOTICE "jffs2: No clean _or_ dirty blocks to GC from! Where are they all?\n");
+		/* Eep. All were empty */
+		printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n");
 		return NULL;
 	}
 
@@ -94,6 +92,17 @@
 		printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset);
 		BUG();
 	}
+	
+	/* Have we accidentally picked a clean block with wasted space ? */
+	if (ret->wasted_size) {
+		D1(printk(KERN_DEBUG "Converting wasted_size %08x to dirty_size\n", ret->wasted_size));
+		ret->dirty_size += ret->wasted_size;
+		c->wasted_size -= ret->wasted_size;
+		c->dirty_size += ret->wasted_size;
+		ret->wasted_size = 0;
+	}
+
+	D1(jffs2_dump_block_lists(c));
 	return ret;
 }
 
@@ -103,21 +112,90 @@
  */
 int jffs2_garbage_collect_pass(struct jffs2_sb_info *c)
 {
-	struct jffs2_eraseblock *jeb;
 	struct jffs2_inode_info *f;
-	struct jffs2_raw_node_ref *raw;
-	struct jffs2_node_frag *frag;
-	struct jffs2_full_dnode *fn = NULL;
-	struct jffs2_full_dirent *fd;
 	struct jffs2_inode_cache *ic;
-	__u32 start = 0, end = 0, nrfrags = 0;
-	struct inode *inode;
-	int ret = 0;
+	struct jffs2_eraseblock *jeb;
+	struct jffs2_raw_node_ref *raw;
+	int ret = 0, inum, nlink;
 
 	if (down_interruptible(&c->alloc_sem))
 		return -EINTR;
 
-	spin_lock_bh(&c->erase_completion_lock);
+	for (;;) {
+		spin_lock(&c->erase_completion_lock);
+		if (!c->unchecked_size)
+			break;
+
+		/* We can't start doing GC yet. We haven't finished checking
+		   the node CRCs etc. Do it now. */
+		
+		/* checked_ino is protected by the alloc_sem */
+		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));
+			spin_unlock(&c->erase_completion_lock);
+			BUG();
+		}
+
+		spin_unlock(&c->erase_completion_lock);
+
+		spin_lock(&c->inocache_lock);
+
+		ic = jffs2_get_ino_cache(c, c->checked_ino++);
+
+		if (!ic) {
+			spin_unlock(&c->inocache_lock);
+			continue;
+		}
+
+		if (!ic->nlink) {
+			D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n",
+				  ic->ino));
+			spin_unlock(&c->inocache_lock);
+			continue;
+		}
+		switch(ic->state) {
+		case INO_STATE_CHECKEDABSENT:
+		case INO_STATE_PRESENT:
+			D1(printk(KERN_DEBUG "Skipping ino #%u already checked\n", ic->ino));
+			spin_unlock(&c->inocache_lock);
+			continue;
+
+		case INO_STATE_GC:
+		case INO_STATE_CHECKING:
+			printk(KERN_WARNING "Inode #%u is in state %d during CRC check phase!\n", ic->ino, ic->state);
+			spin_unlock(&c->inocache_lock);
+			BUG();
+
+		case INO_STATE_READING:
+			/* We need to wait for it to finish, lest we move on
+			   and trigger the BUG() above while we haven't yet 
+			   finished checking all its nodes */
+			D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino));
+			up(&c->alloc_sem);
+			sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+			return 0;
+
+		default:
+			BUG();
+
+		case INO_STATE_UNCHECKED:
+			;
+		}
+		ic->state = INO_STATE_CHECKING;
+		spin_unlock(&c->inocache_lock);
+
+		D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino));
+
+		ret = jffs2_do_crccheck_inode(c, ic);
+		if (ret)
+			printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino);
+
+		jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT);
+		up(&c->alloc_sem);
+		return ret;
+	}
 
 	/* First, work out which block we're garbage-collecting */
 	jeb = c->gcblock;
@@ -127,12 +205,14 @@
 
 	if (!jeb) {
 		printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
-		spin_unlock_bh(&c->erase_completion_lock);
+		spin_unlock(&c->erase_completion_lock);
 		up(&c->alloc_sem);
 		return -EIO;
 	}
 
-	D1(printk(KERN_DEBUG "garbage collect from block at phys 0x%08x\n", jeb->offset));
+	D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size));
+	D1(if (c->nextblock)
+	   printk(KERN_DEBUG "Nextblock at  %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size));
 
 	if (!jeb->used_size) {
 		up(&c->alloc_sem);
@@ -141,92 +221,211 @@
 
 	raw = jeb->gc_node;
 			
-	while(raw->flash_offset & 1) {
-		D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", raw->flash_offset &~3));
-		jeb->gc_node = raw = raw->next_phys;
-		if (!raw) {
+	while(ref_obsolete(raw)) {
+		D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw)));
+		raw = raw->next_phys;
+		if (unlikely(!raw)) {
 			printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n");
 			printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n", 
 			       jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size);
-			spin_unlock_bh(&c->erase_completion_lock);
+			jeb->gc_node = raw;
+			spin_unlock(&c->erase_completion_lock);
 			up(&c->alloc_sem);
 			BUG();
 		}
 	}
-	D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", raw->flash_offset &~3));
+	jeb->gc_node = raw;
+
+	D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw)));
+
 	if (!raw->next_in_ino) {
 		/* Inode-less node. Clean marker, snapshot or something like that */
-		spin_unlock_bh(&c->erase_completion_lock);
+		/* FIXME: If it's something that needs to be copied, including something
+		   we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */
+		spin_unlock(&c->erase_completion_lock);
 		jffs2_mark_node_obsolete(c, raw);
 		up(&c->alloc_sem);
 		goto eraseit_lock;
 	}
-						     
+
 	ic = jffs2_raw_ref_to_ic(raw);
-	D1(printk(KERN_DEBUG "Inode number is #%u\n", ic->ino));
 
-	spin_unlock_bh(&c->erase_completion_lock);
+	/* We need to hold the inocache. Either the erase_completion_lock or
+	   the inocache_lock are sufficient; we trade down since the inocache_lock 
+	   causes less contention. */
+	spin_lock(&c->inocache_lock);
+
+	spin_unlock(&c->erase_completion_lock);
+
+	D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino));
+
+	/* Three possibilities:
+	   1. Inode is already in-core. We must iget it and do proper
+	      updating to its fragtree, etc.
+	   2. Inode is not in-core, node is REF_PRISTINE. We lock the
+	      inocache to prevent a read_inode(), copy the node intact.
+	   3. Inode is not in-core, node is not pristine. We must iget()
+	      and take the slow path.
+	*/
 
-	D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, raw->flash_offset&~3, ic->ino));
-	if (!ic->nlink) {
-		/* The inode has zero nlink but its nodes weren't yet marked
-		   obsolete. This has to be because we're still waiting for 
-		   the final (close() and) iput() to happen.
-
-		   There's a possibility that the final iput() could have 
-		   happened while we were contemplating. In order to ensure
-		   that we don't cause a new read_inode() (which would fail)
-		   for the inode in question, we use ilookup() in this case
-		   instead of iget().
-
-		   The nlink can't _become_ zero at this point because we're 
-		   holding the alloc_sem, and jffs2_do_unlink() would also
-		   need that while decrementing nlink on any inode.
-		*/
-		inode = ilookup(OFNI_BS_2SFFJ(c), ic->ino);
-		if (!inode) {
-			D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n",
+	switch(ic->state) {
+	case INO_STATE_CHECKEDABSENT:
+		/* It's been checked, but it's not currently in-core. 
+		   We can just copy any pristine nodes, but have
+		   to prevent anyone else from doing read_inode() while
+		   we're at it, so we set the state accordingly */
+		if (ref_flags(raw) == REF_PRISTINE)
+			ic->state = INO_STATE_GC;
+		else {
+			D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n", 
 				  ic->ino));
-			up(&c->alloc_sem);
-			return 0;
 		}
-	} else {
-		/* Inode has links to it still; they're not going away because
-		   jffs2_do_unlink() would need the alloc_sem and we have it.
-		   Just iget() it, and if read_inode() is necessary that's OK.
+		break;
+
+	case INO_STATE_PRESENT:
+		/* It's in-core. GC must iget() it. */
+		break;
+
+	case INO_STATE_UNCHECKED:
+	case INO_STATE_CHECKING:
+	case INO_STATE_GC:
+		/* Should never happen. We should have finished checking
+		   by the time we actually start doing any GC, and since 
+		   we're holding the alloc_sem, no other garbage collection 
+		   can happen.
 		*/
-		inode = iget(OFNI_BS_2SFFJ(c), ic->ino);
-		if (!inode) {
-			up(&c->alloc_sem);
-			return -ENOMEM;
+		printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
+		       ic->ino, ic->state);
+		up(&c->alloc_sem);
+		spin_unlock(&c->inocache_lock);
+		BUG();
+
+	case INO_STATE_READING:
+		/* Someone's currently trying to read it. We must wait for
+		   them to finish and then go through the full iget() route
+		   to do the GC. However, sometimes read_inode() needs to get
+		   the alloc_sem() (for marking nodes invalid) so we must
+		   drop the alloc_sem before sleeping. */
+
+		up(&c->alloc_sem);
+		D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n",
+			  ic->ino, ic->state));
+		sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+		/* And because we dropped the alloc_sem we must start again from the 
+		   beginning. Ponder chance of livelock here -- we're returning success
+		   without actually making any progress.
+
+		   Q: What are the chances that the inode is back in INO_STATE_READING 
+		   again by the time we next enter this function? And that this happens
+		   enough times to cause a real delay?
+
+		   A: Small enough that I don't care :) 
+		*/
+		return 0;
+	}
+
+	/* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the
+	   node intact, and we don't have to muck about with the fragtree etc. 
+	   because we know it's not in-core. If it _was_ in-core, we go through
+	   all the iget() crap anyway */
+
+	if (ic->state == INO_STATE_GC) {
+		spin_unlock(&c->inocache_lock);
+
+		ret = jffs2_garbage_collect_pristine(c, ic, raw);
+
+		spin_lock(&c->inocache_lock);
+		ic->state = INO_STATE_CHECKEDABSENT;
+		wake_up(&c->inocache_wq);
+
+		if (ret != -EBADFD) {
+			spin_unlock(&c->inocache_lock);
+			goto release_sem;
 		}
+
+		/* Fall through if it wanted us to, with inocache_lock held */
 	}
-	if (is_bad_inode(inode)) {
-		printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino);
-		/* NB. This will happen again. We need to do something appropriate here. */
-		up(&c->alloc_sem);
-		iput(inode);
-		return -EIO;
+
+	/* Prevent the fairly unlikely race where the gcblock is
+	   entirely obsoleted by the final close of a file which had
+	   the only valid nodes in the block, followed by erasure,
+	   followed by freeing of the ic because the erased block(s)
+	   held _all_ the nodes of that inode.... never been seen but
+	   it's vaguely possible. */
+
+	inum = ic->ino;
+	nlink = ic->nlink;
+	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;
+
+	ret = jffs2_garbage_collect_live(c, jeb, raw, f);
+
+	jffs2_gc_release_inode(c, f);
+
+ release_sem:
+	up(&c->alloc_sem);
+
+ eraseit_lock:
+	/* If we've finished this block, start it erasing */
+	spin_lock(&c->erase_completion_lock);
+
+ eraseit:
+	if (c->gcblock && !c->gcblock->used_size) {
+		D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
+		/* We're GC'ing an empty block? */
+		list_add_tail(&c->gcblock->list, &c->erase_pending_list);
+		c->gcblock = NULL;
+		c->nr_erasing_blocks++;
+		jffs2_erase_pending_trigger(c);
 	}
+	spin_unlock(&c->erase_completion_lock);
+
+	return ret;
+}
+
+static int jffs2_garbage_collect_live(struct jffs2_sb_info *c,  struct jffs2_eraseblock *jeb,
+				      struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f)
+{
+	struct jffs2_node_frag *frag;
+	struct jffs2_full_dnode *fn = NULL;
+	struct jffs2_full_dirent *fd;
+	uint32_t start = 0, end = 0, nrfrags = 0;
+	int ret = 0;
 
-	f = JFFS2_INODE_INFO(inode);
 	down(&f->sem);
+
 	/* Now we have the lock for this inode. Check that it's still the one at the head
 	   of the list. */
 
-	if (raw->flash_offset & 1) {
+	spin_lock(&c->erase_completion_lock);
+
+	if (c->gcblock != jeb) {
+		spin_unlock(&c->erase_completion_lock);
+		D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n"));
+		goto upnout;
+	}
+	if (ref_obsolete(raw)) {
+		spin_unlock(&c->erase_completion_lock);
 		D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n"));
 		/* They'll call again */
 		goto upnout;
 	}
+	spin_unlock(&c->erase_completion_lock);
+
 	/* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */
 	if (f->metadata && f->metadata->raw == raw) {
 		fn = f->metadata;
-		ret = jffs2_garbage_collect_metadata(c, jeb, inode, fn);
+		ret = jffs2_garbage_collect_metadata(c, jeb, f, fn);
 		goto upnout;
 	}
-	
-	for (frag = f->fraglist; frag; frag = frag->next) {
+
+	/* FIXME. Read node and do lookup? */
+	for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
 		if (frag->node && frag->node->raw == raw) {
 			fn = frag->node;
 			end = frag->ofs + frag->size;
@@ -237,13 +436,22 @@
 		}
 	}
 	if (fn) {
+		if (ref_flags(raw) == REF_PRISTINE) {
+			ret = jffs2_garbage_collect_pristine(c, f->inocache, raw);
+			if (!ret) {
+				/* Urgh. Return it sensibly. */
+				frag->node->raw = f->inocache->nodes;
+			}	
+			if (ret != -EBADFD)
+				goto upnout;
+		}
 		/* We found a datanode. Do the GC */
 		if((start >> PAGE_CACHE_SHIFT) < ((end-1) >> PAGE_CACHE_SHIFT)) {
 			/* It crosses a page boundary. Therefore, it must be a hole. */
-			ret = jffs2_garbage_collect_hole(c, jeb, inode, fn, start, end);
+			ret = jffs2_garbage_collect_hole(c, jeb, f, fn, start, end);
 		} else {
 			/* It could still be a hole. But we GC the page this way anyway */
-			ret = jffs2_garbage_collect_dnode(c, jeb, inode, fn, start, end);
+			ret = jffs2_garbage_collect_dnode(c, jeb, f, fn, start, end);
 		}
 		goto upnout;
 	}
@@ -255,12 +463,13 @@
 	}
 
 	if (fd && fd->ino) {
-		ret = jffs2_garbage_collect_dirent(c, jeb, inode, fd);
+		ret = jffs2_garbage_collect_dirent(c, jeb, f, fd);
 	} else if (fd) {
-		ret = jffs2_garbage_collect_deletion_dirent(c, jeb, inode, fd);
+		ret = jffs2_garbage_collect_deletion_dirent(c, jeb, f, fd);
 	} else {
-		printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%lu\n", raw->flash_offset&~3, inode->i_ino);
-		if (raw->flash_offset & 1) {
+		printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%u\n",
+		       ref_offset(raw), f->inocache->ino);
+		if (ref_obsolete(raw)) {
 			printk(KERN_WARNING "But it's obsolete so we don't mind too much\n");
 		} else {
 			ret = -EIO;
@@ -268,53 +477,204 @@
 	}
  upnout:
 	up(&f->sem);
-	up(&c->alloc_sem);
-	iput(inode);
 
- eraseit_lock:
-	/* If we've finished this block, start it erasing */
-	spin_lock_bh(&c->erase_completion_lock);
+	return ret;
+}
 
- eraseit:
-	if (c->gcblock && !c->gcblock->used_size) {
-		D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset));
-		/* We're GC'ing an empty block? */
-		list_add_tail(&c->gcblock->list, &c->erase_pending_list);
-		c->gcblock = NULL;
-		c->nr_erasing_blocks++;
-		jffs2_erase_pending_trigger(c);
+static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, 
+					  struct jffs2_inode_cache *ic,
+					  struct jffs2_raw_node_ref *raw)
+{
+	union jffs2_node_union *node;
+	struct jffs2_raw_node_ref *nraw;
+	size_t retlen;
+	int ret;
+	uint32_t phys_ofs, alloclen;
+	uint32_t crc, rawlen;
+	int retried = 0;
+
+	D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw)));
+
+	rawlen = ref_totlen(c, c->gcblock, raw);
+
+	/* Ask for a small amount of space (or the totlen if smaller) because we
+	   don't want to force wastage of the end of a block if splitting would
+	   work. */
+	ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, 
+					      rawlen), &phys_ofs, &alloclen);
+	if (ret)
+		return ret;
+
+	if (alloclen < rawlen) {
+		/* Doesn't fit untouched. We'll go the old route and split it */
+		return -EBADFD;
+	}
+
+	node = kmalloc(rawlen, GFP_KERNEL);
+	if (!node)
+               return -ENOMEM;
+
+	ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node);
+	if (!ret && retlen != rawlen)
+		ret = -EIO;
+	if (ret)
+		goto out_node;
+
+	crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4);
+	if (je32_to_cpu(node->u.hdr_crc) != crc) {
+		printk(KERN_WARNING "Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+		       ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc);
+		goto bail;
+	}
+
+	switch(je16_to_cpu(node->u.nodetype)) {
+	case JFFS2_NODETYPE_INODE:
+		crc = crc32(0, node, sizeof(node->i)-8);
+		if (je32_to_cpu(node->i.node_crc) != crc) {
+			printk(KERN_WARNING "Node CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+			       ref_offset(raw), je32_to_cpu(node->i.node_crc), crc);
+			goto bail;
+		}
+
+		if (je32_to_cpu(node->i.dsize)) {
+			crc = crc32(0, node->i.data, je32_to_cpu(node->i.csize));
+			if (je32_to_cpu(node->i.data_crc) != crc) {
+				printk(KERN_WARNING "Data CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+				       ref_offset(raw), je32_to_cpu(node->i.data_crc), crc);
+				goto bail;
+			}
+		}
+		break;
+
+	case JFFS2_NODETYPE_DIRENT:
+		crc = crc32(0, node, sizeof(node->d)-8);
+		if (je32_to_cpu(node->d.node_crc) != crc) {
+			printk(KERN_WARNING "Node CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+			       ref_offset(raw), je32_to_cpu(node->d.node_crc), crc);
+			goto bail;
+		}
+
+		if (node->d.nsize) {
+			crc = crc32(0, node->d.name, node->d.nsize);
+			if (je32_to_cpu(node->d.name_crc) != crc) {
+				printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+				       ref_offset(raw), je32_to_cpu(node->d.name_crc), crc);
+				goto bail;
+			}
+		}
+		break;
+	default:
+		printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n", 
+		       ref_offset(raw), je16_to_cpu(node->u.nodetype));
+		goto bail;
+	}
+
+	nraw = jffs2_alloc_raw_node_ref();
+	if (!nraw) {
+		ret = -ENOMEM;
+		goto out_node;
+	}
+
+	/* OK, all the CRCs are good; this node can just be copied as-is. */
+ retry:
+	nraw->flash_offset = phys_ofs;
+	nraw->__totlen = rawlen;
+	nraw->next_phys = NULL;
+
+	ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node);
+
+	if (ret || (retlen != rawlen)) {
+		printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n",
+                       rawlen, phys_ofs, ret, retlen);
+		if (retlen) {
+                        /* Doesn't belong to any inode */
+			nraw->next_in_ino = NULL;
+
+			nraw->flash_offset |= REF_OBSOLETE;
+			jffs2_add_physical_node_ref(c, nraw);
+			jffs2_mark_node_obsolete(c, nraw);
+		} else {
+			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())) {
+			/* Try to reallocate space and retry */
+			uint32_t dummy;
+			struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size];
+
+			retried = 1;
+
+			D1(printk(KERN_DEBUG "Retrying failed write of REF_PRISTINE node.\n"));
+			
+			ACCT_SANITY_CHECK(c,jeb);
+			D1(ACCT_PARANOIA_CHECK(jeb));
+
+			ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy);
+
+			if (!ret) {
+				D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs));
+
+				ACCT_SANITY_CHECK(c,jeb);
+				D1(ACCT_PARANOIA_CHECK(jeb));
+
+				goto retry;
+			}
+			D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
+			jffs2_free_raw_node_ref(nraw);
+		}
+
+		if (!ret)
+			ret = -EIO;
+		goto out_node;
 	}
-	spin_unlock_bh(&c->erase_completion_lock);
+	nraw->flash_offset |= REF_PRISTINE;
+	jffs2_add_physical_node_ref(c, nraw);
+
+	/* 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
+	   going to be fetched from ic->nodes by our caller. */
+        nraw->next_in_ino = ic->nodes;
+        ic->nodes = nraw;
 
+	jffs2_mark_node_obsolete(c, raw);
+	D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw)));
+
+ out_node:
+	kfree(node);
 	return ret;
+ bail:
+	ret = -EBADFD;
+	goto out_node;
 }
 
 static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
-					struct inode *inode, struct jffs2_full_dnode *fn)
+					struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
 {
-	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 	struct jffs2_full_dnode *new_fn;
 	struct jffs2_raw_inode ri;
-	unsigned short dev;
+	jint16_t dev;
 	char *mdata = NULL, mdatalen = 0;
-	__u32 alloclen, phys_ofs;
+	uint32_t alloclen, phys_ofs;
 	int ret;
 
-	if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) {
+	if (S_ISBLK(JFFS2_F_I_MODE(f)) ||
+	    S_ISCHR(JFFS2_F_I_MODE(f)) ) {
 		/* For these, we don't actually need to read the old node */
-		dev =  (MAJOR(to_kdev_t(inode->i_rdev)) << 8) | 
-			MINOR(to_kdev_t(inode->i_rdev));
+		/* FIXME: for minor or major > 255. */
+		dev = cpu_to_je16(((JFFS2_F_I_RDEV_MAJ(f) << 8) | 
+			JFFS2_F_I_RDEV_MIN(f)));
 		mdata = (char *)&dev;
 		mdatalen = sizeof(dev);
 		D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen));
-	} else if (S_ISLNK(inode->i_mode)) {
+	} else if (S_ISLNK(JFFS2_F_I_MODE(f))) {
 		mdatalen = fn->size;
 		mdata = kmalloc(fn->size, GFP_KERNEL);
 		if (!mdata) {
 			printk(KERN_WARNING "kmalloc of mdata failed in jffs2_garbage_collect_metadata()\n");
 			return -ENOMEM;
 		}
-		ret = jffs2_read_dnode(c, fn, mdata, 0, mdatalen);
+		ret = jffs2_read_dnode(c, f, fn, mdata, 0, mdatalen);
 		if (ret) {
 			printk(KERN_WARNING "read of old metadata failed in jffs2_garbage_collect_metadata(): %d\n", ret);
 			kfree(mdata);
@@ -326,34 +686,34 @@
 	
 	ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen);
 	if (ret) {
-		printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_metadata failed: %d\n",
+		printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n",
 		       sizeof(ri)+ mdatalen, ret);
 		goto out;
 	}
 	
 	memset(&ri, 0, sizeof(ri));
-	ri.magic = JFFS2_MAGIC_BITMASK;
-	ri.nodetype = JFFS2_NODETYPE_INODE;
-	ri.totlen = sizeof(ri) + mdatalen;
-	ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
-
-	ri.ino = inode->i_ino;
-	ri.version = ++f->highest_version;
-	ri.mode = inode->i_mode;
-	ri.uid = inode->i_uid;
-	ri.gid = inode->i_gid;
-	ri.isize = inode->i_size;
-	ri.atime = inode->i_atime;
-	ri.ctime = inode->i_ctime;
-	ri.mtime = inode->i_mtime;
-	ri.offset = 0;
-	ri.csize = mdatalen;
-	ri.dsize = mdatalen;
+	ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+	ri.totlen = cpu_to_je32(sizeof(ri) + mdatalen);
+	ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
+
+	ri.ino = cpu_to_je32(f->inocache->ino);
+	ri.version = cpu_to_je32(++f->highest_version);
+	ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
+	ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
+	ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
+	ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
+	ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
+	ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
+	ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
+	ri.offset = cpu_to_je32(0);
+	ri.csize = cpu_to_je32(mdatalen);
+	ri.dsize = cpu_to_je32(mdatalen);
 	ri.compr = JFFS2_COMPR_NONE;
-	ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
-	ri.data_crc = crc32(0, mdata, mdatalen);
+	ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
+	ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen));
 
-	new_fn = jffs2_write_dnode(inode, &ri, mdata, mdatalen, phys_ofs, NULL);
+	new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, ALLOC_GC);
 
 	if (IS_ERR(new_fn)) {
 		printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
@@ -364,41 +724,40 @@
 	jffs2_free_full_dnode(fn);
 	f->metadata = new_fn;
  out:
-	if (S_ISLNK(inode->i_mode))
+	if (S_ISLNK(JFFS2_F_I_MODE(f)))
 		kfree(mdata);
 	return ret;
 }
 
 static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
-					struct inode *inode, struct jffs2_full_dirent *fd)
+					struct jffs2_inode_info *f, struct jffs2_full_dirent *fd)
 {
-	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 	struct jffs2_full_dirent *new_fd;
 	struct jffs2_raw_dirent rd;
-	__u32 alloclen, phys_ofs;
+	uint32_t alloclen, phys_ofs;
 	int ret;
 
-	rd.magic = JFFS2_MAGIC_BITMASK;
-	rd.nodetype = JFFS2_NODETYPE_DIRENT;
+	rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
 	rd.nsize = strlen(fd->name);
-	rd.totlen = sizeof(rd) + rd.nsize;
-	rd.hdr_crc = crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4);
+	rd.totlen = cpu_to_je32(sizeof(rd) + rd.nsize);
+	rd.hdr_crc = cpu_to_je32(crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4));
 
-	rd.pino = inode->i_ino;
-	rd.version = ++f->highest_version;
-	rd.ino = fd->ino;
-	rd.mctime = max(inode->i_mtime, inode->i_ctime);
+	rd.pino = cpu_to_je32(f->inocache->ino);
+	rd.version = cpu_to_je32(++f->highest_version);
+	rd.ino = cpu_to_je32(fd->ino);
+	rd.mctime = cpu_to_je32(max(JFFS2_F_I_MTIME(f), JFFS2_F_I_CTIME(f)));
 	rd.type = fd->type;
-	rd.node_crc = crc32(0, &rd, sizeof(rd)-8);
-	rd.name_crc = crc32(0, fd->name, rd.nsize);
+	rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8));
+	rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize));
 	
 	ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen);
 	if (ret) {
-		printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dirent failed: %d\n",
+		printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n",
 		       sizeof(rd)+rd.nsize, ret);
 		return ret;
 	}
-	new_fd = jffs2_write_dirent(inode, &rd, fd->name, rd.nsize, phys_ofs, NULL);
+	new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, ALLOC_GC);
 
 	if (IS_ERR(new_fd)) {
 		printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd));
@@ -409,19 +768,98 @@
 }
 
 static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
-					struct inode *inode, struct jffs2_full_dirent *fd)
+					struct jffs2_inode_info *f, struct jffs2_full_dirent *fd)
 {
-	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 	struct jffs2_full_dirent **fdp = &f->dents;
 	int found = 0;
 
-	/* FIXME: When we run on NAND flash, we need to work out whether
-	   this deletion dirent is still needed to actively delete a
-	   'real' dirent with the same name that's still somewhere else
-	   on the flash. For now, we know that we've actually obliterated
-	   all the older dirents when they became obsolete, so we didn't
-	   really need to write the deletion to flash in the first place.
-	*/
+	/* On a medium where we can't actually mark nodes obsolete
+	   pernamently, such as NAND flash, we need to work out
+	   whether this deletion dirent is still needed to actively
+	   delete a 'real' dirent with the same name that's still
+	   somewhere else on the flash. */
+	if (!jffs2_can_mark_obsolete(c)) {
+		struct jffs2_raw_dirent *rd;
+		struct jffs2_raw_node_ref *raw;
+		int ret;
+		size_t retlen;
+		int name_len = strlen(fd->name);
+		uint32_t name_crc = crc32(0, fd->name, name_len);
+		uint32_t rawlen = ref_totlen(c, jeb, fd->raw);
+
+		rd = kmalloc(rawlen, GFP_KERNEL);
+		if (!rd)
+			return -ENOMEM;
+
+		/* Prevent the erase code from nicking the obsolete node refs while
+		   we're looking at them. I really don't like this extra lock but
+		   can't see any alternative. Suggestions on a postcard to... */
+		down(&c->erase_free_sem);
+
+		for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) {
+
+			/* We only care about obsolete ones */
+			if (!(ref_obsolete(raw)))
+				continue;
+
+			/* Any dirent with the same name is going to have the same length... */
+			if (ref_totlen(c, NULL, raw) != rawlen)
+				continue;
+
+			/* 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)))
+				continue;
+
+			D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw)));
+
+			/* This is an obsolete node belonging to the same directory, and it's of the right
+			   length. We need to take a closer look...*/
+			ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd);
+			if (ret) {
+				printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading obsolete node at %08x\n", ret, ref_offset(raw));
+				/* If we can't read it, we don't need to continue to obsolete it. Continue */
+				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",
+				       retlen, rawlen, ref_offset(raw));
+				continue;
+			}
+
+			if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT)
+				continue;
+
+			/* If the name CRC doesn't match, skip */
+			if (je32_to_cpu(rd->name_crc) != name_crc)
+				continue;
+
+			/* If the name length doesn't match, or it's another deletion dirent, skip */
+			if (rd->nsize != name_len || !je32_to_cpu(rd->ino))
+				continue;
+
+			/* OK, check the actual name now */
+			if (memcmp(rd->name, fd->name, name_len))
+				continue;
+
+			/* OK. The name really does match. There really is still an older node on
+			   the flash which our deletion dirent obsoletes. So we have to write out
+			   a new deletion dirent to replace it */
+			up(&c->erase_free_sem);
+
+			D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n",
+				  ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino)));
+			kfree(rd);
+
+			return jffs2_garbage_collect_dirent(c, jeb, f, fd);
+		}
+
+		up(&c->erase_free_sem);
+		kfree(rd);
+	}
+
+	/* No need for it any more. Just mark it obsolete and remove it from the list */
 	while (*fdp) {
 		if ((*fdp) == fd) {
 			found = 1;
@@ -431,7 +869,7 @@
 		fdp = &(*fdp)->next;
 	}
 	if (!found) {
-		printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%lu\n", fd->name, inode->i_ino);
+		printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%u\n", fd->name, f->inocache->ino);
 	}
 	jffs2_mark_node_obsolete(c, fd->raw);
 	jffs2_free_full_dirent(fd);
@@ -439,93 +877,95 @@
 }
 
 static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
-				      struct inode *inode, struct jffs2_full_dnode *fn,
-				      __u32 start, __u32 end)
+				      struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
+				      uint32_t start, uint32_t end)
 {
-	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 	struct jffs2_raw_inode ri;
 	struct jffs2_node_frag *frag;
 	struct jffs2_full_dnode *new_fn;
-	__u32 alloclen, phys_ofs;
+	uint32_t alloclen, phys_ofs;
 	int ret;
 
-	D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%lu from offset 0x%x to 0x%x\n",
-		  inode->i_ino, start, end));
+	D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n",
+		  f->inocache->ino, start, end));
 	
 	memset(&ri, 0, sizeof(ri));
 
 	if(fn->frags > 1) {
 		size_t readlen;
-		__u32 crc;
+		uint32_t crc;
 		/* It's partially obsoleted by a later write. So we have to 
 		   write it out again with the _same_ version as before */
-		ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(ri), &readlen, (char *)&ri);
+		ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(ri), &readlen, (char *)&ri);
 		if (readlen != sizeof(ri) || ret) {
-			printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %d. Data will be lost by writing new hold node\n", ret, readlen);
+			printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n", ret, readlen);
 			goto fill;
 		}
-		if (ri.nodetype != JFFS2_NODETYPE_INODE) {
+		if (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) {
 			printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n",
-			       fn->raw->flash_offset & ~3, ri.nodetype, JFFS2_NODETYPE_INODE);
+			       ref_offset(fn->raw),
+			       je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE);
 			return -EIO;
 		}
-		if (ri.totlen != sizeof(ri)) {
-			printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%x\n",
-			       fn->raw->flash_offset & ~3, ri.totlen, sizeof(ri));
+		if (je32_to_cpu(ri.totlen) != sizeof(ri)) {
+			printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n",
+			       ref_offset(fn->raw),
+			       je32_to_cpu(ri.totlen), sizeof(ri));
 			return -EIO;
 		}
 		crc = crc32(0, &ri, sizeof(ri)-8);
-		if (crc != ri.node_crc) {
+		if (crc != je32_to_cpu(ri.node_crc)) {
 			printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n",
-			       fn->raw->flash_offset & ~3, ri.node_crc, crc);
+			       ref_offset(fn->raw), 
+			       je32_to_cpu(ri.node_crc), crc);
 			/* FIXME: We could possibly deal with this by writing new holes for each frag */
-			printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n", 
-			       start, end, inode->i_ino);
+			printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n", 
+			       start, end, f->inocache->ino);
 			goto fill;
 		}
 		if (ri.compr != JFFS2_COMPR_ZERO) {
-			printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", fn->raw->flash_offset & ~3);
-			printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n", 
-			       start, end, inode->i_ino);
+			printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", ref_offset(fn->raw));
+			printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n", 
+			       start, end, f->inocache->ino);
 			goto fill;
 		}
 	} else {
 	fill:
-		ri.magic = JFFS2_MAGIC_BITMASK;
-		ri.nodetype = JFFS2_NODETYPE_INODE;
-		ri.totlen = sizeof(ri);
-		ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
-
-		ri.ino = inode->i_ino;
-		ri.version = ++f->highest_version;
-		ri.offset = start;
-		ri.dsize = end - start;
-		ri.csize = 0;
+		ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+		ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+		ri.totlen = cpu_to_je32(sizeof(ri));
+		ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
+
+		ri.ino = cpu_to_je32(f->inocache->ino);
+		ri.version = cpu_to_je32(++f->highest_version);
+		ri.offset = cpu_to_je32(start);
+		ri.dsize = cpu_to_je32(end - start);
+		ri.csize = cpu_to_je32(0);
 		ri.compr = JFFS2_COMPR_ZERO;
 	}
-	ri.mode = inode->i_mode;
-	ri.uid = inode->i_uid;
-	ri.gid = inode->i_gid;
-	ri.isize = inode->i_size;
-	ri.atime = inode->i_atime;
-	ri.ctime = inode->i_ctime;
-	ri.mtime = inode->i_mtime;
-	ri.data_crc = 0;
-	ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
+	ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
+	ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
+	ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
+	ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
+	ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
+	ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
+	ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
+	ri.data_crc = cpu_to_je32(0);
+	ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
 
 	ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen);
 	if (ret) {
-		printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_hole failed: %d\n",
+		printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n",
 		       sizeof(ri), ret);
 		return ret;
 	}
-	new_fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL);
+	new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC);
 
 	if (IS_ERR(new_fn)) {
 		printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn));
 		return PTR_ERR(new_fn);
 	}
-	if (ri.version == f->highest_version) {
+	if (je32_to_cpu(ri.version) == f->highest_version) {
 		jffs2_add_full_dnode_to_inode(c, f, new_fn);
 		if (f->metadata) {
 			jffs2_mark_node_obsolete(c, f->metadata->raw);
@@ -541,12 +981,17 @@
 	 * number as before. (Except in case of error -- see 'goto fill;' 
 	 * above.)
 	 */
-	D1(if(fn->frags <= 1) {
+	D1(if(unlikely(fn->frags <= 1)) {
 		printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n",
-		       fn->frags, ri.version, f->highest_version, ri.ino);
+		       fn->frags, je32_to_cpu(ri.version), f->highest_version,
+		       je32_to_cpu(ri.ino));
 	});
 
-	for (frag = f->fraglist; frag; frag = frag->next) {
+	/* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */
+	mark_ref_normal(new_fn->raw);
+
+	for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs); 
+	     frag; frag = frag_next(frag)) {
 		if (frag->ofs > fn->size + fn->ofs)
 			break;
 		if (frag->node == fn) {
@@ -571,49 +1016,146 @@
 }
 
 static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
-				       struct inode *inode, struct jffs2_full_dnode *fn,
-				       __u32 start, __u32 end)
+				       struct jffs2_inode_info *f, struct jffs2_full_dnode *fn,
+				       uint32_t start, uint32_t end)
 {
-	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 	struct jffs2_full_dnode *new_fn;
 	struct jffs2_raw_inode ri;
-	__u32 alloclen, phys_ofs, offset, orig_end;	
+	uint32_t alloclen, phys_ofs, offset, orig_end, orig_start;	
 	int ret = 0;
 	unsigned char *comprbuf = NULL, *writebuf;
-	struct page *pg;
+	unsigned long pg;
 	unsigned char *pg_ptr;
-
-
+ 
 	memset(&ri, 0, sizeof(ri));
 
-	D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%lu from offset 0x%x to 0x%x\n",
-		  inode->i_ino, start, end));
+	D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n",
+		  f->inocache->ino, start, end));
 
 	orig_end = end;
+	orig_start = start;
 
+	if (c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge) {
+		/* Attempt to do some merging. But only expand to cover logically
+		   adjacent frags if the block containing them is already considered
+		   to be dirty. Otherwise we end up with GC just going round in 
+		   circles dirtying the nodes it already wrote out, especially 
+		   on NAND where we have small eraseblocks and hence a much higher
+		   chance of nodes having to be split to cross boundaries. */
 
-	/* If we're looking at the last node in the block we're
-	   garbage-collecting, we allow ourselves to merge as if the
-	   block was already erasing. We're likely to be GC'ing a
-	   partial page, and the next block we GC is likely to have
-	   the other half of this page right at the beginning, which
-	   means we'd expand it _then_, as nr_erasing_blocks would have
-	   increased since we checked, and in doing so would obsolete 
-	   the partial node which we'd have written here. Meaning that 
-	   the GC would churn and churn, and just leave dirty blocks in
-	   it's wake.
-	*/
-	if(c->nr_free_blocks + c->nr_erasing_blocks > JFFS2_RESERVED_BLOCKS_GCMERGE - (fn->raw->next_phys?0:1)) {
-		/* Shitloads of space */
-		/* FIXME: Integrate this properly with GC calculations */
-		start &= ~(PAGE_CACHE_SIZE-1);
-		end = min_t(__u32, start + PAGE_CACHE_SIZE, inode->i_size);
-		D1(printk(KERN_DEBUG "Plenty of free space, so expanding to write from offset 0x%x to 0x%x\n",
-			  start, end));
-		if (end < orig_end) {
-			printk(KERN_WARNING "Eep. jffs2_garbage_collect_dnode extended node to write, but it got smaller: start 0x%x, orig_end 0x%x, end 0x%x\n", start, orig_end, end);
-			end = orig_end;
+		struct jffs2_node_frag *frag;
+		uint32_t min, max;
+
+		min = start & ~(PAGE_CACHE_SIZE-1);
+		max = min + PAGE_CACHE_SIZE;
+
+		frag = jffs2_lookup_node_frag(&f->fragtree, start);
+
+		/* BUG_ON(!frag) but that'll happen anyway... */
+
+		BUG_ON(frag->ofs != start);
+
+		/* First grow down... */
+		while((frag = frag_prev(frag)) && frag->ofs >= min) {
+
+			/* If the previous frag doesn't even reach the beginning, there's
+			   excessive fragmentation. Just merge. */
+			if (frag->ofs > min) {
+				D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n",
+					  frag->ofs, frag->ofs+frag->size));
+				start = frag->ofs;
+				continue;
+			}
+			/* OK. This frag holds the first byte of the page. */
+			if (!frag->node || !frag->node->raw) {
+				D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n",
+					  frag->ofs, frag->ofs+frag->size));
+				break;
+			} else {
+
+				/* OK, it's a frag which extends to the beginning of the page. Does it live 
+				   in a block which is still considered clean? If so, don't obsolete it.
+				   If not, cover it anyway. */
+
+				struct jffs2_raw_node_ref *raw = frag->node->raw;
+				struct jffs2_eraseblock *jeb;
+
+				jeb = &c->blocks[raw->flash_offset / c->sector_size];
+
+				if (jeb == c->gcblock) {
+					D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n",
+						  frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
+					start = frag->ofs;
+					break;
+				}
+				if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
+					D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n",
+						  frag->ofs, frag->ofs+frag->size, jeb->offset));
+					break;
+				}
+
+				D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n",
+						  frag->ofs, frag->ofs+frag->size, jeb->offset));
+				start = frag->ofs;
+				break;
+			}
 		}
+
+		/* ... then up */
+
+		/* Find last frag which is actually part of the node we're to GC. */
+		frag = jffs2_lookup_node_frag(&f->fragtree, end-1);
+
+		while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) {
+
+			/* If the previous frag doesn't even reach the beginning, there's lots
+			   of fragmentation. Just merge. */
+			if (frag->ofs+frag->size < max) {
+				D1(printk(KERN_DEBUG "Expanding up to cover partial frag (0x%x-0x%x)\n",
+					  frag->ofs, frag->ofs+frag->size));
+				end = frag->ofs + frag->size;
+				continue;
+			}
+
+			if (!frag->node || !frag->node->raw) {
+				D1(printk(KERN_DEBUG "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n",
+					  frag->ofs, frag->ofs+frag->size));
+				break;
+			} else {
+
+				/* OK, it's a frag which extends to the beginning of the page. Does it live 
+				   in a block which is still considered clean? If so, don't obsolete it.
+				   If not, cover it anyway. */
+
+				struct jffs2_raw_node_ref *raw = frag->node->raw;
+				struct jffs2_eraseblock *jeb;
+
+				jeb = &c->blocks[raw->flash_offset / c->sector_size];
+
+				if (jeb == c->gcblock) {
+					D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n",
+						  frag->ofs, frag->ofs+frag->size, ref_offset(raw)));
+					end = frag->ofs + frag->size;
+					break;
+				}
+				if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) {
+					D1(printk(KERN_DEBUG "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n",
+						  frag->ofs, frag->ofs+frag->size, jeb->offset));
+					break;
+				}
+
+				D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n",
+						  frag->ofs, frag->ofs+frag->size, jeb->offset));
+				end = frag->ofs + frag->size;
+				break;
+			}
+		}
+		D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n", 
+			  orig_start, orig_end, start, end));
+
+		BUG_ON(end > JFFS2_F_I_SIZE(f));
+		BUG_ON(end < orig_end);
+		BUG_ON(start > orig_start);
 	}
 	
 	/* First, use readpage() to read the appropriate page into the page cache */
@@ -623,63 +1165,57 @@
 	 *    page OK. We'll actually write it out again in commit_write, which is a little
 	 *    suboptimal, but at least we're correct.
 	 */
-	pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode);
+	pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg);
 
-	if (IS_ERR(pg)) {
-		printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg));
-		return PTR_ERR(pg);
+	if (IS_ERR(pg_ptr)) {
+		printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr));
+		return PTR_ERR(pg_ptr);
 	}
-	pg_ptr = (char *)kmap(pg);
-	comprbuf = kmalloc(end - start, GFP_KERNEL);
 
 	offset = start;
 	while(offset < orig_end) {
-		__u32 datalen;
-		__u32 cdatalen;
+		uint32_t datalen;
+		uint32_t cdatalen;
 		char comprtype = JFFS2_COMPR_NONE;
 
 		ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen);
 
 		if (ret) {
-			printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dnode failed: %d\n",
+			printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n",
 			       sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret);
 			break;
 		}
-		cdatalen = min(alloclen - sizeof(ri), end - offset);
+		cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset);
 		datalen = end - offset;
 
 		writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1));
 
-		if (comprbuf) {
-			comprtype = jffs2_compress(writebuf, comprbuf, &datalen, &cdatalen);
-		}
-		if (comprtype) {
-			writebuf = comprbuf;
-		} else {
-			datalen = cdatalen;
-		}
-		ri.magic = JFFS2_MAGIC_BITMASK;
-		ri.nodetype = JFFS2_NODETYPE_INODE;
-		ri.totlen = sizeof(ri) + cdatalen;
-		ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4);
-
-		ri.ino = inode->i_ino;
-		ri.version = ++f->highest_version;
-		ri.mode = inode->i_mode;
-		ri.uid = inode->i_uid;
-		ri.gid = inode->i_gid;
-		ri.isize = inode->i_size;
-		ri.atime = inode->i_atime;
-		ri.ctime = inode->i_ctime;
-		ri.mtime = inode->i_mtime;
-		ri.offset = offset;
-		ri.csize = cdatalen;
-		ri.dsize = datalen;
+		comprtype = jffs2_compress(c, f, writebuf, &comprbuf, &datalen, &cdatalen);
+
+		ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+		ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+		ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen);
+		ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4));
+
+		ri.ino = cpu_to_je32(f->inocache->ino);
+		ri.version = cpu_to_je32(++f->highest_version);
+		ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f));
+		ri.uid = cpu_to_je16(JFFS2_F_I_UID(f));
+		ri.gid = cpu_to_je16(JFFS2_F_I_GID(f));
+		ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f));
+		ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f));
+		ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f));
+		ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f));
+		ri.offset = cpu_to_je32(offset);
+		ri.csize = cpu_to_je32(cdatalen);
+		ri.dsize = cpu_to_je32(datalen);
 		ri.compr = comprtype;
-		ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
-		ri.data_crc = crc32(0, writebuf, cdatalen);
+		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
+		ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
 	
-		new_fn = jffs2_write_dnode(inode, &ri, writebuf, cdatalen, phys_ofs, NULL);
+		new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC);
+
+		jffs2_free_comprbuf(comprbuf, writebuf);
 
 		if (IS_ERR(new_fn)) {
 			printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn));
@@ -694,12 +1230,8 @@
 			f->metadata = NULL;
 		}
 	}
-	if (comprbuf) kfree(comprbuf);
 
-	kunmap(pg);
-	/* XXX: Does the page get freed automatically? */
-	/* AAA: Judging by the unmount getting stuck in __wait_on_page, nope. */
-	page_cache_release(pg);
+	jffs2_gc_release_page(c, pg_ptr, &pg);
 	return ret;
 }
 
diff -Nur linux-2.4.30/fs/jffs2/ioctl.c linux-2.4.30-linux-mips.org/fs/jffs2/ioctl.c
--- linux-2.4.30/fs/jffs2/ioctl.c	2001-09-14 23:04:07.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/jffs2/ioctl.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,37 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: ioctl.c,v 1.5 2001/03/15 15:38:24 dwmw2 Exp $
+ * $Id: ioctl.c,v 1.8 2003/10/28 16:16:28 dwmw2 Exp $
  *
  */
 
@@ -42,6 +18,6 @@
 {
 	/* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which
 	   will include compression support etc. */
-	return -EINVAL;
+	return -ENOTTY;
 }
 	
diff -Nur linux-2.4.30/fs/jffs2/Makefile linux-2.4.30-linux-mips.org/fs/jffs2/Makefile
--- linux-2.4.30/fs/jffs2/Makefile	2003-08-25 13:44:43.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/jffs2/Makefile	2005-05-12 18:01:30.000000000 +0200
@@ -1,7 +1,7 @@
 #
 # Makefile for the linux Journalling Flash FileSystem (JFFS) routines.
 #
-# $Id: Makefile,v 1.25.2.1 2002/10/11 09:04:44 dwmw2 Exp $
+# $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
@@ -10,16 +10,31 @@
 # Note 2! The CFLAGS definitions are now in the main makefile...
 
 
-COMPR_OBJS	:= compr.o compr_rubin.o compr_rtime.o pushpull.o \
-			compr_zlib.o
+obj-$(CONFIG_JFFS2_FS) := jffs2.o
+
+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 super.o write.o scan.o gc.o \
-	symlink.o build.o erase.o background.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
 
-O_TARGET := jffs2.o
+BELOW25		:= $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/)
+
+ifeq ($(BELOW25),y)
+LINUX_OBJS	:= super-v24.o crc32.o rbtree.o
+else
+LINUX_OBJS	:= super.o
+endif
 
-obj-y := $(COMPR_OBJS) $(JFFS2_OBJS)
-obj-m := $(O_TARGET)
+NAND_OBJS-$(CONFIG_JFFS2_FS_NAND)	:= wbuf.o
 
+jffs2-objs := $(COMPR_OBJS) $(JFFS2_OBJS) $(VERS_OBJS) $(NAND_OBJS-y) \
+	$(LINUX_OBJS)
+
+
+# 2.4 build compatibility
+ifeq ($(BELOW25),y)
+obj-y := $(jffs2-objs)
+O_TARGET := jffs2.o
 include $(TOPDIR)/Rules.make
+endif
 
diff -Nur linux-2.4.30/fs/jffs2/malloc.c linux-2.4.30-linux-mips.org/fs/jffs2/malloc.c
--- linux-2.4.30/fs/jffs2/malloc.c	2001-09-14 23:04:07.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/jffs2/malloc.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,37 +1,13 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: malloc.c,v 1.16 2001/03/15 15:38:24 dwmw2 Exp $
+ * $Id: malloc.c,v 1.27 2003/10/28 17:14:58 dwmw2 Exp $
  *
  */
 
@@ -47,6 +23,9 @@
 #define JFFS2_SLAB_POISON 0
 #endif
 
+// replace this by #define D3 (x) x for cache debugging
+#define D3(x)
+
 /* These are initialised to NULL in the kernel startup code.
    If you're porting to other operating systems, beware */
 static kmem_cache_t *full_dnode_slab;
@@ -57,57 +36,47 @@
 static kmem_cache_t *node_frag_slab;
 static kmem_cache_t *inode_cache_slab;
 
-void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
-{
-	struct jffs2_tmp_dnode_info *next;
-
-	while (tn) {
-		next = tn;
-		tn = tn->next;
-		jffs2_free_full_dnode(next->fn);
-		jffs2_free_tmp_dnode_info(next);
-	}
-}
-
-void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
-{
-	struct jffs2_full_dirent *next;
-
-	while (fd) {
-		next = fd->next;
-		jffs2_free_full_dirent(fd);
-		fd = next;
-	}
-}
-
 int __init jffs2_create_slab_caches(void)
 {
-	full_dnode_slab = kmem_cache_create("jffs2_full_dnode", sizeof(struct jffs2_full_dnode), 0, JFFS2_SLAB_POISON, NULL, NULL);
+	full_dnode_slab = kmem_cache_create("jffs2_full_dnode", 
+					    sizeof(struct jffs2_full_dnode),
+					    0, JFFS2_SLAB_POISON, NULL, NULL);
 	if (!full_dnode_slab)
 		goto err;
 
-	raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", sizeof(struct jffs2_raw_dirent), 0, JFFS2_SLAB_POISON, NULL, NULL);
+	raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent",
+					    sizeof(struct jffs2_raw_dirent),
+					    0, JFFS2_SLAB_POISON, NULL, NULL);
 	if (!raw_dirent_slab)
 		goto err;
 
-	raw_inode_slab = kmem_cache_create("jffs2_raw_inode", sizeof(struct jffs2_raw_inode), 0, JFFS2_SLAB_POISON, NULL, NULL);
+	raw_inode_slab = kmem_cache_create("jffs2_raw_inode",
+					   sizeof(struct jffs2_raw_inode),
+					   0, JFFS2_SLAB_POISON, NULL, NULL);
 	if (!raw_inode_slab)
 		goto err;
 
-	tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode", sizeof(struct jffs2_tmp_dnode_info), 0, JFFS2_SLAB_POISON, NULL, NULL);
+	tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode",
+						sizeof(struct jffs2_tmp_dnode_info),
+						0, JFFS2_SLAB_POISON, NULL, NULL);
 	if (!tmp_dnode_info_slab)
 		goto err;
 
-	raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref", sizeof(struct jffs2_raw_node_ref), 0, JFFS2_SLAB_POISON, NULL, NULL);
+	raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref",
+					      sizeof(struct jffs2_raw_node_ref),
+					      0, JFFS2_SLAB_POISON, NULL, NULL);
 	if (!raw_node_ref_slab)
 		goto err;
 
-	node_frag_slab = kmem_cache_create("jffs2_node_frag", sizeof(struct jffs2_node_frag), 0, JFFS2_SLAB_POISON, NULL, NULL);
+	node_frag_slab = kmem_cache_create("jffs2_node_frag",
+					   sizeof(struct jffs2_node_frag),
+					   0, JFFS2_SLAB_POISON, NULL, NULL);
 	if (!node_frag_slab)
 		goto err;
 
-	inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), 0, JFFS2_SLAB_POISON, NULL, NULL);
-
+	inode_cache_slab = kmem_cache_create("jffs2_inode_cache",
+					     sizeof(struct jffs2_inode_cache),
+					     0, JFFS2_SLAB_POISON, NULL, NULL);
 	if (inode_cache_slab)
 		return 0;
  err:
@@ -131,7 +100,6 @@
 		kmem_cache_destroy(node_frag_slab);
 	if(inode_cache_slab)
 		kmem_cache_destroy(inode_cache_slab);
-
 }
 
 struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize)
@@ -146,75 +114,92 @@
 
 struct jffs2_full_dnode *jffs2_alloc_full_dnode(void)
 {
-	void *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL);
+	struct jffs2_full_dnode *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL);
+	D3 (printk (KERN_DEBUG "alloc_full_dnode at %p\n", ret));
 	return ret;
 }
 
 void jffs2_free_full_dnode(struct jffs2_full_dnode *x)
 {
+	D3 (printk (KERN_DEBUG "free full_dnode at %p\n", x));
 	kmem_cache_free(full_dnode_slab, x);
 }
 
 struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void)
 {
-	return kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL);
+	struct jffs2_raw_dirent *ret = kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL);
+	D3 (printk (KERN_DEBUG "alloc_raw_dirent\n", ret));
+	return ret;
 }
 
 void jffs2_free_raw_dirent(struct jffs2_raw_dirent *x)
 {
+	D3 (printk (KERN_DEBUG "free_raw_dirent at %p\n", x));
 	kmem_cache_free(raw_dirent_slab, x);
 }
 
 struct jffs2_raw_inode *jffs2_alloc_raw_inode(void)
 {
-	return kmem_cache_alloc(raw_inode_slab, GFP_KERNEL);
+	struct jffs2_raw_inode *ret = kmem_cache_alloc(raw_inode_slab, GFP_KERNEL);
+	D3 (printk (KERN_DEBUG "alloc_raw_inode at %p\n", ret));
+	return ret;
 }
 
 void jffs2_free_raw_inode(struct jffs2_raw_inode *x)
 {
+	D3 (printk (KERN_DEBUG "free_raw_inode at %p\n", x));
 	kmem_cache_free(raw_inode_slab, x);
 }
 
 struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void)
 {
-	return kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL);
+	struct jffs2_tmp_dnode_info *ret = kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL);
+	D3 (printk (KERN_DEBUG "alloc_tmp_dnode_info at %p\n", ret));
+	return ret;
 }
 
 void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x)
 {
+	D3 (printk (KERN_DEBUG "free_tmp_dnode_info at %p\n", x));
 	kmem_cache_free(tmp_dnode_info_slab, x);
 }
 
 struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void)
 {
-	return kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
+	struct jffs2_raw_node_ref *ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL);
+	D3 (printk (KERN_DEBUG "alloc_raw_node_ref at %p\n", ret));
+	return ret;
 }
 
 void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x)
 {
+	D3 (printk (KERN_DEBUG "free_raw_node_ref at %p\n", x));
 	kmem_cache_free(raw_node_ref_slab, x);
 }
 
 struct jffs2_node_frag *jffs2_alloc_node_frag(void)
 {
-	return kmem_cache_alloc(node_frag_slab, GFP_KERNEL);
+	struct jffs2_node_frag *ret = kmem_cache_alloc(node_frag_slab, GFP_KERNEL);
+	D3 (printk (KERN_DEBUG "alloc_node_frag at %p\n", ret));
+	return ret;
 }
 
 void jffs2_free_node_frag(struct jffs2_node_frag *x)
 {
+	D3 (printk (KERN_DEBUG "free_node_frag at %p\n", x));
 	kmem_cache_free(node_frag_slab, x);
 }
 
 struct jffs2_inode_cache *jffs2_alloc_inode_cache(void)
 {
 	struct jffs2_inode_cache *ret = kmem_cache_alloc(inode_cache_slab, GFP_KERNEL);
-	D1(printk(KERN_DEBUG "Allocated inocache at %p\n", ret));
+	D3 (printk(KERN_DEBUG "Allocated inocache at %p\n", ret));
 	return ret;
 }
 
 void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
 {
-	D1(printk(KERN_DEBUG "Freeing inocache at %p\n", x));
+	D3 (printk(KERN_DEBUG "Freeing inocache at %p\n", x));
 	kmem_cache_free(inode_cache_slab, x);
 }
 
diff -Nur linux-2.4.30/fs/jffs2/nodelist.c linux-2.4.30-linux-mips.org/fs/jffs2/nodelist.c
--- linux-2.4.30/fs/jffs2/nodelist.c	2003-06-13 16:51:37.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/jffs2/nodelist.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,44 +1,24 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001, 2002 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: nodelist.c,v 1.30.2.6 2003/02/24 21:49:33 dwmw2 Exp $
+ * $Id: nodelist.c,v 1.86 2003/10/31 15:37:51 dwmw2 Exp $
  *
  */
 
 #include <linux/kernel.h>
-#include <linux/jffs2.h>
+#include <linux/sched.h>
 #include <linux/fs.h>
 #include <linux/mtd/mtd.h>
+#include <linux/rbtree.h>
+#include <linux/crc32.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
 #include "nodelist.h"
 
 void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list)
@@ -78,7 +58,7 @@
 /* Put a new tmp_dnode_info into the list, keeping the list in 
    order of increasing version
 */
-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
+static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list)
 {
 	struct jffs2_tmp_dnode_info **prev = list;
 	
@@ -89,13 +69,37 @@
         *prev = tn;
 }
 
+static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn)
+{
+	struct jffs2_tmp_dnode_info *next;
+
+	while (tn) {
+		next = tn;
+		tn = tn->next;
+		jffs2_free_full_dnode(next->fn);
+		jffs2_free_tmp_dnode_info(next);
+	}
+}
+
+static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd)
+{
+	struct jffs2_full_dirent *next;
+
+	while (fd) {
+		next = fd->next;
+		jffs2_free_full_dirent(fd);
+		fd = next;
+	}
+}
+
+
 /* 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,
 			  struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
-			  __u32 *highest_version, __u32 *latest_mctime,
-			  __u32 *mctime_ver)
+			  uint32_t *highest_version, uint32_t *latest_mctime,
+			  uint32_t *mctime_ver)
 {
 	struct jffs2_raw_node_ref *ref = f->inocache->nodes;
 	struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL;
@@ -109,43 +113,71 @@
 
 	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", ino);
+		printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", (unsigned long)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->flash_offset & 1) {
+		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->flash_offset &~3));
+			D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref)));
 			continue;
 		}
-		err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node);
+		/* 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 */
+		spin_unlock(&c->erase_completion_lock);
+
+		cond_resched();
+
+		/* FIXME: point() */
+		err = jffs2_flash_read(c, (ref_offset(ref)), 
+				       min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)),
+				       &retlen, (void *)&node);
 		if (err) {
-			printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, (ref->flash_offset) & ~3);
+			printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref));
 			goto free_out;
 		}
 			
 
 			/* Check we've managed to read at least the common node header */
-		if (retlen < min(ref->totlen, sizeof(node.u))) {
+		if (retlen < min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node.u))) {
 			printk(KERN_WARNING "short read in get_inode_nodes()\n");
 			err = -EIO;
 			goto free_out;
 		}
 			
-		switch (node.u.nodetype) {
+		switch (je16_to_cpu(node.u.nodetype)) {
 		case JFFS2_NODETYPE_DIRENT:
-			D1(printk(KERN_DEBUG "Node at %08x is a dirent node\n", ref->flash_offset &~3));
+			D1(printk(KERN_DEBUG "Node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref)));
+			if (ref_flags(ref) == REF_UNCHECKED) {
+				printk(KERN_WARNING "BUG: Dirent node at 0x%08x never got checked? How?\n", ref_offset(ref));
+				BUG();
+			}
 			if (retlen < sizeof(node.d)) {
 				printk(KERN_WARNING "short read in get_inode_nodes()\n");
 				err = -EIO;
 				goto free_out;
 			}
-			if (node.d.version > *highest_version)
-				*highest_version = node.d.version;
-			if (ref->flash_offset & 1) {
-				/* Obsoleted */
+			/* sanity check */
+			if (PAD((node.d.nsize + sizeof (node.d))) != PAD(je32_to_cpu (node.d.totlen))) {
+				printk(KERN_NOTICE "jffs2_get_inode_nodes(): Illegal nsize in node at 0x%08x: nsize 0x%02x, totlen %04x\n",
+				       ref_offset(ref), node.d.nsize, je32_to_cpu(node.d.totlen));
+				jffs2_mark_node_obsolete(c, ref);
+				spin_lock(&c->erase_completion_lock);
 				continue;
 			}
+			if (je32_to_cpu(node.d.version) > *highest_version)
+				*highest_version = je32_to_cpu(node.d.version);
+			if (ref_obsolete(ref)) {
+				/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
+				printk(KERN_ERR "Dirent node at 0x%08x became obsolete while we weren't looking\n",
+				       ref_offset(ref));
+				BUG();
+			}
+			
 			fd = jffs2_alloc_full_dirent(node.d.nsize+1);
 			if (!fd) {
 				err = -ENOMEM;
@@ -153,29 +185,30 @@
 			}
 			memset(fd,0,sizeof(struct jffs2_full_dirent) + node.d.nsize+1);
 			fd->raw = ref;
-			fd->version = node.d.version;
-			fd->ino = node.d.ino;
+			fd->version = je32_to_cpu(node.d.version);
+			fd->ino = je32_to_cpu(node.d.ino);
 			fd->type = node.d.type;
 
 			/* Pick out the mctime of the latest dirent */
 			if(fd->version > *mctime_ver) {
 				*mctime_ver = fd->version;
-				*latest_mctime = node.d.mctime;
+				*latest_mctime = je32_to_cpu(node.d.mctime);
 			}
 
 			/* memcpy as much of the name as possible from the raw
 			   dirent we've already read from the flash
 			*/
 			if (retlen > sizeof(struct jffs2_raw_dirent))
-				memcpy(&fd->name[0], &node.d.name[0], min((__u32)node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
+				memcpy(&fd->name[0], &node.d.name[0], min_t(uint32_t, node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent))));
 				
 			/* Do we need to copy any more of the name directly
 			   from the flash?
 			*/
 			if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) {
+				/* FIXME: point() */
 				int already = retlen - sizeof(struct jffs2_raw_dirent);
 					
-				err = c->mtd->read(c->mtd, (ref->flash_offset & ~3) + retlen, 
+				err = jffs2_flash_read(c, (ref_offset(ref)) + retlen, 
 						   node.d.nsize - already, &retlen, &fd->name[already]);
 				if (!err && retlen != node.d.nsize - already)
 					err = -EIO;
@@ -196,21 +229,126 @@
 			break;
 
 		case JFFS2_NODETYPE_INODE:
-			D1(printk(KERN_DEBUG "Node at %08x is a data node\n", ref->flash_offset &~3));
+			D1(printk(KERN_DEBUG "Node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref)));
 			if (retlen < sizeof(node.i)) {
 				printk(KERN_WARNING "read too short for dnode\n");
 				err = -EIO;
 				goto free_out;
 			}
-			if (node.i.version > *highest_version)
-				*highest_version = node.i.version;
-			D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", node.i.version, *highest_version));
-
-			if (ref->flash_offset & 1) {
-				D1(printk(KERN_DEBUG "obsoleted\n"));
-				/* Obsoleted */
-				continue;
+			if (je32_to_cpu(node.i.version) > *highest_version)
+				*highest_version = je32_to_cpu(node.i.version);
+			D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", je32_to_cpu(node.i.version), *highest_version));
+
+			if (ref_obsolete(ref)) {
+				/* Obsoleted. This cannot happen, surely? dwmw2 20020308 */
+				printk(KERN_ERR "Inode node at 0x%08x became obsolete while we weren't looking\n",
+				       ref_offset(ref));
+				BUG();
+			}
+
+			/* If we've never checked the CRCs on this node, check them now. */
+			if (ref_flags(ref) == REF_UNCHECKED) {
+				uint32_t crc, len;
+				struct jffs2_eraseblock *jeb;
+
+				crc = crc32(0, &node, sizeof(node.i)-8);
+				if (crc != je32_to_cpu(node.i.node_crc)) {
+					printk(KERN_NOTICE "jffs2_get_inode_nodes(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+					       ref_offset(ref), je32_to_cpu(node.i.node_crc), crc);
+					jffs2_mark_node_obsolete(c, ref);
+					spin_lock(&c->erase_completion_lock);
+					continue;
+				}
+				
+				/* sanity checks */
+				if ( je32_to_cpu(node.i.offset) > je32_to_cpu(node.i.isize) ||
+				     PAD(je32_to_cpu(node.i.csize) + sizeof (node.i)) != PAD(je32_to_cpu(node.i.totlen))) {
+					printk(KERN_NOTICE "jffs2_get_inode_nodes(): Inode corrupted at 0x%08x, totlen %d, #ino  %d, version %d, isize %d, csize %d, dsize %d \n",
+						ref_offset(ref),  je32_to_cpu(node.i.totlen),  je32_to_cpu(node.i.ino),
+						je32_to_cpu(node.i.version),  je32_to_cpu(node.i.isize), 
+						je32_to_cpu(node.i.csize), je32_to_cpu(node.i.dsize));
+					jffs2_mark_node_obsolete(c, ref);
+					spin_lock(&c->erase_completion_lock);
+					continue;
+				}
+
+				if (node.i.compr != JFFS2_COMPR_ZERO && je32_to_cpu(node.i.csize)) {
+					unsigned char *buf=NULL;
+					uint32_t pointed = 0;
+#ifndef __ECOS
+					if (c->mtd->point) {
+						err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
+								     &retlen, &buf);
+						if (!err && retlen < je32_to_cpu(node.i.csize)) {
+							D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen));
+							c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
+						} else if (err){
+							D1(printk(KERN_DEBUG "MTD point failed %d\n", err));
+						} else
+							pointed = 1; /* succefully pointed to device */
+					}
+#endif					
+					if(!pointed){
+						buf = kmalloc(je32_to_cpu(node.i.csize), GFP_KERNEL);
+						if (!buf)
+							return -ENOMEM;
+						
+						err = jffs2_flash_read(c, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize),
+								       &retlen, buf);
+						if (!err && retlen != je32_to_cpu(node.i.csize))
+							err = -EIO;
+						if (err) {
+							kfree(buf);
+							return err;
+						}
+					}
+					crc = crc32(0, buf, je32_to_cpu(node.i.csize));
+					if(!pointed)
+						kfree(buf);
+#ifndef __ECOS
+					else
+						c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize));
+#endif
+
+					if (crc != je32_to_cpu(node.i.data_crc)) {
+						printk(KERN_NOTICE "jffs2_get_inode_nodes(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+						       ref_offset(ref), je32_to_cpu(node.i.data_crc), crc);
+						jffs2_mark_node_obsolete(c, ref);
+						spin_lock(&c->erase_completion_lock);
+						continue;
+					}
+					
+				}
+
+				/* Mark the node as having been checked and fix the accounting accordingly */
+				spin_lock(&c->erase_completion_lock);
+				jeb = &c->blocks[ref->flash_offset / c->sector_size];
+				len = ref_totlen(c, jeb, ref);
+
+				jeb->used_size += len;
+				jeb->unchecked_size -= len;
+				c->used_size += len;
+				c->unchecked_size -= len;
+
+				/* If node covers at least a whole page, or if it starts at the 
+				   beginning of a page and runs to the end of the file, or if 
+				   it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. 
+
+				   If it's actually overlapped, it'll get made NORMAL (or OBSOLETE) 
+				   when the overlapping node(s) get added to the tree anyway. 
+				*/
+				if ((je32_to_cpu(node.i.dsize) >= PAGE_CACHE_SIZE) ||
+				    ( ((je32_to_cpu(node.i.offset)&(PAGE_CACHE_SIZE-1))==0) &&
+				      (je32_to_cpu(node.i.dsize)+je32_to_cpu(node.i.offset) ==  je32_to_cpu(node.i.isize)))) {
+					D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_PRISTINE\n", ref_offset(ref)));
+					ref->flash_offset = ref_offset(ref) | REF_PRISTINE;
+				} else {
+					D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_NORMAL\n", ref_offset(ref)));
+					ref->flash_offset = ref_offset(ref) | REF_NORMAL;
+				}
+				spin_unlock(&c->erase_completion_lock);
 			}
+
 			tn = jffs2_alloc_tmp_dnode_info();
 			if (!tn) {
 				D1(printk(KERN_DEBUG "alloc tn failed\n"));
@@ -225,36 +363,76 @@
 				jffs2_free_tmp_dnode_info(tn);
 				goto free_out;
 			}
-			tn->version = node.i.version;
-			tn->fn->ofs = node.i.offset;
+			tn->version = je32_to_cpu(node.i.version);
+			tn->fn->ofs = je32_to_cpu(node.i.offset);
 			/* There was a bug where we wrote hole nodes out with
 			   csize/dsize swapped. Deal with it */
-			if (node.i.compr == JFFS2_COMPR_ZERO && !node.i.dsize && node.i.csize)
-				tn->fn->size = node.i.csize;
+			if (node.i.compr == JFFS2_COMPR_ZERO && !je32_to_cpu(node.i.dsize) && je32_to_cpu(node.i.csize))
+				tn->fn->size = je32_to_cpu(node.i.csize);
 			else // normal case...
-				tn->fn->size = node.i.dsize;
+				tn->fn->size = je32_to_cpu(node.i.dsize);
 			tn->fn->raw = ref;
-			D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", ref->flash_offset &~3, node.i.version, node.i.offset, node.i.dsize));
+			D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n",
+				  ref_offset(ref), je32_to_cpu(node.i.version),
+				  je32_to_cpu(node.i.offset), je32_to_cpu(node.i.dsize)));
 			jffs2_add_tn_to_list(tn, &ret_tn);
 			break;
 
 		default:
-			switch(node.u.nodetype & JFFS2_COMPAT_MASK) {
+			if (ref_flags(ref) == REF_UNCHECKED) {
+				struct jffs2_eraseblock *jeb;
+				uint32_t len;
+
+				printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n",
+				       je16_to_cpu(node.u.nodetype), ref_offset(ref));
+
+				/* Mark the node as having been checked and fix the accounting accordingly */
+				spin_lock(&c->erase_completion_lock);
+				jeb = &c->blocks[ref->flash_offset / c->sector_size];
+				len = ref_totlen(c, jeb, ref);
+
+				jeb->used_size += len;
+				jeb->unchecked_size -= len;
+				c->used_size += len;
+				c->unchecked_size -= len;
+
+				mark_ref_normal(ref);
+				spin_unlock(&c->erase_completion_lock);
+			}
+			node.u.nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(node.u.nodetype));
+			if (crc32(0, &node, sizeof(struct jffs2_unknown_node)-4) != je32_to_cpu(node.u.hdr_crc)) {
+				/* Hmmm. This should have been caught at scan time. */
+				printk(KERN_ERR "Node header CRC failed at %08x. But it must have been OK earlier.\n",
+				       ref_offset(ref));
+				printk(KERN_ERR "Node was: { %04x, %04x, %08x, %08x }\n", 
+				       je16_to_cpu(node.u.magic), je16_to_cpu(node.u.nodetype), je32_to_cpu(node.u.totlen),
+				       je32_to_cpu(node.u.hdr_crc));
+				jffs2_mark_node_obsolete(c, ref);
+			} else switch(je16_to_cpu(node.u.nodetype) & JFFS2_COMPAT_MASK) {
 			case JFFS2_FEATURE_INCOMPAT:
-				printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
+				printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
+				/* EEP */
+				BUG();
 				break;
 			case JFFS2_FEATURE_ROCOMPAT:
-				printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
+				printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
+				if (!(c->flags & JFFS2_SB_FLAG_RO))
+					BUG();
 				break;
 			case JFFS2_FEATURE_RWCOMPAT_COPY:
-				printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
+				printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
 				break;
 			case JFFS2_FEATURE_RWCOMPAT_DELETE:
-				printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3);
+				printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref));
+				jffs2_mark_node_obsolete(c, ref);
 				break;
 			}
+
 		}
+		spin_lock(&c->erase_completion_lock);
+
 	}
+	spin_unlock(&c->erase_completion_lock);
 	*tnp = ret_tn;
 	*fdp = ret_fd;
 
@@ -266,19 +444,30 @@
 	return err;
 }
 
+void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state)
+{
+	spin_lock(&c->inocache_lock);
+	ic->state = state;
+	wake_up(&c->inocache_wq);
+	spin_unlock(&c->inocache_lock);
+}
+
+/* During mount, this needs no locking. During normal operation, its
+   callers want to do other stuff while still holding the inocache_lock.
+   Rather than introducing special case get_ino_cache functions or 
+   callbacks, we just let the caller do the locking itself. */
+   
 struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
 {
 	struct jffs2_inode_cache *ret;
 
 	D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino));
-	spin_lock (&c->inocache_lock);
+
 	ret = c->inocache_list[ino % INOCACHE_HASHSIZE];
 	while (ret && ret->ino < ino) {
 		ret = ret->next;
 	}
-
-	spin_unlock(&c->inocache_lock);
-
+	
 	if (ret && ret->ino != ino)
 		ret = NULL;
 
@@ -299,6 +488,7 @@
 	}
 	new->next = *prev;
 	*prev = new;
+
 	spin_unlock(&c->inocache_lock);
 }
 
@@ -316,6 +506,7 @@
 	if ((*prev) == old) {
 		*prev = old->next;
 	}
+
 	spin_unlock(&c->inocache_lock);
 }
 
@@ -352,3 +543,128 @@
 	}
 }
 	
+struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset)
+{
+	/* The common case in lookup is that there will be a node 
+	   which precisely matches. So we go looking for that first */
+	struct rb_node *next;
+	struct jffs2_node_frag *prev = NULL;
+	struct jffs2_node_frag *frag = NULL;
+
+	D2(printk(KERN_DEBUG "jffs2_lookup_node_frag(%p, %d)\n", fragtree, offset));
+
+	next = fragtree->rb_node;
+
+	while(next) {
+		frag = rb_entry(next, struct jffs2_node_frag, rb);
+
+		D2(printk(KERN_DEBUG "Considering frag %d-%d (%p). left %p, right %p\n",
+			  frag->ofs, frag->ofs+frag->size, frag, frag->rb.rb_left, frag->rb.rb_right));
+		if (frag->ofs + frag->size <= offset) {
+			D2(printk(KERN_DEBUG "Going right from frag %d-%d, before the region we care about\n",
+				  frag->ofs, frag->ofs+frag->size));
+			/* Remember the closest smaller match on the way down */
+			if (!prev || frag->ofs > prev->ofs)
+				prev = frag;
+			next = frag->rb.rb_right;
+		} else if (frag->ofs > offset) {
+			D2(printk(KERN_DEBUG "Going left from frag %d-%d, after the region we care about\n",
+				  frag->ofs, frag->ofs+frag->size));
+			next = frag->rb.rb_left;
+		} else {
+			D2(printk(KERN_DEBUG "Returning frag %d,%d, matched\n",
+				  frag->ofs, frag->ofs+frag->size));
+			return frag;
+		}
+	}
+
+	/* Exact match not found. Go back up looking at each parent,
+	   and return the closest smaller one */
+
+	if (prev)
+		D2(printk(KERN_DEBUG "No match. Returning frag %d,%d, closest previous\n",
+			  prev->ofs, prev->ofs+prev->size));
+	else 
+		D2(printk(KERN_DEBUG "Returning NULL, empty fragtree\n"));
+	
+	return prev;
+}
+
+/* Pass 'c' argument to indicate that nodes should be marked obsolete as
+   they're killed. */
+void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c)
+{
+	struct jffs2_node_frag *frag;
+	struct jffs2_node_frag *parent;
+
+	if (!root->rb_node)
+		return;
+
+	frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb));
+
+	while(frag) {
+		if (frag->rb.rb_left) {
+			D2(printk(KERN_DEBUG "Going left from frag (%p) %d-%d\n", 
+				  frag, frag->ofs, frag->ofs+frag->size));
+			frag = frag_left(frag);
+			continue;
+		}
+		if (frag->rb.rb_right) {
+			D2(printk(KERN_DEBUG "Going right from frag (%p) %d-%d\n", 
+				  frag, frag->ofs, frag->ofs+frag->size));
+			frag = frag_right(frag);
+			continue;
+		}
+
+		D2(printk(KERN_DEBUG "jffs2_kill_fragtree: frag at 0x%x-0x%x: node %p, frags %d--\n",
+			  frag->ofs, frag->ofs+frag->size, frag->node,
+			  frag->node?frag->node->frags:0));
+			
+		if (frag->node && !(--frag->node->frags)) {
+			/* Not a hole, and it's the final remaining frag 
+			   of this node. Free the node */
+			if (c)
+				jffs2_mark_node_obsolete(c, frag->node->raw);
+			
+			jffs2_free_full_dnode(frag->node);
+		}
+		parent = frag_parent(frag);
+		if (parent) {
+			if (frag_left(parent) == frag)
+				parent->rb.rb_left = NULL;
+			else 
+				parent->rb.rb_right = NULL;
+		}
+
+		jffs2_free_node_frag(frag);
+		frag = parent;
+
+		cond_resched();
+	}
+}
+
+void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base)
+{
+	struct rb_node *parent = &base->rb;
+	struct rb_node **link = &parent;
+
+	D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag, 
+		  newfrag->ofs, newfrag->ofs+newfrag->size, base));
+
+	while (*link) {
+		parent = *link;
+		base = rb_entry(parent, struct jffs2_node_frag, rb);
+	
+		D2(printk(KERN_DEBUG "fragtree_insert considering frag at 0x%x\n", base->ofs));
+		if (newfrag->ofs > base->ofs)
+			link = &base->rb.rb_right;
+		else if (newfrag->ofs < base->ofs)
+			link = &base->rb.rb_left;
+		else {
+			printk(KERN_CRIT "Duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base);
+			BUG();
+		}
+	}
+
+	rb_link_node(&newfrag->rb, &base->rb, link);
+}
diff -Nur linux-2.4.30/fs/jffs2/nodelist.h linux-2.4.30-linux-mips.org/fs/jffs2/nodelist.h
--- linux-2.4.30/fs/jffs2/nodelist.h	2003-11-28 19:26:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/nodelist.h	2005-05-12 18:01:30.000000000 +0200
@@ -1,48 +1,35 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: nodelist.h,v 1.46.2.5 2003/11/02 13:54:20 dwmw2 Exp $
+ * $Id: nodelist.h,v 1.117 2004/05/05 11:57:52 dwmw2 Exp $
  *
  */
 
+#ifndef __JFFS2_NODELIST_H__
+#define __JFFS2_NODELIST_H__
+
 #include <linux/config.h>
 #include <linux/fs.h>
-
+#include <linux/types.h>
+#include <linux/jffs2.h>
 #include <linux/jffs2_fs_sb.h>
 #include <linux/jffs2_fs_i.h>
 
+#ifdef __ECOS
+#include "os-ecos.h"
+#else
+#include <linux/mtd/compatmac.h> /* For min/max in older kernels */
+#include "os-linux.h"
+#endif
+
 #ifndef CONFIG_JFFS2_FS_DEBUG
-#define CONFIG_JFFS2_FS_DEBUG 2
+#define CONFIG_JFFS2_FS_DEBUG 1
 #endif
 
 #if CONFIG_JFFS2_FS_DEBUG > 0
@@ -57,6 +44,39 @@
 #define D2(x)
 #endif
 
+#define JFFS2_NATIVE_ENDIAN
+
+/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from
+   whatever OS we're actually running on here too. */
+
+#if defined(JFFS2_NATIVE_ENDIAN)
+#define cpu_to_je16(x) ((jint16_t){x})
+#define cpu_to_je32(x) ((jint32_t){x})
+#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)})
+
+#define je16_to_cpu(x) ((x).v16)
+#define je32_to_cpu(x) ((x).v32)
+#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m))
+#elif defined(JFFS2_BIG_ENDIAN)
+#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)})
+#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)})
+#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))})
+
+#define je16_to_cpu(x) (be16_to_cpu(x.v16))
+#define je32_to_cpu(x) (be32_to_cpu(x.v32))
+#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m)))
+#elif defined(JFFS2_LITTLE_ENDIAN)
+#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)})
+#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)})
+#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))})
+
+#define je16_to_cpu(x) (le16_to_cpu(x.v16))
+#define je32_to_cpu(x) (le32_to_cpu(x.v32))
+#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m)))
+#else 
+#error wibble
+#endif
+
 /*
   This is all we need to keep in-core for each raw node during normal
   operation. As and when we do read_inode on a particular inode, we can
@@ -71,17 +91,21 @@
 		for this inode instead. The inode_cache will have NULL in the first
 		word so you know when you've got there :) */
 	struct jffs2_raw_node_ref *next_phys;
-	//	__u32 ino;
-	__u32 flash_offset;
-	__u32 totlen;
-//	__u16 nodetype;
-	
+	uint32_t flash_offset;
+	uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */
+};
+
         /* flash_offset & 3 always has to be zero, because nodes are
 	   always aligned at 4 bytes. So we have a couple of extra bits
-	   to play with. So we set the least significant bit to 1 to
-	   signify that the node is obsoleted by later nodes.
-	*/
-};
+	   to play with, which indicate the node's status; see below: */ 
+#define REF_UNCHECKED	0	/* We haven't yet checked the CRC or built its inode */
+#define REF_OBSOLETE	1	/* Obsolete, can be completely ignored */
+#define REF_PRISTINE	2	/* Completely clean. GC without looking */
+#define REF_NORMAL	3	/* Possibly overlapped. Read the page and write again on GC */
+#define ref_flags(ref)		((ref)->flash_offset & 3)
+#define ref_offset(ref)		((ref)->flash_offset & ~3)
+#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
@@ -101,19 +125,35 @@
    a pointer to the first physical node which is part of this inode, too.
 */
 struct jffs2_inode_cache {
-	struct jffs2_scan_info *scan; /* Used during scan to hold
-		temporary lists of nodes, and later must be set to
+	struct jffs2_full_dirent *scan_dents; /* Used during scan to hold
+		temporary lists of dirents, and later must be set to
 		NULL to mark the end of the raw_node_ref->next_in_ino
 		chain. */
 	struct jffs2_inode_cache *next;
 	struct jffs2_raw_node_ref *nodes;
-	__u32 ino;
+	uint32_t ino;
 	int nlink;
+	int state;
 };
 
+/* Inode states for 'state' above. We need the 'GC' state to prevent
+   someone from doing a read_inode() while we're moving a 'REF_PRISTINE'
+   node without going through all the iget() nonsense */
+#define INO_STATE_UNCHECKED	0	/* CRC checks not yet done */
+#define INO_STATE_CHECKING	1	/* CRC checks in progress */
+#define INO_STATE_PRESENT	2	/* In core */
+#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 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 
@@ -123,9 +163,9 @@
 struct jffs2_full_dnode
 {
 	struct jffs2_raw_node_ref *raw;
-	__u32 ofs; /* Don't really need this, but optimisation */
-	__u32 size;
-	__u32 frags; /* Number of fragments which currently refer
+	uint32_t ofs; /* Don't really need this, but optimisation */
+	uint32_t size;
+	uint32_t frags; /* Number of fragments which currently refer
 			to this node. When this reaches zero, 
 			the node is obsolete.
 		     */
@@ -140,15 +180,15 @@
 {
 	struct jffs2_tmp_dnode_info *next;
 	struct jffs2_full_dnode *fn;
-	__u32 version;
+	uint32_t version;
 };       
 
 struct jffs2_full_dirent
 {
 	struct jffs2_raw_node_ref *raw;
 	struct jffs2_full_dirent *next;
-	__u32 version;
-	__u32 ino; /* == zero for unlink */
+	uint32_t version;
+	uint32_t ino; /* == zero for unlink */
 	unsigned int nhash;
 	unsigned char type;
 	unsigned char name[0];
@@ -159,21 +199,23 @@
 */
 struct jffs2_node_frag
 {
-	struct jffs2_node_frag *next;
+	struct rb_node rb;
 	struct jffs2_full_dnode *node; /* NULL for holes */
-	__u32 size;
-	__u32 ofs; /* Don't really need this, but optimisation */
+	uint32_t size;
+	uint32_t ofs; /* Don't really need this, but optimisation */
 };
 
 struct jffs2_eraseblock
 {
 	struct list_head list;
 	int bad_count;
-	__u32 offset;		/* of this block in the MTD */
+	uint32_t offset;		/* of this block in the MTD */
 
-	__u32 used_size;
-	__u32 dirty_size;
-	__u32 free_size;	/* Note that sector_size - free_size
+	uint32_t unchecked_size;
+	uint32_t used_size;
+	uint32_t dirty_size;
+	uint32_t wasted_size;
+	uint32_t free_size;	/* Note that sector_size - free_size
 				   is the address of the first free space */
 	struct jffs2_raw_node_ref *first_node;
 	struct jffs2_raw_node_ref *last_node;
@@ -190,45 +232,134 @@
 };
 
 #define ACCT_SANITY_CHECK(c, jeb) do { \
-	if (jeb->used_size + jeb->dirty_size + jeb->free_size != c->sector_size) { \
-		printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", jeb->offset); \
-		printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x != total %08x\n", \
-		jeb->free_size, jeb->dirty_size, jeb->used_size, c->sector_size); \
+		struct jffs2_eraseblock *___j = jeb; \
+		if ((___j) && ___j->used_size + ___j->dirty_size + ___j->free_size + ___j->wasted_size + ___j->unchecked_size != c->sector_size) { \
+		printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", ___j->offset); \
+		printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + wasted %08x + unchecked %08x != total %08x\n", \
+		___j->free_size, ___j->dirty_size, ___j->used_size, ___j->wasted_size, ___j->unchecked_size, c->sector_size); \
 		BUG(); \
 	} \
-	if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size != c->flash_size) { \
+	if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size) { \
 		printk(KERN_NOTICE "Eeep. Space accounting superblock info is screwed\n"); \
-		printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x != total %08x\n", \
-		c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->flash_size); \
+		printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x + wasted %08x + unchecked %08x != total %08x\n", \
+		c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->wasted_size, c->unchecked_size, c->flash_size); \
 		BUG(); \
 	} \
 } while(0)
 
+static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb)
+{
+	struct jffs2_raw_node_ref *ref;
+	int i=0;
+
+	printk(KERN_NOTICE);
+	for (ref = jeb->first_node; ref; ref = ref->next_phys) {
+		printk("%08x->", ref_offset(ref));
+		if (++i == 8) {
+			i = 0;
+			printk("\n" KERN_NOTICE);
+		}
+	}
+	printk("\n");
+}
+
+
 #define ACCT_PARANOIA_CHECK(jeb) do { \
-		__u32 my_used_size = 0; \
+		uint32_t my_used_size = 0; \
+		uint32_t my_unchecked_size = 0; \
 		struct jffs2_raw_node_ref *ref2 = jeb->first_node; \
 		while (ref2) { \
-			if (!(ref2->flash_offset & 1)) \
-				my_used_size += ref2->totlen; \
+			if (unlikely(ref2->flash_offset < jeb->offset || \
+				     ref2->flash_offset > jeb->offset + c->sector_size)) { \
+				printk(KERN_NOTICE "Node %08x shouldn't be in block at %08x!\n", \
+				       ref_offset(ref2), jeb->offset); \
+				paranoia_failed_dump(jeb); \
+				BUG(); \
+			} \
+			if (ref_flags(ref2) == REF_UNCHECKED) \
+				my_unchecked_size += ref_totlen(c, jeb, ref2); \
+			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)); \
+				paranoia_failed_dump(jeb); \
+				BUG(); \
+			} \
 			ref2 = ref2->next_phys; \
 		} \
 		if (my_used_size != jeb->used_size) { \
 			printk(KERN_NOTICE "Calculated used size %08x != stored used size %08x\n", my_used_size, jeb->used_size); \
 			BUG(); \
 		} \
+		if (my_unchecked_size != jeb->unchecked_size) { \
+			printk(KERN_NOTICE "Calculated unchecked size %08x != stored unchecked size %08x\n", my_unchecked_size, jeb->unchecked_size); \
+			BUG(); \
+		} \
 	} while(0)
 
+/* Calculate totlen from surrounding nodes or eraseblock */
+static inline uint32_t __ref_totlen(struct jffs2_sb_info *c,
+				    struct jffs2_eraseblock *jeb,
+				    struct jffs2_raw_node_ref *ref)
+{
+	uint32_t ref_end;
+	
+	if (ref->next_phys)
+		ref_end = ref_offset(ref->next_phys);
+	else {
+		if (!jeb)
+			jeb = &c->blocks[ref->flash_offset / c->sector_size];
+
+		/* Last node in block. Use free_space */
+		BUG_ON(ref != jeb->last_node);
+		ref_end = jeb->offset + c->sector_size - jeb->free_size;
+	}
+	return ref_end - ref_offset(ref);
+}
+
+static inline uint32_t ref_totlen(struct jffs2_sb_info *c,
+				  struct jffs2_eraseblock *jeb,
+				  struct jffs2_raw_node_ref *ref)
+{
+	uint32_t ret;
+
+	D1(if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) {
+		printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n",
+		       jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref));
+		BUG();
+	})
+
+#if 1
+	ret = ref->__totlen;
+#else
+	/* This doesn't actually work yet */
+	ret = __ref_totlen(c, jeb, ref);
+	if (ret != ref->__totlen) {
+		printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n",
+		       ref, ref_offset(ref), ref_offset(ref)+ref->__totlen,
+		       ret, ref->__totlen);
+		if (!jeb)
+			jeb = &c->blocks[ref->flash_offset / c->sector_size];
+		paranoia_failed_dump(jeb);
+		BUG();
+	}
+#endif
+	return ret;
+}
+
+
 #define ALLOC_NORMAL	0	/* Normal allocation */
 #define ALLOC_DELETION	1	/* Deletion node. Best to allow it */
 #define ALLOC_GC	2	/* Space requested for GC. Give it or die */
+#define ALLOC_NORETRY	3	/* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */
 
-#define JFFS2_RESERVED_BLOCKS_BASE 3						/* Number of free blocks there must be before we... */
-#define JFFS2_RESERVED_BLOCKS_WRITE (JFFS2_RESERVED_BLOCKS_BASE + 2)		/* ... allow a normal filesystem write */
-#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE + 1)		/* ... allow a normal filesystem deletion */
-#define JFFS2_RESERVED_BLOCKS_GCTRIGGER (JFFS2_RESERVED_BLOCKS_BASE + 3)	/* ... wake up the GC thread */
-#define JFFS2_RESERVED_BLOCKS_GCBAD (JFFS2_RESERVED_BLOCKS_BASE + 1)		/* ... pick a block from the bad_list to GC */
-#define JFFS2_RESERVED_BLOCKS_GCMERGE (JFFS2_RESERVED_BLOCKS_BASE)		/* ... merge pages when garbage collecting */
+/* How much dirty space before it goes on the very_dirty_list */
+#define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2))
 
+/* check if dirty space is more than 255 Byte */
+#define ISDIRTY(size) ((size) >  sizeof (struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN) 
 
 #define PAD(x) (((x)+3)&~3)
 
@@ -241,43 +372,75 @@
 	return ((struct jffs2_inode_cache *)raw);
 }
 
+static inline struct jffs2_node_frag *frag_first(struct rb_root *root)
+{
+	struct rb_node *node = root->rb_node;
+
+	if (!node)
+		return NULL;
+	while(node->rb_left)
+		node = node->rb_left;
+	return rb_entry(node, struct jffs2_node_frag, rb);
+}
+#define rb_parent(rb) ((rb)->rb_parent)
+#define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb)
+#define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb)
+#define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb)
+#define frag_left(frag) rb_entry((frag)->rb.rb_left, struct jffs2_node_frag, rb)
+#define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb)
+#define frag_erase(frag, list) rb_erase(&frag->rb, list);
+
 /* nodelist.c */
 D1(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);
-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list);
 int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f,
 			  struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp,
-			  __u32 *highest_version, __u32 *latest_mctime,
-			  __u32 *mctime_ver);
+			  uint32_t *highest_version, uint32_t *latest_mctime,
+			  uint32_t *mctime_ver);
+void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state);
 struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino);
 void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new);
 void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old);
 void jffs2_free_ino_caches(struct jffs2_sb_info *c);
 void jffs2_free_raw_node_refs(struct jffs2_sb_info *c);
+struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset);
+void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c_delete);
+void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base);
+struct rb_node *rb_next(struct rb_node *);
+struct rb_node *rb_prev(struct rb_node *);
+void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root);
 
 /* nodemgmt.c */
-int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio);
-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len);
-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty);
+int jffs2_thread_should_wake(struct jffs2_sb_info *c);
+int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio);
+int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len);
+int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new);
 void jffs2_complete_reservation(struct jffs2_sb_info *c);
 void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw);
+void jffs2_dump_block_lists(struct jffs2_sb_info *c);
 
 /* write.c */
-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri);
-struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs,  __u32 *writelen);
-struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs,  __u32 *writelen);
+int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri);
+
+struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode);
+struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode);
+int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+			    struct jffs2_raw_inode *ri, unsigned char *buf, 
+			    uint32_t offset, uint32_t writelen, uint32_t *retlen);
+int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen);
+int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f);
+int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen);
+
 
 /* readinode.c */
-void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size);
-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn);
+void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size);
 int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn);
-void jffs2_read_inode (struct inode *);
-void jffs2_clear_inode (struct inode *);
+int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 
+			uint32_t ino, struct jffs2_raw_inode *latest_node);
+int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic);
+void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f);
 
 /* malloc.c */
-void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn);
-void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd);
-
 int jffs2_create_slab_caches(void);
 void jffs2_destroy_slab_caches(void);
 
@@ -301,54 +464,45 @@
 /* gc.c */
 int jffs2_garbage_collect_pass(struct jffs2_sb_info *c);
 
-/* background.c */
-int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
-void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
-void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c);
-
-/* dir.c */
-extern struct file_operations jffs2_dir_operations;
-extern struct inode_operations jffs2_dir_inode_operations;
-
-/* file.c */
-extern struct file_operations jffs2_file_operations;
-extern struct inode_operations jffs2_file_inode_operations;
-extern struct address_space_operations jffs2_file_address_operations;
-int jffs2_null_fsync(struct file *, struct dentry *, int);
-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr);
-int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg);
-int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
-int jffs2_readpage (struct file *, struct page *);
-int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned);
-int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned);
-
-/* ioctl.c */
-int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
-
 /* read.c */
-int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len);
+int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+		     struct jffs2_full_dnode *fd, unsigned char *buf,
+		     int ofs, int len);
+int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+			   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(unsigned char *data_in, unsigned char *cpage_out, 
-			     __u32 *datalen, __u32 *cdatalen);
-int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, 
-		     unsigned char *data_out, __u32 cdatalen, __u32 datalen);
+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);
 
 /* build.c */
-int jffs2_build_filesystem(struct jffs2_sb_info *c);
-
-/* symlink.c */
-extern struct inode_operations jffs2_symlink_inode_operations;
+int jffs2_do_mount_fs(struct jffs2_sb_info *c);
 
 /* erase.c */
 void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c);
-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c);
-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c);
+void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count);
+
+#ifdef CONFIG_JFFS2_FS_NAND
+/* 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 -Nur linux-2.4.30/fs/jffs2/nodemgmt.c linux-2.4.30-linux-mips.org/fs/jffs2/nodemgmt.c
--- linux-2.4.30/fs/jffs2/nodemgmt.c	2002-08-03 02:39:45.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/jffs2/nodemgmt.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,45 +1,21 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: nodemgmt.c,v 1.45.2.1 2002/02/23 14:13:34 dwmw2 Exp $
+ * $Id: nodemgmt.c,v 1.107 2003/11/26 15:30:58 dwmw2 Exp $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
-#include <linux/jffs2.h>
 #include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
+#include <linux/compiler.h>
+#include <linux/sched.h> /* For cond_resched() */
 #include "nodelist.h"
 
 /**
@@ -62,53 +38,95 @@
  *	for the requested allocation.
  */
 
-static int jffs2_do_reserve_space(struct jffs2_sb_info *c,  __u32 minsize, __u32 *ofs, __u32 *len);
+static int jffs2_do_reserve_space(struct jffs2_sb_info *c,  uint32_t minsize, uint32_t *ofs, uint32_t *len);
 
-int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio)
+int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio)
 {
 	int ret = -EAGAIN;
-	int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE;
+	int blocksneeded = c->resv_blocks_write;
 	/* align it */
 	minsize = PAD(minsize);
 
-	if (prio == ALLOC_DELETION)
-		blocksneeded = JFFS2_RESERVED_BLOCKS_DELETION;
-
 	D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize));
 	down(&c->alloc_sem);
 
 	D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n"));
 
-	spin_lock_bh(&c->erase_completion_lock);
+	spin_lock(&c->erase_completion_lock);
 
-	/* this needs a little more thought */
+	/* this needs a little more thought (true <tglx> :)) */
 	while(ret == -EAGAIN) {
 		while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) {
 			int ret;
+			uint32_t dirty, avail;
 
-			up(&c->alloc_sem);
-			if (c->dirty_size < c->sector_size) {
-				D1(printk(KERN_DEBUG "Short on space, but total dirty size 0x%08x < sector size 0x%08x, so -ENOSPC\n", c->dirty_size, c->sector_size));
-				spin_unlock_bh(&c->erase_completion_lock);
+			/* calculate real dirty size
+			 * dirty_size contains blocks on erase_pending_list
+			 * those blocks are counted in c->nr_erasing_blocks.
+			 * If one block is actually erased, it is not longer counted as dirty_space
+			 * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
+			 * with c->nr_erasing_blocks * c->sector_size again.
+			 * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
+			 * This helps us to force gc and pick eventually a clean block to spread the load.
+			 * We add unchecked_size here, as we hopefully will find some space to use.
+			 * This will affect the sum only once, as gc first finishes checking
+			 * of nodes.
+			 */
+			dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size;
+			if (dirty < c->nospc_dirty_size) {
+				if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
+					printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n");
+					break;
+				}
+				D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n",
+					  dirty, c->unchecked_size, c->sector_size));
+
+				spin_unlock(&c->erase_completion_lock);
+				up(&c->alloc_sem);
+				return -ENOSPC;
+			}
+			
+			/* Calc possibly available space. Possibly available means that we
+			 * don't know, if unchecked size contains obsoleted nodes, which could give us some
+			 * more usable space. This will affect the sum only once, as gc first finishes checking
+			 * of nodes.
+			 + Return -ENOSPC, if the maximum possibly available space is less or equal than 
+			 * blocksneeded * sector_size.
+			 * This blocks endless gc looping on a filesystem, which is nearly full, even if
+			 * the check above passes.
+			 */
+			avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size;
+			if ( (avail / c->sector_size) <= blocksneeded) {
+				if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) {
+					printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n");
+					break;
+				}
+
+				D1(printk(KERN_DEBUG "max. available size 0x%08x  < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n",
+					  avail, blocksneeded * c->sector_size));
+				spin_unlock(&c->erase_completion_lock);
+				up(&c->alloc_sem);
 				return -ENOSPC;
 			}
-			D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
-				  c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size,
-				  c->free_size + c->dirty_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
-			spin_unlock_bh(&c->erase_completion_lock);
+
+			up(&c->alloc_sem);
+
+			D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n",
+				  c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size,
+				  c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size));
+			spin_unlock(&c->erase_completion_lock);
 			
 			ret = jffs2_garbage_collect_pass(c);
 			if (ret)
 				return ret;
 
-			if (current->need_resched)
-				schedule();
+			cond_resched();
 
 			if (signal_pending(current))
 				return -EINTR;
 
 			down(&c->alloc_sem);
-			spin_lock_bh(&c->erase_completion_lock);
+			spin_lock(&c->erase_completion_lock);
 		}
 
 		ret = jffs2_do_reserve_space(c, minsize, ofs, len);
@@ -116,45 +134,72 @@
 			D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret));
 		}
 	}
-	spin_unlock_bh(&c->erase_completion_lock);
+	spin_unlock(&c->erase_completion_lock);
 	if (ret)
 		up(&c->alloc_sem);
 	return ret;
 }
 
-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len)
+int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len)
 {
 	int ret = -EAGAIN;
 	minsize = PAD(minsize);
 
 	D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize));
 
-	spin_lock_bh(&c->erase_completion_lock);
+	spin_lock(&c->erase_completion_lock);
 	while(ret == -EAGAIN) {
 		ret = jffs2_do_reserve_space(c, minsize, ofs, len);
 		if (ret) {
 		        D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret));
 		}
 	}
-	spin_unlock_bh(&c->erase_completion_lock);
+	spin_unlock(&c->erase_completion_lock);
 	return ret;
 }
 
 /* Called with alloc sem _and_ erase_completion_lock */
-static int jffs2_do_reserve_space(struct jffs2_sb_info *c,  __u32 minsize, __u32 *ofs, __u32 *len)
+static int jffs2_do_reserve_space(struct jffs2_sb_info *c,  uint32_t minsize, uint32_t *ofs, uint32_t *len)
 {
 	struct jffs2_eraseblock *jeb = c->nextblock;
 	
  restart:
 	if (jeb && minsize > jeb->free_size) {
 		/* Skip the end of this block and file it as having some dirty space */
-		c->dirty_size += jeb->free_size;
+		/* If there's a pending write to it, flush now */
+		if (jffs2_wbuf_dirty(c)) {
+			spin_unlock(&c->erase_completion_lock);
+			D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));			    
+			jffs2_flush_wbuf_pad(c);
+			spin_lock(&c->erase_completion_lock);
+			jeb = c->nextblock;
+			goto restart;
+		}
+		c->wasted_size += jeb->free_size;
 		c->free_size -= jeb->free_size;
-		jeb->dirty_size += jeb->free_size;
+		jeb->wasted_size += jeb->free_size;
 		jeb->free_size = 0;
-		D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+		
+		/* Check, if we have a dirty block now, or if it was dirty already */
+		if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) {
+			c->dirty_size += jeb->wasted_size;
+			c->wasted_size -= jeb->wasted_size;
+			jeb->dirty_size += jeb->wasted_size;
+			jeb->wasted_size = 0;
+			if (VERYDIRTY(c, jeb->dirty_size)) {
+				D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+				  jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+				list_add_tail(&jeb->list, &c->very_dirty_list);
+			} else {
+				D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n",
+				  jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size));
+				list_add_tail(&jeb->list, &c->dirty_list);
+			}
+		} else { 
+			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));
-		list_add_tail(&jeb->list, &c->dirty_list);
+			list_add_tail(&jeb->list, &c->clean_list);
+		}
 		c->nextblock = jeb = NULL;
 	}
 	
@@ -164,33 +209,44 @@
 
 		if (list_empty(&c->free_list)) {
 
-			DECLARE_WAITQUEUE(wait, current);
-			
+			if (!c->nr_erasing_blocks && 
+			    !list_empty(&c->erasable_list)) {
+				struct jffs2_eraseblock *ejeb;
+
+				ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list);
+				list_del(&ejeb->list);
+				list_add_tail(&ejeb->list, &c->erase_pending_list);
+				c->nr_erasing_blocks++;
+				jffs2_erase_pending_trigger(c);
+				D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Triggering erase of erasable block at 0x%08x\n",
+					  ejeb->offset));
+			}
+
+			if (!c->nr_erasing_blocks && 
+			    !list_empty(&c->erasable_pending_wbuf_list)) {
+				D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n"));
+				/* c->nextblock is NULL, no update to c->nextblock allowed */			    
+				spin_unlock(&c->erase_completion_lock);
+				jffs2_flush_wbuf_pad(c);
+				spin_lock(&c->erase_completion_lock);
+				/* Have another go. It'll be on the erasable_list now */
+				return -EAGAIN;
+			}
+
 			if (!c->nr_erasing_blocks) {
-//			if (list_empty(&c->erasing_list) && list_empty(&c->erase_pending_list) && list_empty(c->erase_complete_list)) {
 				/* Ouch. We're in GC, or we wouldn't have got here.
 				   And there's no space left. At all. */
-				printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n", 
-				       c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
+				printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", 
+				       c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no", 
+				       list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no");
 				return -ENOSPC;
 			}
-			/* Make sure this can't deadlock. Someone has to start the erases
-			   of erase_pending blocks */
-			set_current_state(TASK_INTERRUPTIBLE);
-			add_wait_queue(&c->erase_wait, &wait);
-			D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n", 
-				  c->nr_erasing_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"));
-			if (!list_empty(&c->erase_pending_list)) {
-				D1(printk(KERN_DEBUG "Triggering pending erases\n"));
-				jffs2_erase_pending_trigger(c);
-			}
-			spin_unlock_bh(&c->erase_completion_lock);
-			schedule();
-			remove_wait_queue(&c->erase_wait, &wait);
-			spin_lock_bh(&c->erase_completion_lock);
-			if (signal_pending(current)) {
-				return -EINTR;
-			}
+
+			spin_unlock(&c->erase_completion_lock);
+			/* Don't wait for it; just erase one right now */
+			jffs2_erase_pending_blocks(c, 1);
+			spin_lock(&c->erase_completion_lock);
+
 			/* An erase may have failed, decreasing the
 			   amount of free space available. So we must
 			   restart from the beginning */
@@ -201,7 +257,8 @@
 		list_del(next);
 		c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list);
 		c->nr_free_blocks--;
-		if (jeb->free_size != c->sector_size - sizeof(struct jffs2_unknown_node)) {
+
+		if (jeb->free_size != c->sector_size - c->cleanmarker_size) {
 			printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size);
 			goto restart;
 		}
@@ -210,6 +267,20 @@
 	   enough space */
 	*ofs = jeb->offset + (c->sector_size - jeb->free_size);
 	*len = jeb->free_size;
+
+	if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size &&
+	    !jeb->first_node->next_in_ino) {
+		/* Only node in it beforehand was a CLEANMARKER node (we think). 
+		   So mark it obsolete now that there's going to be another node
+		   in the block. This will reduce used_size to zero but We've 
+		   already set c->nextblock so that jffs2_mark_node_obsolete()
+		   won't try to refile it to the dirty_list.
+		*/
+		spin_unlock(&c->erase_completion_lock);
+		jffs2_mark_node_obsolete(c, jeb->first_node);
+		spin_lock(&c->erase_completion_lock);
+	}
+
 	D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs));
 	return 0;
 }
@@ -217,9 +288,9 @@
 /**
  *	jffs2_add_physical_node_ref - add a physical node reference to the list
  *	@c: superblock info
- *	@ofs: physical location of this physical node
+ *	@new: new node reference to add
  *	@len: length of this physical node
- *	@ino: inode number with which this physical node is associated
+ *	@dirty: dirty flag for new node
  *
  *	Should only be used to report nodes for which space has been allocated 
  *	by jffs2_reserve_space.
@@ -227,47 +298,58 @@
  *	Must be called with the alloc_sem held.
  */
  
-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty)
+int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new)
 {
 	struct jffs2_eraseblock *jeb;
+	uint32_t len;
+
+	jeb = &c->blocks[new->flash_offset / c->sector_size];
+	len = ref_totlen(c, jeb, new);
 
-	len = PAD(len);
-	jeb = &c->blocks[(new->flash_offset & ~3) / c->sector_size];
-	D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x, size 0x%x\n", new->flash_offset & ~3, len));
+	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 || (new->flash_offset & ~3) != jeb->offset + (c->sector_size - jeb->free_size)) {
+	if (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;
 	}
 #endif
+	spin_lock(&c->erase_completion_lock);
+
 	if (!jeb->first_node)
 		jeb->first_node = new;
 	if (jeb->last_node)
 		jeb->last_node->next_phys = new;
 	jeb->last_node = new;
 
-	spin_lock_bh(&c->erase_completion_lock);
 	jeb->free_size -= len;
 	c->free_size -= len;
-	if (dirty) {
-		new->flash_offset |= 1;
+	if (ref_obsolete(new)) {
 		jeb->dirty_size += len;
 		c->dirty_size += len;
 	} else {
 		jeb->used_size += len;
 		c->used_size += len;
 	}
-	spin_unlock_bh(&c->erase_completion_lock);
+
 	if (!jeb->free_size && !jeb->dirty_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));
+		if (jffs2_wbuf_dirty(c)) {
+			/* Flush the last write in the block if it's outstanding */
+			spin_unlock(&c->erase_completion_lock);
+			jffs2_flush_wbuf_pad(c);
+			spin_lock(&c->erase_completion_lock);
+		}
+
 		list_add_tail(&jeb->list, &c->clean_list);
 		c->nextblock = NULL;
 	}
 	ACCT_SANITY_CHECK(c,jeb);
-	ACCT_PARANOIA_CHECK(jeb);
+	D1(ACCT_PARANOIA_CHECK(jeb));
+
+	spin_unlock(&c->erase_completion_lock);
 
 	return 0;
 }
@@ -280,20 +362,34 @@
 	up(&c->alloc_sem);
 }
 
+static inline int on_list(struct list_head *obj, struct list_head *head)
+{
+	struct list_head *this;
+
+	list_for_each(this, head) {
+		if (this == obj) {
+			D1(printk("%p is on list at %p\n", obj, head));
+			return 1;
+
+		}
+	}
+	return 0;
+}
+
 void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref)
 {
 	struct jffs2_eraseblock *jeb;
 	int blocknr;
 	struct jffs2_unknown_node n;
-	int ret;
-	ssize_t retlen;
+	int ret, addedsize;
+	size_t retlen;
 
 	if(!ref) {
 		printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n");
 		return;
 	}
-	if (ref->flash_offset & 1) {
-		D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref->flash_offset &~3));
+	if (ref_obsolete(ref)) {
+		D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref_offset(ref)));
 		return;
 	}
 	blocknr = ref->flash_offset / c->sector_size;
@@ -302,22 +398,63 @@
 		BUG();
 	}
 	jeb = &c->blocks[blocknr];
-	if (jeb->used_size < ref->totlen) {
-		printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
-		       ref->totlen, blocknr, ref->flash_offset, jeb->used_size);
-		BUG();
-	}
 
-	spin_lock_bh(&c->erase_completion_lock);
-	jeb->used_size -= ref->totlen;
-	jeb->dirty_size += ref->totlen;
-	c->used_size -= ref->totlen;
-	c->dirty_size += ref->totlen;
-	ref->flash_offset |= 1;
+	spin_lock(&c->erase_completion_lock);
+
+	if (ref_flags(ref) == REF_UNCHECKED) {
+		D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) {
+			printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n",
+			       ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
+			BUG();
+		})
+		D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
+		jeb->unchecked_size -= ref_totlen(c, jeb, ref);
+		c->unchecked_size -= ref_totlen(c, jeb, ref);
+	} else {
+		D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) {
+			printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n",
+			       ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size);
+			BUG();
+		})
+		D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref)));
+		jeb->used_size -= ref_totlen(c, jeb, ref);
+		c->used_size -= ref_totlen(c, jeb, ref);
+	}
+
+	// Take care, that wasted size is taken into concern
+	if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) {
+		D1(printk("Dirtying\n"));
+		addedsize = ref_totlen(c, jeb, ref);
+		jeb->dirty_size += ref_totlen(c, jeb, ref);
+		c->dirty_size += ref_totlen(c, jeb, ref);
+
+		/* Convert wasted space to dirty, if not a bad block */
+		if (jeb->wasted_size) {
+			if (on_list(&jeb->list, &c->bad_used_list)) {
+				D1(printk(KERN_DEBUG "Leaving block at %08x on the bad_used_list\n",
+					  jeb->offset));
+				addedsize = 0; /* To fool the refiling code later */
+			} else {
+				D1(printk(KERN_DEBUG "Converting %d bytes of wasted space to dirty in block at %08x\n",
+					  jeb->wasted_size, jeb->offset));
+				addedsize += jeb->wasted_size;
+				jeb->dirty_size += jeb->wasted_size;
+				c->dirty_size += jeb->wasted_size;
+				c->wasted_size -= jeb->wasted_size;
+				jeb->wasted_size = 0;
+			}
+		}
+	} else {
+		D1(printk("Wasting\n"));
+		addedsize = 0;
+		jeb->wasted_size += ref_totlen(c, jeb, ref);
+		c->wasted_size += ref_totlen(c, jeb, ref);	
+	}
+	ref->flash_offset = ref_offset(ref) | REF_OBSOLETE;
 	
 	ACCT_SANITY_CHECK(c, jeb);
 
-	ACCT_PARANOIA_CHECK(jeb);
+	D1(ACCT_PARANOIA_CHECK(jeb));
 
 	if (c->flags & JFFS2_SB_FLAG_MOUNTING) {
 		/* Mount in progress. Don't muck about with the block
@@ -325,68 +462,280 @@
 		   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_bh(&c->erase_completion_lock);
+		spin_unlock(&c->erase_completion_lock);
 		return;
 	}
+
 	if (jeb == c->nextblock) {
 		D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
-	} else if (jeb == c->gcblock) {
-		D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty/erase_pending list\n", jeb->offset));
-#if 0 /* We no longer do this here. It can screw the wear levelling. If you have a lot of static
-	 data and a few blocks free, and you just create new files and keep deleting/overwriting
-	 them, then you'd keep erasing and reusing those blocks without ever moving stuff around.
-	 So we leave completely obsoleted blocks on the dirty_list and let the GC delete them 
-	 when it finds them there. That way, we still get the 'once in a while, take a clean block'
-	 to spread out the flash usage */
-	} else if (!jeb->used_size) {
-		D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset));
-		list_del(&jeb->list);
-		D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
-		list_add_tail(&jeb->list, &c->erase_pending_list);
-		c->nr_erasing_blocks++;
-		jffs2_erase_pending_trigger(c);
-		//		OFNI_BS_2SFFJ(c)->s_dirt = 1;
+	} else if (!jeb->used_size && !jeb->unchecked_size) {
+		if (jeb == c->gcblock) {
+			D1(printk(KERN_DEBUG "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n", jeb->offset));
+			c->gcblock = NULL;
+		} else {
+			D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset));
+			list_del(&jeb->list);
+		}
+		if (jffs2_wbuf_dirty(c)) {
+			D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n"));
+			list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list);
+		} else {
+			if (jiffies & 127) {
+				/* Most of the time, we just erase it immediately. Otherwise we
+				   spend ages scanning it on mount, etc. */
+				D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
+				list_add_tail(&jeb->list, &c->erase_pending_list);
+				c->nr_erasing_blocks++;
+				jffs2_erase_pending_trigger(c);
+			} else {
+				/* Sometimes, however, we leave it elsewhere so it doesn't get
+				   immediately reused, and we spread the load a bit. */
+				D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
+				list_add_tail(&jeb->list, &c->erasable_list);
+			}				
+		}
 		D1(printk(KERN_DEBUG "Done OK\n"));
-#endif
-	} else if (jeb->dirty_size == ref->totlen) {
+	} else if (jeb == c->gcblock) {
+		D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset));
+	} else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - addedsize)) {
 		D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n", jeb->offset));
 		list_del(&jeb->list);
 		D1(printk(KERN_DEBUG "...and adding to dirty_list\n"));
 		list_add_tail(&jeb->list, &c->dirty_list);
-	}
-	spin_unlock_bh(&c->erase_completion_lock);
+	} else if (VERYDIRTY(c, jeb->dirty_size) &&
+		   !VERYDIRTY(c, jeb->dirty_size - addedsize)) {
+		D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset));
+		list_del(&jeb->list);
+		D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n"));
+		list_add_tail(&jeb->list, &c->very_dirty_list);
+	} else {
+		D1(printk(KERN_DEBUG "Eraseblock at 0x%08x not moved anywhere. (free 0x%08x, dirty 0x%08x, used 0x%08x)\n",
+			  jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); 
+	}			  	
 
-	if (c->mtd->type != MTD_NORFLASH && c->mtd->type != MTD_RAM)
+	spin_unlock(&c->erase_completion_lock);
+
+	if (!jffs2_can_mark_obsolete(c))
 		return;
-	if (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
+	if (jffs2_is_readonly(c))
 		return;
 
-	D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref->flash_offset &~3));
-	ret = c->mtd->read(c->mtd, ref->flash_offset &~3, sizeof(n), &retlen, (char *)&n);
+	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->flash_offset &~3, ret);
+		printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
 		return;
 	}
 	if (retlen != sizeof(n)) {
-		printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
+		printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
 		return;
 	}
-	if (PAD(n.totlen) != PAD(ref->totlen)) {
-		printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", n.totlen, ref->totlen);
+	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;
 	}
-	if (!(n.nodetype & JFFS2_NODE_ACCURATE)) {
-		D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref->flash_offset &~3, n.nodetype));
+	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;
 	}
-	n.nodetype &= ~JFFS2_NODE_ACCURATE;
-	ret = c->mtd->write(c->mtd, ref->flash_offset&~3, sizeof(n), &retlen, (char *)&n);
+	/* 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->flash_offset &~3, ret);
+		printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret);
 		return;
 	}
 	if (retlen != sizeof(n)) {
-		printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen);
+		printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen);
 		return;
 	}
 }
+
+#if CONFIG_JFFS2_FS_DEBUG > 0
+void jffs2_dump_block_lists(struct jffs2_sb_info *c)
+{
+
+
+	printk(KERN_DEBUG "jffs2_dump_block_lists:\n");
+	printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
+	printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
+	printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
+	printk(KERN_DEBUG "wasted_size: %08x\n", c->wasted_size);
+	printk(KERN_DEBUG "unchecked_size: %08x\n", c->unchecked_size);
+	printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
+	printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
+	printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
+	printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
+	printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * c->resv_blocks_write);
+
+	if (c->nextblock) {
+		printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+		       c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->unchecked_size, c->nextblock->free_size);
+	} else {
+		printk(KERN_DEBUG "nextblock: NULL\n");
+	}
+	if (c->gcblock) {
+		printk(KERN_DEBUG "gcblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+		       c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size);
+	} else {
+		printk(KERN_DEBUG "gcblock: NULL\n");
+	}
+	if (list_empty(&c->clean_list)) {
+		printk(KERN_DEBUG "clean_list: empty\n");
+	} else {
+		struct list_head *this;
+		int	numblocks = 0;
+		uint32_t dirty = 0;
+
+		list_for_each(this, &c->clean_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			numblocks ++;
+			dirty += jeb->wasted_size;
+			printk(KERN_DEBUG "clean_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+		printk (KERN_DEBUG "Contains %d blocks with total wasted size %u, average wasted size: %u\n", numblocks, dirty, dirty / numblocks);
+	}
+	if (list_empty(&c->very_dirty_list)) {
+		printk(KERN_DEBUG "very_dirty_list: empty\n");
+	} else {
+		struct list_head *this;
+		int	numblocks = 0;
+		uint32_t dirty = 0;
+
+		list_for_each(this, &c->very_dirty_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			numblocks ++;
+			dirty += jeb->dirty_size;
+			printk(KERN_DEBUG "very_dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+		printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
+			numblocks, dirty, dirty / numblocks);
+	}
+	if (list_empty(&c->dirty_list)) {
+		printk(KERN_DEBUG "dirty_list: empty\n");
+	} else {
+		struct list_head *this;
+		int	numblocks = 0;
+		uint32_t dirty = 0;
+
+		list_for_each(this, &c->dirty_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			numblocks ++;
+			dirty += jeb->dirty_size;
+			printk(KERN_DEBUG "dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+		printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n",
+			numblocks, dirty, dirty / numblocks);
+	}
+	if (list_empty(&c->erasable_list)) {
+		printk(KERN_DEBUG "erasable_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->erasable_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			printk(KERN_DEBUG "erasable_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+	}
+	if (list_empty(&c->erasing_list)) {
+		printk(KERN_DEBUG "erasing_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->erasing_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			printk(KERN_DEBUG "erasing_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+	}
+	if (list_empty(&c->erase_pending_list)) {
+		printk(KERN_DEBUG "erase_pending_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->erase_pending_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			printk(KERN_DEBUG "erase_pending_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+	}
+	if (list_empty(&c->erasable_pending_wbuf_list)) {
+		printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->erasable_pending_wbuf_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			printk(KERN_DEBUG "erasable_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+	}
+	if (list_empty(&c->free_list)) {
+		printk(KERN_DEBUG "free_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->free_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			printk(KERN_DEBUG "free_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+	}
+	if (list_empty(&c->bad_list)) {
+		printk(KERN_DEBUG "bad_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->bad_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			printk(KERN_DEBUG "bad_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+	}
+	if (list_empty(&c->bad_used_list)) {
+		printk(KERN_DEBUG "bad_used_list: empty\n");
+	} else {
+		struct list_head *this;
+
+		list_for_each(this, &c->bad_used_list) {
+			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+			printk(KERN_DEBUG "bad_used_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n",
+			       jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size);
+		}
+	}
+}
+#endif /* CONFIG_JFFS2_FS_DEBUG */
+
+int jffs2_thread_should_wake(struct jffs2_sb_info *c)
+{
+	int ret = 0;
+	uint32_t dirty;
+
+	if (c->unchecked_size) {
+		D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n",
+			  c->unchecked_size, c->checked_ino));
+		return 1;
+	}
+
+	/* dirty_size contains blocks on erase_pending_list
+	 * those blocks are counted in c->nr_erasing_blocks.
+	 * If one block is actually erased, it is not longer counted as dirty_space
+	 * but it is counted in c->nr_erasing_blocks, so we add it and subtract it
+	 * with c->nr_erasing_blocks * c->sector_size again.
+	 * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks
+	 * This helps us to force gc and pick eventually a clean block to spread the load.
+	 */
+	dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size;
+
+	if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger && 
+			(dirty > c->nospc_dirty_size)) 
+		ret = 1;
+
+	D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", 
+		  c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no"));
+
+	return ret;
+}
diff -Nur linux-2.4.30/fs/jffs2/os-linux.h linux-2.4.30-linux-mips.org/fs/jffs2/os-linux.h
--- linux-2.4.30/fs/jffs2/os-linux.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/os-linux.h	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,214 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2002-2003 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+ * 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 $
+ *
+ */
+
+#ifndef __JFFS2_OS_LINUX_H__
+#define __JFFS2_OS_LINUX_H__
+#include <linux/version.h>
+
+/* JFFS2 uses Linux mode bits natively -- no need for conversion */
+#define os_to_jffs2_mode(x) (x)
+#define jffs2_to_os_mode(x) (x)
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
+#define kstatfs statfs
+#endif
+
+#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)
+#define JFFS2_SB_INFO(sb) (sb->s_fs_info)
+#define OFNI_BS_2SFFJ(c)  ((struct super_block *)c->os_priv)
+#elif defined(JFFS2_OUT_OF_KERNEL)
+#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u)
+#define OFNI_EDONI_2SFFJ(f)  ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
+#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u)
+#define OFNI_BS_2SFFJ(c)  ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
+#else
+#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i)
+#define OFNI_EDONI_2SFFJ(f)  ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) )
+#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb)
+#define OFNI_BS_2SFFJ(c)  ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
+#endif
+
+
+#define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size)
+#define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode)
+#define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid)
+#define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid)
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,1)
+#define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f)))
+#define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f)))
+#else
+#define JFFS2_F_I_RDEV_MIN(f) (MINOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
+#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev)))
+#endif
+
+/* Urgh. The things we do to keep the 2.4 build working */
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,47)
+#define ITIME(sec) ((struct timespec){sec, 0})
+#define I_SEC(tv) ((tv).tv_sec)
+#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime.tv_sec)
+#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime.tv_sec)
+#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime.tv_sec)
+#else
+#define ITIME(x) (x)
+#define I_SEC(x) (x)
+#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime)
+#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime)
+#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime)
+#endif
+
+#define sleep_on_spinunlock(wq, s)				\
+	do {							\
+		DECLARE_WAITQUEUE(__wait, current);		\
+		add_wait_queue((wq), &__wait);			\
+		set_current_state(TASK_UNINTERRUPTIBLE);	\
+		spin_unlock(s);					\
+		schedule();					\
+		remove_wait_queue((wq), &__wait);		\
+	} while(0)
+
+static inline void jffs2_init_inode_info(struct jffs2_inode_info *f)
+{
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
+	f->highest_version = 0;
+	f->fragtree = RB_ROOT;
+	f->metadata = NULL;
+	f->dents = NULL;
+	f->flags = 0;
+	f->usercompr = 0;
+#else
+	memset(f, 0, sizeof(*f));
+	init_MUTEX_LOCKED(&f->sem);
+#endif
+}
+
+#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)
+
+#ifndef CONFIG_JFFS2_FS_NAND
+#define jffs2_can_mark_obsolete(c) (1)
+#define jffs2_cleanmarker_oob(c) (0)
+#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO)
+
+#define jffs2_flash_write(c, ofs, len, retlen, buf) ((c)->mtd->write((c)->mtd, ofs, len, retlen, buf))
+#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_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
+
+#else /* NAND support present */
+
+#define jffs2_can_mark_obsolete(c) (c->mtd->type == MTD_NORFLASH || 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_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);
+void jffs2_wbuf_timeout(unsigned long data);
+void jffs2_wbuf_process(void *data);
+int jffs2_nand_flash_setup(struct jffs2_sb_info *c);
+void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c);
+#endif /* NAND */
+
+/* erase.c */
+static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c)
+{
+	OFNI_BS_2SFFJ(c)->s_dirt = 1;
+}
+
+/* background.c */
+int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c);
+void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c);
+void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c);
+
+/* dir.c */
+extern struct file_operations jffs2_dir_operations;
+extern struct inode_operations jffs2_dir_inode_operations;
+
+/* file.c */
+extern struct file_operations jffs2_file_operations;
+extern struct inode_operations jffs2_file_inode_operations;
+extern struct address_space_operations jffs2_file_address_operations;
+int jffs2_fsync(struct file *, struct dentry *, int);
+int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg);
+int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg);
+int jffs2_readpage (struct file *, struct page *);
+int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned);
+int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned);
+
+/* ioctl.c */
+int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+/* symlink.c */
+extern struct inode_operations jffs2_symlink_inode_operations;
+
+/* fs.c */
+int jffs2_setattr (struct dentry *, struct iattr *);
+void jffs2_read_inode (struct inode *);
+void jffs2_clear_inode (struct inode *);
+void jffs2_dirty_inode(struct inode *inode);
+struct inode *jffs2_new_inode (struct inode *dir_i, int mode,
+			       struct jffs2_raw_inode *ri);
+int jffs2_statfs (struct super_block *, struct kstatfs *);
+void jffs2_write_super (struct super_block *);
+int jffs2_remount_fs (struct super_block *, int *, char *);
+int jffs2_do_fill_super(struct super_block *sb, void *data, int silent);
+void jffs2_gc_release_inode(struct jffs2_sb_info *c,
+			    struct jffs2_inode_info *f);
+struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c,
+					      int inum, int nlink);
+
+unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, 
+				   struct jffs2_inode_info *f, 
+				   unsigned long offset,
+				   unsigned long *priv);
+void jffs2_gc_release_page(struct jffs2_sb_info *c,
+			   unsigned char *pg,
+			   unsigned long *priv);
+int jffs2_flash_setup(struct jffs2_sb_info *c);
+void jffs2_flash_cleanup(struct jffs2_sb_info *c);
+     
+
+/* writev.c */
+int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *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 -Nur linux-2.4.30/fs/jffs2/pushpull.h linux-2.4.30-linux-mips.org/fs/jffs2/pushpull.h
--- linux-2.4.30/fs/jffs2/pushpull.h	2001-10-05 00:13:18.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/jffs2/pushpull.h	2005-05-12 18:01:30.000000000 +0200
@@ -1,42 +1,21 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001, 2002 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: pushpull.h,v 1.5 2001/09/23 10:04:15 rmk Exp $
+ * $Id: pushpull.h,v 1.9 2003/10/04 08:33:06 dwmw2 Exp $
  *
  */
 
 #ifndef __PUSHPULL_H__
 #define __PUSHPULL_H__
+
+#include <linux/errno.h>
+
 struct pushpull {
 	unsigned char *buf;
 	unsigned int buflen;
@@ -44,9 +23,36 @@
 	unsigned int reserve;
 };
 
-void init_pushpull(struct pushpull *, char *, unsigned, unsigned, unsigned);
-int pushbit(struct pushpull *pp, int bit, int use_reserved);
-int pushedbits(struct pushpull *pp);
+
+static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve)
+{
+	pp->buf = buf;
+	pp->buflen = buflen;
+	pp->ofs = ofs;
+	pp->reserve = reserve;
+}
+
+static inline int pushbit(struct pushpull *pp, int bit, int use_reserved)
+{
+	if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) {
+		return -ENOSPC;
+	}
+
+	if (bit) {
+		pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7)));
+	}
+	else {
+		pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7)));
+	}
+	pp->ofs++;
+
+	return 0;
+}
+
+static inline int pushedbits(struct pushpull *pp)
+{
+	return pp->ofs;
+}
 
 static inline int pullbit(struct pushpull *pp)
 {
diff -Nur linux-2.4.30/fs/jffs2/rbtree.c linux-2.4.30-linux-mips.org/fs/jffs2/rbtree.c
--- linux-2.4.30/fs/jffs2/rbtree.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/rbtree.c	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,363 @@
+/*
+  Red Black Trees
+  (C) 1999  Andrea Arcangeli <andrea@suse.de>
+  (C) 2002  David Woodhouse <dwmw2@infradead.org>
+  
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  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: rbtree.c,v 1.3 2003/10/16 08:02:19 dwmw2 Exp $
+*/
+
+#ifdef __ECOS /* This file is _not_ under the eCos licence; it is pure GPL. */
+#error "Licence problem. eCos has its own rbtree code."
+#endif
+
+#include <linux/version.h>
+#include <linux/rbtree.h>
+
+/* This wasn't present till 2.4.11, wasn't exported till 2.4.19 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,11) || \
+   (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) && defined(MODULE))
+static void __rb_rotate_left(struct rb_node * node, struct rb_root * root)
+{
+	struct rb_node * right = node->rb_right;
+
+	if ((node->rb_right = right->rb_left))
+		right->rb_left->rb_parent = node;
+	right->rb_left = node;
+
+	if ((right->rb_parent = node->rb_parent))
+	{
+		if (node == node->rb_parent->rb_left)
+			node->rb_parent->rb_left = right;
+		else
+			node->rb_parent->rb_right = right;
+	}
+	else
+		root->rb_node = right;
+	node->rb_parent = right;
+}
+
+static void __rb_rotate_right(struct rb_node * node, struct rb_root * root)
+{
+	struct rb_node * left = node->rb_left;
+
+	if ((node->rb_left = left->rb_right))
+		left->rb_right->rb_parent = node;
+	left->rb_right = node;
+
+	if ((left->rb_parent = node->rb_parent))
+	{
+		if (node == node->rb_parent->rb_right)
+			node->rb_parent->rb_right = left;
+		else
+			node->rb_parent->rb_left = left;
+	}
+	else
+		root->rb_node = left;
+	node->rb_parent = left;
+}
+
+void rb_insert_color(struct rb_node * node, struct rb_root * root)
+{
+	struct rb_node * parent, * gparent;
+
+	while ((parent = node->rb_parent) && parent->rb_color == RB_RED)
+	{
+		gparent = parent->rb_parent;
+
+		if (parent == gparent->rb_left)
+		{
+			{
+				register struct rb_node * uncle = gparent->rb_right;
+				if (uncle && uncle->rb_color == RB_RED)
+				{
+					uncle->rb_color = RB_BLACK;
+					parent->rb_color = RB_BLACK;
+					gparent->rb_color = RB_RED;
+					node = gparent;
+					continue;
+				}
+			}
+
+			if (parent->rb_right == node)
+			{
+				register struct rb_node * tmp;
+				__rb_rotate_left(parent, root);
+				tmp = parent;
+				parent = node;
+				node = tmp;
+			}
+
+			parent->rb_color = RB_BLACK;
+			gparent->rb_color = RB_RED;
+			__rb_rotate_right(gparent, root);
+		} else {
+			{
+				register struct rb_node * uncle = gparent->rb_left;
+				if (uncle && uncle->rb_color == RB_RED)
+				{
+					uncle->rb_color = RB_BLACK;
+					parent->rb_color = RB_BLACK;
+					gparent->rb_color = RB_RED;
+					node = gparent;
+					continue;
+				}
+			}
+
+			if (parent->rb_left == node)
+			{
+				register struct rb_node * tmp;
+				__rb_rotate_right(parent, root);
+				tmp = parent;
+				parent = node;
+				node = tmp;
+			}
+
+			parent->rb_color = RB_BLACK;
+			gparent->rb_color = RB_RED;
+			__rb_rotate_left(gparent, root);
+		}
+	}
+
+	root->rb_node->rb_color = RB_BLACK;
+}
+
+static void __rb_erase_color(struct rb_node * node, struct rb_node * parent,
+			     struct rb_root * root)
+{
+	struct rb_node * other;
+
+	while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node)
+	{
+		if (parent->rb_left == node)
+		{
+			other = parent->rb_right;
+			if (other->rb_color == RB_RED)
+			{
+				other->rb_color = RB_BLACK;
+				parent->rb_color = RB_RED;
+				__rb_rotate_left(parent, root);
+				other = parent->rb_right;
+			}
+			if ((!other->rb_left ||
+			     other->rb_left->rb_color == RB_BLACK)
+			    && (!other->rb_right ||
+				other->rb_right->rb_color == RB_BLACK))
+			{
+				other->rb_color = RB_RED;
+				node = parent;
+				parent = node->rb_parent;
+			}
+			else
+			{
+				if (!other->rb_right ||
+				    other->rb_right->rb_color == RB_BLACK)
+				{
+					register struct rb_node * o_left;
+					if ((o_left = other->rb_left))
+						o_left->rb_color = RB_BLACK;
+					other->rb_color = RB_RED;
+					__rb_rotate_right(other, root);
+					other = parent->rb_right;
+				}
+				other->rb_color = parent->rb_color;
+				parent->rb_color = RB_BLACK;
+				if (other->rb_right)
+					other->rb_right->rb_color = RB_BLACK;
+				__rb_rotate_left(parent, root);
+				node = root->rb_node;
+				break;
+			}
+		}
+		else
+		{
+			other = parent->rb_left;
+			if (other->rb_color == RB_RED)
+			{
+				other->rb_color = RB_BLACK;
+				parent->rb_color = RB_RED;
+				__rb_rotate_right(parent, root);
+				other = parent->rb_left;
+			}
+			if ((!other->rb_left ||
+			     other->rb_left->rb_color == RB_BLACK)
+			    && (!other->rb_right ||
+				other->rb_right->rb_color == RB_BLACK))
+			{
+				other->rb_color = RB_RED;
+				node = parent;
+				parent = node->rb_parent;
+			}
+			else
+			{
+				if (!other->rb_left ||
+				    other->rb_left->rb_color == RB_BLACK)
+				{
+					register struct rb_node * o_right;
+					if ((o_right = other->rb_right))
+						o_right->rb_color = RB_BLACK;
+					other->rb_color = RB_RED;
+					__rb_rotate_left(other, root);
+					other = parent->rb_left;
+				}
+				other->rb_color = parent->rb_color;
+				parent->rb_color = RB_BLACK;
+				if (other->rb_left)
+					other->rb_left->rb_color = RB_BLACK;
+				__rb_rotate_right(parent, root);
+				node = root->rb_node;
+				break;
+			}
+		}
+	}
+	if (node)
+		node->rb_color = RB_BLACK;
+}
+
+void rb_erase(struct rb_node * node, struct rb_root * root)
+{
+	struct rb_node * child, * parent;
+	int color;
+
+	if (!node->rb_left)
+		child = node->rb_right;
+	else if (!node->rb_right)
+		child = node->rb_left;
+	else
+	{
+		struct rb_node * old = node, * left;
+
+		node = node->rb_right;
+		while ((left = node->rb_left))
+			node = left;
+		child = node->rb_right;
+		parent = node->rb_parent;
+		color = node->rb_color;
+
+		if (child)
+			child->rb_parent = parent;
+		if (parent)
+		{
+			if (parent->rb_left == node)
+				parent->rb_left = child;
+			else
+				parent->rb_right = child;
+		}
+		else
+			root->rb_node = child;
+
+		if (node->rb_parent == old)
+			parent = node;
+		node->rb_parent = old->rb_parent;
+		node->rb_color = old->rb_color;
+		node->rb_right = old->rb_right;
+		node->rb_left = old->rb_left;
+
+		if (old->rb_parent)
+		{
+			if (old->rb_parent->rb_left == old)
+				old->rb_parent->rb_left = node;
+			else
+				old->rb_parent->rb_right = node;
+		} else
+			root->rb_node = node;
+
+		old->rb_left->rb_parent = node;
+		if (old->rb_right)
+			old->rb_right->rb_parent = node;
+		goto color;
+	}
+
+	parent = node->rb_parent;
+	color = node->rb_color;
+
+	if (child)
+		child->rb_parent = parent;
+	if (parent)
+	{
+		if (parent->rb_left == node)
+			parent->rb_left = child;
+		else
+			parent->rb_right = child;
+	}
+	else
+		root->rb_node = child;
+
+ color:
+	if (color == RB_BLACK)
+		__rb_erase_color(child, parent, root);
+}
+
+	/* These routines haven't made it into 2.4 (yet) */
+struct rb_node *rb_next(struct rb_node *node)
+{
+	/* If we have a right-hand child, go down and then left as far
+	   as we can. */
+	if (node->rb_right) {
+		node = node->rb_right; 
+		while (node->rb_left)
+			node=node->rb_left;
+		return node;
+	}
+
+	/* No right-hand children.  Everything down and left is
+	   smaller than us, so any 'next' node must be in the general
+	   direction of our parent. Go up the tree; any time the
+	   ancestor is a right-hand child of its parent, keep going
+	   up. First time it's a left-hand child of its parent, said
+	   parent is our 'next' node. */
+	while (node->rb_parent && node == node->rb_parent->rb_right)
+		node = node->rb_parent;
+
+	return node->rb_parent;
+}
+
+struct rb_node *rb_prev(struct rb_node *node)
+{
+	if (node->rb_left) {
+		node = node->rb_left; 
+		while (node->rb_right)
+			node=node->rb_right;
+		return node;
+	}
+	while (node->rb_parent && node == node->rb_parent->rb_left)
+		node = node->rb_parent;
+
+	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)
+{
+	struct rb_node *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;
+}
diff -Nur linux-2.4.30/fs/jffs2/read.c linux-2.4.30-linux-mips.org/fs/jffs2/read.c
--- linux-2.4.30/fs/jffs2/read.c	2003-11-28 19:26:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/read.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,52 +1,31 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: read.c,v 1.13.2.2 2003/11/02 13:51:18 dwmw2 Exp $
+ * $Id: read.c,v 1.35 2004/03/08 15:29:09 dwmw2 Exp $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
-#include <linux/jffs2.h>
+#include <linux/crc32.h>
+#include <linux/pagemap.h>
 #include <linux/mtd/mtd.h>
+#include <linux/compiler.h>
 #include "nodelist.h"
-#include <linux/crc32.h>
 
-int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len)
+int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+		     struct jffs2_full_dnode *fd, unsigned char *buf,
+		     int ofs, int len)
 {
 	struct jffs2_raw_inode *ri;
 	size_t readlen;
-	__u32 crc;
+	uint32_t crc;
 	unsigned char *decomprbuf = NULL;
 	unsigned char *readbuf = NULL;
 	int ret = 0;
@@ -55,35 +34,41 @@
 	if (!ri)
 		return -ENOMEM;
 
-	ret = c->mtd->read(c->mtd, fd->raw->flash_offset & ~3, sizeof(*ri), &readlen, (char *)ri);
+	ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri);
 	if (ret) {
 		jffs2_free_raw_inode(ri);
-		printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", fd->raw->flash_offset & ~3, ret);
+		printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret);
 		return ret;
 	}
 	if (readlen != sizeof(*ri)) {
 		jffs2_free_raw_inode(ri);
-		printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%x bytes, got 0x%x\n", 
-		       fd->raw->flash_offset & ~3, sizeof(*ri), readlen);
+		printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n", 
+		       ref_offset(fd->raw), sizeof(*ri), readlen);
 		return -EIO;
 	}
 	crc = crc32(0, ri, sizeof(*ri)-8);
 
-	D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", fd->raw->flash_offset & ~3, ri->node_crc, crc, ri->dsize, ri->csize, ri->offset, buf));
-	if (crc != ri->node_crc) {
-		printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", ri->node_crc, crc, fd->raw->flash_offset & ~3);
+	D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n",
+		  ref_offset(fd->raw), je32_to_cpu(ri->node_crc),
+		  crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize),
+		  je32_to_cpu(ri->offset), buf));
+	if (crc != je32_to_cpu(ri->node_crc)) {
+		printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n",
+		       je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw));
 		ret = -EIO;
 		goto out_ri;
 	}
 	/* There was a bug where we wrote hole nodes out with csize/dsize
 	   swapped. Deal with it */
-	if (ri->compr == JFFS2_COMPR_ZERO && !ri->dsize && ri->csize) {
+	if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) && 
+	    je32_to_cpu(ri->csize)) {
 		ri->dsize = ri->csize;
-		ri->csize = 0;
+		ri->csize = cpu_to_je32(0);
 	}
 
-	D1(if(ofs + len > ri->dsize) {
-		printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n", len, ofs, ri->dsize);
+	D1(if(ofs + len > je32_to_cpu(ri->dsize)) {
+		printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n",
+		       len, ofs, je32_to_cpu(ri->dsize));
 		ret = -EINVAL;
 		goto out_ri;
 	});
@@ -100,18 +85,18 @@
 	   Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy 
 	   Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy
 	*/
-	if (ri->compr == JFFS2_COMPR_NONE && len == ri->dsize) {
+	if (ri->compr == JFFS2_COMPR_NONE && len == je32_to_cpu(ri->dsize)) {
 		readbuf = buf;
 	} else {
-		readbuf = kmalloc(ri->csize, GFP_KERNEL);
+		readbuf = kmalloc(je32_to_cpu(ri->csize), GFP_KERNEL);
 		if (!readbuf) {
 			ret = -ENOMEM;
 			goto out_ri;
 		}
 	}
 	if (ri->compr != JFFS2_COMPR_NONE) {
-		if (len < ri->dsize) {
-			decomprbuf = kmalloc(ri->dsize, GFP_KERNEL);
+		if (len < je32_to_cpu(ri->dsize)) {
+			decomprbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL);
 			if (!decomprbuf) {
 				ret = -ENOMEM;
 				goto out_readbuf;
@@ -123,31 +108,35 @@
 		decomprbuf = readbuf;
 	}
 
-	D2(printk(KERN_DEBUG "Read %d bytes to %p\n", ri->csize, readbuf));
-	ret = c->mtd->read(c->mtd, (fd->raw->flash_offset &~3) + sizeof(*ri), ri->csize, &readlen, readbuf);
+	D2(printk(KERN_DEBUG "Read %d bytes to %p\n", je32_to_cpu(ri->csize),
+		  readbuf));
+	ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri),
+			       je32_to_cpu(ri->csize), &readlen, readbuf);
 
-	if (!ret && readlen != ri->csize)
+	if (!ret && readlen != je32_to_cpu(ri->csize))
 		ret = -EIO;
 	if (ret)
 		goto out_decomprbuf;
 
-	crc = crc32(0, readbuf, ri->csize);
-	if (crc != ri->data_crc) {
-		printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", ri->data_crc, crc, fd->raw->flash_offset & ~3);
+	crc = crc32(0, readbuf, je32_to_cpu(ri->csize));
+	if (crc != je32_to_cpu(ri->data_crc)) {
+		printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n",
+		       je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw));
 		ret = -EIO;
 		goto out_decomprbuf;
 	}
 	D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc));
 	if (ri->compr != JFFS2_COMPR_NONE) {
-		D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", ri->csize, readbuf, ri->dsize, decomprbuf)); 
-		ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, ri->csize, ri->dsize);
+		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));
 		if (ret) {
 			printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
 			goto out_decomprbuf;
 		}
 	}
 
-	if (len < ri->dsize) {
+	if (len < je32_to_cpu(ri->dsize)) {
 		memcpy(buf, decomprbuf+ofs, len);
 	}
  out_decomprbuf:
@@ -161,3 +150,96 @@
 
 	return ret;
 }
+
+int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+			   unsigned char *buf, uint32_t offset, uint32_t len)
+{
+	uint32_t end = offset + len;
+	struct jffs2_node_frag *frag;
+	int ret;
+
+	D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n",
+		  f->inocache->ino, offset, offset+len));
+
+	frag = jffs2_lookup_node_frag(&f->fragtree, offset);
+
+	/* XXX FIXME: Where a single physical node actually shows up in two
+	   frags, we read it twice. Don't do that. */
+	/* Now we're pointing at the first frag which overlaps our page */
+	while(offset < end) {
+		D2(printk(KERN_DEBUG "jffs2_read_inode_range: offset %d, end %d\n", offset, end));
+		if (unlikely(!frag || frag->ofs > offset)) {
+			uint32_t holesize = end - offset;
+			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));
+			}
+			D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize));
+			memset(buf, 0, holesize);
+			buf += holesize;
+			offset += holesize;
+			continue;
+		} else if (unlikely(!frag->node)) {
+			uint32_t holeend = min(end, frag->ofs + frag->size);
+			D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size));
+			memset(buf, 0, holeend - offset);
+			buf += holeend - offset;
+			offset = holeend;
+			frag = frag_next(frag);
+			continue;
+		} else {
+			uint32_t readlen;
+			uint32_t fragofs; /* offset within the frag to start reading */
+			
+			fragofs = offset - frag->ofs;
+			readlen = min(frag->size - fragofs, end - offset);
+			D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n",
+				  frag->ofs+fragofs, frag->ofs+fragofs+readlen,
+				  ref_offset(frag->node->raw), ref_flags(frag->node->raw)));
+			ret = jffs2_read_dnode(c, f, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen);
+			D2(printk(KERN_DEBUG "node read done\n"));
+			if (ret) {
+				D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret));
+				memset(buf, 0, readlen);
+				return ret;
+			}
+			buf += readlen;
+			offset += readlen;
+			frag = frag_next(frag);
+			D2(printk(KERN_DEBUG "node read was OK. Looping\n"));
+		}
+	}
+	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 -Nur linux-2.4.30/fs/jffs2/readinode.c linux-2.4.30-linux-mips.org/fs/jffs2/readinode.c
--- linux-2.4.30/fs/jffs2/readinode.c	2003-11-28 19:26:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/readinode.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,79 +1,122 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: readinode.c,v 1.58.2.8 2003/11/02 13:54:20 dwmw2 Exp $
+ * $Id: readinode.c,v 1.113 2003/11/03 13:20:33 dwmw2 Exp $
  *
  */
 
-/* Given an inode, probably with existing list of fragments, add the new node
- * to the fragment list.
- */
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
+#include <linux/crc32.h>
+#include <linux/pagemap.h>
 #include <linux/mtd/mtd.h>
-#include <linux/jffs2.h>
+#include <linux/compiler.h>
 #include "nodelist.h"
-#include <linux/crc32.h>
 
+static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag);
 
-D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)
+#if CONFIG_JFFS2_FS_DEBUG >= 1
+static void jffs2_print_fragtree(struct rb_root *list, int permitbug)
 {
-	struct jffs2_node_frag *this = f->fraglist;
+	struct jffs2_node_frag *this = frag_first(list);
+	uint32_t lastofs = 0;
+	int buggy = 0;
 
 	while(this) {
 		if (this->node)
-			printk(KERN_DEBUG "frag %04x-%04x: 0x%08x on flash (*%p->%p)\n", this->ofs, this->ofs+this->size, this->node->raw->flash_offset &~3, this, this->next);
+			printk(KERN_DEBUG "frag %04x-%04x: 0x%08x(%d) on flash (*%p). left (%p), right (%p), parent (%p)\n",
+			       this->ofs, this->ofs+this->size, ref_offset(this->node->raw), ref_flags(this->node->raw),
+			       this, frag_left(this), frag_right(this), frag_parent(this));
 		else 
-			printk(KERN_DEBUG "frag %04x-%04x: hole (*%p->%p)\n", this->ofs, this->ofs+this->size, this, this->next);
-		this = this->next;
+			printk(KERN_DEBUG "frag %04x-%04x: hole (*%p). left (%p} right (%p), parent (%p)\n", this->ofs, 
+			       this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this));
+		if (this->ofs != lastofs)
+			buggy = 1;
+		lastofs = this->ofs+this->size;
+		this = frag_next(this);
+	}
+	if (buggy && !permitbug) {
+		printk(KERN_CRIT "Frag tree got a hole in it\n");
+		BUG();
 	}
+}
+
+void jffs2_print_frag_list(struct jffs2_inode_info *f)
+{
+	jffs2_print_fragtree(&f->fragtree, 0);
+
 	if (f->metadata) {
-		printk(KERN_DEBUG "metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3);
+		printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw));
 	}
-})
-
+}
 
-int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
+static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f)
 {
-	int ret;
-	D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
+	struct jffs2_node_frag *frag;
+	int bitched = 0;
 
-	ret = jffs2_add_full_dnode_to_fraglist(c, &f->fraglist, fn);
+	for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) {
 
-	D2(jffs2_print_frag_list(f));
-	return ret;
+		struct jffs2_full_dnode *fn = frag->node;
+		if (!fn || !fn->raw)
+			continue;
+
+		if (ref_flags(fn->raw) == REF_PRISTINE) {
+
+			if (fn->frags > 1) {
+				printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(fn->raw), fn->frags);
+				bitched = 1;
+			}
+			/* A hole node which isn't multi-page should be garbage-collected
+			   and merged anyway, so we just check for the frag size here,
+			   rather than mucking around with actually reading the node
+			   and checking the compression type, which is the real way
+			   to tell a hole node. */
+			if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) {
+				printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n",
+				       ref_offset(fn->raw));
+				bitched = 1;
+			}
+
+			if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) {
+				printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n",
+				       ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size);
+				bitched = 1;
+			}
+		}
+	}
+	
+	if (bitched) {
+		struct jffs2_node_frag *thisfrag;
+
+		printk(KERN_WARNING "Inode is #%u\n", f->inocache->ino);
+		thisfrag = frag_first(&f->fragtree);
+		while (thisfrag) {
+			if (!thisfrag->node) {
+				printk("Frag @0x%x-0x%x; node-less hole\n",
+				       thisfrag->ofs, thisfrag->size + thisfrag->ofs);
+			} else if (!thisfrag->node->raw) {
+				printk("Frag @0x%x-0x%x; raw-less hole\n",
+				       thisfrag->ofs, thisfrag->size + thisfrag->ofs);
+			} else {
+				printk("Frag @0x%x-0x%x; raw at 0x%08x(%d) (0x%x-0x%x)\n",
+				       thisfrag->ofs, thisfrag->size + thisfrag->ofs,
+				       ref_offset(thisfrag->node->raw), ref_flags(thisfrag->node->raw),
+				       thisfrag->node->ofs, thisfrag->node->ofs+thisfrag->node->size);
+			}
+			thisfrag = frag_next(thisfrag);
+		}
+	}
+	return bitched;
 }
+#endif /* D1 */
 
 static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this)
 {
@@ -82,42 +125,38 @@
 		if (!this->node->frags) {
 			/* The node has no valid frags left. It's totally obsoleted */
 			D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) obsolete\n",
-				  this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size));
+				  ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size));
 			jffs2_mark_node_obsolete(c, this->node->raw);
 			jffs2_free_full_dnode(this->node);
 		} else {
-			D2(printk(KERN_DEBUG "Not marking old node @0x%08x (0x%04x-0x%04x) obsolete. frags is %d\n",
-				  this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size,
+			D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n",
+				  ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size,
 				  this->node->frags));
+			mark_ref_normal(this->node->raw);
 		}
 		
 	}
 	jffs2_free_node_frag(this);
 }
 
-/* Doesn't set inode->i_size */
-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn)
+/* Given an inode, probably with existing list of fragments, add the new node
+ * to the fragment list.
+ */
+int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn)
 {
-	
-	struct jffs2_node_frag *this, **prev, *old;
-	struct jffs2_node_frag *newfrag, *newfrag2;
-	__u32 lastend = 0;
+	int ret;
+	struct jffs2_node_frag *newfrag;
 
+	D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn));
 
 	newfrag = jffs2_alloc_node_frag();
-	if (!newfrag) {
+	if (unlikely(!newfrag))
 		return -ENOMEM;
-	}
 
-	D2(if (fn->raw)
-		printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, fn->raw->flash_offset &~3, newfrag);
-	else
-		printk(KERN_DEBUG "adding hole node %04x-%04x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, newfrag));
+	D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n",
+		  fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag));
 	
-	prev = list;
-	this = *list;
-
-	if (!fn->size) {
+	if (unlikely(!fn->size)) {
 		jffs2_free_node_frag(newfrag);
 		return 0;
 	}
@@ -126,176 +165,358 @@
 	newfrag->size = fn->size;
 	newfrag->node = fn;
 	newfrag->node->frags = 1;
-	newfrag->next = (void *)0xdeadbeef;
 
-	/* Skip all the nodes which are completed before this one starts */
-	while(this && fn->ofs >= this->ofs+this->size) {
-		lastend = this->ofs + this->size;
+	ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag);
+	if (ret)
+		return ret;
+
+	/* If we now share a page with other nodes, mark either previous
+	   or next node REF_NORMAL, as appropriate.  */
+	if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) {
+		struct jffs2_node_frag *prev = frag_prev(newfrag);
+
+		mark_ref_normal(fn->raw);
+		/* If we don't start at zero there's _always_ a previous */	
+		if (prev->node)
+			mark_ref_normal(prev->node->raw);
+	}
 
-		D2(printk(KERN_DEBUG "j_a_f_d_t_f: skipping frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n", 
-			  this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next));
-		prev = &this->next;
-		this = this->next;
+	if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) {
+		struct jffs2_node_frag *next = frag_next(newfrag);
+		
+		if (next) {
+			mark_ref_normal(fn->raw);
+			if (next->node)
+				mark_ref_normal(next->node->raw);
+		}
 	}
+	D2(if (jffs2_sanitycheck_fragtree(f)) {
+		   printk(KERN_WARNING "Just added node %04x-%04x @0x%08x on flash, newfrag *%p\n",
+			  fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag);
+		   return 0;
+	   })
+	D2(jffs2_print_frag_list(f));
+	return 0;
+}
 
+/* Doesn't set inode->i_size */
+static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag)
+{
+	struct jffs2_node_frag *this;
+	uint32_t lastend;
+
+	/* Skip all the nodes which are completed before this one starts */
+	this = jffs2_lookup_node_frag(list, newfrag->node->ofs);
+
+	if (this) {
+		D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n",
+			  this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
+		lastend = this->ofs + this->size;
+	} else {
+		D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n"));
+		lastend = 0;
+	}
+			  
 	/* See if we ran off the end of the list */
-	if (!this) {
+	if (lastend <= newfrag->ofs) {
 		/* We did */
-		if (lastend < fn->ofs) {
+
+		/* Check if 'this' node was on the same page as the new node.
+		   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 (this->node)
+				mark_ref_normal(this->node->raw);
+			mark_ref_normal(newfrag->node->raw);
+		}
+
+		if (lastend < newfrag->node->ofs) {
 			/* ... and we need to put a hole in before the new node */
 			struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag();
-			if (!holefrag)
+			if (!holefrag) {
+				jffs2_free_node_frag(newfrag);
 				return -ENOMEM;
+			}
 			holefrag->ofs = lastend;
-			holefrag->size = fn->ofs - lastend;
-			holefrag->next = NULL;
+			holefrag->size = newfrag->node->ofs - lastend;
 			holefrag->node = NULL;
-			*prev = holefrag;
-			prev = &holefrag->next;
+			if (this) {
+				/* By definition, the 'this' node has no right-hand child, 
+				   because there are no frags with offset greater than it.
+				   So that's where we want to put the hole */
+				D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this));
+				rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right);
+			} else {
+				D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag));
+				rb_link_node(&holefrag->rb, NULL, &list->rb_node);
+			}
+			rb_insert_color(&holefrag->rb, list);
+			this = holefrag;
 		}
-		newfrag->next = NULL;
-		*prev = newfrag;
+		if (this) {
+			/* By definition, the 'this' node has no right-hand child, 
+			   because there are no frags with offset greater than it.
+			   So that's where we want to put the hole */
+			D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this));
+			rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right);			
+		} else {
+			D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag));
+			rb_link_node(&newfrag->rb, NULL, &list->rb_node);
+		}
+		rb_insert_color(&newfrag->rb, list);
 		return 0;
 	}
 
-	D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n", 
-		  this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next));
+	D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", 
+		  this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this));
 
-	/* OK. 'this' is pointing at the first frag that fn->ofs at least partially obsoletes,
-	 * - i.e. fn->ofs < this->ofs+this->size && fn->ofs >= this->ofs  
+	/* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes,
+	 * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs  
 	 */
-	if (fn->ofs > this->ofs) {
+	if (newfrag->ofs > this->ofs) {
 		/* This node isn't completely obsoleted. The start of it remains valid */
-		if (this->ofs + this->size > fn->ofs + fn->size) {
+
+		/* Mark the new node and the partially covered node REF_NORMAL -- let
+		   the GC take a look at them */
+		mark_ref_normal(newfrag->node->raw);
+		if (this->node)
+			mark_ref_normal(this->node->raw);
+
+		if (this->ofs + this->size > newfrag->ofs + newfrag->size) {
 			/* The new node splits 'this' frag into two */
-			newfrag2 = jffs2_alloc_node_frag();
+			struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag();
 			if (!newfrag2) {
 				jffs2_free_node_frag(newfrag);
 				return -ENOMEM;
 			}
-			D1(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
+			D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size);
 			if (this->node)
-				printk("phys 0x%08x\n", this->node->raw->flash_offset &~3);
+				printk("phys 0x%08x\n", ref_offset(this->node->raw));
 			else 
 				printk("hole\n");
 			   )
-			newfrag2->ofs = fn->ofs + fn->size;
+			
+			/* New second frag pointing to this's node */
+			newfrag2->ofs = newfrag->ofs + newfrag->size;
 			newfrag2->size = (this->ofs+this->size) - newfrag2->ofs;
-			newfrag2->next = this->next;
 			newfrag2->node = this->node;
 			if (this->node)
 				this->node->frags++;
-			newfrag->next = newfrag2;
-			this->next = newfrag;
+
+			/* Adjust size of original 'this' */
 			this->size = newfrag->ofs - this->ofs;
+
+			/* Now, we know there's no node with offset
+			   greater than this->ofs but smaller than
+			   newfrag2->ofs or newfrag->ofs, for obvious
+			   reasons. So we can do a tree insert from
+			   'this' to insert newfrag, and a tree insert
+			   from newfrag to insert newfrag2. */
+			jffs2_fragtree_insert(newfrag, this);
+			rb_insert_color(&newfrag->rb, list);
+			
+			jffs2_fragtree_insert(newfrag2, newfrag);
+			rb_insert_color(&newfrag2->rb, list);
+			
 			return 0;
 		}
 		/* New node just reduces 'this' frag in size, doesn't split it */
-		this->size = fn->ofs - this->ofs;
-		newfrag->next = this->next;
-		this->next = newfrag;
-		this = newfrag->next;
+		this->size = newfrag->ofs - this->ofs;
+
+		/* Again, we know it lives down here in the tree */
+		jffs2_fragtree_insert(newfrag, this);
+		rb_insert_color(&newfrag->rb, list);
 	} else {
-		D2(printk(KERN_DEBUG "Inserting newfrag (*%p) in before 'this' (*%p)\n", newfrag, this));
-		*prev = newfrag;
-	        newfrag->next = this;
+		/* New frag starts at the same point as 'this' used to. Replace 
+		   it in the tree without doing a delete and insertion */
+		D2(printk(KERN_DEBUG "Inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n",
+			  newfrag, newfrag->ofs, newfrag->ofs+newfrag->size,
+			  this, this->ofs, this->ofs+this->size));
+	
+		rb_replace_node(&this->rb, &newfrag->rb, list);
+		
+		if (newfrag->ofs + newfrag->size >= this->ofs+this->size) {
+			D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size));
+			jffs2_obsolete_node_frag(c, this);
+		} else {
+			this->ofs += newfrag->size;
+			this->size -= newfrag->size;
+
+			jffs2_fragtree_insert(this, newfrag);
+			rb_insert_color(&this->rb, list);
+			return 0;
+		}
 	}
-	/* OK, now we have newfrag added in the correct place in the list, but
-	   newfrag->next points to a fragment which may be overlapping it
+	/* OK, now we have newfrag added in the correct place in the tree, but
+	   frag_next(newfrag) may be a fragment which is overlapped by it 
 	*/
-	while (this && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
-		/* 'this' frag is obsoleted. */
-		old = this;
-		this = old->next;
-		jffs2_obsolete_node_frag(c, old);
+	while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) {
+		/* 'this' frag is obsoleted completely. */
+		D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size));
+		rb_erase(&this->rb, list);
+		jffs2_obsolete_node_frag(c, this);
 	}
 	/* Now we're pointing at the first frag which isn't totally obsoleted by 
 	   the new frag */
-	newfrag->next = this;
 
 	if (!this || newfrag->ofs + newfrag->size == this->ofs) {
 		return 0;
 	}
-	/* Still some overlap */
+	/* Still some overlap but we don't need to move it in the tree */
 	this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size);
 	this->ofs = newfrag->ofs + newfrag->size;
+
+	/* And mark them REF_NORMAL so the GC takes a look at them */
+	if (this->node)
+		mark_ref_normal(this->node->raw);
+	mark_ref_normal(newfrag->node->raw);
+
 	return 0;
 }
 
-void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size)
+void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size)
 {
+	struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size);
+
 	D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size));
 
-	while (*list) {
-		if ((*list)->ofs >= size) {
-			struct jffs2_node_frag *this = *list;
-			*list = this->next;
-			D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", this->ofs, this->ofs+this->size));
-			jffs2_obsolete_node_frag(c, this);
-			continue;
-		} else if ((*list)->ofs + (*list)->size > size) {
-			D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", (*list)->ofs, (*list)->ofs + (*list)->size));
-			(*list)->size = size - (*list)->ofs;
-		}
-		list = &(*list)->next;
+	/* We know frag->ofs <= size. That's what lookup does for us */
+	if (frag && frag->ofs != size) {
+		if (frag->ofs+frag->size >= size) {
+			D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
+			frag->size = size - frag->ofs;
+		}
+		frag = frag_next(frag);
+	}
+	while (frag && frag->ofs >= size) {
+		struct jffs2_node_frag *next = frag_next(frag);
+
+		D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size));
+		frag_erase(frag, list);
+		jffs2_obsolete_node_frag(c, frag);
+		frag = next;
 	}
 }
 
 /* Scan the list of all nodes present for this ino, build map of versions, etc. */
 
-void jffs2_read_inode (struct inode *inode)
-{
-	struct jffs2_tmp_dnode_info *tn_list, *tn;
-	struct jffs2_full_dirent *fd_list;
-	struct jffs2_inode_info *f;
-	struct jffs2_full_dnode *fn = NULL;
-	struct jffs2_sb_info *c;
-	struct jffs2_raw_inode latest_node;
-	__u32 latest_mctime, mctime_ver;
-	__u32 mdata_ver = 0;
-	int ret;
-	ssize_t retlen;
+static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, 
+					struct jffs2_inode_info *f,
+					struct jffs2_raw_inode *latest_node);
 
-	D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
+int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 
+			uint32_t ino, struct jffs2_raw_inode *latest_node)
+{
+	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
 
-	f = JFFS2_INODE_INFO(inode);
-	c = JFFS2_SB_INFO(inode->i_sb);
+ retry_inocache:
+	spin_lock(&c->inocache_lock);
+	f->inocache = jffs2_get_ino_cache(c, ino);
+
+	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache));
+
+	if (f->inocache) {
+		/* Check its state. We may need to wait before we can use it */
+		switch(f->inocache->state) {
+		case INO_STATE_UNCHECKED:
+		case INO_STATE_CHECKEDABSENT:
+			f->inocache->state = INO_STATE_READING;
+			break;
+			
+		case INO_STATE_CHECKING:
+		case INO_STATE_GC:
+			/* If it's in either of these states, we need
+			   to wait for whoever's got it to finish and
+			   put it back. */
+			D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n",
+				  ino, f->inocache->state));
+			sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock);
+			goto retry_inocache;
+
+		case INO_STATE_READING:
+		case INO_STATE_PRESENT:
+			/* Eep. This should never happen. It can
+			happen if Linux calls read_inode() again
+			before clear_inode() has finished though. */
+			printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state);
+			/* Fail. That's probably better than allowing it to succeed */
+			f->inocache = NULL;
+			break;
 
-	memset(f, 0, sizeof(*f));
-	D2(printk(KERN_DEBUG "getting inocache\n"));
-	init_MUTEX(&f->sem);
-	f->inocache = jffs2_get_ino_cache(c, inode->i_ino);
-	D2(printk(KERN_DEBUG "jffs2_read_inode(): Got inocache at %p\n", f->inocache));
+		default:
+			BUG();
+		}
+	}
+	spin_unlock(&c->inocache_lock);
 
-	if (!f->inocache && inode->i_ino == 1) {
+	if (!f->inocache && ino == 1) {
 		/* Special case - no root inode on medium */
 		f->inocache = jffs2_alloc_inode_cache();
 		if (!f->inocache) {
-			printk(KERN_CRIT "jffs2_read_inode(): Cannot allocate inocache for root inode\n");
-			make_bad_inode(inode);
-			return;
+			printk(KERN_CRIT "jffs2_do_read_inode(): Cannot allocate inocache for root inode\n");
+			return -ENOMEM;
 		}
-		D1(printk(KERN_DEBUG "jffs2_read_inode(): Creating inocache for root inode\n"));
+		D1(printk(KERN_DEBUG "jffs2_do_read_inode(): Creating inocache for root inode\n"));
 		memset(f->inocache, 0, sizeof(struct jffs2_inode_cache));
 		f->inocache->ino = f->inocache->nlink = 1;
 		f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
+		f->inocache->state = INO_STATE_READING;
 		jffs2_add_ino_cache(c, f->inocache);
 	}
 	if (!f->inocache) {
-		printk(KERN_WARNING "jffs2_read_inode() on nonexistent ino %lu\n", (unsigned long)inode->i_ino);
-		make_bad_inode(inode);
-		return;
+		printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino);
+		return -ENOENT;
 	}
-	D1(printk(KERN_DEBUG "jffs2_read_inode(): ino #%lu nlink is %d\n", (unsigned long)inode->i_ino, f->inocache->nlink));
-	inode->i_nlink = f->inocache->nlink;
+
+	return jffs2_do_read_inode_internal(c, f, latest_node);
+}
+
+int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+{
+	struct jffs2_raw_inode n;
+	struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL);
+	int ret;
+
+	if (!f)
+		return -ENOMEM;
+
+	memset(f, 0, sizeof(*f));
+	init_MUTEX_LOCKED(&f->sem);
+	f->inocache = ic;
+
+	ret = jffs2_do_read_inode_internal(c, f, &n);
+	if (!ret) {
+		up(&f->sem);
+		jffs2_do_clear_inode(c, f);
+	}
+	kfree (f);
+	return ret;
+}
+
+static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, 
+					struct jffs2_inode_info *f,
+					struct jffs2_raw_inode *latest_node)
+{
+	struct jffs2_tmp_dnode_info *tn_list, *tn;
+	struct jffs2_full_dirent *fd_list;
+	struct jffs2_full_dnode *fn = NULL;
+	uint32_t crc;
+	uint32_t latest_mctime, mctime_ver;
+	uint32_t mdata_ver = 0;
+	size_t retlen;
+	int ret;
+
+	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, inode->i_ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
+	ret = jffs2_get_inode_nodes(c, f->inocache->ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver);
 
 	if (ret) {
-		printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %lu returned %d\n", inode->i_ino, ret);
-		make_bad_inode(inode);
-		return;
+		printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret);
+		if (f->inocache->state == INO_STATE_READING)
+			jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
+		return ret;
 	}
 	f->dents = fd_list;
 
@@ -304,205 +525,169 @@
 
 		fn = tn->fn;
 
-		if (f->metadata && tn->version > mdata_ver) {
-			D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3));
-			jffs2_mark_node_obsolete(c, f->metadata->raw);
-			jffs2_free_full_dnode(f->metadata);
-			f->metadata = NULL;
-			
-			mdata_ver = 0;
+		if (f->metadata) {
+			if (likely(tn->version >= mdata_ver)) {
+				D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw)));
+				jffs2_mark_node_obsolete(c, f->metadata->raw);
+				jffs2_free_full_dnode(f->metadata);
+				f->metadata = NULL;
+				
+				mdata_ver = 0;
+			} else {
+				/* This should never happen. */
+				printk(KERN_WARNING "Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n",
+					  ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw));
+				jffs2_mark_node_obsolete(c, fn->raw);
+				jffs2_free_full_dnode(fn);
+				/* Fill in latest_node from the metadata, not this one we're about to free... */
+				fn = f->metadata;
+				goto next_tn;
+			}
 		}
 
 		if (fn->size) {
 			jffs2_add_full_dnode_to_inode(c, f, fn);
 		} else {
 			/* Zero-sized node at end of version list. Just a metadata update */
-			D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", fn->raw->flash_offset &~3, tn->version));
+			D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version));
 			f->metadata = fn;
 			mdata_ver = tn->version;
 		}
+	next_tn:
 		tn_list = tn->next;
 		jffs2_free_tmp_dnode_info(tn);
 	}
+	D1(jffs2_sanitycheck_fragtree(f));
+
 	if (!fn) {
 		/* No data nodes for this inode. */
-		if (inode->i_ino != 1) {
-			printk(KERN_WARNING "jffs2_read_inode(): No data nodes found for ino #%lu\n", inode->i_ino);
+		if (f->inocache->ino != 1) {
+			printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino);
 			if (!fd_list) {
-				make_bad_inode(inode);
-				return;
+				if (f->inocache->state == INO_STATE_READING)
+					jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
+				return -EIO;
 			}
-			printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n");
+			printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
 		}
-		inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO;
-		latest_node.version = 0;
-		inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME;
-		inode->i_nlink = f->inocache->nlink;
-		inode->i_size = 0;
-	} else {
-		__u32 crc;
+		latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO);
+		latest_node->version = cpu_to_je32(0);
+		latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0);
+		latest_node->isize = cpu_to_je32(0);
+		latest_node->gid = cpu_to_je16(0);
+		latest_node->uid = cpu_to_je16(0);
+		if (f->inocache->state == INO_STATE_READING)
+			jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
+		return 0;
+	}
 
-		ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(latest_node), &retlen, (void *)&latest_node);
-		if (ret || retlen != sizeof(latest_node)) {
-			printk(KERN_NOTICE "MTD read in jffs2_read_inode() failed: Returned %d, %ld of %d bytes read\n",
-			       ret, (long)retlen, sizeof(latest_node));
-			jffs2_clear_inode(inode);
-			make_bad_inode(inode);
-			return;
-		}
-
-		crc = crc32(0, &latest_node, sizeof(latest_node)-8);
-		if (crc != latest_node.node_crc) {
-			printk(KERN_NOTICE "CRC failed for read_inode of inode %ld at physical location 0x%x\n", inode->i_ino, fn->raw->flash_offset & ~3);
-			jffs2_clear_inode(inode);
-			make_bad_inode(inode);
-			return;
-		}
-
-		inode->i_mode = latest_node.mode;
-		inode->i_uid = latest_node.uid;
-		inode->i_gid = latest_node.gid;
-		inode->i_size = latest_node.isize;
-		if (S_ISREG(inode->i_mode))
-			jffs2_truncate_fraglist(c, &f->fraglist, latest_node.isize);
-		inode->i_atime = latest_node.atime;
-		inode->i_mtime = latest_node.mtime;
-		inode->i_ctime = latest_node.ctime;
-	}
-
-	/* OK, now the special cases. Certain inode types should
-	   have only one data node, and it's kept as the metadata
-	   node */
-	if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode) ||
-	    S_ISLNK(inode->i_mode)) {
-		if (f->metadata) {
-			printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had metadata node\n", inode->i_ino, inode->i_mode);
-			jffs2_clear_inode(inode);
-			make_bad_inode(inode);
-			return;
-		}
-		if (!f->fraglist) {
-			printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o has no fragments\n", inode->i_ino, inode->i_mode);
-			jffs2_clear_inode(inode);
-			make_bad_inode(inode);
-			return;
-		}
-		/* ASSERT: f->fraglist != NULL */
-		if (f->fraglist->next) {
-			printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had more than one node\n", inode->i_ino, inode->i_mode);
-			/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
-			jffs2_clear_inode(inode);
-			make_bad_inode(inode);
-			return;
-		}
-		/* OK. We're happy */
-		f->metadata = f->fraglist->node;
-		jffs2_free_node_frag(f->fraglist);
-		f->fraglist = NULL;
-	}			
-	    
-	inode->i_blksize = PAGE_SIZE;
-	inode->i_blocks = (inode->i_size + 511) >> 9;
-	
-	switch (inode->i_mode & S_IFMT) {
-		unsigned short rdev;
+	ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node);
+	if (ret || retlen != sizeof(*latest_node)) {
+		printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %zd of %zd bytes read\n",
+		       ret, retlen, sizeof(*latest_node));
+		/* FIXME: If this fails, there seems to be a memory leak. Find it. */
+		up(&f->sem);
+		jffs2_do_clear_inode(c, f);
+		return ret?ret:-EIO;
+	}
+
+	crc = crc32(0, latest_node, sizeof(*latest_node)-8);
+	if (crc != je32_to_cpu(latest_node->node_crc)) {
+		printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw));
+		up(&f->sem);
+		jffs2_do_clear_inode(c, f);
+		return -EIO;
+	}
 
-	case S_IFLNK:
-		inode->i_op = &jffs2_symlink_inode_operations;
-		/* Hack to work around broken isize in old symlink code.
-		   Remove this when dwmw2 comes to his senses and stops
-		   symlinks from being an entirely gratuitous special
-		   case. */
-		if (!inode->i_size)
-			inode->i_size = latest_node.dsize;
-		break;
-		
+	switch(jemode_to_cpu(latest_node->mode) & S_IFMT) {
 	case S_IFDIR:
-		if (mctime_ver > latest_node.version) {
+		if (mctime_ver > je32_to_cpu(latest_node->version)) {
 			/* The times in the latest_node are actually older than
 			   mctime in the latest dirent. Cheat. */
-			inode->i_mtime = inode->i_ctime = inode->i_atime = 
-				latest_mctime;
+			latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime);
 		}
-		inode->i_op = &jffs2_dir_inode_operations;
-		inode->i_fop = &jffs2_dir_operations;
 		break;
 
+			
 	case S_IFREG:
-		inode->i_op = &jffs2_file_inode_operations;
-		inode->i_fop = &jffs2_file_operations;
-		inode->i_mapping->a_ops = &jffs2_file_address_operations;
-		inode->i_mapping->nrpages = 0;
+		/* If it was a regular file, truncate it to the latest node's isize */
+		jffs2_truncate_fraglist(c, &f->fragtree, je32_to_cpu(latest_node->isize));
 		break;
 
+	case S_IFLNK:
+		/* Hack to work around broken isize in old symlink code.
+		   Remove this when dwmw2 comes to his senses and stops
+		   symlinks from being an entirely gratuitous special
+		   case. */
+		if (!je32_to_cpu(latest_node->isize))
+			latest_node->isize = latest_node->dsize;
+		/* fall through... */
+
 	case S_IFBLK:
 	case S_IFCHR:
-		/* Read the device numbers from the media */
-		D1(printk(KERN_DEBUG "Reading device numbers from flash\n"));
-		if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) {
-			/* Eep */
-			printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino);
-			jffs2_clear_inode(inode);
-			make_bad_inode(inode);
-			return;
-		}			
-
-	case S_IFSOCK:
-	case S_IFIFO:
-		inode->i_op = &jffs2_file_inode_operations;
-		init_special_inode(inode, inode->i_mode, kdev_t_to_nr(MKDEV(rdev>>8, rdev&0xff)));
+		/* Certain inode types should have only one data node, and it's
+		   kept as the metadata node */
+		if (f->metadata) {
+			printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n",
+			       f->inocache->ino, jemode_to_cpu(latest_node->mode));
+			up(&f->sem);
+			jffs2_do_clear_inode(c, f);
+			return -EIO;
+		}
+		if (!frag_first(&f->fragtree)) {
+			printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n",
+			       f->inocache->ino, jemode_to_cpu(latest_node->mode));
+			up(&f->sem);
+			jffs2_do_clear_inode(c, f);
+			return -EIO;
+		}
+		/* ASSERT: f->fraglist != NULL */
+		if (frag_next(frag_first(&f->fragtree))) {
+			printk(KERN_WARNING "Argh. Special inode #%u with mode 0x%x had more than one node\n",
+			       f->inocache->ino, jemode_to_cpu(latest_node->mode));
+			/* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */
+			up(&f->sem);
+			jffs2_do_clear_inode(c, f);
+			return -EIO;
+		}
+		/* OK. We're happy */
+		f->metadata = frag_first(&f->fragtree)->node;
+		jffs2_free_node_frag(frag_first(&f->fragtree));
+		f->fragtree = RB_ROOT;
 		break;
-
-	default:
-		printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu", inode->i_mode, (unsigned long)inode->i_ino);
 	}
-	D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n"));
+	if (f->inocache->state == INO_STATE_READING)
+		jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT);
+
+	return 0;
 }
 
-void jffs2_clear_inode (struct inode *inode)
+void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f)
 {
-	/* We can forget about this inode for now - drop all 
-	 *  the nodelists associated with it, etc.
-	 */
-	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
-	struct jffs2_node_frag *frag, *frags;
 	struct jffs2_full_dirent *fd, *fds;
-	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
-        int deleted;
-
-	D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode));
+	int deleted;
 
 	down(&f->sem);
 	deleted = f->inocache && !f->inocache->nlink;
 
-	frags = f->fraglist;
-	fds = f->dents;
 	if (f->metadata) {
 		if (deleted)
 			jffs2_mark_node_obsolete(c, f->metadata->raw);
 		jffs2_free_full_dnode(f->metadata);
 	}
 
-	while (frags) {
-		frag = frags;
-		frags = frag->next;
-		D2(printk(KERN_DEBUG "jffs2_clear_inode: frag at 0x%x-0x%x: node %p, frags %d--\n", frag->ofs, frag->ofs+frag->size, frag->node, frag->node?frag->node->frags:0));
-
-		if (frag->node && !(--frag->node->frags)) {
-			/* Not a hole, and it's the final remaining frag of this node. Free the node */
-			if (deleted)
-				jffs2_mark_node_obsolete(c, frag->node->raw);
+	jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
+
+	fds = f->dents;
 
-			jffs2_free_full_dnode(frag->node);
-		}
-		jffs2_free_node_frag(frag);
-	}
 	while(fds) {
 		fd = fds;
 		fds = fd->next;
 		jffs2_free_full_dirent(fd);
 	}
 
-	up(&f->sem);
-};
+	if (f->inocache && f->inocache->state != INO_STATE_CHECKING)
+		jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
 
+	up(&f->sem);
+}
diff -Nur linux-2.4.30/fs/jffs2/scan.c linux-2.4.30-linux-mips.org/fs/jffs2/scan.c
--- linux-2.4.30/fs/jffs2/scan.c	2003-11-28 19:26:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/scan.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,47 +1,25 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: scan.c,v 1.51.2.4 2003/11/02 13:51:18 dwmw2 Exp $
+ * $Id: scan.c,v 1.109 2004/03/19 16:40:50 dwmw2 Exp $
  *
  */
 #include <linux/kernel.h>
+#include <linux/sched.h>
 #include <linux/slab.h>
-#include <linux/jffs2.h>
 #include <linux/mtd/mtd.h>
 #include <linux/pagemap.h>
-#include "nodelist.h"
 #include <linux/crc32.h>
+#include <linux/compiler.h>
+#include "nodelist.h"
 
+#define EMPTY_SCAN_SIZE 1024
 
 #define DIRTY_SPACE(x) do { typeof(x) _x = (x); \
 		c->free_size -= _x; c->dirty_size += _x; \
@@ -51,6 +29,10 @@
 		c->free_size -= _x; c->used_size += _x; \
 		jeb->free_size -= _x ; jeb->used_size += _x; \
 		}while(0)
+#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \
+		c->free_size -= _x; c->unchecked_size += _x; \
+		jeb->free_size -= _x ; jeb->unchecked_size += _x; \
+		}while(0)
 
 #define noisy_printk(noise, args...) do { \
 	if (*(noise)) { \
@@ -63,39 +45,84 @@
 } while(0)
 
 static uint32_t pseudo_random;
-static void jffs2_rotate_lists(struct jffs2_sb_info *c);
 
-static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+				  unsigned char *buf, uint32_t buf_size);
 
 /* These helper functions _must_ increase ofs and also do the dirty/used space accounting. 
  * Returning an error will abort the mount - bad checksums etc. should just mark the space
  * as dirty.
  */
-static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs, int *noise);
-static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs);
-static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs);
+static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
+				 struct jffs2_raw_inode *ri, uint32_t ofs);
+static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+				 struct jffs2_raw_dirent *rd, uint32_t ofs);
+
+#define BLK_STATE_ALLFF		0
+#define BLK_STATE_CLEAN		1
+#define BLK_STATE_PARTDIRTY	2
+#define BLK_STATE_CLEANMARKER	3
+#define BLK_STATE_ALLDIRTY	4
+#define BLK_STATE_BADBLOCK	5
 
+static inline int min_free(struct jffs2_sb_info *c)
+{
+	uint32_t min = 2 * sizeof(struct jffs2_raw_inode);
+#ifdef CONFIG_JFFS2_FS_NAND
+	if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize)
+		return c->wbuf_pagesize;
+#endif
+	return min;
 
+}
 int jffs2_scan_medium(struct jffs2_sb_info *c)
 {
 	int i, ret;
-	__u32 empty_blocks = 0;
-
-	if (!c->blocks) {
-		printk(KERN_WARNING "EEEK! c->blocks is NULL!\n");
-		return -EINVAL;
+	uint32_t empty_blocks = 0, bad_blocks = 0;
+	unsigned char *flashbuf = NULL;
+	uint32_t buf_size = 0;
+#ifndef __ECOS
+	size_t pointlen;
+
+	if (c->mtd->point) {
+		ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf);
+		if (!ret && pointlen < c->mtd->size) {
+			/* Don't muck about if it won't let us point to the whole flash */
+			D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen));
+			c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
+			flashbuf = NULL;
+		}
+		if (ret)
+			D1(printk(KERN_DEBUG "MTD point failed %d\n", ret));
+	}
+#endif
+	if (!flashbuf) {
+		/* For NAND it's quicker to read a whole eraseblock at a time,
+		   apparently */
+		if (jffs2_cleanmarker_oob(c))
+			buf_size = c->sector_size;
+		else
+			buf_size = PAGE_SIZE;
+
+		D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size));
+		flashbuf = kmalloc(buf_size, GFP_KERNEL);
+		if (!flashbuf)
+			return -ENOMEM;
 	}
+
 	for (i=0; i<c->nr_blocks; i++) {
 		struct jffs2_eraseblock *jeb = &c->blocks[i];
 
-		ret = jffs2_scan_eraseblock(c, jeb);
+		ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size);
+
 		if (ret < 0)
-			return ret;
+			goto out;
 
 		ACCT_PARANOIA_CHECK(jeb);
 
 		/* Now decide which list to put it on */
-		if (ret == 1) {
+		switch(ret) {
+		case BLK_STATE_ALLFF:
 			/* 
 			 * Empty block.   Since we can't be sure it 
 			 * was entirely erased, we just queue it for erase
@@ -103,10 +130,12 @@
 			 * is complete.  Meanwhile we still count it as empty
 			 * for later checks.
 			 */
-			list_add(&jeb->list, &c->erase_pending_list);
 			empty_blocks++;
+			list_add(&jeb->list, &c->erase_pending_list);
 			c->nr_erasing_blocks++;
-		} else if (jeb->used_size == PAD(sizeof(struct jffs2_unknown_node)) && !jeb->first_node->next_in_ino) {
+			break;
+
+		case BLK_STATE_CLEANMARKER:
 			/* Only a CLEANMARKER node is valid */
 			if (!jeb->dirty_size) {
 				/* It's actually free */
@@ -118,74 +147,226 @@
 				list_add(&jeb->list, &c->erase_pending_list);
 				c->nr_erasing_blocks++;
 			}
-		} else if (jeb->used_size > c->sector_size - (2*sizeof(struct jffs2_raw_inode))) {
+			break;
+
+		case BLK_STATE_CLEAN:
                         /* Full (or almost full) of clean data. Clean list */
                         list_add(&jeb->list, &c->clean_list);
-                } else if (jeb->used_size) {
+			break;
+
+		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.
                         */
-                        if (jeb->free_size > 2*sizeof(struct jffs2_raw_inode) && 
-                                (!c->nextblock || c->nextblock->free_size < jeb->free_size)) {
+                        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 */
-                                if (c->nextblock)
-                                        list_add(&c->nextblock->list, &c->dirty_list);
+                                if (c->nextblock) {
+					c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
+					c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size;
+					c->free_size -= c->nextblock->free_size;
+					c->wasted_size -= c->nextblock->wasted_size;
+					c->nextblock->free_size = c->nextblock->wasted_size = 0;
+					if (VERYDIRTY(c, c->nextblock->dirty_size)) {
+						list_add(&c->nextblock->list, &c->very_dirty_list);
+					} else {
+						list_add(&c->nextblock->list, &c->dirty_list);
+					}
+				}
                                 c->nextblock = jeb;
                         } else {
-                                list_add(&jeb->list, &c->dirty_list);
+				jeb->dirty_size += jeb->free_size + jeb->wasted_size;
+				c->dirty_size += jeb->free_size + jeb->wasted_size;
+				c->free_size -= jeb->free_size;
+				c->wasted_size -= jeb->wasted_size;
+				jeb->free_size = jeb->wasted_size = 0;
+				if (VERYDIRTY(c, jeb->dirty_size)) {
+					list_add(&jeb->list, &c->very_dirty_list);
+				} else {
+					list_add(&jeb->list, &c->dirty_list);
+				}
                         }
-		} else {
+			break;
+
+		case BLK_STATE_ALLDIRTY:
 			/* Nothing valid - not even a clean marker. Needs erasing. */
                         /* For now we just put it on the erasing list. We'll start the erases later */
-			printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset);
+			D1(printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset));
                         list_add(&jeb->list, &c->erase_pending_list);
 			c->nr_erasing_blocks++;
+			break;
+			
+		case BLK_STATE_BADBLOCK:
+			D1(printk(KERN_NOTICE "JFFS2: Block at 0x%08x is bad\n", jeb->offset));
+                        list_add(&jeb->list, &c->bad_list);
+			c->bad_size += c->sector_size;
+			c->free_size -= c->sector_size;
+			bad_blocks++;
+			break;
+		default:
+			printk(KERN_WARNING "jffs2_scan_medium(): unknown block state\n");
+			BUG();	
 		}
 	}
-	/* Rotate the lists by some number to ensure wear levelling */
-	jffs2_rotate_lists(c);
+	
+	/* Nextblock dirty is always seen as wasted, because we cannot recycle it now */
+	if (c->nextblock && (c->nextblock->dirty_size)) {
+		c->nextblock->wasted_size += c->nextblock->dirty_size;
+		c->wasted_size += c->nextblock->dirty_size;
+		c->dirty_size -= c->nextblock->dirty_size;
+		c->nextblock->dirty_size = 0;
+	}
+#ifdef CONFIG_JFFS2_FS_NAND
+	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,
+		   skip a little and align it. */
+
+		uint32_t skip = c->nextblock->free_size & (c->wbuf_pagesize-1);
+
+		D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n",
+			  skip));
+		c->nextblock->wasted_size += skip;
+		c->wasted_size += skip;
 
+		c->nextblock->free_size -= skip;
+		c->free_size -= skip;
+	}
+#endif
 	if (c->nr_erasing_blocks) {
-		if (!c->used_size && empty_blocks != c->nr_blocks) {
+		if ( !c->used_size && ((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");
-			return -EIO;
+			printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks);
+			ret = -EIO;
+			goto out;
 		}
 		jffs2_erase_pending_trigger(c);
 	}
+	ret = 0;
+ out:
+	if (buf_size)
+		kfree(flashbuf);
+#ifndef __ECOS
+	else 
+		c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size);
+#endif
+	return ret;
+}
+
+static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf,
+				uint32_t ofs, uint32_t len)
+{
+	int ret;
+	size_t retlen;
+
+	ret = jffs2_flash_read(c, ofs, len, &retlen, buf);
+	if (ret) {
+		D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret));
+		return ret;
+	}
+	if (retlen < len) {
+		D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen));
+		return -EIO;
+	}
+	D2(printk(KERN_DEBUG "Read 0x%x bytes from 0x%08x into buf\n", len, ofs));
+	D2(printk(KERN_DEBUG "000: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+		  buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]));
 	return 0;
 }
 
-static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) {
-	struct jffs2_unknown_node node;
-	__u32 ofs, prevofs;
-	__u32 hdr_crc, nodetype;
+static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,
+				  unsigned char *buf, uint32_t buf_size) {
+	struct jffs2_unknown_node *node;
+	struct jffs2_unknown_node crcnode;
+	uint32_t ofs, prevofs;
+	uint32_t hdr_crc, buf_ofs, buf_len;
 	int err;
 	int noise = 0;
+#ifdef CONFIG_JFFS2_FS_NAND
+	int cleanmarkerfound = 0;
+#endif
 
 	ofs = jeb->offset;
 	prevofs = jeb->offset - 1;
 
 	D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs));
 
-	err = jffs2_scan_empty(c, jeb, &ofs, &noise);
-	if (err) return err;
-	if (ofs == jeb->offset + c->sector_size) {
-		D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
-		return 1;	/* special return code */
+#ifdef CONFIG_JFFS2_FS_NAND
+	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));
+		/* Even if it's not found, we still scan to see
+		   if the block is empty. We use this information
+		   to decide whether to erase it or not. */
+		switch (ret) {
+		case 0:		cleanmarkerfound = 1; break;
+		case 1: 	break;
+		case 2: 	return BLK_STATE_BADBLOCK;
+		case 3:		return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */
+		default: 	return ret;
+		}
+	}
+#endif
+	buf_ofs = jeb->offset;
+
+	if (!buf_size) {
+		buf_len = c->sector_size;
+	} else {
+		buf_len = EMPTY_SCAN_SIZE;
+		err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len);
+		if (err)
+			return err;
 	}
 	
+	/* We temporarily use 'ofs' as a pointer into the buffer/jeb */
+	ofs = 0;
+
+	/* Scan only 4KiB of 0xFF before declaring it's empty */
+	while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF)
+		ofs += 4;
+
+	if (ofs == EMPTY_SCAN_SIZE) {
+#ifdef CONFIG_JFFS2_FS_NAND
+		if (jffs2_cleanmarker_oob(c)) {
+			/* scan oob, take care of cleanmarker */
+			int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound);
+			D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret));
+			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 (ofs) {
+		D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset,
+			  jeb->offset + ofs));
+		DIRTY_SPACE(ofs);
+	}
+
+	/* Now ofs is a complete physical flash offset as it always was... */
+	ofs += jeb->offset;
+
 	noise = 10;
 
+scan_more:	
 	while(ofs < jeb->offset + c->sector_size) {
-		ssize_t retlen;
-		ACCT_PARANOIA_CHECK(jeb);
-		
+
+		D1(ACCT_PARANOIA_CHECK(jeb));
+
+		cond_resched();
+
 		if (ofs & 3) {
 			printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs);
-			ofs = (ofs+3)&~3;
+			ofs = PAD(ofs);
 			continue;
 		}
 		if (ofs == prevofs) {
@@ -195,103 +376,184 @@
 			continue;
 		}
 		prevofs = ofs;
-		
-		if (jeb->offset + c->sector_size < ofs + sizeof(node)) {
-			D1(printk(KERN_DEBUG "Fewer than %d bytes left to end of block. Not reading\n", sizeof(struct jffs2_unknown_node)));
+
+		if (jeb->offset + c->sector_size < ofs + sizeof(*node)) {
+			D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node),
+				  jeb->offset, c->sector_size, ofs, sizeof(*node)));
 			DIRTY_SPACE((jeb->offset + c->sector_size)-ofs);
 			break;
 		}
 
-		err = c->mtd->read(c->mtd, ofs, sizeof(node), &retlen, (char *)&node);
-		
-		if (err) {
-			D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", sizeof(node), ofs, err));
-			return err;
-		}
-		if (retlen < sizeof(node)) {
-			D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%x bytes\n", ofs, retlen));
-			DIRTY_SPACE(retlen);
-			ofs += retlen;
-			continue;
-		}
+		if (buf_ofs + buf_len < ofs + sizeof(*node)) {
+			buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+			D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n",
+				  sizeof(struct jffs2_unknown_node), buf_len, ofs));
+			err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+			if (err)
+				return err;
+			buf_ofs = ofs;
+		}
+
+		node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs];
+
+		if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) {
+			uint32_t inbuf_ofs;
+			uint32_t empty_start;
 
-		if (node.magic == JFFS2_EMPTY_BITMASK && node.nodetype == JFFS2_EMPTY_BITMASK) {
-			D1(printk(KERN_DEBUG "Found empty flash at 0x%x\n", ofs));
-			err = jffs2_scan_empty(c, jeb, &ofs, &noise);
-			if (err) return err;
-			continue;
+			empty_start = ofs;
+			ofs += 4;
+
+			D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs));
+		more_empty:
+			inbuf_ofs = ofs - buf_ofs;
+			while (inbuf_ofs < buf_len) {
+				if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) {
+					printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n",
+					       empty_start, ofs);
+					DIRTY_SPACE(ofs-empty_start);
+					goto scan_more;
+				}
+
+				inbuf_ofs+=4;
+				ofs += 4;
+			}
+			/* Ran off end. */
+			D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs));
+
+			/* 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));
+				return BLK_STATE_CLEANMARKER;
+			}
+
+			/* See how much more there is to read in this eraseblock... */
+			buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+			if (!buf_len) {
+				/* No more to read. Break out of main loop without marking 
+				   this range of empty space as dirty (because it's not) */
+				D1(printk(KERN_DEBUG "Empty flash at %08x runs to end of block. Treating as free_space\n",
+					  empty_start));
+				break;
+			}
+			D1(printk(KERN_DEBUG "Reading another 0x%x at 0x%08x\n", buf_len, ofs));
+			err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+			if (err)
+				return err;
+			buf_ofs = ofs;
+			goto more_empty;
 		}
 
-		if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) {
+		if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) {
 			printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs);
 			DIRTY_SPACE(4);
 			ofs += 4;
 			continue;
 		}
-		if (node.magic == JFFS2_DIRTY_BITMASK) {
-			D1(printk(KERN_DEBUG "Empty bitmask at 0x%08x\n", ofs));
+		if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) {
+			D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs));
 			DIRTY_SPACE(4);
 			ofs += 4;
 			continue;
 		}
-		if (node.magic == JFFS2_OLD_MAGIC_BITMASK) {
+		if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) {
 			printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs);
 			printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n");
 			DIRTY_SPACE(4);
 			ofs += 4;
 			continue;
 		}
-		if (node.magic != JFFS2_MAGIC_BITMASK) {
+		if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) {
 			/* OK. We're out of possibilities. Whinge and move on */
-			noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", JFFS2_MAGIC_BITMASK, ofs, node.magic);
+			noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", 
+				     JFFS2_MAGIC_BITMASK, ofs, 
+				     je16_to_cpu(node->magic));
 			DIRTY_SPACE(4);
 			ofs += 4;
 			continue;
 		}
 		/* We seem to have a node of sorts. Check the CRC */
-		nodetype = node.nodetype;
-		node.nodetype |= JFFS2_NODE_ACCURATE;
-		hdr_crc = crc32(0, &node, sizeof(node)-4);
-		node.nodetype = nodetype;
-		if (hdr_crc != node.hdr_crc) {
+		crcnode.magic = node->magic;
+		crcnode.nodetype = cpu_to_je16( je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE);
+		crcnode.totlen = node->totlen;
+		hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4);
+
+		if (hdr_crc != je32_to_cpu(node->hdr_crc)) {
 			noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n",
-				     ofs, node.magic, node.nodetype, node.totlen, node.hdr_crc, hdr_crc);
+				     ofs, je16_to_cpu(node->magic),
+				     je16_to_cpu(node->nodetype), 
+				     je32_to_cpu(node->totlen),
+				     je32_to_cpu(node->hdr_crc),
+				     hdr_crc);
 			DIRTY_SPACE(4);
 			ofs += 4;
 			continue;
 		}
 
-		if (ofs + node.totlen > jeb->offset + c->sector_size) {
+		if (ofs + je32_to_cpu(node->totlen) > 
+		    jeb->offset + c->sector_size) {
 			/* Eep. Node goes over the end of the erase block. */
 			printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n",
-			       ofs, node.totlen);
+			       ofs, je32_to_cpu(node->totlen));
 			printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n");
 			DIRTY_SPACE(4);
 			ofs += 4;
 			continue;
 		}
 
-		switch(node.nodetype | JFFS2_NODE_ACCURATE) {
+		if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) {
+			/* Wheee. This is an obsoleted node */
+			D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs));
+			DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
+			ofs += PAD(je32_to_cpu(node->totlen));
+			continue;
+		}
+
+		switch(je16_to_cpu(node->nodetype)) {
 		case JFFS2_NODETYPE_INODE:
-			err = jffs2_scan_inode_node(c, jeb, &ofs);
+			if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) {
+				buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+				D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n",
+					  sizeof(struct jffs2_raw_inode), buf_len, ofs));
+				err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+				if (err)
+					return err;
+				buf_ofs = ofs;
+				node = (void *)buf;
+			}
+			err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs);
 			if (err) return err;
+			ofs += PAD(je32_to_cpu(node->totlen));
 			break;
 			
 		case JFFS2_NODETYPE_DIRENT:
-			err = jffs2_scan_dirent_node(c, jeb, &ofs);
+			if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) {
+				buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs);
+				D1(printk(KERN_DEBUG "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n",
+					  je32_to_cpu(node->totlen), buf_len, ofs));
+				err = jffs2_fill_scan_buf(c, buf, ofs, buf_len);
+				if (err)
+					return err;
+				buf_ofs = ofs;
+				node = (void *)buf;
+			}
+			err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs);
 			if (err) return err;
+			ofs += PAD(je32_to_cpu(node->totlen));
 			break;
 
 		case JFFS2_NODETYPE_CLEANMARKER:
-			if (node.totlen != sizeof(struct jffs2_unknown_node)) {
+			D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs));
+			if (je32_to_cpu(node->totlen) != c->cleanmarker_size) {
 				printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n", 
-				       ofs, node.totlen, sizeof(struct jffs2_unknown_node));
+				       ofs, je32_to_cpu(node->totlen), c->cleanmarker_size);
 				DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
+				ofs += PAD(sizeof(struct jffs2_unknown_node));
 			} else if (jeb->first_node) {
 				printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset);
 				DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
 				ofs += PAD(sizeof(struct jffs2_unknown_node));
-				continue;
 			} else {
 				struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref();
 				if (!marker_ref) {
@@ -300,98 +562,80 @@
 				}
 				marker_ref->next_in_ino = NULL;
 				marker_ref->next_phys = NULL;
-				marker_ref->flash_offset = ofs;
-				marker_ref->totlen = sizeof(struct jffs2_unknown_node);
+				marker_ref->flash_offset = ofs | REF_NORMAL;
+				marker_ref->__totlen = c->cleanmarker_size;
 				jeb->first_node = jeb->last_node = marker_ref;
 			     
-				USED_SPACE(PAD(sizeof(struct jffs2_unknown_node)));
+				USED_SPACE(PAD(c->cleanmarker_size));
+				ofs += PAD(c->cleanmarker_size);
 			}
-			ofs += PAD(sizeof(struct jffs2_unknown_node));
+			break;
+
+		case JFFS2_NODETYPE_PADDING:
+			DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
+			ofs += PAD(je32_to_cpu(node->totlen));
 			break;
 
 		default:
-			switch (node.nodetype & JFFS2_COMPAT_MASK) {
+			switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) {
 			case JFFS2_FEATURE_ROCOMPAT:
-				printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
+				printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
 			        c->flags |= JFFS2_SB_FLAG_RO;
-				if (!(OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY))
+				if (!(jffs2_is_readonly(c)))
 					return -EROFS;
-				DIRTY_SPACE(PAD(node.totlen));
-				ofs += PAD(node.totlen);
-				continue;
+				DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
+				ofs += PAD(je32_to_cpu(node->totlen));
+				break;
 
 			case JFFS2_FEATURE_INCOMPAT:
-				printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
+				printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs);
 				return -EINVAL;
 
 			case JFFS2_FEATURE_RWCOMPAT_DELETE:
-				printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
-				DIRTY_SPACE(PAD(node.totlen));
-				ofs += PAD(node.totlen);
+				D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
+				DIRTY_SPACE(PAD(je32_to_cpu(node->totlen)));
+				ofs += PAD(je32_to_cpu(node->totlen));
 				break;
 
 			case JFFS2_FEATURE_RWCOMPAT_COPY:
-				printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs);
-				USED_SPACE(PAD(node.totlen));
-				ofs += PAD(node.totlen);
+				D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs));
+				USED_SPACE(PAD(je32_to_cpu(node->totlen)));
+				ofs += PAD(je32_to_cpu(node->totlen));
 				break;
 			}
 		}
 	}
-	D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, 
-		  jeb->free_size, jeb->dirty_size, jeb->used_size));
-	return 0;
-}
 
-/* We're pointing at the first empty word on the flash. Scan and account for the whole dirty region */
-static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *startofs, int *noise)
-{
-	__u32 *buf;
-	__u32 scanlen = (jeb->offset + c->sector_size) - *startofs;
-	__u32 curofs = *startofs;
-	
-	buf = kmalloc(min((__u32)PAGE_SIZE, scanlen), GFP_KERNEL);
-	if (!buf) {
-		printk(KERN_WARNING "Scan buffer allocation failed\n");
-		return -ENOMEM;
-	}
-	while(scanlen) {
-		ssize_t retlen;
-		int ret, i;
-		
-		ret = c->mtd->read(c->mtd, curofs, min((__u32)PAGE_SIZE, scanlen), &retlen, (char *)buf);
-		if(ret) {
-			D1(printk(KERN_WARNING "jffs2_scan_empty(): Read 0x%x bytes at 0x%08x returned %d\n", min((__u32)PAGE_SIZE, scanlen), curofs, ret));
-			kfree(buf);
-			return ret;
-		}
-		if (retlen < 4) {
-			D1(printk(KERN_WARNING "Eep. too few bytes read in scan_empty()\n"));
-			kfree(buf);
-			return -EIO;
-		}
-		for (i=0; i<(retlen / 4); i++) {
-			if (buf[i] != 0xffffffff) {
-				curofs += i*4;
-
-				noisy_printk(noise, "jffs2_scan_empty(): Empty block at 0x%08x ends at 0x%08x (with 0x%08x)! Marking dirty\n", *startofs, curofs, buf[i]);
-				DIRTY_SPACE(curofs - (*startofs));
-				*startofs = curofs;
-				kfree(buf);
-				return 0;
-			}
-		}
-		scanlen -= retlen&~3;
-		curofs += retlen&~3;
+
+	D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset, 
+		  jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size));
+
+	/* mark_node_obsolete can add to wasted !! */
+	if (jeb->wasted_size) {
+		jeb->dirty_size += jeb->wasted_size;
+		c->dirty_size += jeb->wasted_size;
+		c->wasted_size -= jeb->wasted_size;
+		jeb->wasted_size = 0;
 	}
 
-	D1(printk(KERN_DEBUG "Empty flash detected from 0x%08x to 0x%08x\n", *startofs, curofs));
-	kfree(buf);
-	*startofs = curofs;
-	return 0;
+	if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size 
+		&& (!jeb->first_node || !jeb->first_node->next_in_ino) )
+		return BLK_STATE_CLEANMARKER;
+		
+	/* move blocks with max 4 byte dirty space to cleanlist */	
+	else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) {
+		c->dirty_size -= jeb->dirty_size;
+		c->wasted_size += jeb->dirty_size; 
+		jeb->wasted_size += jeb->dirty_size;
+		jeb->dirty_size = 0;
+		return BLK_STATE_CLEAN;
+	} else if (jeb->used_size || jeb->unchecked_size)
+		return BLK_STATE_PARTDIRTY;
+	else
+		return BLK_STATE_ALLDIRTY;
 }
 
-static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, __u32 ino)
+static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino)
 {
 	struct jffs2_inode_cache *ic;
 
@@ -399,137 +643,77 @@
 	if (ic)
 		return ic;
 
+	if (ino > c->highest_ino)
+		c->highest_ino = ino;
+
 	ic = jffs2_alloc_inode_cache();
 	if (!ic) {
 		printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n");
 		return NULL;
 	}
 	memset(ic, 0, sizeof(*ic));
-	ic->scan = kmalloc(sizeof(struct jffs2_scan_info), GFP_KERNEL);
-	if (!ic->scan) {
-		printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of scan info for inode cache failed\n");
-		jffs2_free_inode_cache(ic);
-		return NULL;
-	}
-	memset(ic->scan, 0, sizeof(*ic->scan));
+
 	ic->ino = ino;
 	ic->nodes = (void *)ic;
 	jffs2_add_ino_cache(c, ic);
 	if (ino == 1)
-		ic->nlink=1;
+		ic->nlink = 1;
 	return ic;
 }
 
-static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs)
+static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
+				 struct jffs2_raw_inode *ri, uint32_t ofs)
 {
 	struct jffs2_raw_node_ref *raw;
-	struct jffs2_full_dnode *fn;
-	struct jffs2_tmp_dnode_info *tn, **tn_list;
 	struct jffs2_inode_cache *ic;
-	struct jffs2_raw_inode ri;
-	__u32 crc;
-	__u16 oldnodetype;
-	int ret;
-	ssize_t retlen;
-
-	D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", *ofs));
+	uint32_t ino = je32_to_cpu(ri->ino);
 
-	ret = c->mtd->read(c->mtd, *ofs, sizeof(ri), &retlen, (char *)&ri);
-	if (ret) {
-		printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs, ret);
-		return ret;
-	}
-	if (retlen != sizeof(ri)) {
-		printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", 
-		       retlen, *ofs, sizeof(ri));
-		return -EIO;
-	}
-
-	/* We sort of assume that the node was accurate when it was 
-	   first written to the medium :) */
-	oldnodetype = ri.nodetype;
-	ri.nodetype |= JFFS2_NODE_ACCURATE;
-	crc = crc32(0, &ri, sizeof(ri)-8);
-	ri.nodetype = oldnodetype;
-
-	if(crc != ri.node_crc) {
-		printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-		       *ofs, ri.node_crc, crc);
-		/* FIXME: Why do we believe totlen? */
-		DIRTY_SPACE(4);
-		*ofs += 4;
-		return 0;
-	}
-	/* There was a bug where we wrote hole nodes out with csize/dsize
-	   swapped. Deal with it */
-	if (ri.compr == JFFS2_COMPR_ZERO && !ri.dsize && ri.csize) {
-		ri.dsize = ri.csize;
-		ri.csize = 0;
-	}
+	D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
 
-	if (ri.csize) {
-		/* Check data CRC too */
-		unsigned char *dbuf;
-		__u32 crc;
-
-		dbuf = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
-		if (!dbuf) {
-			printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of temporary data buffer for CRC check failed\n");
-			return -ENOMEM;
-		}
-		ret = c->mtd->read(c->mtd, *ofs+sizeof(ri), ri.csize, &retlen, dbuf);
-		if (ret) {
-			printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs+sizeof(ri), ret);
-			kfree(dbuf);
-			return ret;
-		}
-		if (retlen != ri.csize) {
-			printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", 
-			       retlen, *ofs+ sizeof(ri), ri.csize);
-			kfree(dbuf);
-			return -EIO;
-		}
-		crc = crc32(0, dbuf, ri.csize);
-		kfree(dbuf);
-		if (crc != ri.data_crc) {
-			printk(KERN_NOTICE "jffs2_scan_inode_node(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-			       *ofs, ri.data_crc, crc);
-			DIRTY_SPACE(PAD(ri.totlen));
-			*ofs += PAD(ri.totlen);
-			return 0;
-		}
-	}
+	/* We do very little here now. Just check the ino# to which we should attribute
+	   this node; we can do all the CRC checking etc. later. There's a tradeoff here -- 
+	   we used to scan the flash once only, reading everything we want from it into
+	   memory, then building all our in-core data structures and freeing the extra
+	   information. Now we allow the first part of the mount to complete a lot quicker,
+	   but we have to go _back_ to the flash in order to finish the CRC checking, etc. 
+	   Which means that the _full_ amount of time to get to proper write mode with GC
+	   operational may actually be _longer_ than before. Sucks to be me. */
 
-	/* Wheee. It worked */
 	raw = jffs2_alloc_raw_node_ref();
 	if (!raw) {
 		printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n");
 		return -ENOMEM;
 	}
-	tn = jffs2_alloc_tmp_dnode_info();
-	if (!tn) {
-		jffs2_free_raw_node_ref(raw);
-		return -ENOMEM;
-	}
-	fn = jffs2_alloc_full_dnode();
-	if (!fn) {
-		jffs2_free_tmp_dnode_info(tn);
-		jffs2_free_raw_node_ref(raw);
-		return -ENOMEM;
-	}
-	ic = jffs2_scan_make_ino_cache(c, ri.ino);
+
+	ic = jffs2_get_ino_cache(c, ino);
 	if (!ic) {
-		jffs2_free_full_dnode(fn);
-		jffs2_free_tmp_dnode_info(tn);
-		jffs2_free_raw_node_ref(raw);
-		return -ENOMEM;
+		/* Inocache get failed. Either we read a bogus ino# or it's just genuinely the
+		   first node we found for this inode. Do a CRC check to protect against the former
+		   case */
+		uint32_t crc = crc32(0, ri, sizeof(*ri)-8);
+
+		if (crc != je32_to_cpu(ri->node_crc)) {
+			printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
+			       ofs, je32_to_cpu(ri->node_crc), crc);
+			/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
+			DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen)));
+			jffs2_free_raw_node_ref(raw);
+			return 0;
+		}
+		ic = jffs2_scan_make_ino_cache(c, ino);
+		if (!ic) {
+			jffs2_free_raw_node_ref(raw);
+			return -ENOMEM;
+		}
 	}
 
-	/* Build the data structures and file them for later */
-	raw->flash_offset = *ofs;
-	raw->totlen = PAD(ri.totlen);
+	/* Wheee. It worked */
+
+	raw->flash_offset = ofs | REF_UNCHECKED;
+	raw->__totlen = PAD(je32_to_cpu(ri->totlen));
 	raw->next_phys = NULL;
 	raw->next_in_ino = ic->nodes;
+
 	ic->nodes = raw;
 	if (!jeb->first_node)
 		jeb->first_node = raw;
@@ -538,134 +722,56 @@
 	jeb->last_node = raw;
 
 	D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n", 
-		  ri.ino, ri.version, ri.offset, ri.offset+ri.dsize));
+		  je32_to_cpu(ri->ino), je32_to_cpu(ri->version),
+		  je32_to_cpu(ri->offset),
+		  je32_to_cpu(ri->offset)+je32_to_cpu(ri->dsize)));
 
-	pseudo_random += ri.version;
+	pseudo_random += je32_to_cpu(ri->version);
 
-	for (tn_list = &ic->scan->tmpnodes; *tn_list; tn_list = &((*tn_list)->next)) {
-		if ((*tn_list)->version < ri.version)
-			continue;
-		if ((*tn_list)->version > ri.version) 
-			break;
-		/* Wheee. We've found another instance of the same version number.
-		   We should obsolete one of them. 
-		*/
-		D1(printk(KERN_DEBUG "Duplicate version %d found in ino #%u. Previous one is at 0x%08x\n", ri.version, ic->ino, (*tn_list)->fn->raw->flash_offset &~3));
-		if (!jeb->used_size) {
-			D1(printk(KERN_DEBUG "No valid nodes yet found in this eraseblock 0x%08x, so obsoleting the new instance at 0x%08x\n", 
-				  jeb->offset, raw->flash_offset & ~3));
-			ri.nodetype &= ~JFFS2_NODE_ACCURATE;
-			/* Perhaps we could also mark it as such on the medium. Maybe later */
-		}
-		break;
-	}
-
-	if (ri.nodetype & JFFS2_NODE_ACCURATE) {
-		memset(fn,0,sizeof(*fn));
-
-		fn->ofs = ri.offset;
-		fn->size = ri.dsize;
-		fn->frags = 0;
-		fn->raw = raw;
-
-		tn->next = NULL;
-		tn->fn = fn;
-		tn->version = ri.version;
-
-		USED_SPACE(PAD(ri.totlen));
-		jffs2_add_tn_to_list(tn, &ic->scan->tmpnodes);
-		/* Make sure the one we just added is the _last_ in the list
-		   with this version number, so the older ones get obsoleted */
-		while (tn->next && tn->next->version == tn->version) {
-
-			D1(printk(KERN_DEBUG "Shifting new node at 0x%08x after other node at 0x%08x for version %d in list\n",
-				  fn->raw->flash_offset&~3, tn->next->fn->raw->flash_offset &~3, ri.version));
-
-			if(tn->fn != fn)
-				BUG();
-			tn->fn = tn->next->fn;
-			tn->next->fn = fn;
-			tn = tn->next;
-		}
-	} else {
-		jffs2_free_full_dnode(fn);
-		jffs2_free_tmp_dnode_info(tn);
-		raw->flash_offset |= 1;
-		DIRTY_SPACE(PAD(ri.totlen));
-	}		
-	*ofs += PAD(ri.totlen);
+	UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen)));
 	return 0;
 }
 
-static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs)
+static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 
+				  struct jffs2_raw_dirent *rd, uint32_t ofs)
 {
 	struct jffs2_raw_node_ref *raw;
 	struct jffs2_full_dirent *fd;
 	struct jffs2_inode_cache *ic;
-	struct jffs2_raw_dirent rd;
-	__u16 oldnodetype;
-	int ret;
-	__u32 crc;
-	ssize_t retlen;
-
-	D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", *ofs));
+	uint32_t crc;
 
-	ret = c->mtd->read(c->mtd, *ofs, sizeof(rd), &retlen, (char *)&rd);
-	if (ret) {
-		printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", *ofs, ret);
-		return ret;
-	}
-	if (retlen != sizeof(rd)) {
-		printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", 
-		       retlen, *ofs, sizeof(rd));
-		return -EIO;
-	}
+	D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs));
 
-	/* We sort of assume that the node was accurate when it was 
-	   first written to the medium :) */
-	oldnodetype = rd.nodetype;
-	rd.nodetype |= JFFS2_NODE_ACCURATE;
-	crc = crc32(0, &rd, sizeof(rd)-8);
-	rd.nodetype = oldnodetype;
+	/* We don't get here unless the node is still valid, so we don't have to
+	   mask in the ACCURATE bit any more. */
+	crc = crc32(0, rd, sizeof(*rd)-8);
 
-	if (crc != rd.node_crc) {
+	if (crc != je32_to_cpu(rd->node_crc)) {
 		printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-		       *ofs, rd.node_crc, crc);
-		/* FIXME: Why do we believe totlen? */
-		DIRTY_SPACE(4);
-		*ofs += 4;
+		       ofs, je32_to_cpu(rd->node_crc), crc);
+		/* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */
+		DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
 		return 0;
 	}
 
-	pseudo_random += rd.version;
+	pseudo_random += je32_to_cpu(rd->version);
 
-	fd = jffs2_alloc_full_dirent(rd.nsize+1);
+	fd = jffs2_alloc_full_dirent(rd->nsize+1);
 	if (!fd) {
 		return -ENOMEM;
-}
-	ret = c->mtd->read(c->mtd, *ofs + sizeof(rd), rd.nsize, &retlen, &fd->name[0]);
-	if (ret) {
-		jffs2_free_full_dirent(fd);
-		printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", 
-		       *ofs + sizeof(rd), ret);
-		return ret;
-	}
-	if (retlen != rd.nsize) {
-		jffs2_free_full_dirent(fd);
-		printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", 
-		       retlen, *ofs + sizeof(rd), rd.nsize);
-		return -EIO;
 	}
-	crc = crc32(0, fd->name, rd.nsize);
-	if (crc != rd.name_crc) {
+	memcpy(&fd->name, rd->name, rd->nsize);
+	fd->name[rd->nsize] = 0;
+
+	crc = crc32(0, fd->name, rd->nsize);
+	if (crc != je32_to_cpu(rd->name_crc)) {
 		printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n",
-		       *ofs, rd.name_crc, crc);	
-		fd->name[rd.nsize]=0;
-		D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, rd.ino));
+		       ofs, je32_to_cpu(rd->name_crc), crc);	
+		D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino)));
 		jffs2_free_full_dirent(fd);
 		/* FIXME: Why do we believe totlen? */
-		DIRTY_SPACE(PAD(rd.totlen));
-		*ofs += PAD(rd.totlen);
+		/* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */
+		DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen)));
 		return 0;
 	}
 	raw = jffs2_alloc_raw_node_ref();
@@ -674,15 +780,15 @@
 		printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n");
 		return -ENOMEM;
 	}
-	ic = jffs2_scan_make_ino_cache(c, rd.pino);
+	ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino));
 	if (!ic) {
 		jffs2_free_full_dirent(fd);
 		jffs2_free_raw_node_ref(raw);
 		return -ENOMEM;
 	}
 	
-	raw->totlen = PAD(rd.totlen);
-	raw->flash_offset = *ofs;
+	raw->__totlen = PAD(je32_to_cpu(rd->totlen));
+	raw->flash_offset = ofs | REF_PRISTINE;
 	raw->next_phys = NULL;
 	raw->next_in_ino = ic->nodes;
 	ic->nodes = raw;
@@ -692,24 +798,15 @@
 		jeb->last_node->next_phys = raw;
 	jeb->last_node = raw;
 
-	if (rd.nodetype & JFFS2_NODE_ACCURATE) {
-		fd->raw = raw;
-		fd->next = NULL;
-		fd->version = rd.version;
-		fd->ino = rd.ino;
-		fd->name[rd.nsize]=0;
-		fd->nhash = full_name_hash(fd->name, rd.nsize);
-		fd->type = rd.type;
-
-		USED_SPACE(PAD(rd.totlen));
-		jffs2_add_fd_to_list(c, fd, &ic->scan->dents);
-	} else {
-		raw->flash_offset |= 1;
-		jffs2_free_full_dirent(fd);
+	fd->raw = raw;
+	fd->next = NULL;
+	fd->version = je32_to_cpu(rd->version);
+	fd->ino = je32_to_cpu(rd->ino);
+	fd->nhash = full_name_hash(fd->name, rd->nsize);
+	fd->type = rd->type;
+	USED_SPACE(PAD(je32_to_cpu(rd->totlen)));
+	jffs2_add_fd_to_list(c, fd, &ic->scan_dents);
 
-		DIRTY_SPACE(PAD(rd.totlen));
-	} 
-	*ofs += PAD(rd.totlen);
 	return 0;
 }
 
@@ -731,26 +828,90 @@
 	struct list_head *n = head->next;
 
 	list_del(head);
-	while(count--)
+	while(count--) {
 		n = n->next;
+	}
 	list_add(head, n);
 }
 
-static void jffs2_rotate_lists(struct jffs2_sb_info *c)
+void jffs2_rotate_lists(struct jffs2_sb_info *c)
 {
 	uint32_t x;
+	uint32_t rotateby;
 
 	x = count_list(&c->clean_list);
-	if (x)
-		rotate_list((&c->clean_list), pseudo_random % x);
+	if (x) {
+		rotateby = pseudo_random % x;
+		D1(printk(KERN_DEBUG "Rotating clean_list by %d\n", rotateby));
+
+		rotate_list((&c->clean_list), rotateby);
+
+		D1(printk(KERN_DEBUG "Erase block at front of clean_list is at %08x\n",
+			  list_entry(c->clean_list.next, struct jffs2_eraseblock, list)->offset));
+	} else {
+		D1(printk(KERN_DEBUG "Not rotating empty clean_list\n"));
+	}
+
+	x = count_list(&c->very_dirty_list);
+	if (x) {
+		rotateby = pseudo_random % x;
+		D1(printk(KERN_DEBUG "Rotating very_dirty_list by %d\n", rotateby));
+
+		rotate_list((&c->very_dirty_list), rotateby);
+
+		D1(printk(KERN_DEBUG "Erase block at front of very_dirty_list is at %08x\n",
+			  list_entry(c->very_dirty_list.next, struct jffs2_eraseblock, list)->offset));
+	} else {
+		D1(printk(KERN_DEBUG "Not rotating empty very_dirty_list\n"));
+	}
 
 	x = count_list(&c->dirty_list);
-	if (x)
-		rotate_list((&c->dirty_list), pseudo_random % x);
+	if (x) {
+		rotateby = pseudo_random % x;
+		D1(printk(KERN_DEBUG "Rotating dirty_list by %d\n", rotateby));
+
+		rotate_list((&c->dirty_list), rotateby);
+
+		D1(printk(KERN_DEBUG "Erase block at front of dirty_list is at %08x\n",
+			  list_entry(c->dirty_list.next, struct jffs2_eraseblock, list)->offset));
+	} else {
+		D1(printk(KERN_DEBUG "Not rotating empty dirty_list\n"));
+	}
+
+	x = count_list(&c->erasable_list);
+	if (x) {
+		rotateby = pseudo_random % x;
+		D1(printk(KERN_DEBUG "Rotating erasable_list by %d\n", rotateby));
+
+		rotate_list((&c->erasable_list), rotateby);
 
-	if (c->nr_erasing_blocks)
-		rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks);
+		D1(printk(KERN_DEBUG "Erase block at front of erasable_list is at %08x\n",
+			  list_entry(c->erasable_list.next, struct jffs2_eraseblock, list)->offset));
+	} else {
+		D1(printk(KERN_DEBUG "Not rotating empty erasable_list\n"));
+	}
+
+	if (c->nr_erasing_blocks) {
+		rotateby = pseudo_random % c->nr_erasing_blocks;
+		D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby));
+
+		rotate_list((&c->erase_pending_list), rotateby);
+
+		D1(printk(KERN_DEBUG "Erase block at front of erase_pending_list is at %08x\n",
+			  list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list)->offset));
+	} else {
+		D1(printk(KERN_DEBUG "Not rotating empty erase_pending_list\n"));
+	}
+
+	if (c->nr_free_blocks) {
+		rotateby = pseudo_random % c->nr_free_blocks;
+		D1(printk(KERN_DEBUG "Rotating free_list by %d\n", rotateby));
 
-	if (c->nr_free_blocks) /* Not that it should ever be zero */
-		rotate_list((&c->free_list), pseudo_random % c->nr_free_blocks);
+		rotate_list((&c->free_list), rotateby);
+
+		D1(printk(KERN_DEBUG "Erase block at front of free_list is at %08x\n",
+			  list_entry(c->free_list.next, struct jffs2_eraseblock, list)->offset));
+	} else {
+		D1(printk(KERN_DEBUG "Not rotating empty free_list\n"));
+	}
 }
diff -Nur linux-2.4.30/fs/jffs2/super.c linux-2.4.30-linux-mips.org/fs/jffs2/super.c
--- linux-2.4.30/fs/jffs2/super.c	2002-11-29 00:53:15.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/super.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,291 +1,258 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: super.c,v 1.48.2.3 2002/10/11 09:04:44 dwmw2 Exp $
+ * $Id: super.c,v 1.93 2004/05/05 12:00:31 dwmw2 Exp $
  *
  */
 
 #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/mount.h>
 #include <linux/jffs2.h>
 #include <linux/pagemap.h>
 #include <linux/mtd/mtd.h>
-#include <linux/interrupt.h>
+#include <linux/ctype.h>
+#include <linux/namei.h>
 #include "nodelist.h"
 
-#ifndef MTD_BLOCK_MAJOR
-#define MTD_BLOCK_MAJOR 31
-#endif
+static void jffs2_put_super(struct super_block *);
+
+static kmem_cache_t *jffs2_inode_cachep;
+
+static struct inode *jffs2_alloc_inode(struct super_block *sb)
+{
+	struct jffs2_inode_info *ei;
+	ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, SLAB_KERNEL);
+	if (!ei)
+		return NULL;
+	return &ei->vfs_inode;
+}
+
+static void jffs2_destroy_inode(struct inode *inode)
+{
+	kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode));
+}
+
+static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags)
+{
+	struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo;
 
-extern void jffs2_read_inode (struct inode *);
-void jffs2_put_super (struct super_block *);
-void jffs2_write_super (struct super_block *);
-static int jffs2_statfs (struct super_block *, struct statfs *);
-int jffs2_remount_fs (struct super_block *, int *, char *);
-extern void jffs2_clear_inode (struct inode *);
+	if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) ==
+	    SLAB_CTOR_CONSTRUCTOR) {
+		init_MUTEX_LOCKED(&ei->sem);
+		inode_init_once(&ei->vfs_inode);
+	}
+}
 
 static struct super_operations jffs2_super_operations =
 {
-	read_inode:	jffs2_read_inode,
-//	delete_inode:	jffs2_delete_inode,
-	put_super:	jffs2_put_super,
-	write_super:	jffs2_write_super,
-	statfs:		jffs2_statfs,
-	remount_fs:	jffs2_remount_fs,
-	clear_inode:	jffs2_clear_inode
+	.alloc_inode =	jffs2_alloc_inode,
+	.destroy_inode =jffs2_destroy_inode,
+	.read_inode =	jffs2_read_inode,
+	.put_super =	jffs2_put_super,
+	.write_super =	jffs2_write_super,
+	.statfs =	jffs2_statfs,
+	.remount_fs =	jffs2_remount_fs,
+	.clear_inode =	jffs2_clear_inode,
+	.dirty_inode =	jffs2_dirty_inode,
 };
 
-static int jffs2_statfs(struct super_block *sb, struct statfs *buf)
+static int jffs2_sb_compare(struct super_block *sb, void *data)
 {
+	struct jffs2_sb_info *p = data;
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
-	unsigned long avail;
-
-	buf->f_type = JFFS2_SUPER_MAGIC;
-	buf->f_bsize = 1 << PAGE_SHIFT;
-	buf->f_blocks = c->flash_size >> PAGE_SHIFT;
-	buf->f_files = 0;
-	buf->f_ffree = 0;
-	buf->f_namelen = JFFS2_MAX_NAME_LEN;
-
-	spin_lock_bh(&c->erase_completion_lock);
-
-	avail = c->dirty_size + c->free_size;
-	if (avail > c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE)
-		avail -= c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE;
-	else
-		avail = 0;
-
-	buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT;
-
-#if CONFIG_JFFS2_FS_DEBUG > 0
-	printk(KERN_DEBUG "STATFS:\n");
-	printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size);
-	printk(KERN_DEBUG "used_size: %08x\n", c->used_size);
-	printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size);
-	printk(KERN_DEBUG "free_size: %08x\n", c->free_size);
-	printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size);
-	printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size);
-	printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size);
 
-	if (c->nextblock) {
-		printk(KERN_DEBUG "nextblock: 0x%08x\n", c->nextblock->offset);
+	/* The superblocks are considered to be equivalent if the underlying MTD
+	   device is the same one */
+	if (c->mtd == p->mtd) {
+		D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", p->mtd->index, p->mtd->name));
+		return 1;
 	} else {
-		printk(KERN_DEBUG "nextblock: NULL\n");
+		D1(printk(KERN_DEBUG "jffs2_sb_compare: No match, device %d (\"%s\"), device %d (\"%s\")\n",
+			  c->mtd->index, c->mtd->name, p->mtd->index, p->mtd->name));
+		return 0;
 	}
-	if (c->gcblock) {
-		printk(KERN_DEBUG "gcblock: 0x%08x\n", c->gcblock->offset);
-	} else {
-		printk(KERN_DEBUG "gcblock: NULL\n");
-	}
-	if (list_empty(&c->clean_list)) {
-		printk(KERN_DEBUG "clean_list: empty\n");
-	} else {
-		struct list_head *this;
+}
 
-		list_for_each(this, &c->clean_list) {
-			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
-			printk(KERN_DEBUG "clean_list: %08x\n", jeb->offset);
-		}
-	}
-	if (list_empty(&c->dirty_list)) {
-		printk(KERN_DEBUG "dirty_list: empty\n");
-	} else {
-		struct list_head *this;
+static int jffs2_sb_set(struct super_block *sb, void *data)
+{
+	struct jffs2_sb_info *p = data;
 
-		list_for_each(this, &c->dirty_list) {
-			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
-			printk(KERN_DEBUG "dirty_list: %08x\n", jeb->offset);
-		}
-	}
-	if (list_empty(&c->erasing_list)) {
-		printk(KERN_DEBUG "erasing_list: empty\n");
-	} else {
-		struct list_head *this;
+	/* For persistence of NFS exports etc. we use the same s_dev
+	   each time we mount the device, don't just use an anonymous
+	   device */
+	sb->s_fs_info = p;
+	p->os_priv = sb;
+	sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, p->mtd->index);
 
-		list_for_each(this, &c->erasing_list) {
-			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
-			printk(KERN_DEBUG "erasing_list: %08x\n", jeb->offset);
-		}
-	}
-	if (list_empty(&c->erase_pending_list)) {
-		printk(KERN_DEBUG "erase_pending_list: empty\n");
-	} else {
-		struct list_head *this;
+	return 0;
+}
 
-		list_for_each(this, &c->erase_pending_list) {
-			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
-			printk(KERN_DEBUG "erase_pending_list: %08x\n", jeb->offset);
-		}
-	}
-	if (list_empty(&c->free_list)) {
-		printk(KERN_DEBUG "free_list: empty\n");
-	} else {
-		struct list_head *this;
+static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type,
+					      int flags, const char *dev_name, 
+					      void *data, struct mtd_info *mtd)
+{
+	struct super_block *sb;
+	struct jffs2_sb_info *c;
+	int ret;
 
-		list_for_each(this, &c->free_list) {
-			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
-			printk(KERN_DEBUG "free_list: %08x\n", jeb->offset);
-		}
-	}
-	if (list_empty(&c->bad_list)) {
-		printk(KERN_DEBUG "bad_list: empty\n");
-	} else {
-		struct list_head *this;
+	c = kmalloc(sizeof(*c), GFP_KERNEL);
+	if (!c)
+		return ERR_PTR(-ENOMEM);
+	memset(c, 0, sizeof(*c));
+	c->mtd = mtd;
 
-		list_for_each(this, &c->bad_list) {
-			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
-			printk(KERN_DEBUG "bad_list: %08x\n", jeb->offset);
-		}
+	sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
+
+	if (IS_ERR(sb))
+		goto out_put;
+
+	if (sb->s_root) {
+		/* New mountpoint for JFFS2 which is already mounted */
+		D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n",
+			  mtd->index, mtd->name));
+		goto out_put;
 	}
-	if (list_empty(&c->bad_used_list)) {
-		printk(KERN_DEBUG "bad_used_list: empty\n");
-	} else {
-		struct list_head *this;
 
-		list_for_each(this, &c->bad_used_list) {
-			struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
-			printk(KERN_DEBUG "bad_used_list: %08x\n", jeb->offset);
-		}
+	D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n",
+		  mtd->index, mtd->name));
+
+	sb->s_op = &jffs2_super_operations;
+	sb->s_flags = flags;
+
+	ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0);
+
+	if (ret) {
+		/* Failure case... */
+		up_write(&sb->s_umount);
+		deactivate_super(sb);
+		return ERR_PTR(ret);
 	}
-#endif /* CONFIG_JFFS2_FS_DEBUG */
 
-	spin_unlock_bh(&c->erase_completion_lock);
+	sb->s_flags |= MS_ACTIVE;
+	return sb;
 
+ out_put:
+	kfree(c);
+	put_mtd_device(mtd);
 
-	return 0;
+	return sb;
 }
 
-static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent)
+static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type *fs_type,
+					      int flags, const char *dev_name, 
+					      void *data, int mtdnr)
 {
-	struct jffs2_sb_info *c;
-	struct inode *root_i;
-	int i;
-
-	D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
+	struct mtd_info *mtd;
 
-	if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) {
-		if (!silent)
-			printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev));
-		return NULL;
+	mtd = get_mtd_device(NULL, mtdnr);
+	if (!mtd) {
+		D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr));
+		return ERR_PTR(-EINVAL);
 	}
 
-	c = JFFS2_SB_INFO(sb);
-	memset(c, 0, sizeof(*c));
-	
-	c->mtd = get_mtd_device(NULL, MINOR(sb->s_dev));
-	if (!c->mtd) {
-		D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev)));
-		return NULL;
-	}
-	c->sector_size = c->mtd->erasesize;
-	c->free_size = c->flash_size = c->mtd->size;
-	c->nr_blocks = c->mtd->size / c->mtd->erasesize;
-	c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL);
-	if (!c->blocks)
-		goto out_mtd;
-	for (i=0; i<c->nr_blocks; i++) {
-		INIT_LIST_HEAD(&c->blocks[i].list);
-		c->blocks[i].offset = i * c->sector_size;
-		c->blocks[i].free_size = c->sector_size;
-		c->blocks[i].dirty_size = 0;
-		c->blocks[i].used_size = 0;
-		c->blocks[i].first_node = NULL;
-		c->blocks[i].last_node = NULL;
-	}
-		
-	spin_lock_init(&c->nodelist_lock);
-	init_MUTEX(&c->alloc_sem);
-	init_waitqueue_head(&c->erase_wait);
-	spin_lock_init(&c->erase_completion_lock);
-	spin_lock_init(&c->inocache_lock);
-
-	INIT_LIST_HEAD(&c->clean_list);
-	INIT_LIST_HEAD(&c->dirty_list);
-	INIT_LIST_HEAD(&c->erasing_list);
-	INIT_LIST_HEAD(&c->erase_pending_list);
-	INIT_LIST_HEAD(&c->erase_complete_list);
-	INIT_LIST_HEAD(&c->free_list);
-	INIT_LIST_HEAD(&c->bad_list);
-	INIT_LIST_HEAD(&c->bad_used_list);
-	c->highest_ino = 1;
-
-	if (jffs2_build_filesystem(c)) {
-		D1(printk(KERN_DEBUG "build_fs failed\n"));
-		goto out_nodes;
+	return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd);
+}
+
+static struct super_block *jffs2_get_sb(struct file_system_type *fs_type,
+					int flags, const char *dev_name,
+					void *data)
+{
+	int err;
+	struct nameidata nd;
+	int mtdnr;
+
+	if (!dev_name)
+		return ERR_PTR(-EINVAL);
+
+	D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name));
+
+	/* The preferred way of mounting in future; especially when
+	   CONFIG_BLK_DEV is implemented - we specify the underlying
+	   MTD device by number or by name, so that we don't require 
+	   block device support to be present in the kernel. */
+
+	/* FIXME: How to do the root fs this way? */
+
+	if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') {
+		/* Probably mounting without the blkdev crap */
+		if (dev_name[3] == ':') {
+			struct mtd_info *mtd;
+
+			/* Mount by MTD device name */
+			D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4));
+			for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) {
+				mtd = get_mtd_device(NULL, mtdnr);
+				if (mtd) {
+					if (!strcmp(mtd->name, dev_name+4))
+						return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd);
+					put_mtd_device(mtd);
+				}
+			}
+			printk(KERN_NOTICE "jffs2_get_sb(): MTD device with name \"%s\" not found.\n", dev_name+4);
+		} else if (isdigit(dev_name[3])) {
+			/* Mount by MTD device number name */
+			char *endptr;
+			
+			mtdnr = simple_strtoul(dev_name+3, &endptr, 0);
+			if (!*endptr) {
+				/* It was a valid number */
+				D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr));
+				return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr);
+			}
+		}
 	}
 
-	sb->s_op = &jffs2_super_operations;
+	/* Try the old way - the hack where we allowed users to mount 
+	   /dev/mtdblock$(n) but didn't actually _use_ the blkdev */
+
+	err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
 
-	D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n"));
-	root_i = iget(sb, 1);
-	if (is_bad_inode(root_i)) {
-		D1(printk(KERN_WARNING "get root inode failed\n"));
-		goto out_nodes;
+	D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n",
+		  err, nd.dentry->d_inode));
+
+	if (err)
+		return ERR_PTR(err);
+
+	err = -EINVAL;
+
+	if (!S_ISBLK(nd.dentry->d_inode->i_mode))
+		goto out;
+
+	if (nd.mnt->mnt_flags & MNT_NODEV) {
+		err = -EACCES;
+		goto out;
 	}
 
-	D1(printk(KERN_DEBUG "jffs2_read_super(): d_alloc_root()\n"));
-	sb->s_root = d_alloc_root(root_i);
-	if (!sb->s_root)
-		goto out_root_i;
+	if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) {
+		if (!(flags & MS_VERBOSE)) /* Yes I mean this. Strangely */
+			printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n",
+			       dev_name);
+		goto out;
+	}
 
-#if LINUX_VERSION_CODE >= 0x20403
-	sb->s_maxbytes = 0xFFFFFFFF;
-#endif
-	sb->s_blocksize = PAGE_CACHE_SIZE;
-	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
-	sb->s_magic = JFFS2_SUPER_MAGIC;
-	if (!(sb->s_flags & MS_RDONLY))
-		jffs2_start_garbage_collect_thread(c);
-	return sb;
+	mtdnr = iminor(nd.dentry->d_inode);
+	path_release(&nd);
 
- out_root_i:
-	iput(root_i);
- out_nodes:
-	jffs2_free_ino_caches(c);
-	jffs2_free_raw_node_refs(c);
-	kfree(c->blocks);
- out_mtd:
-	put_mtd_device(c->mtd);
-	return NULL;
+	return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr);
+
+out:
+	path_release(&nd);
+	return ERR_PTR(err);
 }
 
-void jffs2_put_super (struct super_block *sb)
+static void jffs2_put_super (struct super_block *sb)
 {
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
 
@@ -293,74 +260,53 @@
 
 	if (!(sb->s_flags & MS_RDONLY))
 		jffs2_stop_garbage_collect_thread(c);
+	down(&c->alloc_sem);
+	jffs2_flush_wbuf_pad(c);
+	up(&c->alloc_sem);
 	jffs2_free_ino_caches(c);
 	jffs2_free_raw_node_refs(c);
 	kfree(c->blocks);
+	jffs2_flash_cleanup(c);
+	kfree(c->inocache_list);
 	if (c->mtd->sync)
 		c->mtd->sync(c->mtd);
-	put_mtd_device(c->mtd);
-	
-	D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
-}
-
-int jffs2_remount_fs (struct super_block *sb, int *flags, char *data)
-{
-	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
-
-	if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY))
-		return -EROFS;
 
-	/* We stop if it was running, then restart if it needs to.
-	   This also catches the case where it was stopped and this
-	   is just a remount to restart it */
-	if (!(sb->s_flags & MS_RDONLY))
-		jffs2_stop_garbage_collect_thread(c);
-
-	if (!(*flags & MS_RDONLY))
-		jffs2_start_garbage_collect_thread(c);
-	
-	sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY);
-
-	return 0;
+	D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
 }
 
-void jffs2_write_super (struct super_block *sb)
+static void jffs2_kill_sb(struct super_block *sb)
 {
 	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
-	sb->s_dirt = 0;
-
-	if (sb->s_flags & MS_RDONLY)
-		return;
-
-	jffs2_garbage_collect_trigger(c);
-	jffs2_erase_pending_blocks(c);
-	jffs2_mark_erased_blocks(c);
+	generic_shutdown_super(sb);
+	put_mtd_device(c->mtd);
+	kfree(c);
 }
 
-
-static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super);
+static struct file_system_type jffs2_fs_type = {
+	.owner =	THIS_MODULE,
+	.name =		"jffs2",
+	.get_sb =	jffs2_get_sb,
+	.kill_sb =	jffs2_kill_sb,
+};
 
 static int __init init_jffs2_fs(void)
 {
 	int ret;
 
-	printk(KERN_NOTICE "JFFS2 version 2.1. (C) 2001 Red Hat, Inc., designed by Axis Communications AB.\n");
-
-#ifdef JFFS2_OUT_OF_KERNEL
-	/* sanity checks. Could we do these at compile time? */
-	if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) {
-		printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n", 
-		       sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u));
-		return -EIO;
-	}
-
-	if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) {
-		printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n", 
-		       sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u));
-		return -EIO;
-	}
+	printk(KERN_INFO "JFFS2 version 2.2."
+#ifdef CONFIG_JFFS2_FS_NAND
+	       " (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,
+					     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();
 	if (ret) {
 		printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
@@ -388,9 +334,10 @@
 
 static void __exit exit_jffs2_fs(void)
 {
+	unregister_filesystem(&jffs2_fs_type);
 	jffs2_destroy_slab_caches();
 	jffs2_zlib_exit();
-	unregister_filesystem(&jffs2_fs_type);
+	kmem_cache_destroy(jffs2_inode_cachep);
 }
 
 module_init(init_jffs2_fs);
diff -Nur linux-2.4.30/fs/jffs2/super-v24.c linux-2.4.30-linux-mips.org/fs/jffs2/super-v24.c
--- linux-2.4.30/fs/jffs2/super-v24.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/super-v24.c	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,167 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+ * 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 $
+ *
+ */
+
+#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/jffs2.h>
+#include <linux/pagemap.h>
+#include <linux/mtd/mtd.h>
+#include "nodelist.h"
+
+#ifndef MTD_BLOCK_MAJOR
+#define MTD_BLOCK_MAJOR 31
+#endif
+
+static void jffs2_put_super (struct super_block *);
+
+static struct super_operations jffs2_super_operations =
+{
+	.read_inode =	jffs2_read_inode,
+	.put_super =	jffs2_put_super,
+	.write_super =	jffs2_write_super,
+	.statfs =	jffs2_statfs,
+	.remount_fs =	jffs2_remount_fs,
+	.clear_inode =	jffs2_clear_inode,
+	.dirty_inode =	jffs2_dirty_inode,
+};
+
+
+static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent)
+{
+	struct jffs2_sb_info *c;
+	int ret;
+
+	D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev)));
+
+	if (major(sb->s_dev) != MTD_BLOCK_MAJOR) {
+		if (!silent)
+			printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev));
+		return NULL;
+	}
+
+	c = JFFS2_SB_INFO(sb);
+	memset(c, 0, sizeof(*c));
+
+	sb->s_op = &jffs2_super_operations;
+
+	c->mtd = get_mtd_device(NULL, minor(sb->s_dev));
+	if (!c->mtd) {
+		D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", minor(sb->s_dev)));
+		return NULL;
+	}
+
+	ret = jffs2_do_fill_super(sb, data, silent);
+	if (ret) {
+		put_mtd_device(c->mtd);
+		return NULL;
+	}
+
+	return sb;
+}
+
+static void jffs2_put_super (struct super_block *sb)
+{
+	struct jffs2_sb_info *c = JFFS2_SB_INFO(sb);
+
+	D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n"));
+
+
+	if (!(sb->s_flags & MS_RDONLY))
+		jffs2_stop_garbage_collect_thread(c);
+	down(&c->alloc_sem);
+	jffs2_flush_wbuf_pad(c);
+	up(&c->alloc_sem);
+	jffs2_free_ino_caches(c);
+	jffs2_free_raw_node_refs(c);
+	kfree(c->blocks);
+	jffs2_flash_cleanup(c);
+	kfree(c->inocache_list);
+	if (c->mtd->sync)
+		c->mtd->sync(c->mtd);
+	put_mtd_device(c->mtd);
+	
+	D1(printk(KERN_DEBUG "jffs2_put_super returning\n"));
+}
+
+static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super);
+
+static int __init init_jffs2_fs(void)
+{
+	int ret;
+
+	printk(KERN_INFO "JFFS2 version 2.2."
+#ifdef CONFIG_JFFS2_FS_NAND
+	       " (NAND)"
+#endif
+	       " (C) 2001-2003 Red Hat, Inc.\n");
+
+#ifdef JFFS2_OUT_OF_KERNEL
+	/* sanity checks. Could we do these at compile time? */
+	if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) {
+		printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n", 
+		       sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u));
+		return -EIO;
+	}
+
+	if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) {
+		printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n", 
+		       sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u));
+		return -EIO;
+	}
+#endif
+	ret = jffs2_zlib_init();
+	if (ret) {
+		printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
+		goto out;
+	}
+	ret = jffs2_create_slab_caches();
+	if (ret) {
+		printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
+		goto out_zlib;
+	}
+	ret = register_filesystem(&jffs2_fs_type);
+	if (ret) {
+		printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n");
+		goto out_slab;
+	}
+	return 0;
+
+ out_slab:
+	jffs2_destroy_slab_caches();
+ out_zlib:
+	jffs2_zlib_exit();
+ out:
+
+	return ret;
+}
+
+static void __exit exit_jffs2_fs(void)
+{
+	jffs2_destroy_slab_caches();
+	jffs2_zlib_exit();
+	unregister_filesystem(&jffs2_fs_type);
+}
+
+module_init(init_jffs2_fs);
+module_exit(exit_jffs2_fs);
+
+MODULE_DESCRIPTION("The Journalling Flash File System, v2");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for 
+		       // the sake of this tag. It's Free Software.
diff -Nur linux-2.4.30/fs/jffs2/symlink.c linux-2.4.30-linux-mips.org/fs/jffs2/symlink.c
--- linux-2.4.30/fs/jffs2/symlink.c	2002-08-03 02:39:45.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/jffs2/symlink.c	2005-05-12 18:01:30.000000000 +0200
@@ -3,35 +3,11 @@
  *
  * Copyright (C) 2001, 2002 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: symlink.c,v 1.5.2.1 2002/01/15 10:39:06 dwmw2 Exp $
+ * $Id: symlink.c,v 1.12 2003/10/04 08:33:07 dwmw2 Exp $
  *
  */
 
@@ -39,7 +15,6 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
-#include <linux/jffs2.h>
 #include "nodelist.h"
 
 int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
@@ -47,45 +22,17 @@
 
 struct inode_operations jffs2_symlink_inode_operations =
 {	
-	readlink:	jffs2_readlink,
-	follow_link:	jffs2_follow_link,
-	setattr:	jffs2_setattr
+	.readlink =	jffs2_readlink,
+	.follow_link =	jffs2_follow_link,
+	.setattr =	jffs2_setattr
 };
 
-static char *jffs2_getlink(struct dentry *dentry)
-{
-	struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode);
-	char *buf;
-	int ret;
-
-	down(&f->sem);
-	if (!f->metadata) {
-		up(&f->sem);
-		printk(KERN_NOTICE "No metadata for symlink inode #%lu\n", dentry->d_inode->i_ino);
-		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(JFFS2_SB_INFO(dentry->d_inode->i_sb), f->metadata, buf, 0, f->metadata->size);
-	up(&f->sem);
-	if (ret) {
-		kfree(buf);
-		return ERR_PTR(ret);
-	}
-	return buf;
-
-}
 int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen)
 {
 	unsigned char *kbuf;
 	int ret;
 
-	kbuf = jffs2_getlink(dentry);
+	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);
 
@@ -99,7 +46,7 @@
 	unsigned char *buf;
 	int ret;
 
-	buf = jffs2_getlink(dentry);
+	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);
diff -Nur linux-2.4.30/fs/jffs2/wbuf.c linux-2.4.30-linux-mips.org/fs/jffs2/wbuf.c
--- linux-2.4.30/fs/jffs2/wbuf.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/wbuf.c	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,1184 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001-2003 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ * $Id: wbuf.c,v 1.61 2004/04/14 19:01:51 gleixner Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/crc32.h>
+#include <linux/mtd/nand.h>
+#include "nodelist.h"
+
+/* For testing write failures */
+#undef BREAKME
+#undef BREAKMEHEADER
+
+#ifdef BREAKME
+static unsigned char *brokenbuf;
+#endif
+
+/* max. erase failures before we mark a block bad */
+#define MAX_ERASE_FAILURES 	5
+
+/* two seconds timeout for timed wbuf-flushing */
+#define WBUF_FLUSH_TIMEOUT	2 * HZ
+
+struct jffs2_inodirty {
+	uint32_t ino;
+	struct jffs2_inodirty *next;
+};
+
+static struct jffs2_inodirty inodirty_nomem;
+
+static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino)
+{
+	struct jffs2_inodirty *this = c->wbuf_inodes;
+
+	/* If a malloc failed, consider _everything_ dirty */
+	if (this == &inodirty_nomem)
+		return 1;
+
+	/* If ino == 0, _any_ non-GC writes mean 'yes' */
+	if (this && !ino)
+		return 1;
+
+	/* Look to see if the inode in question is pending in the wbuf */
+	while (this) {
+		if (this->ino == ino)
+			return 1;
+		this = this->next;
+	}
+	return 0;
+}
+
+static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c)
+{
+	struct jffs2_inodirty *this;
+
+	this = c->wbuf_inodes;
+
+	if (this != &inodirty_nomem) {
+		while (this) {
+			struct jffs2_inodirty *next = this->next;
+			kfree(this);
+			this = next;
+		}
+	}
+	c->wbuf_inodes = NULL;
+}
+
+static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino)
+{
+	struct jffs2_inodirty *new;
+
+	/* Mark the superblock dirty so that kupdated will flush... */
+	OFNI_BS_2SFFJ(c)->s_dirt = 1;
+
+	if (jffs2_wbuf_pending_for_ino(c, ino))
+		return;
+
+	new = kmalloc(sizeof(*new), GFP_KERNEL);
+	if (!new) {
+		D1(printk(KERN_DEBUG "No memory to allocate inodirty. Fallback to all considered dirty\n"));
+		jffs2_clear_wbuf_ino_list(c);
+		c->wbuf_inodes = &inodirty_nomem;
+		return;
+	}
+	new->ino = ino;
+	new->next = c->wbuf_inodes;
+	c->wbuf_inodes = new;
+	return;
+}
+
+static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c)
+{
+	struct list_head *this, *next;
+	static int n;
+
+	if (list_empty(&c->erasable_pending_wbuf_list))
+		return;
+
+	list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) {
+		struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list);
+
+		D1(printk(KERN_DEBUG "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", jeb->offset));
+		list_del(this);
+		if ((jiffies + (n++)) & 127) {
+			/* Most of the time, we just erase it immediately. Otherwise we
+			   spend ages scanning it on mount, etc. */
+			D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n"));
+			list_add_tail(&jeb->list, &c->erase_pending_list);
+			c->nr_erasing_blocks++;
+			jffs2_erase_pending_trigger(c);
+		} else {
+			/* Sometimes, however, we leave it elsewhere so it doesn't get
+			   immediately reused, and we spread the load a bit. */
+			D1(printk(KERN_DEBUG "...and adding to erasable_list\n"));
+			list_add_tail(&jeb->list, &c->erasable_list);
+		}
+	}
+}
+
+/* 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];
+
+	D1(printk("About to refile bad block at %08x\n", jeb->offset));
+
+	D2(jffs2_dump_block_lists(c));
+	/* File the existing block on the bad_used_list.... */
+	if (c->nextblock == jeb)
+		c->nextblock = NULL;
+	else /* Not sure this should ever happen... need more coffee */
+		list_del(&jeb->list);
+	if (jeb->first_node) {
+		D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset));
+		list_add(&jeb->list, &c->bad_used_list);
+	} else {
+		BUG();
+		/* 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);
+		c->nr_erasing_blocks++;
+		jffs2_erase_pending_trigger(c);
+	}
+	D2(jffs2_dump_block_lists(c));
+
+	/* Adjust its size counts accordingly */
+	c->wasted_size += jeb->free_size;
+	c->free_size -= jeb->free_size;
+	jeb->wasted_size += jeb->free_size;
+	jeb->free_size = 0;
+
+	ACCT_SANITY_CHECK(c,jeb);
+	D1(ACCT_PARANOIA_CHECK(jeb));
+
+	/* Find the first node to be recovered, by skipping over every
+	   node which ends before the wbuf starts, or which is obsolete. */
+	first_raw = &jeb->first_node;
+	while (*first_raw && 
+	       (ref_obsolete(*first_raw) ||
+		(ref_offset(*first_raw)+ref_totlen(c, jeb, *first_raw)) < c->wbuf_ofs)) {
+		D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n",
+			  ref_offset(*first_raw), ref_flags(*first_raw),
+			  (ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw)),
+			  c->wbuf_ofs));
+		first_raw = &(*first_raw)->next_phys;
+	}
+
+	if (!*first_raw) {
+		/* All nodes were obsolete. Nothing to recover. */
+		D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n"));
+		spin_unlock(&c->erase_completion_lock);
+		return;
+	}
+
+	start = ref_offset(*first_raw);
+	end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw);
+
+	/* Find the last node to be recovered */
+	raw = first_raw;
+	while ((*raw)) {
+		if (!ref_obsolete(*raw))
+			end = ref_offset(*raw) + ref_totlen(c, jeb, *raw);
+
+		raw = &(*raw)->next_phys;
+	}
+	spin_unlock(&c->erase_completion_lock);
+
+	D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end));
+
+	buf = NULL;
+	if (start < c->wbuf_ofs) {
+		/* First affected node was already partially written.
+		 * Attempt to reread the old data into our buffer. */
+
+		buf = kmalloc(end - start, GFP_KERNEL);
+		if (!buf) {
+			printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n");
+
+			goto read_failed;
+		}
+
+		/* 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) {
+			/* ECC recovered */
+			ret = 0;
+		}
+		if (ret || retlen != c->wbuf_ofs - start) {
+			printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n");
+
+			kfree(buf);
+			buf = NULL;
+		read_failed:
+			first_raw = &(*first_raw)->next_phys;
+			/* If this was the only node to be recovered, give up */
+			if (!(*first_raw))
+				return;
+
+			/* It wasn't. Go on and try to recover nodes complete in the wbuf */
+			start = ref_offset(*first_raw);
+		} else {
+			/* Read succeeded. Copy the remaining data from the wbuf */
+			memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs);
+		}
+	}
+	/* OK... we're to rewrite (end-start) bytes of data from first_raw onwards.
+	   Either 'buf' contains the data, or we find it in the wbuf */
+
+
+	/* ... and get an allocation of space from a shiny new block instead */
+	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);
+		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? :) */
+
+		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",
+			  towrite, ofs));
+	  
+#ifdef BREAKMEHEADER
+		static int breakme;
+		if (breakme++ == 20) {
+			printk(KERN_NOTICE "Faking write error at 0x%08x\n", ofs);
+			breakme = 0;
+			c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
+					  brokenbuf, NULL, c->oobinfo);
+			ret = -EIO;
+		} else
+#endif
+			ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen,
+						buf, NULL, c->oobinfo);
+
+		if (ret || retlen != towrite) {
+			/* Argh. We tried. Really we did. */
+			printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n");
+			kfree(buf);
+
+			if (retlen) {
+				struct jffs2_raw_node_ref *raw2;
+
+				raw2 = jffs2_alloc_raw_node_ref();
+				if (!raw2)
+					return;
+
+				raw2->flash_offset = ofs | REF_OBSOLETE;
+				raw2->__totlen = ref_totlen(c, jeb, *first_raw);
+				raw2->next_phys = NULL;
+				raw2->next_in_ino = NULL;
+
+				jffs2_add_physical_node_ref(c, raw2);
+			}
+			return;
+		}
+		printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs);
+
+		c->wbuf_len = (end - start) - towrite;
+		c->wbuf_ofs = ofs + towrite;
+		memcpy(c->wbuf, buf + towrite, c->wbuf_len);
+		/* Don't muck about with c->wbuf_inodes. False positives are harmless. */
+
+		kfree(buf);
+	} else {
+		/* OK, now we're left with the dregs in whichever buffer we're using */
+		if (buf) {
+			memcpy(c->wbuf, buf, end-start);
+			kfree(buf);
+		} else {
+			memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start);
+		}
+		c->wbuf_ofs = ofs;
+		c->wbuf_len = end - start;
+	}
+
+	/* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */
+	new_jeb = &c->blocks[ofs / c->sector_size];
+
+	spin_lock(&c->erase_completion_lock);
+	if (new_jeb->first_node) {
+		/* Odd, but possible with ST flash later maybe */
+		new_jeb->last_node->next_phys = *first_raw;
+	} else {
+		new_jeb->first_node = *first_raw;
+	}
+
+	raw = first_raw;
+	while (*raw) {
+		uint32_t rawlen = ref_totlen(c, jeb, *raw);
+
+		D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n",
+			  rawlen, ref_offset(*raw), ref_flags(*raw), ofs));
+
+		if (ref_obsolete(*raw)) {
+			/* Shouldn't really happen much */
+			new_jeb->dirty_size += rawlen;
+			new_jeb->free_size -= rawlen;
+			c->dirty_size += rawlen;
+		} else {
+			new_jeb->used_size += rawlen;
+			new_jeb->free_size -= rawlen;
+			jeb->dirty_size += rawlen;
+			jeb->used_size  -= rawlen;
+			c->dirty_size += rawlen;
+		}
+		c->free_size -= rawlen;
+		(*raw)->flash_offset = ofs | ref_flags(*raw);
+		ofs += rawlen;
+		new_jeb->last_node = *raw;
+
+		raw = &(*raw)->next_phys;
+	}
+
+	/* Fix up the original jeb now it's on the bad_list */
+	*first_raw = NULL;
+	if (first_raw == &jeb->first_node) {
+		jeb->last_node = NULL;
+		D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset));
+		list_del(&jeb->list);
+		list_add(&jeb->list, &c->erase_pending_list);
+		c->nr_erasing_blocks++;
+		jffs2_erase_pending_trigger(c);
+	}
+	else
+		jeb->last_node = container_of(first_raw, struct jffs2_raw_node_ref, next_phys);
+
+	ACCT_SANITY_CHECK(c,jeb);
+        D1(ACCT_PARANOIA_CHECK(jeb));
+
+	ACCT_SANITY_CHECK(c,new_jeb);
+        D1(ACCT_PARANOIA_CHECK(new_jeb));
+
+	spin_unlock(&c->erase_completion_lock);
+
+	D1(printk(KERN_DEBUG "wbuf recovery completed OK\n"));
+}
+
+/* Meaning of pad argument:
+   0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway.
+   1: Pad, do not adjust nextblock free_size
+   2: Pad, adjust nextblock free_size
+*/
+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
+	   del_timer() the timer we never initialised. */
+	if (jffs2_can_mark_obsolete(c))
+		return 0;
+
+	if (!down_trylock(&c->alloc_sem)) {
+		up(&c->alloc_sem);
+		printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n");
+		BUG();
+	}
+
+	if(!c->wbuf || !c->wbuf_len)
+		return 0;
+
+	/* claim remaining space on the page
+	   this happens, if we have a change to a new block,
+	   or if fsync forces us to flush the writebuffer.
+	   if we have a switch to next page, we will not have
+	   enough remaining space for this. 
+	*/
+	if (pad) {
+		c->wbuf_len = PAD(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);
+			padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+			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
+	   buffer for us, and will deal with the node refs etc. later. */
+	
+#ifdef BREAKME
+	static int breakme;
+	if (breakme++ == 20) {
+		printk(KERN_NOTICE "Faking write error at 0x%08x\n", c->wbuf_ofs);
+		breakme = 0;
+		c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize,
+					&retlen, brokenbuf, NULL, c->oobinfo);
+		ret = -EIO;
+	} else 
+#endif
+	ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo);
+
+
+	if (ret || retlen != c->wbuf_pagesize) {
+		if (ret)
+			printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret);
+		else {
+			printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n",
+				retlen, c->wbuf_pagesize);
+			ret = -EIO;
+		}
+
+		jffs2_wbuf_recover(c);
+
+		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();
+		/* 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)) {
+			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);
+			BUG();
+		}
+		c->nextblock->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);
+		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);
+
+	memset(c->wbuf,0xff,c->wbuf_pagesize);
+	/* adjust write buffer offset, else we get a non contiguous write bug */
+	c->wbuf_ofs += c->wbuf_pagesize;
+	c->wbuf_len = 0;
+	return 0;
+}
+
+/* Trigger garbage collection to flush the write-buffer. 
+   If ino arg is zero, do it if _any_ real (i.e. not GC) writes are
+   outstanding. If ino arg non-zero, do it only if a write for the 
+   given inode is outstanding. */
+int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino)
+{
+	uint32_t old_wbuf_ofs;
+	uint32_t old_wbuf_len;
+	int ret = 0;
+
+	D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino));
+
+	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));
+		up(&c->alloc_sem);
+		return 0;
+	}
+
+	old_wbuf_ofs = c->wbuf_ofs;
+	old_wbuf_len = c->wbuf_len;
+
+	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);
+	} else while (old_wbuf_len &&
+		      old_wbuf_ofs == c->wbuf_ofs) {
+
+		up(&c->alloc_sem);
+
+		D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n"));
+
+		ret = jffs2_garbage_collect_pass(c);
+		if (ret) {
+			/* GC failed. Flush it with padding instead */
+			down(&c->alloc_sem);
+			ret = __jffs2_flush_wbuf(c, 2);
+			break;
+		}
+		down(&c->alloc_sem);
+	}
+
+	D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n"));
+
+	up(&c->alloc_sem);
+	return ret;
+}
+
+/* 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);
+}
+
+
+#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)
+{
+	struct iovec outvecs[3];
+	uint32_t totlen = 0;
+	uint32_t split_ofs = 0;
+	uint32_t old_totlen;
+	int ret, splitvec = -1;
+	int invec, outvec;
+	size_t wbuf_retlen;
+	unsigned char *wbuf_ptr;
+	size_t donelen = 0;
+	uint32_t outvec_to = to;
+
+	/* If not NAND flash, don't bother */
+	if (!c->wbuf)
+		return jffs2_flash_direct_writev(c, invecs, count, to, retlen);
+	
+	/* If wbuf_ofs is not initialized, set it to target address */
+	if (c->wbuf_ofs == 0xFFFFFFFF) {
+		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)) ) {
+		/* 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);
+			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;
+			}
+		}
+		/* set pointer to new block */
+		c->wbuf_ofs = PAGE_DIV(to);
+		c->wbuf_len = PAGE_MOD(to);			
+	} 
+
+	if (to != PAD(c->wbuf_ofs + c->wbuf_len)) {
+		/* We're not writing immediately after the writebuffer. Bad. */
+		printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write to %08lx\n", (unsigned long)to);
+		if (c->wbuf_len)
+			printk(KERN_CRIT "wbuf was previously %08x-%08x\n",
+					  c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len);
+		BUG();
+	}
+
+	/* Note outvecs[3] above. We know count is never greater than 2 */
+	if (count > 2) {
+		printk(KERN_CRIT "jffs2_flash_writev(): count is %ld\n", count);
+		BUG();
+	}
+
+	invec = 0;
+	outvec = 0;
+
+
+	/* Fill writebuffer first, if already in use */	
+	if (c->wbuf_len) {
+		uint32_t invec_ofs = 0;
+
+		/* adjust alignment offset */ 
+		if (c->wbuf_len != PAGE_MOD(to)) {
+			c->wbuf_len = PAGE_MOD(to);
+			/* take care of alignment to next page */
+			if (!c->wbuf_len)
+				c->wbuf_len = c->wbuf_pagesize;
+		}
+		
+		while(c->wbuf_len < c->wbuf_pagesize) {
+			uint32_t thislen;
+			
+			if (invec == count)
+				goto alldone;
+
+			thislen = c->wbuf_pagesize - c->wbuf_len;
+
+			if (thislen >= invecs[invec].iov_len)
+				thislen = invecs[invec].iov_len;
+	
+			invec_ofs = thislen;
+
+			memcpy(c->wbuf + c->wbuf_len, invecs[invec].iov_base, thislen);
+			c->wbuf_len += thislen;
+			donelen += thislen;
+			/* Get next invec, if actual did not fill the buffer */
+			if (c->wbuf_len < c->wbuf_pagesize) 
+				invec++;
+		}			
+		
+		/* write buffer is full, flush buffer */
+		ret = __jffs2_flush_wbuf(c, 0);
+		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;
+		}
+		outvec_to += donelen;
+		c->wbuf_ofs = outvec_to;
+
+		/* All invecs done ? */
+		if (invec == count)
+			goto alldone;
+
+		/* Set up the first outvec, containing the remainder of the
+		   invec we partially used */
+		if (invecs[invec].iov_len > invec_ofs) {
+			outvecs[0].iov_base = invecs[invec].iov_base+invec_ofs;
+			totlen = outvecs[0].iov_len = invecs[invec].iov_len-invec_ofs;
+			if (totlen > c->wbuf_pagesize) {
+				splitvec = outvec;
+				split_ofs = outvecs[0].iov_len - PAGE_MOD(totlen);
+			}
+			outvec++;
+		}
+		invec++;
+	}
+
+	/* OK, now we've flushed the wbuf and the start of the bits
+	   we have been asked to write, now to write the rest.... */
+
+	/* totlen holds the amount of data still to be written */
+	old_totlen = totlen;
+	for ( ; invec < count; invec++,outvec++ ) {
+		outvecs[outvec].iov_base = invecs[invec].iov_base;
+		totlen += outvecs[outvec].iov_len = invecs[invec].iov_len;
+		if (PAGE_DIV(totlen) != PAGE_DIV(old_totlen)) {
+			splitvec = outvec;
+			split_ofs = outvecs[outvec].iov_len - PAGE_MOD(totlen);
+			old_totlen = totlen;
+		}
+	}
+
+	/* Now the outvecs array holds all the remaining data to write */
+	/* Up to splitvec,split_ofs is to be written immediately. The rest
+	   goes into the (now-empty) wbuf */
+
+	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 (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) {
+			/* At this point we have no problem,
+			   c->wbuf is empty. 
+			*/
+			*retlen = donelen;
+			return ret;
+		}
+		
+		donelen += wbuf_retlen;
+		c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen);
+
+		if (remainder) {
+			outvecs[splitvec].iov_base += split_ofs;
+			outvecs[splitvec].iov_len = remainder;
+		} else {
+			splitvec++;
+		}
+
+	} else {
+		splitvec = 0;
+	}
+
+	/* Now splitvec points to the start of the bits we have to copy
+	   into the wbuf */
+	wbuf_ptr = c->wbuf;
+
+	for ( ; splitvec < outvec; splitvec++) {
+		/* Don't copy the wbuf into itself */
+		if (outvecs[splitvec].iov_base == c->wbuf)
+			continue;
+		memcpy(wbuf_ptr, outvecs[splitvec].iov_base, outvecs[splitvec].iov_len);
+		wbuf_ptr += outvecs[splitvec].iov_len;
+		donelen += outvecs[splitvec].iov_len;
+	}
+	c->wbuf_len = wbuf_ptr - c->wbuf;
+
+	/* If there's a remainder in the wbuf and it's a non-GC write,
+	   remember that the wbuf affects this ino */
+alldone:
+	*retlen = donelen;
+
+	if (c->wbuf_len && ino)
+		jffs2_wbuf_dirties_inode(c, ino);
+
+	return 0;
+}
+
+/*
+ *	This is the entry for flash write.
+ *	Check, if we work on NAND FLASH, if so build an iovec 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];
+
+	if (jffs2_can_mark_obsolete(c))
+		return c->mtd->write(c->mtd, ofs, len, retlen, buf);
+
+	vecs[0].iov_base = (unsigned char *) buf;
+	vecs[0].iov_len = len;
+	return jffs2_flash_writev(c, vecs, 1, ofs, retlen, 0);
+}
+
+/*
+	Handle readback from writebuffer and ECC failure return
+*/
+int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf)
+{
+	loff_t	orbf = 0, owbf = 0, lwbf = 0;
+	int	ret;
+
+	/* Read flash */
+	if (!jffs2_can_mark_obsolete(c)) {
+		ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo);
+
+		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 no writebuffer available or write buffer empty, return */
+	if (!c->wbuf_pagesize || !c->wbuf_len)
+		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 (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;
+		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;
+		lwbf = len - orbf; 		/* number of bytes to copy */
+		if (lwbf > c->wbuf_len)	
+			lwbf = c->wbuf_len;
+	}	
+	if (lwbf > 0)
+		memcpy(buf+orbf,c->wbuf+owbf,lwbf);
+
+	return ret;
+}
+
+/*
+ *	Check, if the out of band area is empty
+ */
+int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode)
+{
+	unsigned char *buf;
+	int 	ret = 0;
+	int	i,len,page;
+	size_t  retlen;
+	int	oob_size;
+
+	oob_size = c->mtd->oobsize;
+
+	/* allocate a buffer for all oob data in this sector */
+	len = 4 * oob_size;
+	buf = kmalloc(len, GFP_KERNEL);
+	if (!buf) {
+		printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n");
+		return -ENOMEM;
+	}
+	/* 
+	 * if mode = 0, we scan for a total empty oob area, else we have
+	 * to take care of the cleanmarker in the first page of the block
+	*/
+	ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf);
+	if (ret) {
+		D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
+		goto out;
+	}
+	
+	if (retlen < len) {
+		D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read "
+			  "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset));
+		ret = -EIO;
+		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;
+			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)) {
+		unsigned long dat = *(unsigned long *)(&buf[page]);
+		if(dat != -1) {
+			ret = 1; 
+			goto out;
+		}
+	}
+
+out:
+	kfree(buf);	
+	
+	return ret;
+}
+
+/*
+*	Scan for a valid cleanmarker and for bad blocks
+*	For virtual blocks (concatenated physical blocks) check the cleanmarker
+*	only in the first page of the first physical block, but scan for bad blocks in all
+*	physical blocks
+*/
+int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	struct jffs2_unknown_node n;
+	unsigned char buf[2 * NAND_MAX_OOBSIZE];
+	unsigned char *p;
+	int ret, i, cnt, retval = 0;
+	size_t retlen, offset;
+	int oob_size;
+
+	offset = jeb->offset;
+	oob_size = c->mtd->oobsize;
+
+	/* Loop through the physical blocks */
+	for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) {
+		/*
+		   *    We read oob data from page 0 and 1 of the block.
+		   *    page 0 contains cleanmarker and badblock info
+		   *    page 1 contains failure count of this block
+		 */
+		ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf);
+
+		if (ret) {
+			D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset));
+			return ret;
+		}
+		if (retlen < (oob_size << 1)) {
+			D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset));
+			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);
+			n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
+			n.totlen = cpu_to_je32 (8);
+			p = (unsigned char *) &n;
+
+			for (i = 0; i < c->fsdata_len; i++) {
+				if (buf[c->fsdata_pos + i] != p[i]) {
+					retval = 1;
+				}
+			}
+			D1(if (retval == 1) {
+				printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset);
+				printk(KERN_WARNING "OOB at %08x was ", offset);
+				for (i=0; i < oob_size; i++) {
+					printk("%02x ", buf[i]);
+				}
+				printk("\n");
+			})
+		}
+		offset += c->mtd->erasesize;
+	}
+	return retval;
+}
+
+int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
+{
+	struct 	jffs2_unknown_node n;
+	int 	ret;
+	size_t 	retlen;
+
+	n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER);
+	n.totlen = cpu_to_je32(8);
+
+	ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n);
+	
+	if (ret) {
+		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret));
+		return ret;
+	}
+	if (retlen != c->fsdata_len) {
+		D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len));
+		return ret;
+	}
+	return 0;
+}
+
+/* 
+ * 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.
+ * 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)
+{
+	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 (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;
+}
+
+#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,
+	.eccbytes = 6,
+	.eccpos = {0,1,2,3,4,5}
+};
+
+
+
+int jffs2_nand_flash_setup(struct jffs2_sb_info *c)
+{
+	/* 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;
+			c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN;
+			c->oobinfo = &jffs2_oobinfo_swecc_16;
+			c->badblock_pos = 5;
+			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;
+	}
+
+	c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
+	if (!c->wbuf)
+		return -ENOMEM;
+
+#ifdef BREAKME
+	if (!brokenbuf)
+		brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL);
+	if (!brokenbuf) {
+		kfree(c->wbuf);
+		return -ENOMEM;
+	}
+	memset(brokenbuf, 0xdb, c->wbuf_pagesize);
+#endif
+	return 0;
+}
+
+void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c)
+{
+	kfree(c->wbuf);
+}
diff -Nur linux-2.4.30/fs/jffs2/write.c linux-2.4.30-linux-mips.org/fs/jffs2/write.c
--- linux-2.4.30/fs/jffs2/write.c	2003-11-28 19:26:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/write.c	2005-05-12 18:01:30.000000000 +0200
@@ -1,154 +1,69 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: write.c,v 1.30.2.2 2003/11/02 13:51:18 dwmw2 Exp $
+ * $Id: write.c,v 1.83 2004/03/30 09:36:09 dwmw2 Exp $
  *
  */
 
 #include <linux/kernel.h>
 #include <linux/fs.h>
-#include <linux/jffs2.h>
+#include <linux/crc32.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
 #include <linux/mtd/mtd.h>
 #include "nodelist.h"
-#include <linux/crc32.h>
 
-/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash,
-   fill in the raw_inode while you're at it. */
-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri)
+
+int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri)
 {
-	struct inode *inode;
-	struct super_block *sb = dir_i->i_sb;
 	struct jffs2_inode_cache *ic;
-	struct jffs2_sb_info *c;
-	struct jffs2_inode_info *f;
-
-	D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode));
-
-	c = JFFS2_SB_INFO(sb);
-	memset(ri, 0, sizeof(*ri));
 
 	ic = jffs2_alloc_inode_cache();
 	if (!ic) {
-		return ERR_PTR(-ENOMEM);
-	}
-	memset(ic, 0, sizeof(*ic));
-	
-	inode = new_inode(sb);
-	
-	if (!inode) {
-		jffs2_free_inode_cache(ic);
-		return ERR_PTR(-ENOMEM);
+		return -ENOMEM;
 	}
 
-	/* Alloc jffs2_inode_info when that's split in 2.5 */
+	memset(ic, 0, sizeof(*ic));
 
-	f = JFFS2_INODE_INFO(inode);
-	memset(f, 0, sizeof(*f));
-	init_MUTEX_LOCKED(&f->sem);
 	f->inocache = ic;
-	inode->i_nlink = f->inocache->nlink = 1;
+	f->inocache->nlink = 1;
 	f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache;
-	f->inocache->ino = ri->ino = inode->i_ino = ++c->highest_ino;
-	D1(printk(KERN_DEBUG "jffs2_new_inode(): Assigned ino# %d\n", ri->ino));
-	jffs2_add_ino_cache(c, f->inocache);
-
-	ri->magic = JFFS2_MAGIC_BITMASK;
-	ri->nodetype = JFFS2_NODETYPE_INODE;
-	ri->totlen = PAD(sizeof(*ri));
-	ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4);
-	ri->mode = mode;
-	f->highest_version = ri->version = 1;
-	ri->uid = current->fsuid;
-	if (dir_i->i_mode & S_ISGID) {
-		ri->gid = dir_i->i_gid;
-		if (S_ISDIR(mode))
-			ri->mode |= S_ISGID;
-	} else {
-		ri->gid = current->fsgid;
-	}
-	inode->i_mode = ri->mode;
-	inode->i_gid = ri->gid;
-	inode->i_uid = ri->uid;
-	inode->i_atime = inode->i_ctime = inode->i_mtime = 
-		ri->atime = ri->mtime = ri->ctime = CURRENT_TIME;
-	inode->i_blksize = PAGE_SIZE;
-	inode->i_blocks = 0;
-	inode->i_size = 0;
-
-	insert_inode_hash(inode);
+	f->inocache->ino = ++c->highest_ino;
+	f->inocache->state = INO_STATE_PRESENT;
 
-	return inode;
-}
+	ri->ino = cpu_to_je32(f->inocache->ino);
 
-/* 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 int mtd_fake_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen)
-{
-	unsigned long i;
-	size_t totlen = 0, thislen;
-	int ret = 0;
+	D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
+	jffs2_add_ino_cache(c, f->inocache);
 
-	for (i=0; i<count; i++) {
-		ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base);
-		totlen += thislen;
-		if (ret || thislen != vecs[i].iov_len)
-			break;
-		to += vecs[i].iov_len;
-	}
-	if (retlen)
-		*retlen = totlen;
-	return ret;
-}
+	ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+	ri->totlen = cpu_to_je32(PAD(sizeof(*ri)));
+	ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
+	ri->mode = cpu_to_jemode(mode);
 
+	f->highest_version = 1;
+	ri->version = cpu_to_je32(f->highest_version);
 
-static inline int mtd_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen)
-{
-	if (mtd->writev)
-		return mtd->writev(mtd,vecs,count,to,retlen);
-	else
-		return mtd_fake_writev(mtd, vecs, count, to, retlen);
+	return 0;
 }
 
-static void writecheck(struct mtd_info *mtd, __u32 ofs)
+#if CONFIG_JFFS2_FS_DEBUG > 0
+static void writecheck(struct jffs2_sb_info *c, uint32_t ofs)
 {
 	unsigned char buf[16];
-	ssize_t retlen;
+	size_t retlen;
 	int ret, i;
 
-	ret = mtd->read(mtd, ofs, 16, &retlen, buf);
-	if (ret && retlen != 16) {
-		D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %d\n", ret, retlen));
+	ret = jffs2_flash_read(c, ofs, 16, &retlen, buf);
+	if (ret || (retlen != 16)) {
+		D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %zd\n", ret, retlen));
 		return;
 	}
 	ret = 0;
@@ -157,32 +72,31 @@
 			ret = 1;
 	}
 	if (ret) {
-		printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there's data already there:\n", ofs);
+		printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there are data already there:\n", ofs);
 		printk(KERN_WARNING "0x%08x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", 
 		       ofs,
 		       buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7],
 		       buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]);
 	}
 }
+#endif
 
-	
-	
 
 /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it, 
    write it to the flash, link it into the existing inode/fragment list */
 
-struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs,  __u32 *writelen)
+struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode)
 
 {
-	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
-	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 	struct jffs2_raw_node_ref *raw;
 	struct jffs2_full_dnode *fn;
-	ssize_t retlen;
+	size_t retlen;
 	struct iovec vecs[2];
 	int ret;
+	int retried = 0;
+	unsigned long cnt = 2;
 
-	D1(if(ri->hdr_crc != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
+	D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) {
 		printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n");
 		BUG();
 	}
@@ -192,10 +106,10 @@
 	vecs[1].iov_base = (unsigned char *)data;
 	vecs[1].iov_len = datalen;
 
-	writecheck(c->mtd, flash_ofs);
+	D1(writecheck(c, flash_ofs));
 
-	if (ri->totlen != sizeof(*ri) + datalen) {
-		printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08x) + datalen (0x%08x)\n", ri->totlen, sizeof(*ri), datalen);
+	if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) {
+		printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen);
 	}
 	raw = jffs2_alloc_raw_node_ref();
 	if (!raw)
@@ -206,19 +120,28 @@
 		jffs2_free_raw_node_ref(raw);
 		return ERR_PTR(-ENOMEM);
 	}
-	raw->flash_offset = flash_ofs;
-	raw->totlen = PAD(ri->totlen);
-	raw->next_phys = NULL;
 
-	fn->ofs = ri->offset;
-	fn->size = ri->dsize;
+	fn->ofs = je32_to_cpu(ri->offset);
+	fn->size = je32_to_cpu(ri->dsize);
 	fn->frags = 0;
+
+	/* check number of valid vecs */
+	if (!datalen || !data)
+		cnt = 1;
+ retry:
 	fn->raw = raw;
 
-	ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen);
+	raw->flash_offset = flash_ofs;
+	raw->__totlen = PAD(sizeof(*ri)+datalen);
+	raw->next_phys = NULL;
+
+	ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen,
+				 (alloc_mode==ALLOC_GC)?0:f->inocache->ino);
+
 	if (ret || (retlen != sizeof(*ri) + datalen)) {
-		printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", 
+		printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", 
 		       sizeof(*ri)+datalen, flash_ofs, ret, retlen);
+
 		/* Mark the space as dirtied */
 		if (retlen) {
 			/* Doesn't belong to any inode */
@@ -229,48 +152,96 @@
 			   seem corrupted, in which case the scan would skip over
 			   any node we write before the original intended end of 
 			   this node */
-			jffs2_add_physical_node_ref(c, raw, sizeof(*ri)+datalen, 1);
+			raw->flash_offset |= REF_OBSOLETE;
+			jffs2_add_physical_node_ref(c, raw);
 			jffs2_mark_node_obsolete(c, raw);
 		} else {
 			printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
 			jffs2_free_raw_node_ref(raw);
 		}
+		if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) {
+			/* Try to reallocate space and retry */
+			uint32_t dummy;
+			struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
+
+			retried = 1;
+
+			D1(printk(KERN_DEBUG "Retrying failed write.\n"));
+			
+			ACCT_SANITY_CHECK(c,jeb);
+			D1(ACCT_PARANOIA_CHECK(jeb));
+
+			if (alloc_mode == ALLOC_GC) {
+				ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy);
+			} else {
+				/* Locking pain */
+				up(&f->sem);
+				jffs2_complete_reservation(c);
+			
+				ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode);
+				down(&f->sem);
+			}
+
+			if (!ret) {
+				D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
+
+				ACCT_SANITY_CHECK(c,jeb);
+				D1(ACCT_PARANOIA_CHECK(jeb));
 
+				goto retry;
+			}
+			D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
+			jffs2_free_raw_node_ref(raw);
+		}
 		/* Release the full_dnode which is now useless, and return */
 		jffs2_free_full_dnode(fn);
-		if (writelen)
-			*writelen = retlen;
 		return ERR_PTR(ret?ret:-EIO);
 	}
 	/* Mark the space used */
-	jffs2_add_physical_node_ref(c, raw, retlen, 0);
+	/* If node covers at least a whole page, or if it starts at the 
+	   beginning of a page and runs to the end of the file, or if 
+	   it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. 
+	*/
+	if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) ||
+	    ( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) &&
+	      (je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) ==  je32_to_cpu(ri->isize)))) {
+		raw->flash_offset |= REF_PRISTINE;
+	} else {
+		raw->flash_offset |= REF_NORMAL;
+	}
+	jffs2_add_physical_node_ref(c, raw);
 
 	/* Link into per-inode list */
 	raw->next_in_ino = f->inocache->nodes;
 	f->inocache->nodes = raw;
 
-	D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ri->dsize, ri->csize, ri->node_crc, ri->data_crc, ri->totlen));
-	if (writelen)
-		*writelen = retlen;
+	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), 
+		  je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc),
+		  je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen)));
+
+	if (retried) {
+		ACCT_SANITY_CHECK(c,NULL);
+	}
 
-	f->inocache->nodes = raw;
 	return fn;
 }
 
-struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs,  __u32 *writelen)
+struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode)
 {
-	struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb);
-	struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode);
 	struct jffs2_raw_node_ref *raw;
 	struct jffs2_full_dirent *fd;
-	ssize_t retlen;
+	size_t retlen;
 	struct iovec vecs[2];
+	int retried = 0;
 	int ret;
 
-	D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", rd->pino, name, name, rd->ino, rd->name_crc));
-	writecheck(c->mtd, flash_ofs);
+	D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", 
+		  je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino),
+		  je32_to_cpu(rd->name_crc)));
+	D1(writecheck(c, flash_ofs));
 
-	D1(if(rd->hdr_crc != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
+	D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) {
 		printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n");
 		BUG();
 	}
@@ -291,44 +262,441 @@
 		jffs2_free_raw_node_ref(raw);
 		return ERR_PTR(-ENOMEM);
 	}
-	raw->flash_offset = flash_ofs;
-	raw->totlen = PAD(rd->totlen);
-	raw->next_in_ino = f->inocache->nodes;
-	f->inocache->nodes = raw;
-	raw->next_phys = NULL;
 
-	fd->version = rd->version;
-	fd->ino = rd->ino;
+	fd->version = je32_to_cpu(rd->version);
+	fd->ino = je32_to_cpu(rd->ino);
 	fd->nhash = full_name_hash(name, strlen(name));
 	fd->type = rd->type;
 	memcpy(fd->name, name, namelen);
 	fd->name[namelen]=0;
+
+ retry:
 	fd->raw = raw;
 
-	ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen);
-		if (ret || (retlen != sizeof(*rd) + namelen)) {
-			printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", 
+	raw->flash_offset = flash_ofs;
+	raw->__totlen = PAD(sizeof(*rd)+namelen);
+	raw->next_phys = NULL;
+
+	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)) {
+		printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", 
 			       sizeof(*rd)+namelen, flash_ofs, ret, retlen);
 		/* Mark the space as dirtied */
-			if (retlen) {
-				jffs2_add_physical_node_ref(c, raw, sizeof(*rd)+namelen, 1);
-				jffs2_mark_node_obsolete(c, raw);
+		if (retlen) {
+			raw->next_in_ino = NULL;
+			raw->flash_offset |= REF_OBSOLETE;
+			jffs2_add_physical_node_ref(c, raw);
+			jffs2_mark_node_obsolete(c, raw);
+		} else {
+			printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
+			jffs2_free_raw_node_ref(raw);
+		}
+		if (!retried && (raw = jffs2_alloc_raw_node_ref())) {
+			/* Try to reallocate space and retry */
+			uint32_t dummy;
+			struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size];
+
+			retried = 1;
+
+			D1(printk(KERN_DEBUG "Retrying failed write.\n"));
+
+			ACCT_SANITY_CHECK(c,jeb);
+			D1(ACCT_PARANOIA_CHECK(jeb));
+
+			if (alloc_mode == ALLOC_GC) {
+				ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy);
 			} else {
-				printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset);
-				jffs2_free_raw_node_ref(raw);
+				/* Locking pain */
+				up(&f->sem);
+				jffs2_complete_reservation(c);
+			
+				ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode);
+				down(&f->sem);
 			}
 
+			if (!ret) {
+				D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs));
+				ACCT_SANITY_CHECK(c,jeb);
+				D1(ACCT_PARANOIA_CHECK(jeb));
+				goto retry;
+			}
+			D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret));
+			jffs2_free_raw_node_ref(raw);
+		}
 		/* Release the full_dnode which is now useless, and return */
 		jffs2_free_full_dirent(fd);
-		if (writelen)
-			*writelen = retlen;
 		return ERR_PTR(ret?ret:-EIO);
 	}
 	/* Mark the space used */
-	jffs2_add_physical_node_ref(c, raw, retlen, 0);
-	if (writelen)
-		*writelen = retlen;
+	raw->flash_offset |= REF_PRISTINE;
+	jffs2_add_physical_node_ref(c, raw);
 
+	raw->next_in_ino = f->inocache->nodes;
 	f->inocache->nodes = raw;
+
+	if (retried) {
+		ACCT_SANITY_CHECK(c,NULL);
+	}
+
 	return fd;
 }
+
+/* The OS-specific code fills in the metadata in the jffs2_raw_inode for us, so that
+   we don't have to go digging in struct inode or its equivalent. It should set:
+   mode, uid, gid, (starting)isize, atime, ctime, mtime */
+int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+			    struct jffs2_raw_inode *ri, unsigned char *buf, 
+			    uint32_t offset, uint32_t writelen, uint32_t *retlen)
+{
+	int ret = 0;
+	uint32_t writtenlen = 0;
+
+       	D1(printk(KERN_DEBUG "jffs2_write_inode_range(): Ino #%u, ofs 0x%x, len 0x%x\n",
+		  f->inocache->ino, offset, writelen));
+		
+	while(writelen) {
+		struct jffs2_full_dnode *fn;
+		unsigned char *comprbuf = NULL;
+		unsigned char comprtype = JFFS2_COMPR_NONE;
+		uint32_t phys_ofs, alloclen;
+		uint32_t datalen, cdatalen;
+		int retried = 0;
+
+	retry:
+		D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset));
+
+		ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
+		if (ret) {
+			D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret));
+			break;
+		}
+		down(&f->sem);
+		datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1)));
+		cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen);
+
+		comprtype = jffs2_compress(c, f, buf, &comprbuf, &datalen, &cdatalen);
+
+		ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+		ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE);
+		ri->totlen = cpu_to_je32(sizeof(*ri) + cdatalen);
+		ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4));
+
+		ri->ino = cpu_to_je32(f->inocache->ino);
+		ri->version = cpu_to_je32(++f->highest_version);
+		ri->isize = cpu_to_je32(max(je32_to_cpu(ri->isize), offset + datalen));
+		ri->offset = cpu_to_je32(offset);
+		ri->csize = cpu_to_je32(cdatalen);
+		ri->dsize = cpu_to_je32(datalen);
+		ri->compr = comprtype;
+		ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+		ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
+
+		fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY);
+
+		jffs2_free_comprbuf(comprbuf, buf);
+
+		if (IS_ERR(fn)) {
+			ret = PTR_ERR(fn);
+			up(&f->sem);
+			jffs2_complete_reservation(c);
+			if (!retried) {
+				/* Write error to be retried */
+				retried = 1;
+				D1(printk(KERN_DEBUG "Retrying node write in jffs2_write_inode_range()\n"));
+				goto retry;
+			}
+			break;
+		}
+		ret = jffs2_add_full_dnode_to_inode(c, f, fn);
+		if (f->metadata) {
+			jffs2_mark_node_obsolete(c, f->metadata->raw);
+			jffs2_free_full_dnode(f->metadata);
+			f->metadata = NULL;
+		}
+		if (ret) {
+			/* Eep */
+			D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret));
+			jffs2_mark_node_obsolete(c, fn->raw);
+			jffs2_free_full_dnode(fn);
+
+			up(&f->sem);
+			jffs2_complete_reservation(c);
+			break;
+		}
+		up(&f->sem);
+		jffs2_complete_reservation(c);
+		if (!datalen) {
+			printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n");
+			ret = -EIO;
+			break;
+		}
+		D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen));
+		writtenlen += datalen;
+		offset += datalen;
+		writelen -= datalen;
+		buf += datalen;
+	}
+	*retlen = writtenlen;
+	return ret;
+}
+
+int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen)
+{
+	struct jffs2_raw_dirent *rd;
+	struct jffs2_full_dnode *fn;
+	struct jffs2_full_dirent *fd;
+	uint32_t alloclen, phys_ofs;
+	int ret;
+
+	/* Try to reserve enough space for both node and dirent. 
+	 * Just the node will do for now, though 
+	 */
+	ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL);
+	D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen));
+	if (ret) {
+		up(&f->sem);
+		return ret;
+	}
+
+	ri->data_crc = cpu_to_je32(0);
+	ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+
+	fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
+
+	D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n",
+		  jemode_to_cpu(ri->mode)));
+
+	if (IS_ERR(fn)) {
+		D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n"));
+		/* Eeek. Wave bye bye */
+		up(&f->sem);
+		jffs2_complete_reservation(c);
+		return PTR_ERR(fn);
+	}
+	/* No data here. Only a metadata node, which will be 
+	   obsoleted by the first data write
+	*/
+	f->metadata = fn;
+
+	up(&f->sem);
+	jffs2_complete_reservation(c);
+	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+		
+	if (ret) {
+		/* Eep. */
+		D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
+		return ret;
+	}
+
+	rd = jffs2_alloc_raw_dirent();
+	if (!rd) {
+		/* Argh. Now we treat it like a normal delete */
+		jffs2_complete_reservation(c);
+		return -ENOMEM;
+	}
+
+	down(&dir_f->sem);
+
+	rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+	rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
+	rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
+
+	rd->pino = cpu_to_je32(dir_f->inocache->ino);
+	rd->version = cpu_to_je32(++dir_f->highest_version);
+	rd->ino = ri->ino;
+	rd->mctime = ri->ctime;
+	rd->nsize = namelen;
+	rd->type = DT_REG;
+	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+	rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
+
+	fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
+
+	jffs2_free_raw_dirent(rd);
+	
+	if (IS_ERR(fd)) {
+		/* dirent failed to write. Delete the inode normally 
+		   as if it were the final unlink() */
+		jffs2_complete_reservation(c);
+		up(&dir_f->sem);
+		return PTR_ERR(fd);
+	}
+
+	/* Link the fd into the inode's list, obsoleting an old
+	   one if necessary. */
+	jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+
+	jffs2_complete_reservation(c);
+	up(&dir_f->sem);
+
+	return 0;
+}
+
+
+int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f,
+		    const char *name, int namelen, struct jffs2_inode_info *dead_f)
+{
+	struct jffs2_raw_dirent *rd;
+	struct jffs2_full_dirent *fd;
+	uint32_t alloclen, phys_ofs;
+	int ret;
+
+	if (1 /* alternative branch needs testing */ || 
+	    !jffs2_can_mark_obsolete(c)) {
+		/* We can't mark stuff obsolete on the medium. We need to write a deletion dirent */
+
+		rd = jffs2_alloc_raw_dirent();
+		if (!rd)
+			return -ENOMEM;
+
+		ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION);
+		if (ret) {
+			jffs2_free_raw_dirent(rd);
+			return ret;
+		}
+
+		down(&dir_f->sem);
+
+		/* Build a deletion node */
+		rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+		rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+		rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
+		rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
+		
+		rd->pino = cpu_to_je32(dir_f->inocache->ino);
+		rd->version = cpu_to_je32(++dir_f->highest_version);
+		rd->ino = cpu_to_je32(0);
+		rd->mctime = cpu_to_je32(get_seconds());
+		rd->nsize = namelen;
+		rd->type = DT_UNKNOWN;
+		rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+		rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
+
+		fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION);
+		
+		jffs2_free_raw_dirent(rd);
+
+		if (IS_ERR(fd)) {
+			jffs2_complete_reservation(c);
+			up(&dir_f->sem);
+			return PTR_ERR(fd);
+		}
+
+		/* File it. This will mark the old one obsolete. */
+		jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+		up(&dir_f->sem);
+	} else {
+		struct jffs2_full_dirent **prev = &dir_f->dents;
+		uint32_t nhash = full_name_hash(name, namelen);
+
+		down(&dir_f->sem);
+
+		while ((*prev) && (*prev)->nhash <= nhash) {
+			if ((*prev)->nhash == nhash && 
+			    !memcmp((*prev)->name, name, namelen) &&
+			    !(*prev)->name[namelen]) {
+				struct jffs2_full_dirent *this = *prev;
+
+				D1(printk(KERN_DEBUG "Marking old dirent node (ino #%u) @%08x obsolete\n",
+					  this->ino, ref_offset(this->raw)));
+
+				*prev = this->next;
+				jffs2_mark_node_obsolete(c, (this->raw));
+				jffs2_free_full_dirent(this);
+				break;
+			}
+			prev = &((*prev)->next);
+		}
+		up(&dir_f->sem);
+	}
+
+	/* dead_f is NULL if this was a rename not a real unlink */
+	/* Also catch the !f->inocache case, where there was a dirent
+	   pointing to an inode which didn't exist. */
+	if (dead_f && dead_f->inocache) { 
+
+		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));
+			}
+			jffs2_mark_node_obsolete(c, fd->raw);
+			jffs2_free_full_dirent(fd);
+		}
+
+		dead_f->inocache->nlink--;
+		/* NB: Caller must set inode nlink if appropriate */
+		up(&dead_f->sem);
+	}
+
+	jffs2_complete_reservation(c);
+
+	return 0;
+}
+
+
+int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen)
+{
+	struct jffs2_raw_dirent *rd;
+	struct jffs2_full_dirent *fd;
+	uint32_t alloclen, phys_ofs;
+	int ret;
+
+	rd = jffs2_alloc_raw_dirent();
+	if (!rd)
+		return -ENOMEM;
+
+	ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL);
+	if (ret) {
+		jffs2_free_raw_dirent(rd);
+		return ret;
+	}
+	
+	down(&dir_f->sem);
+
+	/* Build a deletion node */
+	rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK);
+	rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT);
+	rd->totlen = cpu_to_je32(sizeof(*rd) + namelen);
+	rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4));
+
+	rd->pino = cpu_to_je32(dir_f->inocache->ino);
+	rd->version = cpu_to_je32(++dir_f->highest_version);
+	rd->ino = cpu_to_je32(ino);
+	rd->mctime = cpu_to_je32(get_seconds());
+	rd->nsize = namelen;
+
+	rd->type = type;
+
+	rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8));
+	rd->name_crc = cpu_to_je32(crc32(0, name, namelen));
+
+	fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL);
+	
+	jffs2_free_raw_dirent(rd);
+
+	if (IS_ERR(fd)) {
+		jffs2_complete_reservation(c);
+		up(&dir_f->sem);
+		return PTR_ERR(fd);
+	}
+
+	/* File it. This will mark the old one obsolete. */
+	jffs2_add_fd_to_list(c, fd, &dir_f->dents);
+
+	jffs2_complete_reservation(c);
+	up(&dir_f->sem);
+
+	return 0;
+}
diff -Nur linux-2.4.30/fs/jffs2/writev.c linux-2.4.30-linux-mips.org/fs/jffs2/writev.c
--- linux-2.4.30/fs/jffs2/writev.c	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/jffs2/writev.c	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,50 @@
+/*
+ * JFFS2 -- Journalling Flash File System, Version 2.
+ *
+ * Copyright (C) 2001, 2002 Red Hat, Inc.
+ *
+ * Created by David Woodhouse <dwmw2@redhat.com>
+ *
+ * For licensing information, see the file 'LICENCE' in this directory.
+ *
+ * $Id: writev.c,v 1.4 2003/10/04 08:33:07 dwmw2 Exp $
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/mtd/mtd.h>
+#include "nodelist.h"
+
+/* 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,
+				  unsigned long count, loff_t to, size_t *retlen)
+{
+	unsigned long i;
+	size_t totlen = 0, thislen;
+	int ret = 0;
+
+	for (i=0; i<count; i++) {
+		if (!vecs[i].iov_len)
+			continue;
+		ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base);
+		totlen += thislen;
+		if (ret || thislen != vecs[i].iov_len)
+			break;
+		to += vecs[i].iov_len;
+	}
+	if (retlen)
+		*retlen = totlen;
+	return ret;
+}
+
+int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs,
+			      unsigned long count, loff_t to, size_t *retlen)
+{
+	if (c->mtd->writev)
+		return c->mtd->writev(c->mtd, vecs, count, to, retlen);
+	else
+		return mtd_fake_writev(c->mtd, vecs, count, to, retlen);
+}
+
diff -Nur linux-2.4.30/fs/partitions/sgi.c linux-2.4.30-linux-mips.org/fs/partitions/sgi.c
--- linux-2.4.30/fs/partitions/sgi.c	2001-10-02 05:03:26.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/partitions/sgi.c	2005-05-12 17:57:37.000000000 +0200
@@ -17,6 +17,11 @@
 #include "check.h"
 #include "sgi.h"
 
+#if CONFIG_BLK_DEV_MD
+extern void md_autodetect_dev(kdev_t dev);
+#endif
+
+
 int sgi_partition(struct gendisk *hd, struct block_device *bdev, unsigned long first_sector, int current_minor)
 {
 	int i, csum, magic;
@@ -77,6 +82,10 @@
 		if(!blocks)
 			continue;
 		add_gd_partition(hd, current_minor, start, blocks);
+#ifdef CONFIG_BLK_DEV_MD
+		if (be32_to_cpu(p->type) == LINUX_RAID_PARTITION)
+			md_autodetect_dev(MKDEV(hd->major, current_minor));
+#endif
 		current_minor++;
 	}
 	printk("\n");
diff -Nur linux-2.4.30/fs/proc/array.c linux-2.4.30-linux-mips.org/fs/proc/array.c
--- linux-2.4.30/fs/proc/array.c	2005-01-19 15:10:11.000000000 +0100
+++ linux-2.4.30-linux-mips.org/fs/proc/array.c	2005-05-12 17:57:37.000000000 +0200
@@ -368,15 +368,15 @@
 		task->cmin_flt,
 		task->maj_flt,
 		task->cmaj_flt,
-		task->times.tms_utime,
-		task->times.tms_stime,
-		task->times.tms_cutime,
-		task->times.tms_cstime,
+		hz_to_std(task->times.tms_utime),
+		hz_to_std(task->times.tms_stime),
+		hz_to_std(task->times.tms_cutime),
+		hz_to_std(task->times.tms_cstime),
 		priority,
 		nice,
 		0UL /* removed */,
 		task->it_real_value,
-		task->start_time,
+		hz_to_std(task->start_time),
 		vsize,
 		mm ? mm->rss : 0, /* you might want to shift this left 3 */
 		task->rlim[RLIMIT_RSS].rlim_cur,
@@ -615,14 +615,14 @@
 
 	len = sprintf(buffer,
 		"cpu  %lu %lu\n",
-		task->times.tms_utime,
-		task->times.tms_stime);
+		hz_to_std(task->times.tms_utime),
+		hz_to_std(task->times.tms_stime));
 		
 	for (i = 0 ; i < smp_num_cpus; i++)
 		len += sprintf(buffer + len, "cpu%d %lu %lu\n",
 			i,
-			task->per_cpu_utime[cpu_logical_map(i)],
-			task->per_cpu_stime[cpu_logical_map(i)]);
+			hz_to_std(task->per_cpu_utime[cpu_logical_map(i)]),
+			hz_to_std(task->per_cpu_stime[cpu_logical_map(i)]));
 
 	return len;
 }
diff -Nur linux-2.4.30/fs/proc/proc_misc.c linux-2.4.30-linux-mips.org/fs/proc/proc_misc.c
--- linux-2.4.30/fs/proc/proc_misc.c	2004-08-08 01:26:06.000000000 +0200
+++ linux-2.4.30-linux-mips.org/fs/proc/proc_misc.c	2005-05-12 17:57:37.000000000 +0200
@@ -308,16 +308,16 @@
 {
 	int i, len = 0;
 	extern unsigned long total_forks;
-	unsigned long jif = jiffies;
+	unsigned long jif = hz_to_std(jiffies);
 	unsigned int sum = 0, user = 0, nice = 0, system = 0;
 	int major, disk;
 
 	for (i = 0 ; i < smp_num_cpus; i++) {
 		int cpu = cpu_logical_map(i), j;
 
-		user += kstat.per_cpu_user[cpu];
-		nice += kstat.per_cpu_nice[cpu];
-		system += kstat.per_cpu_system[cpu];
+		user += hz_to_std(kstat.per_cpu_user[cpu]);
+		nice += hz_to_std(kstat.per_cpu_nice[cpu]);
+		system += hz_to_std(kstat.per_cpu_system[cpu]);
 #if !defined(CONFIG_ARCH_S390)
 		for (j = 0 ; j < NR_IRQS ; j++)
 			sum += kstat.irqs[cpu][j];
@@ -331,10 +331,10 @@
 		proc_sprintf(page, &off, &len,
 			"cpu%d %u %u %u %lu\n",
 			i,
-			kstat.per_cpu_user[cpu_logical_map(i)],
-			kstat.per_cpu_nice[cpu_logical_map(i)],
-			kstat.per_cpu_system[cpu_logical_map(i)],
-			jif - (  kstat.per_cpu_user[cpu_logical_map(i)] \
+			hz_to_std(kstat.per_cpu_user[cpu_logical_map(i)]),
+			hz_to_std(kstat.per_cpu_nice[cpu_logical_map(i)]),
+			hz_to_std(kstat.per_cpu_system[cpu_logical_map(i)]),
+			jif - hz_to_std(  kstat.per_cpu_user[cpu_logical_map(i)] \
 				   + kstat.per_cpu_nice[cpu_logical_map(i)] \
 				   + kstat.per_cpu_system[cpu_logical_map(i)]));
 	proc_sprintf(page, &off, &len,
diff -Nur linux-2.4.30/include/asm-alpha/param.h linux-2.4.30-linux-mips.org/include/asm-alpha/param.h
--- linux-2.4.30/include/asm-alpha/param.h	2000-11-08 08:37:31.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-alpha/param.h	2005-05-12 17:57:39.000000000 +0200
@@ -13,6 +13,9 @@
 # else
 #  define HZ	1200
 # endif
+#ifdef __KERNEL__
+# define hz_to_std(a) (a)
+#endif
 #endif
 
 #define EXEC_PAGESIZE	8192
diff -Nur linux-2.4.30/include/asm-i386/param.h linux-2.4.30-linux-mips.org/include/asm-i386/param.h
--- linux-2.4.30/include/asm-i386/param.h	2000-10-27 20:04:43.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/asm-i386/param.h	2005-05-12 17:57:42.000000000 +0200
@@ -3,6 +3,9 @@
 
 #ifndef HZ
 #define HZ 100
+#ifdef __KERNEL__
+#define hz_to_std(a) (a)
+#endif
 #endif
 
 #define EXEC_PAGESIZE	4096
diff -Nur linux-2.4.30/include/asm-ia64/param.h linux-2.4.30-linux-mips.org/include/asm-ia64/param.h
--- linux-2.4.30/include/asm-ia64/param.h	2004-04-14 15:05:40.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/asm-ia64/param.h	2005-05-12 17:57:43.000000000 +0200
@@ -7,9 +7,15 @@
  * Based on <asm-i386/param.h>.
  *
  * Modified 1998, 1999, 2002-2003
- *	David Mosberger-Tang <davidm@hpl.hp.com>, Hewlett-Packard Co
+ *    David Mosberger-Tang <davidm@hpl.hp.com>, Hewlett-Packard Co
  */
 
+#include <linux/config.h>
+
+#ifdef __KERNEL__
+#define hz_to_std(a) (a)
+#endif
+
 #define EXEC_PAGESIZE	65536
 
 #ifndef NGROUPS
diff -Nur linux-2.4.30/include/asm-m68k/param.h linux-2.4.30-linux-mips.org/include/asm-m68k/param.h
--- linux-2.4.30/include/asm-m68k/param.h	2001-01-04 22:00:55.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-m68k/param.h	2005-05-12 17:57:44.000000000 +0200
@@ -3,6 +3,9 @@
 
 #ifndef HZ
 #define HZ 100
+#ifdef __KERNEL__
+#define hz_to_std(a) (a)
+#endif
 #endif
 
 #define EXEC_PAGESIZE	8192
diff -Nur linux-2.4.30/include/asm-mips/au1000_gpio.h linux-2.4.30-linux-mips.org/include/asm-mips/au1000_gpio.h
--- linux-2.4.30/include/asm-mips/au1000_gpio.h	2002-11-29 00:53:15.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/au1000_gpio.h	2005-05-12 17:57:44.000000000 +0200
@@ -30,6 +30,13 @@
  *  675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
+/*
+ *  Revision history
+ *    01/31/02  0.01   Initial release. Steve Longerbeam, MontaVista
+ *    10/12/03  0.1    Added Au1100/Au1500, GPIO2, and bit operations. K.C. Nishio, AMD
+ *    08/05/04  0.11   Added Au1550 and Au1200. K.C. Nishio
+ */
+
 #ifndef __AU1000_GPIO_H
 #define __AU1000_GPIO_H
 
@@ -44,13 +51,94 @@
 #define AU1000GPIO_TRISTATE	_IOW (AU1000GPIO_IOC_MAGIC, 4, int)
 #define AU1000GPIO_AVAIL_MASK	_IOR (AU1000GPIO_IOC_MAGIC, 5, int)
 
+// bit operations
+#define AU1000GPIO_BIT_READ	_IOW (AU1000GPIO_IOC_MAGIC, 6, int)
+#define AU1000GPIO_BIT_SET	_IOW (AU1000GPIO_IOC_MAGIC, 7, int)
+#define AU1000GPIO_BIT_CLEAR	_IOW (AU1000GPIO_IOC_MAGIC, 8, int)
+#define AU1000GPIO_BIT_TRISTATE	_IOW (AU1000GPIO_IOC_MAGIC, 9, int)
+#define AU1000GPIO_BIT_INIT	_IOW (AU1000GPIO_IOC_MAGIC, 10, int)
+#define AU1000GPIO_BIT_TERM	_IOW (AU1000GPIO_IOC_MAGIC, 11, int)
+
+/* set this major numer same as the CRIS GPIO driver */
+#define AU1X00_GPIO_MAJOR	(120)
+
+#define ENABLED_ZERO		(0)
+#define ENABLED_ONE		(1)
+#define ENABLED_10		(0x2)
+#define ENABLED_11		(0x3)
+#define ENABLED_111		(0x7)
+#define NOT_AVAIL		(-1)
+#define AU1X00_MAX_PRIMARY_GPIO	(32) 
+
+#define AU1000_GPIO_MINOR_MAX	AU1X00_MAX_PRIMARY_GPIO
+/* Au1100, 1500, 1550 and 1200 have the secondary GPIO block */
+#define AU1XX0_GPIO_MINOR_MAX	(48)
+
+#define AU1X00_GPIO_NAME	"gpio"
+
+/* GPIO pins which are not multiplexed */
+#if defined(CONFIG_SOC_AU1000)
+  #define NATIVE_GPIOPIN	((1 << 15) | (1 << 8) | (1 << 7) | (1 << 1) | (1 << 0))
+  #define NATIVE_GPIO2PIN	(0)
+#elif defined(CONFIG_SOC_AU1100)
+  #define NATIVE_GPIOPIN	((1 << 23) | (1 << 22) | (1 << 21) | (1 << 20) | (1 << 19) | (1 << 18) | \
+				 (1 << 17) | (1 << 16) | (1 << 7) | (1 << 1) | (1 << 0))
+  #define NATIVE_GPIO2PIN	(0)
+#elif defined(CONFIG_SOC_AU1500)
+  #define NATIVE_GPIOPIN	((1 << 15) | (1 << 8) | (1 << 7) | (1 << 1) | (1 << 0))
+  /* exclude the PCI reset output signal: GPIO[200], DMA_REQ2 and DMA_REQ3 */
+  #define NATIVE_GPIO2PIN	(0xfffe & ~((1 << 9) | (1 << 8))) 
+#elif defined(CONFIG_SOC_AU1550)
+  #define NATIVE_GPIOPIN	((1 << 15) | (1 << 8) | (1 << 7) | (1 << 6) | (1 << 1) | (1 << 0))
+  /* please refere Au1550 Data Book, chapter 15 */
+  #define NATIVE_GPIO2PIN	(1 << 5) 
+#elif defined(CONFIG_SOC_AU1200)
+  #define NATIVE_GPIOPIN	((1 << 7) | (1 << 5))
+  #define NATIVE_GPIO2PIN	(0) 
+#endif
+
+/* minor as u32 */
+#define MINOR_TO_GPIOPIN(minor)		((minor < AU1X00_MAX_PRIMARY_GPIO) ? minor : (minor - AU1X00_MAX_PRIMARY_GPIO))
+#define IS_PRIMARY_GPIOPIN(minor)	((minor < AU1X00_MAX_PRIMARY_GPIO) ? 1 : 0)
+
+/*
+ * pin to minor mapping.
+ * GPIO0-GPIO31, minor=0-31.
+ * GPIO200-GPIO215, minor=32-47.
+ */
+typedef struct _au1x00_gpio_bit_ctl {
+	int direction;	// The direction of this GPIO pin. 0: IN, 1: OUT.
+	int data;	// Pin output when itized (0/1), or at the term. 0/1/-1 (tristate).
+} au1x00_gpio_bit_ctl;
+
+typedef struct _au1x00_gpio_driver {
+	const char	*driver_name;
+	const char	*name;
+	int		name_base;	/* offset of printed name */
+	short		major;		/* major device number */
+	short		minor_start;	/* start of minor device number*/
+	short		num;		/* number of devices */
+} au1x00_gpio_driver;
+
 #ifdef __KERNEL__
-extern u32 get_au1000_avail_gpio_mask(void);
-extern int au1000gpio_tristate(u32 data);
-extern int au1000gpio_in(u32 *data);
-extern int au1000gpio_set(u32 data);
-extern int au1000gpio_clear(u32 data);
-extern int au1000gpio_out(u32 data);
+extern u32 get_au1000_avail_gpio_mask(u32 *avail_gpio2);
+extern int au1000gpio_tristate(u32 minor, u32 data);
+extern int au1000gpio_in(u32 minor, u32 *data);
+extern int au1000gpio_set(u32 minor, u32 data);
+extern int au1000gpio_clear(u32 minor, u32 data);
+extern int au1000gpio_out(u32 minor, u32 data);
+extern int au1000gpio_bit_read(u32 minor, u32 *read_data);
+extern int au1000gpio_bit_set(u32 minor);
+extern int au1000gpio_bit_clear(u32 minor);
+extern int au1000gpio_bit_tristate(u32 minor);
+extern int check_minor_to_gpio(u32 minor);
+extern int au1000gpio_bit_init(u32 minor, au1x00_gpio_bit_ctl *bit_opt);
+extern int au1000gpio_bit_term(u32 minor, au1x00_gpio_bit_ctl *bit_opt);
+
+extern void gpio_register_devfs (au1x00_gpio_driver *driver, unsigned int flags, unsigned minor);
+extern void gpio_unregister_devfs (au1x00_gpio_driver *driver, unsigned minor);
+extern int gpio_register_driver(au1x00_gpio_driver *driver);
+extern int gpio_unregister_driver(au1x00_gpio_driver *driver);
 #endif
 
 #endif
diff -Nur linux-2.4.30/include/asm-mips/au1000.h linux-2.4.30-linux-mips.org/include/asm-mips/au1000.h
--- linux-2.4.30/include/asm-mips/au1000.h	2005-01-19 15:10:11.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/au1000.h	2005-05-12 18:01:29.000000000 +0200
@@ -153,35 +153,357 @@
 
 #endif /* !defined (_LANGUAGE_ASSEMBLY) */
 
-#ifdef CONFIG_PM
-/* no CP0 timer irq */
-#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4)
-#else
-#define ALLINTS (IE_IRQ0 | IE_IRQ1 | IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5)
-#endif
 
-/* SDRAM Controller */
+/*
+ * SDRAM Register Offsets
+ */
 #if defined(CONFIG_SOC_AU1000) || defined(CONFIG_SOC_AU1500) || defined(CONFIG_SOC_AU1100)
-#define MEM_SDMODE0                0xB4000000
-#define MEM_SDMODE1                0xB4000004
-#define MEM_SDMODE2                0xB4000008
-
-#define MEM_SDADDR0                0xB400000C
-#define MEM_SDADDR1                0xB4000010
-#define MEM_SDADDR2                0xB4000014
-
-#define MEM_SDREFCFG               0xB4000018
-#define MEM_SDPRECMD               0xB400001C
-#define MEM_SDAUTOREF              0xB4000020
-
-#define MEM_SDWRMD0                0xB4000024
-#define MEM_SDWRMD1                0xB4000028
-#define MEM_SDWRMD2                0xB400002C
+#define MEM_SDMODE0		(0x0000)
+#define MEM_SDMODE1		(0x0004)
+#define MEM_SDMODE2		(0x0008)
+#define MEM_SDADDR0		(0x000C)
+#define MEM_SDADDR1		(0x0010)
+#define MEM_SDADDR2		(0x0014)
+#define MEM_SDREFCFG	(0x0018)
+#define MEM_SDPRECMD	(0x001C)
+#define MEM_SDAUTOREF	(0x0020)
+#define MEM_SDWRMD0		(0x0024)
+#define MEM_SDWRMD1		(0x0028)
+#define MEM_SDWRMD2		(0x002C)
+#define MEM_SDSLEEP		(0x0030)
+#define MEM_SDSMCKE		(0x0034)
+
+#ifndef ASSEMBLER
+/*typedef volatile struct
+{
+	uint32 sdmode0;
+	uint32 sdmode1;
+	uint32 sdmode2;
+	uint32 sdaddr0;
+	uint32 sdaddr1;
+	uint32 sdaddr2;
+	uint32 sdrefcfg;
+	uint32 sdautoref;
+	uint32 sdwrmd0;
+	uint32 sdwrmd1;
+	uint32 sdwrmd2;
+	uint32 sdsleep;
+	uint32 sdsmcke;
+
+} AU1X00_SDRAM;*/
+#endif
+
+/*
+ * MEM_SDMODE register content definitions
+ */
+#define MEM_SDMODE_F		(1<<22)
+#define MEM_SDMODE_SR		(1<<21)
+#define MEM_SDMODE_BS		(1<<20)
+#define MEM_SDMODE_RS		(3<<18)
+#define MEM_SDMODE_CS		(7<<15)
+#define MEM_SDMODE_TRAS		(15<<11)
+#define MEM_SDMODE_TMRD		(3<<9)
+#define MEM_SDMODE_TWR		(3<<7)
+#define MEM_SDMODE_TRP		(3<<5)
+#define MEM_SDMODE_TRCD		(3<<3)
+#define MEM_SDMODE_TCL		(7<<0)
+
+#define MEM_SDMODE_BS_2Bank	(0<<20)
+#define MEM_SDMODE_BS_4Bank	(1<<20)
+#define MEM_SDMODE_RS_11Row	(0<<18)
+#define MEM_SDMODE_RS_12Row	(1<<18)
+#define MEM_SDMODE_RS_13Row	(2<<18)
+#define MEM_SDMODE_RS_N(N)	((N)<<18)
+#define MEM_SDMODE_CS_7Col	(0<<15)
+#define MEM_SDMODE_CS_8Col	(1<<15)
+#define MEM_SDMODE_CS_9Col	(2<<15)
+#define MEM_SDMODE_CS_10Col	(3<<15)
+#define MEM_SDMODE_CS_11Col	(4<<15)
+#define MEM_SDMODE_CS_N(N)		((N)<<15)
+#define MEM_SDMODE_TRAS_N(N)	((N)<<11)
+#define MEM_SDMODE_TMRD_N(N)	((N)<<9)
+#define MEM_SDMODE_TWR_N(N)		((N)<<7)
+#define MEM_SDMODE_TRP_N(N)		((N)<<5)
+#define MEM_SDMODE_TRCD_N(N)	((N)<<3)
+#define MEM_SDMODE_TCL_N(N)		((N)<<0)
+
+/*
+ * MEM_SDADDR register contents definitions
+ */
+#define MEM_SDADDR_E			(1<<20)
+#define MEM_SDADDR_CSBA			(0x03FF<<10)
+#define MEM_SDADDR_CSMASK		(0x03FF<<0)
+#define MEM_SDADDR_CSBA_N(N)	((N)&(0x03FF<<22)>>12)
+#define MEM_SDADDR_CSMASK_N(N)	((N)&(0x03FF<<22)>>22)
+
+/*
+ * MEM_SDREFCFG register content definitions
+ */
+#define MEM_SDREFCFG_TRC		(15<<28)
+#define MEM_SDREFCFG_TRPM		(3<<26)
+#define MEM_SDREFCFG_E			(1<<25)
+#define MEM_SDREFCFG_RE			(0x1ffffff<<0)
+#define MEM_SDREFCFG_TRC_N(N)	((N)<<MEM_SDREFCFG_TRC)
+#define MEM_SDREFCFG_TRPM_N(N)	((N)<<MEM_SDREFCFG_TRPM)
+#define MEM_SDREFCFG_REF_N(N)	(N)
+#endif
+
+/***********************************************************************/
+
+/*
+ * Au1550 SDRAM Register Offsets
+ */
+
+/***********************************************************************/
+
+#if defined(CONFIG_SOC_AU1550) || defined(CONFIG_SOC_AU1200)
+#define MEM_SDMODE0		(0x0800)
+#define MEM_SDMODE1		(0x0808)
+#define MEM_SDMODE2		(0x0810)
+#define MEM_SDADDR0		(0x0820)
+#define MEM_SDADDR1		(0x0828)
+#define MEM_SDADDR2		(0x0830)
+#define MEM_SDCONFIGA	(0x0840)
+#define MEM_SDCONFIGB	(0x0848)
+#define MEM_SDSTAT		(0x0850)
+#define MEM_SDERRADDR	(0x0858)
+#define MEM_SDSTRIDE0	(0x0860)
+#define MEM_SDSTRIDE1	(0x0868)
+#define MEM_SDSTRIDE2	(0x0870)
+#define MEM_SDWRMD0		(0x0880)
+#define MEM_SDWRMD1		(0x0888)
+#define MEM_SDWRMD2		(0x0890)
+#define MEM_SDPRECMD	(0x08C0)
+#define MEM_SDAUTOREF	(0x08C8)
+#define MEM_SDSREF		(0x08D0)
+#define MEM_SDSLEEP		MEM_SDSREF
+
+#ifndef ASSEMBLER
+/*typedef volatile struct
+{
+	uint32 sdmode0;
+	uint32 reserved0;
+	uint32 sdmode1;
+	uint32 reserved1;
+	uint32 sdmode2;
+	uint32 reserved2[3];
+	uint32 sdaddr0;
+	uint32 reserved3;
+	uint32 sdaddr1;
+	uint32 reserved4;
+	uint32 sdaddr2;
+	uint32 reserved5[3];
+	uint32 sdconfiga;
+	uint32 reserved6;
+	uint32 sdconfigb;
+	uint32 reserved7;
+	uint32 sdstat;
+	uint32 reserved8;
+	uint32 sderraddr;
+	uint32 reserved9;
+	uint32 sdstride0;
+	uint32 reserved10;
+	uint32 sdstride1;
+	uint32 reserved11;
+	uint32 sdstride2;
+	uint32 reserved12[3];
+	uint32 sdwrmd0;
+	uint32 reserved13;
+	uint32 sdwrmd1;
+	uint32 reserved14;
+	uint32 sdwrmd2;
+	uint32 reserved15[11];
+	uint32 sdprecmd;
+	uint32 reserved16;
+	uint32 sdautoref;
+	uint32 reserved17;
+	uint32 sdsref;
+
+} AU1550_SDRAM;*/
+#endif
+#endif
+
+/*
+ * Physical base addresses for integrated peripherals
+ */
+
+#ifdef CONFIG_SOC_AU1000
+#define	MEM_PHYS_ADDR		0x14000000
+#define	STATIC_MEM_PHYS_ADDR	0x14001000
+#define	DMA0_PHYS_ADDR		0x14002000
+#define	DMA1_PHYS_ADDR		0x14002100
+#define	DMA2_PHYS_ADDR		0x14002200
+#define	DMA3_PHYS_ADDR		0x14002300
+#define	DMA4_PHYS_ADDR		0x14002400
+#define	DMA5_PHYS_ADDR		0x14002500
+#define	DMA6_PHYS_ADDR		0x14002600
+#define	DMA7_PHYS_ADDR		0x14002700
+#define	IC0_PHYS_ADDR		0x10400000
+#define	IC1_PHYS_ADDR		0x11800000
+#define	AC97_PHYS_ADDR		0x10000000
+#define	USBH_PHYS_ADDR		0x10100000
+#define	USBD_PHYS_ADDR		0x10200000
+#define	IRDA_PHYS_ADDR		0x10300000
+#define	MAC0_PHYS_ADDR		0x10500000
+#define	MAC1_PHYS_ADDR		0x10510000
+#define	MACEN_PHYS_ADDR		0x10520000
+#define	MACDMA0_PHYS_ADDR	0x14004000
+#define	MACDMA1_PHYS_ADDR	0x14004200
+#define	I2S_PHYS_ADDR		0x11000000
+#define	UART0_PHYS_ADDR		0x11100000
+#define	UART1_PHYS_ADDR		0x11200000
+#define	UART2_PHYS_ADDR		0x11300000
+#define	UART3_PHYS_ADDR		0x11400000
+#define	SSI0_PHYS_ADDR		0x11600000
+#define	SSI1_PHYS_ADDR		0x11680000
+#define	SYS_PHYS_ADDR		0x11900000
+#define PCMCIA_IO_PHYS_ADDR   0xF00000000
+#define PCMCIA_ATTR_PHYS_ADDR 0xF40000000
+#define PCMCIA_MEM_PHYS_ADDR  0xF80000000
+#endif
+
+/********************************************************************/
+
+#ifdef CONFIG_SOC_AU1500
+#define	MEM_PHYS_ADDR		0x14000000
+#define	STATIC_MEM_PHYS_ADDR	0x14001000
+#define	DMA0_PHYS_ADDR		0x14002000
+#define	DMA1_PHYS_ADDR		0x14002100
+#define	DMA2_PHYS_ADDR		0x14002200
+#define	DMA3_PHYS_ADDR		0x14002300
+#define	DMA4_PHYS_ADDR		0x14002400
+#define	DMA5_PHYS_ADDR		0x14002500
+#define	DMA6_PHYS_ADDR		0x14002600
+#define	DMA7_PHYS_ADDR		0x14002700
+#define	IC0_PHYS_ADDR		0x10400000
+#define	IC1_PHYS_ADDR		0x11800000
+#define	AC97_PHYS_ADDR		0x10000000
+#define	USBH_PHYS_ADDR		0x10100000
+#define	USBD_PHYS_ADDR		0x10200000
+#define PCI_PHYS_ADDR		0x14005000
+#define	MAC0_PHYS_ADDR		0x11500000
+#define	MAC1_PHYS_ADDR		0x11510000
+#define	MACEN_PHYS_ADDR		0x11520000
+#define	MACDMA0_PHYS_ADDR	0x14004000
+#define	MACDMA1_PHYS_ADDR	0x14004200
+#define	I2S_PHYS_ADDR		0x11000000
+#define	UART0_PHYS_ADDR		0x11100000
+#define	UART3_PHYS_ADDR		0x11400000
+#define GPIO2_PHYS_ADDR		0x11700000
+#define	SYS_PHYS_ADDR		0x11900000
+#define PCI_MEM_PHYS_ADDR     0x400000000
+#define PCI_IO_PHYS_ADDR      0x500000000
+#define PCI_CONFIG0_PHYS_ADDR 0x600000000
+#define PCI_CONFIG1_PHYS_ADDR 0x680000000
+#define PCMCIA_IO_PHYS_ADDR   0xF00000000
+#define PCMCIA_ATTR_PHYS_ADDR 0xF40000000
+#define PCMCIA_MEM_PHYS_ADDR  0xF80000000
+#endif
 
-#define MEM_SDSLEEP                0xB4000030
-#define MEM_SDSMCKE                0xB4000034
+/********************************************************************/
+
+#ifdef CONFIG_SOC_AU1100
+#define	MEM_PHYS_ADDR		0x14000000
+#define	STATIC_MEM_PHYS_ADDR	0x14001000
+#define	DMA0_PHYS_ADDR		0x14002000
+#define	DMA1_PHYS_ADDR		0x14002100
+#define	DMA2_PHYS_ADDR		0x14002200
+#define	DMA3_PHYS_ADDR		0x14002300
+#define	DMA4_PHYS_ADDR		0x14002400
+#define	DMA5_PHYS_ADDR		0x14002500
+#define	DMA6_PHYS_ADDR		0x14002600
+#define	DMA7_PHYS_ADDR		0x14002700
+#define	IC0_PHYS_ADDR		0x10400000
+#define SD0_PHYS_ADDR		0x10600000
+#define SD1_PHYS_ADDR		0x10680000
+#define	IC1_PHYS_ADDR		0x11800000
+#define	AC97_PHYS_ADDR		0x10000000
+#define	USBH_PHYS_ADDR		0x10100000
+#define	USBD_PHYS_ADDR		0x10200000
+#define	IRDA_PHYS_ADDR		0x10300000
+#define	MAC0_PHYS_ADDR		0x10500000
+#define	MACEN_PHYS_ADDR		0x10520000
+#define	MACDMA0_PHYS_ADDR	0x14004000
+#define	MACDMA1_PHYS_ADDR	0x14004200
+#define	I2S_PHYS_ADDR		0x11000000
+#define	UART0_PHYS_ADDR		0x11100000
+#define	UART1_PHYS_ADDR		0x11200000
+#define	UART3_PHYS_ADDR		0x11400000
+#define	SSI0_PHYS_ADDR		0x11600000
+#define	SSI1_PHYS_ADDR		0x11680000
+#define GPIO2_PHYS_ADDR		0x11700000
+#define	SYS_PHYS_ADDR		0x11900000
+#define LCD_PHYS_ADDR		0x15000000
+#define PCMCIA_IO_PHYS_ADDR   0xF00000000
+#define PCMCIA_ATTR_PHYS_ADDR 0xF40000000
+#define PCMCIA_MEM_PHYS_ADDR  0xF80000000
+#endif
+
+/***********************************************************************/
+
+#ifdef CONFIG_SOC_AU1550
+#define	MEM_PHYS_ADDR		0x14000000
+#define	STATIC_MEM_PHYS_ADDR	0x14001000
+#define	IC0_PHYS_ADDR		0x10400000
+#define	IC1_PHYS_ADDR		0x11800000
+#define	USBH_PHYS_ADDR		0x14020000
+#define	USBD_PHYS_ADDR		0x10200000
+#define PCI_PHYS_ADDR		0x14005000
+#define	MAC0_PHYS_ADDR		0x10500000
+#define	MAC1_PHYS_ADDR		0x10510000
+#define	MACEN_PHYS_ADDR		0x10520000
+#define	MACDMA0_PHYS_ADDR	0x14004000
+#define	MACDMA1_PHYS_ADDR	0x14004200
+#define	UART0_PHYS_ADDR		0x11100000
+#define	UART1_PHYS_ADDR		0x11200000
+#define	UART3_PHYS_ADDR		0x11400000
+#define GPIO2_PHYS_ADDR		0x11700000
+#define	SYS_PHYS_ADDR		0x11900000
+#define	DDMA_PHYS_ADDR		0x14002000
+#define PE_PHYS_ADDR		0x14008000
+#define PSC0_PHYS_ADDR	 	0x11A00000
+#define PSC1_PHYS_ADDR	 	0x11B00000
+#define PSC2_PHYS_ADDR	 	0x10A00000
+#define PSC3_PHYS_ADDR	 	0x10B00000
+#define PCI_MEM_PHYS_ADDR     0x400000000
+#define PCI_IO_PHYS_ADDR      0x500000000
+#define PCI_CONFIG0_PHYS_ADDR 0x600000000
+#define PCI_CONFIG1_PHYS_ADDR 0x680000000
+#define PCMCIA_IO_PHYS_ADDR   0xF00000000
+#define PCMCIA_ATTR_PHYS_ADDR 0xF40000000
+#define PCMCIA_MEM_PHYS_ADDR  0xF80000000
 #endif
 
+/***********************************************************************/
+
+#ifdef CONFIG_SOC_AU1200
+#define	MEM_PHYS_ADDR		0x14000000
+#define	STATIC_MEM_PHYS_ADDR	0x14001000
+#define AES_PHYS_ADDR		0x10300000
+#define CIM_PHYS_ADDR		0x14004000
+#define	IC0_PHYS_ADDR		0x10400000
+#define	IC1_PHYS_ADDR		0x11800000
+#define USBM_PHYS_ADDR		0x14020000
+#define	USBH_PHYS_ADDR		0x14020100
+#define	UART0_PHYS_ADDR		0x11100000
+#define	UART1_PHYS_ADDR		0x11200000
+#define GPIO2_PHYS_ADDR		0x11700000
+#define	SYS_PHYS_ADDR		0x11900000
+#define	DDMA_PHYS_ADDR		0x14002000
+#define PSC0_PHYS_ADDR	 	0x11A00000
+#define PSC1_PHYS_ADDR	 	0x11B00000
+#define PCMCIA_IO_PHYS_ADDR   0xF00000000
+#define PCMCIA_ATTR_PHYS_ADDR 0xF40000000
+#define PCMCIA_MEM_PHYS_ADDR  0xF80000000
+#define SD0_PHYS_ADDR		0x10600000
+#define SD1_PHYS_ADDR		0x10680000
+#define LCD_PHYS_ADDR		0x15000000
+#define SWCNT_PHYS_ADDR		0x1110010C
+#define MAEFE_PHYS_ADDR		0x14012000
+#define MAEBE_PHYS_ADDR		0x14010000
+#endif
+
+
 /* Static Bus Controller */
 #define MEM_STCFG0                 0xB4001000
 #define MEM_STTIME0                0xB4001004
@@ -367,7 +689,7 @@
 #define AU1000_MAC0_ENABLE       0xB0520000
 #define AU1000_MAC1_ENABLE       0xB0520004
 #define NUM_ETH_INTERFACES 2
-#endif // CONFIG_SOC_AU1000
+#endif /* CONFIG_SOC_AU1000 */
 
 /* Au1500 */
 #ifdef CONFIG_SOC_AU1500
@@ -438,7 +760,7 @@
 #define AU1500_MAC0_ENABLE       0xB1520000
 #define AU1500_MAC1_ENABLE       0xB1520004
 #define NUM_ETH_INTERFACES 2
-#endif // CONFIG_SOC_AU1500
+#endif /* CONFIG_SOC_AU1500 */
 
 /* Au1100 */
 #ifdef CONFIG_SOC_AU1100
@@ -483,6 +805,22 @@
 #define AU1000_GPIO_13            45
 #define AU1000_GPIO_14            46
 #define AU1000_GPIO_15            47
+#define AU1000_GPIO_16            48
+#define AU1000_GPIO_17            49
+#define AU1000_GPIO_18            50
+#define AU1000_GPIO_19            51
+#define AU1000_GPIO_20            52
+#define AU1000_GPIO_21            53
+#define AU1000_GPIO_22            54
+#define AU1000_GPIO_23            55
+#define AU1000_GPIO_24            56
+#define AU1000_GPIO_25            57
+#define AU1000_GPIO_26            58
+#define AU1000_GPIO_27            59
+#define AU1000_GPIO_28            60
+#define AU1000_GPIO_29            61
+#define AU1000_GPIO_30            62
+#define AU1000_GPIO_31            63
 
 #define UART0_ADDR                0xB1100000
 #define UART1_ADDR                0xB1200000
@@ -494,7 +832,7 @@
 #define AU1100_ETH0_BASE	  0xB0500000
 #define AU1100_MAC0_ENABLE       0xB0520000
 #define NUM_ETH_INTERFACES 1
-#endif // CONFIG_SOC_AU1100
+#endif /* CONFIG_SOC_AU1100 */
 
 #ifdef CONFIG_SOC_AU1550
 #define AU1550_UART0_INT          0
@@ -511,14 +849,14 @@
 #define AU1550_PSC1_INT           11
 #define AU1550_PSC2_INT           12
 #define AU1550_PSC3_INT           13
-#define AU1550_TOY_INT			  14
-#define AU1550_TOY_MATCH0_INT     15
-#define AU1550_TOY_MATCH1_INT     16
-#define AU1550_TOY_MATCH2_INT     17
-#define AU1550_RTC_INT            18
-#define AU1550_RTC_MATCH0_INT     19
-#define AU1550_RTC_MATCH1_INT     20
-#define AU1550_RTC_MATCH2_INT     21
+#define AU1000_TOY_INT			  14
+#define AU1000_TOY_MATCH0_INT     15
+#define AU1000_TOY_MATCH1_INT     16
+#define AU1000_TOY_MATCH2_INT     17
+#define AU1000_RTC_INT            18
+#define AU1000_RTC_MATCH0_INT     19
+#define AU1000_RTC_MATCH1_INT     20
+#define AU1000_RTC_MATCH2_INT     21
 #define AU1550_NAND_INT           23
 #define AU1550_USB_DEV_REQ_INT    24
 #define AU1550_USB_DEV_SUS_INT    25
@@ -573,7 +911,7 @@
 #define AU1550_MAC0_ENABLE       0xB0520000
 #define AU1550_MAC1_ENABLE       0xB0520004
 #define NUM_ETH_INTERFACES 2
-#endif // CONFIG_SOC_AU1550
+#endif /* CONFIG_SOC_AU1550 */
 
 #ifdef CONFIG_SOC_AU1200
 #define AU1200_UART0_INT          0
@@ -590,14 +928,14 @@
 #define AU1200_PSC1_INT           11
 #define AU1200_AES_INT            12
 #define AU1200_CAMERA_INT         13
-#define AU1200_TOY_INT			  14
-#define AU1200_TOY_MATCH0_INT     15
-#define AU1200_TOY_MATCH1_INT     16
-#define AU1200_TOY_MATCH2_INT     17
-#define AU1200_RTC_INT            18
-#define AU1200_RTC_MATCH0_INT     19
-#define AU1200_RTC_MATCH1_INT     20
-#define AU1200_RTC_MATCH2_INT     21
+#define AU1000_TOY_INT			  14
+#define AU1000_TOY_MATCH0_INT     15
+#define AU1000_TOY_MATCH1_INT     16
+#define AU1000_TOY_MATCH2_INT     17
+#define AU1000_RTC_INT            18
+#define AU1000_RTC_MATCH0_INT     19
+#define AU1000_RTC_MATCH1_INT     20
+#define AU1000_RTC_MATCH2_INT     21
 #define AU1200_NAND_INT           23
 #define AU1200_GPIO_204           24
 #define AU1200_GPIO_205           25
@@ -605,6 +943,7 @@
 #define AU1200_GPIO_207           27
 #define AU1200_GPIO_208_215       28 // Logical OR of 208:215
 #define AU1200_USB_INT            29
+#define AU1000_USB_HOST_INT		  AU1200_USB_INT
 #define AU1200_LCD_INT            30
 #define AU1200_MAE_BOTH_INT       31
 #define AU1000_GPIO_0             32
@@ -643,43 +982,54 @@
 #define UART0_ADDR                0xB1100000
 #define UART1_ADDR                0xB1200000
 
-#define USB_OHCI_BASE             0x14020000 // phys addr for ioremap
-#define USB_HOST_CONFIG           0xB4027ffc
+#define USB_UOC_BASE              0x14020020
+#define USB_UOC_LEN               0x20
+#define USB_OHCI_BASE             0x14020100
+#define USB_OHCI_LEN              0x100
+#define USB_EHCI_BASE             0x14020200
+#define USB_EHCI_LEN              0x100
+#define USB_UDC_BASE              0x14022000
+#define USB_UDC_LEN               0x2000
+#define USB_MSR_BASE			  0xB4020000
+#define USB_MSR_MCFG              4
+#define USBMSRMCFG_OMEMEN         0
+#define USBMSRMCFG_OBMEN          1
+#define USBMSRMCFG_EMEMEN         2
+#define USBMSRMCFG_EBMEN          3
+#define USBMSRMCFG_DMEMEN         4
+#define USBMSRMCFG_DBMEN          5
+#define USBMSRMCFG_GMEMEN         6
+#define USBMSRMCFG_OHCCLKEN       16
+#define USBMSRMCFG_EHCCLKEN       17
+#define USBMSRMCFG_UDCCLKEN       18
+#define USBMSRMCFG_PHYPLLEN       19
+#define USBMSRMCFG_RDCOMB         30
+#define USBMSRMCFG_PFEN           31
 
-// these are here for prototyping on au1550 (do not exist on au1200)
-#define AU1200_ETH0_BASE      0xB0500000
-#define AU1200_ETH1_BASE      0xB0510000
-#define AU1200_MAC0_ENABLE       0xB0520000
-#define AU1200_MAC1_ENABLE       0xB0520004
-#define NUM_ETH_INTERFACES 2
-#endif // CONFIG_SOC_AU1200
+#endif /* CONFIG_SOC_AU1200 */
 
 #define AU1000_LAST_INTC0_INT     31
+#define AU1000_LAST_INTC1_INT     63
 #define AU1000_MAX_INTR           63
 
-
 /* Programmable Counters 0 and 1 */
 #define SYS_BASE                   0xB1900000
 #define SYS_COUNTER_CNTRL          (SYS_BASE + 0x14)
-  #define SYS_CNTRL_E1S            (1<<23)
-  #define SYS_CNTRL_T1S            (1<<20)
-  #define SYS_CNTRL_M21            (1<<19)
-  #define SYS_CNTRL_M11            (1<<18)
-  #define SYS_CNTRL_M01            (1<<17)
-  #define SYS_CNTRL_C1S            (1<<16)
-  #define SYS_CNTRL_BP             (1<<14)
-  #define SYS_CNTRL_EN1            (1<<13)
-  #define SYS_CNTRL_BT1            (1<<12)
-  #define SYS_CNTRL_EN0            (1<<11)
-  #define SYS_CNTRL_BT0            (1<<10)
-  #define SYS_CNTRL_E0             (1<<8)
-  #define SYS_CNTRL_E0S            (1<<7)
-  #define SYS_CNTRL_32S            (1<<5)
-  #define SYS_CNTRL_T0S            (1<<4)
-  #define SYS_CNTRL_M20            (1<<3)
-  #define SYS_CNTRL_M10            (1<<2)
-  #define SYS_CNTRL_M00            (1<<1)
-  #define SYS_CNTRL_C0S            (1<<0)
+#define SYS_CNTRCTRL_RTS	(1<<20)
+#define SYS_CNTRCTRL_RM2	(1<<19)
+#define SYS_CNTRCTRL_RM1	(1<<18)
+#define SYS_CNTRCTRL_RM0	(1<<17)
+#define SYS_CNTRCTRL_RS		(1<<16)
+#define SYS_CNTRCTRL_BP		(1<<14)
+#define SYS_CNTRCTRL_E0		(1<<8)
+#define SYS_CNTRCTRL_CCS	(1<<7)
+#define SYS_CNTRCTRL_32S	(1<<5)
+#define SYS_CNTRCTRL_TTS	(1<<4)
+#define SYS_CNTRCTRL_TM2	(1<<3)
+#define SYS_CNTRCTRL_TM1	(1<<2)
+#define SYS_CNTRCTRL_TM0	(1<<1)
+#define SYS_CNTRCTRL_TS		(1<<0)
+
 
 /* Programmable Counter 0 Registers */
 #define SYS_TOYTRIM                 (SYS_BASE + 0)
@@ -728,6 +1078,8 @@
   #define I2S_CONTROL_D         (1<<1)
   #define I2S_CONTROL_CE        (1<<0)
 
+#ifndef CONFIG_SOC_AU1200
+
 /* USB Host Controller */
 #define USB_OHCI_LEN              0x00100000
 
@@ -773,6 +1125,8 @@
   #define USBDEV_ENABLE (1<<1)
   #define USBDEV_CE     (1<<0)
 
+#endif /* !CONFIG_SOC_AU1200 */
+
 /* Ethernet Controllers  */
 
 /* 4 byte offsets from AU1000_ETH_BASE */
@@ -1171,6 +1525,37 @@
   #define SYS_PF_PSC1_S1		(1 << 1)
   #define SYS_PF_MUST_BE_SET		((1 << 5) | (1 << 2))
 
+/* Au1200 Only */
+#ifdef CONFIG_SOC_AU1200
+#define SYS_PINFUNC_DMA		(1<<31)
+#define SYS_PINFUNC_S0A		(1<<30)
+#define SYS_PINFUNC_S1A		(1<<29)
+#define SYS_PINFUNC_LP0		(1<<28)
+#define SYS_PINFUNC_LP1		(1<<27)
+#define SYS_PINFUNC_LD16	(1<<26)
+#define SYS_PINFUNC_LD8		(1<<25)
+#define SYS_PINFUNC_LD1		(1<<24)
+#define SYS_PINFUNC_LD0		(1<<23)
+#define SYS_PINFUNC_P1A		(3<<21)
+#define SYS_PINFUNC_P1B		(1<<20)
+#define SYS_PINFUNC_FS3		(1<<19)
+#define SYS_PINFUNC_P0A		(3<<17)
+#define SYS_PINFUNC_CS		(1<<16)
+#define SYS_PINFUNC_CIM		(1<<15)
+#define SYS_PINFUNC_P1C		(1<<14)
+#define SYS_PINFUNC_U1T		(1<<12)
+#define SYS_PINFUNC_U1R		(1<<11)
+#define SYS_PINFUNC_EX1		(1<<10)
+#define SYS_PINFUNC_EX0		(1<<9)
+#define SYS_PINFUNC_U0R		(1<<8)
+#define SYS_PINFUNC_MC		(1<<7)
+#define SYS_PINFUNC_S0B		(1<<6)
+#define SYS_PINFUNC_S0C		(1<<5)
+#define SYS_PINFUNC_P0B		(1<<4)
+#define SYS_PINFUNC_U0T		(1<<3)
+#define SYS_PINFUNC_S1B		(1<<2)
+#endif
+
 #define SYS_TRIOUTRD              0xB1900100
 #define SYS_TRIOUTCLR             0xB1900100
 #define SYS_OUTPUTRD              0xB1900108
@@ -1298,7 +1683,6 @@
 #define SD1_XMIT_FIFO	0xB0680000
 #define SD1_RECV_FIFO	0xB0680004
 
-
 #if defined (CONFIG_SOC_AU1500) || defined(CONFIG_SOC_AU1550)
 /* Au1500 PCI Controller */
 #define Au1500_CFG_BASE           0xB4005000 // virtual, kseg0 addr
@@ -1388,9 +1772,60 @@
 
 #endif
 
+#ifndef _LANGUAGE_ASSEMBLY
+typedef volatile struct
+{
+	/* 0x0000 */ u32 toytrim;
+	/* 0x0004 */ u32 toywrite;
+	/* 0x0008 */ u32 toymatch0;
+	/* 0x000C */ u32 toymatch1;
+	/* 0x0010 */ u32 toymatch2;
+	/* 0x0014 */ u32 cntrctrl;
+	/* 0x0018 */ u32 scratch0;
+	/* 0x001C */ u32 scratch1;
+	/* 0x0020 */ u32 freqctrl0;
+	/* 0x0024 */ u32 freqctrl1;
+	/* 0x0028 */ u32 clksrc;
+	/* 0x002C */ u32 pinfunc;
+	/* 0x0030 */ u32 reserved0;
+	/* 0x0034 */ u32 wakemsk;
+	/* 0x0038 */ u32 endian;
+	/* 0x003C */ u32 powerctrl;
+	/* 0x0040 */ u32 toyread;
+	/* 0x0044 */ u32 rtctrim;
+	/* 0x0048 */ u32 rtcwrite;
+	/* 0x004C */ u32 rtcmatch0;
+	/* 0x0050 */ u32 rtcmatch1;
+	/* 0x0054 */ u32 rtcmatch2;
+	/* 0x0058 */ u32 rtcread;
+	/* 0x005C */ u32 wakesrc;
+	/* 0x0060 */ u32 cpupll;
+	/* 0x0064 */ u32 auxpll;
+	/* 0x0068 */ u32 reserved1;
+	/* 0x006C */ u32 reserved2;
+	/* 0x0070 */ u32 reserved3;
+	/* 0x0074 */ u32 reserved4;
+	/* 0x0078 */ u32 slppwr;
+	/* 0x007C */ u32 sleep;
+	/* 0x0080 */ u32 reserved5[32];
+	/* 0x0100 */ u32 trioutrd;
+#define trioutclr trioutrd
+	/* 0x0104 */ u32 reserved6;
+	/* 0x0108 */ u32 outputrd;
+#define outputset outputrd
+	/* 0x010C */ u32 outputclr;
+	/* 0x0110 */ u32 pinstaterd;
+#define pininputen pinstaterd
+
+} AU1X00_SYS;
+
+static AU1X00_SYS* const sys  = (AU1X00_SYS *)SYS_BASE;
+
+#endif
 /* Processor information base on prid.
  * Copied from PowerPC.
  */
+#ifndef _LANGUAGE_ASSEMBLY
 struct cpu_spec {
 	/* CPU is matched via (PRID & prid_mask) == prid_value */
 	unsigned int	prid_mask;
@@ -1404,3 +1839,6 @@
 extern struct cpu_spec		cpu_specs[];
 extern struct cpu_spec		*cur_cpu_spec[];
 #endif
+
+#endif
+
diff -Nur linux-2.4.30/include/asm-mips/au1000_pcmcia.h linux-2.4.30-linux-mips.org/include/asm-mips/au1000_pcmcia.h
--- linux-2.4.30/include/asm-mips/au1000_pcmcia.h	2005-01-19 15:10:11.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/au1000_pcmcia.h	2005-05-12 17:57:44.000000000 +0200
@@ -38,16 +38,41 @@
 #define AU1X_SOCK0_PHYS_MEM  0xF80000000
 
 /* pcmcia socket 1 needs external glue logic so the memory map
- * differs from board to board.
+ * differs from board to board. the general rule is that
+ * static bus address bit 26 should be used to decode socket 0
+ * from socket 1. alas, some boards dont follow this...
+ * These really belong in a board-specific header file...
  */
-#if defined(CONFIG_MIPS_PB1000) || defined(CONFIG_MIPS_PB1100) || defined(CONFIG_MIPS_PB1500)
-#define AU1X_SOCK1_IO        0xF08000000
-#define AU1X_SOCK1_PHYS_ATTR 0xF48000000
-#define AU1X_SOCK1_PHYS_MEM  0xF88000000
-#elif defined(CONFIG_MIPS_DB1000) || defined(CONFIG_MIPS_DB1100) || defined(CONFIG_MIPS_DB1500) || defined(CONFIG_MIPS_PB1550) || defined(CONFIG_MIPS_DB1550)
-#define AU1X_SOCK1_IO        0xF04000000
-#define AU1X_SOCK1_PHYS_ATTR 0xF44000000
-#define AU1X_SOCK1_PHYS_MEM  0xF84000000
+#ifdef CONFIG_MIPS_PB1000
+#define SOCK1_DECODE (1<<27)
+#endif
+#ifdef CONFIG_MIPS_DB1000
+#define SOCK1_DECODE (1<<26)
+#endif
+#ifdef CONFIG_MIPS_DB1500
+#define SOCK1_DECODE (1<<26)
+#endif
+#ifdef CONFIG_MIPS_DB1100
+#define SOCK1_DECODE (1<<26)
+#endif
+#ifdef CONFIG_MIPS_DB1550
+#define SOCK1_DECODE (1<<26)
+#endif
+#ifdef CONFIG_MIPS_DB1200
+#define SOCK1_DECODE (1<<26)
+#endif
+#ifdef CONFIG_MIPS_PB1550
+#define SOCK1_DECODE (1<<26)
+#endif
+#ifdef CONFIG_MIPS_PB1200
+#define SOCK1_DECODE (1<<26)
+#endif
+
+/* The board has a second PCMCIA socket */
+#ifdef SOCK1_DECODE
+#define AU1X_SOCK1_IO        (0xF00000000|SOCK1_DECODE)
+#define AU1X_SOCK1_PHYS_ATTR (0xF40000000|SOCK1_DECODE)
+#define AU1X_SOCK1_PHYS_MEM  (0xF80000000|SOCK1_DECODE)
 #endif
 
 struct pcmcia_state {
diff -Nur linux-2.4.30/include/asm-mips/au1100_mmc.h linux-2.4.30-linux-mips.org/include/asm-mips/au1100_mmc.h
--- linux-2.4.30/include/asm-mips/au1100_mmc.h	2005-01-19 15:10:11.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/au1100_mmc.h	2005-05-12 17:57:44.000000000 +0200
@@ -39,16 +39,22 @@
 #define __ASM_AU1100_MMC_H
 
 
-#define NUM_AU1100_MMC_CONTROLLERS	2
-
-
-#define AU1100_SD_IRQ	2
-
+#if defined(CONFIG_SOC_AU1100)
+#define NUM_MMC_CONTROLLERS	2
+#define AU1X_MMC_INT AU1100_SD_INT
+#endif
+
+#if defined(CONFIG_SOC_AU1200)
+#define NUM_MMC_CONTROLLERS	2
+#define AU1X_MMC_INT AU1200_SD_INT
+#endif
 
 #define SD0_BASE	0xB0600000
 #define SD1_BASE	0xB0680000
 
 
+
+
 /*
  *  Register offsets.
  */
@@ -201,5 +207,12 @@
 #define SD_CMD_RT_1B	(0x00810000)
 
 
+/* support routines required on a platform-specific basis */
+extern void mmc_card_inserted(int _n_, int *_res_);
+extern void mmc_card_writable(int _n_, int *_res_);
+extern void mmc_power_on(int _n_);
+extern void mmc_power_off(int _n_);
+
+
 #endif /* __ASM_AU1100_MMC_H */
 
diff -Nur linux-2.4.30/include/asm-mips/au1xxx_dbdma.h linux-2.4.30-linux-mips.org/include/asm-mips/au1xxx_dbdma.h
--- linux-2.4.30/include/asm-mips/au1xxx_dbdma.h	2005-01-19 15:10:11.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/au1xxx_dbdma.h	2005-05-12 17:57:44.000000000 +0200
@@ -43,7 +43,7 @@
 #define DDMA_GLOBAL_BASE	0xb4003000
 #define DDMA_CHANNEL_BASE	0xb4002000
 
-typedef struct dbdma_global {
+typedef volatile struct dbdma_global {
 	u32	ddma_config;
 	u32	ddma_intstat;
 	u32	ddma_throttle;
@@ -60,7 +60,7 @@
 
 /* The structure of a DMA Channel.
 */
-typedef struct au1xxx_dma_channel {
+typedef volatile struct au1xxx_dma_channel {
 	u32	ddma_cfg;	/* See below */
 	u32	ddma_desptr;	/* 32-byte aligned pointer to descriptor */
 	u32	ddma_statptr;	/* word aligned pointer to status word */
@@ -96,7 +96,7 @@
 /* "Standard" DDMA Descriptor.
  * Must be 32-byte aligned.
  */
-typedef struct au1xxx_ddma_desc {
+typedef volatile struct au1xxx_ddma_desc {
 	u32	dscr_cmd0;		/* See below */
 	u32	dscr_cmd1;		/* See below */
 	u32	dscr_source0;		/* source phys address */
@@ -105,6 +105,12 @@
 	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 */
@@ -123,6 +129,8 @@
 #define DSCR_CMD0_CV		(0x1 << 2)	/* Clear Valid when done */
 #define DSCR_CMD0_ST_MASK	(0x3 << 0)	/* Status instruction */
 
+#define SW_STATUS_INUSE		(1<<0)
+
 /* Command 0 device IDs.
 */
 #ifdef CONFIG_SOC_AU1550
@@ -169,8 +177,8 @@
 #define DSCR_CMD0_SDMS_RX0	9
 #define DSCR_CMD0_SDMS_TX1	10
 #define DSCR_CMD0_SDMS_RX1	11
-#define DSCR_CMD0_AES_TX	12
-#define DSCR_CMD0_AES_RX	13
+#define DSCR_CMD0_AES_TX	13
+#define DSCR_CMD0_AES_RX	12
 #define DSCR_CMD0_PSC0_TX	14
 #define DSCR_CMD0_PSC0_RX	15
 #define DSCR_CMD0_PSC1_TX	16
@@ -189,6 +197,10 @@
 #define DSCR_CMD0_THROTTLE	30
 #define DSCR_CMD0_ALWAYS	31
 #define DSCR_NDEV_IDS		32
+/* THis macro is used to find/create custom device types */
+#define DSCR_DEV2CUSTOM_ID(x,d)	(((((x)&0xFFFF)<<8)|0x32000000)|((d)&0xFF))
+#define DSCR_CUSTOM2DEV_ID(x)	((x)&0xFF)
+
 
 #define DSCR_CMD0_SID(x)	(((x) & 0x1f) << 25)
 #define DSCR_CMD0_DID(x)	(((x) & 0x1f) << 20)
@@ -277,6 +289,43 @@
 */
 #define NUM_DBDMA_CHANS	16
 
+/*
+ * Ddma API definitions
+ * FIXME: may not fit to this header file
+ */
+typedef struct dbdma_device_table {
+	u32		dev_id;
+	u32		dev_flags;
+	u32		dev_tsize;
+	u32		dev_devwidth;
+	u32		dev_physaddr;		/* If FIFO */
+	u32		dev_intlevel;
+	u32		dev_intpolarity;
+} dbdev_tab_t;
+
+
+typedef struct dbdma_chan_config {
+	spinlock_t      lock;
+
+	u32			chan_flags;
+	u32			chan_index;
+	dbdev_tab_t		*chan_src;
+	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;
+	void			*chan_callparam;
+	void (*chan_callback)(int, void *, struct pt_regs *);
+} chan_tab_t;
+
+#define DEV_FLAGS_INUSE		(1 << 0)
+#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_SYNC		(1 << 5)
+/* end Ddma API definitions */
+
 /* External functions for drivers to use.
 */
 /* Use this to allocate a dbdma channel.  The device ids are one of the
@@ -299,8 +348,8 @@
 
 /* Put buffers on source/destination descriptors.
 */
-u32 au1xxx_dbdma_put_source(u32 chanid, void *buf, int nbytes);
-u32 au1xxx_dbdma_put_dest(u32 chanid, void *buf, int nbytes);
+u32 _au1xxx_dbdma_put_source(u32 chanid, void *buf, int nbytes, u32 flags);
+u32 _au1xxx_dbdma_put_dest(u32 chanid, void *buf, int nbytes, u32 flags);
 
 /* Get a buffer from the destination descriptor.
 */
@@ -314,5 +363,25 @@
 void au1xxx_dbdma_chan_free(u32 chanid);
 void au1xxx_dbdma_dump(u32 chanid);
 
+u32 au1xxx_dbdma_put_dscr(u32 chanid, au1x_ddma_desc_t *dscr );
+
+u32 au1xxx_ddma_add_device( dbdev_tab_t *dev );
+
+/*
+ 	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_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)
+
+/*
+ *	Flags for the put_source/put_dest functions.
+ */
+#define DDMA_FLAGS_IE	(1<<0)
+#define DDMA_FLAGS_NOIE (1<<1)
+
 #endif /* _LANGUAGE_ASSEMBLY */
 #endif /* _AU1000_DBDMA_H_ */
diff -Nur linux-2.4.30/include/asm-mips/au1xxx_gpio.h linux-2.4.30-linux-mips.org/include/asm-mips/au1xxx_gpio.h
--- linux-2.4.30/include/asm-mips/au1xxx_gpio.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/au1xxx_gpio.h	2005-05-12 17:57:44.000000000 +0200
@@ -0,0 +1,22 @@
+
+
+#ifndef __AU1XXX_GPIO_H
+#define __AU1XXX_GPIO_H
+
+void au1xxx_gpio1_set_inputs(void);
+void au1xxx_gpio_tristate(int signal);
+void au1xxx_gpio_write(int signal, int value);
+int  au1xxx_gpio_read(int signal);
+
+typedef volatile struct
+{
+	u32 dir;
+	u32 reserved;
+	u32 output;
+	u32 pinstate;
+	u32 inten;
+	u32 enable;
+
+} AU1X00_GPIO2;
+
+#endif //__AU1XXX_GPIO_H
diff -Nur linux-2.4.30/include/asm-mips/au1xxx_psc.h linux-2.4.30-linux-mips.org/include/asm-mips/au1xxx_psc.h
--- linux-2.4.30/include/asm-mips/au1xxx_psc.h	2005-01-19 15:10:11.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/au1xxx_psc.h	2005-05-12 17:57:44.000000000 +0200
@@ -41,6 +41,11 @@
 #define PSC3_BASE_ADDR		0xb0d00000
 #endif
 
+#ifdef CONFIG_SOC_AU1200
+#define PSC0_BASE_ADDR		0xb1a00000
+#define PSC1_BASE_ADDR		0xb1b00000
+#endif
+
 /* The PSC select and control registers are common to
  * all protocols.
  */
@@ -226,6 +231,8 @@
 #define PSC_I2SCFG_DD_DISABLE	(1 << 27)
 #define PSC_I2SCFG_DE_ENABLE	(1 << 26)
 #define PSC_I2SCFG_SET_WS(x)	(((((x) / 2) - 1) & 0x7f) << 16)
+#define PSC_I2SCFG_WS(n)		((n&0xFF)<<16)
+#define PSC_I2SCFG_WS_MASK	(PSC_I2SCFG_WS(0x3F))
 #define PSC_I2SCFG_WI		(1 << 15)
 
 #define PSC_I2SCFG_DIV_MASK	(3 << 13)
diff -Nur linux-2.4.30/include/asm-mips/bootinfo.h linux-2.4.30-linux-mips.org/include/asm-mips/bootinfo.h
--- linux-2.4.30/include/asm-mips/bootinfo.h	2004-02-18 14:36:32.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/bootinfo.h	2005-05-12 17:57:45.000000000 +0200
@@ -180,6 +180,9 @@
 #define MACH_MTX1		7	/* 4G MTX-1 Au1500-based board */
 #define MACH_CSB250		8	/* Cogent Au1500 */
 #define MACH_PB1550     	9       /* Au1550-based eval board */
+#define MACH_PB1200     	10      /* Au1200-based eval board */
+#define MACH_DB1550     	11      /* Au1550-based eval board */
+#define MACH_DB1200     	12      /* Au1200-based eval board */
 
 /*
  * Valid machtype for group NEC_VR41XX
diff -Nur linux-2.4.30/include/asm-mips/db1200.h linux-2.4.30-linux-mips.org/include/asm-mips/db1200.h
--- linux-2.4.30/include/asm-mips/db1200.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/db1200.h	2005-05-12 17:57:45.000000000 +0200
@@ -0,0 +1,214 @@
+/*
+ * AMD Alchemy DB1200 Referrence Board
+ * Board Registers defines.
+ *
+ * ########################################################################
+ *
+ *  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_DB1200_H
+#define __ASM_DB1200_H
+
+#include <linux/types.h>
+
+// This is defined in au1000.h with bogus value
+#undef AU1X00_EXTERNAL_INT
+
+#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_PSC1_TX
+#define DBDMA_I2S_RX_CHAN DSCR_CMD0_PSC1_RX
+
+/* SPI and SMB are muxed on the Pb1200 board.
+   Refer to board documentation.
+ */
+#define SPI_PSC_BASE        PSC0_BASE_ADDR
+#define SMBUS_PSC_BASE      PSC0_BASE_ADDR
+/* AC97 and I2S are muxed on the Pb1200 board.
+   Refer to board documentation.
+ */
+#define AC97_PSC_BASE       PSC1_BASE_ADDR
+#define I2S_PSC_BASE		PSC1_BASE_ADDR
+
+#define BCSR_KSEG1_ADDR 0xB9800000
+
+typedef volatile struct
+{
+	/*00*/	u16 whoami;
+		u16 reserved0;
+	/*04*/	u16 status;
+		u16 reserved1;
+	/*08*/	u16 switches;
+		u16 reserved2;
+	/*0C*/	u16 resets;
+		u16 reserved3;
+
+	/*10*/	u16 pcmcia;
+		u16 reserved4;
+	/*14*/	u16 board;
+		u16 reserved5;
+	/*18*/	u16 disk_leds;
+		u16 reserved6;
+	/*1C*/	u16 system;
+		u16 reserved7;
+
+	/*20*/	u16 intclr;
+		u16 reserved8;
+	/*24*/	u16 intset;
+		u16 reserved9;
+	/*28*/	u16 intclr_mask;
+		u16 reserved10;
+	/*2C*/	u16 intset_mask;
+		u16 reserved11;
+
+	/*30*/	u16 sig_status;
+		u16 reserved12;
+	/*34*/	u16 int_status;
+		u16 reserved13;
+	/*38*/	u16 reserved14;
+		u16 reserved15;
+	/*3C*/	u16 reserved16;
+		u16 reserved17;
+
+} BCSR;
+
+static BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+
+/*
+ * Register bit definitions for the BCSRs
+ */
+#define BCSR_WHOAMI_DCID	0x000F
+#define BCSR_WHOAMI_CPLD	0x00F0
+#define BCSR_WHOAMI_BOARD	0x0F00
+
+#define BCSR_STATUS_PCMCIA0VS	0x0003
+#define BCSR_STATUS_PCMCIA1VS	0x000C
+#define BCSR_STATUS_SWAPBOOT	0x0040
+#define BCSR_STATUS_FLASHBUSY	0x0100
+#define BCSR_STATUS_IDECBLID	0x0200
+#define BCSR_STATUS_SD0WP		0x0400
+#define BCSR_STATUS_U0RXD		0x1000
+#define BCSR_STATUS_U1RXD		0x2000
+
+#define BCSR_SWITCHES_OCTAL	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_ETH		0x0001
+#define BCSR_RESETS_CAMERA	0x0002
+#define BCSR_RESETS_DC		0x0004
+#define BCSR_RESETS_IDE		0x0008
+#define BCSR_RESETS_TV		0x0010
+/* not resets but in the same register */
+#define BCSR_RESETS_PWMR1mUX 0x0800
+#define BCSR_RESETS_PCS0MUX	0x1000
+#define BCSR_RESETS_PCS1MUX	0x2000
+#define BCSR_RESETS_SPISEL	0x4000
+
+#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_LCDVEE	0x0001
+#define BCSR_BOARD_LCDVDD	0x0002
+#define BCSR_BOARD_LCDBL	0x0004
+#define BCSR_BOARD_CAMSNAP	0x0010
+#define BCSR_BOARD_CAMPWR	0x0020
+#define BCSR_BOARD_SD0PWR	0x0040
+
+#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_SYSTEM_POWEROFF	0x4000
+#define BCSR_SYSTEM_RESET	0x8000
+
+/* Bit positions for the different interrupt sources */
+#define BCSR_INT_IDE		0x0001
+#define BCSR_INT_ETH		0x0002
+#define BCSR_INT_PC0		0x0004
+#define BCSR_INT_PC0STSCHG	0x0008
+#define BCSR_INT_PC1		0x0010
+#define BCSR_INT_PC1STSCHG	0x0020
+#define BCSR_INT_DC			0x0040
+#define BCSR_INT_FLASHBUSY	0x0080
+#define BCSR_INT_PC0INSERT	0x0100
+#define BCSR_INT_PC0EJECT	0x0200
+#define BCSR_INT_PC1INSERT	0x0400
+#define BCSR_INT_PC1EJECT	0x0800
+#define BCSR_INT_SD0INSERT	0x1000
+#define BCSR_INT_SD0EJECT	0x2000
+
+#define AU1XXX_SMC91111_PHYS_ADDR	(0x19000300)
+#define AU1XXX_SMC91111_IRQ			DB1200_ETH_INT
+
+#define AU1XXX_ATA_PHYS_ADDR		(0x18800000)
+#define AU1XXX_ATA_PHYS_LEN			(0x100)
+#define AU1XXX_ATA_REG_OFFSET	(5)
+#define AU1XXX_ATA_INT			DB1200_IDE_INT
+#define AU1XXX_ATA_DDMA_REQ		DSCR_CMD0_DMA_REQ1;
+#define AU1XXX_ATA_RQSIZE		128
+
+#define NAND_PHYS_ADDR   0x20000000
+
+/*
+ *	External Interrupts for Pb1200 as of 8/6/2004.
+ *   Bit positions in the CPLD registers can be calculated by taking
+ *   the interrupt define and subtracting the DB1200_INT_BEGIN value.
+ *    *example: IDE bis pos is  = 64 - 64
+                ETH bit pos is  = 65 - 64
+ */
+#define DB1200_INT_BEGIN		(AU1000_LAST_INTC1_INT + 1)
+#define DB1200_IDE_INT			(DB1200_INT_BEGIN + 0)
+#define DB1200_ETH_INT			(DB1200_INT_BEGIN + 1)
+#define DB1200_PC0_INT			(DB1200_INT_BEGIN + 2)
+#define DB1200_PC0_STSCHG_INT	(DB1200_INT_BEGIN + 3)
+#define DB1200_PC1_INT			(DB1200_INT_BEGIN + 4)
+#define DB1200_PC1_STSCHG_INT	(DB1200_INT_BEGIN + 5)
+#define DB1200_DC_INT			(DB1200_INT_BEGIN + 6)
+#define DB1200_FLASHBUSY_INT	(DB1200_INT_BEGIN + 7)
+#define DB1200_PC0_INSERT_INT	(DB1200_INT_BEGIN + 8)
+#define DB1200_PC0_EJECT_INT	(DB1200_INT_BEGIN + 9)
+#define DB1200_PC1_INSERT_INT	(DB1200_INT_BEGIN + 10)
+#define DB1200_PC1_EJECT_INT	(DB1200_INT_BEGIN + 11)
+#define DB1200_SD0_INSERT_INT	(DB1200_INT_BEGIN + 12)
+#define DB1200_SD0_EJECT_INT	(DB1200_INT_BEGIN + 13)
+
+#define DB1200_INT_END			(DB1200_INT_BEGIN + 15)
+
+/* For drivers/pcmcia/au1000_db1x00.c */
+#define BOARD_PC0_INT DB1200_PC0_INT
+#define BOARD_PC1_INT DB1200_PC1_INT
+#define BOARD_CARD_INSERTED(SOCKET) bcsr->sig_status & (1<<(8+(2*SOCKET)))
+
+#endif /* __ASM_DB1200_H */
+
diff -Nur linux-2.4.30/include/asm-mips/db1x00.h linux-2.4.30-linux-mips.org/include/asm-mips/db1x00.h
--- linux-2.4.30/include/asm-mips/db1x00.h	2005-01-19 15:10:11.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/db1x00.h	2005-05-12 18:01:29.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * AMD Alchemy DB1x00 Reference Boards
+ * AMD Alchemy DB1x00 Reference Boards (BUT NOT DB1200)
  *
  * Copyright 2001 MontaVista Software Inc.
  * Author: MontaVista Software, Inc.
@@ -30,15 +30,28 @@
 #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
+#define NAND_CS 1
+/* 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)))
 
 #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
 
 /*
@@ -66,6 +79,7 @@
 
 } BCSR;
 
+static BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
 
 /*
  * Register/mask bit definitions for the BCSRs
@@ -130,14 +144,6 @@
 
 #define BCSR_SWRESET_RESET		0x0080
 
-/* PCMCIA Db1x00 specific defines */
-#define PCMCIA_MAX_SOCK 1
-#define PCMCIA_NUM_SOCKS (PCMCIA_MAX_SOCK+1)
-
-/* VPP/VCC */
-#define SET_VCC_VPP(VCC, VPP, SLOT)\
-	((((VCC)<<2) | ((VPP)<<0)) << ((SLOT)*8))
-
 /* MTD CONFIG OPTIONS */
 #if defined(CONFIG_MTD_DB1X00_BOOT) && defined(CONFIG_MTD_DB1X00_USER)
 #define DB1X00_BOTH_BANKS
@@ -147,48 +153,15 @@
 #define DB1X00_USER_ONLY
 #endif
 
-/* SD controller macros */
-/*
- * Detect card.
- */
-#define mmc_card_inserted(_n_, _res_) \
-	do { \
-		BCSR * const bcsr = (BCSR *)0xAE000000; \
-		unsigned long mmc_wp, board_specific; \
-		if ((_n_)) { \
-			mmc_wp = BCSR_BOARD_SD1_WP; \
-		} else { \
-			mmc_wp = BCSR_BOARD_SD0_WP; \
-		} \
-		board_specific = au_readl((unsigned long)(&bcsr->specific)); \
-		if (!(board_specific & mmc_wp)) {/* low means card present */ \
-			*(int *)(_res_) = 1; \
-		} else { \
-			*(int *)(_res_) = 0; \
-		} \
-	} while (0)
-
+#if defined(CONFIG_BLK_DEV_IDE_AU1XXX) && defined(CONFIG_MIPS_DB1550)
 /*
- * Apply power to card slot(s).
+ * Daughter card information.
  */
-#define mmc_power_on(_n_) \
-	do { \
-		BCSR * const bcsr = (BCSR *)0xAE000000; \
-		unsigned long mmc_pwr, mmc_wp, board_specific; \
-		if ((_n_)) { \
-			mmc_pwr = BCSR_BOARD_SD1_PWR; \
-			mmc_wp = BCSR_BOARD_SD1_WP; \
-		} else { \
-			mmc_pwr = BCSR_BOARD_SD0_PWR; \
-			mmc_wp = BCSR_BOARD_SD0_WP; \
-		} \
-		board_specific = au_readl((unsigned long)(&bcsr->specific)); \
-		if (!(board_specific & mmc_wp)) {/* low means card present */ \
-			board_specific |= mmc_pwr; \
-			au_writel(board_specific, (int)(&bcsr->specific)); \
-			au_sync(); \
-		} \
-	} while (0)
+#define DAUGHTER_CARD_IRQ		(AU1000_GPIO_8)
+/* DC_IDE */
+#define AU1XXX_ATA_PHYS_ADDR		(0x0C000000)
+#define AU1XXX_ATA_REG_OFFSET		(5)	
+#endif /* CONFIG_MIPS_DB1550 */
 
 #endif /* __ASM_DB1X00_H */
 
diff -Nur linux-2.4.30/include/asm-mips/elf.h linux-2.4.30-linux-mips.org/include/asm-mips/elf.h
--- linux-2.4.30/include/asm-mips/elf.h	2004-02-18 14:36:32.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/elf.h	2005-05-12 17:57:45.000000000 +0200
@@ -66,9 +66,10 @@
 #define USE_ELF_CORE_DUMP
 #define ELF_EXEC_PAGESIZE	PAGE_SIZE
 
-#define ELF_CORE_COPY_REGS(_dest,_regs)				\
-	memcpy((char *) &_dest, (char *) _regs,			\
-	       sizeof(struct pt_regs));
+extern void dump_regs(elf_greg_t *, struct pt_regs *regs);
+
+#define ELF_CORE_COPY_REGS(elf_regs, regs)			\
+	dump_regs((elf_greg_t *)&(elf_regs), regs);
 
 /* This yields a mask that user programs can use to figure out what
    instruction set this cpu supports.  This could be done in userspace,
diff -Nur linux-2.4.30/include/asm-mips/fcntl.h linux-2.4.30-linux-mips.org/include/asm-mips/fcntl.h
--- linux-2.4.30/include/asm-mips/fcntl.h	2002-11-29 00:53:15.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/fcntl.h	2005-05-12 18:01:29.000000000 +0200
@@ -26,6 +26,7 @@
 #define O_DIRECT	0x8000	/* direct disk access hint */
 #define O_DIRECTORY	0x10000	/* must be a directory */
 #define O_NOFOLLOW	0x20000	/* don't follow links */
+#define O_STREAMING    0x400000	/* streaming access */
 
 #define O_NDELAY	O_NONBLOCK
 
diff -Nur linux-2.4.30/include/asm-mips/ficmmp.h linux-2.4.30-linux-mips.org/include/asm-mips/ficmmp.h
--- linux-2.4.30/include/asm-mips/ficmmp.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/ficmmp.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,154 @@
+/*
+ * FIC MMP
+ *
+ * ########################################################################
+ *
+ *  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_FICMMP_H
+#define __ASM_FICMMP_H
+
+#include <linux/types.h>
+#include <asm/au1000.h>
+#include <asm/au1xxx_gpio.h>
+
+// This is defined in au1000.h with bogus value
+#undef AU1X00_EXTERNAL_INT
+
+#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_PSC1_TX
+#define DBDMA_I2S_RX_CHAN DSCR_CMD0_PSC1_RX
+/* SPI and SMB are muxed on the Pb1200 board.
+   Refer to board documentation.
+ */
+#define SPI_PSC_BASE        PSC0_BASE_ADDR
+#define SMBUS_PSC_BASE      PSC0_BASE_ADDR
+/* AC97 and I2S are muxed on the Pb1200 board.
+   Refer to board documentation.
+ */
+#define AC97_PSC_BASE       PSC1_BASE_ADDR
+#define I2S_PSC_BASE		PSC1_BASE_ADDR
+
+
+/*
+ * SMSC LAN91C111
+ */
+#define AU1XXX_SMC91111_PHYS_ADDR	(0xAC000300)
+#define AU1XXX_SMC91111_IRQ			AU1000_GPIO_5
+
+/* DC_IDE and DC_ETHERNET */
+#define FICMMP_IDE_INT	AU1000_GPIO_4
+
+#define AU1XXX_ATA_PHYS_ADDR	(0x0C800000)
+#define AU1XXX_ATA_REG_OFFSET	(5)
+#define AU1XXX_ATA_PHYS_LEN         (0x100)
+#define AU1XXX_ATA_INT			FICMMP_IDE_INT
+#define AU1XXX_ATA_DDMA_REQ     DSCR_CMD0_DMA_REQ1;
+#define AU1XXX_ATA_RQSIZE       128
+
+/* VPP/VCC */
+#define SET_VCC_VPP(VCC, VPP, SLOT)\
+	((((VCC)<<2) | ((VPP)<<0)) << ((SLOT)*8))
+
+	
+#define FICMMP_CONFIG_BASE		0xAD000000
+#define FICMMP_CONFIG_ENABLE	13
+
+#define FICMMP_CONFIG_I2SFREQ(N)	(N<<0)
+#define FICMMP_CONFIG_I2SXTAL0		(1<<0)
+#define FICMMP_CONFIG_I2SXTAL1		(1<<1)
+#define FICMMP_CONFIG_I2SXTAL2		(1<<2)
+#define FICMMP_CONFIG_I2SXTAL3		(1<<3)
+#define FICMMP_CONFIG_ADV1			(1<<4)
+#define FICMMP_CONFIG_IDERST		(1<<5)
+#define FICMMP_CONFIG_LCMEN			(1<<6)
+#define FICMMP_CONFIG_CAMPWDN		(1<<7)
+#define FICMMP_CONFIG_USBPWREN		(1<<8)
+#define FICMMP_CONFIG_LCMPWREN		(1<<9)
+#define FICMMP_CONFIG_TVOUTPWREN	(1<<10)
+#define FICMMP_CONFIG_RS232PWREN	(1<<11)
+#define FICMMP_CONFIG_LCMDATAOUT	(1<<12)
+#define FICMMP_CONFIG_TVODATAOUT	(1<<13)
+#define FICMMP_CONFIG_ADV3			(1<<14)
+#define FICMMP_CONFIG_ADV4			(1<<15)
+
+#define I2S_FREQ_8_192				(0x0)
+#define I2S_FREQ_11_2896			(0x1)
+#define I2S_FREQ_12_288				(0x2)
+#define I2S_FREQ_24_576				(0x3)
+//#define I2S_FREQ_12_288			(0x4)
+#define I2S_FREQ_16_9344			(0x5)
+#define I2S_FREQ_18_432				(0x6)
+#define I2S_FREQ_36_864				(0x7)
+#define I2S_FREQ_16_384				(0x8)
+#define I2S_FREQ_22_5792			(0x9)
+//#define I2S_FREQ_24_576			(0x10)
+#define I2S_FREQ_49_152				(0x11)
+//#define I2S_FREQ_24_576			(0x12)
+#define I2S_FREQ_33_8688			(0x13)
+//#define I2S_FREQ_36_864			(0x14)
+#define I2S_FREQ_73_728				(0x15)
+
+#define FICMMP_IDE_PWR				9
+#define FICMMP_FOCUS_RST			2
+
+static __inline void ficmmp_config_set(u16 bits)
+{
+	extern u16 ficmmp_config;
+	//printk("set_config: %X, Old: %X, New: %X\n", bits, ficmmp_config, ficmmp_config | bits);
+	ficmmp_config |= bits;
+	*((u16*)FICMMP_CONFIG_BASE) = ficmmp_config;
+}
+
+static __inline void ficmmp_config_clear(u16 bits)
+{
+	extern u16 ficmmp_config;
+//	printk("clear_config: %X, Old: %X, New: %X\n", bits, ficmmp_config, ficmmp_config & ~bits);
+	ficmmp_config &= ~bits;
+	*((u16*)FICMMP_CONFIG_BASE) = ficmmp_config;
+}
+
+static __inline void ficmmp_config_init(void)
+{
+	au1xxx_gpio_write(FICMMP_CONFIG_ENABLE, 0);	//Enable configuration latch
+	ficmmp_config_set(FICMMP_CONFIG_LCMDATAOUT | FICMMP_CONFIG_TVODATAOUT | FICMMP_CONFIG_IDERST);  //Disable display data buffers
+	ficmmp_config_set(FICMMP_CONFIG_I2SFREQ(I2S_FREQ_36_864));
+}
+
+static __inline u32 ficmmp_set_i2s_sample_rate(u32 rate)
+{
+	u32 freq;
+	
+	switch(rate)
+	{
+	case 88200: 
+	case 44100:
+	case  8018: freq = I2S_FREQ_11_2896; break;
+	case 48000:
+	case 32000: //freq = I2S_FREQ_18_432; break;
+	case  8000: freq = I2S_FREQ_12_288; break;
+	default:    freq = I2S_FREQ_12_288; rate = 8000; 
+	}
+	ficmmp_config_clear(FICMMP_CONFIG_I2SFREQ(0xF));
+	ficmmp_config_set(FICMMP_CONFIG_I2SFREQ(freq));
+	return rate;
+}
+
+#endif /* __ASM_FICMMP_H */
+
diff -Nur linux-2.4.30/include/asm-mips/hazards.h linux-2.4.30-linux-mips.org/include/asm-mips/hazards.h
--- linux-2.4.30/include/asm-mips/hazards.h	2004-02-18 14:36:32.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/hazards.h	2005-05-12 17:57:45.000000000 +0200
@@ -3,7 +3,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 2003 Ralf Baechle
+ * Copyright (C) 2003, 2004 Ralf Baechle
  */
 #ifndef _ASM_HAZARDS_H
 #define _ASM_HAZARDS_H
@@ -12,38 +12,185 @@
 
 #ifdef __ASSEMBLY__
 
+	.macro	_ssnop
+	sll	$0, $0, 1
+	.endm
+
 /*
  * RM9000 hazards.  When the JTLB is updated by tlbwi or tlbwr, a subsequent
  * use of the JTLB for instructions should not occur for 4 cpu cycles and use
  * for data translations should not occur for 3 cpu cycles.
  */
 #ifdef CONFIG_CPU_RM9000
-#define rm9000_tlb_hazard						\
+
+#define mtc0_tlbw_hazard						\
+	.set	push;							\
+	.set	mips32;							\
+	_ssnop; _ssnop; _ssnop; _ssnop;					\
+	.set	pop
+
+#define tlbw_eret_hazard						\
 	.set	push;							\
 	.set	mips32;							\
-	ssnop; ssnop; ssnop; ssnop;					\
+	_ssnop; _ssnop; _ssnop; _ssnop;					\
 	.set	pop
+
 #else
-#define rm9000_tlb_hazard
+
+/*
+ * The taken branch will result in a two cycle penalty for the two killed
+ * instructions on R4000 / R4400.  Other processors only have a single cycle
+ * hazard so this is nice trick to have an optimal code for a range of
+ * processors.
+ */
+#define mtc0_tlbw_hazard						\
+	b	. + 8
+#define tlbw_eret_hazard
 #endif
 
+/*
+ * mtc0->mfc0 hazard
+ * The 24K has a 2 cycle mtc0/mfc0 execution hazard.
+ * It is a MIPS32R2 processor so ehb will clear the hazard.
+ */
+
+#ifdef CONFIG_CPU_MIPSR2
+/*
+ * Use a macro for ehb unless explicit support for MIPSR2 is enabled
+ */
+	.macro	ehb
+	sll	$0, $0, 3
+	.endm
+
+#define irq_enable_hazard						\
+	ehb		# irq_enable_hazard
+
+#define irq_disable_hazard						\
+	ehb		# irq_disable_hazard
+
 #else
 
+#define irq_enable_hazard
+#define irq_disable_hazard
+
+#endif
+
+#else /* __ASSEMBLY__ */
+
 /*
  * RM9000 hazards.  When the JTLB is updated by tlbwi or tlbwr, a subsequent
  * use of the JTLB for instructions should not occur for 4 cpu cycles and use
  * for data translations should not occur for 3 cpu cycles.
  */
 #ifdef CONFIG_CPU_RM9000
-#define rm9000_tlb_hazard()						\
+
+#define mtc0_tlbw_hazard()						\
 	__asm__ __volatile__(						\
 		".set\tmips32\n\t"					\
-		"ssnop; ssnop; ssnop; ssnop\n\t"			\
+		"_ssnop; _ssnop; _ssnop; _ssnop\n\t"			\
+		".set\tmips0")
+
+#define tlbw_use_hazard()						\
+	__asm__ __volatile__(						\
+		".set\tmips32\n\t"					\
+		"_ssnop; _ssnop; _ssnop; _ssnop\n\t"			\
 		".set\tmips0")
 #else
-#define rm9000_tlb_hazard() do { } while (0)
+
+/*
+ * Overkill warning ...
+ */
+#define mtc0_tlbw_hazard()						\
+	__asm__ __volatile__(						\
+		".set noreorder\n\t"					\
+		"nop; nop; nop; nop; nop; nop;\n\t"			\
+		".set reorder\n\t")
+
+#define tlbw_use_hazard()						\
+	__asm__ __volatile__(						\
+		".set noreorder\n\t"					\
+		"nop; nop; nop; nop; nop; nop;\n\t"			\
+		".set reorder\n\t")
+
 #endif
 
+/*
+ * mtc0->mfc0 hazard
+ * The 24K has a 2 cycle mtc0/mfc0 execution hazard.
+ * It is a MIPS32R2 processor so ehb will clear the hazard.
+ */
+
+#ifdef CONFIG_CPU_MIPSR2
+/*
+ * Use a macro for ehb unless explicit support for MIPSR2 is enabled
+ */
+__asm__(
+	"	.macro	ehb					\n\t"
+	"	sll	$0, $0, 3				\n\t"
+	"	.endm						\n\t"
+	"							\n\t"
+	"	.macro\tirq_enable_hazard			\n\t"
+	"	ehb						\n\t"
+	"	.endm						\n\t"
+	"							\n\t"
+	"	.macro\tirq_disable_hazard			\n\t"
+	"	ehb						\n\t"
+	"	.endm");
+
+#define irq_enable_hazard()						\
+	__asm__ __volatile__(						\
+	"ehb\t\t\t\t# irq_enable_hazard")
+
+#define irq_disable_hazard()						\
+	__asm__ __volatile__(						\
+	"ehb\t\t\t\t# irq_disable_hazard")
+
+#elif defined(CONFIG_CPU_R10000)
+
+/*
+ * R10000 rocks - all hazards handled in hardware, so this becomes a nobrainer.
+ */
+
+__asm__(
+	"	.macro\tirq_enable_hazard			\n\t"
+	"	.endm						\n\t"
+	"							\n\t"
+	"	.macro\tirq_disable_hazard			\n\t"
+	"	.endm");
+
+#define irq_enable_hazard()	do { } while (0)
+#define irq_disable_hazard()	do { } while (0)
+
+#else
+
+/*
+ * Default for classic MIPS processors.  Assume worst case hazards but don't
+ * care about the irq_enable_hazard - sooner or later the hardware will
+ * enable it and we don't care when exactly.
+ */
+
+__asm__(
+	"	.macro	_ssnop					\n\t"
+	"	sll	$0, $2, 1				\n\t"
+	"	.endm						\n\t"
+	"							\n\t"
+	"	#						\n\t"
+	"	# There is a hazard but we do not care		\n\t"
+	"	#						\n\t"
+	"	.macro\tirq_enable_hazard			\n\t"
+	"	.endm						\n\t"
+	"							\n\t"
+	"	.macro\tirq_disable_hazard			\n\t"
+	"	_ssnop; _ssnop; _ssnop				\n\t"
+	"	.endm");
+
+#define irq_enable_hazard()	do { } while (0)
+#define irq_disable_hazard()						\
+	__asm__ __volatile__(						\
+	"_ssnop; _ssnop; _ssnop;\t\t# irq_disable_hazard")
+
 #endif
 
+#endif /* __ASSEMBLY__ */
+
 #endif /* _ASM_HAZARDS_H */
diff -Nur linux-2.4.30/include/asm-mips/ide.h linux-2.4.30-linux-mips.org/include/asm-mips/ide.h
--- linux-2.4.30/include/asm-mips/ide.h	2003-08-25 13:44:43.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/asm-mips/ide.h	2005-05-12 17:57:45.000000000 +0200
@@ -32,12 +32,12 @@
 
 extern struct ide_ops *ide_ops;
 
-static __inline__ int ide_default_irq(ide_ioreg_t base)
+static inline int ide_default_irq(ide_ioreg_t base)
 {
 	return ide_ops->ide_default_irq(base);
 }
 
-static __inline__ ide_ioreg_t ide_default_io_base(int index)
+static inline ide_ioreg_t ide_default_io_base(int index)
 {
 	return ide_ops->ide_default_io_base(index);
 }
@@ -48,7 +48,7 @@
 	ide_ops->ide_init_hwif_ports(hw, data_port, ctrl_port, irq);
 }
 
-static __inline__ void ide_init_default_hwifs(void)
+static inline void ide_init_default_hwifs(void)
 {
 #ifndef CONFIG_BLK_DEV_IDEPCI
 	hw_regs_t hw;
@@ -68,7 +68,89 @@
 #define ide_ack_intr(hwif)	((hwif)->hw.ack_intr ? (hwif)->hw.ack_intr(hwif) : 1)
 #endif
 
-#include <asm-generic/ide_iops.h>
+/* MIPS port and memory-mapped I/O string operations.  */
+
+static inline void __ide_flush_dcache_range(unsigned long addr, unsigned long size)
+{
+	if (cpu_has_dc_aliases) {
+		unsigned long end = addr + size;
+		for (; addr < end; addr += PAGE_SIZE)
+			flush_dcache_page(virt_to_page(addr));
+	}
+}
+
+static inline void __ide_insw(unsigned long port, void *addr,
+	unsigned int count)
+{
+	insw(port, addr, count);
+	__ide_flush_dcache_range((unsigned long)addr, count * 2);
+}
+
+static inline void __ide_insl(unsigned long port, void *addr, unsigned int count)
+{
+	insl(port, addr, count);
+	__ide_flush_dcache_range((unsigned long)addr, count * 4);
+}
+
+static inline void __ide_outsw(unsigned long port, const void *addr,
+	unsigned long count)
+{
+	outsw(port, addr, count);
+	__ide_flush_dcache_range((unsigned long)addr, count * 2);
+}
+
+static inline void __ide_outsl(unsigned long port, const void *addr,
+	unsigned long count)
+{
+	outsl(port, addr, count);
+	__ide_flush_dcache_range((unsigned long)addr, count * 4);
+}
+
+static inline void __ide_mm_insw(unsigned long port, void *addr, u32 count)
+{
+	unsigned long start = (unsigned long) addr;
+
+	while (count--) {
+		*(u16 *)addr = readw(port);
+		addr += 2;
+	}
+	__ide_flush_dcache_range(start, count * 2);
+}
+
+static inline void __ide_mm_insl(unsigned long port, void *addr, u32 count)
+{
+	unsigned long start = (unsigned long) addr;
+
+	while (count--) {
+		*(u32 *)addr = readl(port);
+		addr += 4;
+	}
+	__ide_flush_dcache_range(start, count * 4);
+}
+
+static inline void __ide_mm_outsw(unsigned long port, const void *addr,
+	u32 count)
+{
+	unsigned long start = (unsigned long) addr;
+
+	while (count--) {
+		writew(*(u16 *)addr, port);
+		addr += 2;
+	}
+	__ide_flush_dcache_range(start, count * 2);
+}
+
+static inline void __ide_mm_outsl(unsigned long port, const void *addr,
+	u32 count)
+{
+	unsigned long start = (unsigned long) addr;
+
+	while (count--) {
+		writel(*(u32 *)addr, port);
+		addr += 4;
+	}
+	__ide_flush_dcache_range(start, count * 4);
+}
 
 #endif /* __KERNEL__ */
 
diff -Nur linux-2.4.30/include/asm-mips/io.h linux-2.4.30-linux-mips.org/include/asm-mips/io.h
--- linux-2.4.30/include/asm-mips/io.h	2003-08-25 13:44:43.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/asm-mips/io.h	2005-05-12 17:57:45.000000000 +0200
@@ -392,7 +392,8 @@
 	return __ioswab32(__val);
 }
 
-static inline void __outsb(unsigned long port, void *addr, unsigned int count)
+static inline void __outsb(unsigned long port, const void *addr,
+	unsigned int count)
 {
 	while (count--) {
 		outb(*(u8 *)addr, port);
@@ -408,7 +409,8 @@
 	}
 }
 
-static inline void __outsw(unsigned long port, void *addr, unsigned int count)
+static inline void __outsw(unsigned long port, const void *addr,
+	unsigned int count)
 {
 	while (count--) {
 		outw(*(u16 *)addr, port);
@@ -424,7 +426,8 @@
 	}
 }
 
-static inline void __outsl(unsigned long port, void *addr, unsigned int count)
+static inline void __outsl(unsigned long port, const void *addr,
+	unsigned int count)
 {
 	while (count--) {
 		outl(*(u32 *)addr, port);
diff -Nur linux-2.4.30/include/asm-mips/mipsregs.h linux-2.4.30-linux-mips.org/include/asm-mips/mipsregs.h
--- linux-2.4.30/include/asm-mips/mipsregs.h	2005-01-19 15:10:12.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/mipsregs.h	2005-05-12 17:57:45.000000000 +0200
@@ -757,10 +757,18 @@
 #define read_c0_config1()	__read_32bit_c0_register($16, 1)
 #define read_c0_config2()	__read_32bit_c0_register($16, 2)
 #define read_c0_config3()	__read_32bit_c0_register($16, 3)
+#define read_c0_config4()	__read_32bit_c0_register($16, 4)
+#define read_c0_config5()	__read_32bit_c0_register($16, 5)
+#define read_c0_config6()	__read_32bit_c0_register($16, 6)
+#define read_c0_config7()	__read_32bit_c0_register($16, 7)
 #define write_c0_config(val)	__write_32bit_c0_register($16, 0, val)
 #define write_c0_config1(val)	__write_32bit_c0_register($16, 1, val)
 #define write_c0_config2(val)	__write_32bit_c0_register($16, 2, val)
 #define write_c0_config3(val)	__write_32bit_c0_register($16, 3, val)
+#define write_c0_config4(val)	__write_32bit_c0_register($16, 4, val)
+#define write_c0_config5(val)	__write_32bit_c0_register($16, 5, val)
+#define write_c0_config6(val)	__write_32bit_c0_register($16, 6, val)
+#define write_c0_config7(val)	__write_32bit_c0_register($16, 7, val)
 
 /*
  * The WatchLo register.  There may be upto 8 of them.
@@ -874,42 +882,34 @@
  */
 static inline void tlb_probe(void)
 {
-	rm9000_tlb_hazard();
 	__asm__ __volatile__(
 		".set noreorder\n\t"
 		"tlbp\n\t"
 		".set reorder");
-	rm9000_tlb_hazard();
 }
 
 static inline void tlb_read(void)
 {
-	rm9000_tlb_hazard();
 	__asm__ __volatile__(
 		".set noreorder\n\t"
 		"tlbr\n\t"
 		".set reorder");
-	rm9000_tlb_hazard();
 }
 
 static inline void tlb_write_indexed(void)
 {
-	rm9000_tlb_hazard();
 	__asm__ __volatile__(
 		".set noreorder\n\t"
 		"tlbwi\n\t"
 		".set reorder");
-	rm9000_tlb_hazard();
 }
 
 static inline void tlb_write_random(void)
 {
-	rm9000_tlb_hazard();
 	__asm__ __volatile__(
 		".set noreorder\n\t"
 		"tlbwr\n\t"
 		".set reorder");
-	rm9000_tlb_hazard();
 }
 
 /*
diff -Nur linux-2.4.30/include/asm-mips/mmu_context.h linux-2.4.30-linux-mips.org/include/asm-mips/mmu_context.h
--- linux-2.4.30/include/asm-mips/mmu_context.h	2005-01-19 15:10:12.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/mmu_context.h	2005-05-12 17:57:45.000000000 +0200
@@ -27,7 +27,7 @@
 #define TLBMISS_HANDLER_SETUP_PGD(pgd) \
 	pgd_current[smp_processor_id()] = (unsigned long)(pgd)
 #define TLBMISS_HANDLER_SETUP() \
-	write_c0_context((unsigned long) smp_processor_id() << (23 + 3)); \
+	write_c0_context((unsigned long) smp_processor_id() << 23); \
 	TLBMISS_HANDLER_SETUP_PGD(swapper_pg_dir)
 extern unsigned long pgd_current[];
 
diff -Nur linux-2.4.30/include/asm-mips/pb1100.h linux-2.4.30-linux-mips.org/include/asm-mips/pb1100.h
--- linux-2.4.30/include/asm-mips/pb1100.h	2003-08-25 13:44:44.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/asm-mips/pb1100.h	2005-05-12 17:57:45.000000000 +0200
@@ -1,5 +1,5 @@
 /*
- * Alchemy Semi PB1100 Referrence Board
+ * AMD Alchemy PB1100 Reference Boards
  *
  * Copyright 2001 MontaVista Software Inc.
  * Author: MontaVista Software, Inc.
@@ -27,55 +27,108 @@
 #ifndef __ASM_PB1100_H
 #define __ASM_PB1100_H
 
-#define PB1100_IDENT          0xAE000000
-#define BOARD_STATUS_REG      0xAE000004
-  #define PB1100_ROM_SEL         (1<<15)
-  #define PB1100_ROM_SIZ         (1<<14)
-  #define PB1100_SWAP_BOOT       (1<<13)
-  #define PB1100_FLASH_WP        (1<<12)
-  #define PB1100_ROM_H_STS       (1<<11)
-  #define PB1100_ROM_L_STS       (1<<10)
-  #define PB1100_FLASH_H_STS      (1<<9)
-  #define PB1100_FLASH_L_STS      (1<<8)
-  #define PB1100_SRAM_SIZ         (1<<7)
-  #define PB1100_TSC_BUSY         (1<<6)
-  #define PB1100_PCMCIA_VS_MASK   (3<<4)
-  #define PB1100_RS232_CD         (1<<3)
-  #define PB1100_RS232_CTS        (1<<2)
-  #define PB1100_RS232_DSR        (1<<1)
-  #define PB1100_RS232_RI         (1<<0)
-
-#define PB1100_IRDA_RS232     0xAE00000C
-  #define PB1100_IRDA_FULL       (0<<14) /* full power */
-  #define PB1100_IRDA_SHUTDOWN   (1<<14)
-  #define PB1100_IRDA_TT         (2<<14) /* 2/3 power */
-  #define PB1100_IRDA_OT         (3<<14) /* 1/3 power */
-  #define PB1100_IRDA_FIR        (1<<13)
-
-#define PCMCIA_BOARD_REG     0xAE000010
-  #define PB1100_SD_WP1_RO       (1<<15) /* read only */
-  #define PB1100_SD_WP0_RO       (1<<14) /* read only */
-  #define PB1100_SD_PWR1         (1<<11) /* applies power to SD1 */
-  #define PB1100_SD_PWR0         (1<<10) /* applies power to SD0 */
-  #define PB1100_SEL_SD_CONN1     (1<<9)
-  #define PB1100_SEL_SD_CONN0     (1<<8)
-  #define PC_DEASSERT_RST         (1<<7)
-  #define PC_DRV_EN               (1<<4)
-
-#define PB1100_G_CONTROL      0xAE000014 /* graphics control */
-
-#define PB1100_RST_VDDI       0xAE00001C
-  #define PB1100_SOFT_RESET      (1<<15) /* clear to reset the board */
-  #define PB1100_VDDI_MASK        (0x1F)
+#define BCSR_KSEG1_ADDR 0xAE000000
+
+/*
+ * Overlay data structure of the Pb1100 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 graphics; 
+			unsigned short reserved5;
+	/*18*/	unsigned short leds;
+			unsigned short reserved6;
+	/*1C*/	unsigned short swreset;
+			unsigned short reserved7;
+
+} BCSR;
 
-#define PB1100_LEDS           0xAE000018
 
-/* 11:8 is 4 discreet LEDs. Clearing a bit illuminates the LED.
- * 7:0 is the LED Display's decimal points.
+/*
+ * Register/mask bit definitions for the BCSRs
  */
-#define PB1100_HEX_LED        0xAE000018
+#define BCSR_WHOAMI_DCID		0x000F	
+#define BCSR_WHOAMI_CPLD		0x00F0
+#define BCSR_WHOAMI_BOARD		0x0F00 
+
+#define BCSR_STATUS_RS232_RI	    	0x0001 
+#define BCSR_STATUS_RS232_DSR	 	0x0002 
+#define BCSR_STATUS_RS232_CTS    	0x0004	
+#define BCSR_STATUS_RS232_CD	   	0x0008	 
+#define BCSR_STATUS_PCMCIA_VS_MASK  	0x0030 
+#define BCSR_STATUS_TSC_BUSY        	0x0040 
+#define BCSR_STATUS_SRAM_SIZ		0x0080 
+#define BCSR_STATUS_FLASH_L_STS 	0x0100 
+#define BCSR_STATUS_FLASH_H_STS 	0x0200	
+#define BCSR_STATUS_ROM_H_STS   	0x0400 
+#define BCSR_STATUS_ROM_L_STS   	0x0800	
+#define BCSR_STATUS_FLASH_WP	    	0x1000 
+#define BCSR_STATUS_SWAP_BOOT		0x2000
+#define BCSR_STATUS_ROM_SIZ    		0x4000 
+#define BCSR_STATUS_ROM_SEL      	0x8000	
+
+#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_SWITCHES_SDO_CL     	0x8000
+
+#define BCSR_RESETS_PHY0		0x0001
+#define BCSR_RESETS_PHY1		0x0002
+#define BCSR_RESETS_DC			0x0004
+#define BCSR_RESETS_RS232_RTS		0x0100
+#define BCSR_RESETS_RS232_DTR   	0x0200
+#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_PC0_DR_VEN		0x0010
+#define BCSR_PCMCIA_PC0RST		0x0080
+#define BCSR_PCMCIA_SEL_SD_CON0   	0x0100
+#define BCSR_PCMCIA_SEL_SD_CON1   	0x0200
+#define BCSR_PCMCIA_SD0_PWR		0x0400
+#define BCSR_PCMCIA_SD1_PWR		0x0800
+#define BCSR_PCMCIA_SD0_WP		0x4000
+#define BCSR_PCMCIA_SD1_WP		0x8000
+
+#define PB1100_G_CONTROL		0xAE000014
+#define BCSR_GRAPHICS_GPX_SMPASS    	0x0010
+#define BCSR_GRAPHICS_GPX_BIG_ENDIAN	0x0020
+#define BCSR_GRAPHICS_GPX_RST		0x0040
+
+#define BCSR_LEDS_DECIMALS		0x00FF
+#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
+#define BCSR_VDDI_VDI			0x001F
 
-/* PCMCIA PB1100 specific defines */
+
+ /* PCMCIA Pb1x00 specific defines */
 #define PCMCIA_MAX_SOCK 0
 #define PCMCIA_NUM_SOCKS (PCMCIA_MAX_SOCK+1)
 
@@ -83,3 +136,4 @@
 #define SET_VCC_VPP(VCC, VPP) (((VCC)<<2) | ((VPP)<<0))
 
 #endif /* __ASM_PB1100_H */
+
diff -Nur linux-2.4.30/include/asm-mips/pb1200.h linux-2.4.30-linux-mips.org/include/asm-mips/pb1200.h
--- linux-2.4.30/include/asm-mips/pb1200.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/pb1200.h	2005-05-12 17:57:45.000000000 +0200
@@ -0,0 +1,244 @@
+/*
+ * AMD Alchemy PB1200 Referrence Board
+ * Board Registers defines.
+ *
+ * ########################################################################
+ *
+ *  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_PB1200_H
+#define __ASM_PB1200_H
+
+#include <linux/types.h>
+
+// This is defined in au1000.h with bogus value
+#undef AU1X00_EXTERNAL_INT
+
+#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_PSC1_TX
+#define DBDMA_I2S_RX_CHAN DSCR_CMD0_PSC1_RX
+
+/* SPI and SMB are muxed on the Pb1200 board.
+   Refer to board documentation.
+ */
+#define SPI_PSC_BASE        PSC0_BASE_ADDR
+#define SMBUS_PSC_BASE      PSC0_BASE_ADDR
+/* AC97 and I2S are muxed on the Pb1200 board.
+   Refer to board documentation.
+ */
+#define AC97_PSC_BASE       PSC1_BASE_ADDR
+#define I2S_PSC_BASE		PSC1_BASE_ADDR
+
+#define BCSR_KSEG1_ADDR 0xAD800000
+
+typedef volatile struct
+{
+	/*00*/	u16 whoami;
+		u16 reserved0;
+	/*04*/	u16 status;
+		u16 reserved1;
+	/*08*/	u16 switches;
+		u16 reserved2;
+	/*0C*/	u16 resets;
+		u16 reserved3;
+
+	/*10*/	u16 pcmcia;
+		u16 reserved4;
+	/*14*/	u16 board;
+		u16 reserved5;
+	/*18*/	u16 disk_leds;
+		u16 reserved6;
+	/*1C*/	u16 system;
+		u16 reserved7;
+
+	/*20*/	u16 intclr;
+		u16 reserved8;
+	/*24*/	u16 intset;
+		u16 reserved9;
+	/*28*/	u16 intclr_mask;
+		u16 reserved10;
+	/*2C*/	u16 intset_mask;
+		u16 reserved11;
+
+	/*30*/	u16 sig_status;
+		u16 reserved12;
+	/*34*/	u16 int_status;
+		u16 reserved13;
+	/*38*/	u16 reserved14;
+		u16 reserved15;
+	/*3C*/	u16 reserved16;
+		u16 reserved17;
+
+} BCSR;
+
+static BCSR * const bcsr = (BCSR *)BCSR_KSEG1_ADDR;
+
+/*
+ * Register bit definitions for the BCSRs
+ */
+#define BCSR_WHOAMI_DCID	0x000F
+#define BCSR_WHOAMI_CPLD	0x00F0
+#define BCSR_WHOAMI_BOARD	0x0F00
+
+#define BCSR_STATUS_PCMCIA0VS	0x0003
+#define BCSR_STATUS_PCMCIA1VS	0x000C
+#define BCSR_STATUS_SWAPBOOT	0x0040
+#define BCSR_STATUS_FLASHBUSY	0x0100
+#define BCSR_STATUS_IDECBLID	0x0200
+#define BCSR_STATUS_SD0WP		0x0400
+#define BCSR_STATUS_SD1WP		0x0800
+#define BCSR_STATUS_U0RXD		0x1000
+#define BCSR_STATUS_U1RXD		0x2000
+
+#define BCSR_SWITCHES_OCTAL	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_ETH		0x0001
+#define BCSR_RESETS_CAMERA	0x0002
+#define BCSR_RESETS_DC		0x0004
+#define BCSR_RESETS_IDE		0x0008
+/* not resets but in the same register */
+#define BCSR_RESETS_WSCFSM  0x0800
+#define BCSR_RESETS_PCS0MUX	0x1000
+#define BCSR_RESETS_PCS1MUX	0x2000
+#define BCSR_RESETS_SPISEL	0x4000
+#define BCSR_RESETS_SD1MUX  0x8000
+
+#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_LCDVEE	0x0001
+#define BCSR_BOARD_LCDVDD	0x0002
+#define BCSR_BOARD_LCDBL	0x0004
+#define BCSR_BOARD_CAMSNAP	0x0010
+#define BCSR_BOARD_CAMPWR	0x0020
+#define BCSR_BOARD_SD0PWR	0x0040
+#define BCSR_BOARD_SD1PWR	0x0080
+
+#define BCSR_LEDS_DECIMALS	0x00FF
+#define BCSR_LEDS_LED0		0x0100
+#define BCSR_LEDS_LED1		0x0200
+#define BCSR_LEDS_LED2		0x0400
+#define BCSR_LEDS_LED3		0x0800
+
+#define BCSR_SYSTEM_VDDI	0x001F
+#define BCSR_SYSTEM_POWEROFF	0x4000
+#define BCSR_SYSTEM_RESET	0x8000
+
+/* Bit positions for the different interrupt sources */
+#define BCSR_INT_IDE		0x0001
+#define BCSR_INT_ETH		0x0002
+#define BCSR_INT_PC0		0x0004
+#define BCSR_INT_PC0STSCHG	0x0008
+#define BCSR_INT_PC1		0x0010
+#define BCSR_INT_PC1STSCHG	0x0020
+#define BCSR_INT_DC			0x0040
+#define BCSR_INT_FLASHBUSY	0x0080
+#define BCSR_INT_PC0INSERT	0x0100
+#define BCSR_INT_PC0EJECT	0x0200
+#define BCSR_INT_PC1INSERT	0x0400
+#define BCSR_INT_PC1EJECT	0x0800
+#define BCSR_INT_SD0INSERT	0x1000
+#define BCSR_INT_SD0EJECT	0x2000
+#define BCSR_INT_SD1INSERT	0x4000
+#define BCSR_INT_SD1EJECT	0x8000
+
+#define AU1XXX_SMC91111_PHYS_ADDR	(0x0D000300)
+#define AU1XXX_SMC91111_IRQ			PB1200_ETH_INT
+
+#define AU1XXX_ATA_PHYS_ADDR		(0x0C800000)
+#define AU1XXX_ATA_PHYS_LEN			(0x100)
+#define AU1XXX_ATA_REG_OFFSET	(5)
+#define AU1XXX_ATA_INT			PB1200_IDE_INT
+#define AU1XXX_ATA_DDMA_REQ		DSCR_CMD0_DMA_REQ1;
+#define AU1XXX_ATA_RQSIZE		128
+
+#define NAND_PHYS_ADDR   0x1C000000
+
+/* Timing values as described in databook, * ns value stripped of
+ * lower 2 bits.
+ * These defines are here rather than an SOC1200 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)
+
+
+/*
+ *	External Interrupts for Pb1200 as of 8/6/2004.
+ *   Bit positions in the CPLD registers can be calculated by taking
+ *   the interrupt define and subtracting the PB1200_INT_BEGIN value.
+ *    *example: IDE bis pos is  = 64 - 64
+                ETH bit pos is  = 65 - 64
+ */
+#define PB1200_INT_BEGIN		(AU1000_LAST_INTC1_INT + 1)
+#define PB1200_IDE_INT			(PB1200_INT_BEGIN + 0)
+#define PB1200_ETH_INT			(PB1200_INT_BEGIN + 1)
+#define PB1200_PC0_INT			(PB1200_INT_BEGIN + 2)
+#define PB1200_PC0_STSCHG_INT	(PB1200_INT_BEGIN + 3)
+#define PB1200_PC1_INT			(PB1200_INT_BEGIN + 4)
+#define PB1200_PC1_STSCHG_INT	(PB1200_INT_BEGIN + 5)
+#define PB1200_DC_INT			(PB1200_INT_BEGIN + 6)
+#define PB1200_FLASHBUSY_INT	(PB1200_INT_BEGIN + 7)
+#define PB1200_PC0_INSERT_INT	(PB1200_INT_BEGIN + 8)
+#define PB1200_PC0_EJECT_INT	(PB1200_INT_BEGIN + 9)
+#define PB1200_PC1_INSERT_INT	(PB1200_INT_BEGIN + 10)
+#define PB1200_PC1_EJECT_INT	(PB1200_INT_BEGIN + 11)
+#define PB1200_SD0_INSERT_INT	(PB1200_INT_BEGIN + 12)
+#define PB1200_SD0_EJECT_INT	(PB1200_INT_BEGIN + 13)
+#define PB1200_SD1_INSERT_INT	(PB1200_INT_BEGIN + 14)
+#define PB1200_SD1_EJECT_INT	(PB1200_INT_BEGIN + 15)
+
+#define PB1200_INT_END			(PB1200_INT_BEGIN + 15)
+
+/* For drivers/pcmcia/au1000_db1x00.c */
+#define BOARD_PC0_INT PB1200_PC0_INT
+#define BOARD_PC1_INT PB1200_PC1_INT
+#define BOARD_CARD_INSERTED(SOCKET) bcsr->sig_status & (1<<(8+(2*SOCKET)))
+
+#endif /* __ASM_PB1200_H */
+
diff -Nur linux-2.4.30/include/asm-mips/pb1550.h linux-2.4.30-linux-mips.org/include/asm-mips/pb1550.h
--- linux-2.4.30/include/asm-mips/pb1550.h	2005-01-19 15:10:12.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/pb1550.h	2005-05-12 17:57:45.000000000 +0200
@@ -30,13 +30,11 @@
 
 #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
+#define NAND_CS 1
 
 #define BCSR_PHYS_ADDR 0xAF000000
 
@@ -160,9 +158,23 @@
 #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)
+#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)
+
+/*
+ * Daughter card information.
+ */
+#define DAUGHTER_CARD_BASE		(0xAC000000)
+#define DAUGHTER_CARD_MEM_SIZE		(0xADFFFFFF - DAUGHTER_CARD_BASE + 1)
+#define DAUGHTER_CARD_IRQ		(AU1000_GPIO_3)
+
+/* DC_IDE and DC_ETHERNET */
+#define AU1XXX_ATA_PHYS_ADDR		(0x0C000000)
+#define AU1XXX_ATA_REG_OFFSET		(5)	
+
+#define AU1XXX_SMC91111_PHYS_ADDR	(0x0C000300)
+#define AU1XXX_SMC91111_IRQ		AU1000_GPIO_3
 
 #endif /* __ASM_PB1550_H */
diff -Nur linux-2.4.30/include/asm-mips/reg.h linux-2.4.30-linux-mips.org/include/asm-mips/reg.h
--- linux-2.4.30/include/asm-mips/reg.h	2002-08-03 02:39:45.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/asm-mips/reg.h	2005-05-12 17:57:45.000000000 +0200
@@ -45,6 +45,9 @@
 /*
  * k0/k1 unsaved
  */
+#define EF_REG26		32
+#define EF_REG27		33
+
 #define EF_REG28		34
 #define EF_REG29		35
 #define EF_REG30		36
@@ -60,6 +63,7 @@
 #define EF_CP0_BADVADDR		41
 #define EF_CP0_STATUS		42
 #define EF_CP0_CAUSE		43
+#define EF_UNUSED0		44
 
 #define EF_SIZE			180	/* size in bytes */
 
diff -Nur linux-2.4.30/include/asm-mips/tx4927/tx4927.h linux-2.4.30-linux-mips.org/include/asm-mips/tx4927/tx4927.h
--- linux-2.4.30/include/asm-mips/tx4927/tx4927.h	2003-08-25 13:44:44.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/asm-mips/tx4927/tx4927.h	2005-05-12 17:57:45.000000000 +0200
@@ -88,8 +88,8 @@
 
 
 /* TX4927 Configuration registers (64-bit registers) */
-#define TX4927_CONFIG_BASE                       0xe300
-#define TX4927_CONFIG_CCFG                       0xe300
+#define TX4927_CONFIG_BASE                       0xe000
+#define TX4927_CONFIG_CCFG                       0xe000
 #define TX4927_CONFIG_CCFG_RESERVED_42_63                BM_63_42
 #define TX4927_CONFIG_CCFG_WDRST                         BM_41_41
 #define TX4927_CONFIG_CCFG_WDREXEN                       BM_40_40
@@ -124,14 +124,14 @@
 #define TX4927_CONFIG_CCFG_ENDIAN                        BM_02_02
 #define TX4927_CONFIG_CCFG_ARMODE                        BM_01_01
 #define TX4927_CONFIG_CCFG_ACEHOLD                       BM_00_00
-#define TX4927_CONFIG_REVID                      0xe308 
+#define TX4927_CONFIG_REVID                      0xe008 
 #define TX4927_CONFIG_REVID_RESERVED_32_63               BM_32_63
 #define TX4927_CONFIG_REVID_PCODE                        BM_16_31
 #define TX4927_CONFIG_REVID_MJERREV                      BM_12_15
 #define TX4927_CONFIG_REVID_MINEREV                      BM_08_11
 #define TX4927_CONFIG_REVID_MJREV                        BM_04_07
 #define TX4927_CONFIG_REVID_MINREV                       BM_00_03
-#define TX4927_CONFIG_PCFG                       0xe310 
+#define TX4927_CONFIG_PCFG                       0xe010 
 #define TX4927_CONFIG_PCFG_RESERVED_57_63                BM_57_63
 #define TX4927_CONFIG_PCFG_DRVDATA                       BM_56_56
 #define TX4927_CONFIG_PCFG_DRVCB                         BM_55_55
@@ -197,10 +197,10 @@
 #define TX4927_CONFIG_PCFG_DMASEL0_SIO1                  BM_00_00
 #define TX4927_CONFIG_PCFG_DMASEL0_ACLC0                 BM_01_01
 #define TX4927_CONFIG_PCFG_DMASEL0_ACLC2                 BM_00_01
-#define TX4927_CONFIG_TOEA                       0xe318 
+#define TX4927_CONFIG_TOEA                       0xe018 
 #define TX4927_CONFIG_TOEA_RESERVED_36_63                BM_36_63
 #define TX4927_CONFIG_TOEA_TOEA                          BM_00_35
-#define TX4927_CONFIG_CLKCTR                     0xe320 
+#define TX4927_CONFIG_CLKCTR                     0xe020 
 #define TX4927_CONFIG_CLKCTR_RESERVED_26_63              BM_26_63
 #define TX4927_CONFIG_CLKCTR_ACLCKD                      BM_25_25
 #define TX4927_CONFIG_CLKCTR_PIOCKD                      BM_24_24
@@ -223,7 +223,7 @@
 #define TX4927_CONFIG_CLKCTR_TM2RST                      BM_02_02
 #define TX4927_CONFIG_CLKCTR_SIO0RST                     BM_01_01
 #define TX4927_CONFIG_CLKCTR_SIO1RST                     BM_00_00
-#define TX4927_CONFIG_GARBC                      0xe330 
+#define TX4927_CONFIG_GARBC                      0xe030 
 #define TX4927_CONFIG_GARBC_RESERVED_10_63               BM_10_63
 #define TX4927_CONFIG_GARBC_SET_09                       BM_09_09
 #define TX4927_CONFIG_GARBC_ARBMD                        BM_08_08
@@ -243,7 +243,7 @@
 #define TX4927_CONFIG_GARBC_PRIORITY_H3_PDMAC            BM_00_00
 #define TX4927_CONFIG_GARBC_PRIORITY_H3_DMAC             BM_01_01
 #define TX4927_CONFIG_GARBC_PRIORITY_H3_BAD_VALUE        BM_00_01
-#define TX4927_CONFIG_RAMP                       0xe348 
+#define TX4927_CONFIG_RAMP                       0xe048 
 #define TX4927_CONFIG_RAMP_RESERVED_20_63                BM_20_63
 #define TX4927_CONFIG_RAMP_RAMP                          BM_00_19
 #define TX4927_CONFIG_LIMIT                      0xefff
@@ -456,7 +456,7 @@
 #define TX4927_ACLC_ACINTSTS            0xf710
 #define TX4927_ACLC_ACINTMSTS           0xf714
 #define TX4927_ACLC_ACINTEN             0xf718
-#define TX4927_ACLC_ACINTDIS            0xfR71c
+#define TX4927_ACLC_ACINTDIS            0xf71c
 #define TX4927_ACLC_ACSEMAPH            0xf720
 #define TX4927_ACLC_ACGPIDAT            0xf740
 #define TX4927_ACLC_ACGPODAT            0xf744
diff -Nur linux-2.4.30/include/asm-mips/unistd.h linux-2.4.30-linux-mips.org/include/asm-mips/unistd.h
--- linux-2.4.30/include/asm-mips/unistd.h	2005-01-19 15:10:12.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips/unistd.h	2005-05-12 17:57:45.000000000 +0200
@@ -760,7 +760,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 /*
@@ -788,7 +788,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #define _syscall2(type,name,atype,a,btype,b) \
@@ -813,7 +813,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #define _syscall3(type,name,atype,a,btype,b,ctype,c) \
@@ -839,7 +839,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #define _syscall4(type,name,atype,a,btype,b,ctype,c,dtype,d) \
@@ -865,7 +865,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #if (_MIPS_SIM == _MIPS_SIM_ABI32)
@@ -902,7 +902,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #define _syscall6(type,name,atype,a,btype,b,ctype,c,dtype,d,etype,e,ftype,f) \
@@ -935,7 +935,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #endif /* (_MIPS_SIM == _MIPS_SIM_ABI32) */
@@ -966,7 +966,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #define _syscall6(type,name,atype,a,btype,b,ctype,c,dtype,d,etype,e,ftype,f) \
@@ -995,7 +995,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #endif /* (_MIPS_SIM == _MIPS_SIM_NABI32) || (_MIPS_SIM == _MIPS_SIM_ABI64) */
diff -Nur linux-2.4.30/include/asm-mips64/elf.h linux-2.4.30-linux-mips.org/include/asm-mips64/elf.h
--- linux-2.4.30/include/asm-mips64/elf.h	2004-02-18 14:36:32.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips64/elf.h	2005-05-12 17:57:47.000000000 +0200
@@ -64,9 +64,10 @@
 #define USE_ELF_CORE_DUMP
 #define ELF_EXEC_PAGESIZE	PAGE_SIZE
 
-#define ELF_CORE_COPY_REGS(_dest,_regs)				\
-	memcpy((char *) &_dest, (char *) _regs,			\
-	       sizeof(struct pt_regs));
+extern void dump_regs(elf_greg_t *, struct pt_regs *regs);
+
+#define ELF_CORE_COPY_REGS(elf_regs, regs)			\
+	dump_regs((elf_greg_t *)&(elf_regs), regs);
 
 /* This yields a mask that user programs can use to figure out what
    instruction set this cpu supports.  This could be done in userspace,
diff -Nur linux-2.4.30/include/asm-mips64/hazards.h linux-2.4.30-linux-mips.org/include/asm-mips64/hazards.h
--- linux-2.4.30/include/asm-mips64/hazards.h	2004-02-18 14:36:32.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips64/hazards.h	2005-05-12 17:57:47.000000000 +0200
@@ -3,7 +3,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  *
- * Copyright (C) 2003 Ralf Baechle
+ * Copyright (C) 2003, 2004 Ralf Baechle
  */
 #ifndef _ASM_HAZARDS_H
 #define _ASM_HAZARDS_H
@@ -12,37 +12,185 @@
 
 #ifdef __ASSEMBLY__
 
+	.macro	_ssnop
+	sll	$0, $0, 1
+	.endm
+
 /*
  * RM9000 hazards.  When the JTLB is updated by tlbwi or tlbwr, a subsequent
  * use of the JTLB for instructions should not occur for 4 cpu cycles and use
  * for data translations should not occur for 3 cpu cycles.
  */
 #ifdef CONFIG_CPU_RM9000
-#define rm9000_tlb_hazard						\
+
+#define mtc0_tlbw_hazard						\
+	.set	push;							\
+	.set	mips32;							\
+	_ssnop; _ssnop; _ssnop; _ssnop;					\
+	.set	pop
+
+#define tlbw_eret_hazard						\
+	.set	push;							\
 	.set	mips32;							\
-	ssnop; ssnop; ssnop; ssnop;					\
-	.set	mips0
+	_ssnop; _ssnop; _ssnop; _ssnop;					\
+	.set	pop
+
 #else
-#define rm9000_tlb_hazard
+
+/*
+ * The taken branch will result in a two cycle penalty for the two killed
+ * instructions on R4000 / R4400.  Other processors only have a single cycle
+ * hazard so this is nice trick to have an optimal code for a range of
+ * processors.
+ */
+#define mtc0_tlbw_hazard						\
+	b	. + 8
+#define tlbw_eret_hazard
 #endif
 
+/*
+ * mtc0->mfc0 hazard
+ * The 24K has a 2 cycle mtc0/mfc0 execution hazard.
+ * It is a MIPS32R2 processor so ehb will clear the hazard.
+ */
+
+#ifdef CONFIG_CPU_MIPSR2
+/*
+ * Use a macro for ehb unless explicit support for MIPSR2 is enabled
+ */
+	.macro	ehb
+	sll	$0, $0, 3
+	.endm
+
+#define irq_enable_hazard						\
+	ehb		# irq_enable_hazard
+
+#define irq_disable_hazard						\
+	ehb		# irq_disable_hazard
+
 #else
 
+#define irq_enable_hazard
+#define irq_disable_hazard
+
+#endif
+
+#else /* __ASSEMBLY__ */
+
 /*
  * RM9000 hazards.  When the JTLB is updated by tlbwi or tlbwr, a subsequent
  * use of the JTLB for instructions should not occur for 4 cpu cycles and use
  * for data translations should not occur for 3 cpu cycles.
  */
 #ifdef CONFIG_CPU_RM9000
-#define rm9000_tlb_hazard()						\
+
+#define mtc0_tlbw_hazard()						\
+	__asm__ __volatile__(						\
+		".set\tmips32\n\t"					\
+		"_ssnop; _ssnop; _ssnop; _ssnop\n\t"			\
+		".set\tmips0")
+
+#define tlbw_use_hazard()						\
 	__asm__ __volatile__(						\
 		".set\tmips32\n\t"					\
-		"ssnop; ssnop; ssnop; ssnop\n\t"			\
+		"_ssnop; _ssnop; _ssnop; _ssnop\n\t"			\
 		".set\tmips0")
 #else
-#define rm9000_tlb_hazard() do { } while (0)
+
+/*
+ * Overkill warning ...
+ */
+#define mtc0_tlbw_hazard()						\
+	__asm__ __volatile__(						\
+		".set noreorder\n\t"					\
+		"nop; nop; nop; nop; nop; nop;\n\t"			\
+		".set reorder\n\t")
+
+#define tlbw_use_hazard()						\
+	__asm__ __volatile__(						\
+		".set noreorder\n\t"					\
+		"nop; nop; nop; nop; nop; nop;\n\t"			\
+		".set reorder\n\t")
+
 #endif
 
+/*
+ * mtc0->mfc0 hazard
+ * The 24K has a 2 cycle mtc0/mfc0 execution hazard.
+ * It is a MIPS32R2 processor so ehb will clear the hazard.
+ */
+
+#ifdef CONFIG_CPU_MIPSR2
+/*
+ * Use a macro for ehb unless explicit support for MIPSR2 is enabled
+ */
+__asm__(
+	"	.macro	ehb					\n\t"
+	"	sll	$0, $0, 3				\n\t"
+	"	.endm						\n\t"
+	"							\n\t"
+	"	.macro\tirq_enable_hazard			\n\t"
+	"	ehb						\n\t"
+	"	.endm						\n\t"
+	"							\n\t"
+	"	.macro\tirq_disable_hazard			\n\t"
+	"	ehb						\n\t"
+	"	.endm");
+
+#define irq_enable_hazard()						\
+	__asm__ __volatile__(						\
+	"ehb\t\t\t\t# irq_enable_hazard")
+
+#define irq_disable_hazard()						\
+	__asm__ __volatile__(						\
+	"ehb\t\t\t\t# irq_disable_hazard")
+
+#elif defined(CONFIG_CPU_R10000)
+
+/*
+ * R10000 rocks - all hazards handled in hardware, so this becomes a nobrainer.
+ */
+
+__asm__(
+	"	.macro\tirq_enable_hazard			\n\t"
+	"	.endm						\n\t"
+	"							\n\t"
+	"	.macro\tirq_disable_hazard			\n\t"
+	"	.endm");
+
+#define irq_enable_hazard()	do { } while (0)
+#define irq_disable_hazard()	do { } while (0)
+
+#else
+
+/*
+ * Default for classic MIPS processors.  Assume worst case hazards but don't
+ * care about the irq_enable_hazard - sooner or later the hardware will
+ * enable it and we don't care when exactly.
+ */
+
+__asm__(
+	"	.macro	_ssnop					\n\t"
+	"	sll	$0, $2, 1				\n\t"
+	"	.endm						\n\t"
+	"							\n\t"
+	"	#						\n\t"
+	"	# There is a hazard but we do not care		\n\t"
+	"	#						\n\t"
+	"	.macro\tirq_enable_hazard			\n\t"
+	"	.endm						\n\t"
+	"							\n\t"
+	"	.macro\tirq_disable_hazard			\n\t"
+	"	_ssnop; _ssnop; _ssnop				\n\t"
+	"	.endm");
+
+#define irq_enable_hazard()	do { } while (0)
+#define irq_disable_hazard()						\
+	__asm__ __volatile__(						\
+	"_ssnop; _ssnop; _ssnop;\t\t# irq_disable_hazard")
+
 #endif
 
+#endif /* __ASSEMBLY__ */
+
 #endif /* _ASM_HAZARDS_H */
diff -Nur linux-2.4.30/include/asm-mips64/ide.h linux-2.4.30-linux-mips.org/include/asm-mips64/ide.h
--- linux-2.4.30/include/asm-mips64/ide.h	2003-08-25 13:44:44.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/asm-mips64/ide.h	2005-05-12 17:57:47.000000000 +0200
@@ -32,12 +32,12 @@
 
 extern struct ide_ops *ide_ops;
 
-static __inline__ int ide_default_irq(ide_ioreg_t base)
+static inline int ide_default_irq(ide_ioreg_t base)
 {
 	return ide_ops->ide_default_irq(base);
 }
 
-static __inline__ ide_ioreg_t ide_default_io_base(int index)
+static inline ide_ioreg_t ide_default_io_base(int index)
 {
 	return ide_ops->ide_default_io_base(index);
 }
@@ -48,7 +48,7 @@
 	ide_ops->ide_init_hwif_ports(hw, data_port, ctrl_port, irq);
 }
 
-static __inline__ void ide_init_default_hwifs(void)
+static inline void ide_init_default_hwifs(void)
 {
 #ifndef CONFIG_BLK_DEV_IDEPCI
 	hw_regs_t hw;
@@ -68,7 +68,89 @@
 #define ide_ack_intr(hwif)    ((hwif)->hw.ack_intr ? (hwif)->hw.ack_intr(hwif) : 1)
 #endif
 
-#include <asm-generic/ide_iops.h>
+/* MIPS port and memory-mapped I/O string operations.  */
+
+static inline void __ide_flush_dcache_range(unsigned long addr, unsigned long size)
+{
+	if (cpu_has_dc_aliases) {
+		unsigned long end = addr + size;
+		for (; addr < end; addr += PAGE_SIZE)
+			flush_dcache_page(virt_to_page(addr));
+	}
+}
+
+static inline void __ide_insw(unsigned long port, void *addr,
+	unsigned int count)
+{
+	insw(port, addr, count);
+	__ide_flush_dcache_range((unsigned long)addr, count * 2);
+}
+
+static inline void __ide_insl(unsigned long port, void *addr, unsigned int count)
+{
+	insl(port, addr, count);
+	__ide_flush_dcache_range((unsigned long)addr, count * 4);
+}
+
+static inline void __ide_outsw(unsigned long port, const void *addr,
+	unsigned long count)
+{
+	outsw(port, addr, count);
+	__ide_flush_dcache_range((unsigned long)addr, count * 2);
+}
+
+static inline void __ide_outsl(unsigned long port, const void *addr,
+	unsigned long count)
+{
+	outsl(port, addr, count);
+	__ide_flush_dcache_range((unsigned long)addr, count * 4);
+}
+
+static inline void __ide_mm_insw(unsigned long port, void *addr, u32 count)
+{
+	unsigned long start = (unsigned long) addr;
+
+	while (count--) {
+		*(u16 *)addr = readw(port);
+		addr += 2;
+	}
+	__ide_flush_dcache_range(start, count * 2);
+}
+
+static inline void __ide_mm_insl(unsigned long port, void *addr, u32 count)
+{
+	unsigned long start = (unsigned long) addr;
+
+	while (count--) {
+		*(u32 *)addr = readl(port);
+		addr += 4;
+	}
+	__ide_flush_dcache_range(start, count * 4);
+}
+
+static inline void __ide_mm_outsw(unsigned long port, const void *addr,
+	u32 count)
+{
+	unsigned long start = (unsigned long) addr;
+
+	while (count--) {
+		writew(*(u16 *)addr, port);
+		addr += 2;
+	}
+	__ide_flush_dcache_range(start, count * 2);
+}
+
+static inline void __ide_mm_outsl(unsigned long port, const void *addr,
+	u32 count)
+{
+	unsigned long start = (unsigned long) addr;
+
+	while (count--) {
+		writel(*(u32 *)addr, port);
+		addr += 4;
+	}
+	__ide_flush_dcache_range(start, count * 4);
+}
 
 #endif /* __KERNEL__ */
 
diff -Nur linux-2.4.30/include/asm-mips64/io.h linux-2.4.30-linux-mips.org/include/asm-mips64/io.h
--- linux-2.4.30/include/asm-mips64/io.h	2004-02-18 14:36:32.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips64/io.h	2005-05-12 17:57:47.000000000 +0200
@@ -414,7 +414,8 @@
 	return __ioswab32(__val);
 }
 
-static inline void __outsb(unsigned long port, void *addr, unsigned int count)
+static inline void __outsb(unsigned long port, const void *addr,
+	unsigned int count)
 {
 	while (count--) {
 		outb(*(u8 *)addr, port);
@@ -430,7 +431,8 @@
 	}
 }
 
-static inline void __outsw(unsigned long port, void *addr, unsigned int count)
+static inline void __outsw(unsigned long port, const void *addr,
+	unsigned int count)
 {
 	while (count--) {
 		outw(*(u16 *)addr, port);
@@ -446,7 +448,8 @@
 	}
 }
 
-static inline void __outsl(unsigned long port, void *addr, unsigned int count)
+static inline void __outsl(unsigned long port, const void *addr,
+	unsigned int count)
 {
 	while (count--) {
 		outl(*(u32 *)addr, port);
diff -Nur linux-2.4.30/include/asm-mips64/mipsregs.h linux-2.4.30-linux-mips.org/include/asm-mips64/mipsregs.h
--- linux-2.4.30/include/asm-mips64/mipsregs.h	2005-01-19 15:10:12.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips64/mipsregs.h	2005-05-12 17:57:47.000000000 +0200
@@ -757,10 +757,18 @@
 #define read_c0_config1()	__read_32bit_c0_register($16, 1)
 #define read_c0_config2()	__read_32bit_c0_register($16, 2)
 #define read_c0_config3()	__read_32bit_c0_register($16, 3)
+#define read_c0_config4()	__read_32bit_c0_register($16, 4)
+#define read_c0_config5()	__read_32bit_c0_register($16, 5)
+#define read_c0_config6()	__read_32bit_c0_register($16, 6)
+#define read_c0_config7()	__read_32bit_c0_register($16, 7)
 #define write_c0_config(val)	__write_32bit_c0_register($16, 0, val)
 #define write_c0_config1(val)	__write_32bit_c0_register($16, 1, val)
 #define write_c0_config2(val)	__write_32bit_c0_register($16, 2, val)
 #define write_c0_config3(val)	__write_32bit_c0_register($16, 3, val)
+#define write_c0_config4(val)	__write_32bit_c0_register($16, 4, val)
+#define write_c0_config5(val)	__write_32bit_c0_register($16, 5, val)
+#define write_c0_config6(val)	__write_32bit_c0_register($16, 6, val)
+#define write_c0_config7(val)	__write_32bit_c0_register($16, 7, val)
 
 /*
  * The WatchLo register.  There may be upto 8 of them.
@@ -856,42 +864,34 @@
  */
 static inline void tlb_probe(void)
 {
-	rm9000_tlb_hazard();
 	__asm__ __volatile__(
 		".set noreorder\n\t"
 		"tlbp\n\t"
 		".set reorder");
-	rm9000_tlb_hazard();
 }
 
 static inline void tlb_read(void)
 {
-	rm9000_tlb_hazard();
 	__asm__ __volatile__(
 		".set noreorder\n\t"
 		"tlbr\n\t"
 		".set reorder");
-	rm9000_tlb_hazard();
 }
 
 static inline void tlb_write_indexed(void)
 {
-	rm9000_tlb_hazard();
 	__asm__ __volatile__(
 		".set noreorder\n\t"
 		"tlbwi\n\t"
 		".set reorder");
-	rm9000_tlb_hazard();
 }
 
 static inline void tlb_write_random(void)
 {
-	rm9000_tlb_hazard();
 	__asm__ __volatile__(
 		".set noreorder\n\t"
 		"tlbwr\n\t"
 		".set reorder");
-	rm9000_tlb_hazard();
 }
 
 /*
diff -Nur linux-2.4.30/include/asm-mips64/reg.h linux-2.4.30-linux-mips.org/include/asm-mips64/reg.h
--- linux-2.4.30/include/asm-mips64/reg.h	2003-08-25 13:44:44.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/asm-mips64/reg.h	2005-05-12 17:57:47.000000000 +0200
@@ -46,6 +46,9 @@
 /*
  * k0/k1 unsaved
  */
+#define EF_REG26		26
+#define EF_REG27		27
+
 #define EF_REG28		28
 #define EF_REG29		29
 #define EF_REG30		30
diff -Nur linux-2.4.30/include/asm-mips64/unistd.h linux-2.4.30-linux-mips.org/include/asm-mips64/unistd.h
--- linux-2.4.30/include/asm-mips64/unistd.h	2005-01-19 15:10:12.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-mips64/unistd.h	2005-05-12 17:57:47.000000000 +0200
@@ -760,7 +760,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 /*
@@ -788,7 +788,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #define _syscall2(type,name,atype,a,btype,b) \
@@ -813,7 +813,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #define _syscall3(type,name,atype,a,btype,b,ctype,c) \
@@ -839,7 +839,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #define _syscall4(type,name,atype,a,btype,b,ctype,c,dtype,d) \
@@ -865,7 +865,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #if (_MIPS_SIM == _MIPS_SIM_ABI32)
@@ -902,7 +902,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #define _syscall6(type,name,atype,a,btype,b,ctype,c,dtype,d,etype,e,ftype,f) \
@@ -935,7 +935,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #endif /* (_MIPS_SIM == _MIPS_SIM_ABI32) */
@@ -966,7 +966,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #define _syscall6(type,name,atype,a,btype,b,ctype,c,dtype,d,etype,e,ftype,f) \
@@ -995,7 +995,7 @@
 	if (__a3 == 0) \
 		return (type) __v0; \
 	errno = __v0; \
-	return -1; \
+	return (type)-1; \
 }
 
 #endif /* (_MIPS_SIM == _MIPS_SIM_NABI32) || (_MIPS_SIM == _MIPS_SIM_ABI64) */
diff -Nur linux-2.4.30/include/asm-ppc/param.h linux-2.4.30-linux-mips.org/include/asm-ppc/param.h
--- linux-2.4.30/include/asm-ppc/param.h	2003-06-13 16:51:38.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/asm-ppc/param.h	2005-05-12 17:57:48.000000000 +0200
@@ -3,6 +3,9 @@
 
 #ifndef HZ
 #define HZ 100
+#ifdef __KERNEL__
+#define hz_to_std(a) (a)
+#endif
 #endif
 
 #define EXEC_PAGESIZE	4096
diff -Nur linux-2.4.30/include/asm-s390/param.h linux-2.4.30-linux-mips.org/include/asm-s390/param.h
--- linux-2.4.30/include/asm-s390/param.h	2001-02-13 23:13:44.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-s390/param.h	2005-05-12 17:57:48.000000000 +0200
@@ -11,6 +11,9 @@
 
 #ifndef HZ
 #define HZ 100
+#ifdef __KERNEL__
+#define hz_to_std(a) (a)
+#endif
 #endif
 
 #define EXEC_PAGESIZE	4096
diff -Nur linux-2.4.30/include/asm-sh/param.h linux-2.4.30-linux-mips.org/include/asm-sh/param.h
--- linux-2.4.30/include/asm-sh/param.h	2001-01-04 22:19:13.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-sh/param.h	2005-05-12 17:57:50.000000000 +0200
@@ -3,6 +3,9 @@
 
 #ifndef HZ
 #define HZ 100
+#ifdef __KERNEL__
+#define hz_to_std(a) (a)
+#endif
 #endif
 
 #define EXEC_PAGESIZE	4096
diff -Nur linux-2.4.30/include/asm-sparc/param.h linux-2.4.30-linux-mips.org/include/asm-sparc/param.h
--- linux-2.4.30/include/asm-sparc/param.h	2000-10-30 23:34:12.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-sparc/param.h	2005-05-12 17:57:50.000000000 +0200
@@ -4,6 +4,9 @@
 
 #ifndef HZ
 #define HZ 100
+#ifdef __KERNEL__
+#define hz_to_std(a) (a)
+#endif
 #endif
 
 #define EXEC_PAGESIZE	8192    /* Thanks for sun4's we carry baggage... */
diff -Nur linux-2.4.30/include/asm-sparc64/param.h linux-2.4.30-linux-mips.org/include/asm-sparc64/param.h
--- linux-2.4.30/include/asm-sparc64/param.h	2000-10-30 23:34:12.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/asm-sparc64/param.h	2005-05-12 17:57:52.000000000 +0200
@@ -4,6 +4,9 @@
 
 #ifndef HZ
 #define HZ 100
+#ifdef __KERNEL__
+#define hz_to_std(a) (a)
+#endif
 #endif
 
 #define EXEC_PAGESIZE	8192    /* Thanks for sun4's we carry baggage... */
diff -Nur linux-2.4.30/include/linux/i2c-algo-au1550.h linux-2.4.30-linux-mips.org/include/linux/i2c-algo-au1550.h
--- linux-2.4.30/include/linux/i2c-algo-au1550.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/i2c-algo-au1550.h	2005-05-12 17:57:53.000000000 +0200
@@ -0,0 +1,31 @@
+/*
+ * Copyright (C) 2004 Embedded Edge, LLC <dan@embeddededge.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or
+ *  (at your option) any later version.
+ *
+ *  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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef I2C_ALGO_AU1550_H
+#define I2C_ALGO_AU1550_H 1
+
+struct i2c_algo_au1550_data {
+	u32	psc_base;
+	int	xfer_timeout;
+	int	ack_timeout;
+};
+
+int i2c_au1550_add_bus(struct i2c_adapter *);
+int i2c_au1550_del_bus(struct i2c_adapter *);
+
+#endif /* I2C_ALGO_AU1550_H */
diff -Nur linux-2.4.30/include/linux/i2c-id.h linux-2.4.30-linux-mips.org/include/linux/i2c-id.h
--- linux-2.4.30/include/linux/i2c-id.h	2004-02-18 14:36:32.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/i2c-id.h	2005-05-12 17:57:53.000000000 +0200
@@ -156,6 +156,8 @@
 
 #define I2C_ALGO_SGI	0x130000	/* SGI algorithm		*/
 
+#define I2C_ALGO_AU1550	0x140000	/* Alchemy Au1550 PSC		*/
+
 #define I2C_ALGO_EXP	0x800000	/* experimental			*/
 
 #define I2C_ALGO_MASK	0xff0000	/* Mask for algorithms		*/
@@ -204,6 +206,9 @@
 #define I2C_HW_SGI_VINO	0x00
 #define I2C_HW_SGI_MACE	0x01
 
+/* --- Au1550 PSC adapters						*/
+#define I2C_HW_AU1550_PSC	0x00
+
 /* --- SMBus only adapters						*/
 #define I2C_HW_SMBUS_PIIX4	0x00
 #define I2C_HW_SMBUS_ALI15X3	0x01
diff -Nur linux-2.4.30/include/linux/ide.h linux-2.4.30-linux-mips.org/include/linux/ide.h
--- linux-2.4.30/include/linux/ide.h	2004-11-17 12:54:22.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/ide.h	2005-05-12 18:01:29.000000000 +0200
@@ -302,7 +302,8 @@
 		ide_qd65xx,	ide_umc8672,	ide_ht6560b,
 		ide_pdc4030,	ide_rz1000,	ide_trm290,
 		ide_cmd646,	ide_cy82c693,	ide_4drives,
-		ide_pmac,	ide_etrax100,	ide_acorn
+		ide_pmac,	ide_etrax100,	ide_acorn,
+		ide_au1xxx
 } hwif_chipset_t;
 
 /*
diff -Nur linux-2.4.30/include/linux/jffs2_fs_i.h linux-2.4.30-linux-mips.org/include/linux/jffs2_fs_i.h
--- linux-2.4.30/include/linux/jffs2_fs_i.h	2001-09-14 23:04:07.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/jffs2_fs_i.h	2005-05-12 18:01:30.000000000 +0200
@@ -1,22 +1,12 @@
-/* $Id: jffs2_fs_i.h,v 1.8 2001/04/18 13:05:28 dwmw2 Exp $ */
+/* $Id: jffs2_fs_i.h,v 1.16 2003/01/09 14:03:21 dwmw2 Exp $ */
 
 #ifndef _JFFS2_FS_I
 #define _JFFS2_FS_I
 
-/* Include the pipe_inode_info at the beginning so that we can still
-   use the storage space in the inode when we have a pipe inode.
-   This sucks.
-*/
-
-#undef THISSUCKS /* Only for 2.2 */
-#ifdef THISSUCKS
-#include <linux/pipe_fs_i.h>
-#endif
+#include <linux/version.h>
+#include <linux/rbtree.h>
 
 struct jffs2_inode_info {
-#ifdef THISSUCKS
-        struct pipe_inode_info pipecrap;
-#endif
 	/* We need an internal semaphore similar to inode->i_sem.
 	   Unfortunately, we can't used the existing one, because
 	   either the GC would deadlock, or we'd have to release it
@@ -26,10 +16,10 @@
 	struct semaphore sem;
 
 	/* The highest (datanode) version number used for this ino */
-	__u32 highest_version;
+	uint32_t highest_version;
 
 	/* List of data fragments which make up the file */
-	struct jffs2_node_frag *fraglist;
+	struct rb_root fragtree;
 
 	/* There may be one datanode which isn't referenced by any of the
 	   above fragments, if it contains a metadata update but no actual
@@ -44,19 +34,13 @@
 	/* Some stuff we just have to keep in-core at all times, for each inode. */
 	struct jffs2_inode_cache *inocache;
 
-	/* Keep a pointer to the last physical node in the list. We don't 
-	   use the doubly-linked lists because we don't want to increase
-	   the memory usage that much. This is simpler */
-	//	struct jffs2_raw_node_ref *lastnode;
-	__u16 flags;
-	__u8 usercompr;
-};
-
-#ifdef JFFS2_OUT_OF_KERNEL
-#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u)
-#else
-#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i)
+	uint16_t flags;
+	uint8_t usercompr;
+#if !defined (__ECOS)
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2)
+	struct inode vfs_inode;
 #endif
+#endif
+};
 
 #endif /* _JFFS2_FS_I */
-
diff -Nur linux-2.4.30/include/linux/jffs2_fs_sb.h linux-2.4.30-linux-mips.org/include/linux/jffs2_fs_sb.h
--- linux-2.4.30/include/linux/jffs2_fs_sb.h	2002-08-03 02:39:45.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/jffs2_fs_sb.h	2005-05-12 18:01:30.000000000 +0200
@@ -1,19 +1,22 @@
-/* $Id: jffs2_fs_sb.h,v 1.16.2.1 2002/02/23 14:13:34 dwmw2 Exp $ */
+/* $Id: jffs2_fs_sb.h,v 1.45 2003/10/08 11:46:27 dwmw2 Exp $ */
 
 #ifndef _JFFS2_FS_SB
 #define _JFFS2_FS_SB
 
 #include <linux/types.h>
 #include <linux/spinlock.h>
+#include <linux/workqueue.h>
 #include <linux/completion.h>
 #include <asm/semaphore.h>
+#include <linux/timer.h>
+#include <linux/wait.h>
 #include <linux/list.h>
 
-#define INOCACHE_HASHSIZE 1
-
 #define JFFS2_SB_FLAG_RO 1
 #define JFFS2_SB_FLAG_MOUNTING 2
 
+struct jffs2_inodirty;
+
 /* A struct for the overall file system control.  Pointers to
    jffs2_sb_info structs are named `c' in the source code.  
    Nee jffs_control
@@ -21,36 +24,46 @@
 struct jffs2_sb_info {
 	struct mtd_info *mtd;
 
-	__u32 highest_ino;
+	uint32_t highest_ino;
+	uint32_t checked_ino;
+
 	unsigned int flags;
-	spinlock_t nodelist_lock;
 
-	//	pid_t thread_pid;		/* GC thread's PID */
 	struct task_struct *gc_task;	/* GC task struct */
 	struct semaphore gc_thread_start; /* GC thread start mutex */
 	struct completion gc_thread_exit; /* GC thread exit completion port */
-	//	__u32 gc_minfree_threshold;	/* GC trigger thresholds */
-	//	__u32 gc_maxdirty_threshold;
 
 	struct semaphore alloc_sem;	/* Used to protect all the following 
 					   fields, and also to protect against
 					   out-of-order writing of nodes.
 					   And GC.
 					*/
-	__u32 flash_size;
-	__u32 used_size;
-	__u32 dirty_size;
-	__u32 free_size;
-	__u32 erasing_size;
-	__u32 bad_size;
-	__u32 sector_size;
-	//	__u32 min_free_size;
-	//	__u32 max_chunk_size;
+	uint32_t cleanmarker_size;	/* Size of an _inline_ CLEANMARKER
+					 (i.e. zero for OOB CLEANMARKER */
+
+	uint32_t flash_size;
+	uint32_t used_size;
+	uint32_t dirty_size;
+	uint32_t wasted_size;
+	uint32_t free_size;
+	uint32_t erasing_size;
+	uint32_t bad_size;
+	uint32_t sector_size;
+	uint32_t unchecked_size;
+
+	uint32_t nr_free_blocks;
+	uint32_t nr_erasing_blocks;
+
+	/* Number of free blocks there must be before we... */
+	uint8_t resv_blocks_write;	/* ... allow a normal filesystem write */
+	uint8_t resv_blocks_deletion;	/* ... allow a normal filesystem deletion */
+	uint8_t resv_blocks_gctrigger;	/* ... wake up the GC thread */
+	uint8_t resv_blocks_gcbad;	/* ... pick a block from the bad_list to GC */
+	uint8_t resv_blocks_gcmerge;	/* ... merge pages when garbage collecting */
 
-	__u32 nr_free_blocks;
-	__u32 nr_erasing_blocks;
+	uint32_t nospc_dirty_size;
 
-	__u32 nr_blocks;
+	uint32_t nr_blocks;
 	struct jffs2_eraseblock *blocks;	/* The whole array of blocks. Used for getting blocks 
 						 * from the offset (blocks[ofs / sector_size]) */
 	struct jffs2_eraseblock *nextblock;	/* The block we're currently filling */
@@ -58,9 +71,12 @@
 	struct jffs2_eraseblock *gcblock;	/* The block we're currently garbage-collecting */
 
 	struct list_head clean_list;		/* Blocks 100% full of clean data */
+	struct list_head very_dirty_list;	/* Blocks with lots of dirty space */
 	struct list_head dirty_list;		/* Blocks with some dirty space */
+	struct list_head erasable_list;		/* Blocks which are completely dirty, and need erasing */
+	struct list_head erasable_pending_wbuf_list;	/* Blocks which need erasing but only after the current wbuf is flushed */
 	struct list_head erasing_list;		/* Blocks which are currently erasing */
-	struct list_head erase_pending_list;	/* Blocks which need erasing */
+	struct list_head erase_pending_list;	/* Blocks which need erasing now */
 	struct list_head erase_complete_list;	/* Blocks which are erased and need the clean marker written to them */
 	struct list_head free_list;		/* Blocks which are free and ready to be used */
 	struct list_head bad_list;		/* Bad blocks. */
@@ -69,16 +85,33 @@
 	spinlock_t erase_completion_lock;	/* Protect free_list and erasing_list 
 						   against erase completion handler */
 	wait_queue_head_t erase_wait;		/* For waiting for erases to complete */
-	struct jffs2_inode_cache *inocache_list[INOCACHE_HASHSIZE];
-	spinlock_t inocache_lock;
-};
 
-#ifdef JFFS2_OUT_OF_KERNEL
-#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u)
-#else
-#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb)
+	wait_queue_head_t inocache_wq;
+	struct jffs2_inode_cache **inocache_list;
+	spinlock_t inocache_lock;
+	
+	/* Sem to allow jffs2_garbage_collect_deletion_dirent to
+	   drop the erase_completion_lock while it's holding a pointer 
+	   to an obsoleted node. I don't like this. Alternatives welcomed. */
+	struct semaphore erase_free_sem;
+
+#ifdef CONFIG_JFFS2_FS_NAND
+	/* Write-behind buffer for NAND flash */
+	unsigned char *wbuf;
+	uint32_t wbuf_ofs;
+	uint32_t wbuf_len;
+	uint32_t wbuf_pagesize;
+	struct jffs2_inodirty *wbuf_inodes;
+
+	/* Information about out-of-band area usage... */
+	struct nand_oobinfo *oobinfo;
+	uint32_t badblock_pos;
+	uint32_t fsdata_pos;
+	uint32_t fsdata_len;
 #endif
 
-#define OFNI_BS_2SFFJ(c)  ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) )
+	/* OS-private pointer for getting back to master superblock info */
+	void *os_priv;
+};
 
 #endif /* _JFFS2_FB_SB */
diff -Nur linux-2.4.30/include/linux/jffs2.h linux-2.4.30-linux-mips.org/include/linux/jffs2.h
--- linux-2.4.30/include/linux/jffs2.h	2002-08-03 02:39:45.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/jffs2.h	2005-05-12 18:01:30.000000000 +0200
@@ -1,50 +1,30 @@
 /*
  * JFFS2 -- Journalling Flash File System, Version 2.
  *
- * Copyright (C) 2001 Red Hat, Inc.
+ * Copyright (C) 2001-2003 Red Hat, Inc.
  *
- * Created by David Woodhouse <dwmw2@cambridge.redhat.com>
+ * Created by David Woodhouse <dwmw2@redhat.com>
  *
- * The original JFFS, from which the design for JFFS2 was derived,
- * was designed and implemented by Axis Communications AB.
+ * For licensing information, see the file 'LICENCE' in the 
+ * jffs2 directory.
  *
- * The contents of this file are subject to the Red Hat eCos Public
- * License Version 1.1 (the "Licence"); you may not use this file
- * except in compliance with the Licence.  You may obtain a copy of
- * the Licence at http://www.redhat.com/
- *
- * Software distributed under the Licence is distributed on an "AS IS"
- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied.
- * See the Licence for the specific language governing rights and
- * limitations under the Licence.
- *
- * The Original Code is JFFS2 - Journalling Flash File System, version 2
- *
- * Alternatively, the contents of this file may be used under the
- * terms of the GNU General Public License version 2 (the "GPL"), in
- * which case the provisions of the GPL are applicable instead of the
- * above.  If you wish to allow the use of your version of this file
- * only under the terms of the GPL and not to allow others to use your
- * version of this file under the RHEPL, indicate your decision by
- * deleting the provisions above and replace them with the notice and
- * other provisions required by the GPL.  If you do not delete the
- * provisions above, a recipient may use your version of this file
- * under either the RHEPL or the GPL.
- *
- * $Id: jffs2.h,v 1.19 2001/10/09 13:20:23 dwmw2 Exp $
+ * $Id: jffs2.h,v 1.32 2004/05/05 11:57:53 dwmw2 Exp $
  *
  */
 
 #ifndef __LINUX_JFFS2_H__
 #define __LINUX_JFFS2_H__
 
-#include <asm/types.h>
+/* You must include something which defines the C99 uintXX_t types. 
+   We don't do it from here because this file is used in too many
+   different environments. */
+
 #define JFFS2_SUPER_MAGIC 0x72b6
 
 /* Values we may expect to find in the 'magic' field */
 #define JFFS2_OLD_MAGIC_BITMASK 0x1984
 #define JFFS2_MAGIC_BITMASK 0x1985
-#define KSAMTIB_CIGAM_2SFFJ 0x5981 /* For detecting wrong-endian fs */
+#define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */
 #define JFFS2_EMPTY_BITMASK 0xffff
 #define JFFS2_DIRTY_BITMASK 0x0000
 
@@ -78,16 +58,12 @@
 #define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1)
 #define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2)
 #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
+#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4)
 
 // Maybe later...
 //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3)
 //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4)
 
-/* Same as the non_ECC versions, but with extra space for real 
- * ECC instead of just the checksum. For use on NAND flash 
- */
-//#define JFFS2_NODETYPE_DIRENT_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 5)
-//#define JFFS2_NODETYPE_INODE_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 6)
 
 #define JFFS2_INO_FLAG_PREREAD	  1	/* Do read_inode() for this one at 
 					   mount time, don't wait for it to 
@@ -96,31 +72,46 @@
 					   compression type */
 
 
+/* These can go once we've made sure we've caught all uses without
+   byteswapping */
+
+typedef struct {
+	uint32_t v32;
+} __attribute__((packed))  jint32_t;
+
+typedef struct {
+	uint32_t m;
+} __attribute__((packed))  jmode_t;
+
+typedef struct {
+	uint16_t v16;
+} __attribute__((packed)) jint16_t;
+
 struct jffs2_unknown_node
 {
 	/* All start like this */
-	__u16 magic;
-	__u16 nodetype;
-	__u32 totlen; /* So we can skip over nodes we don't grok */
-	__u32 hdr_crc;
+	jint16_t magic;
+	jint16_t nodetype;
+	jint32_t totlen; /* So we can skip over nodes we don't grok */
+	jint32_t hdr_crc;
 } __attribute__((packed));
 
 struct jffs2_raw_dirent
 {
-	__u16 magic;
-	__u16 nodetype;	/* == JFFS_NODETYPE_DIRENT */
-	__u32 totlen;
-	__u32 hdr_crc;
-	__u32 pino;
-	__u32 version;
-	__u32 ino; /* == zero for unlink */
-	__u32 mctime;
-	__u8 nsize;
-	__u8 type;
-	__u8 unused[2];
-	__u32 node_crc;
-	__u32 name_crc;
-	__u8 name[0];
+	jint16_t magic;
+	jint16_t nodetype;	/* == JFFS_NODETYPE_DIRENT */
+	jint32_t totlen;
+	jint32_t hdr_crc;
+	jint32_t pino;
+	jint32_t version;
+	jint32_t ino; /* == zero for unlink */
+	jint32_t mctime;
+	uint8_t nsize;
+	uint8_t type;
+	uint8_t unused[2];
+	jint32_t node_crc;
+	jint32_t name_crc;
+	uint8_t name[0];
 } __attribute__((packed));
 
 /* The JFFS2 raw inode structure: Used for storage on physical media.  */
@@ -131,28 +122,28 @@
 */
 struct jffs2_raw_inode
 {
-	__u16 magic;      /* A constant magic number.  */
-	__u16 nodetype;   /* == JFFS_NODETYPE_INODE */
-	__u32 totlen;     /* Total length of this node (inc data, etc.) */
-	__u32 hdr_crc;
-	__u32 ino;        /* Inode number.  */
-	__u32 version;    /* Version number.  */
-	__u32 mode;       /* The file's type or mode.  */
-	__u16 uid;        /* The file's owner.  */
-	__u16 gid;        /* The file's group.  */
-	__u32 isize;      /* Total resultant size of this inode (used for truncations)  */
-	__u32 atime;      /* Last access time.  */
-	__u32 mtime;      /* Last modification time.  */
-	__u32 ctime;      /* Change time.  */
-	__u32 offset;     /* Where to begin to write.  */
-	__u32 csize;      /* (Compressed) data size */
-	__u32 dsize;	  /* Size of the node's data. (after decompression) */
-	__u8 compr;       /* Compression algorithm used */
-	__u8 usercompr;	  /* Compression algorithm requested by the user */
-	__u16 flags;	  /* See JFFS2_INO_FLAG_* */
-	__u32 data_crc;   /* CRC for the (compressed) data.  */
-	__u32 node_crc;   /* CRC for the raw inode (excluding data)  */
-//	__u8 data[dsize];
+	jint16_t magic;      /* A constant magic number.  */
+	jint16_t nodetype;   /* == JFFS_NODETYPE_INODE */
+	jint32_t totlen;     /* Total length of this node (inc data, etc.) */
+	jint32_t hdr_crc;
+	jint32_t ino;        /* Inode number.  */
+	jint32_t version;    /* Version number.  */
+	jmode_t mode;       /* The file's type or mode.  */
+	jint16_t uid;        /* The file's owner.  */
+	jint16_t gid;        /* The file's group.  */
+	jint32_t isize;      /* Total resultant size of this inode (used for truncations)  */
+	jint32_t atime;      /* Last access time.  */
+	jint32_t mtime;      /* Last modification time.  */
+	jint32_t ctime;      /* Change time.  */
+	jint32_t offset;     /* Where to begin to write.  */
+	jint32_t csize;      /* (Compressed) data size */
+	jint32_t dsize;	     /* Size of the node's data. (after decompression) */
+	uint8_t compr;       /* Compression algorithm used */
+	uint8_t usercompr;   /* Compression algorithm requested by the user */
+	jint16_t flags;	     /* See JFFS2_INO_FLAG_* */
+	jint32_t data_crc;   /* CRC for the (compressed) data.  */
+	jint32_t node_crc;   /* CRC for the raw inode (excluding data)  */
+	uint8_t data[0];
 } __attribute__((packed));
 
 union jffs2_node_union {
diff -Nur linux-2.4.30/include/linux/mmc/mmc_ll.h linux-2.4.30-linux-mips.org/include/linux/mmc/mmc_ll.h
--- linux-2.4.30/include/linux/mmc/mmc_ll.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/mmc/mmc_ll.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,117 @@
+/*
+ * Header for MultiMediaCard (MMC)
+ *
+ * Copyright (c) 2002 Hewlett-Packard Company
+ *   
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *  
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *  
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Based strongly on code by:
+ *
+ * Author: Yong-iL Joh <tolkien@mizi.com>
+ * Date  : $Date: 2002/06/18 19:07:20 $ 
+ *
+ * Author:  Andrew Christian
+ *          15 May 2002
+ */
+
+#ifndef MMC_MMC_LL_H
+#define MMC_MMC_LL_H
+
+#include <linux/types.h>
+#include <linux/mmc/mmc_protocol.h>
+
+#ifdef __KERNEL__
+
+/* Error codes */
+enum mmc_result_t {
+	MMC_NO_RESPONSE        = -1,
+	MMC_NO_ERROR           = 0,
+	MMC_ERROR_OUT_OF_RANGE,
+	MMC_ERROR_ADDRESS,
+	MMC_ERROR_BLOCK_LEN,
+	MMC_ERROR_ERASE_SEQ,
+	MMC_ERROR_ERASE_PARAM,
+	MMC_ERROR_WP_VIOLATION,
+	MMC_ERROR_CARD_IS_LOCKED,
+	MMC_ERROR_LOCK_UNLOCK_FAILED,
+	MMC_ERROR_COM_CRC,
+	MMC_ERROR_ILLEGAL_COMMAND,
+	MMC_ERROR_CARD_ECC_FAILED,
+	MMC_ERROR_CC,
+	MMC_ERROR_GENERAL,
+	MMC_ERROR_UNDERRUN,
+	MMC_ERROR_OVERRUN,
+	MMC_ERROR_CID_CSD_OVERWRITE,
+	MMC_ERROR_STATE_MISMATCH,
+	MMC_ERROR_HEADER_MISMATCH,
+	MMC_ERROR_TIMEOUT,
+	MMC_ERROR_CRC,
+	MMC_ERROR_DRIVER_FAILURE,
+};
+
+struct mmc_request {
+	int               index;      /* Slot index - used for CS lines */
+	int               cmd;        /* Command to send */
+	u32               arg;        /* Argument to send */
+	enum mmc_rsp_t    rtype;      /* Response type expected */
+
+	/* Data transfer (these may be modified at the low level) */
+	u16               nob;        /* Number of blocks to transfer*/
+	u16               block_len;  /* Block length */
+	u8               *buffer;     /* Data buffer */
+
+	/* Results */
+	u8                response[18]; /* Buffer to store response - CRC is optional */
+	enum mmc_result_t result;
+};
+
+#define MMC_SDFLAG_SPI_MODE   (1<<0)    /* Can use SPI mode */
+#define MMC_SDFLAG_MMC_MODE   (1<<1)    /* Can use MMC mode */
+#define MMC_SDFLAG_SD_MODE    (1<<2)    /* Can use SD mode */
+#define MMC_SDFLAG_VOLTAGE    (1<<3)    /* Can change voltage range */
+
+struct module;
+
+struct mmc_slot_driver {
+	struct module  *owner;
+	char           *name;
+	u32             ocr;         /* Valid voltage ranges */
+	u32             flags;       /* Slot driver flags */
+
+	int  (*init)(void);   
+	void (*cleanup)(void);
+	int  (*is_empty)(int);
+	int  (*set_clock)(int slot, u32 rate);
+	void (*send_cmd)(struct mmc_request *);
+};
+
+/* Calls made by the hardware-specific slot driver code */
+extern int  mmc_register_slot_driver( struct mmc_slot_driver *, int );
+extern void mmc_unregister_slot_driver( struct mmc_slot_driver * );
+extern void mmc_cmd_complete( struct mmc_request * );
+extern void mmc_insert( int );
+extern void mmc_eject( int );
+
+#endif /* #ifdef __KERNEL__ */
+
+#endif /* MMC_MMC_LL_H */
+
diff -Nur linux-2.4.30/include/linux/mmc/mmc_protocol.h linux-2.4.30-linux-mips.org/include/linux/mmc/mmc_protocol.h
--- linux-2.4.30/include/linux/mmc/mmc_protocol.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/mmc/mmc_protocol.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,288 @@
+/*
+ * Header for MultiMediaCard (MMC)
+ *
+ * Copyright (c) 2002 Hewlett-Packard Company
+ *   
+ * Permission is hereby granted, free of charge, to any person obtaining a
+ * copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *  
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *  
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ *
+ * Many thanks to Alessandro Rubini and Jonathan Corbet!
+ *
+ * Based strongly on code by:
+ *
+ * Author: Yong-iL Joh <tolkien@mizi.com>
+ * Date  : $Date: 2002/06/18 12:37:30 $ 
+ *
+ * Author:  Andrew Christian
+ *          15 May 2002
+ */
+
+#ifndef MMC_MMC_PROTOCOL_H
+#define MMC_MMC_PROTOCOL_H
+
+#include <linux/types.h>
+
+/* Standard MMC clock speeds */
+#define MMC_CLOCK_SLOW    400000      /* 400 kHz for initial setup */
+#define MMC_CLOCK_FAST  20000000      /* 20 MHz for maximum for normal operation */
+
+/* Extra MMC commands for state control */
+/* Use negative numbers to disambiguate */
+#define MMC_CIM_RESET            -1
+
+/* Standard MMC commands (3.1)           type  argument     response */
+   /* class 1 */
+#define	MMC_GO_IDLE_STATE         0   /* bc                          */
+#define MMC_SEND_OP_COND          1   /* bcr  [31:0] OCR         R3  */
+#define MMC_ALL_SEND_CID          2   /* bcr                     R2  */
+#define MMC_SET_RELATIVE_ADDR     3   /* ac   [31:16] RCA        R1  */
+#define MMC_SET_DSR               4   /* bc   [31:16] RCA            */
+#define MMC_SELECT_CARD           7   /* ac   [31:16] RCA        R1  */
+#define MMC_SEND_CSD              9   /* ac   [31:16] RCA        R2  */
+#define MMC_SEND_CID             10   /* ac   [31:16] RCA        R2  */
+#define MMC_READ_DAT_UNTIL_STOP  11   /* adtc [31:0] dadr        R1  */
+#define MMC_STOP_TRANSMISSION    12   /* ac                      R1b */
+#define MMC_SEND_STATUS	         13   /* ac   [31:16] RCA        R1  */
+#define MMC_GO_INACTIVE_STATE    15   /* ac   [31:16] RCA            */
+
+  /* class 2 */
+#define MMC_SET_BLOCKLEN         16   /* ac   [31:0] block len   R1  */
+#define MMC_READ_SINGLE_BLOCK    17   /* adtc [31:0] data addr   R1  */
+#define MMC_READ_MULTIPLE_BLOCK  18   /* adtc [31:0] data addr   R1  */
+
+  /* class 3 */
+#define MMC_WRITE_DAT_UNTIL_STOP 20   /* adtc [31:0] data addr   R1  */
+
+  /* class 4 */
+#define MMC_SET_BLOCK_COUNT      23   /* adtc [31:0] data addr   R1  */
+#define MMC_WRITE_BLOCK          24   /* adtc [31:0] data addr   R1  */
+#define MMC_WRITE_MULTIPLE_BLOCK 25   /* adtc                    R1  */
+#define MMC_PROGRAM_CID          26   /* adtc                    R1  */
+#define MMC_PROGRAM_CSD          27   /* adtc                    R1  */
+
+  /* class 6 */
+#define MMC_SET_WRITE_PROT       28   /* ac   [31:0] data addr   R1b */
+#define MMC_CLR_WRITE_PROT       29   /* ac   [31:0] data addr   R1b */
+#define MMC_SEND_WRITE_PROT      30   /* adtc [31:0] wpdata addr R1  */
+
+  /* class 5 */
+#define MMC_ERASE_GROUP_START    35   /* ac   [31:0] data addr   R1  */
+#define MMC_ERASE_GROUP_END      36   /* ac   [31:0] data addr   R1  */
+#define MMC_ERASE                37   /* ac                      R1b */
+
+  /* class 9 */
+#define MMC_FAST_IO              39   /* ac   <Complex>          R4  */
+#define MMC_GO_IRQ_STATE         40   /* bcr                     R5  */
+
+  /* class 7 */
+#define MMC_LOCK_UNLOCK          42   /* adtc                    R1b */
+
+  /* class 8 */
+#define MMC_APP_CMD              55   /* ac   [31:16] RCA        R1  */
+#define MMC_GEN_CMD              56   /* adtc [0] RD/WR          R1b */
+
+/* Don't change the order of these; they are used in dispatch tables */
+enum mmc_rsp_t {
+	RESPONSE_NONE   = 0,
+	RESPONSE_R1     = 1,
+	RESPONSE_R1B    = 2,
+	RESPONSE_R2_CID = 3,
+	RESPONSE_R2_CSD  = 4,
+	RESPONSE_R3      = 5,
+	RESPONSE_R4      = 6,
+	RESPONSE_R5      = 7
+};
+
+
+/*
+  MMC status in R1
+  Type
+  	e : error bit
+	s : status bit
+	r : detected and set for the actual command response
+	x : detected and set during command execution. the host must poll
+            the card by sending status command in order to read these bits.
+  Clear condition
+  	a : according to the card state
+	b : always related to the previous command. Reception of
+            a valid command will clear it (with a delay of one command)
+	c : clear by read
+ */
+
+#define R1_OUT_OF_RANGE		(1 << 31)	/* er, c */
+#define R1_ADDRESS_ERROR	(1 << 30)	/* erx, c */
+#define R1_BLOCK_LEN_ERROR	(1 << 29)	/* er, c */
+#define R1_ERASE_SEQ_ERROR      (1 << 28)	/* er, c */
+#define R1_ERASE_PARAM		(1 << 27)	/* ex, c */
+#define R1_WP_VIOLATION		(1 << 26)	/* erx, c */
+#define R1_CARD_IS_LOCKED	(1 << 25)	/* sx, a */
+#define R1_LOCK_UNLOCK_FAILED	(1 << 24)	/* erx, c */
+#define R1_COM_CRC_ERROR	(1 << 23)	/* er, b */
+#define R1_ILLEGAL_COMMAND	(1 << 22)	/* er, b */
+#define R1_CARD_ECC_FAILED	(1 << 21)	/* ex, c */
+#define R1_CC_ERROR		(1 << 20)	/* erx, c */
+#define R1_ERROR		(1 << 19)	/* erx, c */
+#define R1_UNDERRUN		(1 << 18)	/* ex, c */
+#define R1_OVERRUN		(1 << 17)	/* ex, c */
+#define R1_CID_CSD_OVERWRITE	(1 << 16)	/* erx, c, CID/CSD overwrite */
+#define R1_WP_ERASE_SKIP	(1 << 15)	/* sx, c */
+#define R1_CARD_ECC_DISABLED	(1 << 14)	/* sx, a */
+#define R1_ERASE_RESET		(1 << 13)	/* sr, c */
+#define R1_STATUS(x)            (x & 0xFFFFE000)
+#define R1_CURRENT_STATE(x)    	((x & 0x00001E00) >> 9)	/* sx, b (4 bits) */
+#define R1_READY_FOR_DATA	(1 << 8)	/* sx, a */
+#define R1_APP_CMD		(1 << 7)	/* sr, c */
+
+enum card_state {
+	CARD_STATE_EMPTY = -1,
+	CARD_STATE_IDLE	 = 0,
+	CARD_STATE_READY = 1,
+	CARD_STATE_IDENT = 2,
+	CARD_STATE_STBY	 = 3,
+	CARD_STATE_TRAN	 = 4,
+	CARD_STATE_DATA	 = 5,
+	CARD_STATE_RCV	 = 6,
+	CARD_STATE_PRG	 = 7,
+	CARD_STATE_DIS	 = 8,
+};
+
+/* These are unpacked versions of the actual responses */
+
+struct mmc_response_r1 {
+	u8  cmd;
+	u32 status;
+};
+
+struct mmc_cid {
+	u8  mid;
+	u16 oid;
+	u8  pnm[7];   // Product name (we null-terminate)
+	u8  prv;
+	u32 psn;
+	u8  mdt;
+};
+
+struct mmc_csd {
+	u8  csd_structure;
+	u8  spec_vers;
+	u8  taac;
+	u8  nsac;
+	u8  tran_speed;
+	u16 ccc;
+	u8  read_bl_len;
+	u8  read_bl_partial;
+	u8  write_blk_misalign;
+	u8  read_blk_misalign;
+	u8  dsr_imp;
+	u16 c_size;
+	u8  vdd_r_curr_min;
+	u8  vdd_r_curr_max;
+	u8  vdd_w_curr_min;
+	u8  vdd_w_curr_max;
+	u8  c_size_mult;
+	union {
+		struct { /* MMC system specification version 3.1 */
+			u8  erase_grp_size;  
+			u8  erase_grp_mult; 
+		} v31;
+		struct { /* MMC system specification version 2.2 */
+			u8  sector_size;
+			u8  erase_grp_size;
+		} v22;
+	} erase;
+	u8  wp_grp_size;
+	u8  wp_grp_enable;
+	u8  default_ecc;
+	u8  r2w_factor;
+	u8  write_bl_len;
+	u8  write_bl_partial;
+	u8  file_format_grp;
+	u8  copy;
+	u8  perm_write_protect;
+	u8  tmp_write_protect;
+	u8  file_format;
+	u8  ecc;
+};
+
+struct mmc_response_r3 {  
+	u32 ocr;
+}; 
+
+#define MMC_VDD_145_150	0x00000001	/* VDD voltage 1.45 - 1.50 */
+#define MMC_VDD_150_155	0x00000002	/* VDD voltage 1.50 - 1.55 */
+#define MMC_VDD_155_160	0x00000004	/* VDD voltage 1.55 - 1.60 */
+#define MMC_VDD_160_165	0x00000008	/* VDD voltage 1.60 - 1.65 */
+#define MMC_VDD_165_170	0x00000010	/* VDD voltage 1.65 - 1.70 */
+#define MMC_VDD_17_18	0x00000020	/* VDD voltage 1.7 - 1.8 */
+#define MMC_VDD_18_19	0x00000040	/* VDD voltage 1.8 - 1.9 */
+#define MMC_VDD_19_20	0x00000080	/* VDD voltage 1.9 - 2.0 */
+#define MMC_VDD_20_21	0x00000100	/* VDD voltage 2.0 ~ 2.1 */
+#define MMC_VDD_21_22	0x00000200	/* VDD voltage 2.1 ~ 2.2 */
+#define MMC_VDD_22_23	0x00000400	/* VDD voltage 2.2 ~ 2.3 */
+#define MMC_VDD_23_24	0x00000800	/* VDD voltage 2.3 ~ 2.4 */
+#define MMC_VDD_24_25	0x00001000	/* VDD voltage 2.4 ~ 2.5 */
+#define MMC_VDD_25_26	0x00002000	/* VDD voltage 2.5 ~ 2.6 */
+#define MMC_VDD_26_27	0x00004000	/* VDD voltage 2.6 ~ 2.7 */
+#define MMC_VDD_27_28	0x00008000	/* VDD voltage 2.7 ~ 2.8 */
+#define MMC_VDD_28_29	0x00010000	/* VDD voltage 2.8 ~ 2.9 */
+#define MMC_VDD_29_30	0x00020000	/* VDD voltage 2.9 ~ 3.0 */
+#define MMC_VDD_30_31	0x00040000	/* VDD voltage 3.0 ~ 3.1 */
+#define MMC_VDD_31_32	0x00080000	/* VDD voltage 3.1 ~ 3.2 */
+#define MMC_VDD_32_33	0x00100000	/* VDD voltage 3.2 ~ 3.3 */
+#define MMC_VDD_33_34	0x00200000	/* VDD voltage 3.3 ~ 3.4 */
+#define MMC_VDD_34_35	0x00400000	/* VDD voltage 3.4 ~ 3.5 */
+#define MMC_VDD_35_36	0x00800000	/* VDD voltage 3.5 ~ 3.6 */
+#define MMC_CARD_BUSY	0x80000000	/* Card Power up status bit */
+
+
+/* CSD field definitions */
+ 
+#define CSD_STRUCT_VER_1_0  0           /* Valid for system specification 1.0 - 1.2 */
+#define CSD_STRUCT_VER_1_1  1           /* Valid for system specification 1.4 - 2.2 */
+#define CSD_STRUCT_VER_1_2  2           /* Valid for system specification 3.1       */
+
+#define CSD_SPEC_VER_0      0           /* Implements system specification 1.0 - 1.2 */
+#define CSD_SPEC_VER_1      1           /* Implements system specification 1.4 */
+#define CSD_SPEC_VER_2      2           /* Implements system specification 2.0 - 2.2 */
+#define CSD_SPEC_VER_3      3           /* Implements system specification 3.1 */
+
+
+
+#ifdef CONFIG_MMC_DEBUG
+#ifndef CONFIG_MMC_DEBUG_VERBOSE
+#define CONFIG_MMC_DEBUG_VERBOSE 3
+#endif
+extern int g_mmc_debug;
+#define DEBUG(n, x, args...)			\
+	if ((n) <=  g_mmc_debug) {	\
+		printk("%s%s" x, KERN_INFO, __FUNCTION__, ## args);	\
+	}
+#define START_DEBUG(n) do { if ((n) <= g_mmc_debug)
+#define END_DEBUG      } while (0)
+#else
+#ifndef CONFIG_MMC_DEBUG_VERBOSE
+#define CONFIG_MMC_DEBUG_VERBOSE 0
+#endif
+#define DEBUG(n, args...)
+#define START_DEBUG(n)
+#define END_DEBUG
+#endif /* CONFIG_MMC_DEBUG */
+
+#endif  /* MMC_MMC_PROTOCOL_H */
+
diff -Nur linux-2.4.30/include/linux/mtd/blktrans.h linux-2.4.30-linux-mips.org/include/linux/mtd/blktrans.h
--- linux-2.4.30/include/linux/mtd/blktrans.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/blktrans.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,72 @@
+/*
+ * $Id: blktrans.h,v 1.5 2003/06/23 12:00:08 dwmw2 Exp $
+ *
+ * (C) 2003 David Woodhouse <dwmw2@infradead.org>
+ *
+ * Interface to Linux block layer for MTD 'translation layers'.
+ *
+ */
+
+#ifndef __MTD_TRANS_H__
+#define __MTD_TRANS_H__
+
+#include <asm/semaphore.h>
+
+struct hd_geometry;
+struct mtd_info;
+struct mtd_blktrans_ops;
+struct file;
+struct inode;
+
+struct mtd_blktrans_dev {
+	struct mtd_blktrans_ops *tr;
+	struct list_head list;
+	struct mtd_info *mtd;
+	struct semaphore sem;
+	int devnum;
+	int blksize;
+	unsigned long size;
+	int readonly;
+	void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */
+};
+
+struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */
+
+struct mtd_blktrans_ops {
+	char *name;
+	int major;
+	int part_bits;
+
+	/* Access functions */
+	int (*readsect)(struct mtd_blktrans_dev *dev,
+		    unsigned long block, char *buffer);
+	int (*writesect)(struct mtd_blktrans_dev *dev,
+		     unsigned long block, char *buffer);
+
+	/* Block layer ioctls */
+	int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo);
+	int (*flush)(struct mtd_blktrans_dev *dev);
+
+	/* Called with mtd_table_mutex held; no race with add/remove */
+	int (*open)(struct mtd_blktrans_dev *dev);
+	int (*release)(struct mtd_blktrans_dev *dev);
+
+	/* Called on {de,}registration and on subsequent addition/removal
+	   of devices, with mtd_table_mutex held. */
+	void (*add_mtd)(struct mtd_blktrans_ops *tr, struct mtd_info *mtd);
+	void (*remove_dev)(struct mtd_blktrans_dev *dev);
+
+	struct list_head devs;
+	struct list_head list;
+	struct module *owner;
+
+	struct mtd_blkcore_priv *blkcore_priv;
+};
+
+extern int register_mtd_blktrans(struct mtd_blktrans_ops *tr);
+extern int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr);
+extern int add_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
+extern int del_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
+				 
+
+#endif /* __MTD_TRANS_H__ */
diff -Nur linux-2.4.30/include/linux/mtd/cfi.h linux-2.4.30-linux-mips.org/include/linux/mtd/cfi.h
--- linux-2.4.30/include/linux/mtd/cfi.h	2003-06-13 16:51:38.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/cfi.h	2005-05-12 18:01:29.000000000 +0200
@@ -1,13 +1,14 @@
 
 /* Common Flash Interface structures 
  * See http://support.intel.com/design/flash/technote/index.htm
- * $Id: cfi.h,v 1.32 2002/09/05 05:15:32 acurtis Exp $
+ * $Id: cfi.h,v 1.41 2004/04/15 12:16:12 gleixner Exp $
  */
 
 #ifndef __MTD_CFI_H__
 #define __MTD_CFI_H__
 
 #include <linux/config.h>
+#include <linux/version.h>
 #include <linux/delay.h>
 #include <linux/types.h>
 #include <linux/interrupt.h>
@@ -73,11 +74,11 @@
 typedef __u64 cfi_word;
 
 /* This only works if asm/io.h is included first */
-#ifndef __raw_readll
-#define __raw_readll(addr)	(*(volatile __u64 *)(addr))
+#ifndef __raw_readq
+#define __raw_readq(addr)	(*(volatile __u64 *)(addr))
 #endif
-#ifndef __raw_writell
-#define __raw_writell(v, addr)	(*(volatile __u64 *)(addr) = (v))
+#ifndef __raw_writeq
+#define __raw_writeq(v, addr)	(*(volatile __u64 *)(addr) = (v))
 #endif
 #define CFI_WORD_64
 #else  /* CONFIG_MTD_CFI_B8 */
@@ -260,7 +261,8 @@
   __u8  pri[3];
   __u8  MajorVersion;
   __u8  MinorVersion;
-  __u32 FeatureSupport;
+  __u32 FeatureSupport; /* if bit 31 is set then an additional __u32 feature
+			   block follows - FIXME - not currently supported */
   __u8  SuspendCmdSupport;
   __u16 BlkStatusRegMask;
   __u8  VccOptimal;
@@ -271,6 +273,25 @@
   __u8  UserProtRegSize;
 } __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;
+} __attribute__((packed));
+
 struct cfi_pri_query {
   __u8  NumFields;
   __u32 ProtField[1]; /* Not host ordered */
@@ -287,8 +308,10 @@
 #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
 
 
@@ -303,7 +326,6 @@
 	int cfi_mode;		/* Are we a JEDEC device pretending to be CFI? */
 	int addr_unlock1;
 	int addr_unlock2;
-	int fast_prog;
 	struct mtd_info *(*cmdset_setup)(struct map_info *);
 	struct cfi_ident *cfiq; /* For now only one. We insist that all devs
 				  must be of the same type. */
@@ -314,8 +336,6 @@
 	struct flchip chips[0];  /* per-chip data structure for each chip */
 };
 
-#define MAX_CFI_CHIPS 8 /* Entirely arbitrary to avoid realloc() */
-
 /*
  * Returns the command address according to the given geometry.
  */
@@ -387,13 +407,13 @@
 static inline cfi_word cfi_read(struct map_info *map, __u32 addr)
 {
 	if (cfi_buswidth_is_1()) {
-		return map->read8(map, addr);
+		return map_read8(map, addr);
 	} else if (cfi_buswidth_is_2()) {
-		return map->read16(map, addr);
+		return map_read16(map, addr);
 	} else if (cfi_buswidth_is_4()) {
-		return map->read32(map, addr);
+		return map_read32(map, addr);
 	} else if (cfi_buswidth_is_8()) {
-		return map->read64(map, addr);
+		return map_read64(map, addr);
 	} else {
 		return 0;
 	}
@@ -406,13 +426,13 @@
 static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr)
 {
 	if (cfi_buswidth_is_1()) {
-		map->write8(map, val, addr);
+		map_write8(map, val, addr);
 	} else if (cfi_buswidth_is_2()) {
-		map->write16(map, val, addr);
+		map_write16(map, val, addr);
 	} else if (cfi_buswidth_is_4()) {
-		map->write32(map, val, addr);
+		map_write32(map, val, addr);
 	} else if (cfi_buswidth_is_8()) {
-		map->write64(map, val, addr);
+		map_write64(map, val, addr);
 	}
 }
 
@@ -443,13 +463,13 @@
 static inline __u8 cfi_read_query(struct map_info *map, __u32 addr)
 {
 	if (cfi_buswidth_is_1()) {
-		return map->read8(map, addr);
+		return map_read8(map, addr);
 	} else if (cfi_buswidth_is_2()) {
-		return cfi16_to_cpu(map->read16(map, addr));
+		return cfi16_to_cpu(map_read16(map, addr));
 	} else if (cfi_buswidth_is_4()) {
-		return cfi32_to_cpu(map->read32(map, addr));
+		return cfi32_to_cpu(map_read32(map, addr));
 	} else if (cfi_buswidth_is_8()) {
-		return cfi64_to_cpu(map->read64(map, addr));
+		return cfi64_to_cpu(map_read64(map, addr));
 	} else {
 		return 0;
 	}
@@ -479,5 +499,19 @@
 	spin_unlock_bh(mutex);
 }
 
+struct cfi_extquery *cfi_read_pri(struct map_info *map, __u16 adr, __u16 size,
+			     const char* name);
+
+struct cfi_fixup {
+	__u16 mfr;
+	__u16 id;
+	void (*fixup)(struct map_info *map, 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);
 
 #endif /* __MTD_CFI_H__ */
diff -Nur linux-2.4.30/include/linux/mtd/compatmac.h linux-2.4.30-linux-mips.org/include/linux/mtd/compatmac.h
--- linux-2.4.30/include/linux/mtd/compatmac.h	2003-06-13 16:51:38.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/compatmac.h	2005-05-12 18:01:29.000000000 +0200
@@ -1,571 +1,150 @@
-
 /*
- * mtd/include/compatmac.h
- *
- * $Id: compatmac.h,v 1.45 2003/01/24 15:50:57 dwmw2 Exp $
+ * $Id: compatmac.h,v 1.63 2003/11/14 19:50:04 thayne Exp $
  *
  * Extensions and omissions from the normal 'linux/compatmac.h'
  * files. hopefully this will end up empty as the 'real' one 
  * becomes fully-featured.
  */
 
-
-/* First, include the parts which the kernel is good enough to provide 
- * to us 
- */
-   
 #ifndef __LINUX_MTD_COMPATMAC_H__
 #define __LINUX_MTD_COMPATMAC_H__
 
-#include <linux/config.h>
-#include <linux/module.h>
-#ifndef LINUX_VERSION_CODE
 #include <linux/version.h>
-#endif
-
-#ifndef VERSION_CODE
-#  define VERSION_CODE(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) )
-#endif
-#ifndef KERNEL_VERSION
-#  define KERNEL_VERSION(a,b,c) VERSION_CODE(a,b,c)
-#endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0)
-#  error "This kernel is too old: not supported by this file"
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0)
-#include <linux/types.h> /* used later in this header */
-
-#define memcpy_fromio(a,b,c)    memcpy((a),(void *)(b),(c))
-#define memcpy_toio(a,b,c)      memcpy((void *)(a),(b),(c))
-
-typedef struct wait_queue * wait_queue_head_t;
-
-#define DECLARE_WAITQUEUE(x,y) struct wait_queue x = {y,NULL}
-#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL
-#define init_waitqueue_head init_waitqueue
-#define DECLARE_MUTEX(x) struct semaphore x = MUTEX
-#define DECLARE_MUTEX_LOCKED(x) struct semaphore x = MUTEX_LOCKED
-
-/* from sysdep-2.1.h */
-#  include <asm/segment.h>
-#  define access_ok(t,a,sz)           (verify_area((t),(a),(sz)) ? 0 : 1)
-#  define verify_area_20              verify_area
-#  define copy_to_user(t,f,n)         (memcpy_tofs(t,f,n), 0)
-#  define __copy_to_user(t,f,n)       copy_to_user((t),(f),(n))
-#  define copy_to_user_ret(t,f,n,r)   copy_to_user((t),(f),(n))
-#  define copy_from_user(t,f,n)       (memcpy_fromfs((t),(f),(n)), 0)
-#  define __copy_from_user(t,f,n)     copy_from_user((t),(f),(n))
-#  define copy_from_user_ret(t,f,n,r) copy_from_user((t),(f),(n))
-//xxx #  define PUT_USER(val,add)           (put_user((val),(add)), 0)
-#  define Put_user(val,add)           (put_user((val),(add)), 0)
-#  define __PUT_USER(val,add)         PUT_USER((val),(add))
-#  define PUT_USER_RET(val,add,ret)   PUT_USER((val),(add))
-#  define GET_USER(dest,add)          ((dest)=get_user((add)), 0)
-#  define __GET_USER(dest,add)        GET_USER((dest),(add))
-#  define GET_USER_RET(dest,add,ret)  GET_USER((dest),(add))
-
-#define ioremap(offset,size) vremap(offset,size)
-#define iounmap(adr)	/* */
-
-#define EXPORT_SYMBOL(s) /* */
-#define EXPORT_SYMBOL_NOVERS(s) /* */
-
-/* 2.1.10 and 2.1.43 introduced new functions. They are worth using */
-
-#if LINUX_VERSION_CODE < VERSION_CODE(2,1,10)
-
-#  include <asm/byteorder.h>
-#  ifdef __LITTLE_ENDIAN
-#    define cpu_to_le16(x) (x)
-#    define cpu_to_le32(x) (x)
-#    define cpu_to_be16(x) htons((x))
-#    define cpu_to_be32(x) htonl((x))
-#  else
-#    define cpu_to_be16(x) (x)
-#    define cpu_to_be32(x) (x)
-     extern inline __u16 cpu_to_le16(__u16 x) { return (x<<8) | (x>>8);}
-     extern inline __u32 cpu_to_le32(__u32 x) { return((x>>24) |
-             ((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24));}
-#  endif
-
-#  define le16_to_cpu(x)  cpu_to_le16(x)
-#  define le32_to_cpu(x)  cpu_to_le32(x)
-#  define be16_to_cpu(x)  cpu_to_be16(x)
-#  define be32_to_cpu(x)  cpu_to_be32(x)
-
-#endif
-
-#if LINUX_VERSION_CODE < VERSION_CODE(2,1,43)
-#  define cpu_to_le16p(addr) (cpu_to_le16(*(addr)))
-#  define cpu_to_le32p(addr) (cpu_to_le32(*(addr)))
-#  define cpu_to_be16p(addr) (cpu_to_be16(*(addr)))
-#  define cpu_to_be32p(addr) (cpu_to_be32(*(addr)))
-
-   extern inline void cpu_to_le16s(__u16 *a) {*a = cpu_to_le16(*a);}
-   extern inline void cpu_to_le32s(__u16 *a) {*a = cpu_to_le32(*a);}
-   extern inline void cpu_to_be16s(__u16 *a) {*a = cpu_to_be16(*a);}
-   extern inline void cpu_to_be32s(__u16 *a) {*a = cpu_to_be32(*a);}
-
-#  define le16_to_cpup(x) cpu_to_le16p(x)
-#  define le32_to_cpup(x) cpu_to_le32p(x)
-#  define be16_to_cpup(x) cpu_to_be16p(x)
-#  define be32_to_cpup(x) cpu_to_be32p(x)
-
-#  define le16_to_cpus(x) cpu_to_le16s(x)
-#  define le32_to_cpus(x) cpu_to_le32s(x)
-#  define be16_to_cpus(x) cpu_to_be16s(x)
-#  define be32_to_cpus(x) cpu_to_be32s(x)
-#endif
-
-// from 2.2, linux/types.h
-#ifndef __BIT_TYPES_DEFINED__
-#define __BIT_TYPES_DEFINED__
-
-typedef         __u8            u_int8_t;
-typedef         __s8            int8_t;
-typedef         __u16           u_int16_t;
-typedef         __s16           int16_t;
-typedef         __u32           u_int32_t;
-typedef         __s32           int32_t;
-
-#endif /* !(__BIT_TYPES_DEFINED__) */
-
-#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8)
-  typedef struct { } spinlock_t;
-  #define SPIN_LOCK_UNLOCKED (spinlock_t) { }
-#else
-  typedef struct { int gcc_is_buggy; } spinlock_t;
-  #define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 }
-#endif
-
-#define spin_lock_init(lock)    do { } while(0)
-#define spin_lock(lock)         (void)(lock) /* Not "unused variable". */
-#define spin_trylock(lock)      (1)
-#define spin_unlock_wait(lock)  do { } while(0)
-#define spin_unlock(lock)       do { } while(0)
-#define spin_lock_irq(lock)     cli()
-#define spin_unlock_irq(lock)   sti()
-
-#define spin_lock_irqsave(lock, flags) \
-        do { save_flags(flags); cli(); } while (0)
-#define spin_unlock_irqrestore(lock, flags) \
-        restore_flags(flags)
-
-// Doesn't work when tqueue.h is included. 
-// #define queue_task                   queue_task_irq_off
-#define tty_flip_buffer_push(tty)    queue_task_irq_off(&tty->flip.tqueue, &tq_timer)
-#define signal_pending(current)      (current->signal & ~current->blocked)
-#define schedule_timeout(to)         do {current->timeout = jiffies + (to);schedule ();} while (0)
-#define time_after(t1,t2)            (((long)t1-t2) > 0)
-
-#else
-  #include <linux/compatmac.h>
-#endif  // LINUX_VERSION_CODE < 0x020100
-
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-#include <linux/vmalloc.h>
-#endif
-
-/* Modularization issues */
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,18)
-#  define __USE_OLD_SYMTAB__
-#  define EXPORT_NO_SYMBOLS register_symtab(NULL);
-#  define REGISTER_SYMTAB(tab) register_symtab(tab)
-#else
-#  define REGISTER_SYMTAB(tab) /* nothing */
-#endif
-
-#ifdef __USE_OLD_SYMTAB__
-#  define __MODULE_STRING(s)         /* nothing */
-#  define MODULE_PARM(v,t)           /* nothing */
-#  define MODULE_PARM_DESC(v,t)      /* nothing */
-#  define MODULE_AUTHOR(n)           /* nothing */
-#  define MODULE_DESCRIPTION(d)      /* nothing */
-#  define MODULE_SUPPORTED_DEVICE(n) /* nothing */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
+#error "This kernel is too old: not supported by this file"
 #endif
 
-/*
- * "select" changed in 2.1.23. The implementation is twin, but this
- * header is new
- */
-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,22)
-#  include <linux/poll.h>
-#else
-#  define __USE_OLD_SELECT__
-#endif
+	/* O(1) scheduler stuff. */
 
-/* Other change in the fops are solved using pseudo-types */
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0)
-#  define lseek_t      long long
-#  define lseek_off_t  long long
-#else
-#  define lseek_t      int
-#  define lseek_off_t  off_t
-#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) && !defined(__rh_config_h__)
+#include <linux/sched.h>
+static inline void __recalc_sigpending(void)
+{
+	recalc_sigpending(current);
+}
+#undef recalc_sigpending
+#define recalc_sigpending() __recalc_sigpending ()
 
-/* changed the prototype of read/write */
+#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) || defined(__alpha__)
-# define count_t unsigned long
-# define read_write_t long
-#else
-# define count_t int
-# define read_write_t int
 #endif
 
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,31)
-# define release_t void
-#  define release_return(x) return
-#else
-#  define release_t int
-#  define release_return(x) return (x)
-#endif
-
-#if LINUX_VERSION_CODE < 0x20300
-#define __exit
-#endif
-#if LINUX_VERSION_CODE < 0x20200
-#define __init
-#else
-#include <linux/init.h>
-#endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
-#define init_MUTEX(x) do {*(x) = MUTEX;} while (0)
-#define init_MUTEX_LOCKED(x) do {*(x) = MUTEX_LOCKED;} while (0)
-#endif
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20)
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
-#define RQFUNC_ARG void
-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0)
-#else
-#define RQFUNC_ARG request_queue_t *q
+#ifndef yield
+#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0)
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,32)
-#define blk_cleanup_queue(nr) do {blk_dev[nr].request_fn = 0;} while(0)
-#define BLK_DEFAULT_QUEUE(nr) (blk_dev[nr].request_fn)
-#define blk_init_queue(q, rq) do {q = rq;} while(0)
+#ifndef minor
+#define major(d) (MAJOR(to_kdev_t(d)))
+#define minor(d) (MINOR(to_kdev_t(d)))
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
-#ifdef CONFIG_MODULES
-#define __MOD_INC_USE_COUNT(mod)                                        \
-        (atomic_inc(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED|MOD_USED_ONCE)
-#define __MOD_DEC_USE_COUNT(mod)                                        \
-        (atomic_dec(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED)
-#else
-#define __MOD_INC_USE_COUNT(mod)
-#define __MOD_DEC_USE_COUNT(mod)
-#endif
+#ifndef mk_kdev
+#define mk_kdev(ma,mi) MKDEV(ma,mi)
+#define kdev_t_to_nr(x)	(x)
 #endif
 
+#define need_resched() (current->need_resched)
+#define cond_resched() do { if need_resched() { yield(); } } while(0)
 
-#ifndef HAVE_INTER_MODULE
-static inline void *inter_module_get(char *x) {return NULL;}
-static inline void *inter_module_get_request(char *x, char *y) {return NULL;}
-static inline void inter_module_put(const char *x) {}
-static inline void inter_module_register(const char *x, struct module *y, const void *z) {}
-static inline void inter_module_unregister(const char *x) {}
+#endif /* < 2.4.20 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73)
+#define iminor(i) minor((i)->i_rdev)
+#define imajor(i) major((i)->i_rdev)
+#define old_encode_dev(d) ( (major(d)<<8) | minor(d) )
+#define old_decode_dev(rdev)  (kdev_t_to_nr(mk_kdev((rdev)>>8, (rdev)&0xff)))
+#define old_valid_dev(d) (1)
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61)
 
-#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL
-#define init_waitqueue_head init_waitqueue
+#include <linux/sched.h>
 
+#ifdef __rh_config_h__
+#define sigmask_lock sighand->siglock
+#define sig sighand
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
-
-static inline int try_inc_mod_count(struct module *mod)
+static inline void __daemonize_modvers(void)
 {
-#ifdef CONFIG_MODULES
-	if (mod)
-		__MOD_INC_USE_COUNT(mod);
-#endif
-	return 1;
-}
-#endif
-
-
-/* Yes, I'm aware that it's a fairly ugly hack.
-   Until the __constant_* macros appear in Linus' own kernels, this is
-   the way it has to be done.
- DW 19/1/00
- */
-
-#include <asm/byteorder.h>
-
-#ifndef __constant_cpu_to_le16
-
-#ifdef __BIG_ENDIAN
-#define __constant_cpu_to_le64(x) ___swab64((x))
-#define __constant_le64_to_cpu(x) ___swab64((x))
-#define __constant_cpu_to_le32(x) ___swab32((x))
-#define __constant_le32_to_cpu(x) ___swab32((x))
-#define __constant_cpu_to_le16(x) ___swab16((x))
-#define __constant_le16_to_cpu(x) ___swab16((x))
-#define __constant_cpu_to_be64(x) ((__u64)(x))
-#define __constant_be64_to_cpu(x) ((__u64)(x))
-#define __constant_cpu_to_be32(x) ((__u32)(x))
-#define __constant_be32_to_cpu(x) ((__u32)(x))
-#define __constant_cpu_to_be16(x) ((__u16)(x))
-#define __constant_be16_to_cpu(x) ((__u16)(x))
-#else
-#ifdef __LITTLE_ENDIAN
-#define __constant_cpu_to_le64(x) ((__u64)(x))
-#define __constant_le64_to_cpu(x) ((__u64)(x))
-#define __constant_cpu_to_le32(x) ((__u32)(x))
-#define __constant_le32_to_cpu(x) ((__u32)(x))
-#define __constant_cpu_to_le16(x) ((__u16)(x))
-#define __constant_le16_to_cpu(x) ((__u16)(x))
-#define __constant_cpu_to_be64(x) ___swab64((x))
-#define __constant_be64_to_cpu(x) ___swab64((x))
-#define __constant_cpu_to_be32(x) ___swab32((x))
-#define __constant_be32_to_cpu(x) ___swab32((x))
-#define __constant_cpu_to_be16(x) ___swab16((x))
-#define __constant_be16_to_cpu(x) ___swab16((x))
-#else
-#error No (recognised) endianness defined (unless it,s PDP)
-#endif /* __LITTLE_ENDIAN */
-#endif /* __BIG_ENDIAN */
-
-#endif /* ifndef __constant_cpu_to_le16 */
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0)
-  #define mod_init_t int  __init
-  #define mod_exit_t void  
-#else
-  #define mod_init_t static int __init
-  #define mod_exit_t static void __exit
-#endif
-
-#ifndef THIS_MODULE
-#ifdef MODULE
-#define THIS_MODULE (&__this_module)
-#else
-#define THIS_MODULE (NULL)
-#endif
-#endif
-
-#if LINUX_VERSION_CODE < 0x20300
-#include <linux/interrupt.h>
-#define spin_lock_bh(lock) do {start_bh_atomic();spin_lock(lock);}while(0)
-#define spin_unlock_bh(lock) do {spin_unlock(lock);end_bh_atomic();}while(0)
-#else
-#include <asm/softirq.h>
-#include <linux/spinlock.h>
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18)
-#define set_current_state(state_value)                        \
-        do { current->state = (state_value); } while (0)
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) 
-static inline int invalidate_device(kdev_t dev, int do_sync) {
-
-	if (do_sync)
-		fsync_dev(dev);
-	
-	invalidate_buffers(dev);
-	return 0;
-}
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5)
-static inline int invalidate_device(kdev_t dev, int do_sync) {
-	struct super_block *sb = get_super(dev);
-	int res = 0;
-
-	if (do_sync)
-		fsync_dev(dev);
-	
-	if (sb)
-		res = invalidate_inodes(sb);
-
-	invalidate_buffers(dev);
-	return res;
-}
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
-#undef min
-#undef max
-#undef min_t
-#undef max_t
-/*
- * min()/max() macros that also do
- * strict type-checking.. See the
- * "unnecessary" pointer comparison.
- */
-#define min(x,y) ({ \
-        const typeof(x) _x = (x);       \
-        const typeof(y) _y = (y);       \
-        (void) (&_x == &_y);            \
-        _x < _y ? _x : _y; })
-
-#define max(x,y) ({ \
-        const typeof(x) _x = (x);       \
-        const typeof(y) _y = (y);       \
-        (void) (&_x == &_y);            \
-        _x > _y ? _x : _y; })
-
-/*
- * ..and if you can't take the strict
- * types, you can specify one yourself.
- *
- * Or not use min/max at all, of course.
- */
-#define min_t(type,x,y) \
-        ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; })
-#define max_t(type,x,y) \
-        ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; })
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,7)
-struct completion {
-	struct semaphore s;
-};
-
-#define complete(c) up(&(c)->s)
-#define wait_for_completion(c) down(&(c)->s)
-#define init_completion(c) init_MUTEX_LOCKED(&(c)->s);
+	daemonize();
 
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9)
-/* This came later */
-#define complete_and_exit(c, r) do { complete(c); do_exit(r); } while(0)
-#endif
+	spin_lock_irq(&current->sigmask_lock);
+	sigfillset(&current->blocked);
+	recalc_sigpending();
+	spin_unlock_irq(&current->sigmask_lock);
+}
+#undef daemonize
+#define daemonize(fmt, ...) do {						\
+	snprintf(current->comm, sizeof(current->comm), fmt ,##__VA_ARGS__);	\
+	__daemonize_modvers();							\
+	} while(0)
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \
-    (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__))
+static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
+{
+	unsigned long flags;
+	unsigned long ret;
 
-#include <linux/genhd.h>
+	spin_lock_irqsave(&current->sigmask_lock, flags);
+	ret = dequeue_signal(mask, info);
+	spin_unlock_irqrestore(&current->sigmask_lock, flags);
 
-static inline void add_gendisk(struct gendisk *gp)
-{
-	gp->next = gendisk_head;
-	gendisk_head = gp;
+	return ret;
 }
 
-static inline void del_gendisk(struct gendisk *gp)
+static inline int allow_signal(int sig)
 {
-	struct gendisk *gd, **gdp;
+	if (sig < 1 || sig > _NSIG)
+		return -EINVAL;
 
-	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
-		if (*gdp == gp) {
-			gd = *gdp; *gdp = gd->next;
-			break;
-		}
+        spin_lock_irq(&current->sigmask_lock);
+	sigdelset(&current->blocked, sig);
+	recalc_sigpending();
+	/* Make sure the kernel neither eats it now converts to SIGKILL */
+	current->sig->action[sig-1].sa.sa_handler = (void *)2;
+	spin_unlock_irq(&current->sigmask_lock);
+	return 0;
 }
-#endif
+static inline int disallow_signal(int sig)
+{
+	if (sig < 1 || sig > _NSIG)
+		return -EINVAL;
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) && defined(MODULE)
+	spin_lock_irq(&current->sigmask_lock);
+	sigaddset(&current->blocked, sig);
+	recalc_sigpending();
 
-#define module_init(func)		\
-mod_init_t init_module(void) {		\
-	return func();			\
+	current->sig->action[sig-1].sa.sa_handler = SIG_DFL;
+	spin_unlock_irq(&current->sigmask_lock);
+	return 0;
 }
 
-#define module_exit(func)		\
-mod_exit_t cleanup_module(void) {	\
-	return func();			\
-}
-#endif
+#undef sighand
+#undef sigmask_lock
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \
-    (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__))
-#define MODULE_LICENSE(x) /* */
+#define PF_FREEZE 0
+#define refrigerator(x) do { ; } while(0)
 #endif
 
-/* Removed for 2.4.21 kernel. This really should have been renamed
-   when it was changed -- this is a PITA */
-#if 0 && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
-#include <linux/sched.h>
-static inline void __recalc_sigpending(void)
-{
-	recalc_sigpending(current);
-}
-#undef recalc_sigpending
-#define recalc_sigpending() __recalc_sigpending ()
-#endif
+	/* Module bits */
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
-#define parent_ino(d) ((d)->d_parent->d_inode->i_ino)
-#endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,3)
-#define need_resched() (current->need_resched)
-#define cond_resched() do { if need_resched() schedule(); } while(0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,60)
+#define try_module_get(m) try_inc_mod_count(m)
+#define __module_get(m) do { if (!try_inc_mod_count(m)) BUG(); } while(0)
+#define module_put(m) do { if (m) __MOD_DEC_USE_COUNT((struct module *)(m)); } while(0)
+#define set_module_owner(x) do { x->owner = THIS_MODULE; } while(0)
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19)
-#ifndef yield
-#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0)
-#endif
-#ifndef minor
-#define major(d) (MAJOR(to_kdev_t(d)))
-#define minor(d) (MINOR(to_kdev_t(d)))
-#endif
-#ifndef mk_kdev
-#define mk_kdev(ma,mi) MKDEV(ma,mi)
-#define kdev_t_to_nr(x)	(x)
-#endif
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0)
-	/* Is this right? */
-#define set_user_nice(tsk, n) do { (tsk)->priority = 20-(n); } while(0) 
-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21) && !defined(RED_HAT_LINUX_KERNEL)
-#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21)
-#define rq_data_dir(x)	((x)->cmd)
-#endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
+	/* Random filesystem stuff, only for JFFS2 really */
 
-#define IS_REQ_CMD(req) (1)
-
-#define QUEUE_LOCK(q) (&io_request_lock)
-
-#define BLK_INIT_QUEUE(q, req, lock) blk_init_queue((q), (req)) 
-
-#else /* > 2.5.0 */
-
-#define IS_REQ_CMD(req) ((req)->flags & REQ_CMD)
-
-#define QUEUE_LOCK(q) ((q)->queue_lock)
-
-#define BLK_INIT_QUEUE(q, req, lock) blk_init_queue((q), (req), (lock)) 
-
-#endif
-
-/* Removed cos it broke stuff. Where is this required anyway? 
- * #ifndef QUEUE_EMPTY
- * #define QUEUE_EMPTY  (!CURRENT)
- * #endif
- */
-#if LINUX_VERSION_CODE < 0x20300
-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync)
-#elif LINUX_VERSION_CODE < 0x20500 //FIXME (Si)
-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged)
-#else
-#define QUEUE_PLUGGED (blk_queue_plugged(QUEUE))
-#endif
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14)
-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
-#else
-#define BLK_INC_USE_COUNT do {} while(0)
-#define BLK_DEC_USE_COUNT do {} while(0)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5)
+#define parent_ino(d) ((d)->d_parent->d_inode->i_ino)
 #endif
 
 #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
@@ -580,4 +159,31 @@
 #define generic_file_readonly_mmap generic_file_mmap
 #endif
 
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70)
+
+#include <linux/kmod.h>
+#include <linux/string.h>
+
+static inline char *strlcpy(char *dest, const char *src, int len)
+{
+	dest[len-1] = 0;
+	return strncpy(dest, src, len-1);
+}
+
+static inline int do_old_request_module(const char *mod)
+{
+	return request_module(mod);
+}
+#undef request_module
+#define request_module(fmt, ...) \
+ ({ char modname[32]; snprintf(modname, 31, fmt ,##__VA_ARGS__); do_old_request_module(modname); })
+
+#endif /* 2.5.70 */
+
+#ifndef container_of
+#define container_of(ptr, type, member) ({		     \
+	const typeof( ((type *)0)->member ) *__mptr = (ptr); \
+	(type *)( (char *)__mptr - offsetof(type,member) );})
+#endif
+
 #endif /* __LINUX_MTD_COMPATMAC_H__ */
diff -Nur linux-2.4.30/include/linux/mtd/doc2000.h linux-2.4.30-linux-mips.org/include/linux/mtd/doc2000.h
--- linux-2.4.30/include/linux/mtd/doc2000.h	2001-10-05 00:13:18.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/doc2000.h	2005-05-12 18:01:29.000000000 +0200
@@ -1,13 +1,21 @@
-
-/* Linux driver for Disk-On-Chip 2000       */
-/* (c) 1999 Machine Vision Holdings, Inc.   */
-/* Author: David Woodhouse <dwmw2@mvhi.com> */
-/* $Id: doc2000.h,v 1.15 2001/09/19 00:22:15 dwmw2 Exp $ */
+/* 
+ * Linux driver for Disk-On-Chip devices
+ *
+ * Copyright (C) 1999 Machine Vision Holdings, Inc.   
+ * Copyright (C) 2001-2003 David Woodhouse <dwmw2@infradead.org>
+ * 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 $ 
+ *
+ * Released under GPL
+ */
 
 #ifndef __MTD_DOC2000_H__
 #define __MTD_DOC2000_H__
 
 #include <linux/mtd/mtd.h>
+#include <asm/semaphore.h>
 
 #define DoC_Sig1 0
 #define DoC_Sig2 1
@@ -38,18 +46,47 @@
 #define DoC_Mil_CDSN_IO 	0x0800
 #define DoC_2k_CDSN_IO 		0x1800
 
+#define DoC_Mplus_NOP			0x1002
+#define DoC_Mplus_AliasResolution	0x1004
+#define DoC_Mplus_DOCControl		0x1006
+#define DoC_Mplus_AccessStatus		0x1008
+#define DoC_Mplus_DeviceSelect		0x1008
+#define DoC_Mplus_Configuration		0x100a
+#define DoC_Mplus_OutputControl		0x100c
+#define DoC_Mplus_FlashControl		0x1020
+#define DoC_Mplus_FlashSelect 		0x1022
+#define DoC_Mplus_FlashCmd		0x1024
+#define DoC_Mplus_FlashAddress		0x1026
+#define DoC_Mplus_FlashData0		0x1028
+#define DoC_Mplus_FlashData1		0x1029
+#define DoC_Mplus_ReadPipeInit		0x102a
+#define DoC_Mplus_LastDataRead		0x102c
+#define DoC_Mplus_LastDataRead1		0x102d
+#define DoC_Mplus_WritePipeTerm 	0x102e
+#define DoC_Mplus_ECCSyndrome0		0x1040
+#define DoC_Mplus_ECCSyndrome1		0x1041
+#define DoC_Mplus_ECCSyndrome2		0x1042
+#define DoC_Mplus_ECCSyndrome3		0x1043
+#define DoC_Mplus_ECCSyndrome4		0x1044
+#define DoC_Mplus_ECCSyndrome5		0x1045
+#define DoC_Mplus_ECCConf 		0x1046
+#define DoC_Mplus_Toggle		0x1046
+#define DoC_Mplus_DownloadStatus	0x1074
+#define DoC_Mplus_CtrlConfirm		0x1076
+#define DoC_Mplus_Power			0x1fff
+
 /* How to access the device? 
  * On ARM, it'll be mmap'd directly with 32-bit wide accesses. 
  * On PPC, it's mmap'd and 16-bit wide.
  * Others use readb/writeb 
  */
 #if defined(__arm__)
-#define ReadDOC_(adr, reg)      ((unsigned char)(*(__u32 *)(((unsigned long)adr)+((reg)<<2))))
-#define WriteDOC_(d, adr, reg)  do{ *(__u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0)
+#define ReadDOC_(adr, reg)      ((unsigned char)(*(volatile __u32 *)(((unsigned long)adr)+((reg)<<2))))
+#define WriteDOC_(d, adr, reg)  do{ *(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0)
 #define DOC_IOREMAP_LEN 0x8000
 #elif defined(__ppc__)
-#define ReadDOC_(adr, reg)      ((unsigned char)(*(__u16 *)(((unsigned long)adr)+((reg)<<1))))
-#define WriteDOC_(d, adr, reg)  do{ *(__u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0)
+#define ReadDOC_(adr, reg)      ((unsigned char)(*(volatile __u16 *)(((unsigned long)adr)+((reg)<<1))))
+#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))
@@ -71,13 +108,21 @@
 #define DOC_MODE_RESERVED1 	2
 #define DOC_MODE_RESERVED2 	3
 
-#define DOC_MODE_MDWREN 	4
 #define DOC_MODE_CLR_ERR 	0x80
+#define	DOC_MODE_RST_LAT	0x10
+#define	DOC_MODE_BDECT		0x08
+#define DOC_MODE_MDWREN 	0x04
 
 #define DOC_ChipID_Doc2k 	0x20
+#define DOC_ChipID_Doc2kTSOP 	0x21	/* internal number for MTD */
 #define DOC_ChipID_DocMil 	0x30
+#define DOC_ChipID_DocMilPlus32	0x40
+#define DOC_ChipID_DocMilPlus16	0x41
 
 #define CDSN_CTRL_FR_B 		0x80
+#define CDSN_CTRL_FR_B0		0x40
+#define CDSN_CTRL_FR_B1		0x80
+
 #define CDSN_CTRL_ECC_IO 	0x20
 #define CDSN_CTRL_FLASH_IO 	0x10
 #define CDSN_CTRL_WP 		0x08
@@ -93,6 +138,10 @@
 #define DOC_ECC_RESV 		0x02
 #define DOC_ECC_IGNORE		0x01
 
+#define DOC_FLASH_CE		0x80
+#define DOC_FLASH_WP		0x40
+#define DOC_FLASH_BANK		0x02
+
 /* We have to also set the reserved bit 1 for enable */
 #define DOC_ECC_EN (DOC_ECC__EN | DOC_ECC_RESV)
 #define DOC_ECC_DIS (DOC_ECC_RESV)
@@ -107,9 +156,12 @@
 #define MAX_FLOORS 4
 #define MAX_CHIPS 4
 
-#define MAX_FLOORS_MIL 4
+#define MAX_FLOORS_MIL 1
 #define MAX_CHIPS_MIL 1
 
+#define MAX_FLOORS_MPLUS 2
+#define MAX_CHIPS_MPLUS 1
+
 #define ADDR_COLUMN 1
 #define ADDR_PAGE 2
 #define ADDR_COLUMN_PAGE 3
@@ -118,7 +170,7 @@
 	unsigned long physadr;
 	unsigned long virtadr;
 	unsigned long totlen;
-	char ChipID; /* Type of DiskOnChip */
+	unsigned char ChipID; /* Type of DiskOnChip */
 	int ioreg;
 	
 	unsigned long mfr; /* Flash IDs - only one type of flash per device */
@@ -126,6 +178,7 @@
 	int chipshift;
 	char page256;
 	char pageadrlen;
+	char interleave; /* Internal interleaving - Millennium Plus style */
 	unsigned long erasesize;
 	
 	int curfloor;
diff -Nur linux-2.4.30/include/linux/mtd/flashchip.h linux-2.4.30-linux-mips.org/include/linux/mtd/flashchip.h
--- linux-2.4.30/include/linux/mtd/flashchip.h	2003-06-13 16:51:38.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/flashchip.h	2005-05-12 18:01:29.000000000 +0200
@@ -6,7 +6,7 @@
  *
  * (C) 2000 Red Hat. GPLd.
  *
- * $Id: flashchip.h,v 1.8 2002/10/21 13:20:52 jocke Exp $
+ * $Id: flashchip.h,v 1.10 2004/01/27 10:16:20 dvrabel Exp $
  *
  */
 
@@ -58,6 +58,11 @@
 	int ref_point_counter;
 	flstate_t state;
 	flstate_t oldstate;
+
+	int write_suspended:1;
+	int erase_suspended:1;
+	unsigned long in_progress_block_addr;
+
 	spinlock_t *mutex;
 	spinlock_t _spinlock; /* We do it like this because sometimes they'll be shared. */
 	wait_queue_head_t wq; /* Wait on here when we're waiting for the chip
diff -Nur linux-2.4.30/include/linux/mtd/gen_probe.h linux-2.4.30-linux-mips.org/include/linux/mtd/gen_probe.h
--- linux-2.4.30/include/linux/mtd/gen_probe.h	2001-10-05 00:13:18.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/gen_probe.h	2005-05-12 18:01:29.000000000 +0200
@@ -1,7 +1,7 @@
 /*
  * (C) 2001, 2001 Red Hat, Inc.
  * GPL'd
- * $Id: gen_probe.h,v 1.1 2001/09/02 18:50:13 dwmw2 Exp $
+ * $Id: gen_probe.h,v 1.2 2003/11/08 00:51:21 dsaxena Exp $
  */
 
 #ifndef __LINUX_MTD_GEN_PROBE_H__
@@ -10,12 +10,12 @@
 #include <linux/mtd/flashchip.h>
 #include <linux/mtd/map.h> 
 #include <linux/mtd/cfi.h>
+#include <asm/bitops.h>
 
 struct chip_probe {
 	char *name;
 	int (*probe_chip)(struct map_info *map, __u32 base,
-			  struct flchip *chips, struct cfi_private *cfi);
-
+			  unsigned long *chip_map, struct cfi_private *cfi);
 };
 
 struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp);
diff -Nur linux-2.4.30/include/linux/mtd/inftl.h linux-2.4.30-linux-mips.org/include/linux/mtd/inftl.h
--- linux-2.4.30/include/linux/mtd/inftl.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/inftl.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,56 @@
+/*
+ *	inftl.h -- defines to support the Inverse NAND Flash Translation Layer
+ *
+ *	(C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
+ *
+ *	$Id: inftl.h,v 1.5 2004/05/05 15:16:59 dwmw2 Exp $
+ */
+
+#ifndef __MTD_INFTL_H__
+#define __MTD_INFTL_H__
+
+#ifndef __KERNEL__
+#error This is a kernel header. Perhaps include nftl-user.h instead?
+#endif
+
+#include <linux/mtd/blktrans.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nftl.h>
+
+#include <mtd/inftl-user.h>
+
+#ifndef INFTL_MAJOR
+#define INFTL_MAJOR 94
+#endif
+#define INFTL_PARTN_BITS 4
+
+#ifdef __KERNEL__
+
+struct INFTLrecord {
+	struct mtd_blktrans_dev mbd;
+	__u16 MediaUnit, SpareMediaUnit;
+	__u32 EraseSize;
+	struct INFTLMediaHeader MediaHdr;
+	int usecount;
+	unsigned char heads;
+	unsigned char sectors;
+	unsigned short cylinders;
+	__u16 numvunits;
+	__u16 firstEUN;
+	__u16 lastEUN;
+	__u16 numfreeEUNs;
+	__u16 LastFreeEUN; 		/* To speed up finding a free EUN */
+	int head,sect,cyl;
+	__u16 *PUtable;	 		/* Physical Unit Table  */
+	__u16 *VUtable; 		/* Virtual Unit Table */
+        unsigned int nb_blocks;		/* number of physical blocks */
+        unsigned int nb_boot_blocks;	/* number of blocks used by the bios */
+        struct erase_info instr;
+};
+
+int INFTL_mount(struct INFTLrecord *s);
+int INFTL_formatblock(struct INFTLrecord *s, int block);
+
+#endif /* __KERNEL__ */
+
+#endif /* __MTD_INFTL_H__ */
diff -Nur linux-2.4.30/include/linux/mtd/jedec.h linux-2.4.30-linux-mips.org/include/linux/mtd/jedec.h
--- linux-2.4.30/include/linux/mtd/jedec.h	2001-11-09 23:29:40.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/jedec.h	2005-05-12 18:01:29.000000000 +0200
@@ -7,14 +7,13 @@
  *
  * See the AMD flash databook for information on how to operate the interface.
  *
- * $Id: jedec.h,v 1.2 2001/11/06 14:37:36 dwmw2 Exp $
+ * $Id: jedec.h,v 1.3 2003/05/21 11:51:01 dwmw2 Exp $
  */
 
 #ifndef __LINUX_MTD_JEDEC_H__
 #define __LINUX_MTD_JEDEC_H__
 
 #include <linux/types.h>
-#include <linux/mtd/map.h>
 
 #define MAX_JEDEC_CHIPS 16
 
diff -Nur linux-2.4.30/include/linux/mtd/map.h linux-2.4.30-linux-mips.org/include/linux/mtd/map.h
--- linux-2.4.30/include/linux/mtd/map.h	2003-06-13 16:51:38.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/map.h	2005-05-12 18:01:29.000000000 +0200
@@ -1,14 +1,15 @@
 
 /* Overhauled routines for dealing with different mmap regions of flash */
-/* $Id: map.h,v 1.29 2002/10/21 13:20:52 jocke Exp $ */
+/* $Id: map.h,v 1.35 2004/04/02 01:25:24 sjhill Exp $ */
 
 #ifndef __LINUX_MTD_MAP_H__
 #define __LINUX_MTD_MAP_H__
 
 #include <linux/config.h>
 #include <linux/types.h>
-#include <linux/mtd/mtd.h>
-#include <linux/slab.h>
+#include <linux/list.h>
+#include <asm/system.h>
+#include <asm/io.h>
 
 /* 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
@@ -29,39 +30,44 @@
 struct map_info {
 	char *name;
 	unsigned long size;
+	unsigned long phys;
+#define NO_XIP (-1UL)
+
+	unsigned long virt;
+	void *cached;
+
 	int buswidth; /* in octets */
-	__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);  
+
+#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 */
 	
 	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 (*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 (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
 
-	u_char * (*point) (struct map_info *, loff_t, size_t);
-	void (*unpoint) (struct map_info *, u_char *, loff_t, size_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
+	/* set_vpp() must handle being reentered -- enable, enable, disable 
+	   must leave it enabled. */
 	void (*set_vpp)(struct map_info *, int);
-	/* We put these two here rather than a single void *map_priv, 
-	   because we want mappers to be able to have quickly-accessible
-	   cache for the 'currently-mapped page' without the _extra_
-	   redirection that would be necessary. If you need more than
-	   two longs, turn the second into a pointer. dwmw2 */
+
 	unsigned long map_priv_1;
 	unsigned long map_priv_2;
 	void *fldrv_priv;
 	struct mtd_chip_driver *fldrv;
 };
 
-
 struct mtd_chip_driver {
 	struct mtd_info *(*probe)(struct map_info *map);
 	void (*destroy)(struct mtd_info *);
@@ -74,26 +80,93 @@
 void unregister_mtd_chip_driver(struct mtd_chip_driver *);
 
 struct mtd_info *do_map_probe(const char *name, struct map_info *map);
+void map_destroy(struct mtd_info *mtd);
+
+#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)
 
+extern void simple_map_init(struct map_info *);
+#define map_is_linear(map) (map->phys != NO_XIP)
 
-/*
- * Destroy an MTD device which was created for a map device.
- * Make sure the MTD device is already unregistered before calling this
- */
-static inline void map_destroy(struct mtd_info *mtd)
-{
-	struct map_info *map = mtd->priv;
-
-	if (map->fldrv->destroy)
-		map->fldrv->destroy(mtd);
-#ifdef CONFIG_MODULES
-	if (map->fldrv->module)
-		__MOD_DEC_USE_COUNT(map->fldrv->module);
+#else
+static inline u8 map_read8(struct map_info *map, unsigned long ofs)
+{
+	return __raw_readb(map->virt + ofs);
+}
+
+static inline u16 map_read16(struct map_info *map, unsigned long ofs)
+{
+	return __raw_readw(map->virt + ofs);
+}
+
+static inline u32 map_read32(struct map_info *map, unsigned long ofs)
+{
+	return __raw_readl(map->virt + ofs);
+}
+
+static inline u64 map_read64(struct map_info *map, unsigned long ofs)
+{
+#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
+	BUG();
+	return 0;
+#else
+	return __raw_readq(map->virt + ofs);
 #endif
-	kfree(mtd);
 }
 
-#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)
+static inline void map_write8(struct map_info *map, u8 datum, unsigned long ofs)
+{
+	__raw_writeb(datum, map->virt + ofs);
+	mb();
+}
+
+static inline void map_write16(struct map_info *map, u16 datum, unsigned long ofs)
+{
+	__raw_writew(datum, map->virt + ofs);
+	mb();
+}
+
+static inline void map_write32(struct map_info *map, u32 datum, unsigned long ofs)
+{
+	__raw_writel(datum, map->virt + ofs);
+	mb();
+}
+
+static inline void map_write64(struct map_info *map, u64 datum, unsigned long ofs)
+{
+#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */
+	BUG();
+#else
+	__raw_writeq(datum, map->virt + ofs);
+	mb();
+#endif /* CFI_B8 */
+}
+
+static inline void map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+{
+	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)
+{
+	memcpy_toio(map->virt + to, from, len);
+}
+
+#define simple_map_init(map) do { } while (0)
+#define map_is_linear(map) (1)
+
+#endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */
 
 #endif /* __LINUX_MTD_MAP_H__ */
diff -Nur linux-2.4.30/include/linux/mtd/mtd.h linux-2.4.30-linux-mips.org/include/linux/mtd/mtd.h
--- linux-2.4.30/include/linux/mtd/mtd.h	2003-08-25 13:44:44.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/mtd.h	2005-05-12 18:01:29.000000000 +0200
@@ -1,113 +1,30 @@
-
-/* $Id: mtd.h,v 1.38 2003/01/12 16:30:19 spse Exp $ */
+/* 
+ * $Id: mtd.h,v 1.51 2004/05/05 14:44:56 dwmw2 Exp $
+ *
+ * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
+ *
+ * Released under GPL
+ */
 
 #ifndef __MTD_MTD_H__
 #define __MTD_MTD_H__
 
-#ifdef __KERNEL__
+#ifndef __KERNEL__
+#error This is a kernel header. Perhaps include mtd-user.h instead?
+#endif
 
 #include <linux/config.h>
 #include <linux/version.h>
 #include <linux/types.h>
-#include <linux/mtd/compatmac.h>
 #include <linux/module.h>
 #include <linux/uio.h>
 
-#endif /* __KERNEL__ */
-
-struct erase_info_user {
-	u_int32_t start;
-	u_int32_t length;
-};
-
-struct mtd_oob_buf {
-	u_int32_t start;
-	u_int32_t length;
-	unsigned char *ptr;
-};
-
+#include <mtd/mtd-abi.h>
 
 #define MTD_CHAR_MAJOR 90
 #define MTD_BLOCK_MAJOR 31
 #define MAX_MTD_DEVICES 16
 
-
-
-#define MTD_ABSENT		0
-#define MTD_RAM			1
-#define MTD_ROM			2
-#define MTD_NORFLASH		3
-#define MTD_NANDFLASH		4
-#define MTD_PEROM		5
-#define MTD_OTHER		14
-#define MTD_UNKNOWN		15
-
-
-
-#define MTD_CLEAR_BITS		1       // Bits can be cleared (flash)
-#define MTD_SET_BITS		2       // Bits can be set
-#define MTD_ERASEABLE		4       // Has an erase function
-#define MTD_WRITEB_WRITEABLE	8       // Direct IO is possible
-#define MTD_VOLATILE		16      // Set for RAMs
-#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
-
-// Some common devices / combinations of capabilities
-#define MTD_CAP_ROM		0
-#define MTD_CAP_RAM		(MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
-#define MTD_CAP_NORFLASH        (MTD_CLEAR_BITS|MTD_ERASEABLE)
-#define MTD_CAP_NANDFLASH       (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
-#define MTD_WRITEABLE		(MTD_CLEAR_BITS|MTD_SET_BITS)
-
-
-// Types of automatic ECC/Checksum available
-#define MTD_ECC_NONE		0 	// No automatic ECC available
-#define MTD_ECC_RS_DiskOnChip	1	// Automatic ECC on DiskOnChip
-#define MTD_ECC_SW		2	// SW ECC for Toshiba & Samsung devices
-
-struct mtd_info_user {
-	u_char type;
-	u_int32_t flags;
-	u_int32_t size;	 // Total size of the MTD
-	u_int32_t erasesize;
-	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 ecctype;
-	u_int32_t eccsize;
-};
-
-struct region_info_user {
-	u_int32_t offset;		/* At which this region starts, 
-					 * from the beginning of the MTD */
-	u_int32_t erasesize;		/* For this region */
-	u_int32_t numblocks;		/* Number of blocks in this region */
-	u_int32_t regionindex;
-};
-
-#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)
-#define MEMREADOOB              _IOWR('M', 4, struct mtd_oob_buf)
-#define MEMLOCK                 _IOW('M', 5, struct erase_info_user)
-#define MEMUNLOCK               _IOW('M', 6, struct erase_info_user)
-#define MEMGETREGIONCOUNT	_IOR('M', 7, int)
-#define MEMGETREGIONINFO	_IOWR('M', 8, struct region_info_user)
-#define	MEMREADDATA             _IOWR('M', 9, struct mtd_oob_buf)
-#define	MEMWRITEDATA            _IOWR('M', 10, struct mtd_oob_buf)
-
-#ifndef __KERNEL__
-
-typedef struct mtd_info_user mtd_info_t;
-typedef struct erase_info_user erase_info_t;
-typedef struct region_info_user region_info_t;
-
-	/* User-space ioctl definitions */
-
-
-#else /* __KERNEL__ */
-
-
 #define MTD_ERASE_PENDING      	0x01
 #define MTD_ERASING		0x02
 #define MTD_ERASE_SUSPEND	0x04
@@ -147,13 +64,18 @@
 
 	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;
+	
 
 	// Kernel-only stuff starts here.
 	char *name;
 	int index;
 
+	// oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO)
+	struct nand_oobinfo oobinfo;
+
 	/* Data for variable erase regions. If numeraseregions is zero,
 	 * it means that the whole device has erasesize as given above. 
 	 */
@@ -163,7 +85,6 @@
 	/* This really shouldn't be here. It can go away in 2.5 */
 	u_int32_t bank_size;
 
-	struct module *module;
 	int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
 
 	/* This stuff for eXecute-In-Place */
@@ -176,8 +97,8 @@
 	int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
 	int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
 
-	int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel);
-	int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel);
+	int (*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);
+	int (*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 (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
 	int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
@@ -201,10 +122,10 @@
 	*/
 	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, 
-		size_t *retlen, u_char *eccbuf, int oobsel);
+		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, 
-		size_t *retlen, u_char *eccbuf, int oobsel);
+		size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
 
 	/* Sync */
 	void (*sync) (struct mtd_info *mtd);
@@ -217,7 +138,14 @@
 	int (*suspend) (struct mtd_info *mtd);
 	void (*resume) (struct mtd_info *mtd);
 
+	/* Bad block management functions */
+	int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
+	int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
+
 	void *priv;
+
+	struct module *owner;
+	int usecount;
 };
 
 
@@ -226,31 +154,15 @@
 extern int add_mtd_device(struct mtd_info *mtd);
 extern int del_mtd_device (struct mtd_info *mtd);
 
-extern struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num);
+extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
 
-static inline struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num)
-{
-	struct mtd_info *ret;
-	
-	ret = __get_mtd_device(mtd, num);
-
-	if (ret && ret->module && !try_inc_mod_count(ret->module))
-		return NULL;
-
-	return ret;
-}
-
-static inline void put_mtd_device(struct mtd_info *mtd)
-{
-       if (mtd->module)
-	       __MOD_DEC_USE_COUNT(mtd->module);
-}
+extern void put_mtd_device(struct mtd_info *mtd);
 
 
 struct mtd_notifier {
 	void (*add)(struct mtd_info *mtd);
 	void (*remove)(struct mtd_info *mtd);
-	struct mtd_notifier *next;
+	struct list_head list;
 };
 
 
@@ -263,7 +175,6 @@
 int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
 		      unsigned long count, loff_t from, size_t *retlen);
 
-#ifndef MTDC
 #define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args)
 #define MTD_POINT(mtd, a,b,c,d) (*(mtd->point))(mtd, a,b,c, (u_char **)(d))
 #define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg)
@@ -276,7 +187,6 @@
 #define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args)
 #define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args)
 #define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd);  } while (0) 
-#endif /* MTDC */
 
 /*
  * Debugging macro and defines
@@ -293,9 +203,8 @@
 			printk(KERN_INFO args);		\
 	} while(0)
 #else /* CONFIG_MTD_DEBUG */
-#define DEBUG(n, args...)
-#endif /* CONFIG_MTD_DEBUG */
+#define DEBUG(n, args...) do { } while(0)
 
-#endif /* __KERNEL__ */
+#endif /* CONFIG_MTD_DEBUG */
 
 #endif /* __MTD_MTD_H__ */
diff -Nur linux-2.4.30/include/linux/mtd/nand_ecc.h linux-2.4.30-linux-mips.org/include/linux/mtd/nand_ecc.h
--- linux-2.4.30/include/linux/mtd/nand_ecc.h	2001-06-12 19:30:27.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/nand_ecc.h	2005-05-12 18:01:29.000000000 +0200
@@ -1,9 +1,9 @@
 /*
  *  drivers/mtd/nand_ecc.h
  *
- *  Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
+ *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
  *
- * $Id: nand_ecc.h,v 1.1 2000/10/12 00:57:15 sjhill Exp $
+ * $Id: nand_ecc.h,v 1.3 2003/07/01 23:31:15 dwmw2 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
@@ -12,17 +12,19 @@
  * This file is the header for the ECC algorithm.
  */
 
-/*
- * Creates non-inverted ECC code from line parity
- */
-void nand_trans_result(u_char reg2, u_char reg3, u_char *ecc_code);
+#ifndef __MTD_NAND_ECC_H__
+#define __MTD_NAND_ECC_H__
+
+struct mtd_info;
 
 /*
  * Calculate 3 byte ECC code for 256 byte block
  */
-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
+void 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
  */
-int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc);
+int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
+
+#endif /* __MTD_NAND_ECC_H__ */
diff -Nur linux-2.4.30/include/linux/mtd/nand.h linux-2.4.30-linux-mips.org/include/linux/mtd/nand.h
--- linux-2.4.30/include/linux/mtd/nand.h	2003-06-13 16:51:38.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/nand.h	2005-05-12 18:01:29.000000000 +0200
@@ -2,10 +2,10 @@
  *  linux/include/linux/mtd/nand.h
  *
  *  Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com>
- *                     Steven J. Hill <sjhill@cotw.com>
+ *                     Steven J. Hill <sjhill@realitydiluted.com>
  *		       Thomas Gleixner <tglx@linutronix.de>
  *
- * $Id: nand.h,v 1.19 2002/12/02 21:48:17 gleixner Exp $
+ * $Id: nand.h,v 1.46 2004/05/06 08:11:32 gleixner 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
@@ -44,17 +44,27 @@
  *			NAND_YAFFS_OOB
  *  11-25-2002 tglx	Added Manufacturer code FUJITSU, NATIONAL
  *			Split manufacturer and device ID structures 
+ *
+ *  02-08-2004 tglx 	added option field to nand structure for chip anomalities
  */
 #ifndef __LINUX_MTD_NAND_H
 #define __LINUX_MTD_NAND_H
 
 #include <linux/config.h>
-#include <linux/sched.h>
+#include <linux/wait.h>
+#include <linux/spinlock.h>
 
+struct mtd_info;
 /*
  * Searches for a NAND device
  */
-extern int nand_scan (struct mtd_info *mtd);
+extern int nand_scan (struct mtd_info *mtd, int max_chips);
+
+/* This constant declares the max. oobsize / page, which
+ * is supported now. If you add a chip with bigger oobsize/page
+ * adjust this accordingly.
+ */
+#define NAND_MAX_OOBSIZE	64
 
 /*
  * Constants for hardware specific CLE/ALE/NCE function
@@ -65,6 +75,8 @@
 #define NAND_CTL_CLRCLE		4
 #define NAND_CTL_SETALE		5
 #define NAND_CTL_CLRALE		6
+#define NAND_CTL_SETWP		7
+#define NAND_CTL_CLRWP		8
 
 /*
  * Standard NAND flash commands
@@ -80,6 +92,17 @@
 #define NAND_CMD_ERASE2		0xd0
 #define NAND_CMD_RESET		0xff
 
+/* Extended commands for large page devices */
+#define NAND_CMD_READSTART	0x30
+#define NAND_CMD_CACHEDPROG	0x15
+
+/* Status bits */
+#define NAND_STATUS_FAIL	0x01
+#define NAND_STATUS_FAIL_N1	0x02
+#define NAND_STATUS_TRUE_READY	0x20
+#define NAND_STATUS_READY	0x40
+#define NAND_STATUS_WP		0x80
+
 /* 
  * Constants for ECC_MODES
  *
@@ -102,7 +125,31 @@
 */
 #define NAND_ECC_READ		0
 #define NAND_ECC_WRITE		1
-	
+
+/* Option constants for bizarre disfunctionality and real
+*  features
+*/
+/* Chip can not auto increment pages */
+#define NAND_NO_AUTOINCR	0x00000001
+/* Buswitdh is 16 bit */
+#define NAND_BUSWIDTH_16	0x00000002
+/* Device supports partial programming without padding */
+#define NAND_NO_PADDING		0x00000004
+/* Chip has cache program function */
+#define NAND_CACHEPRG		0x00000008
+/* Chip has copy back function */
+#define NAND_COPYBACK		0x00000010
+
+/* Options valid for Samsung large page devices */
+#define NAND_SAMSUNG_LP_OPTIONS \
+	(NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
+
+/* Macros to identify the above */
+#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR))
+#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
+#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
+#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
+
 /*
  * Enumeration for NAND flash chip state
  */
@@ -111,7 +158,8 @@
 	FL_READING,
 	FL_WRITING,
 	FL_ERASING,
-	FL_SYNCING
+	FL_SYNCING,
+	FL_CACHEDPRG,
 } nand_state_t;
 
 
@@ -160,24 +208,46 @@
 struct nand_chip {
 	unsigned long 	IO_ADDR_R;
 	unsigned long 	IO_ADDR_W;
-	void 		(*hwcontrol)(int cmd);
-	int  		(*dev_ready)(void);
+	
+	u_char		(*read_byte)(struct mtd_info *mtd);
+	void		(*write_byte)(struct mtd_info *mtd, u_char byte);
+	u16		(*read_word)(struct mtd_info *mtd);
+	void		(*write_word)(struct mtd_info *mtd, u16 word);
+	
+	void		(*write_buf)(struct mtd_info *mtd, const u_char *buf, int len);
+	void		(*read_buf)(struct mtd_info *mtd, u_char *buf, int len);
+	int		(*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len);
+	void		(*select_chip)(struct mtd_info *mtd, int chip);
+	int		(*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
+	int		(*block_markbad)(struct mtd_info *mtd, loff_t ofs);
+	void 		(*hwcontrol)(struct mtd_info *mtd, int cmd);
+	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)(const u_char *dat, u_char *ecc_code);
-	int 		(*correct_data)(u_char *dat, u_char *read_ecc, u_char *calc_ecc);
-	void		(*enable_hwecc)(int mode);
+	void		(*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);
 	int		eccmode;
 	int		eccsize;
+	int		eccsteps;
 	int 		chip_delay;
-	spinlock_t 	chip_lock;
+	spinlock_t	chip_lock;
 	wait_queue_head_t wq;
 	nand_state_t 	state;
 	int 		page_shift;
+	int		erase_shift;
 	u_char 		*data_buf;
+	u_char		*oob_buf;
+	int		oobdirty;
 	u_char		*data_poi;
-	u_char 		*data_cache;
-	int		cache_page;
+	unsigned int	options;
+	int		badblockpos;
+	int		numchips;
+	unsigned long	chipsize;
+	int		pagemask;
+	int		pagebuf;
+	struct nand_oobinfo	*autooob;
+	void		*priv;
 };
 
 /*
@@ -193,39 +263,28 @@
  *
  * Structure overview:
  *
- *  name - Identify the device type
- *
- *  id -  device ID code
- *
- *  chipshift - total number of address bits for the device which
- *              is used to calculate address offsets and the total
- *              number of bytes the device is capable of.
- *
- *  page256 - denotes if flash device has 256 byte pages or not.
- *
- *  pageadrlen - number of bytes minus one needed to hold the
- *               complete address into the flash array. Keep in
- *               mind that when a read or write is done to a
- *               specific address, the address is input serially
- *               8 bits at a time. This structure member is used
- *               by the read/write routines as a loop index for
- *               shifting the address out 8 bits at a time.
- *
- *  erasesize - size of an erase block in the flash device.
+ *  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
  */
 struct nand_flash_dev {
-	char * name;
+	char *name;
 	int id;
-	int chipshift;
+	unsigned long pagesize;
+	unsigned long chipsize;
 	unsigned long erasesize;
-	char page256;
+	unsigned long options;
 };
 
 /*
  * NAND Flash Manufacturer ID Structure
- *
  *  name - Manufacturer name
- *
  *  id - manufacturer ID code of device.
 */
 struct nand_manufacturers {
@@ -239,36 +298,7 @@
 /*
 * Constants for oob configuration
 */
-#define NAND_BADBLOCK_POS		5
-
-#define NAND_NONE_OOB			0
-#define NAND_JFFS2_OOB			1
-#define NAND_YAFFS_OOB			2
-
-#define NAND_NOOB_ECCPOS0		0
-#define NAND_NOOB_ECCPOS1		1
-#define NAND_NOOB_ECCPOS2		2
-#define NAND_NOOB_ECCPOS3		3
-#define NAND_NOOB_ECCPOS4		6
-#define NAND_NOOB_ECCPOS5		7
-
-#define NAND_JFFS2_OOB_ECCPOS0		0
-#define NAND_JFFS2_OOB_ECCPOS1		1
-#define NAND_JFFS2_OOB_ECCPOS2		2
-#define NAND_JFFS2_OOB_ECCPOS3		3
-#define NAND_JFFS2_OOB_ECCPOS4		6
-#define NAND_JFFS2_OOB_ECCPOS5		7
-
-#define NAND_YAFFS_OOB_ECCPOS0		8
-#define NAND_YAFFS_OOB_ECCPOS1		9
-#define NAND_YAFFS_OOB_ECCPOS2		10
-#define NAND_YAFFS_OOB_ECCPOS3		13
-#define NAND_YAFFS_OOB_ECCPOS4		14
-#define NAND_YAFFS_OOB_ECCPOS5		15
-
-#define NAND_JFFS2_OOB8_FSDAPOS		6
-#define NAND_JFFS2_OOB16_FSDAPOS	8
-#define NAND_JFFS2_OOB8_FSDALEN		2
-#define NAND_JFFS2_OOB16_FSDALEN	8
+#define NAND_SMALL_BADBLOCK_POS		5
+#define NAND_LARGE_BADBLOCK_POS		0
 
 #endif /* __LINUX_MTD_NAND_H */
diff -Nur linux-2.4.30/include/linux/mtd/nftl.h linux-2.4.30-linux-mips.org/include/linux/mtd/nftl.h
--- linux-2.4.30/include/linux/mtd/nftl.h	2003-06-13 16:51:38.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/nftl.h	2005-05-12 18:01:29.000000000 +0200
@@ -1,81 +1,16 @@
-
-/* Defines for NAND Flash Translation Layer  */
-/* (c) 1999 Machine Vision Holdings, Inc.    */
-/* Author: David Woodhouse <dwmw2@mvhi.com>  */
-/* $Id: nftl.h,v 1.11 2002/06/18 13:54:24 dwmw2 Exp $ */
+/*
+ * $Id: nftl.h,v 1.15 2004/05/05 15:25:23 dwmw2 Exp $
+ *
+ * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
+ */
 
 #ifndef __MTD_NFTL_H__
 #define __MTD_NFTL_H__
 
-#ifndef __BOOT__
 #include <linux/mtd/mtd.h>
-#endif
-
-/* Block Control Information */
-
-struct nftl_bci {
-	unsigned char ECCSig[6];
-	__u8 Status;
-	__u8 Status1;
-}__attribute__((packed));
-
-/* Unit Control Information */
-
-struct nftl_uci0 {
-	__u16 VirtUnitNum;
-	__u16 ReplUnitNum;
-	__u16 SpareVirtUnitNum;
-	__u16 SpareReplUnitNum;
-} __attribute__((packed));
-
-struct nftl_uci1 {
-	__u32 WearInfo;
-	__u16 EraseMark;
-	__u16 EraseMark1;
-} __attribute__((packed));
-
-struct nftl_uci2 {
-        __u16 FoldMark;
-        __u16 FoldMark1;
-	__u32 unused;
-} __attribute__((packed));
-
-union nftl_uci {
-	struct nftl_uci0 a;
-	struct nftl_uci1 b;
-	struct nftl_uci2 c;
-};
+#include <linux/mtd/blktrans.h>
 
-struct nftl_oob {
-	struct nftl_bci b;
-	union nftl_uci u;
-};
-
-/* NFTL Media Header */
-
-struct NFTLMediaHeader {
-	char DataOrgID[6];
-	__u16 NumEraseUnits;
-	__u16 FirstPhysicalEUN;
-	__u32 FormattedSize;
-	unsigned char UnitSizeFactor;
-} __attribute__((packed));
-
-#define MAX_ERASE_ZONES (8192 - 512)
-
-#define ERASE_MARK 0x3c69
-#define SECTOR_FREE 0xff
-#define SECTOR_USED 0x55
-#define SECTOR_IGNORE 0x11
-#define SECTOR_DELETED 0x00
-
-#define FOLD_MARK_IN_PROGRESS 0x5555
-
-#define ZONE_GOOD 0xff
-#define ZONE_BAD_ORIGINAL 0
-#define ZONE_BAD_MARKED 7
-
-#ifdef __KERNEL__
+#include <mtd/nftl-user.h>
 
 /* these info are used in ReplUnitTable */
 #define BLOCK_NIL          0xffff /* last block of a chain */
@@ -84,8 +19,7 @@
 #define BLOCK_RESERVED     0xfffc /* bios block or bad block */
 
 struct NFTLrecord {
-	struct mtd_info *mtd;
-	struct semaphore mutex;
+	struct mtd_blktrans_dev mbd;
 	__u16 MediaUnit, SpareMediaUnit;
 	__u32 EraseSize;
 	struct NFTLMediaHeader MediaHdr;
@@ -97,7 +31,6 @@
 	__u16 lastEUN;                  /* should be suppressed */
 	__u16 numfreeEUNs;
 	__u16 LastFreeEUN; 		/* To speed up finding a free EUN */
-	__u32 nr_sects;
 	int head,sect,cyl;
 	__u16 *EUNtable; 		/* [numvunits]: First EUN for each virtual unit  */
 	__u16 *ReplUnitTable; 		/* [numEUNs]: ReplUnitNumber for each */
@@ -114,9 +47,7 @@
 #endif
 
 #define MAX_NFTLS 16
-#define MAX_SECTORS_PER_UNIT 32
+#define MAX_SECTORS_PER_UNIT 64
 #define NFTL_PARTN_BITS 4
 
-#endif /* __KERNEL__ */
-
 #endif /* __MTD_NFTL_H__ */
diff -Nur linux-2.4.30/include/linux/mtd/partitions.h linux-2.4.30-linux-mips.org/include/linux/mtd/partitions.h
--- linux-2.4.30/include/linux/mtd/partitions.h	2002-08-03 02:39:45.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/partitions.h	2005-05-12 18:01:29.000000000 +0200
@@ -5,7 +5,7 @@
  *
  * This code is GPL
  *
- * $Id: partitions.h,v 1.8 2002/03/08 16:34:36 rkaiser Exp $
+ * $Id: partitions.h,v 1.15 2003/07/09 11:15:43 dwmw2 Exp $
  */
 
 #ifndef MTD_PARTITIONS_H
@@ -37,11 +37,12 @@
  */ 
 
 struct mtd_partition {
-	char *name;		/* identifier string */
-	u_int32_t size;		/* partition size */
+	char *name;			/* identifier string */
+	u_int32_t size;			/* partition size */
 	u_int32_t offset;		/* offset within the master MTD space */
-	u_int32_t mask_flags;	/* master MTD flags to mask out for this partition */
-	struct mtd_info **mtdp;	/* pointer to store the MTD object */
+	u_int32_t mask_flags;		/* master MTD flags to mask out for this partition */
+	struct nand_oobinfo *oobsel;	/* out of band layout for this partition (NAND only)*/
+	struct mtd_info **mtdp;		/* pointer to store the MTD object */
 };
 
 #define MTDPART_OFS_NXTBLK	(-2)
@@ -49,8 +50,27 @@
 #define MTDPART_SIZ_FULL	(0)
 
 
-int add_mtd_partitions(struct mtd_info *, struct mtd_partition *, int);
+int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int);
 int del_mtd_partitions(struct mtd_info *);
 
+/*
+ * Functions dealing with the various ways of partitioning the space
+ */
+
+struct mtd_part_parser {
+	struct list_head list;
+	struct module *owner;
+	const char *name;
+	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, 
+				struct mtd_partition **pparts, unsigned long origin);
+
+#define put_partition_parser(p) do { module_put((p)->owner); } while(0)
+
 #endif
 
diff -Nur linux-2.4.30/include/linux/mtd/physmap.h linux-2.4.30-linux-mips.org/include/linux/mtd/physmap.h
--- linux-2.4.30/include/linux/mtd/physmap.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/mtd/physmap.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,59 @@
+/*
+ * For boards with physically mapped flash and using 
+ * drivers/mtd/maps/physmap.c mapping driver.
+ *
+ * Copyright (C) 2003 MontaVista Software Inc.
+ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
+ *
+ * This program is free software; you can redistribute  it and/or modify it
+ * under  the terms of  the GNU General  Public License as published by the
+ * Free Software Foundation;  either version 2 of the  License, or (at your
+ * option) any later version.
+ *
+ */
+
+#ifndef __LINUX_MTD_PHYSMAP__
+
+#include <linux/config.h>
+
+#if defined(CONFIG_MTD_PHYSMAP) 
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+/*
+ * The map_info for physmap.  Board can override size, buswidth, phys,
+ * (*set_vpp)(), etc in their initial setup routine.
+ */
+extern struct map_info physmap_map;
+
+/*
+ * 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) )
+{
+	physmap_map.phys = addr;
+	physmap_map.size = size;
+	physmap_map.buswidth = buswidth;
+	physmap_map.set_vpp = set_vpp;
+}
+
+#if defined(CONFIG_MTD_PARTITIONS)
+
+/*
+ * Machines that wish to do flash partition may want to call this function in 
+ * their setup routine.  
+ *
+ *	physmap_set_partitions(mypartitions, num_parts);
+ *
+ * Note that one can always override this hard-coded partition with 
+ * command line partition (you need to enable CONFIG_MTD_CMDLINE_PARTS).
+ */
+void physmap_set_partitions(struct mtd_partition *parts, int num_parts);
+
+#endif /* defined(CONFIG_MTD_PARTITIONS) */
+#endif /* defined(CONFIG_MTD) */
+
+#endif /* __LINUX_MTD_PHYSMAP__ */
+
diff -Nur linux-2.4.30/include/linux/rbtree-24.h linux-2.4.30-linux-mips.org/include/linux/rbtree-24.h
--- linux-2.4.30/include/linux/rbtree-24.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/rbtree-24.h	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,133 @@
+/*
+  Red Black Trees
+  (C) 1999  Andrea Arcangeli <andrea@suse.de>
+  
+  This program is free software; you can redistribute it and/or modify
+  it under the terms of the GNU General Public License as published by
+  the Free Software Foundation; either version 2 of the License, or
+  (at your option) any later version.
+
+  This program is distributed in the hope that it will be useful,
+  but WITHOUT ANY WARRANTY; without even the implied warranty of
+  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+  GNU General Public License for more details.
+
+  You should have received a copy of the GNU General Public License
+  along with this program; if not, write to the Free Software
+  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+  linux/include/linux/rbtree.h
+
+  To use rbtrees you'll have to implement your own insert and search cores.
+  This will avoid us to use callbacks and to drop drammatically performances.
+  I know it's not the cleaner way,  but in C (not in C++) to get
+  performances and genericity...
+
+  Some example of insert and search follows here. The search is a plain
+  normal search over an ordered tree. The insert instead must be implemented
+  int two steps: as first thing the code must insert the element in
+  order as a red leaf in the tree, then the support library function
+  rb_insert_color() must be called. Such function will do the
+  not trivial work to rebalance the rbtree if necessary.
+
+-----------------------------------------------------------------------
+static inline struct page * rb_search_page_cache(struct inode * inode,
+						 unsigned long offset)
+{
+	rb_node_t * n = inode->i_rb_page_cache.rb_node;
+	struct page * page;
+
+	while (n)
+	{
+		page = rb_entry(n, struct page, rb_page_cache);
+
+		if (offset < page->offset)
+			n = n->rb_left;
+		else if (offset > page->offset)
+			n = n->rb_right;
+		else
+			return page;
+	}
+	return NULL;
+}
+
+static inline struct page * __rb_insert_page_cache(struct inode * inode,
+						   unsigned long offset,
+						   rb_node_t * node)
+{
+	rb_node_t ** p = &inode->i_rb_page_cache.rb_node;
+	rb_node_t * parent = NULL;
+	struct page * page;
+
+	while (*p)
+	{
+		parent = *p;
+		page = rb_entry(parent, struct page, rb_page_cache);
+
+		if (offset < page->offset)
+			p = &(*p)->rb_left;
+		else if (offset > page->offset)
+			p = &(*p)->rb_right;
+		else
+			return page;
+	}
+
+	rb_link_node(node, parent, p);
+
+	return NULL;
+}
+
+static inline struct page * rb_insert_page_cache(struct inode * inode,
+						 unsigned long offset,
+						 rb_node_t * node)
+{
+	struct page * ret;
+	if ((ret = __rb_insert_page_cache(inode, offset, node)))
+		goto out;
+	rb_insert_color(node, &inode->i_rb_page_cache);
+ out:
+	return ret;
+}
+-----------------------------------------------------------------------
+*/
+
+#ifndef	_LINUX_RBTREE_H
+#define	_LINUX_RBTREE_H
+
+#include <linux/kernel.h>
+#include <linux/stddef.h>
+
+typedef struct rb_node_s
+{
+	struct rb_node_s * rb_parent;
+	int rb_color;
+#define	RB_RED		0
+#define	RB_BLACK	1
+	struct rb_node_s * rb_right;
+	struct rb_node_s * rb_left;
+}
+rb_node_t;
+
+typedef struct rb_root_s
+{
+	struct rb_node_s * rb_node;
+}
+rb_root_t;
+
+#define RB_ROOT	(rb_root_t) { NULL, }
+#define	rb_entry(ptr, type, member)					\
+	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+extern void rb_insert_color(rb_node_t *, rb_root_t *);
+extern void rb_erase(rb_node_t *, rb_root_t *);
+
+static inline void rb_link_node(rb_node_t * node, rb_node_t * parent, rb_node_t ** rb_link)
+{
+	node->rb_parent = parent;
+	node->rb_color = RB_RED;
+	node->rb_left = node->rb_right = NULL;
+
+	*rb_link = node;
+}
+
+#endif	/* _LINUX_RBTREE_H */
diff -Nur linux-2.4.30/include/linux/rbtree.h linux-2.4.30-linux-mips.org/include/linux/rbtree.h
--- linux-2.4.30/include/linux/rbtree.h	2004-11-17 12:54:22.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/rbtree.h	2005-05-12 18:01:30.000000000 +0200
@@ -1,139 +1,25 @@
 /*
-  Red Black Trees
-  (C) 1999  Andrea Arcangeli <andrea@suse.de>
-  
-  This program is free software; you can redistribute it and/or modify
-  it under the terms of the GNU General Public License as published by
-  the Free Software Foundation; either version 2 of the License, or
-  (at your option) any later version.
-
-  This program is distributed in the hope that it will be useful,
-  but WITHOUT ANY WARRANTY; without even the implied warranty of
-  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-  GNU General Public License for more details.
-
-  You should have received a copy of the GNU General Public License
-  along with this program; if not, write to the Free Software
-  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-
-  linux/include/linux/rbtree.h
-
-  To use rbtrees you'll have to implement your own insert and search cores.
-  This will avoid us to use callbacks and to drop drammatically performances.
-  I know it's not the cleaner way,  but in C (not in C++) to get
-  performances and genericity...
-
-  Some example of insert and search follows here. The search is a plain
-  normal search over an ordered tree. The insert instead must be implemented
-  int two steps: as first thing the code must insert the element in
-  order as a red leaf in the tree, then the support library function
-  rb_insert_color() must be called. Such function will do the
-  not trivial work to rebalance the rbtree if necessary.
-
------------------------------------------------------------------------
-static inline struct page * rb_search_page_cache(struct inode * inode,
-						 unsigned long offset)
-{
-	rb_node_t * n = inode->i_rb_page_cache.rb_node;
-	struct page * page;
-
-	while (n)
-	{
-		page = rb_entry(n, struct page, rb_page_cache);
-
-		if (offset < page->offset)
-			n = n->rb_left;
-		else if (offset > page->offset)
-			n = n->rb_right;
-		else
-			return page;
-	}
-	return NULL;
-}
-
-static inline struct page * __rb_insert_page_cache(struct inode * inode,
-						   unsigned long offset,
-						   rb_node_t * node)
-{
-	rb_node_t ** p = &inode->i_rb_page_cache.rb_node;
-	rb_node_t * parent = NULL;
-	struct page * page;
-
-	while (*p)
-	{
-		parent = *p;
-		page = rb_entry(parent, struct page, rb_page_cache);
-
-		if (offset < page->offset)
-			p = &(*p)->rb_left;
-		else if (offset > page->offset)
-			p = &(*p)->rb_right;
-		else
-			return page;
-	}
-
-	rb_link_node(node, parent, p);
-
-	return NULL;
-}
-
-static inline struct page * rb_insert_page_cache(struct inode * inode,
-						 unsigned long offset,
-						 rb_node_t * node)
-{
-	struct page * ret;
-	if ((ret = __rb_insert_page_cache(inode, offset, node)))
-		goto out;
-	rb_insert_color(node, &inode->i_rb_page_cache);
- out:
-	return ret;
-}
------------------------------------------------------------------------
-*/
-
-#ifndef	_LINUX_RBTREE_H
-#define	_LINUX_RBTREE_H
-
-#include <linux/kernel.h>
-#include <linux/stddef.h>
-
-typedef struct rb_node_s
-{
-	struct rb_node_s * rb_parent;
-	int rb_color;
-#define	RB_RED		0
-#define	RB_BLACK	1
-	struct rb_node_s * rb_right;
-	struct rb_node_s * rb_left;
-}
-rb_node_t;
-
-typedef struct rb_root_s
-{
-	struct rb_node_s * rb_node;
-}
-rb_root_t;
-
-#define RB_ROOT	(rb_root_t) { NULL, }
-#define	rb_entry(ptr, type, member)					\
-	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+ * 2.5 compatibility
+ * $Id: rbtree.h,v 1.3 2003/01/14 13:56:05 dwmw2 Exp $
+ */
+
+#ifndef __MTD_COMPAT_RBTREE_H__
+#define __MTD_COMPAT_RBTREE_H__
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,40)
+#include_next <linux/rbtree.h>
+#else
+#define rb_node_s rb_node
+#define rb_root_s rb_root
 
-extern void rb_insert_color(rb_node_t *, rb_root_t *);
-extern void rb_erase(rb_node_t *, rb_root_t *);
+#include <linux/rbtree-24.h>
 
 /* Find logical next and previous nodes in a tree */
-extern rb_node_t *rb_next(rb_node_t *);
-extern rb_node_t *rb_prev(rb_node_t *);
-extern rb_node_t *rb_first(rb_root_t *);
-extern rb_node_t *rb_last(rb_root_t *);
-
-static inline void rb_link_node(rb_node_t * node, rb_node_t * parent, rb_node_t ** rb_link)
-{
-	node->rb_parent = parent;
-	node->rb_color = RB_RED;
-	node->rb_left = node->rb_right = NULL;
+extern struct rb_node *rb_next(struct rb_node *);
+extern struct rb_node *rb_prev(struct rb_node *);
+extern struct rb_node *rb_first(struct rb_root *);
+#endif
 
-	*rb_link = node;
-}
-
-#endif	/* _LINUX_RBTREE_H */
+#endif /*  __MTD_COMPAT_RBTREE_H__ */
diff -Nur linux-2.4.30/include/linux/sched.h linux-2.4.30-linux-mips.org/include/linux/sched.h
--- linux-2.4.30/include/linux/sched.h	2005-01-19 15:10:12.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/sched.h	2005-05-12 17:57:55.000000000 +0200
@@ -617,6 +617,10 @@
 extern int in_group_p(gid_t);
 extern int in_egroup_p(gid_t);
 
+extern ATTRIB_NORET void cpu_idle(void);
+
+extern void release_task(struct task_struct * p);
+
 extern void proc_caches_init(void);
 extern void flush_signals(struct task_struct *);
 extern void flush_signal_handlers(struct task_struct *);
diff -Nur linux-2.4.30/include/linux/serial.h linux-2.4.30-linux-mips.org/include/linux/serial.h
--- linux-2.4.30/include/linux/serial.h	2002-08-03 02:39:45.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/serial.h	2005-05-12 17:57:55.000000000 +0200
@@ -75,7 +75,8 @@
 #define PORT_16654	11
 #define PORT_16850	12
 #define PORT_RSA	13	/* RSA-DV II/S card */
-#define PORT_MAX	13
+#define PORT_SB1250	14
+#define PORT_MAX	14
 
 #define SERIAL_IO_PORT	0
 #define SERIAL_IO_HUB6	1
diff -Nur linux-2.4.30/include/linux/suspend.h linux-2.4.30-linux-mips.org/include/linux/suspend.h
--- linux-2.4.30/include/linux/suspend.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/suspend.h	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,10 @@
+/* $Id: suspend.h,v 1.1 2003/10/13 20:56:47 dwmw2 Exp $ */
+
+#ifndef __MTD_COMPAT_VERSION_H__
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0)
+#include_next <linux/suspend.h>
+#endif
+
+#endif /* __MTD_COMPAT_VERSION_H__ */
diff -Nur linux-2.4.30/include/linux/swap.h linux-2.4.30-linux-mips.org/include/linux/swap.h
--- linux-2.4.30/include/linux/swap.h	2005-01-19 15:10:12.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/swap.h	2005-05-12 17:57:55.000000000 +0200
@@ -1,6 +1,12 @@
 #ifndef _LINUX_SWAP_H
 #define _LINUX_SWAP_H
 
+#include <linux/config.h>
+
+#define MAX_SWAPFILES 32
+
+#ifdef __KERNEL__
+
 #include <linux/spinlock.h>
 #include <asm/page.h>
 
@@ -8,8 +14,6 @@
 #define SWAP_FLAG_PRIO_MASK	0x7fff
 #define SWAP_FLAG_PRIO_SHIFT	0
 
-#define MAX_SWAPFILES 32
-
 /*
  * Magic header for a swap area. The first part of the union is
  * what the swap magic looks like for the old (limited to 128MB)
@@ -39,8 +43,6 @@
 	} info;
 };
 
-#ifdef __KERNEL__
-
 /*
  * Max bad pages in the new format..
  */
diff -Nur linux-2.4.30/include/linux/sysctl.h linux-2.4.30-linux-mips.org/include/linux/sysctl.h
--- linux-2.4.30/include/linux/sysctl.h	2005-04-04 03:42:20.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/linux/sysctl.h	2005-05-12 18:01:29.000000000 +0200
@@ -69,7 +69,8 @@
 /* CTL_BUS names: */
 enum
 {
-	CTL_BUS_ISA=1		/* ISA */
+	CTL_BUS_ISA=1,		/* ISA */
+ 	CTL_BUS_MMC=2		/* MultiMedia Card */
 };
 
 /* CTL_KERN names: */
diff -Nur linux-2.4.30/include/linux/usb_otg.h linux-2.4.30-linux-mips.org/include/linux/usb_otg.h
--- linux-2.4.30/include/linux/usb_otg.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/usb_otg.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,217 @@
+/**
+ * <linux/usb_otg.h>
+ *
+ * Copyright 2004 ADVANCED MICRO DEVICES, INC.
+ *
+ * karsten.boge@amd.com
+ */
+#ifndef __LINUX_USB_OTG_H
+#define __LINUX_USB_OTG_H
+
+
+/*
+ * OTG device flags
+ */
+#define USB_OTG_CONNECT_VALID		0x01	/* connected to OTG port     */
+#define USB_OTG_HNP_ENABLED		0x02	/* device is ready for HNP   */
+
+/*
+ * SET_FEATURE data
+ */
+#define USB_OTG_B_HNP_ENABLE		0x03	/* enables HNP in B-device   */
+#define USB_OTG_A_HNP_SUPPORT		0x04	/* signals HNP capability    */
+#define USB_OTG_A_ALT_HNP_SUPPORT	0x05	/* HNP, but no OTG connect   */
+
+/*
+ * OTG state definitions
+ */
+#define OTG_STATE_MASK                  0xF0
+
+#define OTG_NOT_ASSIGNED                0
+#define OTG_NO_B_DEVICE_A               (0x6 | OTG_NOT_ASSIGNED)
+#define OTG_NO_B_DEVICE_B               (0x4 | OTG_NOT_ASSIGNED)
+
+#define OTG_A_IDLE                      0x10
+#define OTG_A_IDLE_WAIT_DP              (1 | OTG_A_IDLE)
+#define OTG_A_IDLE_WAIT_VP              (2 | OTG_A_IDLE)
+#define OTG_A_IDLE_WAIT_MP              (3 | OTG_A_IDLE)
+#define OTG_A_IDLE_WAIT_DV              (4 | OTG_A_IDLE)
+#define OTG_A_WAIT_VRISE                0xB0
+#define OTG_A_WAIT_BCON                 0x30
+#define OTG_A_WAIT_BCON_VB              (1 | OTG_A_WAIT_BCON)
+#define OTG_A_HOST                      0x50
+#define OTG_A_SUSPEND                   0x70
+#define OTG_A_PERIPHERAL                0x90
+#define OTG_A_VBUS_ERROR                0xF0
+#define OTG_A_WAIT_VFALL                0xD0
+#define OTG_A_WAIT_VFALL_DN             (1 | OTG_A_WAIT_VFALL)
+#ifdef CONFIG_USB_OTG_FORCE_SUSPEND
+#define OTG_A_WAIT_BDISCON              0x40
+#define OTG_A_WAIT_BSUSPEND             (1 | OTG_A_WAIT_BDISCON)
+#endif
+
+#define OTG_B_IDLE                      0x20
+#define OTG_B_PERIPHERAL                0x80
+#define OTG_B_PERIPHERAL_WT             (1 | OTG_B_PERIPHERAL)
+#define OTG_B_PERIPHERAL_DC             (2 | OTG_B_PERIPHERAL)
+#define OTG_B_WAIT_ACON                 0xA0
+#define OTG_B_HOST                      0xE0
+#define OTG_B_SRP_INIT                  0xC0
+#define OTG_B_SRP_INIT_WAIT_SE0         (1 | OTG_B_SRP_INIT)
+#define OTG_B_SRP_INIT_D_PULSE          (2 | OTG_B_SRP_INIT)
+#define OTG_B_SRP_INIT_V_PULSE          (3 | OTG_B_SRP_INIT)
+#define OTG_B_SRP_INIT_V_DCHRG          (4 | OTG_B_SRP_INIT)
+#define OTG_B_SRP_INIT_WAIT_VBUS        (5 | OTG_B_SRP_INIT)
+#ifdef CONFIG_USB_OTG_FORCE_SUSPEND
+#define OTG_B_WAIT_ADISCON              0x60
+#define OTG_B_WAIT_ASUSPEND             (1 | OTG_B_WAIT_ADISCON)
+#endif
+
+/*
+ * typical timer values (ms)
+ */
+/* !!! make sure none of the values below is less then the SYSTEM LATENCY !!!*/
+
+#define OTG_TMR_WAIT_VFALL     10        /* A waits for VBus                 */
+#define OTG_TMR_A_WAIT_VRISE   100       /* A waits for VBus                 */
+#define OTG_TMR_A_WAIT_BCON    200       /* A waits for B-connect (1.. s)    */
+#define OTG_TMR_A_IDLE_BDIS    250       /* A waits for B-disc (200.. ms)    */
+#define OTG_TMR_B_ASE0_BRST    5         /* B waits for A-conn (3.125.. ms)  */
+#define OTG_TMR_B_AIDL_BDIS    50        /* B waits before dc (5..150ms)     */
+#define OTG_TMR_SRP_WAIT_SE0   2         /* B SRP idle wait                  */
+#define OTG_TMR_SRP_WAIT_DP    8         /* B SRP D_PULSE (5..10ms)          */
+#define OTG_TMR_SRP_WAIT_VP    80        /* B SRP V_PULSE (5..100ms)         */
+#define OTG_TMR_SRP_DCHRG_V    30        /* B SRP VBus discharge             */
+#define OTG_TMR_SRP_WAIT_VRS   5800      /* B SRP waits for VBus (5..6s)     */
+#define OTG_TMR_ASRP_WAIT_MP   4         /* A SRP min. pulse                 */
+#define OTG_TMR_ASRP_WAIT_DP   10        /* A SRP D_PULSE TO                 */
+#define OTG_TMR_ASRP_WAIT_VP   200       /* A SRP V_PULSE TO                 */
+#define OTG_TMR_ASRP_WAIT_DV   200       /* A SRP waits for V_PULSE          */
+#define OTG_TMR_A_BCON_VB      50        /* A waits for VBus after connect   */
+#ifdef CONFIG_USB_OTG_FORCE_SUSPEND
+#define OTG_TMR_HOST_WAIT_SUSP 5         /* USB 2.0 ( ..3ms)                 */
+#define OTG_TMR_A_FS_BDIS      (OTG_TMR_HOST_WAIT_SUSP + OTG_TMR_A_IDLE_BDIS)
+#define OTG_TMR_B_WAIT_ADIS    OTG_TMR_A_IDLE_BDIS
+#define OTG_TMR_B_FS_ADIS      (OTG_TMR_HOST_WAIT_SUSP + OTG_TMR_B_WAIT_ADIS)
+#endif
+
+/*
+ * OTG state parameters
+ */
+#define OTG_HOST_READY         (1<<20)   /* indicates a USB host driver is   */
+                                         /* running                          */
+#define OTG_GADGET_READY       (1<<21)   /* indicates a USB gadget driver is */
+                                         /* running                          */
+#define OTG_A_BUS_REQ          (1<<22)   /* used by appl-SW to request a     */
+                                         /* VBus rise, auto-reset by driver  */
+#define OTG_A_BUS_DROP         (1<<23)   /* used by appl-SW to request a     */
+                                         /* VBus drop, auto-reset by driver  */
+#define OTG_A_bHNP_EN          (1<<24)   /* aapl-SW can use this to enable   */
+                                         /* HNP / indicates HNP is enabled   */
+                                         /* reset on USB port reset or SEnd  */
+#define OTG_A_CLR_ERR          (1<<25)   /* used by appl-SW to request VBerr */
+                                         /* clean-up, auto-reset by driver   */
+#define OTG_AB_HNP_REQ         (1<<26)   /* used by appl-SW to initiate      */
+                                         /* HNP, auto-reset by driver        */
+#define OTG_B_BUS_REQ          (1<<27)   /* used by appl-SW to request       */
+                                         /* B-device functionality, ...      */
+#define OTG_B_BUS_DIS          (1<<28)   /* used by appl-SW to request       */
+                                         /* disable B-device functionality   */
+#define OTG_B_aSSN_REQ         (1<<29)   /* used by appl-SW to initiate SRP, */
+                                         /* auto-reset by the driver         */
+#define OTG_B_SRP_ERROR        (1<<30)   /* indicates invalid HW conditions  */
+                                         /* during SRP, reset by writing "1" */
+#define OTG_A_VBUS_FAILED      (1<<31)   /* indicates a VBus error, reset by */
+                                         /* writing "1", when setting        */
+                                         /* CLR_ERR or when leaving A-states */
+
+#ifdef CONFIG_USB_OTG_FORCE_SUSPEND
+#define SW_REQUEST_MASK        (OTG_A_BUS_REQ | OTG_A_BUS_DROP | \
+                                OTG_A_bHNP_EN | OTG_A_CLR_ERR | \
+                                OTG_B_BUS_REQ | OTG_B_BUS_DIS | \
+                                OTG_B_aSSN_REQ | OTG_AB_HNP_REQ)
+#else
+#define SW_REQUEST_MASK        (OTG_A_BUS_REQ | OTG_A_BUS_DROP | \
+                                OTG_A_bHNP_EN | OTG_A_CLR_ERR | \
+                                OTG_B_BUS_REQ | OTG_B_BUS_DIS | \
+                                OTG_B_aSSN_REQ)
+#endif
+
+/*********************************************************************/
+
+/*
+ * gadget events for notify function
+ */
+#define OTG_GADGET_EVT_SVDROP  1         /* Session valid drop       */
+#define OTG_GADGET_EVT_SVALID  2         /* Session valid            */
+
+/*** HS-A0 WA: gadget phy suspend issue                            ***/
+#define OTG_FLAGS_UDC_SUSP     (1<<17)   /* gadget phy suspended     */
+
+/*********************************************************************/
+
+#ifdef __KERNEL__
+/*
+ * fsm variables
+ */
+struct otg_ctl {
+	int  act_state;		/* OTG actual state */
+	int  prv_state;		/* OTG previous state */
+	int  params;		/* OTG state parameters */
+};
+
+struct otg {
+	spinlock_t        lock;
+	int               hostcount;
+	int               gadgetflag;
+	struct usb_device *device;
+	unsigned          enabled : 1,
+	                  got_irq : 1,
+	                  region : 1,
+	                  selfpowered : 1;
+	u16               chiprev;
+	/* pci state used to access those endpoints */
+	struct pci_dev   *pdev;
+	struct otg_regs  *regs;
+	struct otg_ctl   *otg_ctl;
+};
+
+/*
+ * To be allocated once for one OTG controller
+ */
+struct usbcore_otg_extension {
+	int  (*bind) (struct usb_bus *);
+	int  (*unbind) (struct usb_bus *);
+	int  (*request) (u32);	/* function call for state change requests */
+	u32  (*query) (int);	/* function call to query state */
+};
+
+struct usb_gadget_otg_extension {
+	int  (*bind) (void);
+	int  (*unbind) (void);
+	int  (*request) (u32);	/* function call for state change requests */
+	u32  (*query) (int);	/* function call to query state */
+	int  (*notify) (int);   /* report events to the gadget driver */
+};
+
+/*
+ * To be allocated for each OTG capabel host (assuming 1 OTG port)
+ */
+struct usb_otg_host {
+	int   otgport;		/* port supporting OTG (-1 for none) */
+	void *otgpriv;		/* OTG controller private data */
+};
+
+
+int usb_bus_register_otg (struct usbcore_otg_extension *);
+int usb_bus_deregister_otg (struct usbcore_otg_extension *);
+int usb_gadget_register_otg (struct usb_gadget_otg_extension *);
+int usb_gadget_unregister_otg (struct usb_gadget_otg_extension *);
+int usb_otg_enable_b_hnp (struct usb_device *);
+
+int usb_check_otg_support (struct pci_dev *, struct usb_bus *);
+void usb_release_otg_host (struct usb_bus *);
+
+#endif
+
+#endif /* __LINUX_USB_OTG_H */
diff -Nur linux-2.4.30/include/linux/workqueue.h linux-2.4.30-linux-mips.org/include/linux/workqueue.h
--- linux-2.4.30/include/linux/workqueue.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/linux/workqueue.h	2005-05-12 18:01:30.000000000 +0200
@@ -0,0 +1,21 @@
+/*
+ * 2.5 compatibility 
+ * $Id: workqueue.h,v 1.1 2002/11/11 16:39:10 dwmw2 Exp $
+ */
+
+#ifndef __MTD_COMPAT_WORKQUEUE_H__
+#define __MTD_COMPAT_WORKQUEUE_H__
+
+#include <linux/version.h>
+
+#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,40)
+#include_next <linux/workqueue.h>
+#else
+#include <linux/tqueue.h>
+#define work_struct tq_struct
+#define schedule_work(x) schedule_task(x)
+#define flush_scheduled_work flush_scheduled_tasks
+#define INIT_WORK(x,y,z) INIT_TQUEUE(x,y,z)
+#endif
+
+#endif /* __MTD_COMPAT_WORKQUEUE_H__ */
diff -Nur linux-2.4.30/include/mtd/inftl-user.h linux-2.4.30-linux-mips.org/include/mtd/inftl-user.h
--- linux-2.4.30/include/mtd/inftl-user.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/mtd/inftl-user.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,91 @@
+/*
+ * $Id: inftl-user.h,v 1.1 2004/05/05 15:17:00 dwmw2 Exp $
+ *
+ * Parts of INFTL headers shared with userspace 
+ *
+ */
+
+#ifndef __MTD_INFTL_USER_H__
+#define __MTD_INFTL_USER_H__
+
+#define	OSAK_VERSION	0x5120
+#define	PERCENTUSED	98
+
+#define	SECTORSIZE	512
+
+/* Block Control Information */
+
+struct inftl_bci {
+	uint8_t ECCsig[6];
+	uint8_t Status;
+	uint8_t Status1;
+} __attribute__((packed));
+
+struct inftl_unithead1 {
+	uint16_t virtualUnitNo;
+	uint16_t prevUnitNo;
+	uint8_t ANAC;
+	uint8_t NACs;
+	uint8_t parityPerField;
+	uint8_t discarded;
+} __attribute__((packed));
+
+struct inftl_unithead2 {
+	uint8_t parityPerField;
+	uint8_t ANAC;
+	uint16_t prevUnitNo;
+	uint16_t virtualUnitNo;
+	uint8_t NACs;
+	uint8_t discarded;
+} __attribute__((packed));
+
+struct inftl_unittail {
+	uint8_t Reserved[4];
+	uint16_t EraseMark;
+	uint16_t EraseMark1;
+} __attribute__((packed));
+
+union inftl_uci {
+	struct inftl_unithead1 a;
+	struct inftl_unithead2 b;
+	struct inftl_unittail c;
+};
+
+struct inftl_oob {
+	struct inftl_bci b;
+	union inftl_uci u;
+};
+
+
+/* INFTL Media Header */
+
+struct INFTLPartition {
+	__u32 virtualUnits;
+	__u32 firstUnit;
+	__u32 lastUnit;
+	__u32 flags;
+	__u32 spareUnits;
+	__u32 Reserved0;
+	__u32 Reserved1;
+} __attribute__((packed));
+
+struct INFTLMediaHeader {
+	char bootRecordID[8];
+	__u32 NoOfBootImageBlocks;
+	__u32 NoOfBinaryPartitions;
+	__u32 NoOfBDTLPartitions;
+	__u32 BlockMultiplierBits;
+	__u32 FormatFlags;
+	__u32 OsakVersion;
+	__u32 PercentUsed;
+	struct INFTLPartition Partitions[4];
+} __attribute__((packed));
+
+/* Partition flag types */
+#define	INFTL_BINARY	0x20000000
+#define	INFTL_BDTL	0x40000000
+#define	INFTL_LAST	0x80000000
+
+#endif /* __MTD_INFTL_USER_H__ */
+
+
diff -Nur linux-2.4.30/include/mtd/jffs2-user.h linux-2.4.30-linux-mips.org/include/mtd/jffs2-user.h
--- linux-2.4.30/include/mtd/jffs2-user.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/mtd/jffs2-user.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,35 @@
+/*
+ * $Id: jffs2-user.h,v 1.1 2004/05/05 11:57:54 dwmw2 Exp $
+ *
+ * JFFS2 definitions for use in user space only
+ */
+
+#ifndef __JFFS2_USER_H__
+#define __JFFS2_USER_H__
+
+/* This file is blessed for inclusion by userspace */
+#include <linux/jffs2.h>
+#include <endian.h>
+#include <byteswap.h>
+
+#undef cpu_to_je16
+#undef cpu_to_je32
+#undef cpu_to_jemode
+#undef je16_to_cpu
+#undef je32_to_cpu
+#undef jemode_to_cpu
+
+extern int target_endian;
+
+#define t16(x) ({ uint16_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
+#define t32(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
+
+#define cpu_to_je16(x) ((jint16_t){t16(x)})
+#define cpu_to_je32(x) ((jint32_t){t32(x)})
+#define cpu_to_jemode(x) ((jmode_t){t32(x)})
+
+#define je16_to_cpu(x) (t16((x).v16))
+#define je32_to_cpu(x) (t32((x).v32))
+#define jemode_to_cpu(x) (t32((x).m))
+
+#endif /* __JFFS2_USER_H__ */
diff -Nur linux-2.4.30/include/mtd/mtd-abi.h linux-2.4.30-linux-mips.org/include/mtd/mtd-abi.h
--- linux-2.4.30/include/mtd/mtd-abi.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/mtd/mtd-abi.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,93 @@
+/*
+ * $Id: mtd-abi.h,v 1.1 2004/05/05 14:44:57 dwmw2 Exp $
+ *
+ * Portions of MTD ABI definition which are shared by kernel and user space 
+ */
+
+#ifndef __MTD_ABI_H__
+#define __MTD_ABI_H__
+
+struct erase_info_user {
+	uint32_t start;
+	uint32_t length;
+};
+
+struct mtd_oob_buf {
+	uint32_t start;
+	uint32_t length;
+	unsigned char *ptr;
+};
+
+#define MTD_ABSENT		0
+#define MTD_RAM			1
+#define MTD_ROM			2
+#define MTD_NORFLASH		3
+#define MTD_NANDFLASH		4
+#define MTD_PEROM		5
+#define MTD_OTHER		14
+#define MTD_UNKNOWN		15
+
+#define MTD_CLEAR_BITS		1       // Bits can be cleared (flash)
+#define MTD_SET_BITS		2       // Bits can be set
+#define MTD_ERASEABLE		4       // Has an erase function
+#define MTD_WRITEB_WRITEABLE	8       // Direct IO is possible
+#define MTD_VOLATILE		16      // Set for RAMs
+#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
+
+// Some common devices / combinations of capabilities
+#define MTD_CAP_ROM		0
+#define MTD_CAP_RAM		(MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE)
+#define MTD_CAP_NORFLASH        (MTD_CLEAR_BITS|MTD_ERASEABLE)
+#define MTD_CAP_NANDFLASH       (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB)
+#define MTD_WRITEABLE		(MTD_CLEAR_BITS|MTD_SET_BITS)
+
+
+// Types of automatic ECC/Checksum available
+#define MTD_ECC_NONE		0 	// No automatic ECC available
+#define MTD_ECC_RS_DiskOnChip	1	// Automatic ECC on DiskOnChip
+#define MTD_ECC_SW		2	// SW ECC for Toshiba & Samsung devices
+
+/* 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_AUTOPLACE	2	// Use the default placement scheme
+
+struct mtd_info_user {
+	uint8_t type;
+	uint32_t flags;
+	uint32_t size;	 // Total size of the MTD
+	uint32_t erasesize;
+	uint32_t oobblock;  // Size of OOB blocks (e.g. 512)
+	uint32_t oobsize;   // Amount of OOB data per block (e.g. 16)
+	uint32_t ecctype;
+	uint32_t eccsize;
+};
+
+struct region_info_user {
+	uint32_t offset;		/* At which this region starts, 
+					 * from the beginning of the MTD */
+	uint32_t erasesize;		/* For this region */
+	uint32_t numblocks;		/* Number of blocks in this region */
+	uint32_t regionindex;
+};
+
+#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)
+#define MEMREADOOB              _IOWR('M', 4, struct mtd_oob_buf)
+#define MEMLOCK                 _IOW('M', 5, struct erase_info_user)
+#define MEMUNLOCK               _IOW('M', 6, struct erase_info_user)
+#define MEMGETREGIONCOUNT	_IOR('M', 7, int)
+#define MEMGETREGIONINFO	_IOWR('M', 8, struct region_info_user)
+#define MEMSETOOBSEL		_IOW('M', 9, struct nand_oobinfo)
+
+struct nand_oobinfo {
+	int	useecc;
+	int	eccbytes;
+	int	eccpos[24];
+	int	oobfree[8][2];
+};
+
+#endif /* __MTD_ABI_H__ */
diff -Nur linux-2.4.30/include/mtd/mtd-user.h linux-2.4.30-linux-mips.org/include/mtd/mtd-user.h
--- linux-2.4.30/include/mtd/mtd-user.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/mtd/mtd-user.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,20 @@
+/*
+ * $Id: mtd-user.h,v 1.2 2004/05/05 14:44:57 dwmw2 Exp $
+ *
+ * MTD ABI header for use by user space only.
+ */
+
+#ifndef __MTD_USER_H__
+#define __MTD_USER_H__
+
+#include <stdint.h>
+
+/* This file is blessed for inclusion by userspace */
+#include <mtd/mtd-abi.h>
+
+typedef struct mtd_info_user mtd_info_t;
+typedef struct erase_info_user erase_info_t;
+typedef struct region_info_user region_info_t;
+typedef struct nand_oobinfo nand_oobinfo_t;
+
+#endif /* __MTD_USER_H__ */
diff -Nur linux-2.4.30/include/mtd/nftl-user.h linux-2.4.30-linux-mips.org/include/mtd/nftl-user.h
--- linux-2.4.30/include/mtd/nftl-user.h	1970-01-01 01:00:00.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/mtd/nftl-user.h	2005-05-12 18:01:29.000000000 +0200
@@ -0,0 +1,76 @@
+/*
+ * $Id: nftl-user.h,v 1.1 2004/05/05 14:44:57 dwmw2 Exp $
+ *
+ * Parts of NFTL headers shared with userspace 
+ *
+ */
+
+#ifndef __MTD_NFTL_USER_H__
+#define __MTD_NFTL_USER_H__
+
+/* Block Control Information */
+
+struct nftl_bci {
+	unsigned char ECCSig[6];
+	uint8_t Status;
+	uint8_t Status1;
+}__attribute__((packed));
+
+/* Unit Control Information */
+
+struct nftl_uci0 {
+	uint16_t VirtUnitNum;
+	uint16_t ReplUnitNum;
+	uint16_t SpareVirtUnitNum;
+	uint16_t SpareReplUnitNum;
+} __attribute__((packed));
+
+struct nftl_uci1 {
+	uint32_t WearInfo;
+	uint16_t EraseMark;
+	uint16_t EraseMark1;
+} __attribute__((packed));
+
+struct nftl_uci2 {
+        uint16_t FoldMark;
+        uint16_t FoldMark1;
+	uint32_t unused;
+} __attribute__((packed));
+
+union nftl_uci {
+	struct nftl_uci0 a;
+	struct nftl_uci1 b;
+	struct nftl_uci2 c;
+};
+
+struct nftl_oob {
+	struct nftl_bci b;
+	union nftl_uci u;
+};
+
+/* NFTL Media Header */
+
+struct NFTLMediaHeader {
+	char DataOrgID[6];
+	uint16_t NumEraseUnits;
+	uint16_t FirstPhysicalEUN;
+	uint32_t FormattedSize;
+	unsigned char UnitSizeFactor;
+} __attribute__((packed));
+
+#define MAX_ERASE_ZONES (8192 - 512)
+
+#define ERASE_MARK 0x3c69
+#define SECTOR_FREE 0xff
+#define SECTOR_USED 0x55
+#define SECTOR_IGNORE 0x11
+#define SECTOR_DELETED 0x00
+
+#define FOLD_MARK_IN_PROGRESS 0x5555
+
+#define ZONE_GOOD 0xff
+#define ZONE_BAD_ORIGINAL 0
+#define ZONE_BAD_MARKED 7
+
+
+#endif /* __MTD_NFTL_USER_H__ */
diff -Nur linux-2.4.30/include/pcmcia/cs_types.h linux-2.4.30-linux-mips.org/include/pcmcia/cs_types.h
--- linux-2.4.30/include/pcmcia/cs_types.h	2004-02-18 14:36:32.000000000 +0100
+++ linux-2.4.30-linux-mips.org/include/pcmcia/cs_types.h	2005-05-12 18:01:29.000000000 +0200
@@ -36,8 +36,10 @@
 #include <sys/types.h>
 #endif
 
-#if defined(__arm__) || defined(__mips__)
+#if defined(__arm__)
 typedef u_int   ioaddr_t;
+#elif defined(__mips__)
+typedef unsigned long long ioaddr_t;
 #else
 typedef u_short	ioaddr_t;
 #endif
diff -Nur linux-2.4.30/include/pcmcia/ss.h linux-2.4.30-linux-mips.org/include/pcmcia/ss.h
--- linux-2.4.30/include/pcmcia/ss.h	2002-08-03 02:39:46.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/pcmcia/ss.h	2005-05-12 18:01:29.000000000 +0200
@@ -112,7 +112,7 @@
     u_char	map;
     u_char	flags;
     u_short	speed;
-    u_long	sys_start, sys_stop;
+    ioaddr_t	sys_start, sys_stop;
     u_int	card_start;
 } pccard_mem_map;
 
diff -Nur linux-2.4.30/include/video/newport.h linux-2.4.30-linux-mips.org/include/video/newport.h
--- linux-2.4.30/include/video/newport.h	2001-04-12 21:20:31.000000000 +0200
+++ linux-2.4.30-linux-mips.org/include/video/newport.h	2005-05-12 17:57:55.000000000 +0200
@@ -291,8 +291,6 @@
 	unsigned int _unused2[0x1ef];
 	struct newport_cregs cgo;
 };
-extern struct newport_regs *npregs;
-
 
 typedef struct {
 	unsigned int drawmode1;
@@ -450,38 +448,26 @@
 
 /* Miscellaneous NEWPORT routines. */
 #define BUSY_TIMEOUT 100000
-static __inline__ int newport_wait(void)
+static __inline__ int newport_wait(struct newport_regs *regs)
 {
-	int i = 0;
+	int t = BUSY_TIMEOUT;
 
-	while(i < BUSY_TIMEOUT)
-		if(!(npregs->cset.status & NPORT_STAT_GBUSY))
+	while (t--)
+		if (!(regs->cset.status & NPORT_STAT_GBUSY))
 			break;
-	if(i == BUSY_TIMEOUT)
-		return 1;
-	return 0;
+	return !t;
 }
 
-static __inline__ int newport_bfwait(void)
+static __inline__ int newport_bfwait(struct newport_regs *regs)
 {
-	int i = 0;
+	int t = BUSY_TIMEOUT;
 
-	while(i < BUSY_TIMEOUT)
-		if(!(npregs->cset.status & NPORT_STAT_BBUSY))
+	while (t--)
+		if(!(regs->cset.status & NPORT_STAT_BBUSY))
 			break;
-	if(i == BUSY_TIMEOUT)
-		return 1;
-	return 0;
+	return !t;
 }
 
-/* newport.c and cons_newport.c routines */
-extern struct graphics_ops *newport_probe (int, const char **);
-
-void newport_save    (void *);
-void newport_restore (void *);
-void newport_reset   (void);
-int  newport_ioctl   (int card, int cmd, unsigned long arg);
-
 /*
  * DCBMODE register defines:
  */
@@ -564,7 +550,7 @@
 {
         rex->set.dcbmode = DCB_XMAP0 | XM9_CRS_FIFO_AVAIL |
 		DCB_DATAWIDTH_1 | R_DCB_XMAP9_PROTOCOL;
-        newport_bfwait ();
+        newport_bfwait (rex);
 	
         while ((rex->set.dcbdata0.bybytes.b3 & 3) != XM9_FIFO_EMPTY)
 		;
diff -Nur linux-2.4.30/init/do_mounts.c linux-2.4.30-linux-mips.org/init/do_mounts.c
--- linux-2.4.30/init/do_mounts.c	2003-11-28 19:26:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/init/do_mounts.c	2005-05-12 18:01:29.000000000 +0200
@@ -88,7 +88,7 @@
 	const char *name;
 	const int num;
 } root_dev_names[] __initdata = {
-	{ "nfs",     MKDEV(NFS_MAJOR, NFS_MINOR) },
+	{ "nfs",     0x00ff },
 	{ "hda",     0x0300 },
 	{ "hdb",     0x0340 },
 	{ "loop",    0x0700 },
@@ -760,8 +760,7 @@
 static void __init mount_root(void)
 {
 #ifdef CONFIG_ROOT_NFS
-       if (MAJOR(ROOT_DEV) == NFS_MAJOR
-           && MINOR(ROOT_DEV) == NFS_MINOR) {
+	if (MAJOR(ROOT_DEV) == UNNAMED_MAJOR) {
 		if (mount_nfs_root()) {
 			sys_chdir("/root");
 			ROOT_DEV = current->fs->pwdmnt->mnt_sb->s_dev;
diff -Nur linux-2.4.30/init/main.c linux-2.4.30-linux-mips.org/init/main.c
--- linux-2.4.30/init/main.c	2004-11-17 12:54:22.000000000 +0100
+++ linux-2.4.30-linux-mips.org/init/main.c	2005-05-12 17:57:55.000000000 +0200
@@ -296,7 +296,6 @@
 
 
 extern void setup_arch(char **);
-extern void cpu_idle(void);
 
 unsigned long wait_init_idle;
 
diff -Nur linux-2.4.30/kernel/exit.c linux-2.4.30-linux-mips.org/kernel/exit.c
--- linux-2.4.30/kernel/exit.c	2002-11-29 00:53:15.000000000 +0100
+++ linux-2.4.30-linux-mips.org/kernel/exit.c	2005-05-12 17:57:56.000000000 +0200
@@ -26,7 +26,7 @@
 
 int getrusage(struct task_struct *, int, struct rusage *);
 
-static void release_task(struct task_struct * p)
+void release_task(struct task_struct * p)
 {
 	if (p != current) {
 #ifdef CONFIG_SMP
diff -Nur linux-2.4.30/kernel/signal.c linux-2.4.30-linux-mips.org/kernel/signal.c
--- linux-2.4.30/kernel/signal.c	2004-02-18 14:36:32.000000000 +0100
+++ linux-2.4.30-linux-mips.org/kernel/signal.c	2005-05-12 17:57:56.000000000 +0200
@@ -14,6 +14,7 @@
 #include <linux/init.h>
 #include <linux/sched.h>
 
+#include <asm/param.h>
 #include <asm/uaccess.h>
 
 /*
@@ -28,6 +29,14 @@
 #define SIG_SLAB_DEBUG	0
 #endif
 
+#define DEBUG_SIG 0
+
+#if DEBUG_SIG
+#define SIG_SLAB_DEBUG	(SLAB_DEBUG_FREE | SLAB_RED_ZONE /* | SLAB_POISON */)
+#else
+#define SIG_SLAB_DEBUG	0
+#endif
+
 static kmem_cache_t *sigqueue_cachep;
 
 atomic_t nr_queued_signals;
@@ -270,6 +279,11 @@
 	signal_pending(current));
 #endif
 
+#if DEBUG_SIG
+printk("SIG dequeue (%s:%d): %d ", current->comm, current->pid,
+	signal_pending(current));
+#endif
+
 	sig = next_signal(current, mask);
 	if (sig) {
 		if (current->notifier) {
@@ -293,6 +307,10 @@
 printk(" %d -> %d\n", signal_pending(current), sig);
 #endif
 
+#if DEBUG_SIG
+printk(" %d -> %d\n", signal_pending(current), sig);
+#endif
+
 	return sig;
 }
 
@@ -540,6 +558,11 @@
 printk("SIG queue (%s:%d): %d ", t->comm, t->pid, sig);
 #endif
 
+
+#if DEBUG_SIG
+printk("SIG queue (%s:%d): %d ", t->comm, t->pid, sig);
+#endif
+
 	ret = -EINVAL;
 	if (sig < 0 || sig > _NSIG)
 		goto out_nolock;
@@ -778,8 +801,8 @@
 	info.si_uid = tsk->uid;
 
 	/* FIXME: find out whether or not this is supposed to be c*time. */
-	info.si_utime = tsk->times.tms_utime;
-	info.si_stime = tsk->times.tms_stime;
+	info.si_utime = hz_to_std(tsk->times.tms_utime);
+	info.si_stime = hz_to_std(tsk->times.tms_stime);
 
 	status = tsk->exit_code & 0x7f;
 	why = SI_KERNEL;	/* shouldn't happen */
diff -Nur linux-2.4.30/kernel/sys.c linux-2.4.30-linux-mips.org/kernel/sys.c
--- linux-2.4.30/kernel/sys.c	2003-11-28 19:26:21.000000000 +0100
+++ linux-2.4.30-linux-mips.org/kernel/sys.c	2005-05-12 17:57:56.000000000 +0200
@@ -801,16 +801,23 @@
 
 asmlinkage long sys_times(struct tms * tbuf)
 {
+	struct tms temp;
+
 	/*
 	 *	In the SMP world we might just be unlucky and have one of
 	 *	the times increment as we use it. Since the value is an
 	 *	atomically safe type this is just fine. Conceptually its
 	 *	as if the syscall took an instant longer to occur.
 	 */
-	if (tbuf)
-		if (copy_to_user(tbuf, &current->times, sizeof(struct tms)))
+	if (tbuf) {
+		temp.tms_utime = hz_to_std(current->times.tms_utime);
+		temp.tms_stime = hz_to_std(current->times.tms_stime);
+		temp.tms_cutime = hz_to_std(current->times.tms_cutime);
+		temp.tms_cstime = hz_to_std(current->times.tms_cstime);
+		if (copy_to_user(tbuf, &temp, sizeof(struct tms)))
 			return -EFAULT;
-	return jiffies;
+	}
+	return hz_to_std(jiffies);
 }
 
 /*
diff -Nur linux-2.4.30/lib/Makefile linux-2.4.30-linux-mips.org/lib/Makefile
--- linux-2.4.30/lib/Makefile	2004-04-14 15:05:40.000000000 +0200
+++ linux-2.4.30-linux-mips.org/lib/Makefile	2005-05-12 17:57:56.000000000 +0200
@@ -27,6 +27,7 @@
 subdir-$(CONFIG_ZLIB_INFLATE) += zlib_inflate
 subdir-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate
 
+-include $(TOPDIR)/arch/$(ARCH)/Makefile.lib
 include $(TOPDIR)/drivers/net/Makefile.lib
 include $(TOPDIR)/drivers/usb/Makefile.lib
 include $(TOPDIR)/drivers/bluetooth/Makefile.lib
diff -Nur linux-2.4.30/MAINTAINERS linux-2.4.30-linux-mips.org/MAINTAINERS
--- linux-2.4.30/MAINTAINERS	2005-04-04 03:42:19.000000000 +0200
+++ linux-2.4.30-linux-mips.org/MAINTAINERS	2005-05-12 18:01:29.000000000 +0200
@@ -1240,6 +1240,14 @@
 L:	linux-kernel@vger.kernel.org
 S:	Maintained
 
+MULTIMEDIA CARD (MMC) SUBSYSTEM
+P: Andrew Christian
+P: Jamey Hicks
+P: George France
+L: MMC-Developer's <mmc-dev@handhelds.org>
+W: http://www.handhelds.org/pub/linux/mmc/
+S: Maintained
+
 MODULE SUPPORT [GENERAL], KMOD
 P:	Keith Owens
 M:	kaos@ocs.com.au
diff -Nur linux-2.4.30/Makefile linux-2.4.30-linux-mips.org/Makefile
--- linux-2.4.30/Makefile	2005-04-04 03:42:20.000000000 +0200
+++ linux-2.4.30-linux-mips.org/Makefile	2005-05-12 18:01:29.000000000 +0200
@@ -5,7 +5,7 @@
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
-ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
+ARCH = mips
 KERNELPATH=kernel-$(shell echo $(KERNELRELEASE) | sed -e "s/-//g")
 
 CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
@@ -156,6 +156,7 @@
 DRIVERS-$(CONFIG_SCSI) += drivers/scsi/scsidrv.o
 DRIVERS-$(CONFIG_FUSION_BOOT) += drivers/message/fusion/fusion.o
 DRIVERS-$(CONFIG_IEEE1394) += drivers/ieee1394/ieee1394drv.o
+DRIVERS-$(CONFIG_MMC) += drivers/mmc/mmc.o
 
 ifneq ($(CONFIG_CD_NO_IDESCSI)$(CONFIG_BLK_DEV_IDECD)$(CONFIG_BLK_DEV_SR)$(CONFIG_PARIDE_PCD),)
 DRIVERS-y += drivers/cdrom/driver.o
@@ -462,10 +463,11 @@
 	$(MAKE) -C Documentation/DocBook mrproper
 
 distclean: mrproper
-	rm -f core `find . \( -not -type d \) -and \
-		\( -name '*.orig' -o -name '*.rej' -o -name '*~' \
-		-o -name '*.bak' -o -name '#*#' -o -name '.*.orig' \
-		-o -name '.*.rej' -o -name '.SUMS' -o -size 0 \) -type f -print` TAGS tags
+	find . \( -not -type d \) -and \
+		\( -name core -o -name '*.orig' -o -name '*.rej' \
+		-o -name '*~' -o -name '*.bak' -o -name '#*#' \
+		-o -name '.*.rej' -o -name '.SUMS' -o -size 0 \
+		-o -name TAGS -o -name tags \) -print | env -i xargs rm -f
 
 backup: mrproper
 	cd .. && tar cf - linux/ | gzip -9 > backup.gz
@@ -492,7 +494,7 @@
 	$(MAKE) -C Documentation/DocBook man
 
 sums:
-	find . -type f -print | sort | xargs sum > .SUMS
+	find . -type f -print | sort | env -i xargs sum > .SUMS
 
 dep-files: scripts/mkdep archdep include/linux/version.h
 	rm -f .depend .hdepend
diff -Nur linux-2.4.30/mm/filemap.c linux-2.4.30-linux-mips.org/mm/filemap.c
--- linux-2.4.30/mm/filemap.c	2005-04-04 03:42:20.000000000 +0200
+++ linux-2.4.30-linux-mips.org/mm/filemap.c	2005-05-12 18:01:29.000000000 +0200
@@ -1397,6 +1397,90 @@
 		SetPageReferenced(page);
 }
 
+/**
+ * shrink_list - non-blockingly drop pages from the given cache list
+ * @mapping: the mapping from which we want to drop pages
+ * @list: which list (e.g. locked, dirty, clean)?
+ * @max_index: greatest index from which we will drop pages
+ */
+static unsigned long shrink_list(struct address_space *mapping,
+				 struct list_head *list,
+				 unsigned long max_index)
+{
+	struct list_head *curr = list->prev;
+	unsigned long nr_shrunk = 0;
+
+	spin_lock(&pagemap_lru_lock);
+	spin_lock(&pagecache_lock);
+
+	while ((curr != list)) {
+		struct page *page = list_entry(curr, struct page, list);
+
+		curr = curr->prev;
+
+		if (page->index > max_index)
+			continue;
+
+		if (PageDirty(page))
+			continue;
+
+		if (TryLockPage(page))
+			break;
+
+		if (page->buffers && !try_to_release_page(page, 0)) {
+			/* probably dirty buffers */
+			unlock_page(page);
+			break;
+		}
+
+		if (page_count(page) != 1) {
+			unlock_page(page);
+			continue;
+		}
+
+		__lru_cache_del(page);
+		__remove_inode_page(page);
+		unlock_page(page);
+		page_cache_release(page);
+		nr_shrunk++;
+	}
+
+	spin_unlock(&pagecache_lock);
+	spin_unlock(&pagemap_lru_lock);
+
+	return nr_shrunk;
+}
+
+/**
+ * shrink_pagecache - nonblockingly drop pages from the mapping.
+ * @file: the file we are doing I/O on
+ * @max_index: the maximum index from which we are willing to drop pages
+ * 
+ * This is for O_STREAMING, which says "I am streaming data, I know I will not
+ * revisit this; do not cache anything".
+ * 
+ * max_index allows us to only drop pages which are behind `index', to avoid
+ * trashing readahead.
+ */
+static unsigned long shrink_pagecache(struct file *file,
+				      unsigned long max_index)
+{
+	struct address_space *mapping = file->f_dentry->d_inode->i_mapping;
+	unsigned long nr_locked, nr_clean, nr_dirty;
+
+	/*
+	 * ensure we have a decent amount of work todo
+	 */
+	if (mapping->nrpages < 256)
+		return 0;
+
+	nr_locked = shrink_list(mapping, &mapping->locked_pages, max_index);
+	nr_clean = shrink_list(mapping, &mapping->clean_pages, max_index);
+	nr_dirty = shrink_list(mapping, &mapping->dirty_pages, max_index);
+
+	return nr_locked + nr_clean + nr_dirty;
+}
+
 /*
  * This is a generic file read routine, and uses the
  * inode->i_op->readpage() function for the actual low-level
@@ -1613,6 +1697,10 @@
 	filp->f_reada = 1;
 	if (cached_page)
 		page_cache_release(cached_page);
+
+	if (filp->f_flags & O_STREAMING)
+		shrink_pagecache(filp, index);
+
 	UPDATE_ATIME(inode);
 }
 
@@ -3184,6 +3272,9 @@
 	inode->i_ctime = inode->i_mtime = CURRENT_TIME;
 	mark_inode_dirty_sync(inode);
 
+	if (file->f_flags & O_STREAMING)
+		shrink_pagecache(file, pos >> PAGE_CACHE_SHIFT);
+
 	do {
 		unsigned long index, offset;
 		long page_fault;
diff -Nur linux-2.4.30/net/ipv4/ipconfig.c linux-2.4.30-linux-mips.org/net/ipv4/ipconfig.c
--- linux-2.4.30/net/ipv4/ipconfig.c	2005-04-04 03:42:20.000000000 +0200
+++ linux-2.4.30-linux-mips.org/net/ipv4/ipconfig.c	2005-05-12 18:01:29.000000000 +0200
@@ -107,7 +107,7 @@
  */
 int ic_set_manually __initdata = 0;		/* IPconfig parameters set manually */
 
-int ic_enable __initdata = 0;			/* IP config enabled? */
+int ic_enable __initdata = 1;			/* IP config enabled? */
 
 /* Protocol choice */
 int ic_proto_enabled __initdata = 0
@@ -689,8 +689,6 @@
 		b->htype = dev->type;
 	else if (dev->type == ARPHRD_IEEE802_TR) /* fix for token ring */
 		b->htype = ARPHRD_IEEE802;
-	else if (dev->type == ARPHRD_FDDI)
-		b->htype = ARPHRD_ETHER;
 	else {
 		printk("Unknown ARP type 0x%04x for device %s\n", dev->type, dev->name);
 		b->htype = dev->type; /* can cause undefined behavior */
@@ -901,9 +899,6 @@
 				break;
 
 			case DHCPACK:
-				if (memcmp(dev->dev_addr, b->hw_addr, dev->addr_len) != 0)
-					goto drop;
-
 				/* Yeah! */
 				break;
 
