diff -uNrb linux-2.4.30/arch/mips/Makefile linux/arch/mips/Makefile
--- linux-2.4.30/arch/mips/Makefile	Mon Feb 14 11:26:06 2005
+++ linux/arch/mips/Makefile	Mon Feb 21 14:48:03 2005
@@ -17,7 +17,7 @@
 # Select the object file format to substitute into the linker script.
 #
 ifdef CONFIG_CPU_LITTLE_ENDIAN
-tool-prefix	= mipsel-linux-
+tool-prefix	= mipsel-linux-uclibc-
 ld-emul		= elf32ltsmip
 else
 tool-prefix	= mips-linux-
@@ -45,6 +45,7 @@
 #
 GCCFLAGS	:= -I $(TOPDIR)/include/asm/gcc
 GCCFLAGS	+= -G 0 -mno-abicalls -fno-pic -pipe
+GCCFLAGS	+= $(call check_gcc, -fno-unit-at-a-time,)
 GCCFLAGS	+= $(call check_gcc, -finline-limit=100000,)
 LINKFLAGS	+= -G 0 -static -n
 MODFLAGS	+= -mlong-calls
@@ -712,6 +713,33 @@
 LIBS          += $(BOARD)/toshiba_rbtx4927.o $(COMMON)/tx4927.o
 SUBDIRS       += $(BOARD)                    $(COMMON)
 LOADADDR      += 0x80020000
+endif
+
+#
+# IDT 79EB434 evaluation board.
+#
+ifdef CONFIG_IDT_79EB434
+LIBS          += arch/mips/rc32434/79EB434/idt.o
+SUBDIRS       += arch/mips/rc32434/79EB434
+LOADADDR      += 0x80200000
+endif
+
+#
+# Mikrotik RB500 board.
+#
+ifdef CONFIG_MIKROTIK_RB500
+LIBS          += arch/mips/rc32434/rb500/rb500.o
+SUBDIRS       += arch/mips/rc32434/rb500
+LOADADDR      += 0x80200000
+endif
+
+#
+# ADMtek5120 board.
+#
+ifdef CONFIG_MIPS_AM5120
+LIBS	      += arch/mips/am5120/am5120.o
+SUBDIRS       += arch/mips/am5120
+LOADADDR      += 0x80002000
 endif
 
 #
diff -uNrb linux-2.4.30/arch/mips/kernel/proc.c linux/arch/mips/kernel/proc.c
--- linux-2.4.30/arch/mips/kernel/proc.c	Mon Feb 14 11:26:06 2005
+++ linux/arch/mips/kernel/proc.c	Fri Apr  1 09:25:41 2005
@@ -80,6 +80,7 @@
 	[CPU_AU1200]	"Au1200",
 };
 
+extern unsigned int idt_cpu_freq;
 
 static int show_cpuinfo(struct seq_file *m, void *v)
 {
@@ -106,6 +107,9 @@
 	                            current_cpu_data.cputype : CPU_UNKNOWN],
 	                           (version >> 4) & 0x0f, version & 0x0f,
 	                           (fp_vers >> 4) & 0x0f, fp_vers & 0x0f);
+	unsigned cpu_khz = idt_cpu_freq / 500;
+	seq_printf(m, "cpu MHz\t\t\t: %lu.%03lu\n",
+		cpu_khz / 1000, (cpu_khz % 1000));
 	seq_printf(m, "BogoMIPS\t\t: %lu.%02lu\n",
 	              loops_per_jiffy / (500000/HZ),
 	              (loops_per_jiffy / (5000/HZ)) % 100);
diff -uNrb linux-2.4.30/arch/mips/kernel/setup.c linux/arch/mips/kernel/setup.c
--- linux-2.4.30/arch/mips/kernel/setup.c	Mon Feb 14 11:26:06 2005
+++ linux/arch/mips/kernel/setup.c	Mon Feb 21 14:48:03 2005
@@ -496,6 +496,9 @@
 	void hp_setup(void);
 	void au1x00_setup(void);
 	void frame_info_init(void);
+	void idt_setup(void);
+	void rb_setup(void);
+	void am5120_setup(void);
 
 	frame_info_init();
 #if defined(CONFIG_BLK_DEV_FD) || defined(CONFIG_BLK_DEV_FD_MODULE)
@@ -692,6 +695,21 @@
         case MACH_GROUP_TITAN:
                 pmc_yosemite_setup();
                 break;
+#endif
+#ifdef CONFIG_IDT_79EB434
+	case MACH_GROUP_IDT:
+		idt_setup();
+		break;
+#endif
+#ifdef CONFIG_MIKROTIK_RB500
+	case MACH_GROUP_MIKROTIK:
+		rb_setup();
+		break;
+#endif
+#ifdef CONFIG_MIPS_AM5120
+	case MACH_GROUP_ADM_GW:
+		am5120_setup();
+		break;
 #endif
 	default:
 		panic("Unsupported architecture");
diff -uNrb linux-2.4.30/arch/mips/kernel/traps.c linux/arch/mips/kernel/traps.c
--- linux-2.4.30/arch/mips/kernel/traps.c	Mon Feb 14 11:26:06 2005
+++ linux/arch/mips/kernel/traps.c	Mon Feb 14 16:42:20 2005
@@ -129,25 +129,25 @@
 
 	sp = sp ? sp : (long *)&sp;
 
-	printk("Stack:   ");
+	printb("Stack:   ");
 	i = 1;
 	while ((long) sp & (PAGE_SIZE - 1)) {
 		if (i && ((i % 8) == 0))
-			printk("\n");
+			printb("\n");
 		if (i > 40) {
-			printk(" ...");
+			printb(" ...");
 			break;
 		}
 
 		if (__get_user(stackdata, sp++)) {
-			printk(" (Bad stack address)");
+			printb(" (Bad stack address)");
 			break;
 		}
 
-		printk(" %08lx", stackdata);
+		printb(" %08lx", stackdata);
 		i++;
 	}
-	printk("\n");
+	printb("\n");
 }
 
 void show_trace(long *sp)
@@ -157,14 +157,14 @@
 
 	sp = sp ? sp : (long *) &sp;
 
-	printk("Call Trace:  ");
+	printb("Call Trace:  ");
 	i = 1;
 	while ((long) sp & (PAGE_SIZE - 1)) {
 
 		if (__get_user(addr, sp++)) {
 			if (i && ((i % 6) == 0))
-				printk("\n");
-			printk(" (Bad stack address)\n");
+				printb("\n");
+			printb(" (Bad stack address)\n");
 			break;
 		}
 
@@ -179,17 +179,17 @@
 
 		if (kernel_text_address(addr)) {
 			if (i && ((i % 6) == 0))
-				printk("\n");
+				printb("\n");
 			if (i > 40) {
-				printk(" ...");
+				printb(" ...");
 				break;
 			}
 
-			printk(" [<%08lx>]", addr);
+			printb(" [<%08lx>]", addr);
 			i++;
 		}
 	}
-	printk("\n");
+	printb("\n");
 }
 
 void show_trace_task(struct task_struct *tsk)
@@ -201,15 +201,15 @@
 {
 	long i;
 
-	printk("\nCode:");
+	printb("\nCode:");
 
 	for(i = -3 ; i < 6 ; i++) {
 		unsigned long insn;
 		if (__get_user(insn, pc + i)) {
-			printk(" (Bad address in epc)\n");
+			printb(" (Bad address in epc)\n");
 			break;
 		}
-		printk("%c%08lx%c",(i?' ':'<'),insn,(i?' ':'>'));
+		printb("%c%08lx%c",(i?' ':'<'),insn,(i?' ':'>'));
 	}
 }
 
@@ -218,39 +218,39 @@
 	/*
 	 * Saved main processor registers
 	 */
-	printk("$0 : %08x %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
+	printb("$0 : %08x %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
 	       0,             regs->regs[1], regs->regs[2], regs->regs[3],
 	       regs->regs[4], regs->regs[5], regs->regs[6], regs->regs[7]);
-	printk("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
+	printb("$8 : %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
 	       regs->regs[8],  regs->regs[9],  regs->regs[10], regs->regs[11],
 	       regs->regs[12], regs->regs[13], regs->regs[14], regs->regs[15]);
-	printk("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
+	printb("$16: %08lx %08lx %08lx %08lx %08lx %08lx %08lx %08lx\n",
 	       regs->regs[16], regs->regs[17], regs->regs[18], regs->regs[19],
 	       regs->regs[20], regs->regs[21], regs->regs[22], regs->regs[23]);
-	printk("$24: %08lx %08lx                   %08lx %08lx %08lx %08lx\n",
+	printb("$24: %08lx %08lx                   %08lx %08lx %08lx %08lx\n",
 	       regs->regs[24], regs->regs[25],
 	       regs->regs[28], regs->regs[29], regs->regs[30], regs->regs[31]);
-	printk("Hi : %08lx\n", regs->hi);
-	printk("Lo : %08lx\n", regs->lo);
+	printb("Hi : %08lx\n", regs->hi);
+	printb("Lo : %08lx\n", regs->lo);
 
 	/*
 	 * Saved cp0 registers
 	 */
-	printk("epc   : %08lx    %s\n", regs->cp0_epc, print_tainted());
-	printk("Status: %08lx\n", regs->cp0_status);
-	printk("Cause : %08lx\n", regs->cp0_cause);
-	printk("PrId  : %08x\n", read_c0_prid());
+	printb("epc   : %08lx    %s\n", regs->cp0_epc, print_tainted());
+	printb("Status: %08lx\n", regs->cp0_status);
+	printb("Cause : %08lx\n", regs->cp0_cause);
+	printb("PrId  : %08x\n", read_c0_prid());
 }
 
 void show_registers(struct pt_regs *regs)
 {
 	show_regs(regs);
-	printk("Process %s (pid: %d, stackpage=%08lx)\n",
+	printb("Process %s (pid: %d, stackpage=%08lx)\n",
 		current->comm, current->pid, (unsigned long) current);
 	show_stack((long *) regs->regs[29]);
 	show_trace((long *) regs->regs[29]);
 	show_code((unsigned int *) regs->cp0_epc);
-	printk("\n");
+	printb("\n");
 }
 
 static spinlock_t die_lock = SPIN_LOCK_UNLOCKED;
@@ -260,10 +260,10 @@
 {
 	console_verbose();
 	spin_lock_irq(&die_lock);
-	printk("%s", str);
+	printb("%s", str);
 	if (file && func)
-		printk(" in %s:%s, line %ld", file, func, line);
-	printk(":\n");
+		printb(" in %s:%s, line %ld", file, func, line);
+	printb(":\n");
 	show_registers(regs);
 	spin_unlock_irq(&die_lock);
 	do_exit(SIGSEGV);
@@ -379,7 +379,7 @@
 	/*
 	 * Assume it would be too dangerous to continue ...
 	 */
-	printk(KERN_ALERT "%s bus error, epc == %08lx, ra == %08lx\n",
+	printb(KERN_ALERT "%s bus error, epc == %08lx, ra == %08lx\n",
 	       data ? "Data" : "Instruction",
 	       regs->cp0_epc, regs->regs[31]);
 	die_if_kernel("Oops", regs);
@@ -766,7 +766,7 @@
 	switch (current_cpu_data.cputype) {
 	case CPU_5KC:
 		/* Set the PE bit (bit 31) in the c0_ecc register. */
-		printk(KERN_INFO "Enable the cache parity protection for "
+		printb(KERN_INFO "Enable the cache parity protection for "
 		       "MIPS 5KC CPUs.\n");
 		write_c0_ecc(read_c0_ecc() | 0x80000000);
 		break;
@@ -780,15 +780,15 @@
 	unsigned int reg_val;
 
 	/* For the moment, report the problem and hang. */
-	printk("Cache error exception:\n");
-	printk("cp0_errorepc == %08lx\n", read_c0_errorepc());
+	printb("Cache error exception:\n");
+	printb("cp0_errorepc == %08lx\n", read_c0_errorepc());
 	reg_val = read_c0_cacheerr();
-	printk("c0_cacheerr == %08x\n", reg_val);
+	printb("c0_cacheerr == %08x\n", reg_val);
 
-	printk("Decoded c0_cacheerr: %s cache fault in %s reference.\n",
+	printb("Decoded c0_cacheerr: %s cache fault in %s reference.\n",
 	       reg_val & (1<<30) ? "secondary" : "primary",
 	       reg_val & (1<<31) ? "data" : "insn");
-	printk("Error bits: %s%s%s%s%s%s%s\n",
+	printb("Error bits: %s%s%s%s%s%s%s\n",
 	       reg_val & (1<<29) ? "ED " : "",
 	       reg_val & (1<<28) ? "ET " : "",
 	       reg_val & (1<<26) ? "EE " : "",
@@ -796,14 +796,14 @@
 	       reg_val & (1<<24) ? "EI " : "",
 	       reg_val & (1<<23) ? "E1 " : "",
 	       reg_val & (1<<22) ? "E0 " : "");
-	printk("IDX: 0x%08x\n", reg_val & ((1<<22)-1));
+	printb("IDX: 0x%08x\n", reg_val & ((1<<22)-1));
 
 #if defined(CONFIG_CPU_MIPS32) || defined (CONFIG_CPU_MIPS64)
 	if (reg_val & (1<<22))
-		printk("DErrAddr0: 0x%08lx\n", read_c0_derraddr0());
+		printb("DErrAddr0: 0x%08lx\n", read_c0_derraddr0());
 
 	if (reg_val & (1<<23))
-		printk("DErrAddr1: 0x%08lx\n", read_c0_derraddr1());
+		printb("DErrAddr1: 0x%08lx\n", read_c0_derraddr1());
 #endif
 
 	panic("Can't handle the cache error!");
@@ -818,10 +818,10 @@
 	unsigned long depc, old_epc;
 	unsigned int debug;
 
-	printk("SDBBP EJTAG debug exception - not handled yet, just ignored!\n");
+	printb("SDBBP EJTAG debug exception - not handled yet, just ignored!\n");
 	depc = read_c0_depc();
 	debug = read_c0_debug();
-	printk("c0_depc = %08lx, DEBUG = %08x\n", depc, debug);
+	printb("c0_depc = %08lx, DEBUG = %08x\n", depc, debug);
 	if (debug & 0x80000000) {
 		/*
 		 * In branch delay slot.
@@ -839,7 +839,7 @@
 	write_c0_depc(depc);
 
 #if 0
-	printk("\n\n----- Enable EJTAG single stepping ----\n\n");
+	printb("\n\n----- Enable EJTAG single stepping ----\n\n");
 	write_c0_debug(debug | 0x100);
 #endif
 }
@@ -849,7 +849,7 @@
  */
 void nmi_exception_handler(struct pt_regs *regs)
 {
-	printk("NMI taken!!!!\n");
+	printb("NMI taken!!!!\n");
 	die("NMI", regs);
 	while(1) ;
 }
diff -uNrb linux-2.4.30/arch/mips/kernel/unaligned.c linux/arch/mips/kernel/unaligned.c
--- linux-2.4.30/arch/mips/kernel/unaligned.c	Fri Nov 19 12:32:15 2004
+++ linux/arch/mips/kernel/unaligned.c	Thu Mar  3 15:12:05 2005
@@ -92,6 +92,24 @@
 unsigned long unaligned_instructions;
 #endif
 
+#define RATE_BURST (10*5*HZ)
+#define RATE_COST (5*HZ)
+
+static int ratelimit(void) {
+	static unsigned toks = RATE_BURST;
+	static unsigned last_msg;
+
+	unsigned now = jiffies;
+	toks += now - last_msg;
+	if (toks > RATE_BURST) toks = RATE_BURST;
+
+	if (toks >= RATE_COST) {
+		toks -= RATE_COST;
+		return 1;
+	}
+	return 0;
+}
+
 static inline int emulate_load_store_insn(struct pt_regs *regs,
 	void *addr, unsigned long pc,
 	unsigned long **regptr, unsigned long *newvalue)
@@ -522,7 +540,13 @@
 		goto sigbus;
 
 	pc = exception_epc(regs);
-	if ((current->thread.mflags & MF_FIXADE) == 0)
+
+	if (!user_mode(regs)) {
+		if (ratelimit()) {
+			printk(KERN_WARNING
+			       "unaligned data access at 0x%lx\n", pc);
+		}
+	} else if ((current->thread.mflags & MF_FIXADE) == 0)
 		goto sigbus;
 
 	/*
diff -uNrb linux-2.4.30/arch/mips/mm/fault.c linux/arch/mips/mm/fault.c
--- linux-2.4.30/arch/mips/mm/fault.c	Fri Nov 19 12:32:15 2004
+++ linux/arch/mips/mm/fault.c	Tue Jan  4 16:28:03 2005
@@ -64,7 +64,7 @@
 		 * a poke.  Hold onto your hats...
 		 */
 		console_loglevel = 15;		/* NMI oopser may have shut the console up */
-		printk(" ");
+		printb(" ");
 		console_loglevel = loglevel_save;
 	}
 }
@@ -103,7 +103,7 @@
 	if (in_interrupt() || !mm)
 		goto no_context;
 #if 0
-	printk("[%s:%d:%08lx:%ld:%08lx]\n", current->comm, current->pid,
+	printb("[%s:%d:%08lx:%ld:%08lx]\n", current->comm, current->pid,
 	       address, write, regs->cp0_epc);
 #endif
 	down_read(&mm->mmap_sem);
@@ -165,7 +165,7 @@
 		tsk->thread.cp0_badvaddr = address;
 		tsk->thread.error_code = write;
 #if 0
-		printk("do_page_fault() #2: sending SIGSEGV to %s for illegal %s\n"
+		printb("do_page_fault() #2: sending SIGSEGV to %s for illegal %s\n"
 		       "%08lx (epc == %08lx, ra == %08lx)\n",
 		       tsk->comm,
 		       write ? "write access to" : "read access from",
@@ -190,7 +190,7 @@
 		tsk->thread.cp0_baduaddr = address;
 		new_epc = fixup_exception(dpf_reg, fixup, regs->cp0_epc);
 		if (development_version)
-			printk(KERN_DEBUG "%s: Exception at [<%lx>] (%lx)\n",
+			printb(KERN_DEBUG "%s: Exception at [<%lx>] (%lx)\n",
 			       tsk->comm, regs->cp0_epc, new_epc);
 		regs->cp0_epc = new_epc;
 		return;
@@ -200,7 +200,7 @@
 	 * Oops. The kernel tried to access some bad page. We'll have to
 	 * terminate things with extreme prejudice.
 	 */
-	printk(KERN_ALERT "Unable to handle kernel paging request at virtual "
+	printb(KERN_ALERT "Unable to handle kernel paging request at virtual "
 	       "address %08lx, epc == %08lx, ra == %08lx\n",
 	       address, regs->cp0_epc, regs->regs[31]);
 	die("Oops", regs);
@@ -216,7 +216,7 @@
 		goto survive;
 	}
 	up_read(&mm->mmap_sem);
-	printk(KERN_NOTICE "VM: killing process %s\n", tsk->comm);
+	printb(KERN_NOTICE "VM: killing process %s\n", tsk->comm);
 	if (user_mode(regs))
 		do_exit(SIGKILL);
 	goto no_context;
diff -uNrb linux-2.4.30/arch/mips/pci/pci_auto.c linux/arch/mips/pci/pci_auto.c
--- linux-2.4.30/arch/mips/pci/pci_auto.c	Fri Nov 19 12:32:15 2004
+++ linux/arch/mips/pci/pci_auto.c	Wed Jan 19 18:24:34 2005
@@ -396,7 +396,10 @@
 		devfn_stop = hose->last_devfn;
 
 	for (pci_devfn=devfn_start; pci_devfn<devfn_stop; pci_devfn++) {
-
+#ifdef CONFIG_MIKROTIK_RB500
+	        // rb500 patch
+	        if (current_bus == 0 && (pci_devfn >> 3) > 21) break;
+#endif
 		if (PCI_FUNC(pci_devfn) && !found_multi)
 			continue;
 
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/Makefile linux/arch/mips/rc32434/79EB434/Makefile
--- linux-2.4.30/arch/mips/rc32434/79EB434/Makefile	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/Makefile	Fri Sep 24 13:40:52 2004
@@ -0,0 +1,41 @@
+#
+# Makefile for the IDT 79EB434 board specific parts of the kernel
+#
+# 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).
+#######################################################################
+# P. Sadik      Oct 08 2003
+#
+# Started revision history
+# Added nvram driver compilation
+#######################################################################
+#######################################################################
+# P. Sadik      Oct 10 2003
+#
+# Added serial port initialisation.
+# porm.o is exported to make idt_cpu_freq visible to other modules
+#######################################################################
+
+.S.s:
+	$(CPP) $(CFLAGS) $< -o $*.s
+.S.o:
+	$(CC) $(CFLAGS) -c $< -o $*.o
+
+O_TARGET := idt.o
+export-objs = prom.o
+obj-y	 := irq.o prom.o time.o setup.o idtIRQ.o serial_gdb.o serial.o
+obj-$(CONFIG_PCI) += pci_fixup.o pci_ops.o
+obj-$(CONFIG_MIPS_RTC) += rtc-ds1553.o
+ifeq ($(CONFIG_IDT_BOOT_NVRAM),y)
+obj-y += nvram/nvram.o
+endif
+subdir-$(CONFIG_IDT_BOOT_NVRAM) += nvram
+include $(TOPDIR)/Rules.make
+
+
+
+
+
+
+
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/boot/Makefile linux/arch/mips/rc32434/79EB434/boot/Makefile
--- linux-2.4.30/arch/mips/rc32434/79EB434/boot/Makefile	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/boot/Makefile	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,136 @@
+##################################################################
+# create a compressed zImage and Rommable rImage from the original
+# vmlinux
+#
+##################################################################
+# Oct 29 2002
+# P. Sadik   (Pallathu.Sadik@idt.com)
+##################################################################
+# P. Sadik  Oct 08 2003
+#
+# Modified to make  zImage bootable
+# Removed zImage.o rule.
+##################################################################
+
+##################################################################
+# The following is taken from IDT/Sim Makefile
+##################################################################
+TARGET=434
+TARGETDIR=S434
+
+#
+# following refers to memory type in use in eval board and if more than one
+# then the order is implied.  These are values for the switch MEMCFG.
+#
+SRAM_ONLY=1
+SDRAM_ONLY=2
+SRAM_N_SDRAM=3
+SDRAM_N_SRAM=4
+
+# following refers to size of the DRAM space.
+# These are values for the switch DRAMSZ.
+
+MB32=1
+MB64=2
+MB128=3
+MB32SO=4
+
+MACH= -DEB434 -DS434 -DCPU_R32434 -DMIPSEL -DINET -DMEMCFG=$(SDRAM_ONLY) -DDRAMSZ=$(MB32) -DIDTSIM -DNVRAM_RTC -DUSE_SPI
+COMMSWITCHES = $(MACH)
+#***************** END IDT/Sim Makefile ##################################### 
+ZDEBUG=0
+export ZDEBUG
+
+include $(TOPDIR)/.config
+
+SIZE = $(CROSS_COMPILE)size
+
+O_FORMAT = $(shell $(OBJDUMP) -i | head -2 | grep elf32)
+OBJDUMP_VERSION := $(shell if $(OBJDUMP) -i | head -2 2>&1 | \
+			grep 'elf32-trad' >/dev/null; then echo y;else echo n;fi)
+export OBJDUMP_VERSION
+
+SYSTEM	      := $(TOPDIR)/vmlinux
+ZBSS          := -Tbss 0x800A0000
+
+ifdef CONFIG_IDT_FLASH_ADDR
+ZIMSTART      := $(CONFIG_IDT_FLASH_ADDR)
+else
+ZIMSTART      := 0x91000000
+endif
+BFCSTART      := 0x9FC00000
+# working space for gunzip:
+FREE_RAM      := 0x80C00000
+END_RAM       := 0x80E00000
+
+COMMFLAGS  = -static $(ZBSS) -T $(TOPDIR)/arch/mips/ld.script
+RLINKFLAGS = -Ttext $(BFCSTART) -e start $(COMMFLAGS)
+ZLINKFLAGS = -Ttext $(ZIMSTART) -e zstartup  $(COMMFLAGS)
+
+# Some versions of mipsel-linux-objdump displays addresses starting with 0xffffffff.
+# Hence strip that portion of the address.
+ifneq ($(OBJDUMP_VERSION),y)
+LOADADDR      := 0x$(shell $(NM) $(SYSTEM) | \
+			grep _ftext |cut -f1 -d' ' |sed -n -e 's/ffffffff//p')
+KERNEL_ENTRY  := $(shell $(OBJDUMP) -f $(SYSTEM) | \
+			sed -n -e 's/^start address //p' |sed -n -e 's/ffffffff//p')
+else
+LOADADDR      := 0x$(shell $(NM) $(SYSTEM) | grep _ftext |cut -f1 -d' ')
+KERNEL_ENTRY  := $(shell $(OBJDUMP) -f $(SYSTEM) | sed -n -e 's/^start address //p')
+endif
+
+####################################################################################
+OBJECTS := piggy.o head.o misc.o
+ifneq ($(ZDEBUG),0)
+OBJECTS += uart16550.o
+endif
+
+.PHONY: zImage rImage
+zImage: $(TOPDIR)/zImage
+rImage: $(TOPDIR)/rImage
+
+head.o: head.S Makefile $(SYSTEM)
+	$(CC) $(CFLAGS) -DKERNEL_ENTRY=$(KERNEL_ENTRY) -c head.S
+
+misc.o: misc.c Makefile
+	$(CC) $(CFLAGS) -DLOADADDR=$(LOADADDR) -DFREE_RAM=$(FREE_RAM) -DEND_RAM=$(END_RAM) -c misc.c -DZDEBUG=$(ZDEBUG)
+
+piggy.o: $(SYSTEM)
+	$(OBJCOPY) -S -O binary -R .note -R .comment $(SYSTEM) $(SYSTEM).bin
+	gzip -f -9 < $(SYSTEM).bin > $(SYSTEM).gz
+	echo "OUTPUT_ARCH(mips) SECTIONS { .initrd : { input_len = .; LONG(input_data_end - input_data) \
+		input_data = .; *(.data) input_data_end = .; }}" > ld.script
+	echo "O_FORMAT:  " $(O_FORMAT); 
+	$(LD) -T ld.script -b binary --oformat $(O_FORMAT) -o piggy.o $(SYSTEM).gz
+	rm -f $(SYSTEM).bin $(SYSTEM).gz ld.script
+
+$(TOPDIR)/zImage: $(OBJECTS)
+	$(LD) $(ZLINKFLAGS) -o $(TOPDIR)/zImage $(OBJECTS)
+	$(OBJCOPY) -S -O binary -R .note -R .comment $(TOPDIR)/zImage $(TOPDIR)/zImage.bin
+	$(OBJCOPY) -I binary -S -O srec --srec-forceS3 --srec-len=32 --change-start=0x00000000 \
+		 $(TOPDIR)/zImage.bin $(TOPDIR)/zImage.prm
+	$(SIZE) $(TOPDIR)/zImage |awk -F" " '{ print $$4 "\t" $$5 }' > $(TOPDIR)/zImage.size
+	rm -f *.o
+
+csu_idt.o: csu_idt.S Makefile $(SYSTEM)
+	$(CC) $(AFLAGS) $(UFLAGS) $(COMMSWITCHES) -c csu_idt.S
+
+
+ifdef  CONFIG_IDT_BOOT_FROM_PROM
+$(TOPDIR)/rImage: $(OBJECTS) csu_idt.o
+	@rm -f $(TOPDIR)/*.prm
+	$(LD) $(RLINKFLAGS) -o $(TOPDIR)/rImage csu_idt.o $(OBJECTS) 
+	$(OBJCOPY) -S -O binary -R .note -R .comment $(TOPDIR)/rImage $(TOPDIR)/rImage.bin
+	$(OBJCOPY) -I binary -S -O srec --srec-forceS3 --srec-len=32 --change-start=0x00000000 \
+		 $(TOPDIR)/rImage.bin $(TOPDIR)/linux.prm
+	$(SIZE) $(TOPDIR)/rImage |awk -F" " '{ print $$4 "\t" $$5 }' > $(TOPDIR)/linux.size
+	rm -f *.o
+else
+$(TOPDIR)/rImage: $(SYSTEM)
+	@echo
+	@echo "Error! Re-configure kernel with 'Create ROMMABLE Image' option enabled."
+	@echo
+endif
+
+clean:
+	rm -f *.o $(TOPDIR)/zImage $(TOPDIR)/rImage $(TOPDIR)/rImage.bin $(TOPDIR)/*.prm $(TOPDIR)/linux.size
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/boot/csu_idt.S linux/arch/mips/rc32434/79EB434/boot/csu_idt.S
--- linux-2.4.30/arch/mips/rc32434/79EB434/boot/csu_idt.S	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/boot/csu_idt.S	Thu Sep  9 10:48:16 2004
@@ -0,0 +1,331 @@
+/**************************************************************************
+ * csu_idt.S
+ *
+ * Board initialization.
+ **************************************************************************
+ * Ported from IDT/Sim
+ *
+ *   Nov 06 2002
+ * P. Sadik    Pallathu.Sadik@idt.com
+ **************************************************************************
+ * P. Sadik   Oct 08, 2003
+ *
+ * Removed zImage copy to memory
+ * Added jumping to zstartup
+ **************************************************************************
+*/	
+#include <linux/config.h>
+#include <linux/threads.h>
+
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/offset.h>
+#include <asm/cachectl.h>
+
+#define MHZ CONFIG_IDT_BOARD_FREQ
+		
+#include "idthdr.h"
+#include "iregdef.h"
+#include "idtcpu.h"
+#include "s434ram.h"
+#include "s434.h"
+
+#define IndexInvalidate_I       0x00
+
+/*--------------------------------------------------------------
+** prom entry point table
+*-------------------------------------------------------------*/
+
+FRAME(start,sp,0,ra)
+  j idtstart        /* begin monitor from start       |00| */
+
+idtstart:
+
+  .set  noreorder
+      mtc0  zero, C0_CAUSE
+            nop
+      li    v0, 0x0
+      or    v0, (SR_CU0 | SR_BEV)
+      mtc0  v0, C0_SR
+            nop
+      mfc0  v1, C0_CONFIG
+            nop
+      and   v1, ~(0x7)
+      ori   v1, 0x3
+      mtc0  v1, C0_CONFIG
+            nop
+            nop
+            
+/* ------------------- Disable WatchDog Timer ----------------------------- */
+      li    t0, WTC_BASE
+      sw    zero, 0x3C(t0) /* WTC */
+/* ------ Alternate functions for GPIO pins --------------------------------*/
+/* only UART0, UART1 and mem_addr */
+      li    t0, GPIO_BASE
+      li    t1, 0xf3
+      sw    t1, 0x0(t0)
+
+#if !defined(PCISAT)
+/* ------------------- Assert PCI reset ----------------------------------- */
+			li    t0, 0xb8080000
+      lw    t1, (t0)
+      andi  t2, t1, 0x1
+      beqz  t2, 2f
+            nop
+      andi  t2, t1, 0x3fe
+      sw    t2, (t0)
+      li    t2, 0x1000
+1:
+      addi  t2, -1
+      bnez  t2, 1b
+            nop
+2:
+      ori   t2, t1, 0x1
+      sw    t2, (t0)
+      lui   t2, 0x2
+rip:
+      lw    t1, 4(t0)
+      and   t1, t1, t2
+      bnez  t1, rip
+            nop
+#endif
+
+/* ------------------- Setup Device Controller ---------------------------- */
+      li    t0, DEV_CTL_BASE      /* load 2 base address registers' base    */
+      li    t1, DEV0_CTRL         /* device0 control parameter              */
+      sw    t1, 0x8(t0)           /* set the control register  CS0          */
+      li    t1, DEV0_TC           /* device0 timing config parameter        */
+      sw    t1, 0xC(t0)
+      li    t1, DEV1_BASE         /* set the device base register for CS1   */
+      sw    t1, 0x10(t0)
+      li    t1, DEV1_MASK         /* set the device mask register for CS1   */
+      sw    t1, 0x14(t0) 
+      li    t1, DEV1_CTRL         /* set the device control register for CS1*/
+      sw    t1, 0x18(t0)
+      li    t1, DEV1_TC           /* set the device timing register for CS1 */
+      sw    t1, 0x1C(t0) 
+
+      li    t1, DEV2_BASE         /* set the device base register for CS1   */
+      sw    t1, 0x20(t0)
+      li    t1, DEV2_MASK         /* set the device mask register for CS1   */
+      sw    t1, 0x24(t0) 
+      li    t1, DEV2_CTRL         /* set the device control register for CS1*/
+      sw    t1, 0x28(t0)
+      li    t1, DEV2_TC           /* set the device timing register for CS1 */
+      sw    t1, 0x2C(t0) 
+
+      li    t1, DEV3_BASE         /* set the device base register for CS1   */
+      sw    t1, 0x30(t0)
+      li    t1, DEV3_MASK         /* set the device mask register for CS1   */
+      sw    t1, 0x34(t0) 
+      li    t1, DEV3_CTRL         /* set the device control register for CS1*/
+      sw    t1, 0x38(t0)
+      li    t1, DEV3_TC           /* set the device timing register for CS1 */
+      sw    t1, 0x3C(t0) 
+
+      DISPLAY('I','D','D','R')
+#if MEMCFG != SRAM_ONLY
+
+/* ------------- INITIALIZE DDR SDRAM CONTROLLER ---------------------------*/
+
+      li    t1, 0x0               /* Add 200 microseconds of delay */
+      li    t2, DELAY_200USEC
+1:
+      add   t1, 1
+      bne   t1, t2, 1b
+            nop
+
+/*-------------- Initialize DDR Base and Mask Registers --------------------*/
+
+      li    t0, DDR_BASE
+
+  /* Load the DDRC, reset  Refresh Enable */
+      li    t1, DDRC_VAL_AT_INIT
+      sw    t1, 0x10(t0)
+      
+      sw    zero, 0x4(t0)
+      sw    zero, 0xc(t0)
+      sw    zero, 0x18(t0)
+
+  /* Store DDRBASE */
+      li    t1, DDR_BASE_VAL
+      sw    t1, 0x0(t0)
+
+  /* Store DDRMASK */
+      li    t1, DDR_MASK_VAL
+      sw    t1, 0x4(t0)
+
+  /* Store DDRABASE */
+      li    t1, DDR_ABASE_VAL
+      sw    t1, 0x14(t0)
+
+  /* Load DDRAMASK to disable alternate Mapping */
+      li    t1, DDR_AMASK_VAL
+      sw    t1, 0x18(t0)
+
+      li    t1, DDR_CUST_NOP      /* Write to DDR Custom transaction register */
+      sw    t1, 0x20(t0)
+
+      li    t2, DATA_PATTERN
+      li    t1, 0xA0000000 | DDR_BASE_VAL
+      sw    t2, 0x0(t1)
+
+  /* Add 200 microseconds of delay */
+      li    t1, 0x0
+      li    t2, DELAY_200USEC
+1:
+      add   t1, 1
+      bne   t1, t2, 1b
+            nop
+            
+  /* Register t0 carries pointer to the DDR_BASE: 0xB8018000 */
+      li    t1, DDR_CUST_PRECHARGE
+      sw    t1, 0x20(t0)    /* Write to DDR Custom transaction register */
+
+  /* Generate A10 high to pre-charge both the banks */
+      li    t2, DATA_PATTERN
+      li    t1, 0xA0000000 | DDR_PRECHARGE_OFFSET | DDR_BASE_VAL
+      sw    t2, 0x0(t1)
+
+  /* Register t0 carries pointer to the DDR_BASE: 0xB8018000 */
+      li    t1, DDR_LD_EMODE_REG
+      sw    t1, 0x20(t0)    /* Write to DDR Custom transaction register */
+
+  /* Generate EMODE register contents on A15-A2 */
+      li    t2, DATA_PATTERN
+      li    t1, 0xA0000000 | DDR_EMODE_VAL | DDR_BASE_VAL
+      sw    t2, 0x0(t1)
+
+  /* Register t0 carries pointer to the DDR_BASE: 0xB8018000 */
+      li    t1, DDR_LD_MODE_REG
+      sw    t1, 0x20(t0)    /* Write to DDR Custom transaction register */
+
+  /* Generate Mode register contents on the address bus A15-A2  */
+      li    t2, DATA_PATTERN
+      li    t1, 0xA0000000 | DDR_DLL_RES_MODE_VAL | DDR_BASE_VAL
+      sw    t2, 0x0(t1)
+
+  /* Delay of  1.6 microseconds ~ 300 delay iteration value */
+      li    t1, 0x0
+      li    t2, 500
+1:
+      add   t1, 1
+      bne   t1, t2, 1b
+            nop
+
+  /* Register t0 carries pointer to the DDR_BASE: 0xB8018000 */
+      li    t1, DDR_CUST_PRECHARGE
+      sw    t1, 0x20(t0)    /* Write to DDR Custom transaction register */
+
+  /* Generate A10 high to pre-charge both the banks */
+      li    t2, DATA_PATTERN
+      li    t1, 0xA0000000 | DDR_PRECHARGE_OFFSET | DDR_BASE_VAL
+      sw    t2, 0x0(t1)
+
+  /* Implements 9 cycles of Auto refresh allowing
+     sufficient margin for stability*/
+      li    t4, 9
+      li    t3, 0
+1:
+      li    t1, DDR_CUST_REFRESH
+      sw    t1, 0x20(t0)    /* Write to DDR Custom transaction register */
+
+  /* Read it back to flush CPU write buffers */
+      lw    t1, 0x20(t0)
+
+  /* Access DDR */
+      li    t2, DATA_PATTERN
+      li    t1, 0xA0000000 | DDR_BASE_VAL
+      sw    t2, 0x0(t1)
+
+      add   t3, 1
+      bne   t3, t4, 1b
+            nop
+
+  /* Register t0 carries pointer to the DDR_BASE: 0xB8018000 */
+      li    t1, DDR_LD_MODE_REG
+      sw    t1, 0x20(t0)    /* Write to DDR Custom transaction register */
+
+  /* Generate Mode Register contents on the address bus A12-A0 */
+      li    t2, DATA_PATTERN
+      li    t1, 0xA0000000 | DDR_DLL_MODE_VAL | DDR_BASE_VAL
+      sw    t2, 0x0(t1)
+
+  /* Initialize the refresh timer with fast refresh count */
+      li    t0, RCOUNT
+      li    t1, DDR_REF_CMP_FAST
+      
+  /* Set the RCOMPARE register */
+      sw    t1, 0x4(t0)
+
+  /* Enable the Refresh timer */
+      li    t1, 0x1           /* CE set to enabled the  Refresh counter */
+      sw    t1, 0x8(t0)
+
+  /* Enable RE-refresh enable in the DDRC register */
+      li    t0, DDR_BASE
+      li    t1, DDRC_VAL_NORMAL
+      sw    t1, 0x10(t0)
+
+  /* Add 200 microseconds of delay */
+      li    t1, 0x0
+      li    t2, DELAY_200USEC
+1:
+      add   t1, 1
+      bne   t1, t2, 1b
+            nop
+
+      li    t0, RCOUNT
+
+  /* Disable the refresh counter before changing the compare value */
+      sw    zero, 0x8(t0)
+
+  /* Set the RCOMPARE register with value gotten above */
+      li    t3, DDR_REF_CMP_VAL
+      sw    t3, 0x4(t0)
+
+  /* Enable the Refresh timer */
+      li    t1, 0x1           /* CE set to enabled the  Refresh counter */
+      sw    t1, 0x8(t0)
+
+  /* Add 200 microseconds of delay */
+      li    t1, 0x0
+      li    t2, DELAY_200USEC
+1:
+      add   t1, 1
+      bne   t1, t2, 1b
+            nop
+
+#endif
+	li    t0, 0xa0000000
+	li    t1, 0xa0100000
+1:
+	sw    zero, 0x00(t0)
+	sw    zero, 0x04(t0)
+	sw    zero, 0x08(t0)
+	sw    zero, 0x0c(t0)
+	addiu t0, 16
+	nop
+	blt   t0, t1, 1b
+	nop
+	nop
+	nop
+3:
+	mfc0  t0, C0_SR
+	nop
+	nop
+	and   t0, ~SR_BEV
+	mtc0  t0, C0_SR
+	nop
+	nop
+
+4:	
+	
+/* Jump to zImage startup */
+	        
+	la     k0, zstartup
+	j      k0
+	nop
+	nop
+
+ENDFRAME(start)
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/boot/head.S linux/arch/mips/rc32434/79EB434/boot/head.S
--- linux-2.4.30/arch/mips/rc32434/79EB434/boot/head.S	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/boot/head.S	Thu Sep  9 10:48:16 2004
@@ -0,0 +1,102 @@
+/****************************************************************************
+ * P. Sadik
+ * 
+ * Original form			
+ ****************************************************************************
+ * P. Sadik     Oct 08, 2003	
+ *
+ * Added cache flushing code.
+ ****************************************************************************
+*/
+#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(zstartup)
+zstartup:
+
+        la      sp, .stack
+	move	s0, a0
+	move	s1, a1
+	move	s2, a2
+	move	s3, a3
+
+	/* Clear BSS */
+	la	a0, .stack
+	la	a2, _end
+1:	sw	zero, 0(a0)
+	bne	a2, a0, 1b
+	addu	a0, 4
+
+	/* flush the I-Cache */
+	li	k0, 0x80000000  # start address
+	li	k1, 0x80002000  # end address (8KB I-Cache)
+	subu	k1, 128
+
+2:
+	.set mips3
+	cache   IndexInvalidate_I, 0(k0)
+	cache   IndexInvalidate_I, 16(k0)
+	cache   IndexInvalidate_I, 32(k0)
+	cache   IndexInvalidate_I, 48(k0)
+	cache   IndexInvalidate_I, 64(k0)
+	cache   IndexInvalidate_I, 80(k0)
+	cache   IndexInvalidate_I, 96(k0)
+	cache   IndexInvalidate_I, 112(k0)
+	.set mips0
+
+	bne	k0, k1, 2b
+	addu	k0, k0, 128
+	/* done */
+
+	/* flush the D-Cache */
+	li	k0, 0x80000000  # start address
+	li	k1, 0x80002000  # end address (8KB I-Cache)
+	subu	k1, 128
+
+2:
+	.set mips3
+	cache   Index_Writeback_Inv_D, 0(k0)
+	cache   Index_Writeback_Inv_D, 16(k0)
+	cache   Index_Writeback_Inv_D, 32(k0)
+	cache   Index_Writeback_Inv_D, 48(k0)
+	cache   Index_Writeback_Inv_D, 64(k0)
+	cache   Index_Writeback_Inv_D, 80(k0)
+	cache   Index_Writeback_Inv_D, 96(k0)
+	cache   Index_Writeback_Inv_D, 112(k0)
+	.set mips0
+
+	bne	k0, k1, 2b
+	addu	k0, k0, 128
+	/* done */
+
+	la	ra, 3f
+	la	k0, decompress_kernel
+	jr	k0
+	nop
+3:
+
+	move	a0, s0
+	move	a1, s1
+	move	a2, s2
+	move	a3, s3
+	li	k0, KERNEL_ENTRY
+	jr	k0
+	nop
+4:
+	b 4b
+	END(zstartup)
+
+	.bss
+	.fill 0x2000
+	EXPORT(.stack)
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/boot/idtcpu.h linux/arch/mips/rc32434/79EB434/boot/idtcpu.h
--- linux-2.4.30/arch/mips/rc32434/79EB434/boot/idtcpu.h	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/boot/idtcpu.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,313 @@
+/*
+** idtcpu.h -- cpu related defines
+**
+**	941220: Added P3 support - Evelyn Zhan (EZ)
+**	960717	Added R5000 support -- Sugan Subramanian (SS)
+**	970207	Fixed config bit-field  definition for CFG_EW32
+**	971107	Added R32364 support -- Alfredo Edmonds (AE)
+**  020719  Only for R32364 & R32434 -- Nebojsa Bjegovic
+*/
+
+#if !defined(__IDTCPU_H__)
+#define __IDTCPU_H__
+/*
+** memory configuration and mapping
+*/
+#define K0BASE		0x80000000
+#define K0SIZE		0x20000000
+#define K1BASE		0xa0000000
+#define K1SIZE		0x20000000
+#define K2BASE		0xc0000000
+#if defined(CPU_R32364)
+#define K2SIZE		0x40000000
+#define ICEBASE		0xff000000
+#define ICESIZE		0x01000000
+#elif defined(CPU_R32434)
+#define K2SIZE		0x20000000
+#define K3BASE		0xe0000000
+#define K3SIZE    0x20000000
+#define ICEBASE		0xff200000
+#define ICESIZE		0x00200000
+#endif
+
+#define KUBASE		0
+#define KUSIZE		0x80000000
+
+/*
+** Exception Vectors
+*/
+
+#define	T_VEC	(K0BASE + 0x000)			/* tlbmiss vector */
+#define X_VEC	(K0BASE + 0x080)			/* xtlbmiss vector */
+#define C_VEC	(K1BASE + 0x100)			/* cache error vector */
+#define E_VEC	(K0BASE + 0x180)			/* exception vector */
+#define I_VEC	(K0BASE + 0X200)			/* interrupt vector */
+#define	R_VEC	(K1BASE + 0x1fc00000)	/* reset vector */
+
+/*
+** Address conversion macros
+*/
+#ifdef CLANGUAGE
+#define	CAST(as) (as)
+#else
+#define	CAST(as)
+#endif
+
+#define	K0_TO_K1(x)		(CAST(unsigned)(x) | 0xA0000000)	/* kseg0 to kseg1 */
+#define	K1_TO_K0(x)		(CAST(unsigned)(x) & 0x9FFFFFFF)	/* kseg1 to kseg0 */
+#define	K0_TO_PHYS(x)	(CAST(unsigned)(x) & 0x1FFFFFFF)	/* kseg0 to physical */
+#define	K1_TO_PHYS(x)	(CAST(unsigned)(x) & 0x1FFFFFFF)	/* kseg1 to physical */
+#define	PHYS_TO_K0(x)	(CAST(unsigned)(x) | 0x80000000)	/* physical to kseg0 */
+#define	PHYS_TO_K1(x)	(CAST(unsigned)(x) | 0xA0000000)	/* physical to kseg1 */
+
+#if defined(CPU_R32364)             /* Includes RC32332, RC32334 */
+#define	CFG_ICE					0x80000000	/* ICE detect */
+#define	CFG_ECMASK			0x70000000	/* System Clock Ratio */
+#define	CFG_ECBY2				0x00000000 	/* divide by 2 */
+#define	CFG_ECBY3				0x10000000 	/* divide by 3 */
+#define	CFG_ECBY4				0x20000000 	/* divide by 4 */
+#define	CFG_BE					0x00008000	/* Big Endian */
+#define	CFG_ICMASK			0x00000e00	/* Instruction cache size */
+#define	CFG_ICSHIFT			9
+#define	CFG_DCMASK			0x000001c0	/* Data cache size */
+#define	CFG_DCSHIFT			6
+#define	CFG_IB					0x00000020	/* Instruction cache line size */
+#define	CFG_DB					0x00000010	/* Data cache line size */
+#define	CFG_K0MASK			0x00000007	/* KSEG0 coherency algorithm */
+#elif defined(CPU_R32434)
+#define	CFG_MM					0x00060000  /* write buffer Merge Mode */
+#define CFG_BM					0x00010000  /* Burst Mode */
+#define	CFG_BE					0x00008000	/* Big Endian */
+#define	CFG_K0MASK			0x00000007	/* KSEG0 coherency algorithm */
+#endif
+
+/*
+ * Primary cache mode
+ */
+#if defined(CPU_R32364)
+#define CFG_C_NCHRNT_WT_NWA			0
+#define CFG_C_NCHRNT_WT					1
+#define CFG_C_UNCACHED					2
+#define CFG_C_NCHRNT_WB					3
+
+/* Cache Operations */
+#define Index_Invalidate_I      0x0         /* 0       0 */
+#define Index_Writeback_Inv_D   0x1         /* 0       1 */
+#define Index_Invalidate_SI     0x2         /* 0       2 */
+#define Index_Writeback_Inv_SD  0x3         /* 0       3 */
+#define Index_Load_Tag_I        0x4         /* 1       0 */
+#define Index_Load_Tag_D        0x5         /* 1       1 */
+#define Index_Load_Tag_SI       0x6         /* 1       2 */
+#define Index_Load_Tag_SD       0x7         /* 1       3 */
+#define Index_Store_Tag_I       0x8         /* 2       0 */
+#define Index_Store_Tag_D       0x9         /* 2       1 */
+#define Index_Store_Tag_SI      0xA         /* 2       2 */
+#define Index_Store_Tag_SD      0xB         /* 2       3 */
+#define Create_Dirty_Exc_D      0xD         /* 3       1 */
+#define Create_Dirty_Exc_SD     0xF         /* 3       3 */
+#define Hit_Invalidate_I        0x10        /* 4       0 */
+#define Hit_Invalidate_D        0x11        /* 4       1 */
+#define Hit_Invalidate_SI       0x12        /* 4       2 */
+#define Hit_Invalidate_SD       0x13        /* 4       3 */
+#define Hit_Writeback_Inv_D     0x15        /* 5       1 */
+#define Hit_Writeback_Inv_SD    0x17        /* 5       3 */
+#define Fill_I                  0x14        /* 5       0 */
+#define Hit_Writeback_D         0x19        /* 6       1 */
+#define Hit_Writeback_SD        0x1B        /* 6       3 */
+#define Hit_Writeback_I         0x18        /* 6       0 */
+#define Hit_Set_Virtual_SI      0x1E        /* 7       2 */
+#define Hit_Set_Virtual_SD      0x1F        /* 7       3 */
+#define CFG_EW32        				0x00040000      /* 32 bit */
+#elif defined(CPU_R32434)
+#define CFG_C_UNCACHED					2
+#define CFG_C_NCHRNT_WB					3
+
+/* Cache Operations */
+#define Index_Invalidate_I      0x0         /* 0       0 */
+#define Index_Invalidate_D      0x1         /* 0       0 */
+#define Index_Load_Tag_I        0x4         /* 1       0 */
+#define Index_Load_Tag_D        0x5         /* 1       1 */
+#define Index_Store_Tag_I       0x8         /* 2       0 */
+#define Index_Store_Tag_D       0x9         /* 2       1 */
+#define Hit_Invalidate_I        0x10        /* 4       0 */
+#define Hit_Invalidate_D        0x11        /* 4       1 */
+#define Fill_I                  0x14        /* 5       0 */
+#define Fetch_Lock_I						0x1C        /* 7       0 */
+#define Fetch_Lock_D						0x1D        /* 7       1 */
+#define CFG_EW32        				0x00040000      /* 32 bit */
+#endif
+
+/*
+** TLB resource defines
+*/
+
+#define	N_TLB_ENTRIES				16
+#define	TLBHI_VPN2MASK			0xffffe000
+#define	TLBHI_PIDMASK				0x000000ff
+#define	TLBHI_NPID					256
+
+#define	TLBLO_PFNMASK				0x03ffffc0
+#define	TLBLO_PFNSHIFT			6
+#define	TLBLO_D							0x00000004	/* writeable */
+#define	TLBLO_V							0x00000002	/* valid bit */
+#define	TLBLO_G							0x00000001	/* global access bit */
+#define	TLBLO_CMASK					0x00000038	/* cache algorithm mask */
+#define	TLBLO_CSHIFT				3
+
+#define	TLBLO_UNCACHED			(CFG_C_UNCACHED << TLBLO_CSHIFT)
+#define	TLBLO_NCHRNT_WT_NWA	(CFG_C_NCHRNT_WT_NWA << TLBLO_CSHIFT)
+#if defined(CPU_R32364)
+#define	TLBLO_NCHRNT_WT			(CFG_C_NCHRNT_WT << TLBLO_CSHIFT)
+#define	TLBLO_NCHRNT_WB			(CFG_C_NCHRNT_WB << TLBLO_CSHIFT)
+#endif
+
+#define	TLBINX_PROBE				0x80000000
+#define	TLBINX_INXMASK			0x0000003f
+
+#define	TLBRAND_RANDMASK		0x0000003f
+
+#define	TLBCTXT_BASEMASK		0xff800000
+#define	TLBCTXT_BASESHIFT		23
+
+#define	TLBCTXT_VPN2MASK		0x007ffff0
+#define	TLBCTXT_VPN2SHIFT		4
+
+#define	TLBPGMASK_MASK			0x01ffe000
+
+#define	SR_CUMASK				0xf0000000	/* coproc usable bits */
+#define	SR_CU3					0x80000000	/* Coprocessor 3 usable */
+#define	SR_CU2					0x40000000	/* Coprocessor 2 usable */
+#define	SR_CU1					0x20000000	/* Coprocessor 1 usable */
+#define	SR_CU0					0x10000000	/* Coprocessor 0 usable */
+
+/* #define	SR_PE						0x00100000*/  /* cache parity error */
+
+#if defined(CPU_R32364)
+#define	SR_RE						0X02000000	/* Reverse Endianness */
+#define	SR_DL						0x01000000	/* Data Cache Locking */
+#define	SR_IL						0x00800000	/* Instruction Cache Locking */
+
+#define	SR_BEV					0x00400000	/* Use boot exception vectors */
+#define	SR_SR						0x00100000	/* Soft reset */
+#define	SR_CH						0x00040000	/* Cache hit */
+#define	SR_CE						0x00020000	/* Use cache ECC  */
+#define	SR_DE						0x00010000	/* Disable cache exceptions */
+
+#elif defined(CPU_R32434)
+#define	SR_RP						0X08000000	/* Reduced Power mode */
+
+#define	SR_RE						0X02000000	/* Reverse Endianness */
+
+#define	SR_BEV					0x00400000	/* Use boot exception vectors */
+#define	SR_TS						0X00200000	/* TLB Shutdown */
+#define	SR_SR						0x00100000	/* Soft reset */
+#define	SR_NMI					0X00080000	/* NMI */
+#endif
+/*
+**	status register interrupt masks and bits
+*/
+
+#define	SR_IMASK				0x0000ff00	/* Interrupt mask */
+#define	SR_IMASK8				0x00000000	/* mask level 8 */
+#define	SR_IMASK7				0x00008000	/* mask level 7 */
+#define	SR_IMASK6				0x0000c000	/* mask level 6 */
+#define	SR_IMASK5				0x0000e000	/* mask level 5 */
+#define	SR_IMASK4				0x0000f000	/* mask level 4 */
+#define	SR_IMASK3				0x0000f800	/* mask level 3 */
+#define	SR_IMASK2				0x0000fc00	/* mask level 2 */
+#define	SR_IMASK1				0x0000fe00	/* mask level 1 */
+#define	SR_IMASK0				0x0000ff00	/* mask level 0 */
+
+#define	SR_IMASKSHIFT		8
+
+#define	SR_IBIT8				0x00008000	/* bit level 8 */
+#define	SR_IBIT7				0x00004000	/* bit level 7 */
+#define	SR_IBIT6				0x00002000	/* bit level 6 */
+#define	SR_IBIT5				0x00001000	/* bit level 5 */
+#define	SR_IBIT4				0x00000800	/* bit level 4 */
+#define	SR_IBIT3				0x00000400	/* bit level 3 */
+#define	SR_IBIT2				0x00000200	/* bit level 2 */
+#define	SR_IBIT1				0x00000100	/* bit level 1 */
+
+#define	SR_KSMASK				0x00000016	/* Kernel mode mask */
+#define	SR_KSUSER				0x00000000	/* User Mode */
+#define	SR_KSKERNEL			0x00000016	/* Kernel Mode */
+
+#define	SR_ERL					0x00000004	/* Error level */
+#define	SR_EXL					0x00000002	/* Exception level */
+#define	SR_IE						0x00000001	/* Interrupts enabled */
+#define	NOT_SR_IEC      0xfffffffe  /* assembler problem with li ~SR_IEC */
+
+/*
+ * Cause Register
+ */
+#define	CAUSE_BD				0x80000000	/* Branch delay slot */
+#define	CAUSE_CEMASK		0x30000000	/* coprocessor error */
+#define	CAUSE_CESHIFT		28
+#if defined(CPU_R32364)
+#define	CAUSE_IPE				0x04000000	/* Imprecise exception */
+#define	CAUSE_DW				0x02000000	/* Data watch */
+#define	CAUSE_IW				0x01000000	/* Instruction watch */
+#elif defined(CPU_R32434)
+#define CAUSE_IV			 	0x00800000	/* Interrupt Vector location */
+#define CAUSE_WP			 	0x00400000	/* Watch Exception deferred */
+#endif
+
+#define	CAUSE_IPMASK		0x0000FF00	/* Pending interrupt mask */
+#define	CAUSE_IPSHIFT		8
+
+/* Notice: Watch Exception if Exc. Code is 23 is not included in the mask
+ *	   for R32364.
+ */
+#define	CAUSE_EXCMASK		0x0000003C	/* Cause code bits */
+#define	CAUSE_EXCSHIFT	2
+
+#ifndef XDS
+/*
+**  Coprocessor 0 registers
+*/
+#define	C0_INX					$0		/* tlb index */
+#define	C0_RANDOM				$1
+#define	C0_TLBLO0				$2		/* tlb entry low 0 */
+#define	C0_TLBLO1				$3		/* tlb entry low 1 */
+#define	C0_CTXT					$4		/* tlb context */
+#define	C0_PAGEMASK			$5		/* tlb page mask */
+#define	C0_WIRED				$6		/* number of wired tlb entries */
+
+#define	C0_BADVADDR			$8		/* bad virtual address */
+#define	C0_COUNT				$9		/* timer count */
+#define	C0_TLBHI				$10		/* tlb entry hi */
+#define	C0_COMPARE			$11		/* timer comparator  */
+#define	C0_SR						$12		/* status register */
+#define	C0_CAUSE				$13		/* exception cause */
+#define	C0_EPC					$14		/* exception pc */
+#define	C0_PRID					$15		/* revision identifier */
+#define	C0_CONFIG				$16		/* configuration register */
+
+#if defined(CPU_R32364)
+#define	C0_IWATCH				$18		/* Instr brk pt Virtual add. */
+#define	C0_DWATCH				$19		/* Data brk pt Virtual add. */
+
+#define	C0_IEPC					$22		/* Imprecise Exception pc */
+#define	C0_DEPC					$23		/* Debug Exception pc */
+#define	C0_DEBUG				$24		/* Debug control/status reg */
+
+#define	C0_ECC					$26		/* primary cache Parity control */
+#define	C0_CACHEERR			$27		/* cache error status */
+#define	C0_TAGLO				$28		/* cache tag lo */
+#define	C0_ERRPC				$30		/* cache error pc */
+#elif defined(CPU_R32434)
+#define	C0_WATCHLO			$18		/* Watchpoint address (low) */
+#define	C0_WATCHHI			$19		/* Watchpoint address (high) */
+
+#define	C0_DEBUG				$23		/* Debug control/status reg */
+#define	C0_DEPC					$24		/* Debug Exception pc */
+
+#define	C0_ERRCTL				$26		/* Cache Error Control */
+#define	C0_TAGLO				$28		/* Cache Tag Lo */
+#define	C0_ERRPC				$30		/* Cache Error PC */
+#define C0_DESAVE				$31		/* Debug scratchpad reg. */
+#endif 
+
+#endif XDS
+#endif /* defined(__IDTCPU_H__) */
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/boot/idthdr.h linux/arch/mips/rc32434/79EB434/boot/idthdr.h
--- linux-2.4.30/arch/mips/rc32434/79EB434/boot/idthdr.h	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/boot/idthdr.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,27 @@
+
+/*
+**	debugger macros for assembly language routines. Allows the 
+**	programmer to easily set up the necessary stack frame info
+**	required by debuggers to to stack traces.
+*/
+#ifndef XDS
+
+#define	FRAME(name,frm_reg,offset,ret_reg)	\
+	.globl	name;				\
+	.ent	name;				\
+name:;						\
+	.frame	frm_reg,offset,ret_reg
+
+#define ENDFRAME(name) 	.end name
+
+#else
+
+#define FRAME(name,frm_reg,offset,ret_reg)      \
+name:
+
+#define ENDFRAME(name)
+
+#endif
+
+
+
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/boot/iregdef.h linux/arch/mips/rc32434/79EB434/boot/iregdef.h
--- linux-2.4.30/arch/mips/rc32434/79EB434/boot/iregdef.h	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/boot/iregdef.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,253 @@
+/*
+**	iregdef.h - IDT R3000 register structure header file
+**
+**	Copyright 1989 Integrated Device Technology, Inc
+**	All Rights Reserved
+**
+*/
+
+/*
+**	ireg_desc - describes the fields within a register
+*/
+#ifdef CLANGUAGE
+struct ireg_desc {
+	char 	*ptr_field_name;	/* field name   */
+	short	num_digits;				/* number ofdigits to display */
+	short	num_spaces;				/* number of spaces to follow */
+	reg_t	fld_mask;					/* mask to extract value of field */
+	int	fld_shift;					/* shift amount to position field */
+  short    cpu;
+	char *CONST *ptr_enum_list;	/* ptr to an enumeration list */
+	  };
+
+/*
+** reg_name - structure that gives the reg. name, alt. reg name
+**		the reg index for fetching the value, the number
+**		of spaces req. so a tabular display will align
+**		a pointer to a structure defining the fields if
+**		required and a flag for the output type.
+*/
+struct reg_name {
+	char	*register_name;
+	char	*alt_reg_name;
+	short	reg_index;
+	short	space_pad;
+	CONST struct ireg_desc *ptr_reg_desc_flds;
+	unsigned char format_type;
+	unsigned char print_type;
+	short   reg_group;
+  short    cpu;
+	  };
+
+/* print format specifiers */
+#define PRT_HEX		0
+#define PRT_SGL 	1
+#define PRT_DBL 	2
+
+/* register group classifiers */
+#define GRP_CPU		0x0001
+#define GRP_FPR		0x0002
+#define GRP_FPS		0x0004
+#define GRP_FPD		0x0008
+#define GRP_CP0		0x0010
+#define GRP_CP0R	0x0020
+#endif CLANGUAGE
+
+/*
+** register names
+*/
+#define r0		$0
+#define r1		$1
+#define r2		$2
+#define r3		$3
+#define r4		$4
+#define r5		$5
+#define r6		$6
+#define r7		$7
+#define r8		$8
+#define r9		$9
+#define r10		$10
+#define r11		$11
+#define r12		$12
+#define r13		$13
+#define r14		$14
+#define r15		$15
+#define r16		$16
+#define r17		$17
+#define r18		$18
+#define r19		$19
+#define r20		$20
+#define r21		$21
+#define r22		$22
+#define r23		$23
+#define r24		$24
+#define r25		$25
+#define r26		$26
+#define r27		$27
+#define r28		$28
+#define r29		$29
+#define r30		$30
+#define r31		$31
+
+#define zero	$0		/* wired zero */
+#define AT		$at		/* assembler temp */
+#define v0		$2		/* return value */
+#define v1		$3
+#define a0		$4		/* argument registers a0-a3 */
+#define a1		$5
+#define a2		$6
+#define a3		$7
+#define t0		$8		/* caller saved  t0-t9 */
+#define t1		$9
+#define t2		$10
+#define t3		$11
+#define t4		$12
+#define t5		$13
+#define t6		$14
+#define t7		$15
+#define s0		$16		/* callee saved s0-s8 */
+#define s1		$17
+#define s2		$18
+#define s3		$19
+#define s4		$20
+#define s5		$21
+#define s6		$22
+#define s7		$23
+#define t8		$24
+#define t9		$25
+#define k0		$26		/* kernel usage */
+#define k1		$27		/* kernel usage */
+#define gp		$28		/* sdata pointer */
+#define sp		$29		/* stack pointer */
+#define s8		$30		/* yet another saved reg for the callee */
+#define fp		$30		/* frame pointer - this is being phased out by MIPS */
+#define ra		$31		/* return address */
+
+/*
+** relative position of registers in save reg area
+*/
+#define	R_R0		0
+#define	R_R1		1
+#define	R_R2		2
+#define	R_R3		3
+#define	R_R4		4
+#define	R_R5		5
+#define	R_R6		6
+#define	R_R7		7
+#define	R_R8		8
+#define	R_R9		9
+#define	R_R10		10
+#define	R_R11		11
+#define	R_R12		12
+#define	R_R13		13
+#define	R_R14		14
+#define	R_R15		15
+#define	R_R16		16
+#define	R_R17		17
+#define	R_R18		18
+#define	R_R19		19
+#define	R_R20		20
+#define	R_R21		21
+#define	R_R22		22
+#define	R_R23		23
+#define	R_R24		24
+#define	R_R25		25
+#define	R_R26		26
+#define	R_R27		27
+#define	R_R28		28
+#define	R_R29		29
+#define	R_R30		30
+#define	R_R31		31
+#define NCLIENTREGS	32
+#define	R_EPC				32
+#define	R_MDHI			33
+#define	R_MDLO		  34
+#define	R_SR				35
+#define	R_CAUSE			36
+#define	R_TLBHI			37
+#define	R_TLBLO0		38
+#define	R_BADVADDR	39
+#define	R_INX				40
+#define	R_RAND			41
+#define	R_CTXT			42
+#define	R_EXCTYPE		43
+#define R_MODE			44
+#define R_PRID			45
+#define R_TLBLO1		46
+#define R_PAGEMASK	47
+#define R_WIRED			48
+#define R_COUNT			49
+#define R_COMPARE		50
+#define R_CONFIG		51
+#if defined(CPU_R32434)
+#define R_WATCHLO   52
+#define R_WATCHHI   53
+#elif defined(CPU_R32364)
+#define R_IWATCH    52
+#define R_DWATCH    53
+#define R_ECC				54
+#define R_CACHEERR	55
+#endif
+#define R_TAGLO			56
+#define R_TAGHI			57
+#define R_ERRPC			58
+
+#define NREGS			  59
+
+#if __mips >= 3
+
+#define R_SZ		8
+#ifndef CLANGUAGE
+#define sreg		sd
+#define lreg		ld
+#define rmfc0		mfc0
+#define rmtc0		mtc0
+#endif
+
+#else
+
+#define R_SZ		4
+#ifndef CLANGUAGE
+#define sreg		sw
+#define lreg		lw
+#define rmfc0		mfc0
+#define rmtc0		mtc0
+#endif
+
+#endif
+
+/*
+** For those who like to think in terms of the compiler names for the regs
+*/
+#define	R_ZERO	R_R0
+#define	R_AT		R_R1
+#define	R_V0		R_R2
+#define	R_V1		R_R3
+#define	R_A0		R_R4
+#define	R_A1		R_R5
+#define	R_A2		R_R6
+#define	R_A3		R_R7
+#define	R_T0		R_R8
+#define	R_T1		R_R9
+#define	R_T2		R_R10
+#define	R_T3		R_R11
+#define	R_T4		R_R12
+#define	R_T5		R_R13
+#define	R_T6		R_R14
+#define	R_T7		R_R15
+#define	R_S0		R_R16
+#define	R_S1		R_R17
+#define	R_S2		R_R18
+#define	R_S3		R_R19
+#define	R_S4		R_R20
+#define	R_S5		R_R21
+#define	R_S6		R_R22
+#define	R_S7		R_R23
+#define	R_T8		R_R24
+#define	R_T9		R_R25
+#define	R_K0		R_R26
+#define	R_K1		R_R27
+#define	R_GP		R_R28
+#define	R_SP		R_R29
+#define	R_FP		R_R30
+#define	R_RA		R_R31
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/boot/misc.c linux/arch/mips/rc32434/79EB434/boot/misc.c
--- linux-2.4.30/arch/mips/rc32434/79EB434/boot/misc.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/boot/misc.c	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,339 @@
+/*
+ *
+ * This is a collection of several routines from gzip-1.0.3
+ * adapted for Linux.
+ *
+ * malloc by Hannu Savolainen 1993 and Matthias Urlichs 1994
+ * puts by Nick Holloway 1993, better puts by Martin Mares 1995
+ * High loaded stuff by Hans Lermen & Werner Almesberger, Feb. 1996
+ *
+ * Modified by RidgeRun Inc.
+ *
+ * 08/2002 modified for booting zImage from ROM
+ * Johannes Stezenbach <js@convergence.de>
+ * (All statically initialized data is read only, all data which needs to be
+ * in RAM must not be statically initialized.)
+ *
+ **************************************************************************
+ * Kiran Rao
+ * 
+ * Ported from Acacia
+ **************************************************************************
+ * P. Sadik      Oct 08 2003
+ *
+ * Modified so that, by changing ZDEBUG value, the user can get more detailed
+ * debug messages.
+ **************************************************************************
+ */
+
+#include <linux/types.h>
+
+/*
+ * gzip declarations
+ */
+#define OF(args)  args
+#define STATIC static
+#define memzero(s, n)     memset ((s), 0, (n))
+typedef unsigned char uch;
+typedef unsigned short ush;
+typedef unsigned long ulg;
+#define WSIZE 0x8000		/* Window size must be at least 32k, */
+				/* and a power of two */
+static uch *inbuf;		/* input buffer */
+static uch window[WSIZE];	/* Sliding window buffer */
+
+/* gzip flag byte */
+#define ASCII_FLAG   0x01	/* bit 0 set: file probably ASCII text */
+#define CONTINUATION 0x02	/* bit 1 set: continuation of multi-part gzip file */
+#define EXTRA_FIELD  0x04	/* bit 2 set: extra field present */
+#define ORIG_NAME    0x08	/* bit 3 set: original file name present */
+#define COMMENT      0x10	/* bit 4 set: file comment present */
+#define ENCRYPTED    0x20	/* bit 5 set: file is encrypted */
+#define RESERVED     0xC0	/* bit 6,7:   reserved */
+
+
+static unsigned insize;	/* valid bytes in inbuf */
+static unsigned inptr;	/* index of next byte to be processed in inbuf */
+static unsigned outcnt;	/* bytes in output buffer */
+
+void variable_init(void);
+#if ZDEBUG > 0
+static void puts(const char *);
+extern void putc_init(void);
+extern void putc(unsigned char c);
+#endif
+static int fill_inbuf(void);
+static void flush_window(void);
+static void error(char *m);
+static void gzip_mark(void **);
+static void gzip_release(void **);
+
+extern char input_data[];
+extern int input_len;
+
+
+#if ZDEBUG > 0
+void int2hex(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);
+}
+#endif
+
+static unsigned long byte_count;
+
+int get_byte(void)
+{
+#if ZDEBUG > 1
+	static int printCnt;
+#endif
+	unsigned char c = (inptr < insize ? inbuf[inptr++] : fill_inbuf());
+	byte_count++;
+
+#if ZDEBUG > 1
+	if (printCnt++ < 32)
+	{
+	  puts("byte count = ");
+	  int2hex(byte_count);
+	  puts(" byte val = ");
+	  int2hex(c);
+	  puts("\n");
+	}
+#endif
+	return c;
+}
+
+/* Diagnostic functions */
+#ifdef DEBUG
+#  define Assert(cond,msg) {if(!(cond)) 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
+
+/*
+ * This is set up by the setup-routine at boot-time
+ */
+
+static long bytes_out;
+static uch *output_data;
+static unsigned long output_ptr;
+
+
+static void *malloc(int size);
+static void free(void *where);
+static void error(char *m);
+static void gzip_mark(void **);
+static void gzip_release(void **);
+
+static unsigned long free_mem_ptr;
+static unsigned long free_mem_end_ptr;
+
+#include "../../../../../lib/inflate.c"
+
+static void *malloc(int size)
+{
+	void *p;
+
+	if (size < 0)
+		error("Malloc error\n");
+	if (free_mem_ptr <= 0) error("Memory error\n");
+
+	free_mem_ptr = (free_mem_ptr + 3) & ~3;	/* Align */
+
+	p = (void *) free_mem_ptr;
+	free_mem_ptr += size;
+
+	if (free_mem_ptr >= free_mem_end_ptr)
+		error("\nOut of memory\n");
+
+	return p;
+}
+
+static void free(void *where)
+{				/* Don't care */
+}
+
+static void gzip_mark(void **ptr)
+{
+	*ptr = (void *) free_mem_ptr;
+}
+
+static void gzip_release(void **ptr)
+{
+	free_mem_ptr = (long) *ptr;
+}
+#if ZDEBUG > 0
+static void puts(const char *s)
+{
+	while (*s) {
+		if (*s == 10)
+			putc(13);
+		putc(*s++);
+	}
+}
+#endif
+void *memset(void *s, int c, size_t n)
+{
+	int i;
+	char *ss = (char *) s;
+
+	for (i = 0; i < n; i++)
+		ss[i] = c;
+	return s;
+}
+
+void *memcpy(void *__dest, __const void *__src, size_t __n)
+{
+	int i;
+	char *d = (char *) __dest, *s = (char *) __src;
+
+	for (i = 0; i < __n; i++)
+		d[i] = s[i];
+	return __dest;
+}
+
+/* ===========================================================================
+ * Fill the input buffer. This is called only when the buffer is empty
+ * and at least one byte is really needed.
+ */
+static int fill_inbuf(void)
+{
+	if (insize != 0) {
+		error("ran out of input data\n");
+	}
+
+	inbuf = input_data;
+	insize = input_len;
+	inptr = 1;
+	return inbuf[0];
+}
+
+/* ===========================================================================
+ * Write the output window window[0..outcnt-1] and update crc and bytes_out.
+ * (Used for the decompressed data only.)
+ */
+static void flush_window(void)
+{
+	ulg c = crc;		/* temporary variable */
+	unsigned n;
+	uch *in, *out, ch;
+
+	in = window;
+	out = &output_data[output_ptr];
+	for (n = 0; n < outcnt; n++) {
+		ch = *out++ = *in++;
+		c = crc_32_tab[((int) c ^ ch) & 0xff] ^ (c >> 8);
+	}
+	crc = c;
+	bytes_out += (ulg) outcnt;
+	output_ptr += (ulg) outcnt;
+	outcnt = 0;
+}
+
+#if ZDEBUG > 0
+void check_mem(void)
+{
+	int i;
+
+	puts("\ncplens = ");
+	for (i = 0; i < 10; i++) {
+		int2hex(cplens[i]);
+		puts(" ");
+	}
+	puts("\ncplext = ");
+	for (i = 0; i < 10; i++) {
+		int2hex(cplext[i]);
+		puts(" ");
+	}
+	puts("\nborder = ");
+	for (i = 0; i < 10; i++) {
+		int2hex(border[i]);
+		puts(" ");
+	}
+	puts("\n");
+}
+#endif
+static void error(char *x)
+{
+#if ZDEBUG > 1
+	check_mem();
+	puts("\n\n");
+	puts(x);
+	puts("byte_count = ");
+	int2hex(byte_count);
+	puts("\n");
+	puts("\n\n -- Error. System halted");
+#endif
+	while (1);		/* Halt */
+}
+
+void variable_init(void)
+{
+	byte_count = 0;
+	output_data = (char *) LOADADDR;
+	free_mem_ptr = FREE_RAM;
+	free_mem_end_ptr = END_RAM;
+#if ZDEBUG > 0
+	puts("output_data      0x");
+	int2hex((unsigned long)output_data); puts("\n");
+	puts("free_mem_ptr     0x");
+	int2hex(free_mem_ptr); puts("\n");
+	puts("free_mem_end_ptr 0x");
+	int2hex(free_mem_end_ptr); puts("\n");
+	puts("input_data       0x");
+	int2hex((unsigned long)input_data); puts("\n");
+	puts("input_len        0x");
+	int2hex(input_len); puts("\n");
+#endif
+}
+
+int decompress_kernel(void)
+{
+#if ZDEBUG > 0
+  putc_init();
+#if ZDEBUG > 2
+  check_mem();
+#endif
+#endif
+
+  variable_init();
+
+  makecrc();
+#if ZDEBUG > 0
+  puts("\n");
+  puts("Uncompressing Linux... \n");
+#endif
+  gunzip();		// ...see inflate.c
+#if ZDEBUG > 0
+  puts("Ok, booting the kernel.\n");
+#endif
+
+#if ZDEBUG > 1
+ {
+  unsigned long *p = (unsigned long *)LOADADDR;
+  int2hex(p[0]); puts("\n");
+  int2hex(p[1]); puts("\n");
+  int2hex(p[2]); puts("\n");
+  int2hex(p[3]); puts("\n");
+ }
+#endif
+
+  return 0;
+}
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/boot/s434.h linux/arch/mips/rc32434/79EB434/boot/s434.h
--- linux-2.4.30/arch/mips/rc32434/79EB434/boot/s434.h	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/boot/s434.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,118 @@
+/*
+**  S364.h - This header file contains common definitions
+*/
+
+/*
+*******************************************************************************
+**  Copyright 1998 Integrated Device Technology, Inc.
+**  All Rights Reserved
+*******************************************************************************
+*/
+
+#ifndef __S434__
+#define __S434__
+/******************************** D E F I N E S *******************************/
+
+/*
+** following defines simple and uniform to save and restore context
+** when enrtering and leaving as assemblu language program when memory
+** and registers are both premiunm.
+*/
+#define SAVE_CNTXT  \
+  subu  sp, 64;     \
+  sw    t0, 60(sp); \
+  sw    t1, 56(sp); \
+  sw    t2, 52(sp); \
+  sw    t3, 48(sp); \
+  sw    t4, 44(sp); \
+  sw    t5, 40(sp); \
+  sw    t6, 36(sp); \
+  sw    t7, 32(sp); \
+  sw    t8, 28(sp); \
+  sw    t9, 24(sp); \
+  sw    a0, 20(sp); \
+  sw    a1, 16(sp); \
+  sw    a2, 12(sp); \
+  sw    a3,  8(sp); \
+  sw    ra,  4(sp)
+
+#define RSTR_CNTXT  \
+  lw    t0, 60(sp); \
+  lw    t1, 56(sp); \
+  lw    t2, 52(sp); \
+  lw    t3, 48(sp); \
+  lw    t4, 44(sp); \
+  lw    t5, 40(sp); \
+  lw    t6, 36(sp); \
+  lw    t7, 32(sp); \
+  lw    t8, 28(sp); \
+  lw    t9, 24(sp); \
+  lw    a0, 20(sp); \
+  lw    a1, 16(sp); \
+  lw    a2, 12(sp); \
+  lw    a3,  8(sp); \
+  lw    ra,  4(sp); \
+  add   sp, 64
+
+/*
+** Following define is to specify a maximum value for a software
+** busy wait counter.
+*/
+/*
+#define LP_CNT_100NS  1000      
+#define LP_CNT_3S     1000000   
+*/
+
+/*
+** Following are other common timer definitions.
+*/
+#define DDR_BASE           PHYS_TO_K1(0x18018000)
+#define TIMER_BASE        PHYS_TO_K1(0x18028000)  
+#define WTC_BASE          PHYS_TO_K1(0x18030000)
+#define INTERRUPT_BASE    PHYS_TO_K1(0x18038000)
+#define GPIO_BASE         PHYS_TO_K1(0x18050000)
+
+#define TIMEOUT_COUNT     0x00000FFF
+#define ENABLE_TIMER      0x1
+#define DISABLE_TIMER     0x0
+#define BIG_VALUE         0xFFFFFFFF
+
+/*
+** following few lines define a macro DISPLAY
+** which is used to write a set of 4 characters
+** onto the EB434 LED.
+*/
+
+#ifndef LED_BASE
+
+#define LED_BASE    PHYS_TO_K1(0x19000000)
+#define LED_DIGIT0  0x7
+#define LED_DIGIT1  0x6
+#define LED_DIGIT2  0x5
+#define LED_DIGIT3  0x4
+#define LED_CLEAR   0x0
+
+#endif
+
+#define DISPLAY(d0, d1, d2, d3)     \
+        li    t6, LED_BASE                    ;\
+        lb    t7, LED_CLEAR(t6)               ;\
+              nop                             ;\
+        li    t7, (d0) & 0xff                 ;\
+        sb    t7, LED_DIGIT0(t6)              ;\
+        li    t7, (d1) & 0xff                 ;\
+        sb    t7, LED_DIGIT1(t6)              ;\
+        li    t7, (d2) & 0xff                 ;\
+        sb    t7, LED_DIGIT2(t6)              ;\
+        li    t7, (d3) & 0xff                 ;\
+        sb    t7, LED_DIGIT3(t6)
+
+#define LEDCLEAR()              \
+        li    t6, LED_BASE                    ;\
+        lb    t7, LED_CLEAR(t6)               ;\
+              nop
+
+#define DESTRUCTIVE     1
+#define NONDESTRUCTIVE  0
+
+#endif
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/boot/s434ram.h linux/arch/mips/rc32434/79EB434/boot/s434ram.h
--- linux-2.4.30/arch/mips/rc32434/79EB434/boot/s434ram.h	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/boot/s434ram.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,147 @@
+/*
+**  s434ram.h - this header files contains common definitions used by
+**  Korina SRAM and DDR SDRAM configuration.
+*/
+
+
+/*
+*******************************************************************************
+**  Copyright 1998 Integrated Device Technology, Inc.
+**  All Rights Reserved
+*******************************************************************************
+* Revision history
+*
+*******************************************************************************
+*  K. Rao                  Original form
+*******************************************************************************
+*  P. Sadik         Dec 17,2003
+*
+* Updated with new values.
+*******************************************************************************
+*/
+
+#ifndef __S434RAM__
+#define __S434RAM__
+/******************************** D E F I N E S *******************************/
+
+#define MB32      1
+#define MB64      2
+#define MB128     3
+
+#define DEV_CTL_BASE        PHYS_TO_K1(0x18010000)  /* device controller regs */
+#define DDR_CTL_BASE        PHYS_TO_K1(0x18018010)  /* DDR controller regs */
+
+#define DEV0_BASE           0x1FC00000
+#define DEV0_MASK           0xFFC00000
+#define DEV1_BASE           0x1A000000
+#define DEV1_MASK           0xFE000000
+#define DEV2_BASE           0x19000000
+#define DEV2_MASK           0xFF000000
+#define DEV3_BASE           0x00000000
+#define DEV3_MASK           0x00000000
+
+#if MHZ == 100000000
+#define DEV0_CTRL    0x04108324
+#define DEV0_TC      0x00000000
+#define DEV1_CTRL    0x04108324
+#define DEV1_TC      0x00000000
+#define DEV2_CTRL    0x04108324
+#define DEV2_TC      0x00000000
+#define DEV3_CTRL    0x0FFFFFF4
+#define DEV3_TC      0x00001FFF
+#elif MHZ == 133000000
+#define DEV0_CTRL    0x04108324
+#define DEV0_TC      0x00000000
+#define DEV1_CTRL    0x05208324
+#define DEV1_TC      0x00000000
+#define DEV2_CTRL    0x04108324
+#define DEV2_TC      0x00000000
+#define DEV3_CTRL    0x0FFFFFF4
+#define DEV3_TC      0x00001FFF
+#elif MHZ == 150000000
+#define DEV0_CTRL    0x04108324
+#define DEV0_TC      0x00000000
+#define DEV1_CTRL    0x05208324
+#define DEV1_TC      0x00000000
+#define DEV2_CTRL    0x04108324
+#define DEV2_TC      0x00000000
+#define DEV3_CTRL    0x0FFFFFF4
+#define DEV3_TC      0x00001FFF
+#elif MHZ == 175000000
+#define DEV0_CTRL    0x04108324
+#define DEV0_TC      0x00000000
+#define DEV1_CTRL    0x05208324
+#define DEV1_TC      0x00000000
+#define DEV2_CTRL    0x04108324
+#define DEV2_TC      0x00000000
+#define DEV3_CTRL    0x0FFFFFF4
+#define DEV3_TC      0x00001FFF
+#elif MHZ == 200000000
+#define DEV0_CTRL    0x05208324
+#define DEV0_TC      0x00000000
+#define DEV1_CTRL    0x06308324
+#define DEV1_TC      0x00000000
+#define DEV2_CTRL    0x05208324
+#define DEV2_TC      0x00000000
+#define DEV3_CTRL    0x0FFFFFF4
+#define DEV3_TC      0x00001FFF
+#endif
+
+#define DATA_PATTERN        0xA5A5A5A5
+#define RCOUNT              PHYS_TO_K1(0x18028030)
+
+#if DRAMSZ == MB32
+
+#define DDR_BASE_VAL          0x00000000
+#define DDR_MASK_VAL          0xFE000000
+#define DDR_ABASE_VAL         0x08000000
+#define DDR_AMASK_VAL         0x00000000
+
+#if MHZ == 100000000
+#define DDRC_VAL_NORMAL       0x82184800
+#define DDRC_VAL_AT_INIT      0x02184800
+#define DDR_REF_CMP_VAL       0x0000030C
+#elif MHZ == 133000000
+#define DDRC_VAL_NORMAL       0x82984800
+#define DDRC_VAL_AT_INIT      0x02984800
+#define DDR_REF_CMP_VAL       0x0000040E
+#elif MHZ == 150000000
+#define DDRC_VAL_NORMAL       0x82984800
+#define DDRC_VAL_AT_INIT      0x02984800
+#define DDR_REF_CMP_VAL       0x00000492
+#elif MHZ == 175000000
+#define DDRC_VAL_NORMAL       0x82994800
+#define DDRC_VAL_AT_INIT      0x02994800
+#define DDR_REF_CMP_VAL       0x00000516
+#elif MHZ == 200000000
+#define DDRC_VAL_NORMAL       0x832B4800
+#define DDRC_VAL_AT_INIT      0x032B4800
+#define DDR_REF_CMP_VAL       0x00000618
+#else
+#warning illegal value for MHZ
+#endif
+
+#define DDR_REF_CMP_FAST      0x00000080
+
+#define DDR_CUST_NOP          0x0000003F
+#define DDR_CUST_PRECHARGE    0x00000033
+#define DDR_CUST_REFRESH      0x00000027
+#define DDR_LD_MODE_REG       0x00000023
+#define DDR_LD_EMODE_REG      0x00000063
+
+/* 
+ * All generated addresses for DDR init during custom transactions are shifted
+ * by two address lines - see spec for used DDR chip
+ */
+#define DDR_PRECHARGE_OFFSET  0x00001000  /* 0x0400 - 9-bit page*/
+#define DDR_EMODE_VAL         0x00000000  /* 0x0000 */
+#define DDR_DLL_RES_MODE_VAL  0x00000584  /* 0x0161 - Reset DLL, CL2.5 */
+#define DDR_DLL_MODE_VAL      0x00000184  /* 0x0061 - CL2.5 */
+
+#define DELAY_200USEC         50000       /* not exactly */
+
+#else
+#error "unrecognized dram size"
+#endif
+
+#endif
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/boot/uart16550.c linux/arch/mips/rc32434/79EB434/boot/uart16550.c
--- linux-2.4.30/arch/mips/rc32434/79EB434/boot/uart16550.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/boot/uart16550.c	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,155 @@
+/*
+ ******************************************************************
+ * 16550 uart routines.
+ * based on something similar to arch/mips/vr4181/osprey/dbg_io.c
+ * Copyright (C) 2001 MontaVista Software Inc.
+ * Author: jsun@mvista.com or jsun@junsun.net
+ ******************************************************************
+ * P. Sadik Oct 20, 2003
+ *
+ * MAX_BUAD is derived from CONFIG_IDT_BOARD_FREQ that is set duing
+ * kernel configuration.
+ ******************************************************************
+ */
+#include <linux/config.h>
+#include <asm/io.h>
+
+#include <asm/rc32434/rc32434.h>
+
+#define BASE			KSEG1ADDR(RC32434_UART_BASE)
+#define MAX_BAUD		(CONFIG_IDT_BOARD_FREQ / 16)
+#define REG_OFFSET		0x4
+
+/* === CONFIG === */
+
+/*
+ * #define BASE			0xb2001000
+ * #define MAX_BAUD		1152000
+ * #define REG_OFFSET		0x10
+ */
+#if (!defined(BASE) || !defined(MAX_BAUD) || !defined(REG_OFFSET))
+#error You must define BASE, MAX_BAUD and REG_OFFSET in the Makefile.
+#endif
+
+#ifndef INIT_SERIAL_PORT
+#define INIT_SERIAL_PORT	1
+#endif
+
+#ifndef DEFAULT_BAUD
+//#define DEFAULT_BAUD		UART16550_BAUD_115200
+#define DEFAULT_BAUD		UART16550_BAUD_9600
+#endif
+#ifndef DEFAULT_PARITY
+#define DEFAULT_PARITY		UART16550_PARITY_NONE
+#endif
+#ifndef DEFAULT_DATA
+#define DEFAULT_DATA		UART16550_DATA_8BIT
+#endif
+#ifndef DEFAULT_STOP
+#define DEFAULT_STOP		UART16550_STOP_1BIT
+#endif
+
+/* === END OF CONFIG === */
+
+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
+
+/* register offset */
+#define		OFS_RCV_BUFFER		(0*REG_OFFSET)
+#define		OFS_TRANS_HOLD		(0*REG_OFFSET)
+#define		OFS_SEND_BUFFER		(0*REG_OFFSET)
+#define		OFS_INTR_ENABLE		(1*REG_OFFSET)
+#define		OFS_INTR_ID		(2*REG_OFFSET)
+#define		OFS_DATA_FORMAT		(3*REG_OFFSET)
+#define		OFS_LINE_CONTROL	(3*REG_OFFSET)
+#define		OFS_MODEM_CONTROL	(4*REG_OFFSET)
+#define		OFS_RS232_OUTPUT	(4*REG_OFFSET)
+#define		OFS_LINE_STATUS		(5*REG_OFFSET)
+#define		OFS_MODEM_STATUS	(6*REG_OFFSET)
+#define		OFS_RS232_INPUT		(6*REG_OFFSET)
+#define		OFS_SCRATCH_PAD		(7*REG_OFFSET)
+
+#define		OFS_DIVISOR_LSB		(0*REG_OFFSET)
+#define		OFS_DIVISOR_MSB		(1*REG_OFFSET)
+
+#define		UART16550_READ(y)    (*((volatile uint8*)(BASE + y)))
+#define		UART16550_WRITE(y, z)  ((*((volatile uint8*)(BASE + y))) = z)
+
+static void Uart16550Init(uint32 baud, uint8 data, uint8 parity, uint8 stop)
+{
+	/* disable interrupts */
+	UART16550_WRITE(OFS_LINE_CONTROL, 0x0);
+	UART16550_WRITE(OFS_INTR_ENABLE, 0);
+
+	/* set up baud rate */
+	{
+		uint32 divisor;
+
+		/* set DIAB bit */
+		UART16550_WRITE(OFS_LINE_CONTROL, 0x80);
+
+		/* set divisor */
+		divisor = MAX_BAUD / baud;
+		UART16550_WRITE(OFS_DIVISOR_LSB, divisor & 0xff);
+		UART16550_WRITE(OFS_DIVISOR_MSB, (divisor & 0xff00)>>8);
+
+		/* clear DIAB bit */
+		UART16550_WRITE(OFS_LINE_CONTROL, 0x0);
+	}
+
+	/* set data format */
+	UART16550_WRITE(OFS_DATA_FORMAT, data | parity | stop);
+}
+
+
+void
+putc_init(void)
+{
+#if INIT_SERIAL_PORT
+	Uart16550Init(DEFAULT_BAUD, DEFAULT_DATA, DEFAULT_PARITY, DEFAULT_STOP);
+#endif
+}
+
+void
+putc(unsigned char c)
+{
+	while ((UART16550_READ(OFS_LINE_STATUS) &0x20) == 0);
+	UART16550_WRITE(OFS_SEND_BUFFER, c);
+}
+
+#if 0
+unsigned char
+getc(void)
+{
+	while((UART16550_READ(OFS_LINE_STATUS) & 0x1) == 0);
+	return UART16550_READ(OFS_RCV_BUFFER);
+}
+
+int
+tstc(void)
+{
+	return((UART16550_READ(OFS_LINE_STATUS) & 0x01) != 0);
+}
+#endif
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/idtIRQ.S linux/arch/mips/rc32434/79EB434/idtIRQ.S
--- linux-2.4.30/arch/mips/rc32434/79EB434/idtIRQ.S	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/idtIRQ.S	Thu Sep  9 10:48:16 2004
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: stevel@mvista.com
+ *
+ * Interrupt dispatcher for IDT 79S334/79EB355 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.
+ */
+
+#define __ASSEMBLY__ 1
+
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+	.text
+	.set	noreorder
+	.set	noat
+	.align	5
+	NESTED(idtIRQ, PT_SIZE, sp)
+	SAVE_ALL
+	CLI
+
+	.set	at
+	.set	noreorder
+
+	/* Get the pending interrupts */
+	mfc0    t0, CP0_CAUSE
+	nop
+			 
+	/* Isolate the allowed ones by anding the irq mask */
+	mfc0    t2, CP0_STATUS
+	move	a1, sp		/* need a nop here, hence we anticipate */
+	andi    t0, CAUSEF_IP
+	and     t0, t2
+								  
+	/* check for r4k counter/timer IRQ. */
+	
+	andi    t1, t0, CAUSEF_IP7
+	beqz    t1, 1f
+	nop
+
+	jal     ll_timer_interrupt	 
+
+	li	a0, 7
+
+	j	ret_from_irq
+	nop
+1:
+	jal	rc32434_irqdispatch
+	move	a0, t0
+	j	ret_from_irq
+	nop
+
+	END(idtIRQ)
+
+
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/irq.c linux/arch/mips/rc32434/79EB434/irq.c
--- linux-2.4.30/arch/mips/rc32434/79EB434/irq.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/irq.c	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,394 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *	RC32434 interrupt routines.
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *		stevel@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/errno.h>
+#include <linux/init.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/rc32434/rc32434.h>
+#include <asm/rc32434/gpio.h>
+
+#include <asm/irq.h>
+
+
+
+
+
+
+
+
+#define read_32bit_cp0_register(source)                         \
+({ int __res;                                                   \
+        __asm__ __volatile__(                                   \
+	".set\tpush\n\t"					\
+	".set\treorder\n\t"					\
+        "mfc0\t%0,"STR(source)"\n\t"                            \
+	".set\tpop"						\
+        : "=r" (__res));                                        \
+        __res;})
+
+#define write_32bit_cp0_register(register,value)                \
+        __asm__ __volatile__(                                   \
+        "mtc0\t%0,"STR(register)"\n\t"				\
+	"nop"							\
+        : : "r" (value));
+
+/*
+ * Manipulate the status register.
+ * Mostly used to access the interrupt bits.
+ */
+#define __BUILD_SET_CP0(name,register)				\
+static inline unsigned int					\
+set_cp0_##name(unsigned int set)				\
+{								\
+	unsigned int res;					\
+								\
+	res = read_32bit_cp0_register(register);		\
+	res |= set;						\
+	write_32bit_cp0_register(register, res);		\
+								\
+	return res;						\
+}								\
+								\
+static inline unsigned int					\
+clear_cp0_##name(unsigned int clear)				\
+{								\
+	unsigned int res;					\
+								\
+	res = read_32bit_cp0_register(register);		\
+	res &= ~clear;						\
+	write_32bit_cp0_register(register, res);		\
+								\
+	return res;						\
+}								\
+								\
+static inline unsigned int					\
+change_cp0_##name(unsigned int change, unsigned int new)	\
+{								\
+	unsigned int res;					\
+								\
+	res = read_32bit_cp0_register(register);		\
+	res &= ~change;						\
+	res |= (new & change);					\
+	write_32bit_cp0_register(register, res);		\
+								\
+	return res;						\
+}
+
+__BUILD_SET_CP0(status,CP0_STATUS)
+__BUILD_SET_CP0(cause,CP0_CAUSE)
+__BUILD_SET_CP0(config,CP0_CONFIG)
+
+
+
+
+
+
+
+
+
+#define DEBUG_IRQ
+#ifdef DEBUG_IRQ
+/* note: prints function name for you */
+#define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#ifdef CONFIG_REMOTE_DEBUG
+extern void breakpoint(void);
+#endif
+
+extern asmlinkage void idtIRQ(void);
+extern void set_debug_traps(void);
+extern irq_cpustat_t irq_stat [NR_CPUS];
+unsigned int local_bh_count[NR_CPUS];
+unsigned int local_irq_count[NR_CPUS];
+
+static unsigned int startup_irq(unsigned int irq);
+static void end_irq(unsigned int irq_nr);
+static void mask_and_ack_irq(unsigned int irq_nr);
+static void rc32434_enable_irq(unsigned int irq_nr);
+static void rc32434_disable_irq(unsigned int irq_nr);
+
+extern unsigned int do_IRQ(int irq, struct pt_regs *regs);
+extern void __init init_generic_irq(void);
+
+#ifdef CONFIG_PM
+extern void counter0_irq(int irq, void *dev_id, struct pt_regs *regs);
+#endif
+
+typedef struct {
+  u32 mask;       /* mask of valid bits in pending/mask registers */
+  volatile u32 *base_addr;
+} intr_group_t;
+
+#define RC32434_NR_IRQS  (GROUP4_IRQ_BASE + 32)
+
+
+static const intr_group_t intr_group[NUM_INTR_GROUPS] = {
+  { 0x0000efff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 0 * IC_GROUP_OFFSET) },
+  { 0x00001fff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 1 * IC_GROUP_OFFSET) },
+  { 0x00000007, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 2 * IC_GROUP_OFFSET) },
+  { 0x0003ffff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 3 * IC_GROUP_OFFSET) },
+  { 0xffffffff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 4 * IC_GROUP_OFFSET) }
+};
+
+#define READ_PEND(base) (*(base))
+#define READ_MASK(base) (*(base + 2))
+#define WRITE_MASK(base, val) (*(base + 2) = (val))
+
+static inline int irq_to_group(unsigned int irq_nr)
+{
+  return ((irq_nr - GROUP0_IRQ_BASE) >> 5);
+}
+
+static inline int group_to_ip(unsigned int group)
+{
+  return group + 2;
+}
+
+static inline void enable_local_irq(unsigned int ip)
+{
+  int ipnum = 0x100 << ip;
+  clear_cp0_cause(ipnum);
+  set_cp0_status(ipnum);
+}
+
+static inline void disable_local_irq(unsigned int ip)
+{
+  int ipnum = 0x100 << ip;
+  clear_cp0_status(ipnum);
+}
+
+static inline void ack_local_irq(unsigned int ip)
+{
+  int ipnum = 0x100 << ip;
+  clear_cp0_cause(ipnum);
+}
+
+static void rc32434_enable_irq(unsigned int irq_nr)
+{
+  unsigned long flags;
+  int           ip = irq_nr - GROUP0_IRQ_BASE;
+  unsigned int  group, intr_bit;
+  volatile unsigned int  *addr;
+
+  save_and_cli(flags);
+  
+  if (ip < 0)
+    enable_local_irq(irq_nr);
+  else {
+    // calculate group
+    group = ip >> 5;
+    
+    // calc interrupt bit within group
+    ip -= (group << 5);
+    intr_bit = 1 << ip;
+    //  if (!intr_bit)
+    //    return;
+    
+    // first enable the IP mapped to this IRQ
+    enable_local_irq(group_to_ip(group));
+    
+    addr = intr_group[group].base_addr;
+    // unmask intr within group
+    WRITE_MASK(addr, READ_MASK(addr) & ~intr_bit);
+  }
+
+  restore_flags(flags);
+}
+
+static void rc32434_disable_irq(unsigned int irq_nr)
+{
+  unsigned long flags;
+  int           ip = irq_nr - GROUP0_IRQ_BASE;
+  unsigned int  group, intr_bit, mask;
+  volatile unsigned int  *addr;
+
+  save_and_cli(flags);
+  
+  if (ip < 0)
+    disable_local_irq(irq_nr);
+  else {
+    // calculate group
+    group = ip >> 5;
+
+    // calc interrupt bit within group
+    ip -= group << 5;
+    intr_bit = 1 << ip;
+    //    if (!intr_bit)
+    //      return;
+    
+    addr = intr_group[group].base_addr;
+    // mask intr within group
+    mask = READ_MASK(addr);
+    mask |= intr_bit;
+    WRITE_MASK(addr, mask);
+    
+    /*
+      if there are no more interrupts enabled in this
+      group, disable corresponding IP
+    */
+    if (mask == intr_group[group].mask)
+      disable_local_irq(group_to_ip(group));
+  }
+  
+  restore_flags(flags);
+}
+
+static unsigned int startup_irq(unsigned int irq_nr)
+{
+  rc32434_enable_irq(irq_nr);
+  return 0; 
+}
+
+static void shutdown_irq(unsigned int irq_nr)
+{
+  rc32434_disable_irq(irq_nr);
+  return;
+}
+
+static void mask_and_ack_irq(unsigned int irq_nr)
+{
+  rc32434_disable_irq(irq_nr);
+  ack_local_irq(group_to_ip(irq_to_group(irq_nr)));
+}
+
+static void end_irq(unsigned int irq_nr)
+{
+
+  int ip = irq_nr - GROUP0_IRQ_BASE;
+  unsigned int intr_bit, group;
+  volatile unsigned int *addr;
+
+  if (!(irq_desc[irq_nr].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
+    if (ip < 0)
+      enable_local_irq(irq_nr);
+    else {
+      if (irq_nr == GROUP4_IRQ_BASE + 11)
+	gpio->gpioistat = 0xfffff7ff;
+      
+      group = ip >> 5;
+
+      // calc interrupt bit within group
+      ip -= (group << 5);
+      intr_bit = 1 << ip;
+      //  if (!intr_bit)
+      //    return;
+
+      // first enable the IP mapped to this IRQ
+      enable_local_irq(group_to_ip(group));
+  
+      addr = intr_group[group].base_addr;
+      // unmask intr within group
+      WRITE_MASK(addr, READ_MASK(addr) & ~intr_bit);
+    }
+  } 
+  else {
+    printk("warning: end_irq %d did not enable (%x)\n", 
+	   irq_nr, irq_desc[irq_nr].status);
+  }
+}
+
+static struct hw_interrupt_type rc32434_irq_type = {
+  "RC32434",
+  startup_irq,
+  shutdown_irq,
+  rc32434_enable_irq,
+  rc32434_disable_irq,
+  mask_and_ack_irq,
+  end_irq,
+  NULL
+};
+
+
+void __init init_IRQ(void)
+{
+	int i;
+	unsigned long cp0_status;
+
+	printk("Initializing IRQ's: %d out of %d\n",
+	       RC32434_NR_IRQS, NR_IRQS);  
+	cp0_status = read_32bit_cp0_register(CP0_STATUS);
+	memset(irq_desc, 0, sizeof(irq_desc));
+	set_except_vector(0, idtIRQ);
+  
+	//  init_generic_irq();
+  
+	for (i = 0; i < RC32434_NR_IRQS; i++) {
+		irq_desc[i].status = IRQ_DISABLED;
+		irq_desc[i].action = NULL;
+		irq_desc[i].depth = 1;
+		irq_desc[i].handler = &rc32434_irq_type;
+	}
+  
+#ifdef CONFIG_REMOTE_DEBUG
+	/* If local serial I/O used for debug port, enter kgdb at once */
+	puts("Waiting for kgdb to connect...");
+	set_debug_traps();
+	breakpoint(); 
+#endif
+}
+
+/* Main Interrupt dispatcher */
+void rc32434_irqdispatch(unsigned long cp0_cause, struct pt_regs *regs)
+{
+  unsigned int ip, pend, group;
+  volatile unsigned int *addr;
+
+  if ((ip = (cp0_cause & 0x7c00))) {
+    group = 21 - rc32434_clz(ip);
+
+    addr = intr_group[group].base_addr;
+
+    pend = READ_PEND(addr);
+    pend &= ~READ_MASK(addr); // only unmasked interrupts
+    //  if (!pend)
+    //    return; // no interrupts pending in this group
+    pend = 39 - rc32434_clz(pend);
+    do_IRQ((group << 5) + pend, regs);
+    return;
+  } 
+}
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/nvram/Makefile linux/arch/mips/rc32434/79EB434/nvram/Makefile
--- linux-2.4.30/arch/mips/rc32434/79EB434/nvram/Makefile	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/nvram/Makefile	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,20 @@
+#
+# Makefile for nvram driver
+#
+######################################################################
+# P. Sadik    Oct 08 2003
+# Original form
+######################################################################
+
+O_TARGET := nvram.o
+
+obj-y   := nvramproc.o nvram434.o
+obj-m   := $(O_TARGET)
+include $(TOPDIR)/Rules.make
+
+
+
+
+
+
+
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/nvram/nvram434.c linux/arch/mips/rc32434/79EB434/nvram/nvram434.c
--- linux-2.4.30/arch/mips/rc32434/79EB434/nvram/nvram434.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/nvram/nvram434.c	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,359 @@
+/*
+ *****************************************************************************
+ * NVRAM API's
+ *
+ *****************************************************************************
+ * P. Sadik    Oct 08,2003
+ *
+ * Ported from IdtBoot
+ ****************************************************************************
+*/
+#include <linux/ctype.h>
+#include "nvram434.h"
+#include "rtc.h"
+#define  NVRAM_BASE RTC_BASE
+
+extern void setenv (char *e, char *v, int rewrite);
+extern char *getenv (char *e);
+extern void unsetenv (char *e);
+extern void mapenv (int (*func)(char *, char *));
+extern char *getenv (char *s);
+extern void purgeenv(void);
+static void nvram_initenv(void);
+
+static unsigned char
+nvram_getbyte(int offs)
+{
+  return(*((unsigned char*)((unsigned int)NVRAM_BASE + offs)));
+}
+
+static void
+nvram_setbyte(int offs, unsigned char val)
+{
+  unsigned char* nvramDataPointer = (unsigned char*)((unsigned int)NVRAM_BASE + offs);
+
+  *nvramDataPointer = val;
+}
+
+/*
+ * BigEndian!
+ */
+static unsigned short
+nvram_getshort(int offs)
+{
+  return((nvram_getbyte(offs) << 8) | nvram_getbyte(offs + 1));
+}
+
+static void
+nvram_setshort(int offs, unsigned short val)
+{
+  nvram_setbyte(offs, (unsigned char)((val >> 8) & 0xff));
+  nvram_setbyte(offs + 1, (unsigned char)(val & 0xff));
+}
+#if 0
+static unsigned int
+nvram_getint(int offs)
+{
+  unsigned int val;
+  val = nvram_getbyte(offs) << 24;
+  val |= nvram_getbyte(offs + 1) << 16;
+  val |= nvram_getbyte(offs + 2) << 8;
+  val |= nvram_getbyte(offs + 3);
+  return(val);
+}
+
+static void
+nvram_setint(int offs, unsigned int val)
+{
+  nvram_setbyte(offs, val >> 24);
+  nvram_setbyte(offs + 1, val >> 16);
+  nvram_setbyte(offs + 2, val >> 8);
+  nvram_setbyte(offs + 3, val);
+}
+#endif
+/*
+ * calculate NVRAM checksum
+ */
+static unsigned short
+nvram_calcsum(void)
+{
+  unsigned short sum = NV_MAGIC;
+  int     i;
+
+  for (i = ENV_BASE; i < ENV_TOP; i += 2)
+    sum += nvram_getshort(i);
+  return(sum);
+}
+
+/*
+ * update the nvram checksum
+ */
+static void
+nvram_updatesum (void)
+{
+  nvram_setshort(NVOFF_CSUM, nvram_calcsum());
+}
+
+/*
+ * test validity of nvram by checksumming it
+ */
+static int
+nvram_isvalid(void)
+{
+  static  unsigned int is_valid;
+
+  if (is_valid)
+    return(1);
+
+  if (nvram_getshort(NVOFF_MAGIC) != NV_MAGIC)
+    nvram_initenv();
+  is_valid = 1;
+  return(1);
+}
+
+/* return nvram address of environment string */
+static int
+nvram_matchenv(char *s)
+{
+  int envsize, envp, n, i, varsize;
+  char *var;
+
+  envsize = nvram_getshort(NVOFF_ENVSIZE);
+  if (envsize > ENV_AVAIL)
+    return(0);     /* sanity */
+
+  envp = ENV_BASE;
+
+  if ((n = strlen (s)) > 255)
+    return(0);
+
+  while (envsize > 0) {
+    varsize = nvram_getbyte(envp);
+    if (varsize == 0 || (envp + varsize) > ENV_TOP)
+      return(0);   /* sanity */
+    for (i = envp + 1, var = s; i <= envp + n; i++, var++) {
+      char c1 = nvram_getbyte(i);
+      char c2 = *var;
+      if (islower(c1))
+        c1 = toupper(c1);
+      if (islower(c2))
+        c2 = toupper(c2);
+      if (c1 != c2)
+        break;
+    }
+    if (i > envp + n) {       /* match so far */
+      if (n == varsize - 1)   /* match on boolean */
+        return(envp);
+      if (nvram_getbyte(i) == '=')  /* exact match on variable */
+        return(envp);
+    }
+    envsize -= varsize;
+    envp += varsize;
+  }
+  return(0);
+}
+
+static void nvram_initenv(void)
+{
+  nvram_setshort(NVOFF_MAGIC, NV_MAGIC);
+  nvram_setshort(NVOFF_ENVSIZE, 0);
+
+  nvram_updatesum();
+}
+
+static void
+nvram_delenv(char *s)
+{
+  int nenvp, envp, envsize, nbytes;
+
+  envp = nvram_matchenv(s);
+  if (envp == 0)
+    return;
+
+  nenvp = envp + nvram_getbyte(envp);
+  envsize = nvram_getshort(NVOFF_ENVSIZE);
+  nbytes = envsize - (nenvp - ENV_BASE);
+  nvram_setshort(NVOFF_ENVSIZE, envsize - (nenvp - envp));
+  while (nbytes--) {
+    nvram_setbyte(envp, nvram_getbyte(nenvp));
+    envp++;
+    nenvp++;
+  }
+  nvram_updatesum();
+}
+
+static int
+nvram_setenv(char *s, char *v)
+{
+  int ns, nv, total;
+  int envp;
+
+  if (!nvram_isvalid())
+    return(-1);
+
+  nvram_delenv(s);
+  ns = strlen(s);
+  if (ns == 0)
+    return (-1);
+  if (v && *v) {
+    nv = strlen(v);
+    total = ns + nv + 2;
+  }
+  else {
+    nv = 0;
+    total = ns + 1;
+  }
+  if (total > 255 || total > ENV_AVAIL - nvram_getshort(NVOFF_ENVSIZE))
+    return(-1);
+
+  envp = ENV_BASE + nvram_getshort(NVOFF_ENVSIZE);
+
+  nvram_setbyte(envp, (unsigned char) total);
+  envp++;
+
+  while (ns--) {
+    nvram_setbyte(envp, *s);
+    envp++;
+    s++;
+  }
+
+  if (nv) {
+    nvram_setbyte(envp, '=');
+    envp++;
+    while (nv--) {
+      nvram_setbyte(envp, *v);
+      envp++;
+      v++;
+    }
+  }
+  nvram_setshort(NVOFF_ENVSIZE, envp-ENV_BASE);
+  nvram_updatesum();
+  return(0);
+}
+
+static char *
+nvram_getenv(char *s)
+{
+  static char buf[256];   /* FIXME: this cannot be static */
+  int envp, ns, nbytes, i;
+
+  if (!nvram_isvalid())
+    return((char *)0);
+
+  envp = nvram_matchenv(s);
+  if (envp == 0)
+    return((char *)0);
+  ns = strlen(s);
+  if (nvram_getbyte(envp) == ns + 1)  /* boolean */
+    buf[0] = '\0';
+  else {
+    nbytes = nvram_getbyte(envp) - (ns + 2);
+    envp += ns + 2;
+    for (i = 0; i < nbytes; i++)
+      buf[i] = nvram_getbyte(envp++);
+    buf[i] = '\0';
+  }
+  return(buf);
+}
+
+static void
+nvram_unsetenv(char *s)
+{
+  if (!nvram_isvalid())
+    return;
+
+  nvram_delenv(s);
+}
+
+/*
+ * apply func to each string in environment
+ */
+static void
+nvram_mapenv(int (*func)(char *, char *))
+{
+  int envsize, envp, n, i, seeneql;
+  char name[256], value[256];
+  char c, *s;
+
+  if (!nvram_isvalid())
+    return;
+
+  envsize = nvram_getshort(NVOFF_ENVSIZE);
+  envp = ENV_BASE;
+
+  while (envsize > 0) {
+    value[0] = '\0';
+    seeneql = 0;
+    s = name;
+    n = nvram_getbyte(envp);
+    for (i = envp + 1; i < envp + n; i++) {
+      c = nvram_getbyte(i);
+      if ((c == '=') && !seeneql) {
+        *s = '\0';
+        s = value;
+        seeneql = 1;
+        continue;
+      }
+      *s++ = c;
+    }
+    *s = '\0';
+    (*func)(name, value);
+    envsize -= n;
+    envp += n;
+  }
+}
+#if 0
+static unsigned int
+digit(char c)
+{
+  if ('0' <= c && c <= '9')
+    return (c - '0');
+  if ('A' <= c && c <= 'Z')
+    return (10 + c - 'A');
+  if ('a' <= c && c <= 'z')
+    return (10 + c - 'a');
+  return (~0);
+}
+#endif
+/*
+ * Wrappers to allow 'special' environment variables to get processed
+ */
+void
+setenv(char *e, char *v, int rewrite)
+{
+  if (nvram_getenv(e) && !rewrite)
+    return;
+
+  nvram_setenv(e, v);
+}
+
+char *
+getenv(char *e)
+{
+  return(nvram_getenv(e));
+}
+
+void
+unsetenv(char *e)
+{
+  nvram_unsetenv(e);
+}
+
+void
+purgeenv()
+{
+  int i;
+  unsigned char* nvramDataPointer = (unsigned char*)(NVRAM_BASE);
+
+  for (i = ENV_BASE; i < ENV_TOP; i++)
+    *nvramDataPointer++ = 0;
+  nvram_setshort(NVOFF_MAGIC, NV_MAGIC);
+  nvram_setshort(NVOFF_ENVSIZE, 0);
+  nvram_setshort(NVOFF_CSUM, NV_MAGIC);
+}
+
+void
+mapenv(int (*func)(char *, char *))
+{
+  nvram_mapenv(func);
+}
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/nvram/nvram434.h linux/arch/mips/rc32434/79EB434/nvram/nvram434.h
--- linux-2.4.30/arch/mips/rc32434/79EB434/nvram/nvram434.h	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/nvram/nvram434.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,37 @@
+/*
+ ******************************************************************************
+ * nvram434.h: NVRAM layout definitions
+ ******************************************************************************
+ * P. Sadik Oct 08,2003
+ *
+ * Ported from IdtBoot
+ ******************************************************************************
+ */
+
+#ifndef _NVRAM_
+#define _NVRAM_
+#define NVOFFSET        0                 /* use all of NVRAM */
+
+/* Offsets to reserved locations */
+              /* size description */
+#define NVOFF_MAGIC     (NVOFFSET + 0)    /* 2 magic value */
+#define NVOFF_CSUM      (NVOFFSET + 2)    /* 2 NVRAM environment checksum */
+#define NVOFF_ENVSIZE   (NVOFFSET + 4)    /* 2 size of 'environment' */
+#define NVOFF_TEST      (NVOFFSET + 5)    /* 1 cold start test byte */
+#define NVOFF_ETHADDR   (NVOFFSET + 6)    /* 6 decoded ethernet address */
+#define NVOFF_UNUSED    (NVOFFSET + 12)   /* 0 current end of table */
+
+#define NV_MAGIC        0xdeaf            /* nvram magic number */
+#define NV_RESERVED     32                /* number of reserved bytes */
+
+#undef  NVOFF_ETHADDR
+#define NVOFF_ETHADDR   (NVOFFSET + NV_RESERVED - 6)
+
+/* number of bytes available for environment */
+#define ENV_BASE        (NVOFFSET + NV_RESERVED)
+#define ENV_TOP         TD_NVRAM_SIZE
+#define ENV_AVAIL       (ENV_TOP - ENV_BASE)
+
+#endif /* _NVRAM_ */
+
+
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/nvram/nvramproc.c linux/arch/mips/rc32434/79EB434/nvram/nvramproc.c
--- linux-2.4.30/arch/mips/rc32434/79EB434/nvram/nvramproc.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/nvram/nvramproc.c	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,218 @@
+/******************************************************************************
+  Together with nvram438.c, we provide /proc interface for
+  the user to set and modify the environment variables stored in NVRAM.
+ *******************************************************************************
+  Examples
+  
+  #cat /proc/nvram/boot
+     will display the boot environment variables, like netaddr0,bootaddr etc.
+  #echo "netaddr0 192.168.1.3" >/proc/nvram/boot
+     will modify the boot environment variable netaddr0.
+  #echo "netaddr0" >/proc/nvram/boot
+     will erase the "netaddr0" environment variable
+  #echo >/proc/nvram/boot
+     will erase all environment variables stored in nvram
+******************************************************************************
+Pallathu Sadik Oct 08,2003
+Original Form
+******************************************************************************/
+	
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+
+//#define DEBUG 1
+#define MODULE_VERSION "1.0"
+#define MODULE_NAME "nvram"
+#define BOOT_MAJOR 240
+#define BUF_LEN 8192
+
+extern void setenv (char *e, char *v, int rewrite);
+extern char *getenv (char *e);
+extern void unsetenv (char *e);
+extern void mapenv (int (*func)(char *, char *));
+extern void purgeenv(void);
+extern char *getenv (char *s);
+
+static struct proc_dir_entry *nvram, *boot;
+
+static int boot_open(struct inode *, struct file *);
+static int boot_release(struct inode *, struct file *);
+static ssize_t boot_read(struct file *, char *, size_t, loff_t *);
+static ssize_t boot_write(struct file *, const char *, size_t, loff_t *);
+
+static struct file_operations boot_fops = {
+  .read = boot_read, 
+  .write = boot_write,
+  .open = boot_open,
+  .release = boot_release
+};
+
+static char msg[BUF_LEN];
+static char *msg_Ptr;
+const char delimiters[] = " \n";
+
+static int printVal(char *name,char *val)
+{
+  strcat(msg,name);
+  strcat(msg," ");
+  strcat(msg,val);
+  strcat(msg,"\n");
+  return 0;
+}
+
+static int boot_open(struct inode *inode, struct file *file)
+{
+   MOD_INC_USE_COUNT;
+   memset(msg,0,BUF_LEN);
+   msg_Ptr = msg;
+   return 0;
+}
+static int boot_release(struct inode *inode, struct file *file)
+{
+  MOD_DEC_USE_COUNT;
+  return 0;
+}
+
+static ssize_t boot_read(struct file *filp,
+			   char *buffer,    /* The buffer to fill with data */
+			   size_t length,   /* The length of the buffer     */
+			   loff_t *offset)  /* Our offset in the file       */
+{
+  int bytes_read = 0;
+  if(msg_Ptr == msg)
+    {
+      mapenv(&printVal);
+    }
+  if (*msg_Ptr == 0){
+    msg_Ptr = msg;
+    return 0;
+  }
+  while (length && *msg_Ptr)
+    {
+      put_user(*(msg_Ptr++), buffer++);
+      length--;
+      bytes_read++;
+    }
+  return bytes_read;
+}
+
+static ssize_t boot_write(struct file *filp,
+			    const char *buffer,
+			    size_t length,
+			    loff_t *offset)
+{
+  int bytes_written = 0;
+  char *token1,*token2;
+
+  memset(msg,0,BUF_LEN);
+
+  while(length && *buffer)
+    {
+      get_user(msg[bytes_written],buffer++);
+      length--;
+      bytes_written++;
+    }
+
+  token1 = strsep(&msg_Ptr,delimiters);
+  token2 = strsep(&msg_Ptr,delimiters);
+
+  if((*token1 !='\0') && (*token2 != '\0'))
+    {
+#ifdef DEBUG
+    printk(__FILE__": setting environment %s to %s\n",token1,token2);
+#endif
+    setenv(token1,token2,1);
+    }
+  else if(*token1 != '\0')
+    {
+#ifdef DEBUG
+      printk(__FILE__": purging environment variable %s\n",token1);
+#endif
+      unsetenv(token1);
+    }
+  else
+    {
+#ifdef DEBUG
+      printk(__FILE__": purging all environment variables\n");
+#endif
+      purgeenv();
+    }
+  
+  return bytes_written;
+}
+
+
+static int __init init_procfs_nvram(void)
+{
+  int rv = 0;
+
+  /* create directory */
+  nvram = proc_mkdir(MODULE_NAME, NULL);
+  if(nvram == NULL) {
+#ifdef DEBUG
+    printk(__FILE__": Cannot make proc entry for 'nvram'\n");
+#endif
+    rv = -ENOMEM;
+    goto out1;
+  }
+#ifdef DEBUG
+  printk(__FILE__": module %s %s initialised\n", MODULE_NAME, MODULE_VERSION);
+#endif
+  if (register_chrdev(BOOT_MAJOR,"boot", &boot_fops) <0)
+    {
+#ifdef DEBUG     
+      printk(__FILE__": Cannot register 'boot' device.\n");
+#endif
+      rv = -EINVAL;
+      goto out2;
+    }
+      
+#ifdef DEBUG     
+  printk(__FILE__": device 'boot' (major %d) registered\n",BOOT_MAJOR);
+#endif
+        
+  boot = proc_mknod("boot", S_IFCHR | 0644, nvram, MKDEV(BOOT_MAJOR, 0));
+  if(boot == NULL) {
+#ifdef DEBUG
+    printk(__FILE__": Cannot make proc entry for boot.\n");
+#endif
+    rv = -ENOMEM;
+    goto out3;
+  }
+
+  boot->owner = THIS_MODULE;
+  return 0;
+  
+ out3:
+  unregister_chrdev(BOOT_MAJOR,"boot");
+ out2:
+  remove_proc_entry(MODULE_NAME, NULL);
+ out1:
+  return rv;
+}
+
+
+static void __exit cleanup_procfs_nvram(void)
+{
+  remove_proc_entry("boot", nvram);
+  unregister_chrdev(BOOT_MAJOR,"boot");
+  remove_proc_entry(MODULE_NAME, NULL);
+#ifdef DEBUG
+  printk("%s %s removed\n", MODULE_NAME, MODULE_VERSION);
+#endif
+}
+
+
+module_init(init_procfs_nvram);
+module_exit(cleanup_procfs_nvram);
+
+MODULE_AUTHOR("Pallathu Sadik");
+MODULE_DESCRIPTION("proc interface for nvram");
+MODULE_LICENSE("GPL");
+
+EXPORT_NO_SYMBOLS;
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/nvram/rtc.h linux/arch/mips/rc32434/79EB434/nvram/rtc.h
--- linux-2.4.30/arch/mips/rc32434/79EB434/nvram/rtc.h	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/nvram/rtc.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,49 @@
+/*
+ * rtc.h -- DS1553(Dallas Semiconductor) Real Time Clock and Non-Volatile RAM
+ * P. Sadik       Oct 07,2003
+ * Ported from IdtBoot
+ */
+
+#ifndef _DS1553RTC_H_
+#define _DS1553RTC_H_
+
+#define RTC_BASE           0xBA000000
+/*
+ * To maintain endianess independence, make all accesses as 32-bit
+ * words with appropriate shifting.
+ */
+#define TD_NVRAM_SIZE 0x1FF0
+
+typedef struct td_clock {
+  unsigned char ram[TD_NVRAM_SIZE];
+  unsigned char flags;
+  unsigned char dummy;
+  unsigned char alarm_secs;
+  unsigned char alarm_mins;
+  unsigned char alarm_hours;
+  unsigned char alarm_date;
+  unsigned char interrupts;
+  unsigned char watchdog;
+  unsigned char control;
+  unsigned char secs;
+  unsigned char mins;
+  unsigned char hours;
+  unsigned char weekday;
+  unsigned char date;
+  unsigned char month;
+  unsigned char year;
+} RTC;
+
+#define rtc ((volatile RTC *)RTC_BASE)
+
+/*
+ * Control register bit definitions
+ */
+#define TDC_ENA_WRITE   0x80
+#define TDC_DIS_WRITE   0x7f
+#define TDC_ENA_READ    0x40
+#define TDC_DIS_READ    0xBf
+#define TDS_STOP    	0x80
+
+#endif /* _DS1553RTC_H */
+
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/pci_fixup.c linux/arch/mips/rc32434/79EB434/pci_fixup.c
--- linux-2.4.30/arch/mips/rc32434/79EB434/pci_fixup.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/pci_fixup.c	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,108 @@
+/*
+ *
+ * BRIEF MODULE DESCRIPTION
+ *	Board specific pci fixups.
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *         	stevel@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>
+
+#ifdef CONFIG_PCI
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/rc32434/rc32434.h>
+#include <asm/rc32434/pci.h> 
+#include <asm/rc32434/pcikorina.h> 
+
+void __init pcibios_fixup_resources(struct pci_dev *dev)
+{
+}
+
+
+
+void __init pcibios_fixup(void)
+{
+
+}
+
+void __init pcibios_fixup_irqs(void)
+{
+	struct pci_dev *dev;
+	unsigned char pin;
+
+	pci_for_each_dev(dev) {
+//		unsigned int slot;
+		printk("pci dev name: %s\n", dev->name);
+//		if (dev->bus->number != 0) {
+//			return;
+//		}
+
+//		slot = PCI_SLOT(dev->devfn);
+		
+//		if (slot > 0 && slot <= 5) {
+			pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin);
+
+			switch (pin) {
+			case 1: /* INTA*/
+			  	dev->irq = GROUP4_IRQ_BASE + 11;
+				break;
+			case 2: /* INTB */
+				dev->irq = GROUP4_IRQ_BASE + 11;
+				break;
+			case 3: /* INTC */
+				dev->irq = GROUP4_IRQ_BASE + 11;
+				break;
+			case 4: /* INTD */
+				dev->irq = GROUP4_IRQ_BASE + 11;
+				break;
+			default:
+				dev->irq = 0xff; 
+				break;
+			}
+#ifdef DEBUG
+//			printk("irq fixup: slot %d, pin %d, irq %d\n",
+//			       slot, pin, dev->irq);
+#endif
+			pci_write_config_byte(dev, PCI_INTERRUPT_LINE,
+					      dev->irq);
+//		}
+	}
+
+}
+#endif
+
+
+
+
+
+
+
+
+
+
+
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/pci_ops.c linux/arch/mips/rc32434/79EB434/pci_ops.c
--- linux-2.4.30/arch/mips/rc32434/79EB434/pci_ops.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/pci_ops.c	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,429 @@
+/*
+ *
+ * BRIEF MODULE DESCRIPTION
+ *	RC32334 specific pci support.
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *         	stevel@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>
+
+#ifdef CONFIG_PCI
+
+#include <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/pci_channel.h>
+#include <asm/rc32434/rc32434.h>
+#include <asm/rc32434/pci.h> 
+#include <asm/rc32434/pcikorina.h> 
+
+#define PCI_ACCESS_READ  0
+#define PCI_ACCESS_WRITE 1
+
+#undef DEBUG
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+#ifdef __MIPSEB__
+#define SWAP_BIT 1
+#else
+#define SWAP_BIT 0
+#endif
+/* define an unsigned array for the PCI registers */
+unsigned int korinaCnfgRegs[25] = {
+				KORINA_CNFG1,
+				KORINA_CNFG2,
+				KORINA_CNFG3,
+				KORINA_CNFG4,	
+				KORINA_CNFG5,
+				KORINA_CNFG6,
+				KORINA_CNFG7,
+				KORINA_CNFG8,
+				KORINA_CNFG9,
+				KORINA_CNFG10,
+				KORINA_CNFG11,
+				KORINA_CNFG12,
+				KORINA_CNFG13,
+				KORINA_CNFG14,
+				KORINA_CNFG15,
+				KORINA_CNFG16,
+				KORINA_CNFG17,
+				KORINA_CNFG18,
+				KORINA_CNFG19,
+				KORINA_CNFG20,
+				KORINA_CNFG21,				
+				KORINA_CNFG22,
+				KORINA_CNFG23,
+				KORINA_CNFG24};
+unsigned int pciConfigAddr;/*used for writing pci config values */
+int	     loopCount    ;/*used for the loop */
+unsigned int dummyRead    ;/*used to flush CPU write buffers */
+extern struct resource rc32434_res_pci_io1;
+//extern struct resource rc32434_res_pci_io2;
+extern struct resource rc32434_res_pci_mem1;
+//extern struct resource rc32434_res_pci_mem2;
+
+extern char * __init prom_getcmdline(void);
+
+#define PCI_CFG_SET(bus, slot, func, off) \
+	(rc32434_pci->pcicfga = (0x80000000 | ((bus) << 16) | ((slot)<<11) | \
+			    ((func)<<8) | (off)))
+
+static int
+config_access(u32 type, u32 bus, u8 devfn, u32 where, u32 *data)
+{
+	/* 
+	 * config cycles are on 4 byte boundary only
+	 */
+	u32 slot = PCI_SLOT(devfn);
+	u32 func = PCI_FUNC(devfn);
+	
+//	if (bus || slot > 5) {
+//	if (slot > 5) {
+//		*data = 0xFFFFFFFF;
+//		return PCIBIOS_DEVICE_NOT_FOUND;
+//	}
+
+	/* Setup address */
+//	PCI_CFG_SET(0, slot, func, where);
+
+	PCI_CFG_SET(bus, slot, func, where);
+	rc32434_sync();
+
+	if (type == PCI_ACCESS_WRITE) {
+	        rc32434_pci->pcicfgd = *data;
+//		printk("w bus %d, dev %d, func %d, off %02x: %p",
+//		       bus, slot, func, where, *data);
+	}
+	else {
+		*data = rc32434_pci->pcicfgd;
+//		printk("r bus %d, dev %d, func %d, off %02x: %p",
+//		       bus, slot, func, where, *data);
+	}
+	rc32434_sync();
+
+	/*
+	 * Revisit: check for master or target abort.
+	 */
+	return 0;
+}
+
+static inline int config_write(u8 bus, u8 devfn, u8 where, u32 data)
+{
+	return config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data);
+}
+
+static inline int config_read(u8 bus, u8 devfn, u8 where, u32 *data)
+{
+	return config_access(PCI_ACCESS_READ, bus, devfn, where, data);
+}
+
+/*
+ * We can't address 8 and 16 bit words directly.  Instead we have to
+ * read/write a 32bit word and mask/modify the data we actually want.
+ */
+static int
+read_config_byte (struct pci_dev *dev, int where, u8 *val)
+{
+	u32 data = 0;
+	*val = 0xff;
+
+	if (config_read(dev->bus->number, dev->devfn, where, &data))
+		return -1;
+
+	*val = (data >> ((where & 3) << 3)) & 0xff;
+//        printk("r %x where %x: val %x\n", 
+//                dev->devfn, where, *val);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+static int
+read_config_word (struct pci_dev *dev, int where, u16 *val)
+{
+	u32 data = 0;
+	*val = 0xffff;
+
+	if (where & 1)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	if (config_read(dev->bus->number, dev->devfn, where, &data))
+		return -1;
+
+	*val = (data >> ((where & 3) << 3)) & 0xffff;
+//        printk("r %x where %x: val %x\n", 
+//                dev->devfn, where, *val);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+read_config_dword (struct pci_dev *dev, int where, u32 *val)
+{
+	u32 data = 0;
+	*val = 0xffffffff;
+
+	if (where & 3)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	
+	if (config_read(dev->bus->number, dev->devfn, where, &data))
+		return -1;
+
+	*val = data;
+//        printk("r %x where %x: val %x\n", 
+//                dev->devfn, where, *val);
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+static int
+write_config_byte (struct pci_dev *dev, int where, u8 val)
+{
+	u32 data = 0;
+       
+	if (config_read(dev->bus->number, dev->devfn, where, &data))
+		return -1;
+
+	data = (data & ~(0xff << ((where & 3) << 3))) |
+	       (val << ((where & 3) << 3));
+
+//        printk("w %x where %x: val %x\n", 
+//                dev->devfn, where, data);
+	if (config_write(dev->bus->number, dev->devfn, where, data))
+		return -1;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+write_config_word (struct pci_dev *dev, int where, u16 val)
+{
+        u32 data = 0;
+
+	if (where & 1)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+       
+        if (config_read(dev->bus->number, dev->devfn, where, &data))
+		return -1;
+
+	data = (data & ~(0xffff << ((where & 3) << 3))) | 
+	       (val << ((where & 3) << 3));
+
+//        printk("w %x where %x: val %x\n", 
+//                dev->devfn, where, data);
+	if (config_write(dev->bus->number, dev->devfn, where, data))
+	       return -1;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+write_config_dword(struct pci_dev *dev, int where, u32 val)
+{
+	if (where & 3)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+//        printk("w %x where %x: val %x\n", 
+//                dev->devfn, where, val);
+	if (config_write(dev->bus->number, dev->devfn, where, val))
+		return -1;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops rc32434_pci_ops = {
+	read_config_byte,
+        read_config_word,
+	read_config_dword,
+	write_config_byte,
+	write_config_word,
+	write_config_dword
+};
+
+struct pci_channel mips_pci_channels[] = {
+	{ &rc32434_pci_ops, &rc32434_res_pci_io1,
+	  &rc32434_res_pci_mem1, PCI_DEVFN(1,0), PCI_DEVFN(31,0) },
+	{ NULL, NULL, NULL, 0, 0}
+};
+
+unsigned __init int pcibios_assign_all_busses(void)
+{
+       return 1;
+}
+
+/*
+static void rc32434_dump_pci_bridge(void)
+{
+	int i;
+	u32 val;
+	printk("RC32434 PCI Bridge Config:\n");
+
+	printk("PCI_MEM1_BASE: 0x%08x\n", rc32434_pci->pcilba[0].a);
+	printk("PCI_IO1_BASE: 0x%08x\n", rc32434_pci->pcilba[1].a);
+	printk("PCI_MEM2_BASE: 0x%08x\n", rc32434_pci->pcilba[2].a);
+	printk("PCI_IO2_BASE:  0x%08x\n", rc32434_pci->pcilba[3].a);
+
+	for (i=0; i<17; i++) {
+		config_read(0, 0, i*4, &val);
+		printk("dword %d\t%08x\n", i, val);
+	}
+}
+*/
+
+void __init rc32434_pcibridge_init(void)
+{
+
+	unsigned int pcicValue, pcicData=0;
+        unsigned int dummyRead, pciCntlVal;
+	printk("PCI: Initializing PCI\n");
+        pcicValue = rc32434_pci->pcic;
+	pcicValue = (pcicValue >> PCIM_SHFT) & PCIM_BIT_LEN;
+        if (!((pcicValue == PCIM_H_EA) ||
+	     (pcicValue == PCIM_H_IA_FIX) ||
+		(pcicValue == PCIM_H_IA_RR))) {
+		printk("PCI init error!!!\n");
+		/* Not in Host Mode, return ERROR */
+		return;
+	}
+
+        /* Enables the Idle Grant mode, Arbiter Parking */
+	pcicData |=(PCIC_igm_m|PCIC_eap_m|PCIC_en_m);
+        rc32434_pci->pcic = pcicData; /* Enable the PCI bus Interface */
+	/* Zero out the PCI status & PCI Status Mask */
+        for(;;)
+        {
+	   pcicData = rc32434_pci->pcis;
+           if (!(pcicData & PCIS_rip_m))
+                break;
+        }
+
+	rc32434_pci->pcis = 0;
+        rc32434_pci->pcism = 0xFFFFFFFF;
+	/* Zero out the PCI decoupled registers */
+	rc32434_pci->pcidac=0; /* disable PCI decoupled accesses at initialization */
+	rc32434_pci->pcidas=0; /* clear the status */
+	rc32434_pci->pcidasm=0x0000007F; /* Mask all the interrupts */
+	/* Mask PCI Messaging Interrupts */
+	rc32434_pci_msg->pciiic = 0;
+	rc32434_pci_msg->pciiim = 0xFFFFFFFF;
+	rc32434_pci_msg->pciioic = 0;
+	rc32434_pci_msg->pciioim = 0;
+
+
+        /* Setup PCILB0 as Memory Window */
+	rc32434_pci->pcilba[0].a = (unsigned int) (PCI_ADDR_START);
+
+	/* setup the PCI map address as same as the local address */
+
+	rc32434_pci->pcilba[0].m = (unsigned int) (PCI_ADDR_START);
+
+	/* Setup PCILBA1 as MEM */
+#ifdef __MIPSEB__
+	        rc32434_pci->pcilba[0].c = ( ((SIZE_16MB & 0x1f) << PCILBAC_size_b) | PCILBAC_sb_m);
+#else
+	        rc32434_pci->pcilba[0].c = ( ((SIZE_16MB & 0x1f) << PCILBAC_size_b) );
+#endif
+	dummyRead = rc32434_pci->pcilba[0].c; /* flush the CPU write Buffers */
+
+	rc32434_pci->pcilba[1].a = 0x60000000;
+    
+	rc32434_pci->pcilba[1].m = 0x60000000;
+        /* setup PCILBA2 as IO Window*/
+#ifdef __MIPSEB__
+	rc32434_pci->pcilba[1].c = (((SIZE_256MB & 0x1f) << PCILBAC_size_b )| PCILBAC_sb_m);
+#else
+	rc32434_pci->pcilba[1].c = ( ((SIZE_256MB & 0x1f) << PCILBAC_size_b));
+#endif
+  
+	dummyRead = rc32434_pci->pcilba[1].c; /* flush the CPU write Buffers */
+	rc32434_pci->pcilba[2].a = 0x18C00000;
+    
+	rc32434_pci->pcilba[2].m = 0x18FFFFFF;
+        /* setup PCILBA2 as IO Window*/
+#ifdef __MIPSEB__
+	rc32434_pci->pcilba[2].c = (((SIZE_4MB & 0x1f) << PCILBAC_size_b) | PCILBAC_sb_m );
+#else
+	rc32434_pci->pcilba[2].c = ( ((SIZE_4MB & 0x1f) << PCILBAC_size_b)  );
+#endif  
+  
+	dummyRead = rc32434_pci->pcilba[2].c; /* flush the CPU write Buffers */
+
+
+	rc32434_pci->pcilba[3].a = 0x18800000;
+
+	rc32434_pci->pcilba[3].m = 0x18800000;
+	/* Setup PCILBA3 as IO Window */
+
+#ifdef __MIPSEB__
+	rc32434_pci->pcilba[3].c = ( (((SIZE_1MB & 0x1ff) << PCILBAC_size_b) | PCILBAC_msi_m)   |  PCILBAC_sb_m);
+#else
+	rc32434_pci->pcilba[3].c = (((SIZE_1MB & 0x1ff) << PCILBAC_size_b) | PCILBAC_msi_m);
+#endif
+	dummyRead = rc32434_pci->pcilba[2].c; /* flush the CPU write Buffers */
+
+	pciConfigAddr=(unsigned int)(0x80000004);
+	for(loopCount=0;loopCount<24;loopCount++){
+		rc32434_pci->pcicfga=pciConfigAddr;
+		dummyRead=rc32434_pci->pcicfga;
+		rc32434_pci->pcicfgd = korinaCnfgRegs[loopCount];
+		dummyRead=rc32434_pci->pcicfgd;
+		pciConfigAddr += 4;
+	}
+	rc32434_pci->pcitc=(unsigned int)((PCITC_RTIMER_VAL&0xff) << PCITC_rtimer_b) |
+ \
+				((PCITC_DTIMER_VAL&0xff)<<PCITC_dtimer_b);
+
+	pciCntlVal=rc32434_pci->pcic;
+	pciCntlVal &=~(PCIC_tnr_m);
+	rc32434_pci->pcic = pciCntlVal;
+	pciCntlVal=rc32434_pci->pcic;
+
+#ifdef __MIPSEB__
+#define ENDIANNESS_BIT 1
+#else
+#define ENDIANNESS_BIT 0
+#endif
+	rc32434_sync();   
+}
+
+#endif /* CONFIG_PCI */
+
+
+
+
+
+
+
+
+
+
+
+
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/prom.c linux/arch/mips/rc32434/79EB434/prom.c
--- linux-2.4.30/arch/mips/rc32434/79EB434/prom.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/prom.c	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,195 @@
+/*
+* prom.c 
+**********************************************************************
+* P . Sadik Oct 10, 2003
+*
+* Started change log
+* idt_cpu_freq is make a kernel configuration parameter
+* idt_cpu_freq is exported so that other modules can use it.
+* Code cleanup
+**********************************************************************
+* P. Sadik Oct 20, 2003
+*
+* Removed NVRAM code from here, since they are already available under
+* nvram directory.
+* Added serial port initialisation.
+**********************************************************************
+**********************************************************************
+* P. Sadik Oct 30, 2003
+*
+* Added reset_cons_port
+**********************************************************************
+*/
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/console.h>
+#include <asm/bootinfo.h>
+#include <linux/bootmem.h>
+#include <linux/ioport.h>
+
+char arcs_cmdline[COMMAND_LINE_SIZE];
+
+#define PROM_ENTRY(x)   (0xbfc00000+((x)*8))
+extern void __init setup_serial_port(void);
+extern void cons_putc(char c);
+extern void cons_puts(char *s);
+
+void (*prom_puts)(char *str) = cons_puts;
+void (*prom_putchar)(char c) = cons_putc;
+int (*prom_getchar)(void) = (int (*)(void))PROM_ENTRY(11);
+
+unsigned int idt_cpu_freq = CONFIG_IDT_BOARD_FREQ;
+EXPORT_SYMBOL(idt_cpu_freq);
+
+#ifdef CONFIG_REMOTE_DEBUG
+extern int remote_debug;
+#endif
+
+extern void setup_serial_port(void);
+#ifdef CONFIG_IDT_BOOT_NVRAM
+extern void mapenv(int (*func)(char *, char *));
+static int make_bootparm(char *name,char *val)
+{
+  /*
+   * The bootparameters are obtained from NVRAM and formatted here.
+   * For e.g.
+   *
+   *    netaddr=10.0.1.95
+   *    bootaddr=10.0.0.139
+   *    bootfile=vmlinus
+   *    bootparm1=root=/dev/nfs
+   *    bootparm2=ip=10.0.1.95
+   *
+   * is parsed to:
+   *
+   *      root=/dev/nfs ip=10.0.1.95
+   *
+   * in arcs_cmdline[].
+   */
+  if (strncmp(name, "bootparm", 8) == 0) {
+    strcat(arcs_cmdline,val);
+    strcat(arcs_cmdline," ");
+  }
+  else if(strncmp(name, "HZ", 2) == 0) {
+    idt_cpu_freq = simple_strtoul(val, 0, 10);
+    printk("CPU Clock at %d Hz (from HZ environment variable)\n",
+           idt_cpu_freq);
+  }
+  return 0;
+}
+static void prom_init_cmdline(void)
+{ 
+  memset(arcs_cmdline,0,sizeof(arcs_cmdline));
+  mapenv(&make_bootparm);
+}
+#else
+/* Kernel Boot parameters */
+// static unsigned char bootparm[]="ip=157.165.29.36:157.165.29.18::255.255.0.0::eth0";
+static unsigned char bootparm[]="ip=10.0.0.51:10.0.0.112:10.0.0.52:255.255.255.224::eth0: root=/dev/nfs nfsroot=/mipsroot/";
+#endif
+
+void prom_console_write(struct console *con, const char *s, unsigned count)
+{
+	int i;
+
+	for(i = 0; i < count; i++, s++) {
+		if(*s == '\n') prom_putchar('\r');
+		prom_putchar(*s);
+	}
+}
+
+kdev_t prom_console_device(struct console *con)
+{
+	return MKDEV(TTY_MAJOR, 64 + con->index);
+}
+
+int prom_console_wait_key(struct console *con)
+{
+	return prom_getchar();
+}
+
+int prom_console_setup(struct console *con, char *options)
+{
+  reset_cons_port();
+  return 0;
+}
+
+struct console prom_console_driver = {
+	"ttyS",
+	prom_console_write,		/* write */
+	NULL,				/* read */
+	prom_console_device,		/* device */
+//	prom_console_wait_key,		/* wait_key */
+	NULL,				/* unblank */
+	prom_console_setup,   		/* setup */
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
+};
+
+extern unsigned long mips_machgroup;
+extern unsigned long mips_machtype;
+
+/* IDT 79EB434 memory map -- we really should be auto sizing it */
+
+#define RAM_FIRST       0x80000400  /* Leave room for interrupt vectors */
+#define RAM_SIZE        32*1024*1024
+#define RAM_END         (0x80000000 + RAM_SIZE)     
+struct resource rc32434_res_ram = {
+	"RAM",
+	0,
+	RAM_SIZE,
+	IORESOURCE_MEM
+};
+
+int __init prom_init(int argc, char **argv, char **envp)
+{
+#ifdef CONFIG_IDT_BOOT_NVRAM
+        /* set up command line */
+        prom_init_cmdline();
+#else
+        // sprintf(arcs_cmdline,"%s",bootparm);
+
+	unsigned i, offset = 0;
+	for (i = 0; (i < argc && argv[i] != NULL); i++) {
+	    sprintf(arcs_cmdline + offset, "%s ", argv[i]);
+	    offset += strlen(argv[i]) + 1;
+	}
+#endif
+
+	/* turn on the console */
+
+#define CONFIG_IDT_PROM_CONSOLE
+#ifdef CONFIG_IDT_PROM_CONSOLE
+
+	register_console(&prom_console_driver); 
+
+#endif
+	setup_serial_port();
+	/* set our arch type */
+
+	mips_machgroup = MACH_GROUP_IDT;
+
+	/*
+	 * give all RAM to boot allocator,
+	 * except where the kernel was loaded
+	 */
+
+	add_memory_region(0,rc32434_res_ram.end - rc32434_res_ram.start, BOOT_MEM_RAM);
+#ifdef CONFIG_REMOTE_DEBUG
+	if(strstr(arcs_cmdline, "kgdb=on")) {
+		remote_debug = 1;
+	}
+#endif
+
+	return 0;
+}
+
+void prom_free_prom_memory(void)
+{
+	printk("stubbed prom_free_prom_memory()\n");
+}
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/rtc-ds1553.c linux/arch/mips/rc32434/79EB434/rtc-ds1553.c
--- linux-2.4.30/arch/mips/rc32434/79EB434/rtc-ds1553.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/rtc-ds1553.c	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,136 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *      DS1501 (Dallas Semiconductor) Real Time Clock Support
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *         	stevel@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/types.h>
+#include <linux/time.h>
+#include <asm/time.h>
+#include <asm/addrspace.h>
+#include <asm/debug.h>
+#include <asm/rc32434/rc32434.h>
+#include "nvram/rtc.h"
+
+#undef BCD_TO_BIN
+#define BCD_TO_BIN(val) (((val)&15) + ((val)>>4)*10)
+
+#undef BIN_TO_BCD
+#define BIN_TO_BCD(val) ((((val)/10)<<4) + (val)%10)
+
+#define EPOCH 2000
+
+static unsigned long
+rtc_ds1553_get_time(void)
+{	
+	u32 century, year, month, day, hour, minute, second;
+
+	/* read time data */
+	writeb(readb(&rtc->control) | TDC_ENA_READ, &rtc->control);
+	century  = BCD_TO_BIN(readb(&rtc->control) & 0x3f);
+	year = BCD_TO_BIN(readb(&rtc->year)) + (century * 100);
+	month = BCD_TO_BIN(readb(&rtc->month) & 0x1f);
+	day = BCD_TO_BIN(readb(&rtc->date) & 0x3f);
+	hour = BCD_TO_BIN(readb(&rtc->hours) & 0x3f);	/* 24 hour format */
+	minute = BCD_TO_BIN(readb(&rtc->mins) & 0x7f);
+	second = BCD_TO_BIN(readb(&rtc->secs) & 0x7f);
+	writeb(readb(&rtc->control) & TDC_DIS_READ, &rtc->control);
+
+	return mktime(year, month, day, hour, minute, second);
+}
+
+static int 
+rtc_ds1553_set_time(unsigned long t)
+{
+	struct rtc_time tm;
+	u8 temp, ctrl;
+	u8 year, month, day, hour, minute, second;
+
+	/* freeze external registers */
+	ctrl = readb(&rtc->control);
+	writeb(ctrl | TDC_ENA_WRITE, &rtc->control);
+
+	/* convert */
+	to_tm(t, &tm);
+
+	/* check each field one by one */
+
+	year = BIN_TO_BCD(tm.tm_year - EPOCH);
+	if (year != readb(&rtc->year))
+		writeb(year, &rtc->year);
+
+	temp = readb(&rtc->month);
+	month = BIN_TO_BCD(tm.tm_mon + 1); /* tm_mon starts from 0 to 11 */
+	if (month != (temp & 0x1f))
+		writeb((month & 0x1f) | (temp & ~0x1f), &rtc->month);
+
+	temp = readb(&rtc->date);
+	day = BIN_TO_BCD(tm.tm_mday);
+	if (day != (temp & 0x3f))
+		writeb(day, &rtc->date);
+
+	temp = readb(&rtc->hours);
+	hour = BIN_TO_BCD(tm.tm_hour) & 0x3f;	/* 24 hour format */
+	if (hour != (temp & 0x3f))
+		writeb(hour, &rtc->hours);
+
+	temp = readb(&rtc->mins);
+	minute = BIN_TO_BCD(tm.tm_min);
+	if (minute != (temp & 0x7f))
+		writeb(minute, &rtc->mins);
+
+	temp = readb(&rtc->secs);
+	second = BIN_TO_BCD(tm.tm_sec);
+	if (second != (temp & 0x7f))
+		writeb(second, &rtc->secs);
+	
+	writeb(ctrl & TDC_DIS_WRITE, &rtc->control);
+	return 0;
+}
+
+void
+rtc_ds1553_init(void)
+{
+	u8 stop;
+
+	writeb(readb(&rtc->control) | TDC_ENA_READ, &rtc->control);
+	stop = readb(&rtc->secs);
+	writeb(readb(&rtc->control) & TDC_DIS_READ, &rtc->control);
+
+	if (stop & TDS_STOP) {
+		/* start clock */
+		writeb(readb(&rtc->control) | TDC_ENA_WRITE, &rtc->control);
+		writeb(stop | TDS_STOP, &rtc->secs);
+		writeb(readb(&rtc->control) & TDC_DIS_WRITE, &rtc->control);
+		/* wait 2s till oscillator stabilizes */
+		mdelay(2000);
+	}
+	
+	/* set the function pointers */
+	rtc_get_time = rtc_ds1553_get_time;
+	rtc_set_time = rtc_ds1553_set_time;
+}
+
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/serial.c linux/arch/mips/rc32434/79EB434/serial.c
--- linux-2.4.30/arch/mips/rc32434/79EB434/serial.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/serial.c	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,51 @@
+/*
+***********************************************************************
+* serial.c
+* Setup a serial console that is a function of board frequency.
+*
+***********************************************************************
+* P. Sadik     Oct 10, 2003
+*
+* Original form
+***********************************************************************
+* P. Sadik     Oct 20, 2003
+*
+* Changed setup_serial_port from static to extern, so that prom.c can call
+* this function directly.
+***********************************************************************
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+
+#include <linux/types.h>
+#include <linux/serial.h>
+#include <asm/serial.h>
+#include <asm/io.h>
+#include <asm/rc32434/rc32434.h>
+extern unsigned int idt_cpu_freq;
+extern int __init setup_serial_port(void)
+{
+  struct serial_struct serial_req;
+  memset(&serial_req,0,sizeof(serial_req));
+  serial_req.type = PORT_16550;
+  serial_req.baud_base =  (idt_cpu_freq / 16);
+  serial_req.iomem_base = (unsigned char *) KSEG1ADDR(RC32434_UART_BASE);
+  serial_req.irq = RC32434_UART_IRQ;
+  serial_req.iomem_reg_shift = 2;
+  serial_req.io_type= SERIAL_IO_MEM;
+  serial_req.flags= STD_COM_FLAGS;
+  serial_req.type= 3;
+
+  if (early_serial_setup(&serial_req) < 0) {
+    printk("early_serial_setup() for serial console port failed\n");
+    return(-1);
+  }
+  printk("Serial console port initialized\n");
+  return(0);
+}
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/serial_gdb.c linux/arch/mips/rc32434/79EB434/serial_gdb.c
--- linux-2.4.30/arch/mips/rc32434/79EB434/serial_gdb.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/serial_gdb.c	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,248 @@
+/*
+*********************************************************************
+ * serial_gdb.c - polling driver for 16550 UART in IDT RC32334
+ *
+ * Copyright (C) 2000 by Lineo, Inc.
+ * Written by Quinn Jensen (jensenq@lineo.com)
+ *
+*********************************************************************
+* P. Sadik  Oct 20, 2003
+*
+* DIVISOR is made a function of idt_cpu_freq
+*********************************************************************
+*********************************************************************
+* P. Sadik  Oct 30, 2003
+*
+* added reset_cons_port
+*********************************************************************
+ */
+#include <linux/serial_reg.h>
+
+/* set remote gdb baud rate at 115200 */
+
+#define GDB_BAUD 115200
+#define CONS_BAUD 9600
+
+extern unsigned int idt_cpu_freq;
+
+
+/* turn this on to watch the debug protocol echoed on the console port */
+#undef DEBUG_REMOTE_DEBUG
+
+#ifdef __MIPSEB__
+#define CONS_PORT 0xb8058003u
+#define GDB_PORT  0xb8058023u 
+#else
+#define CONS_PORT 0xb8058000u
+#define GDB_PORT  0xb8058020u
+#endif
+           
+volatile unsigned char *ports[2] = {
+	(volatile unsigned char *)CONS_PORT,
+	(volatile unsigned char *)GDB_PORT
+};
+
+
+void reset_gdb_port(void);
+void cons_putc(char c);
+int port_getc(int port);
+void port_putc(int port, char c);
+
+int cons_getc(void)
+{
+	return port_getc(0);
+}
+
+void cons_putc(char c)
+{
+	port_putc(0, c);
+}
+
+void cons_puts(char *s)
+{
+	while(*s) {
+		if(*s == '\n') cons_putc('\r');
+		cons_putc(*s);
+		s++;
+	}
+}
+
+void cons_do_putn(int n)
+{
+	if(n) {
+		cons_do_putn(n / 10);
+		cons_putc(n % 10 + '0');
+	}
+}
+
+void cons_putn(int n)
+{
+	if(n < 0) {
+		cons_putc('-');
+		n = -n;
+	}
+
+	if (n == 0) {
+		cons_putc('0');
+	} else {
+		cons_do_putn(n);
+	}
+}
+
+#ifdef DEBUG_REMOTE_DEBUG
+static enum {HUH, SENDING, GETTING} state;
+
+static void sent(int c)
+{
+	switch(state) {
+	case HUH:
+	case GETTING:
+		cons_puts("\nSNT ");
+		state = SENDING;
+		/* fall through */
+	case SENDING:
+		cons_putc(c);
+		break;
+	}       
+}
+
+static void got(int c)
+{
+	switch(state) {
+	case HUH:
+	case SENDING:
+		cons_puts("\nGOT ");
+		state = GETTING;
+		/* fall through */
+	case GETTING:
+		cons_putc(c);
+		break;
+	}       
+}
+#endif /* DEBUG_REMOTE */
+
+static int first = 1;
+
+int getDebugChar(void)
+{
+	int c;
+
+	if(first) reset_gdb_port();
+
+	c = port_getc(1);
+
+#ifdef DEBUG_REMOTE_DEBUG
+	got(c);
+#endif
+
+	return c;
+}
+
+int port_getc(int p)
+{
+	volatile unsigned char *port = ports[p];
+	int c;
+
+	while((*(port + UART_LSR * 4) & UART_LSR_DR) == 0) {
+		continue;
+	}       	
+
+	c = *(port + UART_RX * 4);
+
+	return c;
+}
+
+void putDebugChar(char c)
+{
+	if(first) reset_gdb_port();
+
+#ifdef DEBUG_REMOTE_DEBUG
+	sent(c);
+#endif
+	idt_disp_char(3, '%');
+	port_putc(1, c);
+}
+
+#define OK_TO_XMT (UART_LSR_TEMT | UART_LSR_THRE)
+
+void port_putc(int p, char c)
+{
+	volatile unsigned char *port = ports[p];
+	volatile unsigned char *lsr = port + UART_LSR * 4;
+
+	while((*lsr & OK_TO_XMT) != OK_TO_XMT) {
+		continue;
+	}
+
+	*(port + UART_TX * 4) = c;
+}
+
+void reset_gdb_port(void)
+{
+	volatile unsigned char *port = ports[1];
+	unsigned int DIVISOR = (idt_cpu_freq / 16 / GDB_BAUD);
+
+	first = 0;
+#if 1	
+	cons_puts("reset_gdb_port: initializing remote debug serial port (internal UART 1, ");
+	cons_putn(GDB_BAUD);
+	cons_puts("baud, MHz=");
+	cons_putn(idt_cpu_freq);
+	cons_puts(", divisor=");
+	cons_putn(DIVISOR);
+	cons_puts(")\n");
+
+	/* reset the port */
+	*(port + UART_CSR * 4) = 0;
+
+	/* clear and enable the FIFOs */
+	*(port + UART_FCR * 4) = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | 
+		UART_FCR_CLEAR_XMIT | UART_FCR_TRIGGER_14;
+
+	/* set the baud rate */
+	*(port + UART_LCR * 4) = UART_LCR_DLAB;		/* enable DLL, DLM registers */
+	*(port + UART_DLL * 4) = DIVISOR;
+	*(port + UART_DLM * 4) = DIVISOR >> 8;
+
+	/* set the line control stuff and disable DLL, DLM regs */
+
+	*(port + UART_LCR * 4) = UART_LCR_STOP | 	/* 2 stop bits */
+		UART_LCR_WLEN8;				/* 8 bit word length */
+	
+	/* leave interrupts off */
+	*(port + UART_IER * 4) = 0;
+
+	/* the modem controls don't leave the chip on this port, so leave them alone */
+	*(port + UART_MCR * 4) = 0;
+#endif
+
+}
+
+void reset_cons_port(void)
+{
+  volatile unsigned char *port = ports[0];
+  unsigned int DIVISOR = (idt_cpu_freq / 16 / CONS_BAUD);
+
+  /* reset the port */
+  *(port + UART_CSR * 4) = 0;
+
+  /* clear and enable the FIFOs */
+  *(port + UART_FCR * 4) = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | 
+    UART_FCR_CLEAR_XMIT | UART_FCR_TRIGGER_14;
+
+  /* set the baud rate */
+  *(port + UART_LCR * 4) = UART_LCR_DLAB;         /* enable DLL, DLM registers */
+
+  *(port + UART_DLL * 4) = DIVISOR;
+  *(port + UART_DLM * 4) = DIVISOR >> 8;
+  /* set the line control stuff and disable DLL, DLM regs */
+
+  *(port + UART_LCR * 4) = UART_LCR_STOP |        /* 2 stop bits */
+    UART_LCR_WLEN8;                         /* 8 bit word length */
+        
+  /* leave interrupts off */
+  *(port + UART_IER * 4) = 0;
+
+  /* the modem controls don't leave the chip on this port, so leave them alone */
+  *(port + UART_MCR * 4) = 0;
+}
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/setup.c linux/arch/mips/rc32434/79EB434/setup.c
--- linux-2.4.30/arch/mips/rc32434/79EB434/setup.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/setup.c	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,192 @@
+/*
+ * setup.c - boot time setup code for IDT 79S355/79EB355 (Banyan) board
+ *
+ * Copyright (C) 2000 by Lineo, Inc.
+ * Written by Quinn Jensen (jensenq@lineo.com)
+ * Ammended by Gray Girling (GGirling@uk.research.att.com) from IDT 79S334
+ * (can still be used with 79S334 board)
+ *
+ */
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <asm/mipsregs.h>
+#include <asm/pgtable.h>
+#include <linux/mc146818rtc.h>	/* for rtc_ops, we fake the RTC */
+#include <asm/reboot.h>
+#include <asm/addrspace.h>     /* for KSEG1ADDR() */
+#include <asm/rc32434/rc32434.h>
+#include <asm/rc32434/pci.h> 
+#include <asm/rc32434/pcikorina.h> 
+
+
+
+
+#define write_32bit_cp0_register(register,value)                \
+        __asm__ __volatile__(                                   \
+        "mtc0\t%0,"STR(register)"\n\t"				\
+	"nop"							\
+        : : "r" (value));
+
+
+
+
+
+
+extern char * __init prom_getcmdline(void);
+
+extern void (*board_time_init)(void);
+extern void (*board_timer_setup)(struct irqaction *irq);
+extern void rc32434_time_init(void);
+extern void rc32434_timer_setup(struct irqaction *irq);
+void idt_disp_str(char *s);
+#ifdef CONFIG_PCI
+extern void __init rc32434_pcibridge_init(void);
+#endif
+struct resource rc32434_res_pci_mem1;
+struct resource rc32434_res_pci_mem2;
+struct resource rc32434_res_pci_mem3;
+
+#define PCI_MEM1_START PCI_ADDR_START
+#define PCI_MEM1_END   PCI_ADDR_START + CPUTOPCI_MEM_WIN - 1
+#define PCI_MEM2_START PCI_ADDR_START + CPUTOPCI_MEM_WIN 
+#define PCI_MEM2_END   PCI_ADDR_START + ( 2* CPUTOPCI_MEM_WIN)  - 1
+#define PCI_IO1_START   PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN) 
+#define PCI_IO1_END     PCI_ADDR_START + (2* CPUTOPCI_MEM_WIN) + CPUTOPCI_IO_WIN -1
+#define PCI_IO2_START   PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN) + CPUTOPCI_IO_WIN
+#define PCI_IO2_END     PCI_ADDR_START + (2* CPUTOPCI_MEM_WIN) + (2 * CPUTOPCI_IO_WIN) -1
+
+
+struct resource rc32434_res_pci_mem1 = {
+	"PCI Mem1",
+	0x50000000,
+	0x5FFFFFFF,
+	IORESOURCE_MEM,
+	&rc32434_res_pci_mem1,
+	NULL,
+	&rc32434_res_pci_mem2
+};
+struct resource rc32434_res_pci_mem2 = {
+	"PCI Mem2",
+	0x60000000,
+	0x6FFFFFFF,
+	IORESOURCE_MEM,
+	&rc32434_res_pci_mem1,
+	NULL,
+	NULL
+};
+struct resource rc32434_res_pci_mem3 = {
+	"PCI Mem3",
+	0x18C00000,
+	0x18FFFFFF,
+	IORESOURCE_MEM,
+	&rc32434_res_pci_mem1,
+	NULL,
+	NULL
+};
+
+
+struct resource rc32434_res_pci_io1 = {
+	"PCI I/O1",
+	0x18800000,
+	0x188FFFFF,
+	IORESOURCE_IO
+};
+
+#define DIG_CLEAR ((volatile unsigned char *)0xB9000004)
+#define DIG0 ((volatile unsigned char *)0xB9000007)
+#define DIG1 ((volatile unsigned char *)0xB9000006)
+#define DIG2 ((volatile unsigned char *)0xB9000005)
+#define DIG3 ((volatile unsigned char *)0xB9000004)
+#define epldMask ((volatile unsigned char *)0xB900000d)
+
+void idt_disp_char(int i, char c)
+{
+	switch(i) {
+		case 0: *DIG0 = c; break;
+		case 1: *DIG1 = c; break;
+		case 2: *DIG2 = c; break;
+		case 3: *DIG3 = c; break;
+		default: *DIG0 = '?'; break;
+	}
+}
+
+void idt_disp_str(char *s)
+{
+	if(s == 0) {
+		char c = *DIG_CLEAR;
+	} else {
+		int i;
+		for(i = 0; i < 4; i++) {
+			if(s[i]) idt_disp_char(i, s[i]);
+		}
+	}
+}
+
+
+static void idt_machine_restart(char *command)
+{
+	printk("idt_machine_restart: command=%s\n", command);
+
+	/* just jump to the reset vector */
+
+	((void (*)(void))KSEG1ADDR(0x1FC00000u))();
+}
+
+static void idt_machine_halt(void)
+{
+	printk("idt_machine_halt:  halted\n");
+	for(;;) continue;
+}
+
+static void idt_machine_power_off(void)
+{
+	printk("idt_machine_power_off:  It is now safe to turn off the power\n");
+	for(;;) continue;
+}
+
+void __init idt_setup(void)
+{
+	unsigned int pciCntlVal;
+//	char* argptr;
+	idt_disp_str("-MT-");
+	board_time_init = rc32434_time_init;
+
+	board_timer_setup = rc32434_timer_setup;
+
+	_machine_restart = idt_machine_restart;
+	_machine_halt = idt_machine_halt;
+	_machine_power_off = idt_machine_power_off;
+	set_io_port_base(KSEG1);
+
+	ioport_resource.start = rc32434_res_pci_io1.start;
+	ioport_resource.end = rc32434_res_pci_io1.end;
+	iomem_resource.start = rc32434_res_pci_mem1.start;
+	iomem_resource.end = rc32434_res_pci_mem2.end;
+	write_32bit_cp0_register(CP0_WIRED, 0);
+ /* Disable the IP bus error for PCI scaning */
+	pciCntlVal=rc32434_pci->pcic;
+	pciCntlVal &= 0xFFFFFF7;
+	rc32434_pci->pcic = pciCntlVal;
+#ifdef CONFIG_PCI
+/* Enable PCI interrupts in EPLD Mask register */	
+	*epldMask = 0x0;		
+	*(epldMask + 1) = 0x0;		
+	rc32434_pcibridge_init();
+#endif
+
+}
+
+int page_is_ram(unsigned long pagenr)
+{
+	return 1;
+}
+
+const char *get_system_type(void)
+{
+	return "MIPS IDT32434";
+}
diff -uNrb linux-2.4.30/arch/mips/rc32434/79EB434/time.c linux/arch/mips/rc32434/79EB434/time.c
--- linux-2.4.30/arch/mips/rc32434/79EB434/time.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/79EB434/time.c	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,124 @@
+/*
+****************************************************************************
+* Carsten Langgaard, carstenl@mips.com
+* Copyright (C) 1999,2000 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
+*  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.
+*
+****************************************************************************
+*
+* Setting up the clock on the MIPS boards.
+*
+****************************************************************************
+* P. Sadik Oct 10, 2003
+*
+* Started change log.
+* mips_counter_frequency is now calculated at run time, based on idt_cpu_freq.
+* Code cleanup
+****************************************************************************
+*/
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mc146818rtc.h>
+#include <linux/irq.h>
+#include <linux/timex.h>
+
+#include <asm/mipsregs.h>
+#include <asm/ptrace.h>
+#include <asm/debug.h>
+#include <asm/rc32434/rc32434.h>
+
+static unsigned long r4k_offset; /* Amount to incr compare reg each time */
+static unsigned long r4k_cur;    /* What counter should be at next timer irq */
+extern void ll_timer_interrupt(int irq, struct pt_regs *regs);
+extern unsigned int mips_hpt_frequency;
+extern unsigned int idt_cpu_freq;
+#if defined(CONFIG_IDT_79EB434) && defined(CONFIG_MIPS_RTC)
+extern void rtc_ds1553_init(void);
+#endif
+
+
+#define read_32bit_cp0_register(source)                         \
+({ int __res;                                                   \
+        __asm__ __volatile__(                                   \
+	".set\tpush\n\t"					\
+	".set\treorder\n\t"					\
+        "mfc0\t%0,"STR(source)"\n\t"                            \
+	".set\tpop"						\
+        : "=r" (__res));                                        \
+        __res;})
+
+#define write_32bit_cp0_register(register,value)                \
+        __asm__ __volatile__(                                   \
+        "mtc0\t%0,"STR(register)"\n\t"				\
+	"nop"							\
+        : : "r" (value));
+
+
+/* 
+ * Figure out the r4k offset, the amount to increment the compare
+ * register for each time tick. There is no RTC available.
+ *
+ * The RC32434 counts at half the CPU *core* speed.
+ */
+static unsigned long __init cal_r4koff(void)
+{
+	mips_hpt_frequency = idt_cpu_freq * IDT_CLOCK_MULT / 2;
+	return (mips_hpt_frequency / HZ);
+}
+
+
+void __init rc32434_time_init(void)
+{
+        unsigned int est_freq, flags;
+
+	__save_and_cli(flags);
+
+	printk("calculating r4koff... ");
+	r4k_offset = cal_r4koff();
+	printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset);
+
+	est_freq = 2*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);
+	__restore_flags(flags);
+
+#if defined(CONFIG_IDT_79EB434) && defined(CONFIG_MIPS_RTC)
+	rtc_ds1553_init(); 
+#endif
+}
+
+void __init rc32434_timer_setup(struct irqaction *irq)
+{
+        /* we are using the cpu counter for timer interrupts */
+	setup_irq(MIPS_CPU_TIMER_IRQ, irq);
+        /* to generate the first timer interrupt */
+	r4k_cur = (read_32bit_cp0_register(CP0_COUNT) + r4k_offset);
+	write_32bit_cp0_register(CP0_COMPARE, r4k_cur);
+	/* this is the last board dependent code */
+	db_run(board_init_done_flag = 1);
+
+}
+
+
+
+
diff -uNrb linux-2.4.30/arch/mips/rc32434/rb500/Makefile linux/arch/mips/rc32434/rb500/Makefile
--- linux-2.4.30/arch/mips/rc32434/rb500/Makefile	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/rb500/Makefile	Fri Sep 24 13:40:52 2004
@@ -0,0 +1,16 @@
+#
+# Makefile for the RB500 board specific parts of the kernel
+#
+
+.S.s:
+	$(CPP) $(CFLAGS) $< -o $*.s
+.S.o:
+	$(CC) $(CFLAGS) -c $< -o $*.o
+
+O_TARGET := rb500.o
+export-objs = prom.o rb.o
+obj-y	 := irq.o prom.o time.o setup.o rbIRQ.o serial_gdb.o serial.o rb.o
+obj-$(CONFIG_PCI) += pci_fixup.o pci_ops.o
+
+include $(TOPDIR)/Rules.make
+
diff -uNrb linux-2.4.30/arch/mips/rc32434/rb500/irq.c linux/arch/mips/rc32434/rb500/irq.c
--- linux-2.4.30/arch/mips/rc32434/rb500/irq.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/rb500/irq.c	Tue Oct  5 13:37:06 2004
@@ -0,0 +1,319 @@
+/*
+ * BRIEF MODULE DESCRIPTION
+ *	RC32434 interrupt routines.
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *		stevel@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/errno.h>
+#include <linux/init.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/rc32434/rc32434.h>
+#include <asm/rc32434/gpio.h>
+
+#include <asm/irq.h>
+
+
+#define DEBUG_IRQ
+#ifdef DEBUG_IRQ
+/* note: prints function name for you */
+#define DPRINTK(fmt, args...) printk("%s: " fmt, __FUNCTION__ , ## args)
+#else
+#define DPRINTK(fmt, args...)
+#endif
+
+#ifdef CONFIG_REMOTE_DEBUG
+extern void breakpoint(void);
+#endif
+
+extern asmlinkage void rbIRQ(void);
+extern void set_debug_traps(void);
+extern irq_cpustat_t irq_stat [NR_CPUS];
+unsigned int local_bh_count[NR_CPUS];
+unsigned int local_irq_count[NR_CPUS];
+
+static unsigned int startup_irq(unsigned int irq);
+static void end_irq(unsigned int irq_nr);
+static void mask_and_ack_irq(unsigned int irq_nr);
+static void rc32434_enable_irq(unsigned int irq_nr);
+static void rc32434_disable_irq(unsigned int irq_nr);
+
+extern unsigned int do_IRQ(int irq, struct pt_regs *regs);
+extern void __init init_generic_irq(void);
+
+#ifdef CONFIG_PM
+extern void counter0_irq(int irq, void *dev_id, struct pt_regs *regs);
+#endif
+
+typedef struct {
+  u32 mask;       /* mask of valid bits in pending/mask registers */
+  volatile u32 *base_addr;
+} intr_group_t;
+
+#define RC32434_NR_IRQS  (GROUP4_IRQ_BASE + 32)
+
+
+static const intr_group_t intr_group[NUM_INTR_GROUPS] = {
+  { 0x0000efff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 0 * IC_GROUP_OFFSET) },
+  { 0x00001fff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 1 * IC_GROUP_OFFSET) },
+  { 0x00000007, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 2 * IC_GROUP_OFFSET) },
+  { 0x0003ffff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 3 * IC_GROUP_OFFSET) },
+  { 0xffffffff, (u32 *)KSEG1ADDR(IC_GROUP0_PEND + 4 * IC_GROUP_OFFSET) }
+};
+
+#define READ_PEND(base) (*(base))
+#define READ_MASK(base) (*(base + 2))
+#define WRITE_MASK(base, val) (*(base + 2) = (val))
+
+static inline int irq_to_group(unsigned int irq_nr)
+{
+  return ((irq_nr - GROUP0_IRQ_BASE) >> 5);
+}
+
+static inline int group_to_ip(unsigned int group)
+{
+  return group + 2;
+}
+
+static inline void enable_local_irq(unsigned int ip)
+{
+  int ipnum = 0x100 << ip;
+  clear_c0_cause(ipnum);
+  set_c0_status(ipnum);
+}
+
+static inline void disable_local_irq(unsigned int ip)
+{
+  int ipnum = 0x100 << ip;
+  clear_c0_status(ipnum);
+}
+
+static inline void ack_local_irq(unsigned int ip)
+{
+  int ipnum = 0x100 << ip;
+  clear_c0_cause(ipnum);
+}
+
+static void rc32434_enable_irq(unsigned int irq_nr)
+{
+  unsigned long flags;
+  int           ip = irq_nr - GROUP0_IRQ_BASE;
+  unsigned int  group, intr_bit;
+  volatile unsigned int  *addr;
+
+  save_and_cli(flags);
+  
+  if (ip < 0)
+    enable_local_irq(irq_nr);
+  else {
+    // calculate group
+    group = ip >> 5;
+    
+    // calc interrupt bit within group
+    ip -= (group << 5);
+    intr_bit = 1 << ip;
+    //  if (!intr_bit)
+    //    return;
+    
+    // first enable the IP mapped to this IRQ
+    enable_local_irq(group_to_ip(group));
+    
+    addr = intr_group[group].base_addr;
+    // unmask intr within group
+    WRITE_MASK(addr, READ_MASK(addr) & ~intr_bit);
+  }
+
+  restore_flags(flags);
+}
+
+static void rc32434_disable_irq(unsigned int irq_nr)
+{
+  unsigned long flags;
+  int           ip = irq_nr - GROUP0_IRQ_BASE;
+  unsigned int  group, intr_bit, mask;
+  volatile unsigned int  *addr;
+
+  save_and_cli(flags);
+  
+  if (ip < 0)
+    disable_local_irq(irq_nr);
+  else {
+    // calculate group
+    group = ip >> 5;
+
+    // calc interrupt bit within group
+    ip -= group << 5;
+    intr_bit = 1 << ip;
+    //    if (!intr_bit)
+    //      return;
+    
+    addr = intr_group[group].base_addr;
+    // mask intr within group
+    mask = READ_MASK(addr);
+    mask |= intr_bit;
+    WRITE_MASK(addr, mask);
+    
+    /*
+      if there are no more interrupts enabled in this
+      group, disable corresponding IP
+    */
+    if (mask == intr_group[group].mask)
+      disable_local_irq(group_to_ip(group));
+  }
+  
+  restore_flags(flags);
+}
+
+static unsigned int startup_irq(unsigned int irq_nr)
+{
+  rc32434_enable_irq(irq_nr);
+  return 0; 
+}
+
+static void shutdown_irq(unsigned int irq_nr)
+{
+  rc32434_disable_irq(irq_nr);
+  return;
+}
+
+static void mask_and_ack_irq(unsigned int irq_nr)
+{
+  rc32434_disable_irq(irq_nr);
+  ack_local_irq(group_to_ip(irq_to_group(irq_nr)));
+}
+
+static void end_irq(unsigned int irq_nr)
+{
+
+  int ip = irq_nr - GROUP0_IRQ_BASE;
+  unsigned int intr_bit, group;
+  volatile unsigned int *addr;
+
+  if (!(irq_desc[irq_nr].status & (IRQ_DISABLED | IRQ_INPROGRESS))) {
+    if (ip < 0)
+      enable_local_irq(irq_nr);
+    else {
+      group = ip >> 5;
+
+      // calc interrupt bit within group
+      ip -= (group << 5);
+      intr_bit = 1 << ip;
+      //  if (!intr_bit)
+      //    return;
+
+      if (irq_nr >= GROUP4_IRQ_BASE && irq_nr <= (GROUP4_IRQ_BASE + 13)) {
+	  gpio->gpioistat = gpio->gpioistat & ~intr_bit;
+      }
+
+      // first enable the IP mapped to this IRQ
+      enable_local_irq(group_to_ip(group));
+  
+      addr = intr_group[group].base_addr;
+      // unmask intr within group
+      WRITE_MASK(addr, READ_MASK(addr) & ~intr_bit);
+    }
+  } 
+  else {
+    printk("warning: end_irq %d did not enable (%x)\n", 
+	   irq_nr, irq_desc[irq_nr].status);
+  }
+}
+
+static struct hw_interrupt_type rc32434_irq_type = {
+  "RC32434",
+  startup_irq,
+  shutdown_irq,
+  rc32434_enable_irq,
+  rc32434_disable_irq,
+  mask_and_ack_irq,
+  end_irq,
+  NULL
+};
+
+
+void __init init_IRQ(void)
+{
+	int i;
+	unsigned long cp0_status;
+
+	printk("Initializing IRQ's: %d out of %d\n",
+	       RC32434_NR_IRQS, NR_IRQS);  
+	cp0_status = read_c0_status();
+	memset(irq_desc, 0, sizeof(irq_desc));
+	set_except_vector(0, rbIRQ);
+  
+	//  init_generic_irq();
+  
+	for (i = 0; i < RC32434_NR_IRQS; i++) {
+		irq_desc[i].status = IRQ_DISABLED;
+		irq_desc[i].action = NULL;
+		irq_desc[i].depth = 1;
+		irq_desc[i].handler = &rc32434_irq_type;
+	}
+  
+#ifdef CONFIG_REMOTE_DEBUG
+	/* If local serial I/O used for debug port, enter kgdb at once */
+	puts("Waiting for kgdb to connect...");
+	set_debug_traps();
+	breakpoint(); 
+#endif
+}
+
+/* Main Interrupt dispatcher */
+void rc32434_irqdispatch(unsigned long cp0_cause, struct pt_regs *regs)
+{
+  unsigned int ip, pend, group;
+  volatile unsigned int *addr;
+
+  if ((ip = (cp0_cause & 0x7c00))) {
+    group = 21 - rc32434_clz(ip);
+
+    addr = intr_group[group].base_addr;
+
+    pend = READ_PEND(addr);
+    pend &= ~READ_MASK(addr); // only unmasked interrupts
+    //  if (!pend)
+    //    return; // no interrupts pending in this group
+    pend = 39 - rc32434_clz(pend);
+    do_IRQ((group << 5) + pend, regs);
+    return;
+  } 
+}
diff -uNrb linux-2.4.30/arch/mips/rc32434/rb500/pci_fixup.c linux/arch/mips/rc32434/rb500/pci_fixup.c
--- linux-2.4.30/arch/mips/rc32434/rb500/pci_fixup.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/rb500/pci_fixup.c	Mon Jan 31 17:22:50 2005
@@ -0,0 +1,151 @@
+/*
+ *
+ * BRIEF MODULE DESCRIPTION
+ *	Board specific pci fixups.
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *         	stevel@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 <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/rc32434/rc32434.h>
+#include <asm/rc32434/pci.h> 
+#include <asm/rc32434/pcikorina.h> 
+
+void __init pcibios_fixup_resources(struct pci_dev *dev)    
+{
+    if (PCI_SLOT(dev->devfn) == 6 && dev->bus->number == 0) {
+ 	pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, 4);
+//	pci_write_config_byte(dev, PCI_LATENCY_TIMER, 0x4c);
+//	pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, 0x4c);
+    }
+}
+
+void __init pcibios_fixup(void)
+{
+}
+
+void __init pcibios_update_irq(struct pci_dev *dev, int irq)
+{
+	pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq);
+}
+
+static inline u8 bridge_swizzle(u8 pin, u8 slot)
+{
+	return (((pin-1) + slot) % 4) + 1;
+}
+
+static u8 __devinit pci_swizzle(struct pci_dev *dev, u8 *pinp)
+{
+        // swizzle is unnecessary as designers has messed up interupt lines
+	return PCI_SLOT(dev->devfn);
+
+	u8 pin = *pinp;
+
+	while (dev->bus->self) {	/* Move up the chain of bridges. */
+		pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn));
+		dev = dev->bus->self;
+	}
+	*pinp = pin;
+
+	return PCI_SLOT(dev->devfn);
+}
+
+static int __devinit pci_map_irq(struct pci_dev *dev, u8 slot, u8 pin)
+{
+	int irq = 0;
+
+	switch (dev->bus->number) {
+	case 0: {
+	    switch (PCI_SLOT(dev->devfn)) {
+	    case 2:
+		irq = 2;
+		break;
+	    case 3:
+		irq = 3;
+		break;
+	    case 4:
+		irq = 2;
+		break;
+	    case 5:
+		irq = 3;
+		break;
+	    case 10:
+		irq = 0;
+		break;
+	    case 11:
+		irq = 1;
+		break;
+	    }
+	    break;
+	}
+	case 1: {
+	    switch (PCI_SLOT(dev->devfn)) {
+	    case 2:
+		irq = 1;
+		break;
+	    case 3:
+		irq = 3;
+		break;
+	    case 4:
+		irq = 0;
+		break;
+	    case 5:
+		irq = 2;
+		break;
+	    case 6:
+		irq = 1;
+		break;
+	    case 7:
+		irq = 3;
+		break;
+	    case 8:
+		irq = 0;
+		break;
+	    case 9:
+		irq = 2;
+		break;
+	    case 10:
+		irq = 1;
+		break;
+	    case 11:
+		irq = 3;
+		break;
+	    }
+	    break;
+	}	    
+	}
+
+	irq += GROUP4_IRQ_BASE + 4;
+
+	return irq;
+}
+
+void __init pcibios_fixup_irqs(void)
+{
+	pci_fixup_irqs(pci_swizzle, pci_map_irq);
+}
diff -uNrb linux-2.4.30/arch/mips/rc32434/rb500/pci_ops.c linux/arch/mips/rc32434/rb500/pci_ops.c
--- linux-2.4.30/arch/mips/rc32434/rb500/pci_ops.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/rb500/pci_ops.c	Tue Mar 22 10:14:39 2005
@@ -0,0 +1,367 @@
+/*
+ *
+ * BRIEF MODULE DESCRIPTION
+ *	RB500 specific pci support.
+ *
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *         	stevel@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 <linux/types.h>
+#include <linux/pci.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <asm/pci_channel.h>
+#include <asm/rc32434/rc32434.h>
+#include <asm/rc32434/pci.h> 
+#include <asm/rc32434/pcikorina.h> 
+
+#define PCI_ACCESS_READ  0
+#define PCI_ACCESS_WRITE 1
+
+#undef DEBUG
+#ifdef DEBUG
+#define DBG(x...) printk(x)
+#else
+#define DBG(x...)
+#endif
+
+#ifdef __MIPSEB__
+#define SWAP_BIT 1
+#else
+#define SWAP_BIT 0
+#endif
+/* define an unsigned array for the PCI registers */
+unsigned int korinaCnfgRegs[25] = {
+				KORINA_CNFG1,
+				KORINA_CNFG2,
+				KORINA_CNFG3,
+				KORINA_CNFG4,	
+				KORINA_CNFG5,
+				KORINA_CNFG6,
+				KORINA_CNFG7,
+				KORINA_CNFG8,
+				KORINA_CNFG9,
+				KORINA_CNFG10,
+				KORINA_CNFG11,
+				KORINA_CNFG12,
+				KORINA_CNFG13,
+				KORINA_CNFG14,
+				KORINA_CNFG15,
+				KORINA_CNFG16,
+				KORINA_CNFG17,
+				KORINA_CNFG18,
+				KORINA_CNFG19,
+				KORINA_CNFG20,
+				KORINA_CNFG21,				
+				KORINA_CNFG22,
+				KORINA_CNFG23,
+				KORINA_CNFG24};
+unsigned int pciConfigAddr;/*used for writing pci config values */
+int	     loopCount    ;/*used for the loop */
+unsigned int dummyRead    ;/*used to flush CPU write buffers */
+extern struct resource rc32434_res_pci_io1;
+extern struct resource rc32434_res_pci_mem1;
+
+extern char * __init prom_getcmdline(void);
+
+#define PCI_CFG_SET(bus, slot, func, off) \
+	(rc32434_pci->pcicfga = (0x80000000 | ((bus) << 16) | ((slot)<<11) | \
+			    ((func)<<8) | (off)))
+
+static int
+config_access(u32 type, u32 bus, u8 devfn, u32 where, u32 *data)
+{
+	/* 
+	 * config cycles are on 4 byte boundary only
+	 */
+	u32 slot = PCI_SLOT(devfn);
+	u32 func = PCI_FUNC(devfn);
+	
+	PCI_CFG_SET(bus, slot, func, where);
+	rc32434_sync();
+
+	if (type == PCI_ACCESS_WRITE) {
+	        rc32434_pci->pcicfgd = *data;
+	}
+	else {
+		*data = rc32434_pci->pcicfgd;
+	}
+	rc32434_sync();
+
+	/*
+	 * Revisit: check for master or target abort.
+	 */
+	return 0;
+}
+
+static inline int config_write(u8 bus, u8 devfn, u8 where, u32 data)
+{
+	return config_access(PCI_ACCESS_WRITE, bus, devfn, where, &data);
+}
+
+static inline int config_read(u8 bus, u8 devfn, u8 where, u32 *data)
+{
+	return config_access(PCI_ACCESS_READ, bus, devfn, where, data);
+}
+
+/*
+ * We can't address 8 and 16 bit words directly.  Instead we have to
+ * read/write a 32bit word and mask/modify the data we actually want.
+ */
+static int
+read_config_byte (struct pci_dev *dev, int where, u8 *val)
+{
+	u32 data = 0;
+	*val = 0xff;
+
+	if (config_read(dev->bus->number, dev->devfn, where, &data))
+		return -1;
+
+	*val = (data >> ((where & 3) << 3)) & 0xff;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+static int
+read_config_word (struct pci_dev *dev, int where, u16 *val)
+{
+	u32 data = 0;
+	*val = 0xffff;
+
+	if (where & 1)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	if (config_read(dev->bus->number, dev->devfn, where, &data))
+		return -1;
+
+	*val = (data >> ((where & 3) << 3)) & 0xffff;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+read_config_dword (struct pci_dev *dev, int where, u32 *val)
+{
+	u32 data = 0;
+	*val = 0xffffffff;
+
+	if (where & 3)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+	
+	if (config_read(dev->bus->number, dev->devfn, where, &data))
+		return -1;
+
+	*val = data;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+
+static int
+write_config_byte (struct pci_dev *dev, int where, u8 val)
+{
+	u32 data = 0;
+       
+	if (config_read(dev->bus->number, dev->devfn, where, &data))
+		return -1;
+
+	data = (data & ~(0xff << ((where & 3) << 3))) |
+	       (val << ((where & 3) << 3));
+
+	if (config_write(dev->bus->number, dev->devfn, where, data))
+		return -1;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+write_config_word (struct pci_dev *dev, int where, u16 val)
+{
+        u32 data = 0;
+
+	if (where & 1)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+       
+        if (config_read(dev->bus->number, dev->devfn, where, &data))
+		return -1;
+
+	data = (data & ~(0xffff << ((where & 3) << 3))) | 
+	       (val << ((where & 3) << 3));
+
+	if (config_write(dev->bus->number, dev->devfn, where, data))
+	       return -1;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static int
+write_config_dword(struct pci_dev *dev, int where, u32 val)
+{
+	if (where & 3)
+		return PCIBIOS_BAD_REGISTER_NUMBER;
+
+	if (config_write(dev->bus->number, dev->devfn, where, val))
+		return -1;
+
+	return PCIBIOS_SUCCESSFUL;
+}
+
+static struct pci_ops rc32434_pci_ops = {
+	read_config_byte,
+        read_config_word,
+	read_config_dword,
+	write_config_byte,
+	write_config_word,
+	write_config_dword
+};
+
+struct pci_channel mips_pci_channels[] = {
+	{ &rc32434_pci_ops, &rc32434_res_pci_io1,
+	  &rc32434_res_pci_mem1, PCI_DEVFN(1,0), PCI_DEVFN(31,0) },
+	{ NULL, NULL, NULL, 0, 0}
+};
+
+unsigned __init int pcibios_assign_all_busses(void)
+{
+       return 1;
+}
+
+void __init rc32434_pcibridge_init(void)
+{
+
+	unsigned int pcicValue, pcicData=0;
+        unsigned int dummyRead, pciCntlVal;
+	printk("PCI: Initializing PCI\n");
+        pcicValue = rc32434_pci->pcic;
+	pcicValue = (pcicValue >> PCIM_SHFT) & PCIM_BIT_LEN;
+        if (!((pcicValue == PCIM_H_EA) ||
+	     (pcicValue == PCIM_H_IA_FIX) ||
+		(pcicValue == PCIM_H_IA_RR))) {
+		printk("PCI init error!!!\n");
+		/* Not in Host Mode, return ERROR */
+		return;
+	}
+
+        /* Enables the Idle Grant mode, Arbiter Parking */
+	pcicData |=(PCIC_igm_m|PCIC_eap_m|PCIC_en_m);
+        rc32434_pci->pcic = pcicData; /* Enable the PCI bus Interface */
+	/* Zero out the PCI status & PCI Status Mask */
+        for(;;)
+        {
+	   pcicData = rc32434_pci->pcis;
+           if (!(pcicData & PCIS_rip_m))
+                break;
+        }
+
+	rc32434_pci->pcis = 0;
+        rc32434_pci->pcism = 0xFFFFFFFF;
+	/* Zero out the PCI decoupled registers */
+	rc32434_pci->pcidac=0; /* disable PCI decoupled accesses at initialization */
+	rc32434_pci->pcidas=0; /* clear the status */
+	rc32434_pci->pcidasm=0x0000007F; /* Mask all the interrupts */
+	/* Mask PCI Messaging Interrupts */
+	rc32434_pci_msg->pciiic = 0;
+	rc32434_pci_msg->pciiim = 0xFFFFFFFF;
+	rc32434_pci_msg->pciioic = 0;
+	rc32434_pci_msg->pciioim = 0;
+
+
+        /* Setup PCILB0 as Memory Window */
+	rc32434_pci->pcilba[0].a = (unsigned int) (PCI_ADDR_START);
+
+	/* setup the PCI map address as same as the local address */
+
+	rc32434_pci->pcilba[0].m = (unsigned int) (PCI_ADDR_START);
+
+	/* Setup PCILBA1 as MEM */
+#ifdef __MIPSEB__
+	        rc32434_pci->pcilba[0].c = ( ((SIZE_16MB & 0x1f) << PCILBAC_size_b) | PCILBAC_sb_m);
+#else
+	        rc32434_pci->pcilba[0].c = ( ((SIZE_16MB & 0x1f) << PCILBAC_size_b) );
+#endif
+	dummyRead = rc32434_pci->pcilba[0].c; /* flush the CPU write Buffers */
+
+	rc32434_pci->pcilba[1].a = 0x60000000;
+    
+	rc32434_pci->pcilba[1].m = 0x60000000;
+        /* setup PCILBA2 as IO Window*/
+#ifdef __MIPSEB__
+	rc32434_pci->pcilba[1].c = (((SIZE_256MB & 0x1f) << PCILBAC_size_b )| PCILBAC_sb_m);
+#else
+	rc32434_pci->pcilba[1].c = ( ((SIZE_256MB & 0x1f) << PCILBAC_size_b));
+#endif
+  
+	dummyRead = rc32434_pci->pcilba[1].c; /* flush the CPU write Buffers */
+	rc32434_pci->pcilba[2].a = 0x18C00000;
+    
+	rc32434_pci->pcilba[2].m = 0x18FFFFFF;
+        /* setup PCILBA2 as IO Window*/
+#ifdef __MIPSEB__
+	rc32434_pci->pcilba[2].c = (((SIZE_4MB & 0x1f) << PCILBAC_size_b) | PCILBAC_sb_m );
+#else
+	rc32434_pci->pcilba[2].c = ( ((SIZE_4MB & 0x1f) << PCILBAC_size_b)  );
+#endif  
+  
+	dummyRead = rc32434_pci->pcilba[2].c; /* flush the CPU write Buffers */
+
+
+	rc32434_pci->pcilba[3].a = 0x18800000;
+
+	rc32434_pci->pcilba[3].m = 0x18800000;
+	/* Setup PCILBA3 as IO Window */
+
+#ifdef __MIPSEB__
+	rc32434_pci->pcilba[3].c = ( (((SIZE_1MB & 0x1ff) << PCILBAC_size_b) | PCILBAC_msi_m)   |  PCILBAC_sb_m);
+#else
+	rc32434_pci->pcilba[3].c = (((SIZE_1MB & 0x1ff) << PCILBAC_size_b) | PCILBAC_msi_m);
+#endif
+	dummyRead = rc32434_pci->pcilba[2].c; /* flush the CPU write Buffers */
+
+	pciConfigAddr=(unsigned int)(0x80000004);
+	for(loopCount=0;loopCount<24;loopCount++){
+		rc32434_pci->pcicfga=pciConfigAddr;
+		dummyRead=rc32434_pci->pcicfga;
+		rc32434_pci->pcicfgd = korinaCnfgRegs[loopCount];
+		dummyRead=rc32434_pci->pcicfgd;
+		pciConfigAddr += 4;
+	}
+	rc32434_pci->pcitc=(unsigned int)((PCITC_RTIMER_VAL&0xff) << PCITC_rtimer_b) |
+ \
+				((PCITC_DTIMER_VAL&0xff)<<PCITC_dtimer_b);
+
+	pciCntlVal=rc32434_pci->pcic;
+	pciCntlVal &=~(PCIC_tnr_m);
+	rc32434_pci->pcic = pciCntlVal;
+	pciCntlVal=rc32434_pci->pcic;
+
+#ifdef __MIPSEB__
+#define ENDIANNESS_BIT 1
+#else
+#define ENDIANNESS_BIT 0
+#endif
+	rc32434_sync();   
+}
+
diff -uNrb linux-2.4.30/arch/mips/rc32434/rb500/prom.c linux/arch/mips/rc32434/rb500/prom.c
--- linux-2.4.30/arch/mips/rc32434/rb500/prom.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/rb500/prom.c	Tue May  3 15:14:57 2005
@@ -0,0 +1,192 @@
+/*
+* prom.c 
+**********************************************************************
+* P . Sadik Oct 10, 2003
+*
+* Started change log
+* idt_cpu_freq is make a kernel configuration parameter
+* idt_cpu_freq is exported so that other modules can use it.
+* Code cleanup
+**********************************************************************
+* P. Sadik Oct 20, 2003
+*
+* Removed NVRAM code from here, since they are already available under
+* nvram directory.
+* Added serial port initialisation.
+**********************************************************************
+**********************************************************************
+* P. Sadik Oct 30, 2003
+*
+* Added reset_cons_port
+**********************************************************************
+*/
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/console.h>
+#include <asm/bootinfo.h>
+#include <linux/bootmem.h>
+#include <linux/ioport.h>
+#include <linux/blk.h>
+
+char arcs_cmdline[COMMAND_LINE_SIZE];
+
+#define PROM_ENTRY(x)   (0xbfc00000+((x)*8))
+extern void __init setup_serial_port(void);
+extern void cons_putc(char c);
+extern void cons_puts(char *s);
+
+void (*prom_puts)(char *str) = cons_puts;
+void (*prom_putchar)(char c) = cons_putc;
+int (*prom_getchar)(void) = (int (*)(void))PROM_ENTRY(11);
+
+unsigned int idt_cpu_freq = 132000000;
+EXPORT_SYMBOL(idt_cpu_freq);
+unsigned int board_type = 500;
+EXPORT_SYMBOL(board_type);
+unsigned int gpio_bootup_state = 0;
+EXPORT_SYMBOL(gpio_bootup_state);
+char korina_mac_address[18] = "08:00:06:05:40:01";
+EXPORT_SYMBOL(korina_mac_address);
+
+#ifdef CONFIG_MIKROTIK_RB500
+unsigned soft_reboot = 0;
+EXPORT_SYMBOL(soft_reboot);
+#endif
+
+#define SR_NMI			0X00180000      /* NMI */
+#define SERIAL_SPEED_ENTRY	0x00000001
+
+static unsigned baudrate_table[] = {
+    115200, 57600, 38400, 19200, 9600, 4800, 2400, 1200 
+};
+
+#ifdef CONFIG_REMOTE_DEBUG
+extern int remote_debug;
+#endif
+
+extern void setup_serial_port(void);
+
+void prom_console_write(struct console *con, const char *s, unsigned count)
+{
+	int i;
+
+	for(i = 0; i < count; i++, s++) {
+		if(*s == '\n') prom_putchar('\r');
+		prom_putchar(*s);
+	}
+}
+
+kdev_t prom_console_device(struct console *con)
+{
+	return MKDEV(TTY_MAJOR, 64 + con->index);
+}
+
+int prom_console_wait_key(struct console *con)
+{
+	return prom_getchar();
+}
+
+int prom_console_setup(struct console *con, char *options)
+{
+  // reset_cons_port();
+  return 0;
+}
+
+struct console prom_console_driver = {
+	"ttyS",
+	prom_console_write,		/* write */
+	NULL,				/* read */
+	prom_console_device,		/* device */
+//	prom_console_wait_key,		/* wait_key */
+	NULL,				/* unblank */
+	prom_console_setup,   		/* setup */
+	CON_PRINTBUFFER,
+	-1,
+	0,
+	NULL
+};
+
+extern unsigned long mips_machgroup;
+extern unsigned long mips_machtype;
+
+/* RB500 memory map -- please fix it */
+
+#define RAM_FIRST       0x80000400  /* Leave room for interrupt vectors */
+#define RAM_SIZE        32*1024*1024
+#define RAM_END         (0x80000000 + RAM_SIZE)     
+struct resource rc32434_res_ram = {
+	"RAM",
+	0,
+	RAM_SIZE,
+	IORESOURCE_MEM
+};
+
+#define FREQ_TAG   "HZ="
+#define GPIO_TAG   "gpio="
+#define KMAC_TAG   "kmac="
+#define BOARD_TAG  "board="
+
+void parse_soft_settings(unsigned *ptr, unsigned size);
+void parse_hard_settings(unsigned *ptr, unsigned size);
+
+int __init prom_init(int argc, char **argv, char **envp)
+{
+	unsigned i, offset = 0;
+
+#ifdef CONFIG_MIKROTIK_RB500
+	soft_reboot = read_c0_status() & SR_NMI;
+#endif
+
+	for (i = 0; (i < argc && argv[i] != NULL); i++) {
+	    printk("kparm | %s\n", argv[i]);
+	    if(strncmp(argv[i], FREQ_TAG, strlen(FREQ_TAG)) == 0) {
+		idt_cpu_freq = 
+		    simple_strtoul(argv[i] + strlen(FREQ_TAG), 0, 10);
+	    }
+	    if(strncmp(argv[i], GPIO_TAG, strlen(GPIO_TAG)) == 0) {
+		gpio_bootup_state = 
+		    simple_strtoul(argv[i] + strlen(GPIO_TAG), 0, 10);
+	    }
+	    if(strncmp(argv[i], KMAC_TAG, strlen(KMAC_TAG)) == 0) {
+		memcpy(korina_mac_address, argv[i] + strlen(KMAC_TAG), 18);
+		korina_mac_address[17] = 0;
+	    }
+	    if(strncmp(argv[i], BOARD_TAG, strlen(BOARD_TAG)) == 0) {
+		board_type = 
+		    simple_strtoul(argv[i] + strlen(BOARD_TAG), 0, 10);
+	    }
+
+	    sprintf(arcs_cmdline + offset, "%s ", argv[i]);
+	    offset += strlen(argv[i]) + 1;
+	}
+
+	/* turn on the console */
+	// register_console(&prom_console_driver); 
+
+	setup_serial_port();
+
+	/* set our arch type */
+	mips_machgroup = MACH_GROUP_MIKROTIK;
+
+	/*
+	 * give all RAM to boot allocator,
+	 * except where the kernel was loaded
+	 */
+
+	add_memory_region(0,rc32434_res_ram.end - rc32434_res_ram.start, BOOT_MEM_RAM);
+#ifdef CONFIG_REMOTE_DEBUG
+	if(strstr(arcs_cmdline, "kgdb=on")) {
+		remote_debug = 1;
+	}
+#endif
+
+	return 0;
+}
+
+void prom_free_prom_memory(void)
+{
+	printk("stubbed prom_free_prom_memory()\n");
+}
diff -uNrb linux-2.4.30/arch/mips/rc32434/rb500/rb.c linux/arch/mips/rc32434/rb500/rb.c
--- linux-2.4.30/arch/mips/rc32434/rb500/rb.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/rb500/rb.c	Fri Sep 24 13:40:52 2004
@@ -0,0 +1,103 @@
+#include <linux/module.h> /* module to be loadable (include kernel version) */
+#include <linux/miscdevice.h> /* misc_register() */
+#include <linux/kernel.h>   /* printk() */
+#include <linux/types.h>    /* size_t */
+#include <asm/uaccess.h>    /* copy_from_user() */
+#include <linux/pci.h>
+#include <asm/rc32434/rb.h>
+
+#define RBMIPS_MINOR 245
+
+#define POKE_MIPS_IOCTL 1
+
+static volatile unsigned char *devCtl3Base = 0;
+static unsigned char latchU5State = 0;
+static struct semaphore lock;
+static spinlock_t clu5Lock = SPIN_LOCK_UNLOCKED;
+
+void set434Reg(unsigned regOffs, unsigned bit, unsigned len, unsigned val) {
+    unsigned flags, data;
+    unsigned i = 0;
+    spin_lock_irqsave(&clu5Lock, flags);
+    data = *(volatile unsigned *) (IDT434_REG_BASE + regOffs);
+    for (i = 0; i != len; ++i) {
+	if (val & (1 << i)) data |= (1 << (i + bit));
+	else data &= ~(1 << (i + bit));
+    }
+    *(volatile unsigned *) (IDT434_REG_BASE + regOffs) = data;
+    spin_unlock_irqrestore(&clu5Lock, flags);
+}
+
+void changeLatchU5(unsigned char orMask, unsigned char nandMask) {
+    unsigned flags;
+    spin_lock_irqsave(&clu5Lock, flags);
+    if (!devCtl3Base) {
+        devCtl3Base = (volatile unsigned char *)
+	        KSEG1ADDR(*(volatile unsigned *) KSEG1ADDR(0x18010030));
+    }
+    latchU5State = (latchU5State | orMask) & ~nandMask;
+    *devCtl3Base = latchU5State;
+    spin_unlock_irqrestore(&clu5Lock, flags);
+}
+
+static int rbmips_open(struct inode *inode, struct file *filp)
+{
+    MOD_INC_USE_COUNT;
+    return 0;
+}
+
+static int rbmips_release(struct inode *inode, struct file *filp)
+{
+    MOD_DEC_USE_COUNT;
+    return 0;
+}
+
+static int rbmips_ioctl(struct inode *inode, struct file *file,
+	unsigned int cmd, unsigned long arg)
+{
+    switch (cmd) {
+    default: return -ENOTTY;
+    case POKE_MIPS_IOCTL: {
+	if (down_interruptible(&lock)) return -ERESTARTSYS;
+	up(&lock);
+	return 0;
+    }
+    }
+}
+
+struct file_operations rbmips_fops = {
+    owner:	THIS_MODULE,
+    open:       rbmips_open,
+    release:    rbmips_release,
+    ioctl:      rbmips_ioctl,
+};
+
+static struct miscdevice rbmips_miscdev = {
+    RBMIPS_MINOR,
+    "rbmips",
+    &rbmips_fops
+};
+
+int init_module(void)
+{
+    {
+	int result = misc_register(&rbmips_miscdev);
+	if (result < 0) {
+	    printk(KERN_WARNING "rbmips: failed to register char device\n");
+	    return result;
+	}
+    }
+    sema_init(&lock, 1);
+    changeLatchU5(LO_WPX | LO_FOFF, LO_ULED);
+    printk(KERN_INFO "rbmips module loaded\n");
+    return 0;
+}
+
+void cleanup_module(void)
+{
+    misc_deregister(&rbmips_miscdev);
+    printk(KERN_INFO "rbmips module removed\n");
+}
+
+EXPORT_SYMBOL(set434Reg);
+EXPORT_SYMBOL(changeLatchU5);
diff -uNrb linux-2.4.30/arch/mips/rc32434/rb500/rbIRQ.S linux/arch/mips/rc32434/rb500/rbIRQ.S
--- linux-2.4.30/arch/mips/rc32434/rb500/rbIRQ.S	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/rb500/rbIRQ.S	Fri Sep 24 13:40:52 2004
@@ -0,0 +1,61 @@
+/*
+ * Copyright 2001 MontaVista Software Inc.
+ * Author: stevel@mvista.com
+ *
+ * Interrupt dispatcher for RB500 board.
+ *
+ * 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 __ASSEMBLY__ 1
+
+#include <asm/asm.h>
+#include <asm/mipsregs.h>
+#include <asm/regdef.h>
+#include <asm/stackframe.h>
+
+	.text
+	.set	noreorder
+	.set	noat
+	.align	5
+	NESTED(rbIRQ, PT_SIZE, sp)
+	SAVE_ALL
+	CLI
+
+	.set	at
+	.set	noreorder
+
+	/* Get the pending interrupts */
+	mfc0    t0, CP0_CAUSE
+	nop
+			 
+	/* Isolate the allowed ones by anding the irq mask */
+	mfc0    t2, CP0_STATUS
+	move	a1, sp		/* need a nop here, hence we anticipate */
+	andi    t0, CAUSEF_IP
+	and     t0, t2
+								  
+	/* check for r4k counter/timer IRQ. */
+	
+	andi    t1, t0, CAUSEF_IP7
+	beqz    t1, 1f
+	nop
+
+	jal     ll_timer_interrupt	 
+
+	li	a0, 7
+
+	j	ret_from_irq
+	nop
+1:
+	jal	rc32434_irqdispatch
+	move	a0, t0
+	j	ret_from_irq
+	nop
+
+	END(rbIRQ)
+
+
diff -uNrb linux-2.4.30/arch/mips/rc32434/rb500/serial.c linux/arch/mips/rc32434/rb500/serial.c
--- linux-2.4.30/arch/mips/rc32434/rb500/serial.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/rb500/serial.c	Fri Sep 24 13:40:52 2004
@@ -0,0 +1,51 @@
+/*
+***********************************************************************
+* serial.c
+* Setup a serial console that is a function of board frequency.
+*
+***********************************************************************
+* P. Sadik     Oct 10, 2003
+*
+* Original form
+***********************************************************************
+* P. Sadik     Oct 20, 2003
+*
+* Changed setup_serial_port from static to extern, so that prom.c can call
+* this function directly.
+***********************************************************************
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <asm/uaccess.h>
+#include <linux/string.h>
+
+#include <linux/types.h>
+#include <linux/serial.h>
+#include <asm/serial.h>
+#include <asm/io.h>
+#include <asm/rc32434/rc32434.h>
+extern unsigned int idt_cpu_freq;
+extern int __init setup_serial_port(void)
+{
+  struct serial_struct serial_req;
+  memset(&serial_req,0,sizeof(serial_req));
+  serial_req.type = PORT_16550;
+  serial_req.baud_base =  (idt_cpu_freq / 16);
+  serial_req.iomem_base = (unsigned char *) KSEG1ADDR(RC32434_UART_BASE);
+  serial_req.irq = RC32434_UART_IRQ;
+  serial_req.iomem_reg_shift = 2;
+  serial_req.io_type= SERIAL_IO_MEM;
+  serial_req.flags= STD_COM_FLAGS;
+  serial_req.type= 3;
+
+  if (early_serial_setup(&serial_req) < 0) {
+    printk("early_serial_setup() for serial console port failed\n");
+    return(-1);
+  }
+  printk("Serial console port initialized\n");
+  return(0);
+}
diff -uNrb linux-2.4.30/arch/mips/rc32434/rb500/serial_gdb.c linux/arch/mips/rc32434/rb500/serial_gdb.c
--- linux-2.4.30/arch/mips/rc32434/rb500/serial_gdb.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/rb500/serial_gdb.c	Fri Sep 24 13:40:52 2004
@@ -0,0 +1,247 @@
+/*
+*********************************************************************
+ * serial_gdb.c - polling driver for 16550 UART in IDT RC32334
+ *
+ * Copyright (C) 2000 by Lineo, Inc.
+ * Written by Quinn Jensen (jensenq@lineo.com)
+ *
+*********************************************************************
+* P. Sadik  Oct 20, 2003
+*
+* DIVISOR is made a function of idt_cpu_freq
+*********************************************************************
+*********************************************************************
+* P. Sadik  Oct 30, 2003
+*
+* added reset_cons_port
+*********************************************************************
+ */
+#include <linux/serial_reg.h>
+
+/* set remote gdb baud rate at 115200 */
+
+#define GDB_BAUD 115200
+#define CONS_BAUD 9600
+
+extern unsigned int idt_cpu_freq;
+
+
+/* turn this on to watch the debug protocol echoed on the console port */
+#undef DEBUG_REMOTE_DEBUG
+
+#ifdef __MIPSEB__
+#define CONS_PORT 0xb8058003u
+#define GDB_PORT  0xb8058023u 
+#else
+#define CONS_PORT 0xb8058000u
+#define GDB_PORT  0xb8058020u
+#endif
+           
+volatile unsigned char *ports[2] = {
+	(volatile unsigned char *)CONS_PORT,
+	(volatile unsigned char *)GDB_PORT
+};
+
+
+void reset_gdb_port(void);
+void cons_putc(char c);
+int port_getc(int port);
+void port_putc(int port, char c);
+
+int cons_getc(void)
+{
+	return port_getc(0);
+}
+
+void cons_putc(char c)
+{
+	port_putc(0, c);
+}
+
+void cons_puts(char *s)
+{
+	while(*s) {
+		if(*s == '\n') cons_putc('\r');
+		cons_putc(*s);
+		s++;
+	}
+}
+
+void cons_do_putn(int n)
+{
+	if(n) {
+		cons_do_putn(n / 10);
+		cons_putc(n % 10 + '0');
+	}
+}
+
+void cons_putn(int n)
+{
+	if(n < 0) {
+		cons_putc('-');
+		n = -n;
+	}
+
+	if (n == 0) {
+		cons_putc('0');
+	} else {
+		cons_do_putn(n);
+	}
+}
+
+#ifdef DEBUG_REMOTE_DEBUG
+static enum {HUH, SENDING, GETTING} state;
+
+static void sent(int c)
+{
+	switch(state) {
+	case HUH:
+	case GETTING:
+		cons_puts("\nSNT ");
+		state = SENDING;
+		/* fall through */
+	case SENDING:
+		cons_putc(c);
+		break;
+	}       
+}
+
+static void got(int c)
+{
+	switch(state) {
+	case HUH:
+	case SENDING:
+		cons_puts("\nGOT ");
+		state = GETTING;
+		/* fall through */
+	case GETTING:
+		cons_putc(c);
+		break;
+	}       
+}
+#endif /* DEBUG_REMOTE */
+
+static int first = 1;
+
+int getDebugChar(void)
+{
+	int c;
+
+	if(first) reset_gdb_port();
+
+	c = port_getc(1);
+
+#ifdef DEBUG_REMOTE_DEBUG
+	got(c);
+#endif
+
+	return c;
+}
+
+int port_getc(int p)
+{
+	volatile unsigned char *port = ports[p];
+	int c;
+
+	while((*(port + UART_LSR * 4) & UART_LSR_DR) == 0) {
+		continue;
+	}       	
+
+	c = *(port + UART_RX * 4);
+
+	return c;
+}
+
+void putDebugChar(char c)
+{
+	if(first) reset_gdb_port();
+
+#ifdef DEBUG_REMOTE_DEBUG
+	sent(c);
+#endif
+	port_putc(1, c);
+}
+
+#define OK_TO_XMT (UART_LSR_TEMT | UART_LSR_THRE)
+
+void port_putc(int p, char c)
+{
+	volatile unsigned char *port = ports[p];
+	volatile unsigned char *lsr = port + UART_LSR * 4;
+
+	while((*lsr & OK_TO_XMT) != OK_TO_XMT) {
+		continue;
+	}
+
+	*(port + UART_TX * 4) = c;
+}
+
+void reset_gdb_port(void)
+{
+	volatile unsigned char *port = ports[1];
+	unsigned int DIVISOR = (idt_cpu_freq / 16 / GDB_BAUD);
+
+	first = 0;
+#if 1	
+	cons_puts("reset_gdb_port: initializing remote debug serial port (internal UART 1, ");
+	cons_putn(GDB_BAUD);
+	cons_puts("baud, MHz=");
+	cons_putn(idt_cpu_freq);
+	cons_puts(", divisor=");
+	cons_putn(DIVISOR);
+	cons_puts(")\n");
+
+	/* reset the port */
+	*(port + UART_CSR * 4) = 0;
+
+	/* clear and enable the FIFOs */
+	*(port + UART_FCR * 4) = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | 
+		UART_FCR_CLEAR_XMIT | UART_FCR_TRIGGER_14;
+
+	/* set the baud rate */
+	*(port + UART_LCR * 4) = UART_LCR_DLAB;		/* enable DLL, DLM registers */
+	*(port + UART_DLL * 4) = DIVISOR;
+	*(port + UART_DLM * 4) = DIVISOR >> 8;
+
+	/* set the line control stuff and disable DLL, DLM regs */
+
+	*(port + UART_LCR * 4) = UART_LCR_STOP | 	/* 2 stop bits */
+		UART_LCR_WLEN8;				/* 8 bit word length */
+	
+	/* leave interrupts off */
+	*(port + UART_IER * 4) = 0;
+
+	/* the modem controls don't leave the chip on this port, so leave them alone */
+	*(port + UART_MCR * 4) = 0;
+#endif
+
+}
+
+void reset_cons_port(void)
+{
+  volatile unsigned char *port = ports[0];
+  unsigned int DIVISOR = (idt_cpu_freq / 16 / CONS_BAUD);
+
+  /* reset the port */
+  *(port + UART_CSR * 4) = 0;
+
+  /* clear and enable the FIFOs */
+  *(port + UART_FCR * 4) = UART_FCR_ENABLE_FIFO | UART_FCR_CLEAR_RCVR | 
+    UART_FCR_CLEAR_XMIT | UART_FCR_TRIGGER_14;
+
+  /* set the baud rate */
+  *(port + UART_LCR * 4) = UART_LCR_DLAB;         /* enable DLL, DLM registers */
+
+  *(port + UART_DLL * 4) = DIVISOR;
+  *(port + UART_DLM * 4) = DIVISOR >> 8;
+  /* set the line control stuff and disable DLL, DLM regs */
+
+  *(port + UART_LCR * 4) = UART_LCR_STOP |        /* 2 stop bits */
+    UART_LCR_WLEN8;                         /* 8 bit word length */
+        
+  /* leave interrupts off */
+  *(port + UART_IER * 4) = 0;
+
+  /* the modem controls don't leave the chip on this port, so leave them alone */
+  *(port + UART_MCR * 4) = 0;
+}
diff -uNrb linux-2.4.30/arch/mips/rc32434/rb500/setup.c linux/arch/mips/rc32434/rb500/setup.c
--- linux-2.4.30/arch/mips/rc32434/rb500/setup.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/rb500/setup.c	Tue Feb 22 12:41:45 2005
@@ -0,0 +1,143 @@
+/*
+ * setup.c - boot time setup code
+ */
+#include <linux/init.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/irq.h>
+#include <asm/bootinfo.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <asm/mipsregs.h>
+#include <asm/pgtable.h>
+#include <linux/mc146818rtc.h>	/* for rtc_ops, we fake the RTC */
+#include <asm/reboot.h>
+#include <asm/addrspace.h>     /* for KSEG1ADDR() */
+#include <asm/rc32434/rc32434.h>
+#include <asm/rc32434/pci.h> 
+#include <asm/rc32434/pcikorina.h> 
+
+extern char * __init prom_getcmdline(void);
+
+extern void (*board_time_init)(void);
+extern void (*board_timer_setup)(struct irqaction *irq);
+extern void rc32434_time_init(void);
+extern void rc32434_timer_setup(struct irqaction *irq);
+#ifdef CONFIG_PCI
+extern void __init rc32434_pcibridge_init(void);
+#endif
+struct resource rc32434_res_pci_mem1;
+struct resource rc32434_res_pci_mem2;
+struct resource rc32434_res_pci_mem3;
+
+#define PCI_MEM1_START PCI_ADDR_START
+#define PCI_MEM1_END   PCI_ADDR_START + CPUTOPCI_MEM_WIN - 1
+#define PCI_MEM2_START PCI_ADDR_START + CPUTOPCI_MEM_WIN 
+#define PCI_MEM2_END   PCI_ADDR_START + ( 2* CPUTOPCI_MEM_WIN)  - 1
+#define PCI_IO1_START   PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN) 
+#define PCI_IO1_END     PCI_ADDR_START + (2* CPUTOPCI_MEM_WIN) + CPUTOPCI_IO_WIN -1
+#define PCI_IO2_START   PCI_ADDR_START + (2 * CPUTOPCI_MEM_WIN) + CPUTOPCI_IO_WIN
+#define PCI_IO2_END     PCI_ADDR_START + (2* CPUTOPCI_MEM_WIN) + (2 * CPUTOPCI_IO_WIN) -1
+
+
+struct resource rc32434_res_pci_mem1 = {
+	"PCI Mem1",
+	0x50000000,
+	0x5FFFFFFF,
+	IORESOURCE_MEM,
+	&rc32434_res_pci_mem1,
+	NULL,
+	&rc32434_res_pci_mem2
+};
+struct resource rc32434_res_pci_mem2 = {
+	"PCI Mem2",
+	0x60000000,
+	0x6FFFFFFF,
+	IORESOURCE_MEM,
+	&rc32434_res_pci_mem1,
+	NULL,
+	NULL
+};
+struct resource rc32434_res_pci_mem3 = {
+	"PCI Mem3",
+	0x18C00000,
+	0x18FFFFFF,
+	IORESOURCE_MEM,
+	&rc32434_res_pci_mem1,
+	NULL,
+	NULL
+};
+
+
+struct resource rc32434_res_pci_io1 = {
+	"PCI I/O1",
+	0x18800000,
+	0x188FFFFF,
+	IORESOURCE_IO
+};
+
+#define epldMask ((volatile unsigned char *)0xB900000d)
+
+static void rb_machine_restart(char *command)
+{
+	printk("rb_machine_restart: command=%s\n", command);
+
+	/* just jump to the reset vector */
+
+	* (volatile unsigned *) KSEG1ADDR(0x18008000) = 0x80000001;
+	
+	((void (*)(void))KSEG1ADDR(0x1FC00000u))();
+}
+
+static void rb_machine_halt(void)
+{
+	printk("rb_machine_halt:  halted\n");
+	for(;;) continue;
+}
+
+static void rb_machine_power_off(void)
+{
+	printk("rb_machine_power_off:  It is now safe to turn off the power\n");
+	for(;;) continue;
+}
+
+void __init rb_setup(void)
+{
+	unsigned int pciCntlVal;
+//	char* argptr;
+	board_time_init = rc32434_time_init;
+
+	board_timer_setup = rc32434_timer_setup;
+
+	_machine_restart = rb_machine_restart;
+	_machine_halt = rb_machine_halt;
+	_machine_power_off = rb_machine_power_off;
+	set_io_port_base(KSEG1);
+
+	ioport_resource.start = rc32434_res_pci_io1.start;
+	ioport_resource.end = rc32434_res_pci_io1.end;
+	iomem_resource.start = rc32434_res_pci_mem1.start;
+	iomem_resource.end = rc32434_res_pci_mem2.end;
+	write_c0_wired(0);
+ /* Disable the IP bus error for PCI scaning */
+	pciCntlVal=rc32434_pci->pcic;
+	pciCntlVal &= 0xFFFFFF7;
+	rc32434_pci->pcic = pciCntlVal;
+#ifdef CONFIG_PCI
+/* Enable PCI interrupts in EPLD Mask register */	
+	*epldMask = 0x0;		
+	*(epldMask + 1) = 0x0;		
+	rc32434_pcibridge_init();
+#endif
+
+}
+
+int page_is_ram(unsigned long pagenr)
+{
+	return 1;
+}
+
+const char *get_system_type(void)
+{
+	return "MIPS RB500";
+}
diff -uNrb linux-2.4.30/arch/mips/rc32434/rb500/time.c linux/arch/mips/rc32434/rb500/time.c
--- linux-2.4.30/arch/mips/rc32434/rb500/time.c	Thu Jan  1 03:00:00 1970
+++ linux/arch/mips/rc32434/rb500/time.c	Fri Sep 24 13:40:52 2004
@@ -0,0 +1,99 @@
+/*
+****************************************************************************
+* Carsten Langgaard, carstenl@mips.com
+* Copyright (C) 1999,2000 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
+*  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.
+*
+****************************************************************************
+*
+* Setting up the clock on the MIPS boards.
+*
+****************************************************************************
+* P. Sadik Oct 10, 2003
+*
+* Started change log.
+* mips_counter_frequency is now calculated at run time, based on idt_cpu_freq.
+* Code cleanup
+****************************************************************************
+*/
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/kernel_stat.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/mc146818rtc.h>
+#include <linux/irq.h>
+#include <linux/timex.h>
+
+#include <asm/mipsregs.h>
+#include <asm/ptrace.h>
+#include <asm/debug.h>
+#include <asm/rc32434/rc32434.h>
+
+static unsigned long r4k_offset; /* Amount to incr compare reg each time */
+static unsigned long r4k_cur;    /* What counter should be at next timer irq */
+extern void ll_timer_interrupt(int irq, struct pt_regs *regs);
+extern unsigned int mips_hpt_frequency;
+extern unsigned int idt_cpu_freq;
+
+/* 
+ * Figure out the r4k offset, the amount to increment the compare
+ * register for each time tick. There is no RTC available.
+ *
+ * The RC32434 counts at half the CPU *core* speed.
+ */
+static unsigned long __init cal_r4koff(void)
+{
+	mips_hpt_frequency = idt_cpu_freq * IDT_CLOCK_MULT / 2;
+	return (mips_hpt_frequency / HZ);
+}
+
+
+void __init rc32434_time_init(void)
+{
+        unsigned int est_freq, flags;
+
+	__save_and_cli(flags);
+
+	printk("calculating r4koff... ");
+	r4k_offset = cal_r4koff();
+	printk("%08lx(%d)\n", r4k_offset, (int) r4k_offset);
+
+	est_freq = 2*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);
+	__restore_flags(flags);
+}
+
+void __init rc32434_timer_setup(struct irqaction *irq)
+{
+        /* we are using the cpu counter for timer interrupts */
+	setup_irq(MIPS_CPU_TIMER_IRQ, irq);
+        /* to generate the first timer interrupt */
+	r4k_cur = (read_c0_count() + r4k_offset);
+	write_c0_compare(r4k_cur);
+	/* this is the last board dependent code */
+	db_run(board_init_done_flag = 1);
+
+}
+
+
+
+
diff -uNrb linux-2.4.30/drivers/block/Makefile linux/drivers/block/Makefile
--- linux-2.4.30/drivers/block/Makefile	Fri Nov 19 12:46:58 2004
+++ linux/drivers/block/Makefile	Fri Nov 19 13:44:03 2004
@@ -32,7 +32,9 @@
 obj-$(CONFIG_BLK_DEV_UMEM)	+= umem.o
 obj-$(CONFIG_BLK_DEV_NBD)	+= nbd.o
 obj-$(CONFIG_BLK_DEV_SX8)	+= sx8.o
+obj-$(CONFIG_MIKROTIK_RB500)	+= cf-mips/cf-mips.o
 
 subdir-$(CONFIG_PARIDE) += paride
+subdir-$(CONFIG_MIKROTIK_RB500) += cf-mips
 
 include $(TOPDIR)/Rules.make
diff -uNrb linux-2.4.30/drivers/block/cf-mips/Makefile linux/drivers/block/cf-mips/Makefile
--- linux-2.4.30/drivers/block/cf-mips/Makefile	Thu Jan  1 03:00:00 1970
+++ linux/drivers/block/cf-mips/Makefile	Mon Oct  4 12:44:35 2004
@@ -0,0 +1,5 @@
+O_TARGET := cf-mips.o
+
+obj-y		+= bdev.o ata.o
+
+include $(TOPDIR)/Rules.make
diff -uNrb linux-2.4.30/drivers/block/cf-mips/ata.c linux/drivers/block/cf-mips/ata.c
--- linux-2.4.30/drivers/block/cf-mips/ata.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/block/cf-mips/ata.c	Mon Mar 14 16:08:27 2005
@@ -0,0 +1,613 @@
+#include <linux/kernel.h>	/* printk() */
+#include <linux/module.h>	/* module to be loadable */
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/ioport.h>	/* request_mem_region() */
+#include <asm/io.h>		/* ioremap() */
+
+#include "ata.h"
+
+#define REQUEST_MEM_REGION 0
+#define DEBUG 0
+
+#if DEBUG
+#define DEBUGP printk
+#else
+#define DEBUGP(format, args...)
+#endif
+
+#define CF_GPIO_NUM	13
+#define IRQ_CFRDY	(8 + 4 * 32 + CF_GPIO_NUM)	/* 149 */
+
+#define GPIO_BADDR	0xb8050000
+#define GPIO_FUNC	0x00
+#define GPIO_CFG	0x04
+#define GPIO_DATA	0x08
+#define GPIO_ILEVEL	0x0c
+#define GPIO_ISTAT	0x10
+
+#define SECS	1000000		/* unit for wait_not_busy() is 1us */
+
+unsigned long cfdev_base = 0x18A10000;
+
+unsigned cf_head = 0;
+unsigned cf_cyl = 0;
+unsigned cf_spt = 0;
+unsigned cf_sectors = 0;
+static unsigned cf_block_size = 1;
+
+static void *baddr = 0;
+
+#define DBUF ((volatile void *)((unsigned long)baddr | ATA_DBUF_OFFSET))
+
+enum trans_state {
+	TS_IDLE,
+	TS_AFTER_RESET,
+	TS_READY,
+	TS_CMD,
+	TS_TRANS
+};
+
+/* local state */
+static enum trans_state tstate = TS_IDLE;
+static char *tbuf;
+static unsigned tbuf_size;
+static unsigned tlba_offset;
+static unsigned tsector_count;
+static unsigned tsectors_left;
+static int tread;
+static unsigned tcmd;
+
+static int async_mode = 0;
+static unsigned long irq_enable_time;
+#if DEBUG
+static unsigned long busy_time;
+#endif
+
+static void cf_do_tasklet(unsigned long unused);
+DECLARE_TASKLET(cf_tasklet, cf_do_tasklet, 0);
+
+static struct timer_list timeout_timer;
+
+static inline void
+wareg(u8 val, unsigned reg)
+{
+	writeb(val, (unsigned long) baddr | ATA_REG_OFFSET | reg);
+}
+
+static inline u8
+rareg(unsigned reg)
+{
+	return readb((unsigned long) baddr | ATA_REG_OFFSET | reg);
+}
+
+extern inline int
+get_gpio_bit(unsigned long offset)
+{
+	return (readl(GPIO_BADDR | offset) >> CF_GPIO_NUM) & 1;
+}
+
+extern inline void
+set_gpio_bit(int bit, unsigned long offset)
+{
+	unsigned long val = readl(GPIO_BADDR | offset);
+	if (bit)
+		val |= (1 << CF_GPIO_NUM);
+	else
+		val &= ~(1 << CF_GPIO_NUM);
+	writel(val, GPIO_BADDR | offset);
+}
+
+extern inline int
+cfrdy(void)
+{
+	return get_gpio_bit(GPIO_DATA);
+}
+
+extern inline void
+prepare_cf_irq(void)
+{
+	set_gpio_bit(1, GPIO_ILEVEL);	/* interrupt on cf ready (not busy) */
+	set_gpio_bit(0, GPIO_ISTAT);	/* clear interrupt status */
+}
+
+static int
+fix_dc_cfg(void)
+{
+	unsigned mask;
+	unsigned devc;
+	unsigned devtc;
+
+	mask = readl(CFG_DC_DEV1 | CFG_DC_DEVMASK);
+	if (!mask) {
+		printk(KERN_ERR
+		       "DEV1 in Device Controller is not mapped anywhere!\n");
+		return 0;
+	}
+	cfdev_base = readl(CFG_DC_DEV1 | CFG_DC_DEVBASE);
+	devc = readl(CFG_DC_DEV1 | CFG_DC_DEVC);
+	devtc = readl(CFG_DC_DEV1 | CFG_DC_DEVTC);
+	printk(KERN_INFO
+	       "DC DEV1 base 0x%08lx ctrl 0x%08x tctrl 0x%08x cfrdy %d\n",
+	       cfdev_base, devc, devtc, cfrdy());
+
+	return 1;
+}
+
+static int
+cf_present(void)
+{
+	/* TODO: read and configure CIS into memory mapped mode
+	 * TODO:   parse CISTPL_CONFIG on CF+ cards to get base address (0x200)
+	 * TODO:   maybe adjust power saving setting for Hitachi Microdrive
+	 */
+
+	/* setup CFRDY GPIO as input */
+	set_gpio_bit(0, GPIO_FUNC);
+	set_gpio_bit(0, GPIO_CFG);
+
+	int i;
+	for (i = 0; i < 0x10; ++i) {
+		if (rareg(i) != 0xff)
+			return 1;
+	}
+	return 0;
+}
+
+extern inline int
+is_busy(void)
+{
+	return !cfrdy();
+}
+
+static int
+wait_not_busy(int to_us, int wait_for_busy)
+{
+	int us_passed = 0;
+	if (wait_for_busy && !is_busy()) {
+		/* busy must appear within 400ns,
+		 * but it may dissapear before we see it
+		 *  => must not wait for busy in a loop
+		 */
+		ndelay(400);
+	}
+
+	do {
+		if (us_passed)
+			udelay(1);	/* never reached in async mode */
+		if (!is_busy()) {
+#if !DEBUG
+			if (us_passed > 1 * SECS) {
+#endif
+				printk(KERN_WARNING
+				       "cf-mips:   not busy ok (after %dus)"
+				       ", status 0x%02x\n",
+				       us_passed, (unsigned) rareg(ATA_REG_ST));
+#if !DEBUG
+			}
+#endif
+			return CF_TRANS_OK;
+		}
+		if (us_passed == 1 * SECS) {
+			printk(KERN_WARNING "cf-mips: wait not busy %dus..\n",
+			       to_us);
+		}
+		if (async_mode) {
+			timeout_timer.expires = jiffies + (to_us * HZ / SECS);
+			irq_enable_time = jiffies;
+			prepare_cf_irq();
+			if (is_busy()) {
+				add_timer(&timeout_timer);
+				enable_irq(IRQ_CFRDY);
+				return CF_TRANS_IN_PROGRESS;
+			}
+			continue;
+		}
+		++us_passed;
+	} while (us_passed < to_us);
+
+	printk(KERN_ERR "cf-mips:  wait not busy timeout (%dus)"
+	       ", status 0x%02x, state %d\n",
+	       to_us, (unsigned) rareg(ATA_REG_ST), tstate);
+	return CF_TRANS_FAILED;
+}
+
+static void
+cf_irq_handler(int irq, void *dev_id, struct pt_regs *regs)
+{
+	/* While tasklet has not disabled irq, irq will be retried all the time
+	 * because of ILEVEL matching GPIO pin status => deadlock.
+	 * To avoid this, we change ILEVEL to 0.
+	 */
+	set_gpio_bit(0, GPIO_ILEVEL);
+
+#if DEBUG
+	busy_time = jiffies - irq_enable_time;
+#endif
+	del_timer(&timeout_timer);
+	tasklet_schedule(&cf_tasklet);
+}
+
+static int
+do_reset(void)
+{
+	printk(KERN_INFO "cf-mips: resetting..\n");
+
+	wareg(ATA_REG_DC_SRST, ATA_REG_DC);
+	udelay(1);		/* FIXME: how long should we wait here? */
+	wareg(0, ATA_REG_DC);
+
+	return wait_not_busy(30 * SECS, 1);
+}
+
+static int
+set_multiple(void)
+{
+	if (cf_block_size <= 1)
+		return CF_TRANS_OK;
+
+	wareg(cf_block_size, ATA_REG_SC);
+	wareg(ATA_REG_DH_BASE | ATA_REG_DH_LBA, ATA_REG_DH);
+	wareg(ATA_CMD_SET_MULTIPLE, ATA_REG_CMD);
+
+	return wait_not_busy(10 * SECS, 1);
+}
+
+static int
+set_cmd(void)
+{
+	DEBUGP(KERN_INFO "cf-mips: ata cmd 0x%02x\n", tcmd);
+
+	if (tsector_count) {
+		wareg(tsector_count & 0xff, ATA_REG_SC);
+		wareg(tlba_offset & 0xff, ATA_REG_SN);
+		wareg((tlba_offset >> 8) & 0xff, ATA_REG_CL);
+		wareg((tlba_offset >> 16) & 0xff, ATA_REG_CH);
+	}
+	wareg(((tlba_offset >> 24) & 0x0f) | ATA_REG_DH_BASE | ATA_REG_DH_LBA,
+	      ATA_REG_DH);	/* select drive on all commands */
+	wareg(tcmd, ATA_REG_CMD);
+	return wait_not_busy(10 * SECS, 1);
+}
+
+static int
+do_trans(void)
+{
+	int res;
+	unsigned st;
+
+	while (tsectors_left) {
+		static int transfered;
+		transfered = 0;
+
+		st = rareg(ATA_REG_ST);
+		if (!(st & ATA_REG_ST_DRQ)) {
+			printk(KERN_ERR
+			       "cf-mips: do_trans without DRQ (status 0x%x)!\n",
+			       st);
+			if (st & ATA_REG_ST_ERR) {
+				int errId = rareg(ATA_REG_ERR);
+				printk(KERN_ERR
+				       "cf-mips: %s error, status 0x%x, errid 0x%x\n",
+				       (tread ? "read" : "write"), st, errId);
+			}
+			return CF_TRANS_FAILED;
+		}
+		do {
+			unsigned tcnt = CF_SECT_SIZE / 4;
+			u32 *qbuf;
+			if (tbuf_size == 0)
+				tbuf = cf_get_next_buf(&tbuf_size);
+			qbuf = (u32 *)tbuf;
+			if (tread) {
+				while (tcnt) {
+					*qbuf = *(volatile u32 *)DBUF;
+					++qbuf;
+					--tcnt;
+				}
+			}
+			else {
+				while (tcnt) {
+					*(volatile u32 *)DBUF = *qbuf;
+					++qbuf;
+					--tcnt;
+				}
+			}
+
+			--tsectors_left;
+			tbuf += CF_SECT_SIZE;
+			tbuf_size -= CF_SECT_SIZE;
+			++transfered;
+		} while (transfered != cf_block_size && tsectors_left > 0);
+
+		res = wait_not_busy(10 * SECS, 1);
+		if (res != CF_TRANS_OK)
+			return res;
+	};
+
+	st = rareg(ATA_REG_ST);
+	if (st & (ATA_REG_ST_DRQ | ATA_REG_ST_DWF | ATA_REG_ST_ERR)) {
+		if (st & ATA_REG_ST_DRQ) {
+			printk(KERN_ERR
+			       "cf-mips: DRQ after all %d sectors are %s"
+			       ", status 0x%x\n",
+			       tsector_count, (tread ? "read" : "written"), st);
+		} else if (st & ATA_REG_ST_DWF) {
+			printk(KERN_ERR "cf-mips: write fault, status 0x%x\n",
+			       st);
+		} else {
+			int errId = rareg(ATA_REG_ERR);
+			printk(KERN_ERR
+			       "cf-mips: %s error, status 0x%x, errid 0x%x\n",
+			       (tread ? "read" : "write"), st, errId);
+		}
+		return CF_TRANS_FAILED;
+	}
+	return CF_TRANS_OK;
+}
+
+static int
+cf_do_state(void)
+{
+	int res;
+	switch (tstate) {	/* fall through everywhere */
+	case TS_IDLE:
+		tstate = TS_READY;
+		if (is_busy()) {
+			tstate = TS_AFTER_RESET;
+			res = do_reset();
+			if (res != CF_TRANS_OK)
+				break;
+		}
+	case TS_AFTER_RESET:
+		if (tstate == TS_AFTER_RESET) {
+			tstate = TS_READY;
+			res = set_multiple();
+			if (res != CF_TRANS_OK)
+				break;
+		}
+	case TS_READY:
+		tstate = TS_CMD;
+		res = set_cmd();
+		if (res != CF_TRANS_OK)
+			break;;
+	case TS_CMD:
+		tstate = TS_TRANS;
+	case TS_TRANS:
+		res = do_trans();
+		break;
+	default:
+		printk(KERN_ERR "cf-mips: BUG: unknown tstate %d\n", tstate);
+		return CF_TRANS_FAILED;
+	}
+	if (res != CF_TRANS_IN_PROGRESS)
+		tstate = TS_IDLE;
+	return res;
+}
+
+static void
+cf_do_tasklet(unsigned long unused)
+{
+	int res;
+
+	if (tstate == TS_IDLE)
+		return;		/* can happen when irq is first registered */
+	disable_irq(IRQ_CFRDY);
+
+#if DEBUG
+	DEBUGP(KERN_WARNING
+	       "cf-mips:   not busy ok (after %lucs), status 0x%02x\n",
+	       busy_time, (unsigned) rareg(ATA_REG_ST));
+#endif
+
+	res = cf_do_state();
+	if (res == CF_TRANS_IN_PROGRESS)
+		return;
+	cf_async_trans_done(res);
+}
+
+static void
+cf_async_timeout(unsigned long unused)
+{
+	disable_irq(IRQ_CFRDY);
+	printk(KERN_ERR "cf-mips:  wait not busy timeout (%lucs)"
+	       ", status 0x%02x, state %d\n",
+	       jiffies - irq_enable_time, (unsigned) rareg(ATA_REG_ST), tstate);
+	tstate = TS_IDLE;
+	cf_async_trans_done(CF_TRANS_FAILED);
+}
+
+int
+cf_do_transfer(char *buf, unsigned buf_size, unsigned lba_offset,
+	       unsigned sector_count, int cmd_read)
+{
+	tbuf = buf;
+	tbuf_size = buf_size;
+	tlba_offset = lba_offset;
+	tsector_count = sector_count;
+	tsectors_left = tsector_count;
+	tread = cmd_read;
+	tcmd = (cf_block_size == 1 ?
+		(tread ? ATA_CMD_READ_SECTORS : ATA_CMD_WRITE_SECTORS) :
+		(tread ? ATA_CMD_READ_MULTIPLE : ATA_CMD_WRITE_MULTIPLE));
+
+	if (tstate != TS_IDLE) {
+		printk(KERN_ERR "cf-mips: BUG: tstate %d at cf_transfer\n",
+		       tstate);
+		tstate = TS_IDLE;
+	}
+	if (tsector_count > ATA_MAX_SECT_PER_CMD) {
+		printk(KERN_WARNING "cf-mips: sector count %u out of range\n",
+		       tsector_count);
+		return CF_TRANS_FAILED;
+	}
+	if (tlba_offset + tsector_count > cf_sectors) {
+		printk(KERN_WARNING "cf-mips: lba offset %u out of range\n",
+		       tlba_offset);
+		return CF_TRANS_FAILED;
+	}
+	return cf_do_state();
+}
+
+static int
+do_identify(void)
+{
+	u16 sbuf[CF_SECT_SIZE >> 1];
+	int res;
+
+	tbuf = (char *) sbuf;
+	tbuf_size = CF_SECT_SIZE;
+	tlba_offset = 0;
+	tsector_count = 0;
+	tsectors_left = 1;
+	tread = 1;
+	tcmd = ATA_CMD_IDENTIFY_DRIVE;
+
+	DEBUGP(KERN_INFO "cf-mips: identify drive..\n");
+	res = cf_do_state();
+	if (res == CF_TRANS_IN_PROGRESS) {
+		printk(KERN_ERR "cf-mips: BUG: async identify cmd\n");
+		return CF_TRANS_FAILED;
+	}
+	if (res != CF_TRANS_OK)
+		return 0;
+
+	cf_head = sbuf[3];
+	cf_cyl = sbuf[1];
+	cf_spt = sbuf[6];
+	cf_sectors = ((unsigned) sbuf[7] << 16) | sbuf[8];
+
+	printk(KERN_INFO
+	       "cf-mips: %s detected, C/H/S=%d/%d/%d sectors=%u (%uMB)\n",
+	       (sbuf[0] == 0x848A ? "CF card" : "ATA drive"), cf_cyl, cf_head,
+	       cf_spt, cf_sectors, cf_sectors >> 11);
+	return 1;
+}
+
+static int
+set_apm_level(unsigned level)
+{
+	if (level == ATA_APM_DISABLED) {
+		wareg(ATA_FEATURE_DISABLE_APM, ATA_REG_FEAT);
+	} else {
+		wareg(ATA_FEATURE_ENABLE_APM, ATA_REG_FEAT);
+		wareg(level, ATA_REG_SC);
+	}
+	wareg(ATA_REG_DH_BASE | ATA_REG_DH_LBA, ATA_REG_DH);
+	wareg(ATA_CMD_SET_FEATURES, ATA_REG_CMD);
+
+	if (wait_not_busy(10 * SECS, 1) != CF_TRANS_OK) {
+		printk(KERN_ERR
+		       "cf-mips: failed to set APM level %u: busy!\n", level);
+		return 0;
+	}
+	if ((rareg(ATA_REG_ST) & ATA_REG_ST_ERR) == 0) {
+		DEBUGP(KERN_INFO "cf-mips: changed APM level to %u\n", level);
+		return 1;
+	} else {
+		printk(KERN_WARNING "cf-mips: failed to change APM level to %u"
+		       ", status 0x%02x, error 0x%02x\n",
+		       level, (unsigned) rareg(ATA_REG_ST),
+		       (unsigned) rareg(ATA_REG_ERR));
+		return 1;
+	}
+}
+
+static void
+init_multiple(void)
+{
+	DEBUGP(KERN_INFO "cf-mips: detecting block size\n");
+	int res;
+
+	cf_block_size = 128;	/* max block size = 128 sectors (64KB) */
+	do {
+		wareg(cf_block_size, ATA_REG_SC);
+		wareg(ATA_REG_DH_BASE | ATA_REG_DH_LBA, ATA_REG_DH);
+		wareg(ATA_CMD_SET_MULTIPLE, ATA_REG_CMD);
+
+		res = wait_not_busy(10 * SECS, 1);
+		if (res != CF_TRANS_OK) {
+			printk(KERN_ERR
+			       "cf-mips: failed to detect block size: busy!\n");
+			cf_block_size = 1;
+			return;
+		}
+		if ((rareg(ATA_REG_ST) & ATA_REG_ST_ERR) == 0)
+			break;
+		cf_block_size /= 2;
+	} while (cf_block_size > 1);
+
+	printk(KERN_INFO "cf-mips: multiple sectors = %d\n", cf_block_size);
+}
+
+int
+cf_init(void)
+{
+	if (!fix_dc_cfg())
+		return -EINVAL;
+
+#if REQUEST_MEM_REGION
+	if (!request_mem_region(cfdev_base, CFDEV_BUF_SIZE, "cf-mips")) {
+	     printk(KERN_WARNING
+		    "cf-mips: cf_init: cf mem region 0x%x/0x%x"
+		    " is already in use\n",
+		    cfdev_base, CFDEV_BUF_SIZE);
+	     return -EBUSY;
+	}
+#endif
+	baddr = ioremap_nocache(cfdev_base, CFDEV_BUF_SIZE);
+	if (!baddr) {
+		printk(KERN_ERR
+		       "cf-mips: cf_init: ioremap for (%lx,%x) failed\n",
+		       cfdev_base, CFDEV_BUF_SIZE);
+		return -EBUSY;
+	}
+
+	if (!cf_present()) {
+		printk(KERN_WARNING "cf-mips: cf card not present\n");
+		iounmap(baddr);
+		return -ENODEV;
+	}
+
+	if (do_reset() != CF_TRANS_OK) {
+		printk(KERN_ERR "cf-mips: cf reset failed\n");
+		iounmap(baddr);
+		return -EBUSY;
+	}
+
+	if (!do_identify()) {
+		printk(KERN_ERR "cf-mips: cf identify failed\n");
+		iounmap(baddr);
+		return -EBUSY;
+	}
+
+/*	set_apm_level(ATA_APM_WITH_STANDBY); */
+	init_multiple();
+
+	init_timer(&timeout_timer);
+	timeout_timer.function = cf_async_timeout;
+	timeout_timer.data = 0;
+
+	prepare_cf_irq();
+	if (request_irq(IRQ_CFRDY, cf_irq_handler, 0, "CF Mips", NULL)) {
+		printk(KERN_ERR "cf-mips: failed to get irq\n");
+		iounmap(baddr);
+		return -EBUSY;
+	}
+	disable_irq(IRQ_CFRDY);
+	async_mode = 1;
+
+	return 0;
+}
+
+void
+cf_cleanup(void)
+{
+	iounmap(baddr);
+	free_irq(IRQ_CFRDY, NULL);
+#if REQUEST_MEM_REGION
+	release_mem_region(cfdev_base, CFDEV_BUF_SIZE);
+#endif
+}
diff -uNrb linux-2.4.30/drivers/block/cf-mips/ata.h linux/drivers/block/cf-mips/ata.h
--- linux-2.4.30/drivers/block/cf-mips/ata.h	Thu Jan  1 03:00:00 1970
+++ linux/drivers/block/cf-mips/ata.h	Thu Jan 27 16:59:49 2005
@@ -0,0 +1,76 @@
+#ifndef __CFMIPS_ATA_H__
+#define __CFMIPS_ATA_H__
+
+extern unsigned cf_head;
+extern unsigned cf_cyl;
+extern unsigned cf_spt;
+extern unsigned cf_sectors;
+
+#define CFG_DC_DEV1	0xb8010010
+#define   CFG_DC_DEVBASE	0x0
+#define   CFG_DC_DEVMASK	0x4
+#define   CFG_DC_DEVC		0x8
+#define   CFG_DC_DEVTC		0xC
+
+#define CFDEV_BUF_SIZE	0x1000
+#define ATA_CIS_OFFSET	0x200
+#define ATA_REG_OFFSET	0x800
+#define ATA_DBUF_OFFSET	0xC00
+
+#define ATA_REG_FEAT	0x1
+#define ATA_REG_SC	0x2
+#define ATA_REG_SN	0x3
+#define ATA_REG_CL	0x4
+#define ATA_REG_CH	0x5
+#define ATA_REG_DH	0x6
+#define   ATA_REG_DH_BASE	0xa0
+#define   ATA_REG_DH_LBA	0x40
+#define   ATA_REG_DH_DRV	0x10
+#define ATA_REG_CMD	0x7
+#define ATA_REG_ST	0x7
+#define   ATA_REG_ST_BUSY	0x80
+#define   ATA_REG_ST_RDY	0x40
+#define   ATA_REG_ST_DWF	0x20
+#define   ATA_REG_ST_DSC	0x10
+#define   ATA_REG_ST_DRQ	0x08
+#define   ATA_REG_ST_CORR	0x04
+#define   ATA_REG_ST_ERR	0x01
+#define ATA_REG_ERR	0xd
+#define ATA_REG_DC	0xe
+#define   ATA_REG_DC_IEN	0x02
+#define   ATA_REG_DC_SRST	0x04
+
+#define ATA_CMD_READ_SECTORS	0x20
+#define ATA_CMD_WRITE_SECTORS	0x30
+#define ATA_CMD_EXEC_DRIVE_DIAG	0x90
+#define ATA_CMD_READ_MULTIPLE	0xC4
+#define ATA_CMD_WRITE_MULTIPLE	0xC5
+#define ATA_CMD_SET_MULTIPLE	0xC6
+#define ATA_CMD_IDENTIFY_DRIVE	0xEC
+#define ATA_CMD_SET_FEATURES	0xEF
+
+#define ATA_FEATURE_ENABLE_APM	0x05
+#define ATA_FEATURE_DISABLE_APM	0x85
+#define ATA_APM_DISABLED	0x00
+#define ATA_APM_MIN_POWER	0x01
+#define ATA_APM_WITH_STANDBY	0x7f
+#define ATA_APM_WITHOUT_STANDBY	0x80
+#define ATA_APM_MAX_PERFORMANCE	0xfe
+
+#define CF_SECT_SIZE	0x200
+#define ATA_MAX_SECT_PER_CMD	0x100
+
+#define CF_TRANS_FAILED		0
+#define CF_TRANS_OK		1
+#define CF_TRANS_IN_PROGRESS	2
+
+int cf_do_transfer(char *buf, unsigned buf_size, unsigned lba_offset,
+		   unsigned sector_count, int cmd_read);
+
+int cf_init(void);
+void cf_cleanup(void);
+
+void cf_async_trans_done(int result);
+void *cf_get_next_buf(unsigned *buf_size);
+
+#endif
diff -uNrb linux-2.4.30/drivers/block/cf-mips/bdev.c linux/drivers/block/cf-mips/bdev.c
--- linux-2.4.30/drivers/block/cf-mips/bdev.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/block/cf-mips/bdev.c	Wed Mar  2 16:13:59 2005
@@ -0,0 +1,290 @@
+#include <linux/kernel.h>	/* printk() */
+#include <linux/module.h>	/* module to be loadable */
+#include <linux/init.h>
+#include <linux/time.h>
+#include <linux/wait.h>
+#include <linux/fs.h>		/* register_blkdev() */
+#include <linux/blkdev.h>	/* blk_init_queue() */
+#include <linux/blkpg.h>	/* blk_ioctl */
+#include <linux/hdreg.h>	/* hd_geometry */
+
+#include <asm/io.h>		/* ioremap() */
+#include <asm/uaccess.h>	/* copy_from_user() */
+
+#define DEBUG 0
+
+#if DEBUG
+#define DEBUGP printk
+#define DLEVEL 1
+#else
+#define DEBUGP(format, args...)
+#define DLEVEL 0
+#endif
+
+#define CF_MIPS_MAJOR 13
+#define MAJOR_NR	CF_MIPS_MAJOR
+#define CF_MAX_PART	4		/* max 16 partitions */
+
+#include <linux/blk.h>
+
+#undef DEVICE_REQUEST
+#undef DEVICE_NAME
+#undef DEVICE_NR
+#define DEVICE_REQUEST cf_request
+#define DEVICE_NAME	"xda"
+#define DEVICE_NR(dev)	0		/* one drive only */
+
+#include "ata.h"
+
+extern struct block_device_operations cf_bdops;
+extern struct gendisk cf_gendisk;
+
+static struct hd_struct cf_parts[CF_MAX_PART];
+static int cf_part_sizes[CF_MAX_PART];
+static int cf_hsect_sizes[CF_MAX_PART];
+static int cf_max_sectors[CF_MAX_PART];
+static int cf_blksize_sizes[CF_MAX_PART];
+
+static spinlock_t lock = SPIN_LOCK_UNLOCKED;
+
+volatile int cf_busy = 0;
+
+static int
+cf_transfer(const struct request *req)
+{
+	unsigned minor = MINOR(req->rq_dev);
+	struct hd_struct *hd = &cf_parts[minor];
+
+	DEBUGP(KERN_INFO "cf_transfer dev %u at 0x%lx size %ld rw %d\n",
+	       minor, req->sector, req->nr_sectors, req->cmd);
+
+	if (req->sector + req->nr_sectors > hd->nr_sects) {
+		static int count = 0;
+		if (count++ < 5)
+			printk(KERN_ERR
+			       "cf-mips: request 0x%lx past end of device %d\n",
+			       req->sector, minor);
+		return CF_TRANS_FAILED;
+	}
+
+	/* Looks good, do the transfer. */
+	switch (req->cmd) {
+	case READ:
+	case WRITE:
+		return cf_do_transfer(req->bh->b_data, req->bh->b_size,
+				      req->sector + hd->start_sect,
+				      req->nr_sectors,
+				      (req->cmd == READ ? 1 : 0));
+	default:
+		/* can't happen */
+		return CF_TRANS_FAILED;
+	}
+}
+
+static void
+cf_request(request_queue_t * q)
+{
+	int status;
+	long flags;
+
+	if (cf_busy)
+		return;
+
+	while (1) {
+		INIT_REQUEST;	/* returns when queue is empty */
+
+		/* start the transfer */
+		spin_lock_irqsave(&lock, flags);
+		status = cf_transfer(CURRENT);
+		spin_unlock_irqrestore(&lock, flags);
+
+		if (status == CF_TRANS_IN_PROGRESS) {
+			cf_busy = 1;
+			return;
+		}
+		end_request(status);
+	}
+}
+
+void *
+cf_get_next_buf(unsigned *buf_size)
+{
+	struct request *req = CURRENT;
+
+	if (!end_that_request_first(req, CF_TRANS_OK, DEVICE_NAME))
+		BUG();
+
+	*buf_size = req->bh->b_size;
+	return req->bh->b_data;
+}
+
+void
+cf_async_trans_done(int result)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&io_request_lock, flags);
+	end_request(result);
+	cf_busy = 0;
+	if (!QUEUE_EMPTY)
+		DEVICE_REQUEST(NULL);
+	spin_unlock_irqrestore(&io_request_lock, flags);
+}
+
+static int
+cf_ioctl(struct inode *inode, struct file *filp,
+	 unsigned int cmd, unsigned long arg)
+{
+	unsigned minor = MINOR(inode->i_rdev);
+
+	DEBUGP(KERN_INFO "cf_ioctl cmd %u\n", cmd);
+	switch (cmd) {
+	case BLKRRPART:	/* re-read partition table */
+		if (!capable(CAP_SYS_ADMIN))
+			return -EACCES;
+		printk(KERN_INFO "cf-mips partition check: \n");
+		register_disk(&cf_gendisk, inode->i_rdev,
+			      MINOR(inode->i_rdev) ? 1 : CF_MAX_PART,
+			      &cf_bdops, cf_sectors);
+		return 0;
+
+	case HDIO_GETGEO:{
+			struct hd_geometry geo;
+			geo.cylinders = cf_cyl;
+			geo.heads = cf_head;
+			geo.sectors = cf_spt;
+			geo.start = cf_gendisk.part[minor].start_sect;
+			if (copy_to_user((void *) arg, &geo, sizeof (geo)))
+				return -EFAULT;
+			return 0;
+		}
+	default:
+		/*
+		 * For ioctls we don't understand,
+		 * let the block layer handle them.
+		 */
+		return blk_ioctl(inode->i_rdev, cmd, arg);
+	}
+
+	return -ENOTTY;		/* unknown command */
+}
+
+static int
+cf_open(struct inode *inode, struct file *filp)
+{
+	int minor = MINOR(inode->i_rdev);
+	if (minor >= CF_MAX_PART)
+		return -ENODEV;
+	DEBUGP(KERN_INFO "cf-mips module opened, minor %d\n", minor);
+	MOD_INC_USE_COUNT;
+	/* dirty workaround to set CFRDY GPIO as an input when some other
+	   program sets it as an output */
+	*(volatile unsigned *)(0xb8050004) &= ~(1 << 13);
+	return 0;		/* success */
+}
+
+static int
+cf_release(struct inode *inode, struct file *filp)
+{
+	int minor = MINOR(inode->i_rdev);
+	DEBUGP(KERN_INFO "cf-mips module closed, minor %d\n", minor);
+	MOD_DEC_USE_COUNT;
+	return 0;
+}
+
+static struct block_device_operations cf_bdops = {
+      owner:THIS_MODULE,
+      open:cf_open,
+      release:cf_release,
+      ioctl:cf_ioctl,
+      check_media_change:NULL,
+      revalidate:0,
+};
+
+static struct gendisk cf_gendisk = {
+      major:MAJOR_NR,
+      major_name:"cf",
+      minor_shift:8,		/* only one major device */
+      max_p:CF_MAX_PART,
+      part:cf_parts,
+      sizes:cf_part_sizes,
+      nr_real:1,
+      fops:&cf_bdops,
+};
+
+int __init
+cf_mips_init(void)
+{
+	int result;
+	int i;
+
+	result = register_blkdev(MAJOR_NR, "cf-mips", &cf_bdops);
+	if (result < 0) {
+		printk(KERN_WARNING "cf-mips: can't get major %d\n", MAJOR_NR);
+		return result;
+	}
+
+	result = cf_init();
+	if (result) {
+		unregister_blkdev(MAJOR_NR, "cf-mips");
+		return result;
+	}
+
+	/* default cfg for all partitions */
+	memset(cf_parts, 0, sizeof (cf_parts[0]) * CF_MAX_PART);
+	memset(cf_part_sizes, 0, sizeof (cf_part_sizes[0]) * CF_MAX_PART);
+	for (i = 0; i < CF_MAX_PART; ++i) {
+		cf_hsect_sizes[i] = CF_SECT_SIZE;
+		cf_max_sectors[i] = ATA_MAX_SECT_PER_CMD;
+		cf_blksize_sizes[i] = BLOCK_SIZE;
+	}
+
+	/* setup info for whole disk (partition 0) */
+	cf_part_sizes[0] = cf_sectors / 2;
+	cf_parts[0].nr_sects = cf_sectors;
+
+	blk_size[MAJOR_NR] = cf_part_sizes;
+	blksize_size[MAJOR_NR] = cf_blksize_sizes;
+	max_sectors[MAJOR_NR] = cf_max_sectors;
+	hardsect_size[MAJOR_NR] = cf_hsect_sizes;
+	read_ahead[MAJOR_NR] = 8;	/* (4kB) */
+
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), DEVICE_REQUEST);
+
+	add_gendisk(&cf_gendisk);
+	printk(KERN_INFO "cf-mips partition check: \n");
+	register_disk(&cf_gendisk, MKDEV(MAJOR_NR, 0), CF_MAX_PART,
+		      &cf_bdops, cf_sectors);
+
+	printk(KERN_INFO "cf-mips module loaded\n");
+	return 0;
+}
+
+static void __exit
+cf_mips_cleanup(void)
+{
+	int i;
+
+	unregister_blkdev(MAJOR_NR, "cf-mips");
+	blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+
+	for (i = 0; i < CF_MAX_PART; ++i) {
+		invalidate_device(MKDEV(MAJOR_NR, i), 1);
+	}
+
+	blk_size[MAJOR_NR] = NULL;
+	blksize_size[MAJOR_NR] = NULL;
+	max_sectors[MAJOR_NR] = NULL;
+	hardsect_size[MAJOR_NR] = NULL;
+	read_ahead[MAJOR_NR] = 0;
+
+	del_gendisk(&cf_gendisk);
+
+	cf_cleanup();
+	printk(KERN_INFO "cf-mips module removed\n");
+}
+
+module_init(cf_mips_init);
+module_exit(cf_mips_cleanup);
+
+MODULE_LICENSE("GPL");
diff -uNrb linux-2.4.30/drivers/block/ll_rw_blk.c linux/drivers/block/ll_rw_blk.c
--- linux-2.4.30/drivers/block/ll_rw_blk.c	Fri Nov 19 12:46:58 2004
+++ linux/drivers/block/ll_rw_blk.c	Fri Nov 19 13:44:03 2004
@@ -32,6 +32,8 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 
+unsigned rootDevice[2] = { 0, 0 };
+
 /*
  * MAC Floppy IWM hooks
  */
@@ -707,6 +709,10 @@
 	unsigned int major = MAJOR(dev);
 	unsigned int index;
 
+	if (rootDevice[0] && major == rootDevice[0]
+			&& MINOR(dev) == rootDevice[1] && rw == WRITE) {
+		kstat.rootdrv_wblk += nr_sectors;
+        }
 	index = disk_index(dev);
 	if ((index >= DK_MAX_DISK) || (major >= DK_MAX_MAJOR))
 		return;
diff -uNrb linux-2.4.30/drivers/char/n_tty.c linux/drivers/char/n_tty.c
--- linux-2.4.30/drivers/char/n_tty.c	Mon Feb 14 11:26:06 2005
+++ linux/drivers/char/n_tty.c	Mon Feb 14 16:42:20 2005
@@ -389,7 +389,7 @@
 		/* opost('\a', tty); */		/* what do you think? */
 		return;
 	}
-	if (c == ERASE_CHAR(tty))
+	if (c == ERASE_CHAR(tty) || c == 8)
 		kill_type = ERASE;
 	else if (c == WERASE_CHAR(tty))
 		kill_type = WERASE;
@@ -693,7 +693,7 @@
 		}
 	}
 	if (tty->icanon) {
-		if (c == ERASE_CHAR(tty) || c == KILL_CHAR(tty) ||
+		if (c == ERASE_CHAR(tty) || c == 8 || c == KILL_CHAR(tty) ||
 		    (c == WERASE_CHAR(tty) && L_IEXTEN(tty))) {
 			eraser(c, tty);
 			return;
@@ -972,6 +972,7 @@
 			set_bit('\n', &tty->process_char_map);
 
 		if (L_ICANON(tty)) {
+			set_bit(8, &tty->process_char_map);
 			set_bit(ERASE_CHAR(tty), &tty->process_char_map);
 			set_bit(KILL_CHAR(tty), &tty->process_char_map);
 			set_bit(EOF_CHAR(tty), &tty->process_char_map);
diff -uNrb linux-2.4.30/drivers/char/pc_keyb.c linux/drivers/char/pc_keyb.c
--- linux-2.4.30/drivers/char/pc_keyb.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/char/pc_keyb.c	Tue Sep  7 16:21:36 2004
@@ -73,7 +73,7 @@
 #define kbd_controller_present()	1
 #endif
 static spinlock_t kbd_controller_lock = SPIN_LOCK_UNLOCKED;
-static unsigned char handle_kbd_event(void);
+static unsigned char handle_kbd_event(int);
 
 /* used only by send_data - set by keyboard_interrupt */
 static volatile unsigned char reply_expected;
@@ -124,7 +124,7 @@
 		 * "handle_kbd_event()" will handle any incoming events
 		 * while we wait - keypresses or mouse movement.
 		 */
-		unsigned char status = handle_kbd_event();
+		unsigned char status = handle_kbd_event(0);
 
 		if (! (status & KBD_STAT_IBF))
 			return;
@@ -484,10 +484,10 @@
  * It requires that we hold the keyboard controller
  * spinlock.
  */
-static unsigned char handle_kbd_event(void)
+static unsigned char handle_kbd_event(int intr)
 {
 	unsigned char status = kbd_read_status();
-	unsigned int work = 10000;
+	unsigned int work = intr ? 10000 : 2;
 
 	while ((--work > 0) && (status & KBD_STAT_OBF)) {
 		unsigned char scancode;
@@ -510,7 +510,7 @@
 		status = kbd_read_status();
 	}
 		
-	if (!work)
+	if (!work && intr)
 		printk(KERN_ERR "pc_keyb: controller jammed (0x%02X).\n", status);
 
 	return status;
@@ -524,7 +524,7 @@
 #endif
 
 	spin_lock_irq(&kbd_controller_lock);
-	handle_kbd_event();
+	handle_kbd_event(1);
 	spin_unlock_irq(&kbd_controller_lock);
 }
 
diff -uNrb linux-2.4.30/drivers/char/tty_io.c linux/drivers/char/tty_io.c
--- linux-2.4.30/drivers/char/tty_io.c	Mon Feb 14 11:26:06 2005
+++ linux/drivers/char/tty_io.c	Mon Feb 21 14:48:03 2005
@@ -2647,7 +2647,7 @@
 	memcpy(tty_std_termios.c_cc, INIT_C_CC, NCCS);
 	tty_std_termios.c_iflag = ICRNL | IXON;
 	tty_std_termios.c_oflag = OPOST | ONLCR;
-	tty_std_termios.c_cflag = B38400 | CS8 | CREAD | HUPCL;
+	tty_std_termios.c_cflag = B115200 | CS8 | CREAD | HUPCL;
 	tty_std_termios.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK |
 		ECHOCTL | ECHOKE | IEXTEN;
 
@@ -2878,5 +2878,8 @@
 #endif
 #ifdef CONFIG_A2232
 	a2232board_init();
+#endif
+#ifdef CONFIG_SERIAL_5120
+	serial5120_init();
 #endif
 }
diff -uNrb linux-2.4.30/drivers/mtd/Config.in linux/drivers/mtd/Config.in
--- linux-2.4.30/drivers/mtd/Config.in	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/Config.in	Fri Sep 10 15:15:04 2004
@@ -1,5 +1,5 @@
 
-# $Id: Config.in,v 1.74 2002/04/23 13:52:14 mag Exp $
+# $Id: Config.in,v 1.78 2004/08/09 18:46:03 dmarlin Exp $
 
 mainmenu_option next_comment
 comment 'Memory Technology Devices (MTD)'
@@ -11,10 +11,16 @@
    if [ "$CONFIG_MTD_DEBUG" = "y" ]; then
       int '  Debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_MTD_DEBUG_VERBOSE 0
    fi
-   dep_tristate '  MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD
+   bool '  MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD
    dep_tristate '  MTD concatenating support' CONFIG_MTD_CONCAT $CONFIG_MTD
    dep_tristate '  RedBoot partition table parsing' CONFIG_MTD_REDBOOT_PARTS $CONFIG_MTD_PARTITIONS
-   dep_tristate '  Command line partition table parsing' CONFIG_MTD_CMDLINE_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
+   if [ "$CONFIG_MTD_PARTITIONS" = "y" ]; then
+      bool '  Command line partition table parsing' CONFIG_MTD_CMDLINE_PARTS 
+   fi
    if [ "$CONFIG_ARM" = "y" ]; then
       dep_tristate '  ARM Firmware Suite partition parsing' CONFIG_MTD_AFS_PARTS $CONFIG_MTD_PARTITIONS
    fi
@@ -30,6 +36,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 -uNrb linux-2.4.30/drivers/mtd/Makefile linux/drivers/mtd/Makefile
--- linux-2.4.30/drivers/mtd/Makefile	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/Makefile	Fri Sep 10 15:15:04 2004
@@ -1,66 +1,54 @@
 #
-# Makefile for the memory technology device drivers.
+# linux/drivers/Makefile.24
+# Makefile for obsolete kernels.
 #
-# 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)
-
-#                       *** BIG UGLY NOTE ***
-#
-# The shiny new inter_module_xxx has introduced yet another ugly link
-# order dependency, which I'd previously taken great care to avoid.
-# We now have to ensure that the chip drivers are initialised before the
-# map drivers, and that the doc200[01] drivers are initialised before
-# docprobe.
-#
-# We'll hopefully merge the doc200[01] drivers and docprobe back into
-# a single driver some time soon, but the CFI drivers are going to have
-# to stay like that.
-#
-# Urgh.
-# 
-# dwmw2 21/11/0
+# $Id: Makefile.24,v 1.3 2004/08/11 14:45:53 dmarlin Exp $
 
 # Core functionality.
-obj-$(CONFIG_MTD)		+= mtdcore.o
+mtd-y				:= mtdcore.o
+mtd-$(CONFIG_MTD_PARTITIONS)	+= mtdpart.o
+obj-$(CONFIG_MTD)		+= $(mtd-y)
+
 obj-$(CONFIG_MTD_CONCAT)	+= mtdconcat.o
-obj-$(CONFIG_MTD_PARTITIONS)	+= mtdpart.o
 obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
 obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
 obj-$(CONFIG_MTD_AFS_PARTS)	+= afs.o
 
 # 'Users' - code which presents functionality to userspace.
 obj-$(CONFIG_MTD_CHAR)		+= mtdchar.o
-obj-$(CONFIG_MTD_BLOCK)		+= mtdblock.o
-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-24.o
+obj-$(CONFIG_MTD_BLOCK_RO)      += mtdblock_ro.o mtd_blkdevs-24.o
+obj-$(CONFIG_FTL)               += ftl.o mtd_blkdevs-24.o
+obj-$(CONFIG_NFTL)              += nftl.o mtd_blkdevs-24.o
+obj-$(CONFIG_INFTL)             += inftl.o mtd_blkdevs-24.o
 
 nftl-objs	:= nftlcore.o nftlmount.o
+inftl-objs              := inftlcore.o inftlmount.o
+
+export-objs	:= mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o \
+		   mtdconcat.o mtd_blkdevs-24.o
+
+mtd_blkdevs-objs := mtd_blkdevs-24.o
+
+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-24.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)
 
diff -uNrb linux-2.4.30/drivers/mtd/Makefile.common linux/drivers/mtd/Makefile.common
--- linux-2.4.30/drivers/mtd/Makefile.common	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/Makefile.common	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,27 @@
+#
+# Makefile for the memory technology device drivers.
+#
+# $Id: Makefile.common,v 1.5 2004/08/10 20:51:49 dwmw2 Exp $
+
+# Core functionality.
+mtd-y				:= mtdcore.o
+mtd-$(CONFIG_MTD_PARTITIONS)	+= mtdpart.o
+obj-$(CONFIG_MTD)		+= $(mtd-y)
+
+obj-$(CONFIG_MTD_CONCAT)	+= mtdconcat.o
+obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o
+obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o
+obj-$(CONFIG_MTD_AFS_PARTS)	+= afs.o
+
+# 'Users' - code which presents functionality to userspace.
+obj-$(CONFIG_MTD_CHAR)		+= mtdchar.o
+obj-$(CONFIG_MTD_BLOCK)		+= mtdblock.o mtd_blkdevs.o
+obj-$(CONFIG_MTD_BLOCK_RO)	+= mtdblock_ro.o mtd_blkdevs.o
+obj-$(CONFIG_FTL)		+= ftl.o mtd_blkdevs.o
+obj-$(CONFIG_NFTL)		+= nftl.o mtd_blkdevs.o
+obj-$(CONFIG_INFTL)		+= inftl.o mtd_blkdevs.o
+
+nftl-objs		:= nftlcore.o nftlmount.o
+inftl-objs		:= inftlcore.o inftlmount.o
+
+obj-y		+= chips/ maps/ devices/ nand/
diff -uNrb linux-2.4.30/drivers/mtd/afs.c linux/drivers/mtd/afs.c
--- linux-2.4.30/drivers/mtd/afs.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/afs.c	Fri Sep 10 15:15:04 2004
@@ -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)
+		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 -uNrb linux-2.4.30/drivers/mtd/chips/Config.in linux/drivers/mtd/chips/Config.in
--- linux-2.4.30/drivers/mtd/chips/Config.in	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/chips/Config.in	Fri Sep 10 15:15:04 2004
@@ -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.20 2004/08/09 18:46:03 dmarlin 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
@@ -27,11 +28,13 @@
 	 LITTLE_ENDIAN_BYTE	CONFIG_MTD_CFI_LE_BYTE_SWAP"	NO
      bool '  Specific CFI Flash geometry selection' CONFIG_MTD_CFI_GEOMETRY
      if [ "$CONFIG_MTD_CFI_GEOMETRY" = "y" ]; then
-       bool '    Support  8-bit buswidth' CONFIG_MTD_CFI_B1
-       bool '    Support 16-bit buswidth' CONFIG_MTD_CFI_B2
-       bool '    Support 32-bit buswidth' CONFIG_MTD_CFI_B4
-       bool '    Support 64-bit buswidth' CONFIG_MTD_CFI_B8
-	 if [ "$CONFIG_MTD_CFI_B1" = "y" ]; then
+       bool '    Support   8-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_1
+       bool '    Support  16-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_2
+       bool '    Support  32-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_4
+       bool '    Support  64-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_8
+       bool '    Support 128-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_16
+       bool '    Support 256-bit buswidth' CONFIG_MTD_MAP_BANK_WIDTH_32
+	 if [ "$CONFIG_MTD_MAP_BANK_WIDTH_1" = "y" ]; then
 	   define_bool CONFIG_MTD_CFI_I1 y
          else
            bool '    Support 1-chip flash interleave' CONFIG_MTD_CFI_I1
@@ -45,6 +48,20 @@
 dep_tristate '  Support for Intel/Sharp flash chips' CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_GEN_PROBE
 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
diff -uNrb linux-2.4.30/drivers/mtd/chips/Makefile linux/drivers/mtd/chips/Makefile
--- linux-2.4.30/drivers/mtd/chips/Makefile	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/chips/Makefile	Fri Sep 10 15:15:04 2004
@@ -1,31 +1,12 @@
 #
-# linux/drivers/chips/Makefile
+# linux/drivers/chips/Makefile.24
+# Makefile for obsolete kernels.
 #
-# $Id: Makefile,v 1.8 2002/01/10 20:27:40 eric Exp $
+# $Id: Makefile.24,v 1.1 2004/07/12 16:08:16 dwmw2 Exp $
 
 O_TARGET	:= chipslink.o
+export-objs	:= chipreg.o gen_probe.o cfi_util.o
 
-export-objs	:= chipreg.o gen_probe.o
-
-#                       *** BIG UGLY NOTE ***
-#
-# The removal of get_module_symbol() and replacement with
-# inter_module_register() et al has introduced a link order dependency
-# here where previously there was none.  We now have to ensure that
-# the CFI command set drivers are linked before cfi_probe.o
-
-obj-$(CONFIG_MTD)		+= chipreg.o
-obj-$(CONFIG_MTD_AMDSTD)	+= amd_flash.o 
-obj-$(CONFIG_MTD_CFI)		+= cfi_probe.o
-obj-$(CONFIG_MTD_CFI_STAA)	+= cfi_cmdset_0020.o
-obj-$(CONFIG_MTD_CFI_AMDSTD)	+= cfi_cmdset_0002.o
-obj-$(CONFIG_MTD_CFI_INTELEXT)	+= cfi_cmdset_0001.o
-obj-$(CONFIG_MTD_GEN_PROBE)	+= gen_probe.o
-obj-$(CONFIG_MTD_JEDEC)		+= jedec.o
-obj-$(CONFIG_MTD_JEDECPROBE)	+= jedec_probe.o
-obj-$(CONFIG_MTD_RAM)		+= map_ram.o
-obj-$(CONFIG_MTD_ROM)		+= map_rom.o
-obj-$(CONFIG_MTD_SHARP)		+= sharp.o
-obj-$(CONFIG_MTD_ABSENT)	+= map_absent.o
+include Makefile.common
 
 include $(TOPDIR)/Rules.make
diff -uNrb linux-2.4.30/drivers/mtd/chips/Makefile.common linux/drivers/mtd/chips/Makefile.common
--- linux-2.4.30/drivers/mtd/chips/Makefile.common	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/chips/Makefile.common	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,26 @@
+#
+# linux/drivers/chips/Makefile
+#
+# $Id: Makefile.common,v 1.4 2004/07/12 16:07:30 dwmw2 Exp $
+
+#                       *** BIG UGLY NOTE ***
+#
+# The removal of get_module_symbol() and replacement with
+# inter_module_register() et al has introduced a link order dependency
+# here where previously there was none.  We now have to ensure that
+# the CFI command set drivers are linked before gen_probe.o
+
+obj-$(CONFIG_MTD)		+= chipreg.o
+obj-$(CONFIG_MTD_AMDSTD)	+= amd_flash.o 
+obj-$(CONFIG_MTD_CFI)		+= cfi_probe.o
+obj-$(CONFIG_MTD_CFI_UTIL)	+= cfi_util.o
+obj-$(CONFIG_MTD_CFI_STAA)	+= cfi_cmdset_0020.o
+obj-$(CONFIG_MTD_CFI_AMDSTD)	+= cfi_cmdset_0002.o
+obj-$(CONFIG_MTD_CFI_INTELEXT)	+= cfi_cmdset_0001.o
+obj-$(CONFIG_MTD_GEN_PROBE)	+= gen_probe.o
+obj-$(CONFIG_MTD_JEDEC)		+= jedec.o
+obj-$(CONFIG_MTD_JEDECPROBE)	+= jedec_probe.o
+obj-$(CONFIG_MTD_RAM)		+= map_ram.o
+obj-$(CONFIG_MTD_ROM)		+= map_rom.o
+obj-$(CONFIG_MTD_SHARP)		+= sharp.o
+obj-$(CONFIG_MTD_ABSENT)	+= map_absent.o
diff -uNrb linux-2.4.30/drivers/mtd/chips/amd_flash.c linux/drivers/mtd/chips/amd_flash.c
--- linux-2.4.30/drivers/mtd/chips/amd_flash.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/chips/amd_flash.c	Fri Sep 10 15:15:04 2004
@@ -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.25 2004/08/09 13:19:43 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 }
 		}
 	} 
 	};
@@ -720,7 +718,7 @@
 		       "memory for MTD erase region info\n", map->name);
 		kfree(mtd);
 		map->fldrv_priv = NULL;
-		return 0;
+		return NULL;
 	}
 
 	reg_idx = 0;
@@ -782,8 +780,8 @@
 	map->fldrv_priv = private;
 
 	map->fldrv = &amd_flash_chipdrv;
-	MOD_INC_USE_COUNT;
 
+	__module_get(THIS_MODULE);
 	return mtd;
 }
 
@@ -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;
 			}
@@ -1309,9 +1307,7 @@
 	}
 		
 	instr->state = MTD_ERASE_DONE;
-	if (instr->callback) {
-		instr->callback(instr);
-	}
+	mtd_erase_callback(instr);
 	
 	return 0;
 }
diff -uNrb linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0001.c linux/drivers/mtd/chips/cfi_cmdset_0001.c
--- linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0001.c	Fri Nov 19 12:46:58 2004
+++ linux/drivers/mtd/chips/cfi_cmdset_0001.c	Tue Sep 28 14:38:13 2004
@@ -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.156 2004/09/17 11:45:05 eric 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,14 +30,24 @@
 #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 if set to 1
+#define FORCE_WORD_WRITE 0
 
-// debugging, turns off buffer write mode #define FORCE_WORD_WRITE
+#define MANUFACTURER_INTEL	0x0089
+#define I82802AB	0x00ad
+#define I82802AC	0x00ac
+#define MANUFACTURER_ST         0x0020
+#define M50LPW080       0x002F
 
 static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
-static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
-static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+//static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
+//static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *);
 static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
 static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
 static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *);
@@ -50,18 +61,29 @@
 
 struct mtd_info *cfi_cmdset_0001(struct map_info *, int);
 
-static struct mtd_info *cfi_intelext_setup (struct map_info *);
+static struct mtd_info *cfi_intelext_setup (struct mtd_info *);
+static int cfi_intelext_partition_fixup(struct map_info *, struct cfi_private **);
 
-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);
 
+static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
+static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr);
+#include "fwh_lock.h"
+
+
+
+/*
+ *  *********** SETUP AND PROBE BITS  ***********
+ */
+
 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 */
@@ -81,7 +103,8 @@
 	printk("     - Protection Bits:    %s\n", extp->FeatureSupport&64?"supported":"unsupported");
 	printk("     - Page-mode read:     %s\n", extp->FeatureSupport&128?"supported":"unsupported");
 	printk("     - Synchronous read:   %s\n", extp->FeatureSupport&256?"supported":"unsupported");
-	for (i=9; i<32; i++) {
+	printk("     - Simultaneous operations: %s\n", extp->FeatureSupport&512?"supported":"unsupported");
+	for (i=10; i<32; i++) {
 		if (extp->FeatureSupport & (1<<i)) 
 			printk("     - Unknown Bit %X:      supported\n", i);
 	}
@@ -102,12 +125,92 @@
 	}
 	
 	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 mtd_info *mtd, void* param)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
+
+	printk(KERN_WARNING "cfi_cmdset_0001: Suspend "
+	                    "erase on write disabled.\n");
+	extp->SuspendCmdSupport &= ~1;
+}
+#endif
+
+static void fixup_st_m28w320ct(struct mtd_info *mtd, void* param)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	
+	cfi->cfiq->BufWriteTimeoutTyp = 0;	/* Not supported */
+	cfi->cfiq->BufWriteTimeoutMax = 0;	/* Not supported */
+}
+
+static void fixup_st_m28w320cb(struct mtd_info *mtd, void* param)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	
+	/* Note this is done after the region info is endian swapped */
+	cfi->cfiq->EraseRegionInfo[1] =
+		(cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e;
+};
+
+static void fixup_use_point(struct mtd_info *mtd, void *param)
+{
+	struct map_info *map = mtd->priv;
+	if (!mtd->point && map_is_linear(map)) {
+		mtd->point   = cfi_intelext_point;
+		mtd->unpoint = cfi_intelext_unpoint;
+	}
 }
+
+static void fixup_use_write_buffers(struct mtd_info *mtd, void *param)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	if (cfi->cfiq->BufWriteTimeoutTyp) {
+		printk(KERN_INFO "Using buffer write method\n" );
+		mtd->write = cfi_intelext_write_buffers;
+	}
+}
+
+static struct cfi_fixup cfi_fixup_table[] = {
+#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE
+	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_intel_strataflash, NULL }, 
+#endif
+#if !FORCE_WORD_WRITE
+	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL },
 #endif
+	{ CFI_MFR_ST, 0x00ba, /* M28W320CT */ fixup_st_m28w320ct, NULL },
+	{ CFI_MFR_ST, 0x00bb, /* M28W320CB */ fixup_st_m28w320cb, NULL },
+	{ 0, 0, NULL, NULL }
+};
+
+static struct cfi_fixup jedec_fixup_table[] = {
+	{ MANUFACTURER_INTEL, I82802AB,   fixup_use_fwh_lock, NULL, },
+	{ MANUFACTURER_INTEL, I82802AC,   fixup_use_fwh_lock, NULL, },
+	{ MANUFACTURER_ST,    M50LPW080,  fixup_use_fwh_lock, NULL, },
+	{ 0, 0, NULL, NULL }
+};
+static struct cfi_fixup fixup_table[] = {
+	/* The CFI vendor ids and the JEDEC vendor IDs appear
+	 * to be common.  It is like the devices id's are as
+	 * well.  This table is to pick all cases where
+	 * we know that is the case.
+	 */
+	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_point, NULL },
+	{ 0, 0, NULL, NULL }
+};
 
 /* This routine is made available to other mtd code via
  * inter_module_register.  It must only be accessed through
@@ -119,8 +222,29 @@
 struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
+	struct mtd_info *mtd;
 	int i;
-	__u32 base = cfi->chips[0].start;
+
+	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+	if (!mtd) {
+		printk(KERN_ERR "Failed to allocate memory for MTD device\n");
+		return NULL;
+	}
+	memset(mtd, 0, sizeof(*mtd));
+	mtd->priv = map;
+	mtd->type = MTD_NORFLASH;
+
+	/* Fill in the default mtd operations */
+	mtd->erase   = cfi_intelext_erase_varsize;
+	mtd->read    = cfi_intelext_read;
+	mtd->write   = cfi_intelext_write_words;
+	mtd->sync    = cfi_intelext_sync;
+	mtd->lock    = cfi_intelext_lock;
+	mtd->unlock  = cfi_intelext_unlock;
+	mtd->suspend = cfi_intelext_suspend;
+	mtd->resume  = cfi_intelext_resume;
+	mtd->flags   = MTD_CAP_NORFLASH;
+	mtd->name    = map->name;
 
 	if (cfi->cfi_mode == CFI_MODE_CFI) {
 		/* 
@@ -130,33 +254,10 @@
 		 */
 		__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)
-			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);
+		extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "Intel/Sharp");
 		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);
+			kfree(mtd);
 			return NULL;
 		}
 		
@@ -165,25 +266,26 @@
 		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(mtd, cfi_fixup_table);
+			
 #ifdef DEBUG_CFI_FEATURES
 		/* Tell the user about it in lots of lovely detail */
 		cfi_tell_features(extp);
 #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;	
 	}
+	else if (cfi->cfi_mode == CFI_MODE_JEDEC) {
+		/* Apply jedec specific fixups */
+		cfi_fixup(mtd, jedec_fixup_table);
+	}
+	/* Apply generic fixups */
+	cfi_fixup(mtd, fixup_table);
 
 	for (i=0; i< cfi->numchips; i++) {
 		cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
@@ -194,30 +296,19 @@
 
 	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);
+	return cfi_intelext_setup(mtd);
 }
 
-static struct mtd_info *cfi_intelext_setup(struct map_info *map)
+static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd)
 {
+	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	struct mtd_info *mtd;
 	unsigned long offset = 0;
 	int i,j;
 	unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
 
-	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
 	//printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips);
 
-	if (!mtd) {
-		printk(KERN_ERR "Failed to allocate memory for MTD device\n");
-		goto setup_err;
-	}
-
-	memset(mtd, 0, sizeof(*mtd));
-	mtd->priv = map;
-	mtd->type = MTD_NORFLASH;
 	mtd->size = devsize * cfi->numchips;
 
 	mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips;
@@ -257,37 +348,17 @@
 		       mtd->eraseregions[i].numblocks);
 	}
 
-	/* Also select the correct geometry setup too */ 
-	mtd->erase = cfi_intelext_erase_varsize;
-	mtd->read = cfi_intelext_read;
-
-	if(map->point && map->unpoint){
-		mtd->point = do_point;
-		mtd->unpoint = do_unpoint;
-	}
-
-#ifndef FORCE_WORD_WRITE
-	if ( cfi->cfiq->BufWriteTimeoutTyp ) {
-		printk("Using buffer write method\n" );
-		mtd->write = cfi_intelext_write_buffers;
-	} else {
-#else
-	{
-#endif
-		printk("Using word write method\n" );
-		mtd->write = cfi_intelext_write_words;
-	}
+#if 0
 	mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg;
 	mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg;
-	mtd->sync = cfi_intelext_sync;
-	mtd->lock = cfi_intelext_lock;
-	mtd->unlock = cfi_intelext_unlock;
-	mtd->suspend = cfi_intelext_suspend;
-	mtd->resume = cfi_intelext_resume;
-	mtd->flags = MTD_CAP_NORFLASH;
-	map->fldrv = &cfi_intelext_chipdrv;
-	MOD_INC_USE_COUNT;
-	mtd->name = map->name;
+#endif
+
+	/* This function has the potential to distort the reality
+	   a bit and therefore should be called last. */
+	if (cfi_intelext_partition_fixup(map, &cfi) != 0)
+		goto setup_err;
+
+	__module_get(THIS_MODULE);
 	return mtd;
 
  setup_err:
@@ -297,82 +368,341 @@
 		kfree(mtd);
 	}
 	kfree(cfi->cmdset_priv);
-	kfree(cfi->cfiq);
 	return NULL;
 }
 
-static int do_point_onechip (struct map_info *map,  struct flchip *chip, loff_t adr, size_t len)
+static int cfi_intelext_partition_fixup(struct map_info *map,
+					struct cfi_private **pcfi)
 {
-	cfi_word status, status_OK;
-	unsigned long timeo;
-	DECLARE_WAITQUEUE(wait, current);
-	unsigned long cmd_addr;
-	struct cfi_private *cfi = map->fldrv_priv;
+	struct cfi_private *cfi = *pcfi;
+	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
 
-	adr += chip->start;
+	/*
+	 * Probing of multi-partition flash ships.
+	 *
+	 * This is extremely crude at the moment and should probably be
+	 * extracted entirely from the Intel extended query data instead.
+	 * Right now a L18 flash is assumed if multiple operations is
+	 * detected.
+	 *
+	 * To support multiple partitions when available, we simply arrange
+	 * for each of them to have their own flchip structure even if they
+	 * are on the same physical chip.  This means completely recreating
+	 * a new cfi_private structure right here which is a blatent code
+	 * layering violation, but this is still the least intrusive
+	 * arrangement at this point. This can be rearranged in the future
+	 * if someone feels motivated enough.  --nico
+	 */
+	if (extp && extp->FeatureSupport & (1 << 9)) {
+		struct cfi_private *newcfi;
+		struct flchip *chip;
+		struct flchip_shared *shared;
+		int numparts, partshift, numvirtchips, i, j;
 
-	/* Ensure cmd read/writes are aligned. */ 
-	cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); 
+		/*
+		 * The L18 flash memory array is divided
+		 * into multiple 8-Mbit partitions.
+		 */
+		numparts = 1 << (cfi->cfiq->DevSize - 20);
+		partshift = 20 + __ffs(cfi->interleave);
+		numvirtchips = cfi->numchips * numparts;
+
+		newcfi = kmalloc(sizeof(struct cfi_private) + numvirtchips * sizeof(struct flchip), GFP_KERNEL);
+		if (!newcfi)
+			return -ENOMEM;
+		shared = kmalloc(sizeof(struct flchip_shared) * cfi->numchips, GFP_KERNEL);
+		if (!shared) {
+			kfree(newcfi);
+			return -ENOMEM;
+		}
+		memcpy(newcfi, cfi, sizeof(struct cfi_private));
+		newcfi->numchips = numvirtchips;
+		newcfi->chipshift = partshift;
+
+		chip = &newcfi->chips[0];
+		for (i = 0; i < cfi->numchips; i++) {
+			shared[i].writing = shared[i].erasing = NULL;
+			spin_lock_init(&shared[i].lock);
+			for (j = 0; j < numparts; j++) {
+				*chip = cfi->chips[i];
+				chip->start += j << partshift;
+				chip->priv = &shared[i];
+				/* those should be reset too since
+				   they create memory references. */
+				init_waitqueue_head(&chip->wq);
+				spin_lock_init(&chip->_spinlock);
+				chip->mutex = &chip->_spinlock;
+				chip++;
+			}
+		}
 
-	/* Let's determine this according to the interleave only once */
-	status_OK = CMD(0x80);
+		printk(KERN_DEBUG "%s: %d sets of %d interleaved chips "
+				  "--> %d partitions of %#x bytes\n",
+				  map->name, cfi->numchips, cfi->interleave,
+				  newcfi->numchips, 1<<newcfi->chipshift);
+
+		map->fldrv_priv = newcfi;
+		*pcfi = newcfi;
+		kfree(cfi);
+	}
+
+	return 0;
+}
+
+/*
+ *  *********** CHIP ACCESS FUNCTIONS ***********
+ */
+
+static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode)
+{
+	DECLARE_WAITQUEUE(wait, current);
+	struct cfi_private *cfi = map->fldrv_priv;
+	map_word status, status_OK = CMD(0x80), status_PWS = CMD(0x01);
+	unsigned long timeo;
+	struct cfi_pri_intelext *cfip = cfi->cmdset_priv;
 
+ resettime:
 	timeo = jiffies + HZ;
  retry:
+	if (chip->priv && (mode == FL_WRITING || mode == FL_ERASING)) {
+		/*
+		 * OK. We have possibility for contension on the write/erase
+		 * operations which are global to the real chip and not per
+		 * partition.  So let's fight it over in the partition which
+		 * currently has authority on the operation.
+		 *
+		 * The rules are as follows:
+		 *
+		 * - any write operation must own shared->writing.
+		 *
+		 * - any erase operation must own _both_ shared->writing and
+		 *   shared->erasing.
+		 *
+		 * - contension arbitration is handled in the owner's context.
+		 *
+		 * The 'shared' struct can be read when its lock is taken.
+		 * However any writes to it can only be made when the current
+		 * owner's lock is also held.
+		 */
+		struct flchip_shared *shared = chip->priv;
+		struct flchip *contender;
+		spin_lock(&shared->lock);
+		contender = shared->writing;
+		if (contender && contender != chip) {
+			/*
+			 * The engine to perform desired operation on this
+			 * partition is already in use by someone else.
+			 * Let's fight over it in the context of the chip
+			 * currently using it.  If it is possible to suspend,
+			 * that other partition will do just that, otherwise
+			 * it'll happily send us to sleep.  In any case, when
+			 * get_chip returns success we're clear to go ahead.
+			 */
+			int ret = spin_trylock(contender->mutex);
+			spin_unlock(&shared->lock);
+			if (!ret)
+				goto retry;
+			spin_unlock(chip->mutex);
+			ret = get_chip(map, contender, contender->start, mode);
 	spin_lock(chip->mutex);
+			if (ret) {
+				spin_unlock(contender->mutex);
+				return ret;
+			}
+			timeo = jiffies + HZ;
+			spin_lock(&shared->lock);
+		}
+
+		/* We now own it */
+		shared->writing = chip;
+		if (mode == FL_ERASING)
+			shared->erasing = chip;
+		if (contender && contender != chip)
+			spin_unlock(contender->mutex);
+		spin_unlock(&shared->lock);
+	}
 
-	/* 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:
+	case FL_STATUS:
+		for (;;) {
+			status = map_read(map, adr);
+			if (map_word_andequal(map, status, status_OK, status_OK))
 		break;
 
+			/* At this point we're fine with write operations
+			   in other partitions as they don't conflict. */
+			if (chip->priv && map_word_andequal(map, status, status_PWS, status_PWS))
+				break;
+
+			if (time_after(jiffies, timeo)) {
+				printk(KERN_ERR "Waiting for chip to be ready timed out. Status %lx\n", 
+				       status.x[0]);
+				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_STATUS:
-		status = cfi_read(map, cmd_addr);
-		if ((status & status_OK) == status_OK) {
-			cfi_write(map, CMD(0xff), cmd_addr);
-			chip->state = FL_READY;
+	case FL_ERASING:
+		if (!cfip ||
+		    !(cfip->FeatureSupport & 2) ||
+		    !(mode == FL_READY || mode == FL_POINT ||
+		     (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1))))
+			goto sleep;
+
+
+		/* Erase suspend */
+		map_write(map, CMD(0xB0), adr);
+
+		/* If the flash has finished erasing, then 'erase suspend'
+		 * appears to make some (28F320) flash devices switch to
+		 * 'read' mode.  Make sure that we switch to 'read status'
+		 * mode so we get the right data. --rmk
+		 */
+		map_write(map, CMD(0x70), adr);
+		chip->oldstate = FL_ERASING;
+		chip->state = FL_ERASE_SUSPENDING;
+		chip->erase_suspended = 1;
+		for (;;) {
+			status = map_read(map, adr);
+			if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
-		}
 		
-		/* Urgh. Chip not yet ready to talk to us. */
 		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);
+				/* Urgh. Resume and pretend we weren't here.  */
+				map_write(map, CMD(0xd0), adr);
+				/* Make sure we're in 'read status' mode if it had finished */
+				map_write(map, CMD(0x70), adr);
+				chip->state = FL_ERASING;
+				chip->oldstate = FL_READY;
+				printk(KERN_ERR "Chip not ready after erase "
+				       "suspended: status = 0x%lx\n", status.x[0]);
 			return -EIO;
 		}
 
-		/* Latency issues. Drop the lock, wait a while and retry */
 		spin_unlock(chip->mutex);
 		cfi_udelay(1);
-		goto retry;
+			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;
+
+	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;
+
+	if (chip->priv) {
+		struct flchip_shared *shared = chip->priv;
+		spin_lock(&shared->lock);
+		if (shared->writing == chip) {
+			/* We own the ability to write, but we're done */
+			shared->writing = shared->erasing;
+			if (shared->writing && shared->writing != chip) {
+				/* give back ownership to who we loaned it from */
+				struct flchip *loaner = shared->writing;
+				spin_lock(loaner->mutex);
+				spin_unlock(&shared->lock);
+				spin_unlock(chip->mutex);
+				put_chip(map, loaner, loaner->start);
+				spin_lock(chip->mutex);
+				spin_unlock(loaner->mutex);
+			} else {
+				if (chip->oldstate != FL_ERASING) {
+					shared->erasing = NULL;
+					if (chip->oldstate != FL_WRITING)
+						shared->writing = NULL;
+				}
+				spin_unlock(&shared->lock);
+			}
+		}
+	}
+
+	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. */
+		map_write(map, CMD(0xd0), adr);
+		map_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 & ~(map_bankwidth(map)-1); 
+
+	spin_lock(chip->mutex);
+
+	ret = get_chip(map, chip, cmd_addr, FL_POINT);
+
+	if (!ret) {
+		if (chip->state != FL_POINT && chip->state != FL_READY)
+			map_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,12 +710,10 @@
 	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 */
@@ -418,14 +746,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 +773,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 +791,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); 
+	cmd_addr = adr & ~(map_bankwidth(map)-1); 
 
-	/* 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.
-	 * 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;
+	ret = get_chip(map, chip, cmd_addr, FL_READY);
+	if (ret) {
 				spin_unlock(chip->mutex);
-				printk(KERN_ERR "Chip not ready after erase "
-				       "suspended: status = 0x%llx\n", (__u64)status);
-				return -EIO;
+		return ret;
 			}
 			
-			spin_unlock(chip->mutex);
-			cfi_udelay(1);
-			spin_lock(chip->mutex);
-		}
+	if (chip->state != FL_POINT && chip->state != FL_READY) {
+		map_write(map, CMD(0xff), cmd_addr);
 		
-		suspended = 1;
-		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;
-	}
+	map_copy_from(map, buf, adr, len);
 
-	map->copy_from(map, buf, adr, len);
+	put_chip(map, chip, cmd_addr);
 
-	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);		
-	}
-
-	wake_up(&chip->wq);
 	spin_unlock(chip->mutex);
 	return 0;
 }
@@ -635,75 +859,57 @@
 	}
 	return ret;
 }
-
+#if 0
 static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, int base_offst, int reg_sz)
 {
 	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	struct cfi_pri_intelext *extp=cfi->cmdset_priv;
-	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=0;
-	/* Calculate which chip & protection register offset we need */
-	chip_num=((unsigned int)from/reg_sz);
-	offst=from-(reg_sz*chip_num)+base_offst;
+	chip_num = ((unsigned int)from/reg_sz);
+	offst = from - (reg_sz*chip_num)+base_offst;
 
-	while(count){
+	while (count) {
+	/* Calculate which chip & protection register offset we need */
 		
-		if(chip_num>=cfi->numchips)
+		if (chip_num >= cfi->numchips)
 			goto out;
 
-		/* Make sure that the chip is in the right state */
+		chip = &cfi->chips[chip_num];
 
-		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;
-		
-		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, 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 */
+		if (chip->state != FL_JEDEC_QUERY) {
+			map_write(map, CMD(0x90), chip->start);
+			chip->state = FL_JEDEC_QUERY;
+		}
        
-		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));
+		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;
 }
 	
@@ -715,7 +921,7 @@
 	int base_offst,reg_sz;
 	
 	/* Check that we actually have some protection registers */
-	if(!(extp->FeatureSupport&64)){
+	if(!extp || !(extp->FeatureSupport&64)){
 		printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name);
 		return 0;
 	}
@@ -734,7 +940,7 @@
 	int base_offst,reg_sz;
 	
 	/* Check that we actually have some protection registers */
-	if(!(extp->FeatureSupport&64)){
+	if(!extp || !(extp->FeatureSupport&64)){
 		printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name);
 		return 0;
 	}
@@ -744,116 +950,34 @@
 
 	return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz);
 }
+#endif
 
-
-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum)
+static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, map_word datum)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	struct cfi_pri_intelext *extp = cfi->cmdset_priv;
-	cfi_word status, status_OK;
+	map_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;
-
-	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);
+	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);
-	cfi_write(map, CMD(0x40), adr);
-	cfi_write(map, datum, adr);
+	map_write(map, CMD(0x40), adr);
+	map_write(map, datum, adr);
 	chip->state = FL_WRITING;
 
 	spin_unlock(chip->mutex);
+	INVALIDATE_CACHED_RANGE(map, adr, map_bankwidth(map));
 	cfi_udelay(chip->word_write_time);
 	spin_lock(chip->mutex);
 
@@ -862,6 +986,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);
@@ -872,14 +998,13 @@
 			continue;
 		}
 
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
 			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;
@@ -895,40 +1020,24 @@
 		chip->word_write_time--;
 		if (!chip->word_write_time)
 			chip->word_write_time++;
-	}
-	if (z > 1) 
-		chip->word_write_time++;
-
-	/* Done and happy. */
-	chip->state = FL_STATUS;
-	/* check for lock bit */
-	if (status & CMD(0x02)) {
-		/* clear status */
-		cfi_write(map, CMD(0x50), adr);
-		/* put back into read status register mode */
-		cfi_write(map, CMD(0x70), adr);
-		ret = -EROFS;
-		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 */
+	}
+	if (z > 1) 
+		chip->word_write_time++;
 
-	wake_up(&chip->wq);
+	/* Done and happy. */
+	chip->state = FL_STATUS;
+	/* check for lock bit */
+	if (map_word_bitsset(map, status, CMD(0x02))) {
+		/* clear status */
+		map_write(map, CMD(0x50), adr);
+		/* put back into read status register mode */
+		map_write(map, CMD(0x70), adr);
+		ret = -EROFS;
+	}
+ out:
+	put_chip(map, chip, adr);
 	spin_unlock(chip->mutex);
+
 	return ret;
 }
 
@@ -949,35 +1058,22 @@
 	ofs = to  - (chipnum << cfi->chipshift);
 
 	/* If it's not bus-aligned, do the first byte write */
-	if (ofs & (CFIDEV_BUSWIDTH-1)) {
-		unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1);
+	if (ofs & (map_bankwidth(map)-1)) {
+		unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1);
 		int gap = ofs - bus_ofs;
-		int i = 0, n = 0;
-		u_char tmp_buf[8];
-		cfi_word datum;
-
-		while (gap--)
-			tmp_buf[i++] = 0xff;
-		while (len && i < CFIDEV_BUSWIDTH)
-			tmp_buf[i++] = buf[n++], len--;
-		while (i < CFIDEV_BUSWIDTH)
-			tmp_buf[i++] = 0xff;
-
-		if (cfi_buswidth_is_2()) {
-			datum = *(__u16*)tmp_buf;
-		} else if (cfi_buswidth_is_4()) {
-			datum = *(__u32*)tmp_buf;
-		} else if (cfi_buswidth_is_8()) {
-			datum = *(__u64*)tmp_buf;
-		} else {
-			return -EINVAL;  /* should never happen, but be safe */
-		}
+		int n;
+		map_word datum;
+
+		n = min_t(int, len, map_bankwidth(map)-gap);
+		datum = map_word_ff(map);
+		datum = map_word_load_partial(map, datum, buf, gap, n);
 
 		ret = do_write_oneword(map, &cfi->chips[chipnum],
 					       bus_ofs, datum);
 		if (ret) 
 			return ret;
 		
+		len -= n;
 		ofs += n;
 		buf += n;
 		(*retlen) += n;
@@ -990,30 +1086,18 @@
 		}
 	}
 	
-	while(len >= CFIDEV_BUSWIDTH) {
-		cfi_word datum;
-
-		if (cfi_buswidth_is_1()) {
-			datum = *(__u8*)buf;
-		} else if (cfi_buswidth_is_2()) {
-			datum = *(__u16*)buf;
-		} else if (cfi_buswidth_is_4()) {
-			datum = *(__u32*)buf;
-		} else if (cfi_buswidth_is_8()) {
-			datum = *(__u64*)buf;
-		} else {
-			return -EINVAL;
-		}
+	while(len >= map_bankwidth(map)) {
+		map_word datum = map_word_load(map, buf);
 
 		ret = do_write_oneword(map, &cfi->chips[chipnum],
 				ofs, datum);
 		if (ret)
 			return ret;
 
-		ofs += CFIDEV_BUSWIDTH;
-		buf += CFIDEV_BUSWIDTH;
-		(*retlen) += CFIDEV_BUSWIDTH;
-		len -= CFIDEV_BUSWIDTH;
+		ofs += map_bankwidth(map);
+		buf += map_bankwidth(map);
+		(*retlen) += map_bankwidth(map);
+		len -= map_bankwidth(map);
 
 		if (ofs >> cfi->chipshift) {
 			chipnum ++; 
@@ -1023,32 +1107,18 @@
 		}
 	}
 
-	if (len & (CFIDEV_BUSWIDTH-1)) {
-		int i = 0, n = 0;
-		u_char tmp_buf[8];
-		cfi_word datum;
-
-		while (len--)
-			tmp_buf[i++] = buf[n++];
-		while (i < CFIDEV_BUSWIDTH)
-			tmp_buf[i++] = 0xff;
-
-		if (cfi_buswidth_is_2()) {
-			datum = *(__u16*)tmp_buf;
-		} else if (cfi_buswidth_is_4()) {
-			datum = *(__u32*)tmp_buf;
-		} else if (cfi_buswidth_is_8()) {
-			datum = *(__u64*)tmp_buf;
-		} else {
-			return -EINVAL;  /* should never happen, but be safe */
-		}
+	if (len & (map_bankwidth(map)-1)) {
+		map_word datum;
+
+		datum = map_word_ff(map);
+		datum = map_word_load_partial(map, datum, buf, 0, len);
 
 		ret = do_write_oneword(map, &cfi->chips[chipnum],
 					       ofs, datum);
 		if (ret) 
 			return ret;
 		
-		(*retlen) += n;
+		(*retlen) += len;
 	}
 
 	return 0;
@@ -1059,122 +1129,46 @@
 				  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;
+	map_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;
+	wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
 	adr += chip->start;
 	cmd_adr = adr & ~(wbufsize-1);
 	
 	/* Let's determine this according to the interleave only once */
 	status_OK = CMD(0x80);
 
-	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 */
-		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;
-			}
-			
+	ret = get_chip(map, chip, cmd_adr, FL_WRITING);
+	if (ret) {
 			spin_unlock(chip->mutex);
-			cfi_udelay(1);
-			spin_lock(chip->mutex);
+		return ret;
 		}
-		suspended = 1;
-		chip->state = FL_STATUS;
-		break;
 
-	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
 	   we're just pissing in the wind */
-	if (status & CMD(0x30)) {
-		printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %x). Clearing.\n", status);
-		cfi_write(map, CMD(0x50), cmd_adr);
-		cfi_write(map, CMD(0x70), cmd_adr);
+	if (chip->state != FL_STATUS)
+		map_write(map, CMD(0x70), cmd_adr);
+	status = map_read(map, cmd_adr);
+	if (map_word_bitsset(map, status, CMD(0x30))) {
+		printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %lx). Clearing.\n", status.x[0]);
+		map_write(map, CMD(0x50), cmd_adr);
+		map_write(map, CMD(0x70), cmd_adr);
 	}
+
 	ENABLE_VPP(map);
 	chip->state = FL_WRITING_TO_BUFFER;
 
 	z = 0;
 	for (;;) {
-		cfi_write(map, CMD(0xe8), cmd_adr);
+		map_write(map, CMD(0xe8), cmd_adr);
 
-		status = cfi_read(map, cmd_adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, cmd_adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 
 		spin_unlock(chip->mutex);
@@ -1183,46 +1177,47 @@
 
 		if (++z > 20) {
 			/* Argh. Not ready for write to buffer */
-			cfi_write(map, CMD(0x70), cmd_adr);
+			map_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));
+			printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx, status = %lx\n",
+			       status.x[0], map_read(map, cmd_adr).x[0]);
 			/* Odd. Clear status bits */
-			cfi_write(map, CMD(0x50), cmd_adr);
-			cfi_write(map, CMD(0x70), cmd_adr);
+			map_write(map, CMD(0x50), cmd_adr);
+			map_write(map, CMD(0x70), cmd_adr);
 			ret = -EIO;
 			goto out;
 		}
 	}
 
 	/* Write length of data to come */
-	cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr );
+	bytes = len & (map_bankwidth(map)-1);
+	words = len / map_bankwidth(map);
+	map_write(map, CMD(words - !bytes), cmd_adr );
 
 	/* 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);
-		} else if (cfi_buswidth_is_2()) {
-			map->write16 (map, *(__u16*)buf, adr+z);
-			buf += sizeof(__u16);
-		} else if (cfi_buswidth_is_4()) {
-			map->write32 (map, *(__u32*)buf, adr+z);
-			buf += sizeof(__u32);
-		} else if (cfi_buswidth_is_8()) {
-			map->write64 (map, *(__u64*)buf, adr+z);
-			buf += sizeof(__u64);
-		} else {
-			DISABLE_VPP(map);
-			ret = -EINVAL;
-			goto out;
+	z = 0;
+	while(z < words * map_bankwidth(map)) {
+		map_word datum = map_word_load(map, buf);
+		map_write(map, datum, adr+z);
+
+		z += map_bankwidth(map);
+		buf += map_bankwidth(map);
 		}
+
+	if (bytes) {
+		map_word datum;
+
+		datum = map_word_ff(map);
+		datum = map_word_load_partial(map, datum, buf, 0, bytes);
+		map_write(map, datum, adr+z);
 	}
+
 	/* GO GO GO */
-	cfi_write(map, CMD(0xd0), cmd_adr);
+	map_write(map, CMD(0xd0), cmd_adr);
 	chip->state = FL_WRITING;
 
 	spin_unlock(chip->mutex);
+	INVALIDATE_CACHED_RANGE(map, adr, len);
 	cfi_udelay(chip->buffer_write_time);
 	spin_lock(chip->mutex);
 
@@ -1231,6 +1226,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);
@@ -1241,14 +1237,13 @@
 			continue;
 		}
 
-		status = cfi_read(map, cmd_adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, cmd_adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
 			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;
@@ -1270,33 +1265,18 @@
 
 	/* Done and happy. */
 	chip->state = FL_STATUS;
+
 	/* check for lock bit */
-	if (status & CMD(0x02)) {
+	if (map_word_bitsset(map, status, CMD(0x02))) {
 		/* clear status */
-		cfi_write(map, CMD(0x50), cmd_adr);
+		map_write(map, CMD(0x50), cmd_adr);
 		/* put back into read status register mode */
-		cfi_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0x70), adr);
 		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;
 }
@@ -1306,7 +1286,7 @@
 {
 	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
+	int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
 	int ret = 0;
 	int chipnum;
 	unsigned long ofs;
@@ -1319,8 +1299,8 @@
 	ofs = to  - (chipnum << cfi->chipshift);
 
 	/* If it's not bus-aligned, do the first word write */
-	if (ofs & (CFIDEV_BUSWIDTH-1)) {
-		size_t local_len = (-ofs)&(CFIDEV_BUSWIDTH-1);
+	if (ofs & (map_bankwidth(map)-1)) {
+		size_t local_len = (-ofs)&(map_bankwidth(map)-1);
 		if (local_len > len)
 			local_len = len;
 		ret = cfi_intelext_write_words(mtd, to, local_len,
@@ -1339,13 +1319,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,116 +1342,14 @@
 				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;
-}
-
-typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip,
-			      unsigned long adr, void *thunk);
-
-static int cfi_intelext_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
-				     loff_t ofs, size_t len, void *thunk)
-{
-	struct map_info *map = mtd->priv;
-	struct cfi_private *cfi = map->fldrv_priv;
-	unsigned long adr;
-	int chipnum, ret = 0;
-	int i, first;
-	struct mtd_erase_region_info *regions = mtd->eraseregions;
-
-	if (ofs > mtd->size)
-		return -EINVAL;
-
-	if ((len + ofs) > mtd->size)
-		return -EINVAL;
-
-	/* Check that both start and end of the requested erase are
-	 * aligned with the erasesize at the appropriate addresses.
-	 */
-
-	i = 0;
-
-	/* Skip all erase regions which are ended before the start of 
-	   the requested erase. Actually, to save on the calculations,
-	   we skip to the first erase region which starts after the
-	   start of the requested erase, and then go back one.
-	*/
-	
-	while (i < mtd->numeraseregions && ofs >= regions[i].offset)
-	       i++;
-	i--;
-
-	/* OK, now i is pointing at the erase region in which this 
-	   erase request starts. Check the start of the requested
-	   erase range is aligned with the erase size which is in
-	   effect here.
-	*/
-
-	if (ofs & (regions[i].erasesize-1))
-		return -EINVAL;
-
-	/* Remember the erase region we start on */
-	first = i;
-
-	/* Next, check that the end of the requested erase is aligned
-	 * with the erase region at that address.
-	 */
-
-	while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
-		i++;
-
-	/* As before, drop back one to point at the region in which
-	   the address actually falls
-	*/
-	i--;
-	
-	if ((ofs + len) & (regions[i].erasesize-1))
-		return -EINVAL;
-
-	chipnum = ofs >> cfi->chipshift;
-	adr = ofs - (chipnum << cfi->chipshift);
-
-	i=first;
-
-	while(len) {
-		ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk);
-		
-		if (ret)
-			return ret;
-
-		adr += regions[i].erasesize;
-		len -= regions[i].erasesize;
-
-		if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift)))
-			i++;
-
-		if (adr >> cfi->chipshift) {
-			adr = 0;
-			chipnum++;
-			
-			if (chipnum >= cfi->numchips)
-			break;
-		}
-	}
-
 	return 0;
 }
 
-
-static int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
+static int do_erase_oneblock(struct map_info *map, struct flchip *chip,
+			     unsigned long adr, int len, void *thunk)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	cfi_word status, status_OK;
+	map_word status, status_OK;
 	unsigned long timeo;
 	int retries = 3;
 	DECLARE_WAITQUEUE(wait, current);
@@ -1483,60 +1360,27 @@
 	/* 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);
 	/* Clear the status register first */
-	cfi_write(map, CMD(0x50), adr);
+	map_write(map, CMD(0x50), adr);
 
 	/* Now erase */
-	cfi_write(map, CMD(0x20), adr);
-	cfi_write(map, CMD(0xD0), adr);
+	map_write(map, CMD(0x20), adr);
+	map_write(map, CMD(0xD0), adr);
 	chip->state = FL_ERASING;
-	chip->oldstate = 0;
+	chip->erase_suspended = 0;
 
 	spin_unlock(chip->mutex);
-	set_current_state(TASK_UNINTERRUPTIBLE);
-	schedule_timeout((chip->erase_time*HZ)/(2*1000));
+	INVALIDATE_CACHED_RANGE(map, adr, len);
+	msleep(chip->erase_time / 2);
 	spin_lock(chip->mutex);
 
 	/* FIXME. Use a timer to check this, and return immediately. */
@@ -1554,26 +1398,26 @@
 			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);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
-			cfi_write(map, CMD(0x70), adr);
+			map_write(map, CMD(0x70), adr);
 			chip->state = FL_STATUS;
-			printk(KERN_ERR "waiting for erase at %08lx to complete timed out. Xstatus = %llx, status = %llx.\n",
-			       adr, (__u64)status, (__u64)cfi_read(map, adr));
+			printk(KERN_ERR "waiting for erase at %08lx to complete timed out. Xstatus = %lx, status = %lx.\n",
+			       adr, status.x[0], map_read(map, adr).x[0]);
 			/* Clear status bits */
-			cfi_write(map, CMD(0x50), adr);
-			cfi_write(map, CMD(0x70), adr);
+			map_write(map, CMD(0x50), adr);
+			map_write(map, CMD(0x70), adr);
 			DISABLE_VPP(map);
 			spin_unlock(chip->mutex);
 			return -EIO;
@@ -1590,43 +1434,46 @@
 	ret = 0;
 
 	/* We've broken this before. It doesn't hurt to be safe */
-	cfi_write(map, CMD(0x70), adr);
+	map_write(map, CMD(0x70), adr);
 	chip->state = FL_STATUS;
-	status = cfi_read(map, adr);
+	status = map_read(map, adr);
 
 	/* check for lock bit */
-	if (status & CMD(0x3a)) {
-		unsigned char chipstatus = status;
-		if (status != CMD(status & 0xff)) {
-			int i;
-			for (i = 1; i<CFIDEV_INTERLEAVE; i++) {
-				      chipstatus |= status >> (cfi->device_type * 8);
+	if (map_word_bitsset(map, status, CMD(0x3a))) {
+		unsigned char chipstatus = status.x[0];
+		if (!map_word_equal(map, status, CMD(chipstatus))) {
+			int i, w;
+			for (w=0; w<map_words(map); w++) {
+				for (i = 0; i<cfi_interleave(cfi); i++) {
+					chipstatus |= status.x[w] >> (cfi->device_type * 8);
+				}
 			}
-			printk(KERN_WARNING "Status is not identical for all chips: 0x%llx. Merging to give 0x%02x\n", (__u64)status, chipstatus);
+			printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n",
+			       status.x[0], chipstatus);
 		}
 		/* Reset the error bits */
-		cfi_write(map, CMD(0x50), adr);
-		cfi_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0x50), adr);
+		map_write(map, CMD(0x70), adr);
 		
 		if ((chipstatus & 0x30) == 0x30) {
-			printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%llx\n", (__u64)status);
+			printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus);
 			ret = -EIO;
 		} else if (chipstatus & 0x02) {
 			/* Protection bit set */
 			ret = -EROFS;
 		} else if (chipstatus & 0x8) {
 			/* Voltage */
-			printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%llx\n", (__u64)status);
+			printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus);
 			ret = -EIO;
 		} else if (chipstatus & 0x20) {
 			if (retries--) {
-				printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%llx. Retrying...\n", adr, (__u64)status);
+				printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus);
 				timeo = jiffies + HZ;
 				chip->state = FL_STATUS;
 				spin_unlock(chip->mutex);
 				goto retry;
 			}
-			printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%llx\n", adr, (__u64)status);
+			printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus);
 			ret = -EIO;
 		}
 	}
@@ -1644,13 +1491,12 @@
 	ofs = instr->addr;
 	len = instr->len;
 
-	ret = cfi_intelext_varsize_frob(mtd, do_erase_oneblock, ofs, len, 0);
+	ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL);
 	if (ret)
 		return ret;
 
 	instr->state = MTD_ERASE_DONE;
-	if (instr->callback)
-		instr->callback(instr);
+	mtd_erase_callback(instr);
 	
 	return 0;
 }
@@ -1662,39 +1508,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 */
@@ -1713,7 +1542,8 @@
 }
 
 #ifdef DEBUG_LOCK_BITS
-static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
+static int do_printlockstatus_oneblock(struct map_info *map, struct flchip *chip,
+				       unsigned long adr, int len, void *thunk)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	int ofs_factor = cfi->interleave * cfi->device_type;
@@ -1721,8 +1551,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
@@ -1730,67 +1559,34 @@
 #define DO_XXLOCK_ONEBLOCK_LOCK		((void *) 1)
 #define DO_XXLOCK_ONEBLOCK_UNLOCK	((void *) 2)
 
-static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk)
+static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip,
+			      unsigned long adr, int len, void *thunk)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	cfi_word status, status_OK;
+	map_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);
-	cfi_write(map, CMD(0x60), adr);
+	map_write(map, CMD(0x60), adr);
 
 	if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) {
-		cfi_write(map, CMD(0x01), adr);
+		map_write(map, CMD(0x01), adr);
 		chip->state = FL_LOCKING;
 	} else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) {
-		cfi_write(map, CMD(0xD0), adr);
+		map_write(map, CMD(0xD0), adr);
 		chip->state = FL_UNLOCKING;
 	} else
 		BUG();
@@ -1805,15 +1601,16 @@
 	timeo = jiffies + (HZ*20);
 	for (;;) {
 
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
-			cfi_write(map, CMD(0x70), adr);
+			map_write(map, CMD(0x70), adr);
 			chip->state = FL_STATUS;
-			printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %llx, status = %llx.\n", (__u64)status, (__u64)cfi_read(map, adr));
+			printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %lx, status = %lx.\n",
+			       status.x[0], map_read(map, adr).x[0]);
 			DISABLE_VPP(map);
 			spin_unlock(chip->mutex);
 			return -EIO;
@@ -1827,8 +1624,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;
 }
@@ -1840,17 +1636,17 @@
 #ifdef DEBUG_LOCK_BITS
 	printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
 	       __FUNCTION__, ofs, len);
-	cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock,
+	cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
 				  ofs, len, 0);
 #endif
 
-	ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock, 
+	ret = cfi_varsize_frob(mtd, do_xxlock_oneblock, 
 					ofs, len, DO_XXLOCK_ONEBLOCK_LOCK);
 	
 #ifdef DEBUG_LOCK_BITS
-	printk(KERN_DEBUG __FUNCTION__
-	       "%s: lock status after, ret=%d\n", __FUNCTION__, ret);
-	cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock,
+	printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
+	       __FUNCTION__, ret);
+	cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
 				  ofs, len, 0);
 #endif
 
@@ -1864,16 +1660,17 @@
 #ifdef DEBUG_LOCK_BITS
 	printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n",
 	       __FUNCTION__, ofs, len);
-	cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock,
+	cfi_varsize_frob(mtd, do_printlockstatus_oneblock,
 				  ofs, len, 0);
 #endif
 
-	ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock,
+	ret = cfi_varsize_frob(mtd, do_xxlock_oneblock,
 					ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK);
 	
 #ifdef DEBUG_LOCK_BITS
-	printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret);
-	cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, 
+	printk(KERN_DEBUG "%s: lock status after, ret=%d\n",
+	       __FUNCTION__, ret);
+	cfi_varsize_frob(mtd, do_printlockstatus_oneblock, 
 				  ofs, len, 0);
 #endif
 	
@@ -1893,22 +1690,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:
+			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.
 			 */
-		case FL_PM_SUSPENDED:
+			}
 			break;
-
 		default:
 			ret = -EAGAIN;
+		case FL_PM_SUSPENDED:
 			break;
 		}
 		spin_unlock(chip->mutex);
@@ -1951,7 +1749,7 @@
 		
 		/* Go to known state. Chip may have been power cycled */
 		if (chip->state == FL_PM_SUSPENDED) {
-			cfi_write(map, CMD(0xFF), 0);
+			map_write(map, CMD(0xFF), cfi->chips[i].start);
 			chip->state = FL_READY;
 			wake_up(&chip->wq);
 		}
@@ -1966,6 +1764,7 @@
 	struct cfi_private *cfi = map->fldrv_priv;
 	kfree(cfi->cmdset_priv);
 	kfree(cfi->cfiq);
+	kfree(cfi->chips[0].priv);
 	kfree(cfi);
 	kfree(mtd->eraseregions);
 }
diff -uNrb linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0002.c linux/drivers/mtd/chips/cfi_cmdset_0002.c
--- linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0002.c	Fri Nov 19 12:46:58 2004
+++ linux/drivers/mtd/chips/cfi_cmdset_0002.c	Tue Sep 28 14:38:13 2004
@@ -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.109 2004/09/15 23:48:09 thayne 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,15 +30,24 @@
 #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
+
+#define MAX_WORD_RETRIES 3
+
+#define MANUFACTURER_AMD	0x0001
+#define MANUFACTURER_SST	0x00BF
+#define SST49LF004B	        0x0060
 
 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 void cfi_amdstd_sync (struct mtd_info *);
 static int cfi_amdstd_suspend (struct mtd_info *);
@@ -41,59 +57,213 @@
 static void cfi_amdstd_destroy(struct mtd_info *);
 
 struct mtd_info *cfi_cmdset_0002(struct map_info *, int);
-static struct mtd_info *cfi_amdstd_setup (struct map_info *);
+static struct mtd_info *cfi_amdstd_setup (struct mtd_info *);
 
+static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode);
+static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr);
+#include "fwh_lock.h"
 
 static struct mtd_chip_driver cfi_amdstd_chipdrv = {
-	probe: NULL, /* Not usable directly */
-	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
 };
 
-struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
+
+/* #define DEBUG_CFI_FEATURES */
+
+
+#ifdef DEBUG_CFI_FEATURES
+static void cfi_tell_features(struct cfi_pri_amdstd *extp)
 {
-	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;
+	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 (cfi->cfi_mode==CFI_MODE_CFI){
-		__u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
+	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);
 
-		cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
+	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);
 		
-		major = cfi_read_query(map, base + (adr+3)*ofs_factor);
-		minor = cfi_read_query(map, base + (adr+4)*ofs_factor);
+	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);
 		
-		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);    
+	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
 
-		/* Wheee. Bring me the head of someone at AMD. */
 #ifdef AMD_BOOTLOC_BUG
+/* Wheee. Bring me the head of someone at AMD. */
+static void fixup_amd_bootblock(struct mtd_info *mtd, void* param)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	struct cfi_pri_amdstd *extp = cfi->cmdset_priv;
+	__u8 major = extp->MajorVersion;
+	__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);
-				bootloc = 3;	/* top boot */
+			extp->TopBottom = 3;	/* top boot */
 			} else {
-				bootloc = 2;	/* bottom boot */
+			extp->TopBottom = 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);
+
+static void fixup_use_write_buffers(struct mtd_info *mtd, void *param)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	if (cfi->cfiq->BufWriteTimeoutTyp) {
+		DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" );
+		mtd->write = cfi_amdstd_write_buffers;
 			}
+}
+
+static void fixup_use_secsi(struct mtd_info *mtd, void *param)
+{
+	/* Setup for chips with a secsi area */
+	mtd->read_user_prot_reg = cfi_amdstd_secsi_read;
+	mtd->read_fact_prot_reg = cfi_amdstd_secsi_read;
+}
+
+static void fixup_use_erase_chip(struct mtd_info *mtd, void *param)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	if ((cfi->cfiq->NumEraseRegions == 1) &&
+		((cfi->cfiq->EraseRegionInfo[0] & 0xffff) == 0)) {
+		mtd->erase = cfi_amdstd_erase_chip;
+	}
+	
+}
+
+static struct cfi_fixup cfi_fixup_table[] = {
+#ifdef AMD_BOOTLOC_BUG
+	{ CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL },
+#endif
+	{ CFI_MFR_AMD, 0x0050, fixup_use_secsi, NULL, },
+	{ CFI_MFR_AMD, 0x0053, fixup_use_secsi, NULL, },
+	{ CFI_MFR_AMD, 0x0055, fixup_use_secsi, NULL, },
+	{ CFI_MFR_AMD, 0x0056, fixup_use_secsi, NULL, },
+	{ CFI_MFR_AMD, 0x005C, fixup_use_secsi, NULL, },
+	{ CFI_MFR_AMD, 0x005F, fixup_use_secsi, NULL, },
+#if !FORCE_WORD_WRITE
+	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },
+#endif
+	{ 0, 0, NULL, NULL }
+};
+static struct cfi_fixup jedec_fixup_table[] = {
+	{ MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, },
+	{ 0, 0, NULL, NULL }
+};
+
+static struct cfi_fixup fixup_table[] = {
+	/* The CFI vendor ids and the JEDEC vendor IDs appear
+	 * to be common.  It is like the devices id's are as
+	 * well.  This table is to pick all cases where
+	 * we know that is the case.
+	 */
+	{ CFI_MFR_ANY, CFI_ID_ANY, fixup_use_erase_chip, NULL },
+	{ 0, 0, NULL, NULL }
+};
+
+
+struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+	struct mtd_info *mtd;
+	int i;
+
+	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
+	if (!mtd) {
+		printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
+		return NULL;
+	}
+	memset(mtd, 0, sizeof(*mtd));
+	mtd->priv = map;
+	mtd->type = MTD_NORFLASH;
+
+	/* Fill in the default mtd operations */
+	mtd->erase   = cfi_amdstd_erase_varsize;
+	mtd->write   = cfi_amdstd_write_words;
+	mtd->read    = cfi_amdstd_read;
+	mtd->sync    = cfi_amdstd_sync;
+	mtd->suspend = cfi_amdstd_suspend;
+	mtd->resume  = cfi_amdstd_resume;
+	mtd->flags   = MTD_CAP_NORFLASH;
+	mtd->name    = map->name;
+
+	if (cfi->cfi_mode==CFI_MODE_CFI){
+		unsigned char bootloc;
+		/* 
+		 * It's a real CFI chip, not one for which the probe
+		 * routine faked a CFI structure. So we read the feature
+		 * table from it.
+		 */
+		__u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR;
+		struct cfi_pri_amdstd *extp;
+
+		extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu");
+		if (!extp) {
+			kfree(mtd);
+			return NULL;
+		}
+
+		/* Install our own private info structure */
+		cfi->cmdset_priv = extp;	
+
+		/* Apply cfi device specific fixups */
+		cfi_fixup(mtd, cfi_fixup_table);
+
+#ifdef DEBUG_CFI_FEATURES
+		/* Tell the user about it in lots of lovely detail */
+		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;
+		}
+
 		if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) {
 			printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name);
 			
@@ -106,6 +276,11 @@
 				cfi->cfiq->EraseRegionInfo[j] = swap;
 			}
 		}
+		/*
+		 * These might already be setup (more correctly) by
+		 * jedec_probe.c - still need it for cfi_probe.c path.
+		 */
+		if ( ! (cfi->addr_unlock1 && cfi->addr_unlock2) ) {
 		switch (cfi->device_type) {
 		case CFI_DEVICETYPE_X8:
 			cfi->addr_unlock1 = 0x555; 
@@ -113,7 +288,7 @@
 			break;
 		case CFI_DEVICETYPE_X16:
 			cfi->addr_unlock1 = 0xaaa;
-			if (map->buswidth == cfi->interleave) {
+				if (map_bankwidth(map) == cfi_interleave(cfi)) {
 				/* X16 chip(s) in X8 mode */
 				cfi->addr_unlock2 = 0x555;
 			} else {
@@ -121,14 +296,31 @@
 			}
 			break;
 		case CFI_DEVICETYPE_X32:
-			cfi->addr_unlock1 = 0x1555; 
-			cfi->addr_unlock2 = 0xaaa; 
+				cfi->addr_unlock1 = 0x1554;
+				if (map_bankwidth(map) == cfi_interleave(cfi)*2) {
+					/* X32 chip(s) in X16 mode */
+					cfi->addr_unlock1 = 0xaaa;
+				} else {
+					cfi->addr_unlock2 = 0xaa8; 
+				}
 			break;
 		default:
-			printk(KERN_NOTICE "Eep. Unknown cfi_cmdset_0002 device type %d\n", cfi->device_type);
+				printk(KERN_WARNING
+				       "MTD %s(): Unsupported device type %d\n",
+				       __func__, cfi->device_type);
+				kfree(mtd);
+				kfree(extp);
 			return NULL;
 		}
+		}
+
 	} /* CFI mode */
+	else if (cfi->cfi_mode == CFI_MODE_JEDEC) {
+		/* Apply jedec specific fixups */
+		cfi_fixup(mtd, jedec_fixup_table);
+	}
+	/* Apply generic fixups */
+	cfi_fixup(mtd, fixup_table);
 
 	for (i=0; i< cfi->numchips; i++) {
 		cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp;
@@ -138,40 +330,26 @@
 	
 	map->fldrv = &cfi_amdstd_chipdrv;
 
-	cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL);
-	return cfi_amdstd_setup(map);
+	return cfi_amdstd_setup(mtd);
 }
 
-static struct mtd_info *cfi_amdstd_setup(struct map_info *map)
+
+static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd)
 {
+	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	struct mtd_info *mtd;
 	unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave;
+	unsigned long offset = 0;
+	int i,j;
 
-	mtd = kmalloc(sizeof(*mtd), GFP_KERNEL);
 	printk(KERN_NOTICE "number of %s chips: %d\n", 
 		(cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips);
-
-	if (!mtd) {
-	  printk(KERN_WARNING "Failed to allocate memory for MTD device\n");
-	  goto setup_err;
-	}
-
-	memset(mtd, 0, sizeof(*mtd));
-	mtd->priv = map;
-	mtd->type = MTD_NORFLASH;
-	/* Also select the correct geometry setup too */ 
+	/* Select the correct geometry setup */ 
 	mtd->size = devsize * cfi->numchips;
 	
-	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);
+	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;
@@ -206,67 +384,12 @@
 			       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
-#endif
-		if (((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1)
-			mtd->erase = cfi_amdstd_erase_chip;
-		else
-			mtd->erase = cfi_amdstd_erase_onesize;
-		mtd->read = cfi_amdstd_read;
-		mtd->write = cfi_amdstd_write;
-		break;
+	/* FIXME: erase-suspend-program is broken.  See
+	   http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */
+	printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n");
 
-	default:
-	        printk(KERN_WARNING "Unsupported buswidth\n");
-		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;
-	}
-
-
-        /* does this chip have a secsi area? */
-	if(cfi->mfr==1){
-		
-		switch(cfi->id){
-		case 0x50:
-		case 0x53:
-		case 0x55:
-		case 0x56:
-		case 0x5C:
-		case 0x5F:
-			/* Yes */
-			mtd->read_user_prot_reg = cfi_amdstd_secsi_read;
-			mtd->read_fact_prot_reg = cfi_amdstd_secsi_read;
-		default:		       
-			;
-		}
-	}
-	
-		
-	mtd->sync = cfi_amdstd_sync;
-	mtd->suspend = cfi_amdstd_suspend;
-	mtd->resume = cfi_amdstd_resume;
-	mtd->flags = MTD_CAP_NORFLASH;
-	map->fldrv = &cfi_amdstd_chipdrv;
-	mtd->name = map->name;
-	MOD_INC_USE_COUNT;
+	__module_get(THIS_MODULE);
 	return mtd;
 
  setup_err:
@@ -280,46 +403,182 @@
 	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)
+{
+	map_word d, t;
+
+	d = map_read(map, addr);
+	t = map_read(map, addr);
+
+	return map_word_equal(map, d, t);
+}
+
+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:
+	switch (chip->state) {
+
+	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;
+		}
 
-	if (chip->state != FL_READY){
-#if 0
-	        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);
+	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
+		      || !cfip
+		      || (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. */
+		map_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. */
+				map_write(map, CMD(0x30), chip->in_progress_block_addr);
+				chip->state = FL_ERASING;
+				chip->oldstate = FL_READY;
+				printk(KERN_ERR "MTD %s(): chip not ready after erase suspend\n", __func__);
+				return -EIO;
+			}
                 
 		cfi_spin_unlock(chip->mutex);
+			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;
+		map_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;
 
+	/* Ensure cmd read/writes are aligned. */ 
+	cmd_addr = adr & ~(map_bankwidth(map)-1); 
+
+	cfi_spin_lock(chip->mutex);
+	ret = get_chip(map, chip, cmd_addr, FL_READY);
+	if (ret) {
+		cfi_spin_unlock(chip->mutex);
+		return ret;
+	}
+
+	if (chip->state != FL_POINT && chip->state != FL_READY) {
+		map_write(map, CMD(0xf0), cmd_addr);
 	chip->state = FL_READY;
+	}
 
-	map->copy_from(map, buf, adr, len);
+	map_copy_from(map, buf, adr, len);
 
-	wake_up(&chip->wq);
-	cfi_spin_unlock(chip->mutex);
+	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 +620,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);
@@ -394,12 +654,14 @@
 
 	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 +716,388 @@
 	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, map_word datum)
 {
+	struct cfi_private *cfi = map->fldrv_priv;
 	unsigned long timeo = jiffies + HZ;
-	unsigned int oldstatus, status;
-	unsigned int dq6, dq5;	
+	/*
+	 * 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;
+	map_word oldd, curd;
+	int retry_cnt = 0;
+
+	adr += chip->start;
+
+	cfi_spin_lock(chip->mutex);
+	ret = get_chip(map, chip, adr, FL_WRITING);
+	if (ret) {
+		cfi_spin_unlock(chip->mutex);
+		return ret;
+	}
+
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
+	       __func__, adr, datum.x[0] );
+
+	/*
+	 * Check for a NOP for the case when the datum to write is already
+	 * 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.
+	 */
+	oldd = map_read(map, adr);
+	if (map_word_equal(map, oldd, datum)) {
+		DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): NOP\n",
+		       __func__);
+		goto op_done;
+	}
+
+	ENABLE_VPP(map);
+ retry:
+	/*
+	 * 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);
+	map_write(map, datum, adr);
+	chip->state = FL_WRITING;
+
+	cfi_spin_unlock(chip->mutex);
+	cfi_udelay(chip->word_write_time);
+	cfi_spin_lock(chip->mutex);
+
+	/* 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);
+
+			set_current_state(TASK_UNINTERRUPTIBLE);
+			add_wait_queue(&chip->wq, &wait);
+			cfi_spin_unlock(chip->mutex);
+			schedule();
+			remove_wait_queue(&chip->wq, &wait);
+			timeo = jiffies + (HZ / 2); /* FIXME */
+			cfi_spin_lock(chip->mutex);
+			continue;
+		}
+
+		/* Test to see if toggling has stopped. */
+		oldd = map_read(map, adr);
+		curd = map_read(map, adr);
+		if (map_word_equal(map, curd, oldd)) {
+			/* Do we have the correct value? */
+			if (map_word_equal(map, curd, datum)) {
+				goto op_done;
+			}
+			/* Nope something has gone wrong. */
+			break;
+		}
+
+		if (time_after(jiffies, timeo)) {
+			printk(KERN_WARNING "MTD %s(): software timeout\n",
+				__func__ );
+			break;
+		}
+
+		/* Latency issues. Drop the lock, wait a while and retry */
+		cfi_spin_unlock(chip->mutex);
+		cfi_udelay(1);
+		cfi_spin_lock(chip->mutex);
+	}
+
+	/* reset on all failures. */
+	map_write( map, CMD(0xF0), chip->start );
+	/* FIXME - should have reset delay before continuing */
+	if (++retry_cnt <= MAX_WORD_RETRIES) 
+		goto retry;
+
+	ret = -EIO;
+ op_done:
+	chip->state = FL_READY;
+	put_chip(map, chip, adr);
+	cfi_spin_unlock(chip->mutex);
+
+	return ret;
+}
+
+
+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)
+		return 0;
+
+	chipnum = to >> cfi->chipshift;
+	ofs = to  - (chipnum << cfi->chipshift);
+	chipstart = cfi->chips[chipnum].start;
+
+	/* If it's not bus-aligned, do the first byte write */
+	if (ofs & (map_bankwidth(map)-1)) {
+		unsigned long bus_ofs = ofs & ~(map_bankwidth(map)-1);
+		int i = ofs - bus_ofs;
+		int n = 0;
+		map_word tmp_buf;
+
  retry:
-	cfi_spin_lock(chip->mutex);
+		cfi_spin_lock(cfi->chips[chipnum].mutex);
 
-	if (chip->state != FL_READY) {
+		if (cfi->chips[chipnum].state != FL_READY) {
 #if 0
-	        printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", chip->state);
+			printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state);
 #endif
 		set_current_state(TASK_UNINTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
+			add_wait_queue(&cfi->chips[chipnum].wq, &wait);
                 
-		cfi_spin_unlock(chip->mutex);
+			cfi_spin_unlock(cfi->chips[chipnum].mutex);
 
 		schedule();
-		remove_wait_queue(&chip->wq, &wait);
+			remove_wait_queue(&cfi->chips[chipnum].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;
+		/* Load 'tmp_buf' with old contents of flash */
+		tmp_buf = map_read(map, bus_ofs+chipstart);
+
+		cfi_spin_unlock(cfi->chips[chipnum].mutex);
+
+		/* Number of bytes to copy from buffer */
+		n = min_t(int, len, map_bankwidth(map)-i);
+		
+		tmp_buf = map_word_load_partial(map, tmp_buf, buf, i, n);
+
+		ret = do_write_oneword(map, &cfi->chips[chipnum], 
+				       bus_ofs, tmp_buf);
+		if (ret) 
+			return ret;
+		
+		ofs += n;
+		buf += n;
+		(*retlen) += n;
+		len -= n;
+
+		if (ofs >> cfi->chipshift) {
+			chipnum ++; 
+			ofs = 0;
+			if (chipnum == cfi->numchips)
+				return 0;
+		}
+	}
+	
+	/* We are now aligned, write as much as possible */
+	while(len >= map_bankwidth(map)) {
+		map_word datum;
+
+		datum = map_word_load(map, buf);
+
+		ret = do_write_oneword(map, &cfi->chips[chipnum],
+				       ofs, datum);
+		if (ret)
+			return ret;
+
+		ofs += map_bankwidth(map);
+		buf += map_bankwidth(map);
+		(*retlen) += map_bankwidth(map);
+		len -= map_bankwidth(map);
+
+		if (ofs >> cfi->chipshift) {
+			chipnum ++; 
+			ofs = 0;
+			if (chipnum == cfi->numchips)
+				return 0;
+			chipstart = cfi->chips[chipnum].start;
+		}
+	}
+
+	/* Write the trailing bytes if any */
+	if (len & (map_bankwidth(map)-1)) {
+		map_word tmp_buf;
+
+ retry1:
+		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 retry1;
+		}
+
+		tmp_buf = map_read(map, ofs + chipstart);
+
+		cfi_spin_unlock(cfi->chips[chipnum].mutex);
+
+		tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len);
+	
+		ret = do_write_oneword(map, &cfi->chips[chipnum], 
+				ofs, tmp_buf);
+		if (ret) 
+			return ret;
+		
+		(*retlen) += len;
+	}
+
+	return 0;
+}
+
+
+/*
+ * 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)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+	unsigned long timeo = jiffies + HZ;
+	/* see comments in do_write_oneword() regarding uWriteTimeo. */
+	unsigned long uWriteTimeout = ( HZ / 1000 ) + 1;
+	int ret = -EIO;
+	unsigned long cmd_adr;
+	int z, words;
+	map_word 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);
+	cmd_adr = adr;
+
+	cfi_spin_lock(chip->mutex);
+	ret = get_chip(map, chip, adr, FL_WRITING);
+	if (ret) {
+		cfi_spin_unlock(chip->mutex);
+		return ret;
 	}
-	else {
+
+	datum = map_word_load(map, buf);
+
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8lx)\n",
+	       __func__, adr, datum.x[0] );
+
+	ENABLE_VPP(map);
 	        cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
 	        cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
-	        cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+	//cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL);
+
+	/* Write Buffer Load */
+	map_write(map, CMD(0x25), cmd_adr);
+
+	chip->state = FL_WRITING_TO_BUFFER;
+
+	/* Write length of data to come */
+	words = len / map_bankwidth(map);
+	map_write(map, CMD(words - 1), cmd_adr);
+	/* Write data */
+	z = 0;
+	while(z < words * map_bankwidth(map)) {
+		datum = map_word_load(map, buf);
+		map_write(map, datum, adr + z);
+
+		z += map_bankwidth(map);
+		buf += map_bankwidth(map);
 	}
+	z -= map_bankwidth(map);
+
+	adr += z;
+
+	/* Write Buffer Program Confirm: GO GO GO */
+	map_write(map, CMD(0x29), cmd_adr);
+	chip->state = FL_WRITING;
+
+	cfi_spin_unlock(chip->mutex);
+	cfi_udelay(chip->buffer_write_time);
+	cfi_spin_lock(chip->mutex);
 
-	cfi_write(map, datum, adr);
+	timeo = jiffies + uWriteTimeout; 
 
+	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);
-	cfi_udelay(chip->word_write_time);
+			schedule();
+			remove_wait_queue(&chip->wq, &wait);
+			timeo = jiffies + (HZ / 2); /* FIXME */
 	cfi_spin_lock(chip->mutex);
+			continue;
+		}
 
-	/* 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) ) {
-
-		if (need_resched()) {
-			cfi_spin_unlock(chip->mutex);
-			yield();
-			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 (chip_ready(map, adr))
+			goto op_done;
 		    
-			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.");        
+		if( time_after(jiffies, timeo))
+			break;
 			
-			chip->state = FL_READY;
-			wake_up(&chip->wq);
+		/* Latency issues. Drop the lock, wait a while and retry */
 			cfi_spin_unlock(chip->mutex);
-			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__ );
+
+	/* reset on all failures. */
+	map_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_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 = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
 	int ret = 0;
 	int chipnum;
-	unsigned long ofs, chipstart;
+	unsigned long ofs;
 
 	*retlen = 0;
 	if (!len)
@@ -579,36 +1105,19 @@
 
 	chipnum = to >> cfi->chipshift;
 	ofs = to  - (chipnum << cfi->chipshift);
-	chipstart = cfi->chips[chipnum].start;
-
-	/* If it's not bus-aligned, do the first byte write */
-	if (ofs & (CFIDEV_BUSWIDTH-1)) {
-		unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1);
-		int i = ofs - bus_ofs;
-		int n = 0;
-		u_char tmp_buf[4];
-		__u32 datum;
-
-		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--;
-
-		if (cfi_buswidth_is_2()) {
-			datum = *(__u16*)tmp_buf;
-		} else if (cfi_buswidth_is_4()) {
-			datum = *(__u32*)tmp_buf;
-		} else {
-			return -EINVAL;  /* should never happen, but be safe */
-		}
 
-		ret = do_write_oneword(map, &cfi->chips[chipnum], 
-				bus_ofs, datum, 0);
+	/* If it's not bus-aligned, do the first word write */
+	if (ofs & (map_bankwidth(map)-1)) {
+		size_t local_len = (-ofs)&(map_bankwidth(map)-1);
+		if (local_len > len)
+			local_len = len;
+		ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<<cfi->chipshift),
+					     local_len, retlen, buf);
 		if (ret) 
 			return ret;
-		
-		ofs += n;
-		buf += n;
-		(*retlen) += n;
+		ofs += local_len;
+		buf += local_len;
+		len -= local_len;
 
 		if (ofs >> cfi->chipshift) {
 			chipnum ++; 
@@ -618,132 +1127,72 @@
 		}
 	}
 	
-	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;
+	/* Write buffer is worth it only if more than one word to write... */
+	while (len >= map_bankwidth(map) * 2) {
+		/* We must not cross write block boundaries */
+		int size = wbufsize - (ofs & (wbufsize-1));
+
+		if (size > len)
+			size = len;
+		if (size % map_bankwidth(map))
+			size -= size % map_bankwidth(map);
 
-		if (cfi_buswidth_is_1()) {
-			datum = *(__u8*)buf;
-		} else if (cfi_buswidth_is_2()) {
-			datum = *(__u16*)buf;
-		} else if (cfi_buswidth_is_4()) {
-			datum = *(__u32*)buf;
-		} else {
-			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);
-			}
+		ret = do_write_buffer(map, &cfi->chips[chipnum], 
+				      ofs, buf, size);
+		if (ret)
 			return ret;
-		}
 
-		ofs += CFIDEV_BUSWIDTH;
-		buf += CFIDEV_BUSWIDTH;
-		(*retlen) += CFIDEV_BUSWIDTH;
-		len -= CFIDEV_BUSWIDTH;
+		ofs += size;
+		buf += size;
+		(*retlen) += size;
+		len -= size;
 
 		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);
-	}
+	if (len) {
+		size_t retlen_dregs = 0;
 
-	/* Write the trailing bytes if any */
-	if (len & (CFIDEV_BUSWIDTH-1)) {
-		int i = 0, n = 0;
-		u_char tmp_buf[4];
-		__u32 datum;
-
-		map->copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH);
-		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;
-		} else {
-			return -EINVAL;  /* should never happen, but be safe */
-		}
+		ret = cfi_amdstd_write_words(mtd, ofs + (chipnum<<cfi->chipshift),
+					     len, &retlen_dregs, buf);
 
-		ret = do_write_oneword(map, &cfi->chips[chipnum], 
-				ofs, datum, 0);
-		if (ret) 
+		*retlen += retlen_dregs;
 			return ret;
-		
-		(*retlen) += n;
 	}
 
 	return 0;
 }
 
+
+/*
+ * 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;
-	unsigned int adr;
 	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);
-
-	if (chip->state != FL_READY){
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
+	adr = cfi->addr_unlock1;
                 
+	cfi_spin_lock(chip->mutex);
+	ret = get_chip(map, chip, adr, FL_WRITING);
+	if (ret) {
 		cfi_spin_unlock(chip->mutex);
-
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-#if 0
-		if(signal_pending(current))
-			return -EINTR;
-#endif
-		timeo = jiffies + HZ;
-
-		goto retry;
+		return ret;
 	}	
 
-	chip->state = FL_ERASING;
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
+	       __func__, chip->start );
 	
-	/* 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);
@@ -751,372 +1200,170 @@
 	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;
 
-	/* 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);
-
-	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_ERASING;
+	chip->erase_suspended = 0;
+	chip->in_progress_block_addr = adr;
 
-		/* an initial short sleep */
 		cfi_spin_unlock(chip->mutex);
-		schedule_timeout(HZ/100);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout((chip->erase_time*HZ)/(2*1000));
 		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("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;
 		}
+		if (chip->erase_suspended) {
+			/* This erase was suspended and resumed.
+			   Adjust the timeout */
+			timeo = jiffies + (HZ*20); /* FIXME */
+			chip->erase_suspended = 0;
+		}
+
+		if (chip_ready(map, adr))
+			goto op_done;
 
-		/* Busy wait for 1/10 of a milisecond */
-		for(wait_reps = 0;
-		    	(wait_reps < 100) &&
-			((status & dq6) != (oldstatus & dq6)) && 
-			((status & dq5) != dq5);
-			wait_reps++) {
+		if (time_after(jiffies, timeo))
+			break;
 			
 			/* Latency issues. Drop the lock, wait a while and retry */
 			cfi_spin_unlock(chip->mutex);
-			
-			cfi_udelay(1);
-		
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(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);
-		cfi_spin_unlock(chip->mutex);
-		printk("waiting for erase to complete timed out.");
-		DISABLE_VPP(map);
-		return -EIO;
 	}
-	DISABLE_VPP(map);
+
+	printk(KERN_WARNING "MTD %s(): software timeout\n",
+	       __func__ );
+
+	/* reset on all failures. */
+	map_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 inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr)
+
+static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, int len, void *thunk)
 {
-	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;
 	DECLARE_WAITQUEUE(wait, current);
+	int ret = 0;
 
- retry:
-	cfi_spin_lock(chip->mutex);
-
-	if (chip->state != FL_READY){
-		set_current_state(TASK_UNINTERRUPTIBLE);
-		add_wait_queue(&chip->wq, &wait);
+	adr += chip->start;
                 
+	cfi_spin_lock(chip->mutex);
+	ret = get_chip(map, chip, adr, FL_ERASING);
+	if (ret) {
 		cfi_spin_unlock(chip->mutex);
-
-		schedule();
-		remove_wait_queue(&chip->wq, &wait);
-#if 0
-		if(signal_pending(current))
-			return -EINTR;
-#endif
-		timeo = jiffies + HZ;
-
-		goto retry;
+		return ret;
 	}	
 
-	chip->state = FL_ERASING;
+	DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n",
+	       __func__, adr );
 
-	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);
-
-	/* 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);
+	map_write(map, CMD(0x30), 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_ERASING;
+	chip->erase_suspended = 0;
+	chip->in_progress_block_addr = adr;
 
-		/* an initial short sleep */
 		cfi_spin_unlock(chip->mutex);
-		schedule_timeout(HZ/100);
+	set_current_state(TASK_UNINTERRUPTIBLE);
+	schedule_timeout((chip->erase_time*HZ)/(2*1000));
 		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;
 		}
+		if (chip->erase_suspended) {
+			/* This erase was suspended and resumed.
+			   Adjust the timeout */
+			timeo = jiffies + (HZ*20); /* FIXME */
+			chip->erase_suspended = 0;
+		}
+
+		if (chip_ready(map, adr))
+			goto op_done;
 
-		/* Busy wait for 1/10 of a milisecond */
-		for(wait_reps = 0;
-		    	(wait_reps < 100) &&
-			((status & dq6) != (oldstatus & dq6)) && 
-			((status & dq5) != dq5);
-			wait_reps++) {
+		if (time_after(jiffies, timeo))
+			break;
 			
 			/* Latency issues. Drop the lock, wait a while and retry */
 			cfi_spin_unlock(chip->mutex);
-			
-			cfi_udelay(1);
-		
+		set_current_state(TASK_UNINTERRUPTIBLE);
+		schedule_timeout(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" );
-			}
 		}
-        else
-        {
-		    printk( "Waiting for erase to complete timed out in do_erase_oneblock.");        
 		    
-		chip->state = FL_READY;
-		wake_up(&chip->wq);
-		cfi_spin_unlock(chip->mutex);
-		DISABLE_VPP(map);
-		return -EIO;
-	}
-	}
+	printk(KERN_WARNING "MTD %s(): software timeout\n",
+	       __func__ );
 
-	DISABLE_VPP(map);
+	/* reset on all failures. */
+	map_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;
-}
-
-static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
-{
-	struct map_info *map = mtd->priv;
-	struct cfi_private *cfi = map->fldrv_priv;
-	unsigned long adr, len;
-	int chipnum, ret = 0;
-	int i, first;
-	struct mtd_erase_region_info *regions = mtd->eraseregions;
-
-	if (instr->addr > mtd->size)
-		return -EINVAL;
-
-	if ((instr->len + instr->addr) > mtd->size)
-		return -EINVAL;
-
-	/* Check that both start and end of the requested erase are
-	 * aligned with the erasesize at the appropriate addresses.
-	 */
-
-	i = 0;
-
-	/* Skip all erase regions which are ended before the start of 
-	   the requested erase. Actually, to save on the calculations,
-	   we skip to the first erase region which starts after the
-	   start of the requested erase, and then go back one.
-	*/
-	
-	while (i < mtd->numeraseregions && instr->addr >= regions[i].offset)
-	       i++;
-	i--;
-
-	/* OK, now i is pointing at the erase region in which this 
-	   erase request starts. Check the start of the requested
-	   erase range is aligned with the erase size which is in
-	   effect here.
-	*/
-
-	if (instr->addr & (regions[i].erasesize-1))
-		return -EINVAL;
-
-	/* Remember the erase region we start on */
-	first = i;
-
-	/* Next, check that the end of the requested erase is aligned
-	 * with the erase region at that address.
-	 */
-
-	while (i<mtd->numeraseregions && (instr->addr + instr->len) >= regions[i].offset)
-		i++;
-
-	/* As before, drop back one to point at the region in which
-	   the address actually falls
-	*/
-	i--;
-	
-	if ((instr->addr + instr->len) & (regions[i].erasesize-1))
-		return -EINVAL;
-	
-	chipnum = instr->addr >> cfi->chipshift;
-	adr = instr->addr - (chipnum << cfi->chipshift);
-	len = instr->len;
-
-	i=first;
-
-	while(len) {
-		ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr);
-
-		if (ret)
 			return ret;
-
-		adr += regions[i].erasesize;
-		len -= regions[i].erasesize;
-
-		if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift)))
-			i++;
-
-		if (adr >> cfi->chipshift) {
-			adr = 0;
-			chipnum++;
-			
-			if (chipnum >= cfi->numchips)
-			break;
-		}
-	}
-
-	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)
-{
-	struct map_info *map = mtd->priv;
-	struct cfi_private *cfi = map->fldrv_priv;
-	unsigned long adr, len;
-	int chipnum, ret = 0;
-
-	if (instr->addr & (mtd->erasesize - 1))
-		return -EINVAL;
-
-	if (instr->len & (mtd->erasesize -1))
-		return -EINVAL;
 
-	if ((instr->len + instr->addr) > mtd->size)
-		return -EINVAL;
+int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr)
+{
+	unsigned long ofs, len;
+	int ret;
 
-	chipnum = instr->addr >> cfi->chipshift;
-	adr = instr->addr - (chipnum << cfi->chipshift);
+	ofs = instr->addr;
 	len = instr->len;
 
-	while(len) {
-		ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr);
-
+	ret = cfi_varsize_frob(mtd, do_erase_oneblock, ofs, len, NULL);
 		if (ret)
 			return ret;
 
-		adr += mtd->erasesize;
-		len -= mtd->erasesize;
-
-		if (adr >> cfi->chipshift) {
-			adr = 0;
-			chipnum++;
-			
-			if (chipnum >= cfi->numchips)
-			break;
-		}
-	}
-		
 	instr->state = MTD_ERASE_DONE;
-	if (instr->callback)
-		instr->callback(instr);
+	mtd_erase_callback(instr);
 	
 	return 0;
 }
 
+
 static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr)
 {
 	struct map_info *map = mtd->priv;
@@ -1134,12 +1381,12 @@
 		return ret;
 
 	instr->state = MTD_ERASE_DONE;
-	if (instr->callback)
-		instr->callback(instr);
+	mtd_erase_callback(instr);
 	
 	return 0;
 }
 
+
 static void cfi_amdstd_sync (struct mtd_info *mtd)
 {
 	struct map_info *map = mtd->priv;
@@ -1253,6 +1500,7 @@
 	return ret;
 }
 
+
 static void cfi_amdstd_resume(struct mtd_info *mtd)
 {
 	struct map_info *map = mtd->priv;
@@ -1268,7 +1516,7 @@
 		
 		if (chip->state == FL_PM_SUSPENDED) {
 			chip->state = FL_READY;
-			cfi_write(map, CMD(0xF0), chip->start);
+			map_write(map, CMD(0xF0), chip->start);
 			wake_up(&chip->wq);
 		}
 		else
@@ -1290,21 +1538,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 -uNrb linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0020.c linux/drivers/mtd/chips/cfi_cmdset_0020.c
--- linux-2.4.30/drivers/mtd/chips/cfi_cmdset_0020.c	Fri Nov 19 12:46:58 2004
+++ linux/drivers/mtd/chips/cfi_cmdset_0020.c	Fri Sep 10 15:15:04 2004
@@ -4,6 +4,7 @@
  *
  * (C) 2000 Red Hat. GPL'd
  *
+ * $Id: cfi_cmdset_0020.c,v 1.15 2004/08/09 13:19:43 dwmw2 Exp $
  * 
  * 10/10/2000	Nicolas Pitre <nico@cam.org>
  * 	- completely revamped method functions so they are aware and
@@ -17,10 +18,12 @@
  *	- added a writev function
  */
 
+#include <linux/version.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>
 
@@ -30,12 +33,13 @@
 #include <linux/interrupt.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/cfi.h>
+#include <linux/mtd/mtd.h>
 #include <linux/mtd/compatmac.h>
 
 
 static int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *);
 static int cfi_staa_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *);
-static int cfi_staa_writev(struct mtd_info *mtd, const struct iovec *vecs,
+static int cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs,
 		unsigned long count, loff_t to, size_t *retlen);
 static int cfi_staa_erase_varsize(struct mtd_info *, struct erase_info *);
 static void cfi_staa_sync (struct mtd_info *);
@@ -51,10 +55,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 +117,6 @@
 {
 	struct cfi_private *cfi = map->fldrv_priv;
 	int i;
-	__u32 base = cfi->chips[0].start;
 
 	if (cfi->cfi_mode) {
 		/* 
@@ -123,35 +126,10 @@
 		 */
 		__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)
-			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);
+		extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "ST Microelectronics");
+		if (!extp)
                     return NULL;
-		}
 		
 		/* Do some byteswapping if necessary */
 		extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport);
@@ -172,11 +150,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 +181,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 +206,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 +231,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;
 }
@@ -264,7 +239,7 @@
 
 static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf)
 {
-	__u32 status, status_OK;
+	map_word status, status_OK;
 	unsigned long timeo;
 	DECLARE_WAITQUEUE(wait, current);
 	int suspended = 0;
@@ -274,7 +249,7 @@
 	adr += chip->start;
 
 	/* Ensure cmd read/writes are aligned. */ 
-	cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); 
+	cmd_addr = adr & ~(map_bankwidth(map)-1); 
 
 	/* Let's determine this according to the interleave only once */
 	status_OK = CMD(0x80);
@@ -288,33 +263,33 @@
 	 */
 	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);
+		map_write (map, CMD(0xb0), cmd_addr);
 		/* If the flash has finished erasing, then 'erase suspend'
 		 * appears to make some (28F320) flash devices switch to
 		 * 'read' mode.  Make sure that we switch to 'read status'
 		 * mode so we get the right data. --rmk
 		 */
-		cfi_write(map, CMD(0x70), cmd_addr);
+		map_write(map, CMD(0x70), cmd_addr);
 		chip->oldstate = FL_ERASING;
 		chip->state = FL_ERASE_SUSPENDING;
 		//		printk("Erase suspending at 0x%lx\n", cmd_addr);
 		for (;;) {
-			status = cfi_read(map, cmd_addr);
-			if ((status & status_OK) == status_OK)
+			status = map_read(map, cmd_addr);
+			if (map_word_andequal(map, status, status_OK, status_OK))
 				break;
 			
 			if (time_after(jiffies, timeo)) {
 				/* Urgh */
-				cfi_write(map, CMD(0xd0), cmd_addr);
+				map_write(map, CMD(0xd0), cmd_addr);
 				/* make sure we're in 'read status' mode */
-				cfi_write(map, CMD(0x70), cmd_addr);
+				map_write(map, CMD(0x70), cmd_addr);
 				chip->state = FL_ERASING;
 				spin_unlock_bh(chip->mutex);
 				printk(KERN_ERR "Chip not ready after erase "
-				       "suspended: status = 0x%x\n", status);
+				       "suspended: status = 0x%lx\n", status.x[0]);
 				return -EIO;
 			}
 			
@@ -324,7 +299,7 @@
 		}
 		
 		suspended = 1;
-		cfi_write(map, CMD(0xff), cmd_addr);
+		map_write(map, CMD(0xff), cmd_addr);
 		chip->state = FL_READY;
 		break;
 	
@@ -338,13 +313,13 @@
 
 	case FL_CFI_QUERY:
 	case FL_JEDEC_QUERY:
-		cfi_write(map, CMD(0x70), cmd_addr);
+		map_write(map, CMD(0x70), cmd_addr);
 		chip->state = FL_STATUS;
 
 	case FL_STATUS:
-		status = cfi_read(map, cmd_addr);
-		if ((status & status_OK) == status_OK) {
-			cfi_write(map, CMD(0xff), cmd_addr);
+		status = map_read(map, cmd_addr);
+		if (map_word_andequal(map, status, status_OK, status_OK)) {
+			map_write(map, CMD(0xff), cmd_addr);
 			chip->state = FL_READY;
 			break;
 		}
@@ -352,7 +327,7 @@
 		/* Urgh. Chip not yet ready to talk to us. */
 		if (time_after(jiffies, timeo)) {
 			spin_unlock_bh(chip->mutex);
-			printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %x\n", status);
+			printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %lx\n", status.x[0]);
 			return -EIO;
 		}
 
@@ -374,7 +349,7 @@
 		goto retry;
 	}
 
-	map->copy_from(map, buf, adr, len);
+	map_copy_from(map, buf, adr, len);
 
 	if (suspended) {
 		chip->state = chip->oldstate;
@@ -387,8 +362,8 @@
 		   sending the 0x70 (Read Status) command to an erasing
 		   chip and expecting it to be ignored, that's what we 
 		   do. */
-		cfi_write(map, CMD(0xd0), cmd_addr);
-		cfi_write(map, CMD(0x70), cmd_addr);		
+		map_write(map, CMD(0xd0), cmd_addr);
+		map_write(map, CMD(0x70), cmd_addr);		
 	}
 
 	wake_up(&chip->wq);
@@ -439,16 +414,16 @@
 				  unsigned long adr, const u_char *buf, int len)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	__u32 status, status_OK;
+	map_word status, status_OK;
 	unsigned long cmd_adr, timeo;
 	DECLARE_WAITQUEUE(wait, current);
 	int wbufsize, z;
         
         /* M58LW064A requires bus alignment for buffer wriets -- saw */
-        if (adr & (CFIDEV_BUSWIDTH-1))
+        if (adr & (map_bankwidth(map)-1))
             return -EINVAL;
 
-        wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
+        wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
         adr += chip->start;
 	cmd_adr = adr & ~(wbufsize-1);
 	
@@ -474,21 +449,21 @@
 		
 	case FL_CFI_QUERY:
 	case FL_JEDEC_QUERY:
-		cfi_write(map, CMD(0x70), cmd_adr);
+		map_write(map, CMD(0x70), cmd_adr);
                 chip->state = FL_STATUS;
 #ifdef DEBUG_CFI_FEATURES
-        printk("%s: 1 status[%x]\n", __FUNCTION__, cfi_read(map, cmd_adr));
+        printk("%s: 1 status[%x]\n", __FUNCTION__, map_read(map, cmd_adr));
 #endif
 
 	case FL_STATUS:
-		status = cfi_read(map, cmd_adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, cmd_adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		/* Urgh. Chip not yet ready to talk to us. */
 		if (time_after(jiffies, timeo)) {
 			spin_unlock_bh(chip->mutex);
-                        printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %x, status = %x\n",
-                               status, cfi_read(map, cmd_adr));
+                        printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %lx, status = %lx\n",
+                               status.x[0], map_read(map, cmd_adr).x[0]);
 			return -EIO;
 		}
 
@@ -510,13 +485,13 @@
 	}
 
 	ENABLE_VPP(map);
-	cfi_write(map, CMD(0xe8), cmd_adr);
+	map_write(map, CMD(0xe8), cmd_adr);
 	chip->state = FL_WRITING_TO_BUFFER;
 
 	z = 0;
 	for (;;) {
-		status = cfi_read(map, cmd_adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, cmd_adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 
 		spin_unlock_bh(chip->mutex);
@@ -526,35 +501,26 @@
 		if (++z > 100) {
 			/* Argh. Not ready for write to buffer */
 			DISABLE_VPP(map);
-                        cfi_write(map, CMD(0x70), cmd_adr);
+                        map_write(map, CMD(0x70), cmd_adr);
 			chip->state = FL_STATUS;
 			spin_unlock_bh(chip->mutex);
-			printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %x\n", status);
+			printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx\n", status.x[0]);
 			return -EIO;
 		}
 	}
 
 	/* Write length of data to come */
-	cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr );
+	map_write(map, CMD(len/map_bankwidth(map)-1), cmd_adr );
         
 	/* Write data */
-	for (z = 0; z < len; z += CFIDEV_BUSWIDTH) {
-		if (cfi_buswidth_is_1()) {
-			map->write8 (map, *(__u8*)buf, adr+z);
-			buf += sizeof(__u8);
-		} else if (cfi_buswidth_is_2()) {
-			map->write16 (map, *(__u16*)buf, adr+z);
-			buf += sizeof(__u16);
-		} else if (cfi_buswidth_is_4()) {
-			map->write32 (map, *(__u32*)buf, adr+z);
-			buf += sizeof(__u32);
-		} else {
-			DISABLE_VPP(map);
-			return -EINVAL;
-		}
+	for (z = 0; z < len;
+	     z += map_bankwidth(map), buf += map_bankwidth(map)) {
+		map_word d;
+		d = map_word_load(map, buf);
+		map_write(map, d, adr+z);
 	}
 	/* GO GO GO */
-	cfi_write(map, CMD(0xd0), cmd_adr);
+	map_write(map, CMD(0xd0), cmd_adr);
 	chip->state = FL_WRITING;
 
 	spin_unlock_bh(chip->mutex);
@@ -576,16 +542,16 @@
 			continue;
 		}
 
-		status = cfi_read(map, cmd_adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, cmd_adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
                         /* clear status */
-                        cfi_write(map, CMD(0x50), cmd_adr);
+                        map_write(map, CMD(0x50), cmd_adr);
                         /* put back into read status register mode */
-                        cfi_write(map, CMD(0x70), adr);
+                        map_write(map, CMD(0x70), adr);
 			chip->state = FL_STATUS;
 			DISABLE_VPP(map);
 			spin_unlock_bh(chip->mutex);
@@ -612,18 +578,17 @@
 	chip->state = FL_STATUS;
 
         /* check for errors: 'lock bit', 'VPP', 'dead cell'/'unerased cell' or 'incorrect cmd' -- saw */
-        if ((status & CMD(0x02)) || (status & CMD(0x08)) ||
-            (status & CMD(0x10)) || (status & CMD(0x20))) {
+        if (map_word_bitsset(map, status, CMD(0x3a))) {
 #ifdef DEBUG_CFI_FEATURES
-            printk("%s: 2 status[%x]\n", __FUNCTION__, status);
+		printk("%s: 2 status[%lx]\n", __FUNCTION__, status.x[0]);
 #endif
             /* clear status */
-            cfi_write(map, CMD(0x50), cmd_adr);
+		map_write(map, CMD(0x50), cmd_adr);
             /* put back into read status register mode */
-            cfi_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0x70), adr);
             wake_up(&chip->wq);
             spin_unlock_bh(chip->mutex);
-            return (status & CMD(0x02)) ? -EROFS : -EIO;
+		return map_word_bitsset(map, status, CMD(0x02)) ? -EROFS : -EIO;
         }
 	wake_up(&chip->wq);
 	spin_unlock_bh(chip->mutex);
@@ -636,7 +601,7 @@
 {
 	struct map_info *map = mtd->priv;
 	struct cfi_private *cfi = map->fldrv_priv;
-	int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize;
+	int wbufsize = cfi_interleave(cfi) << cfi->cfiq->MaxBufWriteSize;
 	int ret = 0;
 	int chipnum;
 	unsigned long ofs;
@@ -649,7 +614,7 @@
 	ofs = to  - (chipnum << cfi->chipshift);
 
 #ifdef DEBUG_CFI_FEATURES
-        printk("%s: CFIDEV_BUSWIDTH[%x]\n", __FUNCTION__, CFIDEV_BUSWIDTH);
+        printk("%s: map_bankwidth(map)[%x]\n", __FUNCTION__, map_bankwidth(map));
         printk("%s: chipnum[%x] wbufsize[%x]\n", __FUNCTION__, chipnum, wbufsize);
         printk("%s: ofs[%x] len[%x]\n", __FUNCTION__, ofs, len);
 #endif
@@ -692,7 +657,7 @@
 #define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1))
 #define ECCBUF_MOD(x) ((x) &  (ECCBUF_SIZE - 1))
 static int
-cfi_staa_writev(struct mtd_info *mtd, const struct iovec *vecs,
+cfi_staa_writev(struct mtd_info *mtd, const struct kvec *vecs,
 		unsigned long count, loff_t to, size_t *retlen)
 {
 	unsigned long i;
@@ -761,7 +726,7 @@
 static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	__u32 status, status_OK;
+	map_word status, status_OK;
 	unsigned long timeo;
 	int retries = 3;
 	DECLARE_WAITQUEUE(wait, current);
@@ -781,12 +746,12 @@
 	case FL_CFI_QUERY:
 	case FL_JEDEC_QUERY:
 	case FL_READY:
-		cfi_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0x70), adr);
 		chip->state = FL_STATUS;
 
 	case FL_STATUS:
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* Urgh. Chip not yet ready to talk to us. */
@@ -815,11 +780,11 @@
 
 	ENABLE_VPP(map);
 	/* Clear the status register first */
-	cfi_write(map, CMD(0x50), adr);
+	map_write(map, CMD(0x50), adr);
 
 	/* Now erase */
-	cfi_write(map, CMD(0x20), adr);
-	cfi_write(map, CMD(0xD0), adr);
+	map_write(map, CMD(0x20), adr);
+	map_write(map, CMD(0xD0), adr);
 	chip->state = FL_ERASING;
 	
 	spin_unlock_bh(chip->mutex);
@@ -843,15 +808,15 @@
 			continue;
 		}
 
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
-			cfi_write(map, CMD(0x70), adr);
+			map_write(map, CMD(0x70), adr);
 			chip->state = FL_STATUS;
-			printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr));
+			printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]);
 			DISABLE_VPP(map);
 			spin_unlock_bh(chip->mutex);
 			return -EIO;
@@ -867,43 +832,46 @@
 	ret = 0;
 
 	/* We've broken this before. It doesn't hurt to be safe */
-	cfi_write(map, CMD(0x70), adr);
+	map_write(map, CMD(0x70), adr);
 	chip->state = FL_STATUS;
-	status = cfi_read(map, adr);
+	status = map_read(map, adr);
 
 	/* check for lock bit */
-	if (status & CMD(0x3a)) {
-		unsigned char chipstatus = status;
-		if (status != CMD(status & 0xff)) {
-			int i;
-			for (i = 1; i<CFIDEV_INTERLEAVE; i++) {
-				      chipstatus |= status >> (cfi->device_type * 8);
+	if (map_word_bitsset(map, status, CMD(0x3a))) {
+		unsigned char chipstatus = status.x[0];
+		if (!map_word_equal(map, status, CMD(chipstatus))) {
+			int i, w;
+			for (w=0; w<map_words(map); w++) {
+				for (i = 0; i<cfi_interleave(cfi); i++) {
+					chipstatus |= status.x[w] >> (cfi->device_type * 8);
+				}
 			}
-			printk(KERN_WARNING "Status is not identical for all chips: 0x%x. Merging to give 0x%02x\n", status, chipstatus);
+			printk(KERN_WARNING "Status is not identical for all chips: 0x%lx. Merging to give 0x%02x\n",
+			       status.x[0], chipstatus);
 		}
 		/* Reset the error bits */
-		cfi_write(map, CMD(0x50), adr);
-		cfi_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0x50), adr);
+		map_write(map, CMD(0x70), adr);
 		
 		if ((chipstatus & 0x30) == 0x30) {
-			printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", status);
+			printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", chipstatus);
 			ret = -EIO;
 		} else if (chipstatus & 0x02) {
 			/* Protection bit set */
 			ret = -EROFS;
 		} else if (chipstatus & 0x8) {
 			/* Voltage */
-			printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", status);
+			printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", chipstatus);
 			ret = -EIO;
 		} else if (chipstatus & 0x20) {
 			if (retries--) {
-				printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, status);
+				printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus);
 				timeo = jiffies + HZ;
 				chip->state = FL_STATUS;
 				spin_unlock_bh(chip->mutex);
 				goto retry;
 			}
-			printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, status);
+			printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus);
 			ret = -EIO;
 		}
 	}
@@ -998,8 +966,7 @@
 	}
 		
 	instr->state = MTD_ERASE_DONE;
-	if (instr->callback)
-		instr->callback(instr);
+	mtd_erase_callback(instr);
 	
 	return 0;
 }
@@ -1064,7 +1031,7 @@
 static inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	__u32 status, status_OK;
+	map_word status, status_OK;
 	unsigned long timeo = jiffies + HZ;
 	DECLARE_WAITQUEUE(wait, current);
 
@@ -1082,12 +1049,12 @@
 	case FL_CFI_QUERY:
 	case FL_JEDEC_QUERY:
 	case FL_READY:
-		cfi_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0x70), adr);
 		chip->state = FL_STATUS;
 
 	case FL_STATUS:
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK) 
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK)) 
 			break;
 		
 		/* Urgh. Chip not yet ready to talk to us. */
@@ -1115,8 +1082,8 @@
 	}
 
 	ENABLE_VPP(map);
-	cfi_write(map, CMD(0x60), adr);
-	cfi_write(map, CMD(0x01), adr);
+	map_write(map, CMD(0x60), adr);
+	map_write(map, CMD(0x01), adr);
 	chip->state = FL_LOCKING;
 	
 	spin_unlock_bh(chip->mutex);
@@ -1129,15 +1096,15 @@
 	timeo = jiffies + (HZ*2);
 	for (;;) {
 
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
-			cfi_write(map, CMD(0x70), adr);
+			map_write(map, CMD(0x70), adr);
 			chip->state = FL_STATUS;
-			printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr));
+			printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]);
 			DISABLE_VPP(map);
 			spin_unlock_bh(chip->mutex);
 			return -EIO;
@@ -1213,7 +1180,7 @@
 static inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr)
 {
 	struct cfi_private *cfi = map->fldrv_priv;
-	__u32 status, status_OK;
+	map_word status, status_OK;
 	unsigned long timeo = jiffies + HZ;
 	DECLARE_WAITQUEUE(wait, current);
 
@@ -1231,12 +1198,12 @@
 	case FL_CFI_QUERY:
 	case FL_JEDEC_QUERY:
 	case FL_READY:
-		cfi_write(map, CMD(0x70), adr);
+		map_write(map, CMD(0x70), adr);
 		chip->state = FL_STATUS;
 
 	case FL_STATUS:
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* Urgh. Chip not yet ready to talk to us. */
@@ -1264,8 +1231,8 @@
 	}
 
 	ENABLE_VPP(map);
-	cfi_write(map, CMD(0x60), adr);
-	cfi_write(map, CMD(0xD0), adr);
+	map_write(map, CMD(0x60), adr);
+	map_write(map, CMD(0xD0), adr);
 	chip->state = FL_UNLOCKING;
 	
 	spin_unlock_bh(chip->mutex);
@@ -1278,15 +1245,15 @@
 	timeo = jiffies + (HZ*2);
 	for (;;) {
 
-		status = cfi_read(map, adr);
-		if ((status & status_OK) == status_OK)
+		status = map_read(map, adr);
+		if (map_word_andequal(map, status, status_OK, status_OK))
 			break;
 		
 		/* OK Still waiting */
 		if (time_after(jiffies, timeo)) {
-			cfi_write(map, CMD(0x70), adr);
+			map_write(map, CMD(0x70), adr);
 			chip->state = FL_STATUS;
-			printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr));
+			printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]);
 			DISABLE_VPP(map);
 			spin_unlock_bh(chip->mutex);
 			return -EIO;
@@ -1415,7 +1382,7 @@
 		
 		/* Go to known state. Chip may have been power cycled */
 		if (chip->state == FL_PM_SUSPENDED) {
-			cfi_write(map, CMD(0xFF), 0);
+			map_write(map, CMD(0xFF), 0);
 			chip->state = FL_READY;
 			wake_up(&chip->wq);
 		}
@@ -1432,23 +1399,20 @@
 	kfree(cfi);
 }
 
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define cfi_staa_init init_module
-#define cfi_staa_exit cleanup_module
-#endif
-
 static char im_name[]="cfi_cmdset_0020";
 
-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);
 }
 
 module_init(cfi_staa_init);
 module_exit(cfi_staa_exit);
+
+MODULE_LICENSE("GPL");
diff -uNrb linux-2.4.30/drivers/mtd/chips/cfi_probe.c linux/drivers/mtd/chips/cfi_probe.c
--- linux-2.4.30/drivers/mtd/chips/cfi_probe.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/chips/cfi_probe.c	Fri Sep 10 15:15:04 2004
@@ -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.77 2004/07/14 08:38:44 dwmw2 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);
@@ -34,21 +35,36 @@
    in: interleave,type,mode
    ret: table index, <0 for error
  */
-static inline int qry_present(struct map_info *map, __u32 base,
+static int qry_present(struct map_info *map, __u32 base,
 				struct cfi_private *cfi)
 {
 	int osf = cfi->interleave * cfi->device_type;	// scale factor
+	map_word val;
+	map_word qry;
 
-	if (cfi_read(map,base+osf*0x10)==cfi_build_cmd('Q',map,cfi) &&
-	    cfi_read(map,base+osf*0x11)==cfi_build_cmd('R',map,cfi) &&
-	    cfi_read(map,base+osf*0x12)==cfi_build_cmd('Y',map,cfi))
-		return 1;	// ok !
+	qry =  cfi_build_cmd('Q', map, cfi);
+	val = map_read(map, base + osf*0x10);
 
-	return 0; 	// nothing found
+	if (!map_word_equal(map, qry, val))
+		return 0;
+
+	qry =  cfi_build_cmd('R', map, cfi);
+	val = map_read(map, base + osf*0x11);
+
+	if (!map_word_equal(map, qry, val))
+		return 0;
+
+	qry =  cfi_build_cmd('Y', map, cfi);
+	val = map_read(map, base + osf*0x12);
+
+	if (!map_word_equal(map, qry, val))
+		return 0;
+
+	return 1; 	// nothing found
 }
 
 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;
 	
@@ -65,6 +81,7 @@
 		return 0;
 	}
 	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
 	cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL);
 
 	if (!qry_present(map,base,cfi))
@@ -77,18 +94,25 @@
 	}
 
 	/* 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);
+			cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL);
 
 			/* If the QRY marker goes away, it's an alias */
-			if (!qry_present(map, 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 
@@ -96,10 +120,11 @@
 			 * too and if it's the same, assume it's an alias. */
 			/* FIXME: Use other modes to do a proper check */
 			cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
+			cfi_send_gen_cmd(0xFF, 0, start, map, cfi, cfi->device_type, NULL);
 			
 			if (qry_present(map, base, cfi)) {
 				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,21 +132,16 @@
 	
 	/* 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 */
 	cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL);
+	cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
 
-	printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n",
+	printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n",
 	       map->name, cfi->interleave, cfi->device_type*8, base,
-	       map->buswidth*8);
+	       map->bankwidth*8);
 	
 	return 1;
 }
@@ -149,7 +169,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,8 +198,29 @@
 		       (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);
+	/* ... even if it's an Intel chip */
+	cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL);
+
+	printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n",
+	       map->name, cfi->interleave, cfi->device_type*8, base,
+	       map->bankwidth*8);
 
 	return 1;
 }
@@ -240,11 +280,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 +343,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 +357,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 -uNrb linux-2.4.30/drivers/mtd/chips/cfi_util.c linux/drivers/mtd/chips/cfi_util.c
--- linux-2.4.30/drivers/mtd/chips/cfi_util.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/chips/cfi_util.c	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,188 @@
+/*
+ * 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.5 2004/08/12 06:40:23 eric 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/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/cfi.h>
+#include <linux/mtd/compatmac.h>
+
+struct cfi_extquery *
+cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name)
+{
+	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);
+	cfi_send_gen_cmd(0xff, 0, base, map, cfi, cfi->device_type, NULL);
+
+	return extp;
+}
+
+EXPORT_SYMBOL(cfi_read_pri);
+
+void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup *fixups)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	struct cfi_fixup *f;
+
+	for (f=fixups; f->fixup; f++) {
+		if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) &&
+		    ((f->id  == CFI_ID_ANY)  || (f->id  == cfi->id))) {
+			f->fixup(mtd, f->param);
+		}
+	}
+}
+
+EXPORT_SYMBOL(cfi_fixup);
+
+int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
+				     loff_t ofs, size_t len, void *thunk)
+{
+	struct map_info *map = mtd->priv;
+	struct cfi_private *cfi = map->fldrv_priv;
+	unsigned long adr;
+	int chipnum, ret = 0;
+	int i, first;
+	struct mtd_erase_region_info *regions = mtd->eraseregions;
+
+	if (ofs > mtd->size)
+		return -EINVAL;
+
+	if ((len + ofs) > mtd->size)
+		return -EINVAL;
+
+	/* Check that both start and end of the requested erase are
+	 * aligned with the erasesize at the appropriate addresses.
+	 */
+
+	i = 0;
+
+	/* Skip all erase regions which are ended before the start of 
+	   the requested erase. Actually, to save on the calculations,
+	   we skip to the first erase region which starts after the
+	   start of the requested erase, and then go back one.
+	*/
+	
+	while (i < mtd->numeraseregions && ofs >= regions[i].offset)
+	       i++;
+	i--;
+
+	/* OK, now i is pointing at the erase region in which this 
+	   erase request starts. Check the start of the requested
+	   erase range is aligned with the erase size which is in
+	   effect here.
+	*/
+
+	if (ofs & (regions[i].erasesize-1))
+		return -EINVAL;
+
+	/* Remember the erase region we start on */
+	first = i;
+
+	/* Next, check that the end of the requested erase is aligned
+	 * with the erase region at that address.
+	 */
+
+	while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset)
+		i++;
+
+	/* As before, drop back one to point at the region in which
+	   the address actually falls
+	*/
+	i--;
+	
+	if ((ofs + len) & (regions[i].erasesize-1))
+		return -EINVAL;
+
+	chipnum = ofs >> cfi->chipshift;
+	adr = ofs - (chipnum << cfi->chipshift);
+
+	i=first;
+
+	while(len) {
+		unsigned long chipmask;
+		int size = regions[i].erasesize;
+
+		ret = (*frob)(map, &cfi->chips[chipnum], adr, size, thunk);
+		
+		if (ret)
+			return ret;
+
+		adr += size;
+		len -= size;
+
+		chipmask = (1 << cfi->chipshift) - 1;
+		if ((adr & chipmask) == ((regions[i].offset + size * regions[i].numblocks) & chipmask))
+			i++;
+
+		if (adr >> cfi->chipshift) {
+			adr = 0;
+			chipnum++;
+			
+			if (chipnum >= cfi->numchips)
+			break;
+		}
+	}
+
+	return 0;
+}
+
+EXPORT_SYMBOL(cfi_varsize_frob);
+
+MODULE_LICENSE("GPL");
diff -uNrb linux-2.4.30/drivers/mtd/chips/chipreg.c linux/drivers/mtd/chips/chipreg.c
--- linux-2.4.30/drivers/mtd/chips/chipreg.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/chips/chipreg.c	Fri Sep 10 15:15:04 2004
@@ -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 -uNrb linux-2.4.30/drivers/mtd/chips/fwh_lock.h linux/drivers/mtd/chips/fwh_lock.h
--- linux-2.4.30/drivers/mtd/chips/fwh_lock.h	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/chips/fwh_lock.h	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,107 @@
+#ifndef FWH_LOCK_H
+#define FWH_LOCK_H
+
+
+enum fwh_lock_state {
+        FWH_UNLOCKED   = 0,
+	FWH_DENY_WRITE = 1,
+	FWH_IMMUTABLE  = 2,
+	FWH_DENY_READ  = 4,
+};
+
+struct fwh_xxlock_thunk {
+	enum fwh_lock_state val;
+	flstate_t state;
+};
+
+
+#define FWH_XXLOCK_ONEBLOCK_LOCK   ((struct fwh_xxlock_thunk){ FWH_DENY_WRITE, FL_LOCKING})
+#define FWH_XXLOCK_ONEBLOCK_UNLOCK ((struct fwh_xxlock_thunk){ FWH_UNLOCKED,   FL_UNLOCKING})
+
+/*
+ * This locking/unlock is specific to firmware hub parts.  Only one
+ * is known that supports the Intel command set.    Firmware
+ * hub parts cannot be interleaved as they are on the LPC bus
+ * so this code has not been tested with interleaved chips,
+ * and will likely fail in that context.
+ */
+static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, 
+	unsigned long adr, int len, void *thunk)
+{
+	struct cfi_private *cfi = map->fldrv_priv;
+	struct fwh_xxlock_thunk *xxlt = (struct fwh_xxlock_thunk *)thunk;
+	int ret;
+
+	/* Refuse the operation if the we cannot look behind the chip */
+	if (chip->start < 0x400000) {
+		DEBUG( MTD_DEBUG_LEVEL3,
+			"MTD %s(): chip->start: %lx wanted >= 0x400000\n",
+			__func__, chip->start );
+		return -EIO;
+	}
+	/*
+	 * lock block registers:
+	 * - on 64k boundariesand
+	 * - bit 1 set high
+	 * - block lock registers are 4MiB lower - overflow subtract (danger)
+	 * 
+	 * The address manipulation is first done on the logical address
+	 * which is 0 at the start of the chip, and then the offset of
+	 * the individual chip is addted to it.  Any other order a weird
+	 * map offset could cause problems.
+	 */
+	adr = (adr & ~0xffffUL) | 0x2;
+	adr += chip->start - 0x400000;
+
+	/*
+	 * This is easy because these are writes to registers and not writes
+	 * to flash memory - that means that we don't have to check status
+	 * and timeout.
+	 */
+	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;
+	map_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 fwh_lock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+	int ret;
+
+	ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len,
+		(void *)&FWH_XXLOCK_ONEBLOCK_LOCK);
+
+	return ret;
+}
+
+
+static int fwh_unlock_varsize(struct mtd_info *mtd, loff_t ofs, size_t len)
+{
+	int ret;
+
+	ret = cfi_varsize_frob(mtd, fwh_xxlock_oneblock, ofs, len,
+		(void *)&FWH_XXLOCK_ONEBLOCK_UNLOCK);
+	
+	return ret;
+}
+
+static void fixup_use_fwh_lock(struct mtd_info *mtd, void *param)
+{
+	printk(KERN_NOTICE "using fwh lock/unlock method\n");
+	/* Setup for the chips with the fwh lock method */
+	mtd->lock   = fwh_lock_varsize;
+	mtd->unlock = fwh_unlock_varsize;
+}
+#endif /* FWH_LOCK_H */
diff -uNrb linux-2.4.30/drivers/mtd/chips/gen_probe.c linux/drivers/mtd/chips/gen_probe.c
--- linux-2.4.30/drivers/mtd/chips/gen_probe.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/chips/gen_probe.c	Fri Sep 10 15:15:04 2004
@@ -1,11 +1,13 @@
 /*
  * 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.21 2004/08/14 15:14:05 dwmw2 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>
@@ -48,13 +50,13 @@
 EXPORT_SYMBOL(mtd_do_chip_probe);
 
 
-struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp)
+static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp)
 {
-	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, mapsize;
+	int max_chips;
 
 	memset(&cfi, 0, sizeof(cfi));
 
@@ -62,7 +64,7 @@
 	   interleave and device type, etc. */
 	if (!genprobe_new_chip(map, cp, &cfi)) {
 		/* The probe didn't like it */
-		printk(KERN_WARNING "%s: Found no %s device at location zero\n",
+		printk(KERN_DEBUG "%s: Found no %s device at location zero\n",
 		       cp->name, map->name);
 		return NULL;
 	}		
@@ -77,46 +79,47 @@
 		return NULL;
 	}
 #endif
-	chip[0].start = 0;
-	chip[0].state = FL_READY;
 	cfi.chipshift = cfi.cfiq->DevSize;
 
-	switch(cfi.interleave) {
-#ifdef CFIDEV_INTERLEAVE_1
-	case 1:
-		break;
-#endif
-#ifdef CFIDEV_INTERLEAVE_2
-	case 2:
+	if (cfi_interleave_is_1(&cfi)) {
+		;
+	} else if (cfi_interleave_is_2(&cfi)) {
 		cfi.chipshift++;
-		break;
-#endif
-#ifdef CFIDEV_INTERLEAVE_4
-	case 4:
-		cfi.chipshift+=2;
-		break;
-#endif
-	default:
+	} else if (cfi_interleave_is_4((&cfi))) {
+		cfi.chipshift += 2;
+	} else if (cfi_interleave_is_8(&cfi)) {
+		cfi.chipshift += 3;
+	} else {
 		BUG();
 	}
 		
 	cfi.numchips = 1;
 
 	/*
+	 * Allocate memory for bitmap of valid chips. 
+	 * Align bitmap storage size to full byte. 
+	 */ 
+	max_chips = map->size >> cfi.chipshift;
+	mapsize = (max_chips / 8) + ((max_chips % 8) ? 1 : 0);
+	chip_map = kmalloc(mapsize, GFP_KERNEL);
+	if (!chip_map) {
+		printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name);
+		kfree(cfi.cfiq);
+		return NULL;
+	}
+	memset (chip_map, 0, mapsize);
+
+	set_bit(0, chip_map); /* Mark first chip valid */
+
+	/*
 	 * 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 +131,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);
+
+	for (i = 0, j = 0; (j < cfi.numchips) && (i < max_chips); i++) {
+		if(test_bit(i, chip_map)) {
+			struct flchip *pchip = &retcfi->chips[j++];
 
-	/* 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;
+			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;
 }
 
@@ -148,130 +158,30 @@
 static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp,
 			     struct cfi_private *cfi)
 {
-	switch (map->buswidth) {
-#ifdef CFIDEV_BUSWIDTH_1		
-	case CFIDEV_BUSWIDTH_1:
-		cfi->interleave = CFIDEV_INTERLEAVE_1;
+	int min_chips = (map_bankwidth(map)/4?:1); /* At most 4-bytes wide. */
+	int max_chips = map_bankwidth(map); /* And minimum 1 */
+	int nr_chips, type;
 
-		cfi->device_type = CFI_DEVICETYPE_X8;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
+	for (nr_chips = min_chips; nr_chips <= max_chips; nr_chips <<= 1) {
 
-		cfi->device_type = CFI_DEVICETYPE_X16;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-		break;			
-#endif /* CFIDEV_BUSWITDH_1 */
-
-#ifdef CFIDEV_BUSWIDTH_2		
-	case CFIDEV_BUSWIDTH_2:
-#ifdef CFIDEV_INTERLEAVE_1
-		cfi->interleave = CFIDEV_INTERLEAVE_1;
+		if (!cfi_interleave_supported(nr_chips))
+		    continue;
 
-		cfi->device_type = CFI_DEVICETYPE_X16;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_1 */
-#ifdef CFIDEV_INTERLEAVE_2
-		cfi->interleave = CFIDEV_INTERLEAVE_2;
+		cfi->interleave = nr_chips;
 
-		cfi->device_type = CFI_DEVICETYPE_X8;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
+		/* Minimum device size. Don't look for one 8-bit device
+		   in a 16-bit bus, etc. */
+		type = map_bankwidth(map) / nr_chips;
 
-		cfi->device_type = CFI_DEVICETYPE_X16;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_2 */
-		break;			
-#endif /* CFIDEV_BUSWIDTH_2 */
-
-#ifdef CFIDEV_BUSWIDTH_4
-	case CFIDEV_BUSWIDTH_4:
-#if defined(CFIDEV_INTERLEAVE_1) && defined(SOMEONE_ACTUALLY_MAKES_THESE)
-                cfi->interleave = CFIDEV_INTERLEAVE_1;
+		for (; type <= CFI_DEVICETYPE_X32; type<<=1) {
+			cfi->device_type = type;
 
-                cfi->device_type = CFI_DEVICETYPE_X32;
 		if (cp->probe_chip(map, 0, NULL, cfi))
 			return 1;
-#endif /* CFIDEV_INTERLEAVE_1 */
-#ifdef CFIDEV_INTERLEAVE_2
-		cfi->interleave = CFIDEV_INTERLEAVE_2;
-
-#ifdef SOMEONE_ACTUALLY_MAKES_THESE
-		cfi->device_type = CFI_DEVICETYPE_X32;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif
-		cfi->device_type = CFI_DEVICETYPE_X16;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-
-		cfi->device_type = CFI_DEVICETYPE_X8;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_2 */
-#ifdef CFIDEV_INTERLEAVE_4
-		cfi->interleave = CFIDEV_INTERLEAVE_4;
-
-#ifdef SOMEONE_ACTUALLY_MAKES_THESE
-		cfi->device_type = CFI_DEVICETYPE_X32;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif
-		cfi->device_type = CFI_DEVICETYPE_X16;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-
-		cfi->device_type = CFI_DEVICETYPE_X8;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_4 */
-		break;
-#endif /* CFIDEV_BUSWIDTH_4 */
-
-#ifdef CFIDEV_BUSWIDTH_8
-	case CFIDEV_BUSWIDTH_8:
-#if defined(CFIDEV_INTERLEAVE_2) && defined(SOMEONE_ACTUALLY_MAKES_THESE)
-                cfi->interleave = CFIDEV_INTERLEAVE_2;
-
-                cfi->device_type = CFI_DEVICETYPE_X32;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_2 */
-#ifdef CFIDEV_INTERLEAVE_4
-		cfi->interleave = CFIDEV_INTERLEAVE_4;
-
-#ifdef SOMEONE_ACTUALLY_MAKES_THESE
-		cfi->device_type = CFI_DEVICETYPE_X32;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif
-		cfi->device_type = CFI_DEVICETYPE_X16;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_4 */
-#ifdef CFIDEV_INTERLEAVE_8
-		cfi->interleave = CFIDEV_INTERLEAVE_8;
-
-		cfi->device_type = CFI_DEVICETYPE_X16;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-
-		cfi->device_type = CFI_DEVICETYPE_X8;
-		if (cp->probe_chip(map, 0, NULL, cfi))
-			return 1;
-#endif /* CFIDEV_INTERLEAVE_8 */
-		break;
-#endif /* CFIDEV_BUSWIDTH_8 */
-
-	default:
-		printk(KERN_WARNING "genprobe_new_chip called with unsupported buswidth %d\n", map->buswidth);
-		return 0;
+		}
 	}
 	return 0;
 }
-
 
 typedef struct mtd_info *cfi_cmdset_fn_t(struct map_info *, int);
 
diff -uNrb linux-2.4.30/drivers/mtd/chips/jedec.c linux/drivers/mtd/chips/jedec.c
--- linux-2.4.30/drivers/mtd/chips/jedec.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/chips/jedec.c	Fri Sep 10 15:15:04 2004
@@ -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.21 2004/08/09 13:19:43 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 */
@@ -85,7 +128,7 @@
    {
       printk("mtd: Increase MAX_JEDEC_CHIPS, too many banks.\n");
       kfree(MTD);
-      return 0;
+      return NULL;
    }
    
    for (Base = 0; Base < map->size; Base += my_bank_size)
@@ -98,7 +141,7 @@
 	 if (jedec_probe8(map,Base,priv) == 0) {
 		 printk("did recognize jedec chip\n");
 		 kfree(MTD);
-	         return 0;
+	         return NULL;
 	 }
       }
       if (map->buswidth == 2)
@@ -124,15 +167,14 @@
       {
 	 printk("mtd: Failed. Device has incompatible mixed sector sizes\n");
 	 kfree(MTD);
-	 return 0;
+	 return NULL;
       }      
    }
    
    /* 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++)
@@ -151,7 +193,7 @@
       {
 	 printk("mtd: Internal Error, JEDEC not set\n");
 	 kfree(MTD);
-	 return 0;
+	 return NULL;
       }
       
       if (Uniq != 0)
@@ -179,7 +221,7 @@
    if (!priv->size) {
 	   printk("priv->size is zero\n");
 	   kfree(MTD);
-	   return 0;
+	   return NULL;
    }
    if (priv->size/my_bank_size) {
 	   if (priv->size/my_bank_size == 1) {
@@ -198,7 +240,7 @@
 		      {
 			 printk("mtd: Failed. Cannot handle unsymmetric banking\n");
 			 kfree(MTD);
-			 return 0;
+			 return NULL;
 		      }      
 		   }
 	   }
@@ -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;
 }
 
@@ -344,15 +385,15 @@
    for (I = 0; JEDEC_table[I].jedec != 0; I++)
       if (JEDEC_table[I].jedec == Id)
 	 return JEDEC_table + I;
-   return 0;
+   return NULL;
 }
 
 // Look for flash using an 8 bit bus interface
 static int jedec_probe8(struct map_info *map,unsigned long base,
 		  struct jedec_private *priv)
 { 
-   #define flread(x) map->read8(map,base+x)
-   #define flwrite(v,x) map->write8(map,v,base+x)
+   #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++;
@@ -739,8 +780,7 @@
        	    
    //printk("done\n");
    instr->state = MTD_ERASE_DONE;
-   if (instr->callback)
-	instr->callback(instr);
+   mtd_erase_callback(instr);
    return 0;
    
    #undef flread
@@ -755,10 +795,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 +834,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 +849,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 +860,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 -uNrb linux-2.4.30/drivers/mtd/chips/jedec_probe.c linux/drivers/mtd/chips/jedec_probe.c
--- linux-2.4.30/drivers/mtd/chips/jedec_probe.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/chips/jedec_probe.c	Tue Sep 28 14:38:13 2004
@@ -1,13 +1,16 @@
 /* 
    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.57 2004/09/17 11:45:05 eric 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>
 #include <linux/module.h>
+#include <linux/init.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
 #include <asm/io.h>
@@ -15,7 +18,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>
@@ -24,26 +29,35 @@
 #define MANUFACTURER_AMD	0x0001
 #define MANUFACTURER_ATMEL	0x001f
 #define MANUFACTURER_FUJITSU	0x0004
+#define MANUFACTURER_HYUNDAI	0x00AD
 #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
 #define AM29F032B	0x0041
+#define AM29F002T	0x00B0
 
 /* Atmel */
 #define AT49BV512	0x0003
@@ -54,6 +68,7 @@
 #define AT49BV32XT	0x00C9
 
 /* Fujitsu */
+#define MBM29F040C	0x00A4
 #define MBM29LV650UE	0x22D7
 #define MBM29LV320TE	0x22F6
 #define MBM29LV320BE	0x22F9
@@ -61,6 +76,11 @@
 #define MBM29LV160BE	0x2249
 #define MBM29LV800BA	0x225B
 #define MBM29LV800TA	0x22DA
+#define MBM29LV400TC	0x22B9
+#define MBM29LV400BC	0x22BA
+
+/* Hyundai */
+#define HY29F002T	0x00B0
 
 /* Intel */
 #define I28F004B3T	0x00d4
@@ -87,19 +107,33 @@
 #define I82802AC	0x00ac
 
 /* Macronix */
+#define MX29LV040C	0x004F
 #define MX29LV160T	0x22C4
 #define MX29LV160B	0x2249
 #define MX29F016	0x00AD
+#define MX29F002T	0x00B0
 #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
+#define M50FW040	0x002C
+#define M50FW080	0x002D
+#define M50FW016	0x002E
+#define M50LPW080       0x002F
 
 /* SST */
+#define SST29EE020	0x0010
+#define SST29LE020	0x0012
 #define SST29EE512	0x005d
 #define SST29LE512	0x003d
 #define SST39LF800	0x2781
@@ -110,6 +144,8 @@
 #define SST39LF040	0x00D7
 #define SST39SF010A	0x00B5
 #define SST39SF020A	0x00B6
+#define SST49LF004B	0x0060
+#define SST49LF008A	0x005a
 #define SST49LF030A	0x001C
 #define SST49LF040A	0x0051
 #define SST49LF080A	0x005B
@@ -122,16 +158,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 {
+	u32 addr1;
+	u32 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 +253,1404 @@
 #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),
+		.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",
-		DevSize: SIZE_2MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {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_TOSHIBA,
-		dev_id: TC58FVT160,
-		name: "Toshiba TC58FVT160",
-		DevSize: SIZE_2MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {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_TOSHIBA,
-		dev_id: TC58FVB160,
-		name: "Toshiba TC58FVB160",
-		DevSize: SIZE_2MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {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,31)
+			ERASEINFO(0x10000,15),
 		}
 	}, {
-		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)
+/* 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_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_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_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_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_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_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_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_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_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_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_FUJITSU,
-		dev_id: MBM29LV160TE,
-		name: "Fujitsu MBM29LV160TE",
-		DevSize: SIZE_2MiB,
-		CmdSet:	P_ID_AMD_STD,
+		mfr_id: MANUFACTURER_AMD,
+		dev_id: AM29F002T,
+		name: "AMD AM29F002T",
+		DevSize: SIZE_256KiB,
 		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,31),
+		regions: {ERASEINFO(0x10000,3),
 			  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),
+		.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_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_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_FUJITSU,
-		dev_id: MBM29LV800TA,
-		name: "Fujitsu MBM29LV800TA",
-		DevSize: SIZE_1MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,15),
+		.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_AMD,
-		dev_id: AM29LV800BB,
-		name: "AMD AM29LV800BB",
-		DevSize: SIZE_1MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {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,15),
+			ERASEINFO(0x10000,31)
 		}
 	}, {
-		mfr_id: MANUFACTURER_AMD,
-		dev_id: AM29F800BB,
-		name: "AMD AM29F800BB",
-		DevSize: SIZE_1MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x04000,1),
+		.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),
+			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),
+		.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_AMD,
-		dev_id: AM29F800BT,
-		name: "AMD AM29F800BT",
-		DevSize: SIZE_1MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,15),
+		.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_AMD,
-		dev_id: AM29LV800BB,
-		name: "AMD AM29LV800BB",
-		DevSize: SIZE_1MiB,
-		CmdSet:	P_ID_AMD_STD,
+		mfr_id: MANUFACTURER_HYUNDAI,
+		dev_id: HY29F002T,
+		name: "Hyundai HY29F002T",
+		DevSize: SIZE_256KiB,
 		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,15),
+		regions: {ERASEINFO(0x10000,3),
 			  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_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),
+		.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		= MX29LV040C,
+		.name		= "Macronix MX29LV040C",
+		.uaddr		= {
+			[0] = MTD_UADDR_0x0555_0x02AA,  /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,8),
+		}
+	}, {
+		.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_ST,
-		dev_id: M29W160DB,
-		name: "ST M29W160DB",
-		DevSize: SIZE_2MiB,
-		CmdSet:	P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {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_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_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_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_AMD,
-		dev_id: AM29F017D,
-		name: "AMD AM29F017D",
-		DevSize: SIZE_2MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,32),
+		.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_AMD,
-		dev_id: AM29F016,
-		name: "AMD AM29F016",
-		DevSize: SIZE_2MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,32),
+		mfr_id: MANUFACTURER_MACRONIX,
+		dev_id: MX29F002T,
+		name: "Macronix MX29F002T",
+		DevSize: SIZE_256KiB,
+		NumEraseRegions: 4,
+		regions: {ERASEINFO(0x10000,3),
+			  ERASEINFO(0x08000,1),
+			  ERASEINFO(0x02000,2),
+			  ERASEINFO(0x04000,1)
 		}
 	}, {
-		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_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_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		= 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 	= 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_AMD,
-		dev_id: AM29F040,
-		name: "AMD AM29F040",
-		DevSize: SIZE_512KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,8),
+ 		.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_AMD,
-		dev_id: AM29LV040B,
-		name: "AMD AM29LV040B",
-		DevSize: SIZE_512KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,8),
+		.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		= 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		= 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		= 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		= SST49LF008A,
+		.name		= "SST 49LF008A",
+ 		.uaddr		= {
+			[0] = MTD_UADDR_0x5555_0x2AAA /* x8 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_AMD_STD,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x01000,256),
+		}
+	}, {
+		.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_SST,     /* should be CFI */
+               .dev_id         = SST39LF160,
+               .name           = "SST 39LF160",
+               .uaddr          = {
+                       [0] = MTD_UADDR_0x5555_0x2AAA,  /* x8 */
+                       [1] = MTD_UADDR_0x5555_0x2AAA   /* x16 */
+               },
+               .DevSize        = SIZE_2MiB,
+               .CmdSet         = P_ID_AMD_STD,
+               .NumEraseRegions= 2,
+               .regions        = {
+                       ERASEINFO(0x1000,256),
+                       ERASEINFO(0x1000,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,
-		dev_id: M29W040B,
-		name: "ST M29W040B",
-		DevSize: SIZE_512KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x10000,8),
+		.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_MACRONIX,
-		dev_id: MX29LV160T,
-		name: "MXIC MX29LV160T",
-		DevSize: SIZE_2MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,31),
+		.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_MACRONIX,
-		dev_id: MX29LV160B,
-		name: "MXIC MX29LV160B",
-		DevSize: SIZE_2MiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {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_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",
-		DevSize: SIZE_512KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {ERASEINFO(0x10000,7),
+		.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_ST,
+		.dev_id		= M50FW040,
+		.name		= "ST M50FW040",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_512KiB,
+		.CmdSet		= P_ID_INTEL_EXT,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,8),
+		}
+        }, {
+		.mfr_id		= MANUFACTURER_ST,
+		.dev_id		= M50FW080,
+		.name		= "ST M50FW080",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_INTEL_EXT,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,16),
+		}
+        }, {
+		.mfr_id		= MANUFACTURER_ST,
+		.dev_id		= M50FW016,
+		.name		= "ST M50FW016",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_2MiB,
+		.CmdSet		= P_ID_INTEL_EXT,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,32),
+		}
+	}, {
+		.mfr_id		= MANUFACTURER_ST,
+		.dev_id		= M50LPW080,
+		.name		= "ST M50LPW080",
+		.uaddr		= {
+			[0] = MTD_UADDR_UNNECESSARY,    /* x8 */
+		},
+		.DevSize	= SIZE_1MiB,
+		.CmdSet		= P_ID_INTEL_EXT,
+		.NumEraseRegions= 1,
+		.regions	= {
+			ERASEINFO(0x10000,16),
+		}
+	}, {
+		.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),
+			ERASEINFO(0x04000,1)
 		}
         }, {
-		mfr_id: MANUFACTURER_MACRONIX,
-		dev_id: MX29F004B,
-		name: "Macronix MX29F004B",
-		DevSize: SIZE_512KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 4,
-		regions: {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,7),
+			ERASEINFO(0x10000,31)
 		}
         }, {
-		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",
-		DevSize: SIZE_128KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x01000,32),
-		}
-        }, {
-		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_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_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",
-		DevSize: SIZE_128KiB,
-		CmdSet: P_ID_AMD_STD,
-		NumEraseRegions: 1,
-		regions: {ERASEINFO(0x01000,32),
-		}
-        }, {
-		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_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_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_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,48 +1659,94 @@
 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);
 
 static inline u32 jedec_read_mfr(struct map_info *map, __u32 base, 
 	struct cfi_private *cfi)
 {
-	u32 result, mask;
+	map_word result;
+	unsigned long mask;
+	u32 ofs = cfi_build_cmd_addr(0, cfi_interleave(cfi), cfi->device_type);
 	mask = (1 << (cfi->device_type * 8)) -1;
-	result = cfi_read(map, base);
-	result &= mask;
-	return result;
+	result = map_read(map, base + ofs);
+	return result.x[0] & mask;
 }
 
 static inline u32 jedec_read_id(struct map_info *map, __u32 base, 
 	struct cfi_private *cfi)
 {
-	int osf;
-	u32 result, mask;
-	osf = cfi->interleave *cfi->device_type;
+	map_word result;
+	unsigned long mask;
+	u32 ofs = cfi_build_cmd_addr(1, cfi_interleave(cfi), cfi->device_type);
 	mask = (1 << (cfi->device_type * 8)) -1;
-	result = cfi_read(map, base + osf);
-	result &= mask;
-	return result;
+	result = map_read(map, base + ofs);
+	return result.x[0] & mask;
 }
 
 static inline void jedec_reset(u32 base, struct map_info *map, 
 	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) {
+		DEBUG( MTD_DEBUG_LEVEL3,
+		       "reset unlock called %x %x \n",
+		       cfi->addr_unlock1,cfi->addr_unlock2);
+		cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
+		cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, 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;
+	__u8 uaddr;
 
 	printk("Found: %s\n",jedec_table[index].name);
 
@@ -970,42 +1768,166 @@
 	for (i=0; i<num_erase_regions; i++){
 		p_cfi->cfiq->EraseRegionInfo[i] = jedec_table[index].regions[i];
 	}
-	p_cfi->cmdset_priv = 0;
+	p_cfi->cmdset_priv = NULL;
+
+	/* 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 */
+	p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1;
+	p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2;
+
 	return 1; 	/* ok */
 }
 
-static int jedec_probe_chip(struct map_info *map, __u32 base,
-			      struct flchip *chips, struct cfi_private *cfi)
+
+/*
+ * 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 i;
-	int unlockpass = 0;
+	int rc = 0;           /* failure until all tests pass */
+	u32 mfr, id;
+	__u8 uaddr;
 
-	if (!cfi->numchips) {
+	/*
+	 * 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:
-			cfi->addr_unlock1 = 0x555; 
-			cfi->addr_unlock2 = 0x2aa; 
+		mfr = (__u8)finfo->mfr_id;
+		id = (__u8)finfo->dev_id;
 			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;
-			}
+		mfr = (__u16)finfo->mfr_id;
+		id = (__u16)finfo->dev_id;
 			break;
 		case CFI_DEVICETYPE_X32:
-			cfi->addr_unlock1 = 0x1555; 
-			cfi->addr_unlock2 = 0xaaa; 
+		mfr = (__u16)finfo->mfr_id;
+		id = (__u32)finfo->dev_id;
 			break;
 		default:
-			printk(KERN_NOTICE "Eep. Unknown jedec_probe device type %d\n", cfi->device_type);
-		return 0;
+		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(cfi) * ( 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;
+	}
+
+	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 != cfi->addr_unlock1 ||
+		  unlock_addrs[uaddr].addr2 != cfi->addr_unlock2 ) ) {
+		DEBUG( MTD_DEBUG_LEVEL3,
+			"MTD %s(): 0x%.4x 0x%.4x did not match\n",
+			__func__,
+			unlock_addrs[uaddr].addr1,
+			unlock_addrs[uaddr].addr2);
+		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->device_type, NULL);
+		cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL);
+	}
+	cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
+	/* FIXME - should have a delay before continuing */
+
+ match_done:	
+	return rc;
+}
+
+
+static int jedec_probe_chip(struct map_info *map, __u32 base,
+			    unsigned long *chip_map, struct cfi_private *cfi)
+{
+	int i;
+	enum uaddr uaddr_idx = MTD_UADDR_NOT_SUPPORTED;
+	u32 probe_offset1, probe_offset2;
+
  retry:
+	if (!cfi->numchips) {
+		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;
+		cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2;
+	}
+
 	/* Make certain we aren't probing past the end of map */
 	if (base >= map->size) {
 		printk(KERN_NOTICE
@@ -1014,19 +1936,19 @@
 		return 0;
 		
 	}
-	if ((base + cfi->addr_unlock1) >= map->size) {
-		printk(KERN_NOTICE
-			"Probe at addr_unlock1(0x%08x + 0x%08x) past the end of the map(0x%08lx)\n",
-			base, cfi->addr_unlock1, map->size -1);
-
-		return 0;
-	}
-	if ((base + cfi->addr_unlock2) >= map->size) {
-		printk(KERN_NOTICE
-			"Probe at addr_unlock2(0x%08x + 0x%08x) past the end of the map(0x%08lx)\n",
-			base, cfi->addr_unlock2, map->size -1);
-		return 0;
-		
+	/* Ensure the unlock addresses we try stay inside the map */
+	probe_offset1 = cfi_build_cmd_addr(
+		cfi->addr_unlock1, 
+		cfi_interleave(cfi), 
+		cfi->device_type);
+	probe_offset2 = cfi_build_cmd_addr(
+		cfi->addr_unlock1, 
+		cfi_interleave(cfi), 
+		cfi->device_type);
+	if (	((base + probe_offset1 + map_bankwidth(map)) >= map->size) ||
+		((base + probe_offset2 + map_bankwidth(map)) >= map->size))
+	{
+		goto retry;
 	}
 
 	/* Reset */
@@ -1034,10 +1956,11 @@
 
 	/* Autoselect Mode */
 	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(0xaa, cfi->addr_unlock1, base, map, cfi, cfi->device_type, NULL);
+		cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, cfi->device_type, NULL);
 	}
-	cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL);
+	cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, cfi->device_type, 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 +1968,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", 
-			cfi->mfr, cfi->id, cfi->interleave, cfi->device_type);
+		DEBUG(MTD_DEBUG_LEVEL3,
+		      "Search for id:(%02x %02x) interleave(%d) type(%d)\n", 
+			cfi->mfr, cfi->id, cfi_interleave(cfi), 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;
 	} else {
 		__u16 mfr;
 		__u16 id;
@@ -1081,21 +1999,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 +2028,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,29 +2036,23 @@
 		
 	/* 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:
 	/* Put it back into Read Mode */
 	jedec_reset(base, map, cfi);
 
-	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);
+	printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n",
+	       map->name, cfi_interleave(cfi), cfi->device_type*8, base, 
+	       map->bankwidth*8);
 	
 	return 1;
 }
 
 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 +2065,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 -uNrb linux-2.4.30/drivers/mtd/chips/map_absent.c linux/drivers/mtd/chips/map_absent.c
--- linux-2.4.30/drivers/mtd/chips/map_absent.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/chips/map_absent.c	Fri Sep 10 15:15:04 2004
@@ -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 -uNrb linux-2.4.30/drivers/mtd/chips/map_ram.c linux/drivers/mtd/chips/map_ram.c
--- linux-2.4.30/drivers/mtd/chips/map_ram.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/chips/map_ram.c	Fri Sep 10 15:15:04 2004
@@ -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.20 2004/08/09 13:19:43 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;
 }
@@ -102,13 +104,17 @@
 	/* Yeah, it's inefficient. Who cares? It's faster than a _real_
 	   flash erase. */
 	struct map_info *map = (struct map_info *)mtd->priv;
+	map_word allff;
 	unsigned long i;
 
-	for (i=0; i<instr->len; i++)
-		map->write8(map, 0xFF, instr->addr + i);
+	allff = map_word_ff(map);
+
+	for (i=0; i<instr->len; i += map_bankwidth(map))
+		map_write(map, allff, instr->addr + i);
+
+	instr->state = MTD_ERASE_DONE;
 
-	if (instr->callback)
-		instr->callback(instr);
+	mtd_erase_callback(instr);
 
 	return 0;
 }
diff -uNrb linux-2.4.30/drivers/mtd/chips/map_rom.c linux/drivers/mtd/chips/map_rom.c
--- linux-2.4.30/drivers/mtd/chips/map_rom.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/chips/map_rom.c	Fri Sep 10 15:15:04 2004
@@ -1,10 +1,9 @@
 /*
  * 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.21 2004/07/12 14:06:01 dwmw2 Exp $
  */
 
-#include <linux/version.h>
 #include <linux/module.h>
 #include <linux/types.h>
 #include <linux/kernel.h>
@@ -12,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 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 +22,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 +50,7 @@
  	while(mtd->size & (mtd->erasesize - 1))
 		mtd->erasesize >>= 1;
 
-	MOD_INC_USE_COUNT;
+	__module_get(THIS_MODULE);
 	return mtd;
 }
 
@@ -58,7 +59,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 -uNrb linux-2.4.30/drivers/mtd/chips/sharp.c linux/drivers/mtd/chips/sharp.c
--- linux-2.4.30/drivers/mtd/chips/sharp.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/chips/sharp.c	Fri Sep 10 15:15:04 2004
@@ -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.14 2004/08/09 13:19:43 dwmw2 Exp $
  *
  * Devices supported:
  *   LH28F016SCT Symmetrical block flash memory, 2Mx8
@@ -22,14 +22,15 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/types.h>
 #include <linux/sched.h>
 #include <linux/errno.h>
 #include <linux/interrupt.h>
 #include <linux/mtd/map.h>
+#include <linux/mtd/mtd.h>
 #include <linux/mtd/cfi.h>
 #include <linux/delay.h>
+#include <linux/init.h>
 
 #define CMD_RESET		0xffffffff
 #define CMD_READ_ID		0x90909090
@@ -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));
 
@@ -152,7 +155,7 @@
 	map->fldrv = &sharp_chipdrv;
 	map->fldrv_priv = sharp;
 
-	MOD_INC_USE_COUNT;
+	__module_get(THIS_MODULE);
 	return 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);
@@ -422,8 +425,7 @@
 	}
 
 	instr->state = MTD_ERASE_DONE;
-	if(instr->callback)
-		instr->callback(instr);
+	mtd_erase_callback(instr);
 
 	return 0;
 }
@@ -432,18 +434,18 @@
 	unsigned long adr)
 {
 	int ret;
-	int timeo;
+	unsigned long timeo;
 	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 +487,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 +520,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 +540,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 -uNrb linux-2.4.30/drivers/mtd/cmdlinepart.c linux/drivers/mtd/cmdlinepart.c
--- linux-2.4.30/drivers/mtd/cmdlinepart.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/cmdlinepart.c	Tue Sep 28 14:38:13 2004
@@ -1,5 +1,5 @@
 /*
- * $Id: cmdlinepart.c,v 1.6 2002/11/16 01:37:39 dneuer Exp $
+ * $Id: cmdlinepart.c,v 1.15 2004/09/21 12:11:41 lavinen 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,6 @@
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
-#include <asm/setup.h>
 #include <linux/bootmem.h>
 
 /* error message prefix */
@@ -95,7 +94,7 @@
 		if (size < PAGE_SIZE)
 		{
 			printk(KERN_ERR ERRP "partition size too small (%lx)\n", size);
-			return 0;
+			return NULL;
 		}
 	}
 
@@ -122,7 +121,7 @@
 		if ((p = strchr(name, delim)) == 0)
 		{
 			printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim);
-			return 0;
+			return NULL;
 		}
 		name_len = p - name;
 		s = p + 1;
@@ -149,12 +148,12 @@
 		if (size == SIZE_REMAINING)
 		{
 			printk(KERN_ERR ERRP "no partitions allowed after a fill-up partition\n");
-			return 0;
+			return NULL;
 		}
 		/* more partitions follow, parse them */
 		if ((parts = newpart(s + 1, &s, num_parts, 
 		                     this_part + 1, &extra_mem, extra_mem_size)) == 0)
-		  return 0;
+		  return NULL;
 	}
 	else
 	{	/* this is the last partition: allocate space for all */
@@ -167,7 +166,7 @@
 		if (!parts)
 		{
 			printk(KERN_ERR ERRP "out of memory\n");
-			return 0;
+			return NULL;
 		}
 		memset(parts, 0, alloc_size);
 		extra_mem = (unsigned char *)(parts + *num_parts);
@@ -178,8 +177,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 +256,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 +288,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;
@@ -341,7 +339,7 @@
  * main.c::checksetup(). Note that we can not yet kmalloc() anything,
  * so we only save the commandline for later processing.
  */
-static int __init mtdpart_setup(char *s)
+int mtdpart_setup(char *s)
 {
 	cmdline = s;
 	return 1;
@@ -349,7 +347,18 @@
 
 __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);
+}
+
+module_init(cmdline_parser_init);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>");
diff -uNrb linux-2.4.30/drivers/mtd/devices/Config.in linux/drivers/mtd/devices/Config.in
--- linux-2.4.30/drivers/mtd/devices/Config.in	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/devices/Config.in	Fri Sep 10 15:15:04 2004
@@ -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.14 2004/08/10 14:12:29 dmarlin Exp $
 
 mainmenu_option next_comment
 
@@ -10,7 +10,7 @@
    bool '    PMC551 256M DRAM Bugfix' CONFIG_MTD_PMC551_BUGFIX
    bool '    PMC551 Debugging' CONFIG_MTD_PMC551_DEBUG
 fi
-if [ "$CONFIG_DECSTATION" = "y" ]; then
+if [ "$CONFIG_MACH__DECSTATION" = "y" ]; then
    dep_tristate '  DEC MS02-NV NVRAM module support' CONFIG_MTD_MS02NV $CONFIG_MTD
 fi
 dep_tristate '  Uncached system RAM' CONFIG_MTD_SLRAM $CONFIG_MTD
@@ -28,16 +28,26 @@
 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 2000 and Millennium (DEPRECATED)' CONFIG_MTD_DOC2000 $CONFIG_MTD
+   dep_tristate '  M-Systems Disk-On-Chip Millennium-only alternative driver (DEPRECATED)' CONFIG_MTD_DOC2001 $CONFIG_MTD
+   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
+      fi
+   fi
+
+   if [ "$CONFIG_MTD_DOCPROBE" = "y" -o "$CONFIG_MTD_NAND_DISKONCHIP" = "y" ]; then
+      define_bool CONFIG_MTD_DOCECC y
+   else
+      if [ "$CONFIG_MTD_DOCPROBE" = "m" -o "$CONFIG_MTD_NAND_DISKONCHIP" = "m" ]; then
+         define_bool CONFIG_MTD_DOCECC m
+      else
+         define_bool CONFIG_MTD_DOCECC n
       fi
    fi
 
diff -uNrb linux-2.4.30/drivers/mtd/devices/Makefile linux/drivers/mtd/devices/Makefile
--- linux-2.4.30/drivers/mtd/devices/Makefile	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/devices/Makefile	Fri Sep 10 15:15:04 2004
@@ -1,26 +1,23 @@
 #
-# linux/drivers/devices/Makefile
+# linux/drivers/maps/Makefile.24
+# Makefile for obsolete kernels
 #
-# $Id: Makefile,v 1.4 2001/06/26 21:10:05 spse Exp $
+# $Id: Makefile.24,v 1.2 2004/08/09 18:46:04 dmarlin Exp $
 
 O_TARGET	:= devlink.o
+export-objs	:= docecc.o
 
-#                       *** BIG UGLY NOTE ***
-#
-# The removal of get_module_symbol() and replacement with
-# inter_module_register() et al has introduced a link order dependency
-# here where previously there was none.  We now have to ensure that
-# 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_DOCPROBE)	+= docprobe.o docecc.o
+obj-$(CONFIG_MTD_DOC2001PLUS)   += doc2001plus.o
+obj-$(CONFIG_MTD_DOCPROBE)      += docprobe.o
+obj-$(CONFIG_MTD_DOCECC)        += 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
+obj-$(CONFIG_MTD_BLKMTD)        += blkmtd-24.o
 
 include $(TOPDIR)/Rules.make
diff -uNrb linux-2.4.30/drivers/mtd/devices/Makefile.common linux/drivers/mtd/devices/Makefile.common
--- linux-2.4.30/drivers/mtd/devices/Makefile.common	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/devices/Makefile.common	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,24 @@
+#
+# linux/drivers/devices/Makefile
+#
+# $Id: Makefile.common,v 1.6 2004/07/12 16:07:30 dwmw2 Exp $
+
+#                       *** BIG UGLY NOTE ***
+#
+# The removal of get_module_symbol() and replacement with
+# inter_module_register() et al has introduced a link order dependency
+# here where previously there was none.  We now have to ensure that
+# doc200[01].o are linked before docprobe.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
+obj-$(CONFIG_MTD_DOCECC)	+= 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
diff -uNrb linux-2.4.30/drivers/mtd/devices/blkmtd-24.c linux/drivers/mtd/devices/blkmtd-24.c
--- linux-2.4.30/drivers/mtd/devices/blkmtd-24.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/devices/blkmtd-24.c	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,1056 @@
+/*
+ * $Id: blkmtd-24.c,v 1.23 2004/08/09 18:49:42 dmarlin 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 kiobuf.
+ *	When the kiobuf becomes full or the next extry is to an earlier
+ *	block in the kiobuf then it is flushed to disk. This allows
+ *	writes to remained ordered and gives a small and simple outgoing
+ *	write cache.
+ *
+ *	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/iobuf.h>
+#include <linux/slab.h>
+#include <linux/pagemap.h>
+#include <linux/list.h>
+#include <linux/mtd/mtd.h>
+
+#ifdef CONFIG_MTD_DEBUG
+#ifdef CONFIG_PROC_FS
+#  include <linux/proc_fs.h>
+#  define BLKMTD_PROC_DEBUG
+   static struct proc_dir_entry *blkmtd_proc;
+#endif
+#endif
+
+
+#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 KiB, always make it a multiple of PAGE_SIZE */
+#define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10)	/* 128KiB */
+#define VERSION "1.10"
+
+/* Info for the block device */
+struct blkmtd_dev {
+	struct list_head list;
+	struct block_device *binding;
+	struct mtd_info mtd_info;
+	struct kiobuf *rd_buf, *wr_buf;
+	long iobuf_locks;
+	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");
+
+
+/**
+ * read_pages - read in pages via the page cache
+ * @dev: device to read from
+ * @pagenrs: list of page numbers wanted
+ * @pagelst: storage for struce page * pointers
+ * @pages: count of pages wanted
+ *
+ * Read pages, getting them from the page cache if available
+ * else reading them in from disk if not. pagelst must be preallocated
+ * to hold the page count.
+ */
+static int read_pages(struct blkmtd_dev *dev, int pagenrs[], struct page **pagelst, int pages)
+{
+	kdev_t kdev;
+	struct page *page;
+	int cnt = 0;
+	struct kiobuf *iobuf;
+	int err = 0;
+
+	if(!dev) {
+		err("read_pages: PANIC dev == NULL");
+		return -EIO;
+	}
+	kdev = to_kdev_t(dev->binding->bd_dev);
+
+	DEBUG(2, "read_pages: reading %d pages\n", pages);
+	if(test_and_set_bit(0, &dev->iobuf_locks)) {
+		err = alloc_kiovec(1, &iobuf);
+		if (err) {
+			crit("cant allocate kiobuf");
+			return -ENOMEM;
+		}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+		iobuf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL);
+		if(iobuf->blocks == NULL) {
+			crit("cant allocate iobuf blocks");
+			free_kiovec(1, &iobuf);
+			return -ENOMEM;
+		}
+#endif
+	} else {
+		iobuf = dev->rd_buf;
+	}
+
+	iobuf->nr_pages = 0;
+	iobuf->length = 0;
+	iobuf->offset = 0;
+	iobuf->locked = 1;
+	
+	for(cnt = 0; cnt < pages; cnt++) {
+		page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenrs[cnt]);
+		pagelst[cnt] = page;
+		if(!Page_Uptodate(page)) {
+				iobuf->blocks[iobuf->nr_pages] = pagenrs[cnt];
+				iobuf->maplist[iobuf->nr_pages++] = page;
+		}
+	}
+
+	if(iobuf->nr_pages) {
+		iobuf->length = iobuf->nr_pages << PAGE_SHIFT;
+		err = brw_kiovec(READ, 1, &iobuf, kdev, iobuf->blocks, PAGE_SIZE);
+		DEBUG(3, "blkmtd: read_pages: finished, err = %d\n", err);
+		if(err < 0) {
+			while(pages--) {
+				ClearPageUptodate(pagelst[pages]);
+				unlock_page(pagelst[pages]);
+				page_cache_release(pagelst[pages]);
+			}
+		} else {
+			while(iobuf->nr_pages--) {
+				SetPageUptodate(iobuf->maplist[iobuf->nr_pages]);
+			}
+			err = 0;
+		}
+	}
+
+
+	if(iobuf != dev->rd_buf) {
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+		kfree(iobuf->blocks);
+#endif
+		free_kiovec(1, &iobuf);
+	} else {
+		clear_bit(0, &dev->iobuf_locks);
+	}
+	DEBUG(2, "read_pages: done, err = %d\n", err);
+	return err;
+}
+
+
+/**
+ * commit_pages - commit pages in the writeout kiobuf to disk
+ * @dev: device to write to
+ *
+ * If the current dev has pages in the dev->wr_buf kiobuf,
+ * they are written to disk using brw_kiovec()
+ */
+static int commit_pages(struct blkmtd_dev *dev)
+{
+	struct kiobuf *iobuf = dev->wr_buf;
+	kdev_t kdev = to_kdev_t(dev->binding->bd_dev);
+	int err = 0;
+
+	iobuf->length = iobuf->nr_pages << PAGE_SHIFT;
+	iobuf->locked = 1;
+	if(iobuf->length) {
+		int i;
+		DEBUG(2, "blkmtd: commit_pages: nrpages = %d\n", iobuf->nr_pages);
+		/* Check all the pages are dirty and lock them */
+		for(i = 0; i < iobuf->nr_pages; i++) {
+			struct page *page = iobuf->maplist[i];
+			BUG_ON(!PageDirty(page));
+			lock_page(page);
+		}
+		err = brw_kiovec(WRITE, 1, &iobuf, kdev, iobuf->blocks, PAGE_SIZE);
+		DEBUG(3, "commit_write: committed %d pages err = %d\n", iobuf->nr_pages, err);
+		while(iobuf->nr_pages) {
+			struct page *page = iobuf->maplist[--iobuf->nr_pages];
+			ClearPageDirty(page);
+			SetPageUptodate(page);
+			unlock_page(page);
+			page_cache_release(page);
+		}
+	}
+
+	DEBUG(2, "blkmtd: sync: end, err = %d\n", err);
+	iobuf->offset = 0;
+	iobuf->nr_pages = 0;
+	iobuf->length = 0;
+	return err;
+}
+
+
+/**
+ * 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 wr_buf kiobuf
+ * until this becomes full or the next page written to has a lower pagenr
+ * then the current max pagenr in the kiobuf.
+ */
+static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to,
+		    size_t len, int *retlen)
+{
+	int pagenr, offset;
+	size_t start_len = 0, end_len;
+	int pagecnt = 0;
+	struct kiobuf *iobuf = dev->wr_buf;
+	int err = 0;
+	struct page *pagelst[2];
+	int pagenrs[2];
+	int readpages = 0;
+	int ignorepage = -1;
+
+	pagenr = to >> PAGE_SHIFT;
+	offset = to & ~PAGE_MASK;
+
+	DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %zd pagenr = %d offset = %d\n",
+	      buf, (long)to, len, pagenr, offset);
+
+	*retlen = 0;
+	/* 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) {
+		pagenrs[0] = pagenr;
+		readpages++;
+		pagecnt++;
+	}
+	if(len)
+		pagecnt += len >> PAGE_SHIFT;
+	if(end_len) {
+		pagenrs[readpages] = pagenr + pagecnt;
+		readpages++;
+		pagecnt++;
+	}
+
+	DEBUG(3, "blkmtd: write: start_len = %zd len = %zd end_len = %zd pagecnt = %d\n",
+	      start_len, len, end_len, pagecnt);
+
+	down(&dev->wrbuf_mutex);
+
+	if(iobuf->nr_pages && ((pagenr <= iobuf->blocks[iobuf->nr_pages-1])
+			       || (iobuf->nr_pages + pagecnt) >= KIO_STATIC_PAGES)) {
+
+		if((pagenr == iobuf->blocks[iobuf->nr_pages-1])
+		   && ((iobuf->nr_pages + pagecnt) < KIO_STATIC_PAGES)) {
+			iobuf->nr_pages--;
+			ignorepage = pagenr;
+		} else {
+			DEBUG(3, "blkmtd: doing writeout pagenr = %d max_pagenr = %ld pagecnt = %d idx = %d\n",
+			      pagenr, iobuf->blocks[iobuf->nr_pages-1],
+			      pagecnt, iobuf->nr_pages);
+			commit_pages(dev);
+		}
+	}
+	
+	if(readpages) {
+		err = read_pages(dev, pagenrs, pagelst, readpages);
+		if(err < 0)
+			goto readin_err;
+	}
+
+	if(start_len) {
+		/* do partial start region */
+		struct page *page;
+
+		DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %zd offset = %d\n",
+		      pagenr, start_len, offset);
+		page = pagelst[0];
+		BUG_ON(!buf);
+		if(PageDirty(page) && pagenr != ignorepage) {
+			err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d ignorepage = %d\n",
+			    to, start_len, len, end_len, pagenr, ignorepage);
+			BUG();
+		}
+		memcpy(page_address(page)+offset, buf, start_len);
+		SetPageDirty(page);
+		SetPageUptodate(page);
+		unlock_page(page);
+		buf += start_len;
+		*retlen = start_len;
+		err = 0;
+		iobuf->blocks[iobuf->nr_pages] = pagenr++;
+		iobuf->maplist[iobuf->nr_pages] = page;
+		iobuf->nr_pages++;
+	}
+
+	/* 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->binding->bd_inode->i_mapping, pagenr);
+			if(PageDirty(page) && pagenr != ignorepage) {
+				BUG();
+			}
+			if(!page) {
+				warn("write: cant 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;
+			}
+			iobuf->blocks[iobuf->nr_pages] = pagenr++;
+			iobuf->maplist[iobuf->nr_pages] = page;
+			iobuf->nr_pages++;
+			SetPageDirty(page);
+			SetPageUptodate(page);
+			unlock_page(page);
+			pagesc--;
+			*retlen += PAGE_SIZE;
+		}
+	}
+
+	if(end_len) {
+		/* do the third region */
+		struct page *page;
+		DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %zd\n",
+		      pagenr, end_len);
+		page = pagelst[readpages-1];
+		BUG_ON(!buf);
+		if(PageDirty(page) && pagenr != ignorepage) {
+			err("to = %lld start_len = %zd len = %zd end_len = %zd pagenr = %d ignorepage = %d\n",
+			    to, start_len, len, end_len, pagenr, ignorepage);
+			BUG();
+		}
+		memcpy(page_address(page), buf, end_len);
+		SetPageDirty(page);
+		SetPageUptodate(page);
+		unlock_page(page);
+		DEBUG(3, "blkmtd: write: writing out partial end\n");
+		*retlen += end_len;
+		err = 0;
+		iobuf->blocks[iobuf->nr_pages] = pagenr;
+		iobuf->maplist[iobuf->nr_pages] = page;
+		iobuf->nr_pages++;
+	}
+
+	DEBUG(2, "blkmtd: write: end, retlen = %zd, err = %d\n", *retlen, err);
+
+	if(sync) {
+write_err:
+		commit_pages(dev);
+	}
+
+readin_err:
+	up(&dev->wrbuf_mutex);
+	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;
+	size_t retlen;
+
+	/* check readonly */
+	if(!dev->wr_buf) {
+		err("error: mtd%d trying to erase readonly device %s",
+		    mtd->index, mtd->name);
+		instr->state = MTD_ERASE_FAILED;
+		goto erase_callback;
+	}
+
+	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%zx len = 0x%lx\n",
+	      bdevname(dev->binding->bd_dev), 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%08zX", len, from);
+		instr->state = MTD_ERASE_FAILED;
+		err = -EIO;
+	}
+
+	if(instr->state != MTD_ERASE_FAILED) {
+		/* do the erase */
+		DEBUG(3, "Doing erase from = %zd len = %ld\n", from, len);
+		err = write_pages(dev, NULL, from, len, &retlen);
+		if(err < 0) {
+			err("erase failed err = %d", err);
+			instr->state = MTD_ERASE_FAILED;
+		} else {
+			instr->state = MTD_ERASE_DONE;
+			err = 0;
+		}
+	}
+
+	DEBUG(3, "blkmtd: erase: checking callback\n");
+ erase_callback:
+	mtd_erase_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;
+	struct page **pagelst;
+	int *pagenrs;
+	int i;
+
+	*retlen = 0;
+
+	DEBUG(2, "blkmtd: read: dev = `%s' from = %lld len = %zd buf = %p\n",
+	      bdevname(dev->binding->bd_dev), from, len, buf);
+
+	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);
+
+	pagelst = kmalloc(sizeof(struct page *) * pages, GFP_KERNEL);
+	if(!pagelst)
+		return -ENOMEM;
+	pagenrs = kmalloc(sizeof(int) * pages, GFP_KERNEL);
+	if(!pagenrs) {
+		kfree(pagelst);
+		return -ENOMEM;
+	}
+	for(i = 0; i < pages; i++)
+		pagenrs[i] = pagenr+i;
+
+	err = read_pages(dev, pagenrs, pagelst, pages);
+	if(err)
+		goto readerr;
+
+	pagenr = 0;
+	while(pages) {
+		struct page *page;
+		int cpylen;
+
+		DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr);
+		page = pagelst[pagenr];
+
+		cpylen = (PAGE_SIZE > len) ? len : PAGE_SIZE;
+		if(offset+cpylen > PAGE_SIZE)
+			cpylen = PAGE_SIZE-offset;
+
+		memcpy(buf + *retlen, page_address(page) + offset, cpylen);
+		offset = 0;
+		len -= cpylen;
+		*retlen += cpylen;
+		pagenr++;
+		pages--;
+		unlock_page(page);
+		if(!PageDirty(page))
+			page_cache_release(page);
+	}
+
+ readerr:
+	kfree(pagelst);
+	kfree(pagenrs);
+	DEBUG(2, "blkmtd: end read: retlen = %zd, err = %d\n", *retlen, 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;
+
+	*retlen = 0;
+	if(!len)
+		return 0;
+
+	DEBUG(2, "blkmtd: write: dev = `%s' to = %lld len = %zd buf = %p\n",
+	      bdevname(dev->binding->bd_dev), to, len, buf);
+
+	/* handle readonly and out of range numbers */
+
+	if(!dev->wr_buf) {
+		err("error: trying to write to a readonly device %s", mtd->name);
+		return -EROFS;
+	}
+
+	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)
+		*retlen = 0;
+	else
+		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)
+{
+	struct blkmtd_dev *dev = mtd->priv;
+	struct kiobuf *iobuf = dev->wr_buf;
+
+	DEBUG(2, "blkmtd: sync: called\n");
+	if(iobuf == NULL)
+		return;
+
+	DEBUG(3, "blkmtd: kiovec: length = %d nr_pages = %d\n",
+	      iobuf->length, iobuf->nr_pages);
+	down(&dev->wrbuf_mutex);
+	if(iobuf->nr_pages)
+		commit_pages(dev);
+	up(&dev->wrbuf_mutex);
+}
+
+
+#ifdef BLKMTD_PROC_DEBUG
+/* procfs stuff */
+static int blkmtd_proc_read(char *page, char **start, off_t off,
+			    int count, int *eof, void *data)
+{
+	int len;
+	struct list_head *temp1, *temp2;
+
+	MOD_INC_USE_COUNT;
+
+	/* Count the size of the page lists */
+
+	len = sprintf(page, "dev\twr_idx\tmax_idx\tnrpages\tclean\tdirty\tlocked\tlru\n");
+	list_for_each_safe(temp1, temp2, &blkmtd_device_list) {
+		struct blkmtd_dev *dev = list_entry(temp1,  struct blkmtd_dev,
+						    list);
+		struct list_head *temp;
+		struct page *pagei;
+
+		int clean = 0, dirty = 0, locked = 0, lru = 0;
+		/* Count the size of the page lists */
+		list_for_each(temp, &dev->binding->bd_inode->i_mapping->clean_pages) {
+			pagei = list_entry(temp, struct page, list);
+			clean++;
+			if(PageLocked(pagei))
+				locked++;
+			if(PageDirty(pagei))
+				dirty++;
+			if(PageLRU(pagei))
+				lru++;
+		}
+		list_for_each(temp, &dev->binding->bd_inode->i_mapping->dirty_pages) {
+			pagei = list_entry(temp, struct page, list);
+			if(PageLocked(pagei))
+				locked++;
+			if(PageDirty(pagei))
+				dirty++;
+			if(PageLRU(pagei))
+				lru++;
+		}
+		list_for_each(temp, &dev->binding->bd_inode->i_mapping->locked_pages) {
+			pagei = list_entry(temp, struct page, list);
+			if(PageLocked(pagei))
+				locked++;
+			if(PageDirty(pagei))
+				dirty++;
+			if(PageLRU(pagei))
+				lru++;
+		}
+
+		len += sprintf(page+len, "mtd%d:\t%ld\t%d\t%ld\t%d\t%d\t%d\t%d\n",
+			       dev->mtd_info.index,
+			       (dev->wr_buf && dev->wr_buf->nr_pages) ?
+			       dev->wr_buf->blocks[dev->wr_buf->nr_pages-1] : 0,
+			       (dev->wr_buf) ? dev->wr_buf->nr_pages : 0,
+			       dev->binding->bd_inode->i_mapping->nrpages,
+			       clean, dirty, locked, lru);
+	}
+
+	if(len <= count)
+		*eof = 1;
+
+	MOD_DEC_USE_COUNT;
+	return len;
+}
+#endif
+
+
+static void free_device(struct blkmtd_dev *dev)
+{
+	DEBUG(2, "blkmtd: free_device() dev = %p\n", dev);
+	if(dev) {
+		del_mtd_device(&dev->mtd_info);
+		info("mtd%d: [%s] removed", dev->mtd_info.index,
+		     dev->mtd_info.name + strlen("blkmtd: "));
+		if(dev->mtd_info.eraseregions)
+			kfree(dev->mtd_info.eraseregions);
+		if(dev->mtd_info.name)
+			kfree(dev->mtd_info.name);
+
+		if(dev->rd_buf) {
+			dev->rd_buf->locked = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+			if(dev->rd_buf->blocks)
+				kfree(dev->rd_buf->blocks);
+#endif
+			free_kiovec(1, &dev->rd_buf);
+		}
+		if(dev->wr_buf) {
+			dev->wr_buf->locked = 0;
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)			
+			if(dev->wr_buf->blocks)
+				kfree(dev->rw_buf->blocks);
+#endif
+			free_kiovec(1, &dev->wr_buf);
+		}
+
+		if(dev->binding) {
+			kdev_t kdev = to_kdev_t(dev->binding->bd_dev);
+			invalidate_inode_pages(dev->binding->bd_inode);
+			set_blocksize(kdev, 1 << 10);
+			blkdev_put(dev->binding, 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 = %zd size = %zd 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 = %zd size = %zd regions = %d\n",
+	      erase_size, total_size, *regions);
+	return info;
+}
+
+
+extern kdev_t name_to_kdev_t(char *line) __init;
+
+
+static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size)
+{
+	int maj, min;
+	kdev_t kdev;
+	int mode;
+	struct blkmtd_dev *dev;
+
+#ifdef MODULE
+	struct file *file = NULL;
+	struct inode *inode;
+#endif
+
+	if(!devname)
+		return NULL;
+
+	/* Get a handle on the device */
+	mode = (readonly) ? O_RDONLY : O_RDWR;
+
+#ifdef MODULE
+
+	file = filp_open(devname, mode, 0);
+	if(IS_ERR(file)) {
+		err("error: cant open device %s", devname);
+		DEBUG(2, "blkmtd: filp_open returned %ld\n", PTR_ERR(file));
+		return NULL;
+	}
+
+	/* determine is this is a block device and
+	 * if so get its major and minor numbers
+	 */
+	inode = file->f_dentry->d_inode;
+	if(!S_ISBLK(inode->i_mode)) {
+		err("%s not a block device", devname);
+		filp_close(file, NULL);
+		return NULL;
+	}
+	kdev = inode->i_rdev;
+	filp_close(file, NULL);
+#else
+	kdev = name_to_kdev_t(devname);
+#endif	/* MODULE */
+
+	if(!kdev) {
+		err("bad block device: `%s'", devname);
+		return NULL;
+	}
+
+	maj = MAJOR(kdev);
+	min = MINOR(kdev);
+	DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n",
+	      maj, min);
+
+	if(maj == MTD_BLOCK_MAJOR) {
+		err("attempting to use an MTD device as a block device");
+		return NULL;
+	}
+
+	DEBUG(1, "blkmtd: devname = %s\n", bdevname(kdev));
+
+	dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL);
+	if(dev == NULL)
+		return NULL;
+
+	memset(dev, 0, sizeof(struct blkmtd_dev));
+	if(alloc_kiovec(1, &dev->rd_buf)) {
+		err("cant allocate read iobuf");
+		goto devinit_err;
+	}
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+	dev->rd_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL);
+	if(dev->rd_buf->blocks == NULL) {
+		crit("cant allocate rd_buf blocks");
+		goto devinit_err;
+	}
+#endif
+	
+	if(!readonly) {
+		if(alloc_kiovec(1, &dev->wr_buf)) {
+			err("cant allocate kiobuf - readonly enabled");
+
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
+		} else {
+			dev->wr_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL);
+			if(dev->wr_buf->blocks == NULL) {
+				crit("cant allocate wr_buf blocks - readonly enabled");
+				free_kiovec(1, &iobuf);
+			}
+#endif
+		}
+		if(dev->wr_buf)
+			init_MUTEX(&dev->wrbuf_mutex);
+	}
+
+	/* get the block device */
+	dev->binding = bdget(kdev_t_to_nr(MKDEV(maj, min)));
+	if(blkdev_get(dev->binding, mode, 0, BDEV_RAW))
+		goto devinit_err;
+
+	if(set_blocksize(kdev, PAGE_SIZE)) {
+		err("cant set block size to PAGE_SIZE on %s", bdevname(kdev));
+		goto devinit_err;
+	}
+
+	dev->mtd_info.size = dev->binding->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.read = blkmtd_read;
+	dev->mtd_info.write = blkmtd_write;
+	dev->mtd_info.sync = blkmtd_sync;
+	dev->mtd_info.point = 0;
+	dev->mtd_info.unpoint = 0;
+	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);
+		free_device(dev);
+		return NULL;
+	} else {
+		info("mtd%d: [%s] erase_size = %dKiB %s",
+		     dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "),
+		     dev->mtd_info.erasesize >> 10,
+		     (dev->wr_buf) ? "" : "(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;
+#ifdef BLKMTD_PROC_DEBUG
+	if(blkmtd_proc) {
+		remove_proc_entry("blkmtd_debug", NULL);
+	}
+#endif
+
+	/* 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);
+		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;
+
+	/* 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))
+		goto init_err;
+
+	info("version " VERSION);
+
+#ifdef BLKMTD_PROC_DEBUG
+	/* create proc entry */
+	DEBUG(2, "Creating /proc/blkmtd_debug\n");
+	blkmtd_proc = create_proc_read_entry("blkmtd_debug", 0444,
+					     NULL, blkmtd_proc_read, NULL);
+	if(blkmtd_proc == NULL) {
+		err("Cant create /proc/blkmtd_debug");
+	} else {
+		blkmtd_proc->owner = THIS_MODULE;
+	}
+#endif
+
+	if(!list_empty(&blkmtd_device_list))
+		/* Everything is ok if we got here */
+		return 0;
+
+ init_err:
+	return -EINVAL;
+}
+
+module_init(init_blkmtd);
+module_exit(cleanup_blkmtd);
diff -uNrb linux-2.4.30/drivers/mtd/devices/blkmtd.c linux/drivers/mtd/devices/blkmtd.c
--- linux-2.4.30/drivers/mtd/devices/blkmtd.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/devices/blkmtd.c	Fri Sep 10 15:15:04 2004
@@ -1,5 +1,5 @@
 /*
- * $Id: blkmtd.c,v 1.17 2003/01/24 13:00:24 dwmw2 Exp $
+ * $Id: blkmtd.c,v 1.23 2004/08/09 14:03:19 dwmw2 Exp $
  *
  * blkmtd.c - use a block device as a fake MTD
  *
@@ -12,11 +12,8 @@
  * 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 kiobuf.
- *	When the kiobuf becomes full or the next extry is to an earlier
- *	block in the kiobuf then it is flushed to disk. This allows
- *	writes to remained ordered and gives a small and simple outgoing
- *	write cache.
+ *	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.
@@ -27,20 +24,12 @@
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/blkdev.h>
-#include <linux/iobuf.h>
-#include <linux/slab.h>
+#include <linux/bio.h>
 #include <linux/pagemap.h>
 #include <linux/list.h>
+#include <linux/init.h>
 #include <linux/mtd/mtd.h>
 
-#ifdef CONFIG_MTD_DEBUG
-#ifdef CONFIG_PROC_FS
-#  include <linux/proc_fs.h>
-#  define BLKMTD_PROC_DEBUG
-   static struct proc_dir_entry *blkmtd_proc;
-#endif
-#endif
-
 
 #define err(format, arg...) printk(KERN_ERR "blkmtd: " format "\n" , ## arg)
 #define info(format, arg...) printk(KERN_INFO "blkmtd: " format "\n" , ## arg)
@@ -48,17 +37,15 @@
 #define crit(format, arg...) printk(KERN_CRIT "blkmtd: " format "\n" , ## arg)
 
 
-/* Default erase size in KiB, always make it a multiple of PAGE_SIZE */
+/* Default erase size in K, always make it a multiple of PAGE_SIZE */
 #define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10)	/* 128KiB */
-#define VERSION "1.10"
+#define VERSION "$Revision: 1.23 $"
 
 /* Info for the block device */
 struct blkmtd_dev {
 	struct list_head list;
-	struct block_device *binding;
+	struct block_device *blkdev;
 	struct mtd_info mtd_info;
-	struct kiobuf *rd_buf, *wr_buf;
-	long iobuf_locks;
 	struct semaphore wrbuf_mutex;
 };
 
@@ -91,136 +78,145 @@
 MODULE_PARM_DESC(sync, "1=Synchronous writes");
 
 
-/**
- * read_pages - read in pages via the page cache
- * @dev: device to read from
- * @pagenrs: list of page numbers wanted
- * @pagelst: storage for struce page * pointers
- * @pages: count of pages wanted
- *
- * Read pages, getting them from the page cache if available
- * else reading them in from disk if not. pagelst must be preallocated
- * to hold the page count.
- */
-static int read_pages(struct blkmtd_dev *dev, int pagenrs[], struct page **pagelst, int pages)
+/* completion handler for BIO reads */
+static int bi_read_complete(struct bio *bio, unsigned int bytes_done, int error)
 {
-	kdev_t kdev;
-	struct page *page;
-	int cnt = 0;
-	struct kiobuf *iobuf;
-	int err = 0;
+	if (bio->bi_size)
+		return 1;
 
-	if(!dev) {
-		err("read_pages: PANIC dev == NULL");
-		return -EIO;
-	}
-	kdev = to_kdev_t(dev->binding->bd_dev);
-
-	DEBUG(2, "read_pages: reading %d pages\n", pages);
-	if(test_and_set_bit(0, &dev->iobuf_locks)) {
-		err = alloc_kiovec(1, &iobuf);
-		if (err) {
-			crit("cant allocate kiobuf");
-			return -ENOMEM;
-		}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
-		iobuf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL);
-		if(iobuf->blocks == NULL) {
-			crit("cant allocate iobuf blocks");
-			free_kiovec(1, &iobuf);
-			return -ENOMEM;
-		}
-#endif
+	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 {
-		iobuf = dev->rd_buf;
+			ClearPageUptodate(page);
+			SetPageError(page);
 	}
+		ClearPageDirty(page);
+		unlock_page(page);
+		page_cache_release(page);
+	} while (bvec >= bio->bi_io_vec);
 
-	iobuf->nr_pages = 0;
-	iobuf->length = 0;
-	iobuf->offset = 0;
-	iobuf->locked = 1;
-	
-	for(cnt = 0; cnt < pages; cnt++) {
-		page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenrs[cnt]);
-		pagelst[cnt] = page;
-		if(!PageUptodate(page)) {
-				iobuf->blocks[iobuf->nr_pages] = pagenrs[cnt];
-				iobuf->maplist[iobuf->nr_pages++] = page;
-		}
-	}
+	complete((struct completion*)bio->bi_private);
+	return 0;
+}
 
-	if(iobuf->nr_pages) {
-		iobuf->length = iobuf->nr_pages << PAGE_SHIFT;
-		err = brw_kiovec(READ, 1, &iobuf, kdev, iobuf->blocks, PAGE_SIZE);
-		DEBUG(3, "blkmtd: read_pages: finished, err = %d\n", err);
-		if(err < 0) {
-			while(pages--) {
-				ClearPageUptodate(pagelst[pages]);
-				unlock_page(pagelst[pages]);
-				page_cache_release(pagelst[pages]);
-			}
-		} else {
-			while(iobuf->nr_pages--) {
-				SetPageUptodate(iobuf->maplist[iobuf->nr_pages]);
+
+/* 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;
 			}
-			err = 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_SYNC, bio);
+			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;
+}
 
-	if(iobuf != dev->rd_buf) {
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
-		kfree(iobuf->blocks);
-#endif
-		free_kiovec(1, &iobuf);
-	} else {
-		clear_bit(0, &dev->iobuf_locks);
+
+/* 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;
 	}
-	DEBUG(2, "read_pages: done, err = %d\n", err);
+
+	init_completion(&event);
+	bio->bi_private = &event;
+	bio->bi_end_io = bi_write_complete;
+	submit_bio(WRITE_SYNC, bio);
+	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;
 }
 
 
 /**
- * commit_pages - commit pages in the writeout kiobuf to disk
- * @dev: device to write to
+ * 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
  *
- * If the current dev has pages in the dev->wr_buf kiobuf,
- * they are written to disk using brw_kiovec()
+ * 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 int commit_pages(struct blkmtd_dev *dev)
+static struct bio *blkmtd_add_page(struct bio *bio, struct block_device *blkdev,
+				   struct page *page, int pagecnt)
 {
-	struct kiobuf *iobuf = dev->wr_buf;
-	kdev_t kdev = to_kdev_t(dev->binding->bd_dev);
-	int err = 0;
 
-	iobuf->length = iobuf->nr_pages << PAGE_SHIFT;
-	iobuf->locked = 1;
-	if(iobuf->length) {
-		int i;
-		DEBUG(2, "blkmtd: commit_pages: nrpages = %d\n", iobuf->nr_pages);
-		/* Check all the pages are dirty and lock them */
-		for(i = 0; i < iobuf->nr_pages; i++) {
-			struct page *page = iobuf->maplist[i];
-			BUG_ON(!PageDirty(page));
-			lock_page(page);
-		}
-		err = brw_kiovec(WRITE, 1, &iobuf, kdev, iobuf->blocks, PAGE_SIZE);
-		DEBUG(3, "commit_write: committed %d pages err = %d\n", iobuf->nr_pages, err);
-		while(iobuf->nr_pages) {
-			struct page *page = iobuf->maplist[--iobuf->nr_pages];
-			ClearPageDirty(page);
-			SetPageUptodate(page);
-			unlock_page(page);
-			page_cache_release(page);
-		}
+ 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;
 	}
 
-	DEBUG(2, "blkmtd: sync: end, err = %d\n", err);
-	iobuf->offset = 0;
-	iobuf->nr_pages = 0;
-	iobuf->length = 0;
-	return err;
+	if(bio_add_page(bio, page, PAGE_SIZE, 0) != PAGE_SIZE) {
+		blkmtd_write_out(bio);
+		bio = NULL;
+		goto retry;
+	}
+	return bio;
 }
 
 
@@ -234,30 +230,25 @@
  *
  * 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 wr_buf kiobuf
- * until this becomes full or the next page written to has a lower pagenr
- * then the current max pagenr in the kiobuf.
+ * 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, int *retlen)
+		    size_t len, size_t *retlen)
 {
 	int pagenr, offset;
 	size_t start_len = 0, end_len;
 	int pagecnt = 0;
-	struct kiobuf *iobuf = dev->wr_buf;
 	int err = 0;
-	struct page *pagelst[2];
-	int pagenrs[2];
-	int readpages = 0;
-	int ignorepage = -1;
+	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",
+	DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %zd pagenr = %d offset = %d\n",
 	      buf, (long)to, len, pagenr, offset);
 
-	*retlen = 0;
 	/* see if we have to do a partial write at the start */
 	if(offset) {
 		start_len = ((offset + len) > PAGE_SIZE) ? PAGE_SIZE - offset : len;
@@ -268,68 +259,48 @@
 	end_len = len & ~PAGE_MASK;
 	len -= end_len;
 
-	if(start_len) {
-		pagenrs[0] = pagenr;
-		readpages++;
+	if(start_len)
 		pagecnt++;
-	}
+
 	if(len)
 		pagecnt += len >> PAGE_SHIFT;
-	if(end_len) {
-		pagenrs[readpages] = pagenr + pagecnt;
-		readpages++;
-		pagecnt++;
-	}
 
-	DEBUG(3, "blkmtd: write: start_len = %d len = %d end_len = %d pagecnt = %d\n",
-	      start_len, len, end_len, pagecnt);
+	if(end_len)
+		pagecnt++;
 
 	down(&dev->wrbuf_mutex);
 
-	if(iobuf->nr_pages && ((pagenr <= iobuf->blocks[iobuf->nr_pages-1])
-			       || (iobuf->nr_pages + pagecnt) >= KIO_STATIC_PAGES)) {
-
-		if((pagenr == iobuf->blocks[iobuf->nr_pages-1])
-		   && ((iobuf->nr_pages + pagecnt) < KIO_STATIC_PAGES)) {
-			iobuf->nr_pages--;
-			ignorepage = pagenr;
-		} else {
-			DEBUG(3, "blkmtd: doing writeout pagenr = %d max_pagenr = %ld pagecnt = %d idx = %d\n",
-			      pagenr, iobuf->blocks[iobuf->nr_pages-1],
-			      pagecnt, iobuf->nr_pages);
-			commit_pages(dev);
-		}
-	}
-	
-	if(readpages) {
-		err = read_pages(dev, pagenrs, pagelst, readpages);
-		if(err < 0)
-			goto readin_err;
-	}
+	DEBUG(3, "blkmtd: write: start_len = %zd len = %zd end_len = %zd 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",
+		DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %zd offset = %d\n",
 		      pagenr, start_len, offset);
-		page = pagelst[0];
+
 		BUG_ON(!buf);
-		if(PageDirty(page) && pagenr != ignorepage) {
-			err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d ignorepage = %d\n",
-			    to, start_len, len, end_len, pagenr, ignorepage);
+		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 = %zd len = %zd end_len = %zd pagenr = %d\n",
+			    to, start_len, len, end_len, pagenr);
 			BUG();
 		}
 		memcpy(page_address(page)+offset, buf, start_len);
 		SetPageDirty(page);
 		SetPageUptodate(page);
-		unlock_page(page);
 		buf += start_len;
-		*retlen = start_len;
-		err = 0;
-		iobuf->blocks[iobuf->nr_pages] = pagenr++;
-		iobuf->maplist[iobuf->nr_pages] = page;
-		iobuf->nr_pages++;
+		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 */
@@ -342,12 +313,12 @@
 
 			/* 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->binding->bd_inode->i_mapping, pagenr);
-			if(PageDirty(page) && pagenr != ignorepage) {
+			page = grab_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr);
+			if(PageDirty(page)) {
 				BUG();
 			}
 			if(!page) {
-				warn("write: cant grab cache page %d", pagenr);
+				warn("write: cannot grab cache page %d", pagenr);
 				err = -ENOMEM;
 				goto write_err;
 			}
@@ -357,50 +328,58 @@
 				memcpy(page_address(page), buf, PAGE_SIZE);
 				buf += PAGE_SIZE;
 			}
-			iobuf->blocks[iobuf->nr_pages] = pagenr++;
-			iobuf->maplist[iobuf->nr_pages] = page;
-			iobuf->nr_pages++;
+			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);
-			unlock_page(page);
 			pagesc--;
-			*retlen += PAGE_SIZE;
+			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",
+		DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %zd\n",
 		      pagenr, end_len);
-		page = pagelst[readpages-1];
 		BUG_ON(!buf);
-		if(PageDirty(page) && pagenr != ignorepage) {
-			err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d ignorepage = %d\n",
-			    to, start_len, len, end_len, pagenr, ignorepage);
+		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 = %zd len = %zd end_len = %zd pagenr = %d\n",
+			    to, start_len, len, end_len, pagenr);
 			BUG();
 		}
 		memcpy(page_address(page), buf, end_len);
 		SetPageDirty(page);
 		SetPageUptodate(page);
-		unlock_page(page);
 		DEBUG(3, "blkmtd: write: writing out partial end\n");
-		*retlen += end_len;
-		err = 0;
-		iobuf->blocks[iobuf->nr_pages] = pagenr;
-		iobuf->maplist[iobuf->nr_pages] = page;
-		iobuf->nr_pages++;
+		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;
 	}
-
-	DEBUG(2, "blkmtd: write: end, retlen = %d, err = %d\n", *retlen, err);
-
-	if(sync) {
-write_err:
-		commit_pages(dev);
+		pagenr++;
 	}
 
-readin_err:
+	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 = %zd, err = %d\n", *retlen, err);
 	up(&dev->wrbuf_mutex);
+
+	if(retlen)
+		*retlen = thislen;
 	return err;
 }
 
@@ -414,23 +393,15 @@
 	size_t from;
 	u_long len;
 	int err = -EIO;
-	int retlen;
-
-	/* check readonly */
-	if(!dev->wr_buf) {
-		err("error: mtd%d trying to erase readonly device %s",
-		    mtd->index, mtd->name);
-		instr->state = MTD_ERASE_FAILED;
-		goto erase_callback;
-	}
+	size_t 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",
-	      bdevname(dev->binding->bd_dev), from, len);
+	DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%zx 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);
@@ -446,29 +417,25 @@
 
 	if(!numregions) {
 		/* Not a valid erase block */
-		err("erase: invalid erase request 0x%lX @ 0x%08X", len, from);
+		err("erase: invalid erase request 0x%lX @ 0x%08zX", 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);
+		DEBUG(3, "Doing erase from = %zd len = %ld\n", from, len);
 		err = write_pages(dev, NULL, from, len, &retlen);
-		if(err < 0) {
+		if(err || retlen != len) {
 			err("erase failed err = %d", err);
 			instr->state = MTD_ERASE_FAILED;
 		} else {
 			instr->state = MTD_ERASE_DONE;
-			err = 0;
 		}
 	}
 
 	DEBUG(3, "blkmtd: erase: checking callback\n");
- erase_callback:
-	if (instr->callback) {
-		(*(instr->callback))(instr);
-	}
+	mtd_erase_callback(instr);
 	DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err);
 	return err;
 }
@@ -482,14 +449,15 @@
 	int err = 0;
 	int offset;
 	int pagenr, pages;
-	struct page **pagelst;
-	int *pagenrs;
-	int i;
+	size_t thislen = 0;
 
-	*retlen = 0;
+	DEBUG(2, "blkmtd: read: dev = `%s' from = %lld len = %zd buf = %p\n",
+	      mtd->name+9, from, len, buf);
 
-	DEBUG(2, "blkmtd: read: dev = `%s' from = %ld len = %d buf = %p\n",
-	      bdevname(dev->binding->bd_dev), (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);
@@ -498,48 +466,35 @@
 	DEBUG(3, "blkmtd: read: pagenr = %d offset = %d, pages = %d\n",
 	      pagenr, offset, pages);
 
-	pagelst = kmalloc(sizeof(struct page *) * pages, GFP_KERNEL);
-	if(!pagelst)
-		return -ENOMEM;
-	pagenrs = kmalloc(sizeof(int) * pages, GFP_KERNEL);
-	if(!pagenrs) {
-		kfree(pagelst);
-		return -ENOMEM;
-	}
-	for(i = 0; i < pages; i++)
-		pagenrs[i] = pagenr+i;
-
-	err = read_pages(dev, pagenrs, pagelst, pages);
-	if(err)
-		goto readerr;
-
-	pagenr = 0;
 	while(pages) {
 		struct page *page;
 		int cpylen;
 
 		DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr);
-		page = pagelst[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 + *retlen, page_address(page) + offset, cpylen);
+		memcpy(buf + thislen, page_address(page) + offset, cpylen);
 		offset = 0;
 		len -= cpylen;
-		*retlen += cpylen;
+		thislen += cpylen;
 		pagenr++;
 		pages--;
-		unlock_page(page);
 		if(!PageDirty(page))
 			page_cache_release(page);
 	}
 
  readerr:
-	kfree(pagelst);
-	kfree(pagenrs);
-	DEBUG(2, "blkmtd: end read: retlen = %d, err = %d\n", *retlen, err);
+	if(retlen)
+		*retlen = thislen;
+	DEBUG(2, "blkmtd: end read: retlen = %zd, err = %d\n", thislen, err);
 	return err;
 }
 
@@ -551,32 +506,22 @@
 	struct blkmtd_dev *dev = mtd->priv;
 	int err;
 
-	*retlen = 0;
 	if(!len)
 		return 0;
 
-	DEBUG(2, "blkmtd: write: dev = `%s' to = %ld len = %d buf = %p\n",
-	      bdevname(dev->binding->bd_dev), (long int)to, len, buf);
-
-	/* handle readonly and out of range numbers */
-
-	if(!dev->wr_buf) {
-		err("error: trying to write to a readonly device %s", mtd->name);
-		return -EROFS;
-	}
+	DEBUG(2, "blkmtd: write: dev = `%s' to = %lld len = %zd buf = %p\n",
+	      mtd->name+9, to, len, buf);
 
 	if(to >= mtd->size) {
 		return -ENOSPC;
 	}
 
 	if(to + len > mtd->size) {
-		len = (mtd->size - to);
+		len = mtd->size - to;
 	}
 
 	err = write_pages(dev, buf, to, len, retlen);
-	if(err < 0)
-		*retlen = 0;
-	else
+	if(err > 0)
 		err = 0;
 	DEBUG(2, "blkmtd: write: end, err = %d\n", err);
 	return err;
@@ -586,124 +531,22 @@
 /* sync the device - wait until the write queue is empty */
 static void blkmtd_sync(struct mtd_info *mtd)
 {
-	struct blkmtd_dev *dev = mtd->priv;
-	struct kiobuf *iobuf = dev->wr_buf;
-
-	DEBUG(2, "blkmtd: sync: called\n");
-	if(iobuf == NULL)
-		return;
-
-	DEBUG(3, "blkmtd: kiovec: length = %d nr_pages = %d\n",
-	      iobuf->length, iobuf->nr_pages);
-	down(&dev->wrbuf_mutex);
-	if(iobuf->nr_pages)
-		commit_pages(dev);
-	up(&dev->wrbuf_mutex);
+	/* Currently all writes are synchronous */
 }
 
 
-#ifdef BLKMTD_PROC_DEBUG
-/* procfs stuff */
-static int blkmtd_proc_read(char *page, char **start, off_t off,
-			    int count, int *eof, void *data)
-{
-	int len;
-	struct list_head *temp1, *temp2;
-
-	MOD_INC_USE_COUNT;
-
-	/* Count the size of the page lists */
-
-	len = sprintf(page, "dev\twr_idx\tmax_idx\tnrpages\tclean\tdirty\tlocked\tlru\n");
-	list_for_each_safe(temp1, temp2, &blkmtd_device_list) {
-		struct blkmtd_dev *dev = list_entry(temp1,  struct blkmtd_dev,
-						    list);
-		struct list_head *temp;
-		struct page *pagei;
-
-		int clean = 0, dirty = 0, locked = 0, lru = 0;
-		/* Count the size of the page lists */
-		list_for_each(temp, &dev->binding->bd_inode->i_mapping->clean_pages) {
-			pagei = list_entry(temp, struct page, list);
-			clean++;
-			if(PageLocked(pagei))
-				locked++;
-			if(PageDirty(pagei))
-				dirty++;
-			if(PageLRU(pagei))
-				lru++;
-		}
-		list_for_each(temp, &dev->binding->bd_inode->i_mapping->dirty_pages) {
-			pagei = list_entry(temp, struct page, list);
-			if(PageLocked(pagei))
-				locked++;
-			if(PageDirty(pagei))
-				dirty++;
-			if(PageLRU(pagei))
-				lru++;
-		}
-		list_for_each(temp, &dev->binding->bd_inode->i_mapping->locked_pages) {
-			pagei = list_entry(temp, struct page, list);
-			if(PageLocked(pagei))
-				locked++;
-			if(PageDirty(pagei))
-				dirty++;
-			if(PageLRU(pagei))
-				lru++;
-		}
-
-		len += sprintf(page+len, "mtd%d:\t%ld\t%d\t%ld\t%d\t%d\t%d\t%d\n",
-			       dev->mtd_info.index,
-			       (dev->wr_buf && dev->wr_buf->nr_pages) ?
-			       dev->wr_buf->blocks[dev->wr_buf->nr_pages-1] : 0,
-			       (dev->wr_buf) ? dev->wr_buf->nr_pages : 0,
-			       dev->binding->bd_inode->i_mapping->nrpages,
-			       clean, dirty, locked, lru);
-	}
-
-	if(len <= count)
-		*eof = 1;
-
-	MOD_DEC_USE_COUNT;
-	return len;
-}
-#endif
-
-
 static void free_device(struct blkmtd_dev *dev)
 {
 	DEBUG(2, "blkmtd: free_device() dev = %p\n", dev);
 	if(dev) {
-		del_mtd_device(&dev->mtd_info);
-		info("mtd%d: [%s] removed", dev->mtd_info.index,
-		     dev->mtd_info.name + strlen("blkmtd: "));
 		if(dev->mtd_info.eraseregions)
 			kfree(dev->mtd_info.eraseregions);
 		if(dev->mtd_info.name)
 			kfree(dev->mtd_info.name);
 
-		if(dev->rd_buf) {
-			dev->rd_buf->locked = 0;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
-			if(dev->rd_buf->blocks)
-				kfree(dev->rd_buf->blocks);
-#endif
-			free_kiovec(1, &dev->rd_buf);
-		}
-		if(dev->wr_buf) {
-			dev->wr_buf->locked = 0;
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)			
-			if(dev->wr_buf->blocks)
-				kfree(dev->rw_buf->blocks);
-#endif
-			free_kiovec(1, &dev->wr_buf);
-		}
-
-		if(dev->binding) {
-			kdev_t kdev = to_kdev_t(dev->binding->bd_dev);
-			invalidate_inode_pages(dev->binding->bd_inode);
-			set_blocksize(kdev, 1 << 10);
-			blkdev_put(dev->binding, BDEV_RAW);
+		if(dev->blkdev) {
+			invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping);
+			close_bdev_excl(dev->blkdev);
 		}
 		kfree(dev);
 	}
@@ -720,7 +563,7 @@
 {
 	struct mtd_erase_region_info *info = NULL;
 
-	DEBUG(2, "calc_erase_regions, es = %d size = %d regions = %d\n",
+	DEBUG(2, "calc_erase_regions, es = %zd size = %zd regions = %d\n",
 	      erase_size, total_size, *regions);
 	/* Make any user specified erasesize be a power of 2
 	   and at least PAGE_SIZE */
@@ -768,119 +611,62 @@
 				break;
 		}
 	} while(!(*regions));
-	DEBUG(2, "calc_erase_regions done, es = %d size = %d regions = %d\n",
+	DEBUG(2, "calc_erase_regions done, es = %zd size = %zd regions = %d\n",
 	      erase_size, total_size, *regions);
 	return info;
 }
 
 
-extern kdev_t name_to_kdev_t(char *line) __init;
-
+extern dev_t __init name_to_dev_t(const char *line);
 
 static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size)
 {
-	int maj, min;
-	kdev_t kdev;
+	struct block_device *bdev;
 	int mode;
 	struct blkmtd_dev *dev;
 
-#ifdef MODULE
-	struct file *file = NULL;
-	struct inode *inode;
-#endif
-
 	if(!devname)
 		return NULL;
 
 	/* Get a handle on the device */
-	mode = (readonly) ? O_RDONLY : O_RDWR;
 
-#ifdef MODULE
-
-	file = filp_open(devname, mode, 0);
-	if(IS_ERR(file)) {
-		err("error: cant open device %s", devname);
-		DEBUG(2, "blkmtd: filp_open returned %ld\n", PTR_ERR(file));
-		return NULL;
-	}
 
-	/* determine is this is a block device and
-	 * if so get its major and minor numbers
-	 */
-	inode = file->f_dentry->d_inode;
-	if(!S_ISBLK(inode->i_mode)) {
-		err("%s not a block device", devname);
-		filp_close(file, NULL);
-		return NULL;
-	}
-	kdev = inode->i_rdev;
-	filp_close(file, NULL);
+#ifdef MODULE
+	mode = (readonly) ? O_RDONLY : O_RDWR;
+	bdev = open_bdev_excl(devname, mode, NULL);
 #else
-	kdev = name_to_kdev_t(devname);
-#endif	/* MODULE */
-
-	if(!kdev) {
-		err("bad block device: `%s'", devname);
+	mode = (readonly) ? FMODE_READ : FMODE_WRITE;
+	bdev = open_by_devnum(name_to_dev_t(devname), mode);
+#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;
 	}
 
-	maj = MAJOR(kdev);
-	min = MINOR(kdev);
 	DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n",
-	      maj, min);
+	      MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev));
 
-	if(maj == MTD_BLOCK_MAJOR) {
+	if(MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) {
 		err("attempting to use an MTD device as a block device");
+		blkdev_put(bdev);
 		return NULL;
 	}
 
-	DEBUG(1, "blkmtd: devname = %s\n", bdevname(kdev));
-
 	dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL);
-	if(dev == NULL)
+	if(dev == NULL) {
+		blkdev_put(bdev);
 		return NULL;
-
-	memset(dev, 0, sizeof(struct blkmtd_dev));
-	if(alloc_kiovec(1, &dev->rd_buf)) {
-		err("cant allocate read iobuf");
-		goto devinit_err;
-	}
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
-	dev->rd_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL);
-	if(dev->rd_buf->blocks == NULL) {
-		crit("cant allocate rd_buf blocks");
-		goto devinit_err;
 	}
-#endif
 	
+	memset(dev, 0, sizeof(struct blkmtd_dev));
+	dev->blkdev = bdev;
+	atomic_set(&(dev->blkdev->bd_inode->i_mapping->truncate_count), 0);
 	if(!readonly) {
-		if(alloc_kiovec(1, &dev->wr_buf)) {
-			err("cant allocate kiobuf - readonly enabled");
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,4)
-		} else {
-			dev->wr_buf->blocks = kmalloc(KIO_MAX_SECTORS * sizeof(unsigned long), GFP_KERNEL);
-			if(dev->wr_buf->blocks == NULL) {
-				crit("cant allocate wr_buf blocks - readonly enabled");
-				free_kiovec(1, &iobuf);
-			}
-#endif
-		}
-		if(dev->wr_buf)
 			init_MUTEX(&dev->wrbuf_mutex);
 	}
 
-	/* get the block device */
-	dev->binding = bdget(kdev_t_to_nr(MKDEV(maj, min)));
-	if(blkdev_get(dev->binding, mode, 0, BDEV_RAW))
-		goto devinit_err;
-
-	if(set_blocksize(kdev, PAGE_SIZE)) {
-		err("cant set block size to PAGE_SIZE on %s", bdevname(kdev));
-		goto devinit_err;
-	}
-
-	dev->mtd_info.size = dev->binding->bd_inode->i_size & PAGE_MASK;
+	dev->mtd_info.size = dev->blkdev->bd_inode->i_size & PAGE_MASK;
 
 	/* Setup the MTD structure */
 	/* make the name contain the block device in */
@@ -904,27 +690,26 @@
 	} else {
 		dev->mtd_info.type = MTD_RAM;
 		dev->mtd_info.flags = MTD_CAP_RAM;
-	}
 	dev->mtd_info.erase = blkmtd_erase;
-	dev->mtd_info.read = blkmtd_read;
 	dev->mtd_info.write = blkmtd_write;
+		dev->mtd_info.writev = default_mtd_writev;
 	dev->mtd_info.sync = blkmtd_sync;
-	dev->mtd_info.point = 0;
-	dev->mtd_info.unpoint = 0;
+	}
+	dev->mtd_info.read = blkmtd_read;
+	dev->mtd_info.readv = default_mtd_readv;
 	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)) {
 		/* Device didnt get added, so free the entry */
 		list_del(&dev->list);
-		free_device(dev);
-		return NULL;
+		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,
-		     (dev->wr_buf) ? "" : "(read-only)");
+		     readonly ? "(read-only)" : "");
 	}
 	
 	return dev;
@@ -939,17 +724,16 @@
 static void __devexit cleanup_blkmtd(void)
 {
 	struct list_head *temp1, *temp2;
-#ifdef BLKMTD_PROC_DEBUG
-	if(blkmtd_proc) {
-		remove_proc_entry("blkmtd_debug", NULL);
-	}
-#endif
 
 	/* 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);
 	}
 }
@@ -1020,6 +804,7 @@
 {
 	int i;
 
+	info("version " VERSION);
 	/* Check args - device[0] is the bare minimum*/
 	if(!device[0]) {
 		err("error: missing `device' name\n");
@@ -1030,28 +815,9 @@
 		add_device(device[i], ro[i], erasesz[i] << 10);
 
 	if(list_empty(&blkmtd_device_list))
-		goto init_err;
-
-	info("version " VERSION);
-
-#ifdef BLKMTD_PROC_DEBUG
-	/* create proc entry */
-	DEBUG(2, "Creating /proc/blkmtd_debug\n");
-	blkmtd_proc = create_proc_read_entry("blkmtd_debug", 0444,
-					     NULL, blkmtd_proc_read, NULL);
-	if(blkmtd_proc == NULL) {
-		err("Cant create /proc/blkmtd_debug");
-	} else {
-		blkmtd_proc->owner = THIS_MODULE;
-	}
-#endif
+		return -EINVAL;
 
-	if(!list_empty(&blkmtd_device_list))
-		/* Everything is ok if we got here */
 		return 0;
-
- init_err:
-	return -EINVAL;
 }
 
 module_init(init_blkmtd);
diff -uNrb linux-2.4.30/drivers/mtd/devices/doc2000.c linux/drivers/mtd/devices/doc2000.c
--- linux-2.4.30/drivers/mtd/devices/doc2000.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/devices/doc2000.c	Tue Sep 28 14:38:13 2004
@@ -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.63 2004/09/16 23:51:56 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 kvec *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,
@@ -84,7 +89,7 @@
 /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
 static int _DoC_WaitReady(struct DiskOnChip *doc)
 {
-	unsigned long docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 	unsigned long timeo = jiffies + (HZ * 10);
 
 	DEBUG(MTD_DEBUG_LEVEL3,
@@ -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;
@@ -105,7 +114,8 @@
 
 static inline int DoC_WaitReady(struct DiskOnChip *doc)
 {
-	unsigned long docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
+
 	/* This is inline, to optimise the common case, where it's ready instantly */
 	int ret = 0;
 
@@ -131,7 +141,7 @@
 static inline int DoC_Command(struct DiskOnChip *doc, unsigned char command,
 			      unsigned char xtraflags)
 {
-	unsigned long docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 
 	if (DoC_is_2000(doc))
 		xtraflags |= CDSN_CTRL_FLASH_IO;
@@ -145,6 +155,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);
@@ -161,10 +173,8 @@
 static int DoC_Address(struct DiskOnChip *doc, int numbytes, unsigned long ofs,
 		       unsigned char xtraflags1, unsigned char xtraflags2)
 {
-	unsigned long docptr;
 	int i;
-
-	docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 
 	if (DoC_is_2000(doc))
 		xtraflags1 |= CDSN_CTRL_FLASH_IO;
@@ -206,6 +216,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 
@@ -226,11 +239,9 @@
 {
 	volatile int dummy;
 	int modulus = 0xffff;
-	unsigned long docptr;
+	void __iomem *docptr = doc->virtadr;
 	int i;
 
-	docptr = doc->virtadr;
-
 	if (len <= 0)
 		return;
 
@@ -257,11 +268,9 @@
 /* Write a buffer to DoC, taking care of Millennium odditys */
 static void DoC_WriteBuf(struct DiskOnChip *doc, const u_char * buf, int len)
 {
-	unsigned long docptr;
+	void __iomem *docptr = doc->virtadr;
 	int i;
 
-	docptr = doc->virtadr;
-
 	if (len <= 0)
 		return;
 
@@ -278,7 +287,7 @@
 
 static inline int DoC_SelectChip(struct DiskOnChip *doc, int chip)
 {
-	unsigned long docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 
 	/* Software requirement 11.4.4 before writing DeviceSelect */
 	/* Deassert the CE line to eliminate glitches on the FCE# outputs */
@@ -302,7 +311,7 @@
 
 static inline int DoC_SelectFloor(struct DiskOnChip *doc, int floor)
 {
-	unsigned long docptr = doc->virtadr;
+	void __iomem *docptr = doc->virtadr;
 
 	/* Select the floor (bank) of chips required */
 	WriteDOC(floor, docptr, FloorSelect);
@@ -344,15 +353,25 @@
 
 	/* Read the manufacturer and device id codes from the device */
 
-	/* CDSN Slow IO register see Software Requirement 11.4 item 5. */
+	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 Requirement 11.4 item 5. */
+		/* 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)
@@ -387,10 +406,9 @@
 				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;
+					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 +428,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 +529,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 +553,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 +582,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 +595,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 +608,7 @@
 	init_MUTEX(&this->lock);
 
 	/* Ident all the chips present. */
-	DoC_ScanChips(this);
+	DoC_ScanChips(this, maxchips);
 
 	if (!this->totlen) {
 		kfree(mtd);
@@ -596,20 +627,19 @@
 		    size_t * retlen, u_char * buf)
 {
 	/* Just a special case of doc_read_ecc */
-	return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 0);
+	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, 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;
+	void __iomem *docptr = this->virtadr;
 	struct Nand *mychip;
 	unsigned char syndrome[6];
 	volatile char dummy;
 	int i, len256 = 0, ret=0;
-
-	docptr = this->virtadr;
+	size_t left = len;
 
 	/* Don't allow read past end of device */
 	if (from >= this->totlen)
@@ -617,6 +647,10 @@
 
 	down(&this->lock);
 
+	*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;
@@ -673,7 +707,7 @@
 	DoC_ReadBuf(this, &buf[len256], len - len256);
 
 	/* Let the caller know we completed it */
-	*retlen = len;
+		*retlen += len;
 
 	if (eccbuf) {
 		/* Read the ECC data through the DiskOnChip ECC logic */
@@ -730,11 +764,16 @@
 
 	/* 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))
+		if(0 == ((from + len) & 0x1ff))
 	{
 	    DoC_WaitReady(this);
 	}
 
+		from += len;
+		left -= len;
+		buf += len;
+	}
+
 	up(&this->lock);
 
 	return ret;
@@ -744,21 +783,21 @@
 		     size_t * retlen, const u_char * buf)
 {
 	char eccbuf[6];
-	return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, 0);
+	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, 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 */
-	unsigned long docptr;
+	void __iomem *docptr = this->virtadr;
 	volatile char dummy;
 	int len256 = 0;
 	struct Nand *mychip;
-
-	docptr = this->virtadr;
+	size_t left = len;
+	int status;
 
 	/* Don't allow write past end of device */
 	if (to >= this->totlen)
@@ -766,15 +805,21 @@
 
 	down(&this->lock);
 
+	*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); */
 
@@ -853,6 +898,9 @@
 			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);
@@ -874,10 +922,16 @@
 	DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
 	/* There's an implicit DoC_WaitReady() in DoC_Command */
 
+		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;
@@ -886,7 +940,7 @@
 	}
 
 	/* Let the caller know we completed it */
-	*retlen = len;
+		*retlen += len;
 		
 	if (eccbuf) {
 		unsigned char x[8];
@@ -901,25 +955,90 @@
 		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;
+	}
+
 	up(&this->lock);
 	return 0;
 }
 
+static int doc_writev_ecc(struct mtd_info *mtd, const struct kvec *vecs, 
+			  unsigned long count, loff_t to, size_t *retlen,
+			  u_char *eccbuf, struct nand_oobinfo *oobsel)
+{
+	static char static_buf[512];
+	static DECLARE_MUTEX(writev_buf_sem);
+
+	size_t totretlen = 0;
+	size_t thisvecofs = 0;
+	int ret= 0;
+
+	down(&writev_buf_sem);
+
+	while(count) {
+		size_t thislen, thisretlen;
+		unsigned char *buf;
+
+		buf = vecs->iov_base + thisvecofs;
+		thislen = vecs->iov_len - thisvecofs;
+
+
+		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);
+
+		totretlen += thisretlen;
+
+		if (ret || thisretlen != thislen)
+			break;
+
+		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)
 {
 	struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
 	int len256 = 0, ret;
-	unsigned long docptr;
 	struct Nand *mychip;
 
 	down(&this->lock);
 
-	docptr = this->virtadr;
-
 	mychip = &this->chips[ofs >> this->chipshift];
 
 	if (this->curfloor != mychip->floor) {
@@ -974,9 +1093,10 @@
 {
 	struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv;
 	int len256 = 0;
-	unsigned long docptr = this->virtadr;
+	void __iomem *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 +1145,16 @@
 		DoC_Command(this, NAND_CMD_STATUS, 0);
 		/* DoC_WaitReady() is implicit in DoC_Command */
 
+		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 +1170,16 @@
 	DoC_Command(this, NAND_CMD_STATUS, 0);
 	/* DoC_WaitReady() is implicit in DoC_Command */
 
+	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;
@@ -1078,8 +1210,9 @@
 	__u32 ofs = instr->addr;
 	__u32 len = instr->len;
 	volatile int dummy;
-	unsigned long docptr;
+	void __iomem *docptr = this->virtadr;
 	struct Nand *mychip;
+	int status;
 
  	down(&this->lock);
 
@@ -1090,8 +1223,6 @@
 
 	instr->state = MTD_ERASING;
 		
-	docptr = this->virtadr;
-
 	/* FIXME: Do this in the background. Use timers or schedule_task() */
 	while(len) {
 		mychip = &this->chips[ofs >> this->chipshift];
@@ -1111,10 +1242,16 @@
 
 		DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP);
 
+		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 erasing at 0x%x\n", ofs);
 			/* There was an error */
 			instr->state = MTD_ERASE_FAILED;
@@ -1126,8 +1263,7 @@
 	instr->state = MTD_ERASE_DONE;
 
  callback:
-	if (instr->callback)
-		instr->callback(instr);
+	mtd_erase_callback(instr);
 
 	up(&this->lock);
 	return 0;
diff -uNrb linux-2.4.30/drivers/mtd/devices/doc2001.c linux/drivers/mtd/devices/doc2001.c
--- linux-2.4.30/drivers/mtd/devices/doc2001.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/devices/doc2001.c	Tue Sep 28 14:38:13 2004
@@ -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.45 2004/09/16 23:51:57 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>
@@ -37,9 +38,11 @@
 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_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,
@@ -49,7 +52,7 @@
 static struct mtd_info *docmillist = NULL;
 
 /* Perform the required delay cycles by reading from the NOP register */
-static void DoC_Delay(unsigned long docptr, unsigned short cycles)
+static void DoC_Delay(void __iomem * docptr, unsigned short cycles)
 {
 	volatile char dummy;
 	int i;
@@ -59,7 +62,7 @@
 }
 
 /* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
-static int _DoC_WaitReady(unsigned long docptr)
+static int _DoC_WaitReady(void __iomem * docptr)
 {
 	unsigned short c = 0xffff;
 
@@ -76,7 +79,7 @@
 	return (c == 0);
 }
 
-static inline int DoC_WaitReady(unsigned long docptr)
+static inline int DoC_WaitReady(void __iomem * docptr)
 {
 	/* This is inline, to optimise the common case, where it's ready instantly */
 	int ret = 0;
@@ -100,7 +103,7 @@
    with the internal pipeline. Each of 4 delay cycles (read from the NOP register) is
    required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */
 
-static inline void DoC_Command(unsigned long docptr, unsigned char command,
+static inline void DoC_Command(void __iomem * docptr, unsigned char command,
 			       unsigned char xtraflags)
 {
 	/* Assert the CLE (Command Latch Enable) line to the flash chip */
@@ -120,7 +123,7 @@
    with the internal pipeline. Each of 4 delay cycles (read from the NOP register) is
    required after writing to CDSN Control register, see Software Requirement 11.4 item 3. */
 
-static inline void DoC_Address(unsigned long docptr, int numbytes, unsigned long ofs,
+static inline void DoC_Address(void __iomem * docptr, int numbytes, unsigned long ofs,
 			       unsigned char xtraflags1, unsigned char xtraflags2)
 {
 	/* Assert the ALE (Address Latch Enable) line to the flash chip */
@@ -158,7 +161,7 @@
 }
 
 /* DoC_SelectChip: Select a given flash chip within the current floor */
-static int DoC_SelectChip(unsigned long docptr, int chip)
+static int DoC_SelectChip(void __iomem * docptr, int chip)
 {
 	/* Select the individual flash chip requested */
 	WriteDOC(chip, docptr, CDSNDeviceSelect);
@@ -169,7 +172,7 @@
 }
 
 /* DoC_SelectFloor: Select a given floor (bank of flash chips) */
-static int DoC_SelectFloor(unsigned long docptr, int floor)
+static int DoC_SelectFloor(void __iomem * docptr, int floor)
 {
 	/* Select the floor (bank) of chips required */
 	WriteDOC(floor, docptr, FloorSelect);
@@ -226,7 +229,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 +362,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;
@@ -402,17 +406,18 @@
 		     size_t *retlen, u_char *buf)
 {
 	/* Just a special case of doc_read_ecc */
-	return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 0);
+	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, int oobsel)
+			 size_t *retlen, u_char *buf, u_char *eccbuf,
+			 struct nand_oobinfo *oobsel)
 {
 	int i, ret;
 	volatile char dummy;
 	unsigned char syndrome[6];
 	struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
-	unsigned long docptr = this->virtadr;
+	void __iomem *docptr = this->virtadr;
 	struct Nand *mychip = &this->chips[from >> (this->chipshift)];
 
 	/* Don't allow read past end of device */
@@ -528,16 +533,17 @@
 		      size_t *retlen, const u_char *buf)
 {
 	char eccbuf[6];
-	return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, 0);
+	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, int oobsel)
+			  size_t *retlen, const u_char *buf, u_char *eccbuf,
+			 struct nand_oobinfo *oobsel)
 {
 	int i,ret = 0;
 	volatile char dummy;
 	struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
-	unsigned long docptr = this->virtadr;
+	void __iomem *docptr = this->virtadr;
 	struct Nand *mychip = &this->chips[to >> (this->chipshift)];
 
 	/* Don't allow write past end of device */
@@ -672,7 +678,7 @@
 #endif
 	volatile char dummy;
 	struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
-	unsigned long docptr = this->virtadr;
+	void __iomem *docptr = this->virtadr;
 	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
 
 	/* Find the chip which is to be used and select it */
@@ -724,7 +730,7 @@
 	volatile char dummy;
 	int ret = 0;
 	struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
-	unsigned long docptr = this->virtadr;
+	void __iomem *docptr = this->virtadr;
 	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
 
 	/* Find the chip which is to be used and select it */
@@ -793,7 +799,7 @@
 	struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv;
 	__u32 ofs = instr->addr;
 	__u32 len = instr->len;
-	unsigned long docptr = this->virtadr;
+	void __iomem *docptr = this->virtadr;
 	struct Nand *mychip = &this->chips[ofs >> this->chipshift];
 
 	if (len != mtd->erasesize) 
@@ -839,8 +845,7 @@
 		instr->state = MTD_ERASE_DONE;
 	dummy = ReadDOC(docptr, LastDataRead);
 
-	if (instr->callback) 
-		instr->callback(instr);
+	mtd_erase_callback(instr);
 
 	return 0;
 }
diff -uNrb linux-2.4.30/drivers/mtd/devices/doc2001plus.c linux/drivers/mtd/devices/doc2001plus.c
--- linux-2.4.30/drivers/mtd/devices/doc2001plus.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/devices/doc2001plus.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,1154 @@
+/*
+ * 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.10 2004/09/16 23:51:57 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(void __iomem * 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(void __iomem * 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(void __iomem * 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(void __iomem * 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(void __iomem * 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)
+{
+	void __iomem * 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(void __iomem * 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(void __iomem * 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(void __iomem * 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(void __iomem * 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;
+	void __iomem * 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(conf, this->virtadr, 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;
+	void __iomem * 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;
+	void __iomem * 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;
+	void __iomem * 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;
+	void __iomem * 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;
+	void __iomem * 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;
+	void __iomem * 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);
+
+	mtd_erase_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 -uNrb linux-2.4.30/drivers/mtd/devices/docecc.c linux/drivers/mtd/devices/docecc.c
--- linux-2.4.30/drivers/mtd/devices/docecc.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/devices/docecc.c	Fri Sep 10 15:15:04 2004
@@ -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
@@ -518,6 +518,8 @@
     kfree(Index_of);
     return nb_errors;
 }
+
+EXPORT_SYMBOL_GPL(doc_decode_ecc);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Fabrice Bellard <fabrice.bellard@netgem.com>");
diff -uNrb linux-2.4.30/drivers/mtd/devices/docprobe.c linux/drivers/mtd/devices/docprobe.c
--- linux-2.4.30/drivers/mtd/devices/docprobe.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/devices/docprobe.c	Tue Sep 28 14:38:13 2004
@@ -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.42 2004/09/16 23:51:57 gleixner 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)
+		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)
+		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:
-#ifndef CONFIG_MTD_DOCPROBE_55AA
-		printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n",
+			break;
+		}
+		/* FALL TRHU */
+
+	default:
+
+#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);
 
@@ -215,12 +270,18 @@
 		memset((char *)this, 0, sizeof(struct DiskOnChip));
 
 		mtd->priv = this;
-		this->virtadr = docptr;
+		this->virtadr = (void __iomem *)docptr;
 		this->physadr = physadr;
 		this->ChipID = ChipID;
 		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 -uNrb linux-2.4.30/drivers/mtd/devices/lart.c linux/drivers/mtd/devices/lart.c
--- linux-2.4.30/drivers/mtd/devices/lart.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/devices/lart.c	Fri Sep 10 15:15:04 2004
@@ -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.7 2004/08/09 13:19:44 dwmw2 Exp $
  *
  * Author: Abraham vd Merwe <abraham@2d3d.co.za>
  *
@@ -42,7 +42,7 @@
 #include <linux/kernel.h>
 #include <linux/module.h>
 #include <linux/types.h>
-#include <linux/version.h>
+#include <linux/init.h>
 #include <linux/errno.h>
 #include <linux/mtd/mtd.h>
 #ifdef HAVE_PARTITIONS
@@ -433,7 +433,7 @@
 	 }
 
    instr->state = MTD_ERASE_DONE;
-   if (instr->callback) instr->callback (instr);
+   mtd_erase_callback(instr);
 
    return (0);
 }
@@ -584,45 +584,40 @@
 
 static struct mtd_info mtd;
 
-static struct mtd_erase_region_info erase_regions[] =
-{
+static struct mtd_erase_region_info erase_regions[] = {
    /* parameter blocks */
    {
-	     offset: 0x00000000,
-	  erasesize: FLASH_BLOCKSIZE_PARAM,
-	  numblocks: FLASH_NUMBLOCKS_16m_PARAM
+		.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
+		.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[] =
-{
+static struct mtd_partition lart_partitions[] = {
    /* blob */
    {
-	       name: "blob",
-	     offset: BLOB_START,
-	       size: BLOB_LEN,
-	 mask_flags: 0
+		.name	= "blob",
+		.offset	= BLOB_START,
+		.size	= BLOB_LEN,
    },
    /* kernel */
    {
-	       name: "kernel",
-	     offset: KERNEL_START,			/* MTDPART_OFS_APPEND */
-	       size: KERNEL_LEN,
-	 mask_flags: 0
+		.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 */
-	 mask_flags: 0
+		.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 -uNrb linux-2.4.30/drivers/mtd/devices/ms02-nv.c linux/drivers/mtd/devices/ms02-nv.c
--- linux-2.4.30/drivers/mtd/devices/ms02-nv.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/devices/ms02-nv.c	Fri Sep 10 15:15:04 2004
@@ -6,7 +6,7 @@
  *      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.7 2004/07/29 14:16:45 macro Exp $
  */
 
 #include <linux/init.h>
@@ -31,16 +31,16 @@
 static char version[] __initdata =
         "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_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>");
 MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver");
 MODULE_LICENSE("GPL");
 
 
 /*
  * 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 -uNrb linux-2.4.30/drivers/mtd/devices/ms02-nv.h linux/drivers/mtd/devices/ms02-nv.h
--- linux-2.4.30/drivers/mtd/devices/ms02-nv.h	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/devices/ms02-nv.h	Fri Sep 10 15:15:04 2004
@@ -1,31 +1,95 @@
 /*
- *      Copyright (c) 2001 Maciej W. Rozycki
+ *	Copyright (c) 2001, 2003  Maciej W. Rozycki
+ *
+ *	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;
 
diff -uNrb linux-2.4.30/drivers/mtd/devices/mtdram.c linux/drivers/mtd/devices/mtdram.c
--- linux-2.4.30/drivers/mtd/devices/mtdram.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/devices/mtdram.c	Fri Sep 10 15:15:04 2004
@@ -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.33 2004/08/09 13:19:44 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>
 
@@ -55,9 +57,8 @@
   memset((char *)mtd->priv + instr->addr, 0xff, instr->len);
 	
   instr->state = MTD_ERASE_DONE;
+  mtd_erase_callback(instr);
 
-  if (instr->callback)
-    (*(instr->callback))(instr);
   return 0;
 }
 
@@ -136,7 +137,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 -uNrb linux-2.4.30/drivers/mtd/devices/phram.c linux/drivers/mtd/devices/phram.c
--- linux-2.4.30/drivers/mtd/devices/phram.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/devices/phram.c	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,359 @@
+/**
+ *
+ * $Id: phram.c,v 1.2 2004/08/09 13:19:44 dwmw2 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;
+
+	mtd_erase_callback(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 -uNrb linux-2.4.30/drivers/mtd/devices/pmc551.c linux/drivers/mtd/devices/pmc551.c
--- linux-2.4.30/drivers/mtd/devices/pmc551.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/devices/pmc551.c	Fri Sep 10 15:15:04 2004
@@ -1,5 +1,5 @@
 /*
- * $Id: pmc551.c,v 1.22 2003/01/24 13:34:30 dwmw2 Exp $
+ * $Id: pmc551.c,v 1.28 2004/08/09 13:19:44 dwmw2 Exp $
  *
  * PMC551 PCI Mezzanine Ram Device
  *
@@ -82,6 +82,7 @@
  *       * Comb the init routine.  It's still a bit cludgy on a few things.
  */
 
+#include <linux/version.h>
 #include <linux/config.h>
 #include <linux/kernel.h>
 #include <linux/module.h>
@@ -108,12 +109,6 @@
 #include <linux/mtd/pmc551.h>
 #include <linux/mtd/compatmac.h>
 
-#if LINUX_VERSION_CODE > 0x20300
-#define PCI_BASE_ADDRESS(dev) (dev->resource[0].start)
-#else
-#define PCI_BASE_ADDRESS(dev) (dev->base_address[0])
-#endif
-
 static struct mtd_info *pmc551list;
 
 static int pmc551_erase (struct mtd_info *mtd, struct erase_info *instr)
@@ -174,9 +169,7 @@
 	printk(KERN_DEBUG "pmc551_erase() done\n");
 #endif
 
-        if (instr->callback) {
-                (*(instr->callback))(instr);
-	}
+        mtd_erase_callback(instr);
         return 0;
 }
 
@@ -563,7 +556,7 @@
 	       (size<1024)?size:(size<1048576)?size>>10:size>>20,
                (size<1024)?'B':(size<1048576)?'K':'M',
 	       size, ((dcmd&(0x1<<3)) == 0)?"non-":"",
-               PCI_BASE_ADDRESS(dev)&PCI_BASE_ADDRESS_MEM_MASK );
+               (dev->resource[0].start)&PCI_BASE_ADDRESS_MEM_MASK );
 
         /*
          * Check to see the state of the memory
@@ -681,11 +674,6 @@
 
         printk(KERN_INFO PMC551_VERSION);
 
-        if(!pci_present()) {
-                printk(KERN_NOTICE "pmc551: PCI not enabled.\n");
-                return -ENODEV;
-        }
-
         /*
          * PCU-bus chipset probe.
          */
@@ -698,7 +686,7 @@
                 }
 
                 printk(KERN_NOTICE "pmc551: Found PCI V370PDC at 0x%lX\n",
-				    PCI_BASE_ADDRESS(PCI_Device));
+				    PCI_Device->resource[0].start);
 
                 /*
                  * The PMC551 device acts VERY weird if you don't init it
@@ -752,7 +740,7 @@
 			printk(KERN_NOTICE "pmc551: Using specified aperture size %dM\n", asize>>20);
 			priv->asize = asize;
 		}
-                priv->start = ioremap((PCI_BASE_ADDRESS(PCI_Device)
+                priv->start = ioremap(((PCI_Device->resource[0].start)
                                        & PCI_BASE_ADDRESS_MEM_MASK),
                                       priv->asize);
 		
@@ -787,10 +775,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 -uNrb linux-2.4.30/drivers/mtd/devices/slram.c linux/drivers/mtd/devices/slram.c
--- linux-2.4.30/drivers/mtd/devices/slram.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/devices/slram.c	Fri Sep 10 15:15:04 2004
@@ -1,6 +1,6 @@
 /*======================================================================
 
-  $Id: slram.c,v 1.28 2003/01/24 13:35:34 dwmw2 Exp $
+  $Id: slram.c,v 1.31 2004/08/09 13:19:44 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
@@ -98,12 +98,7 @@
 
 	instr->state = MTD_ERASE_DONE;
 
-	if (instr->callback) {
-		(*(instr->callback))(instr);
-	}
-	else {
-		kfree(instr);
-	}
+	mtd_erase_callback(instr);
 
 	return(0);
 }
@@ -199,7 +194,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 -uNrb linux-2.4.30/drivers/mtd/ftl.c linux/drivers/mtd/ftl.c
--- linux-2.4.30/drivers/mtd/ftl.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/ftl.c	Fri Sep 10 15:15:04 2004
@@ -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.53 2004/08/09 13:55:43 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
 
 /*======================================================================
 
@@ -262,19 +167,20 @@
 {
     erase_unit_header_t header;
     loff_t offset, max_offset;
-    int ret;
+    size_t ret;
+    int err;
     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, 
+	err = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &ret, 
 			      (unsigned char *)&header);
 	
-	if (ret) 
-	    return ret;
+	if (err) 
+	    return err;
 
 	if (strcmp(header.DataOrgTuple+3, "FTL100") == 0) break;
     }
@@ -283,15 +189,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 +232,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 +297,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 +362,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 +429,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 +445,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 +492,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 +510,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 +530,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 +538,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 +557,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 +567,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 +655,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 +754,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 +783,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 +817,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 +856,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 +893,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,13 +953,13 @@
 	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) {
 	    printk(KERN_NOTICE "ftl_cs: block write failed!\n");
 	    printk(KERN_NOTICE "ftl_cs:   log_addr = 0x%x, virt_addr"
-		   " = 0x%x, Offset = 0x%x\n", log_addr, virt_addr,
+		   " = 0x%x, Offset = 0x%zx\n", log_addr, virt_addr,
 		   offset);
 	    return -EIO;
 	}
@@ -1151,164 +985,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];
+	partition_t *part = (void *)dev;
     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 */
+	/* Sort of arbitrary: round size down to 4KiB 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 */
-
-/*======================================================================
-
-    Handler for block device requests
 
-======================================================================*/
-
-static int ftl_reread_partitions(int minor)
-{
-    partition_t *part = myparts[minor >> 4];
-    int i, whole;
+	geo->heads = 1;
+	geo->sectors = 8;
+	geo->cylinders = sect >> 3;
 
-    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;
 }
 
-/*======================================================================
-
-    Handler for block device requests
-
-======================================================================*/
-
-static void do_ftl_request(request_arg_t)
+static int ftl_readsect(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;
-	}
+	return ftl_read((void *)dev, buf, block, 1);
+}
 	
-	end_request((ret == 0) ? 1 : 0);
-    } while (1);
-} /* do_ftl_request */
+static int ftl_writesect(struct mtd_blktrans_dev *dev,
+			      unsigned long block, char *buf)
+{
+	return ftl_write((void *)dev, buf, block, 1);
+}
 
 /*====================================================================*/
 
@@ -1337,19 +1039,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 +1053,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;
-
-    memset(myparts, 0, sizeof(myparts));
+	DEBUG(0, "$Id: ftl.c,v 1.53 2004/08/09 13:55:43 dwmw2 Exp $\n");
     
-    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 -uNrb linux-2.4.30/drivers/mtd/inftlcore.c linux/drivers/mtd/inftlcore.c
--- linux-2.4.30/drivers/mtd/inftlcore.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/inftlcore.c	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,912 @@
+/* 
+ * 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.17 2004/08/09 13:56:48 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->type != MTD_NANDFLASH)
+		return;
+	/* OK, this is moderately ugly.  But probably safe.  Alternatives? */
+	if (memcmp(mtd->name, "DiskOnChip", 10))
+		return;
+
+	if (!mtd->block_isbad) {
+		printk(KERN_ERR
+"INFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
+"Please use the new diskonchip driver under the NAND subsystem.\n");
+		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;
+	memcpy(&inftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo));
+	inftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY;
+
+        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=%p,"
+		"desperate=%d)\n", 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=%p,thisVUC=%d,"
+		"pending=%d)\n", 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_READ(inftl->mbd.mtd, (inftl->EraseSize *
+			BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE,
+			&retlen, movebuf); 
+                if (ret < 0) {
+			ret = MTD_READ(inftl->mbd.mtd, (inftl->EraseSize *
+				BlockMap[block]) + (block * SECTORSIZE),
+				SECTORSIZE, &retlen, movebuf);
+			if (ret != -EIO) 
+                        	DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went "
+					"away on retry?\n");
+                }
+                memset(&oob, 0xff, sizeof(struct inftl_oob));
+                oob.b.Status = oob.b.Status1 = SECTOR_USED;
+                MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) +
+			(block * SECTORSIZE), SECTORSIZE, &retlen,
+			movebuf, (char *)&oob, &inftl->oobinfo);
+	}
+
+	/*
+	 * 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.
+			 */
+			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=%p,"
+		"pending=%d)\n", 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=%p,"
+		"block=%d)\n", 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=%p,"
+		"thisVUC=%d)\n", 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.
+			 */
+			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=%p,"
+		"block=%d)\n", 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;
+	struct inftl_oob oob;
+	char *p, *pend;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=%p,block=%ld,"
+		"buffer=%p)\n", inftl, block, 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;
+		}
+
+		memset(&oob, 0xff, sizeof(struct inftl_oob));
+		oob.b.Status = oob.b.Status1 = SECTOR_USED;
+		MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) +
+			blockofs, SECTORSIZE, &retlen, (char *)buffer,
+			(char *)&oob, &inftl->oobinfo);
+		/*
+		 * need to write SECTOR_USED flags since they are not 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=%p,block=%ld,"
+		"buffer=%p)\n", inftl, block, 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;
+		if (MTD_READ(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen,
+		    buffer))
+			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.17 $, "
+		"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 -uNrb linux-2.4.30/drivers/mtd/inftlmount.c linux/drivers/mtd/inftlmount.c
--- linux-2.4.30/drivers/mtd/inftlmount.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/inftlmount.c	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,801 @@
+/* 
+ * 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.14 2004/08/09 13:57:42 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.14 $";
+
+/*
+ * 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;
+	u8 buf[SECTORSIZE];
+	struct INFTLMediaHeader *mh = &inftl->MediaHdr;
+	struct INFTLPartition *ip;
+	size_t retlen;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=%p)\n", 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;
+
+	/* 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.
+		 */
+		ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize,
+		    SECTORSIZE, &retlen, buf);
+		/* We ignore ret in case the ECC of the MediaHeader is invalid
+		   (which is apparently acceptable) */
+		if (retlen != SECTORSIZE) {
+			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;
+		}
+
+
+		/*
+		 * This is the first we've seen.
+		 * Copy the media header structure into place.
+		 */
+		memcpy(mh, buf, sizeof(struct INFTLMediaHeader));
+
+		/* Read the spare media header at offset 4096 */
+		MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize + 4096,
+		    SECTORSIZE, &retlen, buf);
+		if (retlen != SECTORSIZE) {
+			printk(KERN_WARNING "INFTL: Unable to read spare "
+			       "Media Header\n");
+			return -1;
+		}
+		/* Check if this one is the same as the first one we found. */
+		if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) {
+			printk(KERN_WARNING "INFTL: Primary and spare Media "
+			       "Headers disagree.\n");
+			return -1;
+		}
+
+		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 <<
+				mh->BlockMultiplierBits;
+			inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize;
+			block >>= mh->BlockMultiplierBits;
+		}
+
+		/* 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 (%zd 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 (%zd 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;
+
+		/* Read Bad Erase Unit Table and modify PUtable[] accordingly */
+		for (i = 0; i < inftl->nb_blocks; i++) {
+			int physblock;
+			/* If any of the physical eraseblocks are bad, don't
+			   use the unit. */
+			for (physblock = 0; physblock < inftl->EraseSize; physblock += inftl->mbd.mtd->erasesize) {
+				if (inftl->mbd.mtd->block_isbad(inftl->mbd.mtd, i * inftl->EraseSize + physblock))
+					inftl->PUtable[i] = BLOCK_RESERVED;
+			}
+		}
+
+		inftl->MediaUnit = block;
+		return 0;
+	}
+
+	/* Not found. */
+	return -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)
+{
+	u8 buf[SECTORSIZE + inftl->mbd.mtd->oobsize];
+	size_t retlen;
+	int i;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=%p,"
+		"address=0x%x,len=%d,check_oob=%d)\n", inftl,
+		address, len, check_oob);
+
+	for (i = 0; i < len; i += SECTORSIZE) {
+		if (MTD_READECC(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &inftl->oobinfo) < 0)
+			return -1;
+		if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
+			return -1;
+
+		if (check_oob) {
+			if (memcmpb(buf + SECTORSIZE, 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 ?? 
+ */
+int INFTL_formatblock(struct INFTLrecord *inftl, int block)
+{
+	size_t retlen;
+	struct inftl_unittail uci;
+	struct erase_info *instr = &inftl->instr;
+	int physblock;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=%p,"
+		"block=%d)\n", 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->mbd.mtd->erasesize;
+	/* Erase one physical eraseblock at a time, even though the NAND api
+	   allows us to group them.  This way we if we have a failure, we can
+	   mark only the failed block in the bbt. */
+	for (physblock = 0; physblock < inftl->EraseSize; physblock += instr->len, instr->addr += instr->len) {
+		MTD_ERASE(inftl->mbd.mtd, instr);
+
+		if (instr->state == MTD_ERASE_FAILED) {
+			printk(KERN_WARNING "INFTL: error while formatting block %d\n",
+				block);
+			goto fail;
+		}
+
+		/*
+	 	* 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, instr->len, 1) != 0)
+			goto fail;
+	}
+
+	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;
+	instr->addr = block * inftl->EraseSize + SECTORSIZE * 2;
+	if (MTD_WRITEOOB(inftl->mbd.mtd, instr->addr +
+	    8, 8, &retlen, (char *)&uci) < 0)
+		goto fail;
+	return 0;
+fail:
+	/* could not format, update the bad block table (caller is responsible
+	   for setting the PUtable to BLOCK_RESERVED on failure) */
+	inftl->mbd.mtd->block_markbad(inftl->mbd.mtd, instr->addr);
+	return -1;
+}
+
+/*
+ * 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,
+			 */
+			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;
+	size_t retlen;
+	int i;
+	u8 *ANACtable, ANAC;
+
+	DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=%p)\n", 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 -uNrb linux-2.4.30/drivers/mtd/maps/Config.in linux/drivers/mtd/maps/Config.in
--- linux-2.4.30/drivers/mtd/maps/Config.in	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/Config.in	Fri Sep 10 15:15:04 2004
@@ -1,16 +1,18 @@
 # drivers/mtd/maps/Config.in
 
-# $Id: Config.in,v 1.43 2003/01/24 14:26:38 dwmw2 Exp $
+# $Id: Config.in,v 1.69 2004/09/02 01:27:07 eric 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
+bool '  Support for non-linear mappings of flash chips' CONFIG_MTD_COMPLEX_MAPPINGS
+
+bool '  CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_GEN_PROBE
+if [ "$CONFIG_MTD_PHYSMAP" = "y" ]; then
    hex '    Physical start address of flash mapping' CONFIG_MTD_PHYSMAP_START 0x8000000
    hex '    Physical length of flash mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000
-   int '    Bus width in octets' CONFIG_MTD_PHYSMAP_BUSWIDTH 2
+   int '    Bank width in octets' CONFIG_MTD_PHYSMAP_BANKWIDTH 2
 fi
 
 if [ "$CONFIG_SPARC" = "y" -o "$CONFIG_SPARC64" = "y" ]; then
@@ -21,40 +23,82 @@
    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
    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
+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
-   dep_tristate '  CFI Flash device mapped on IBM Redwood-4/5' CONFIG_MTD_REDWOOD $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" ]; 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_MIPS" = "y" -o "$CONFIG_MIPS64" = "y" ]; then
+   if [ "$CONFIG_MIPS_PB1000" = "y" -o "$CONFIG_MIPS_PB1100" = "y" -o "$CONFIG_MIPS_PB1500" = "y" ]; then
+      tristate '  Pb1x00 MTD support' CONFIG_MTD_PB1XXX
+   fi
+   if [ "$CONFIG_MTD_PB1XXX" = "y" -o "$CONFIG_MTD_PB1XXX" = "m" ]; then
+      if [ "$CONFIG_MIPS_PB1500" = "y" -o "$CONFIG_MIPS_PB1100" = "y" ]; then
+         bool '  Pb1[15]00 boot flash device' CONFIG_MTD_PB1500_BOOT 
+         bool '  Pb1[15]00 user flash device (2nd 32MB bank)' CONFIG_MTD_PB1500_USER
+      fi
+   fi
+   if [ "$CONFIG_MIPS_DB1000" = "y" -o "$CONFIG_MIPS_DB1100" = "y" -o "$CONFIG_MIPS_DB1500" = "y" ]; then
+      tristate '  Db1x00 MTD support' CONFIG_MTD_DB1X00
+      if [ "$CONFIG_MTD_DB1X00" = "y" -o "$CONFIG_MTD_DB1X00" = "m" ]; then
+         bool '    Db1x00 boot flash device (1st bank)' CONFIG_MTD_DB1X00_BOOT
+         bool '    Db1x00 user flash device (2nd bank)' CONFIG_MTD_DB1X00_USER
+      fi
+   fi
+   dep_tristate '  Pb1550 MTD support' CONFIG_MTD_PB1550 $CONFIG_MIPS_PB1550
+   dep_tristate '  Db1550 MTD support' CONFIG_MTD_DB1550 $CONFIG_MIPS_DB1550
+   dep_tristate '  XXS1500 boot flash device' CONFIG_MTD_XXS1500 $CONFIG_MIPS_XXS1500
+   if [ "$CONFIG_MTD_PB1550" = "y" -o "$CONFIG_MTD_PB1550" = "m" ]; then
+      bool '  Pb1550 Boot Flash' CONFIG_MTD_PB1550_BOOT
+      bool '  Pb1550 User Parameter Flash' CONFIG_MTD_PB1550_USER
+   fi
+   if [ "$CONFIG_MTD_DB1550" = "y" -o "$CONFIG_MTD_DB1550" = "m" ]; then
+      bool '  Db1550 Boot Flash' CONFIG_MTD_DB1550_BOOT
+      bool '  Db1550 User Parameter Flash' CONFIG_MTD_DB1550_USER
    fi
    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
@@ -63,7 +107,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 +119,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 +144,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 -uNrb linux-2.4.30/drivers/mtd/maps/Makefile linux/drivers/mtd/maps/Makefile
--- linux-2.4.30/drivers/mtd/maps/Makefile	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/Makefile	Fri Sep 10 15:15:04 2004
@@ -1,65 +1,11 @@
 #
-# linux/drivers/maps/Makefile
+# linux/drivers/maps/Makefile.24
+# Makefile for obsolete kernels
 #
-# $Id: Makefile,v 1.37 2003/01/24 14:26:38 dwmw2 Exp $
+# $Id: Makefile.24,v 1.1 2004/07/12 16:08:16 dwmw2 Exp $
 
-BELOW25		:= $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/)
-
-ifeq ($(BELOW25),y)
 O_TARGET	:= mapslink.o
-endif
-
-# Chip mappings
-obj-$(CONFIG_MTD_CDB89712)	+= cdb89712.o
-obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o
-obj-$(CONFIG_MTD_CFI_FLAGADM)	+= cfi_flagadm.o
-obj-$(CONFIG_MTD_CSTM_MIPS_IXX)	+= cstm_mips_ixx.o
-obj-$(CONFIG_MTD_DC21285)	+= dc21285.o
-obj-$(CONFIG_MTD_DILNETPC)	+= dilnetpc.o
-obj-$(CONFIG_MTD_ELAN_104NC)	+= elan-104nc.o
-obj-$(CONFIG_MTD_EPXA10DB)	+= epxa10db-flash.o
-obj-$(CONFIG_MTD_IQ80310)	+= iq80310.o
-obj-$(CONFIG_MTD_L440GX)	+= l440gx.o
-obj-$(CONFIG_MTD_AMD76XROM)	+= amd76xrom.o
-obj-$(CONFIG_MTD_ICH2ROM)	+= ich2rom.o
-obj-$(CONFIG_MTD_TSUNAMI)	+= tsunami_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_PNC2000)	+= pnc2000.o
-obj-$(CONFIG_MTD_PCMCIA)	+= pcmciamtd.o
-obj-$(CONFIG_MTD_RPXLITE)	+= rpxlite.o
-obj-$(CONFIG_MTD_TQM8XXL)	+= tqm8xxl.o
-obj-$(CONFIG_MTD_SA1100)	+= sa1100-flash.o
-obj-$(CONFIG_MTD_SBC_GXX)	+= sbc_gxx.o
-obj-$(CONFIG_MTD_SC520CDP)	+= sc520cdp.o
-obj-$(CONFIG_MTD_NETSC520)	+= netsc520.o
-obj-$(CONFIG_MTD_SUN_UFLASH)	+= sun_uflash.o
-obj-$(CONFIG_MTD_VMAX)		+= vmax301.o
-obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
-obj-$(CONFIG_MTD_DBOX2)		+= dbox2-flash.o
-obj-$(CONFIG_MTD_OCELOT)	+= ocelot.o
-obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
-obj-$(CONFIG_MTD_PCI)		+= pci.o
-obj-$(CONFIG_MTD_PB1000)	+= pb1xxx-flash.o
-obj-$(CONFIG_MTD_PB1100)	+= pb1xxx-flash.o
-obj-$(CONFIG_MTD_PB1500)	+= pb1xxx-flash.o
-obj-$(CONFIG_MTD_LASAT)		+= lasat.o
-obj-$(CONFIG_MTD_AUTCPU12)	+= autcpu12-nvram.o
-obj-$(CONFIG_MTD_EDB7312)	+= edb7312.o
-obj-$(CONFIG_MTD_IMPA7)		+= impa7.o
-obj-$(CONFIG_MTD_FORTUNET)	+= fortunet.o
-obj-$(CONFIG_MTD_REDWOOD)	+= redwood.o
-obj-$(CONFIG_MTD_UCLINUX)	+= uclinux.o
-obj-$(CONFIG_MTD_NETtel)	+= nettel.o
-obj-$(CONFIG_MTD_SCB2_FLASH)	+= scb2_flash.o
+export-objs	:= map_funcs.o
 
+include Makefile.common
 include $(TOPDIR)/Rules.make
diff -uNrb linux-2.4.30/drivers/mtd/maps/Makefile.common linux/drivers/mtd/maps/Makefile.common
--- linux-2.4.30/drivers/mtd/maps/Makefile.common	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/Makefile.common	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,71 @@
+#
+# linux/drivers/maps/Makefile
+#
+# $Id: Makefile.common,v 1.19 2004/09/21 14:27:16 bjd Exp $
+
+ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y)
+obj-$(CONFIG_MTD)		+= map_funcs.o
+endif
+
+# Chip mappings
+obj-$(CONFIG_MTD_CDB89712)	+= cdb89712.o
+obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o
+obj-$(CONFIG_MTD_BAST)		+= bast-flash.o
+obj-$(CONFIG_MTD_CFI_FLAGADM)	+= cfi_flagadm.o
+obj-$(CONFIG_MTD_CSTM_MIPS_IXX)	+= cstm_mips_ixx.o
+obj-$(CONFIG_MTD_DC21285)	+= dc21285.o
+obj-$(CONFIG_MTD_DILNETPC)	+= dilnetpc.o
+obj-$(CONFIG_MTD_ELAN_104NC)	+= elan-104nc.o
+obj-$(CONFIG_MTD_EPXA10DB)	+= epxa10db-flash.o
+obj-$(CONFIG_MTD_IQ80310)	+= iq80310.o
+obj-$(CONFIG_MTD_L440GX)	+= l440gx.o
+obj-$(CONFIG_MTD_AMD76XROM)	+= amd76xrom.o
+obj-$(CONFIG_MTD_ICHXROM)	+= ichxrom.o
+obj-$(CONFIG_MTD_TSUNAMI)	+= tsunami_flash.o
+obj-$(CONFIG_MTD_LUBBOCK)	+= lubbock-flash.o
+obj-$(CONFIG_MTD_MBX860)	+= mbx860.o
+obj-$(CONFIG_MTD_CEIVA)		+= ceiva.o
+obj-$(CONFIG_MTD_OCTAGON)	+= octagon-5066.o
+obj-$(CONFIG_MTD_PHYSMAP)	+= physmap.o 
+obj-$(CONFIG_MTD_PNC2000)	+= pnc2000.o
+obj-$(CONFIG_MTD_PCMCIA)	+= pcmciamtd.o
+obj-$(CONFIG_MTD_RPXLITE)	+= rpxlite.o
+obj-$(CONFIG_MTD_TQM8XXL)	+= tqm8xxl.o
+obj-$(CONFIG_MTD_SA1100)	+= sa1100-flash.o
+obj-$(CONFIG_MTD_IPAQ)		+= ipaq-flash.o
+obj-$(CONFIG_MTD_SBC_GXX)	+= sbc_gxx.o
+obj-$(CONFIG_MTD_SC520CDP)	+= sc520cdp.o
+obj-$(CONFIG_MTD_NETSC520)	+= netsc520.o
+obj-$(CONFIG_MTD_TS5500)	+= ts5500_flash.o
+obj-$(CONFIG_MTD_SUN_UFLASH)	+= sun_uflash.o
+obj-$(CONFIG_MTD_VMAX)		+= vmax301.o
+obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
+obj-$(CONFIG_MTD_DBOX2)		+= dbox2-flash.o
+obj-$(CONFIG_MTD_OCELOT)	+= ocelot.o
+obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
+obj-$(CONFIG_MTD_PCI)		+= pci.o
+obj-$(CONFIG_MTD_PB1XXX)	+= pb1xxx-flash.o
+obj-$(CONFIG_MTD_DB1X00)        += db1x00-flash.o
+obj-$(CONFIG_MTD_PB1550)        += pb1550-flash.o
+obj-$(CONFIG_MTD_DB1550)        += db1550-flash.o
+obj-$(CONFIG_MTD_LASAT)		+= lasat.o
+obj-$(CONFIG_MTD_AUTCPU12)	+= autcpu12-nvram.o
+obj-$(CONFIG_MTD_EDB7312)	+= edb7312.o
+obj-$(CONFIG_MTD_IMPA7)		+= impa7.o
+obj-$(CONFIG_MTD_FORTUNET)	+= fortunet.o
+obj-$(CONFIG_MTD_REDWOOD)	+= redwood.o
+obj-$(CONFIG_MTD_UCLINUX)	+= uclinux.o
+obj-$(CONFIG_MTD_NETtel)	+= nettel.o
+obj-$(CONFIG_MTD_SCB2_FLASH)	+= scb2_flash.o
+obj-$(CONFIG_MTD_EBONY)		+= ebony.o
+obj-$(CONFIG_MTD_OCOTEA)	+= ocotea.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_IXP4XX)	+= ixp4xx.o
+obj-$(CONFIG_MTD_IXP2000)	+= ixp2000.o
+obj-$(CONFIG_MTD_WRSBC8260)	+= wr_sbc82xx_flash.o
+obj-$(CONFIG_MTD_DMV182)	+= dmv182.o
diff -uNrb linux-2.4.30/drivers/mtd/maps/amd76xrom.c linux/drivers/mtd/maps/amd76xrom.c
--- linux-2.4.30/drivers/mtd/maps/amd76xrom.c	Fri Nov 19 12:46:58 2004
+++ linux/drivers/mtd/maps/amd76xrom.c	Tue Sep 28 14:38:13 2004
@@ -2,133 +2,138 @@
  * 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.17 2004/09/18 01:59:56 eric 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/cfi.h>
+#include <linux/mtd/flashchip.h>
 #include <linux/config.h>
 #include <linux/pci.h>
 #include <linux/pci_ids.h>
+#include <linux/list.h>
 
 
-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;
-};
+#define xstr(s) str(s)
+#define str(s) #s
+#define MOD_NAME xstr(KBUILD_BASENAME)
 
-static __u8 amd76xrom_read8(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readb(map->map_priv_1 + ofs);
-}
+#define ADDRESS_NAME_LEN 18
 
-static __u16 amd76xrom_read16(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readw(map->map_priv_1 + ofs);
-}
+#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */
 
-static __u32 amd76xrom_read32(struct map_info *map, unsigned long ofs)
-{
-	return __raw_readl(map->map_priv_1 + ofs);
-}
+struct amd76xrom_window {
+	void __iomem *virt;
+	unsigned long phys;
+	unsigned long size;
+	struct list_head maps;
+	struct resource rsrc;
+	struct pci_dev *pdev;
+};
 
-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);
-}
+struct amd76xrom_map_info {
+	struct list_head list;
+	struct map_info map;
+	struct mtd_info *mtd;
+	struct resource rsrc;
+	char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
+};
 
-static void amd76xrom_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	__raw_writeb(d, map->map_priv_1 + adr);
-	mb();
-}
+static struct amd76xrom_window amd76xrom_window = {
+	.maps = LIST_HEAD_INIT(amd76xrom_window.maps),
+};
 
-static void amd76xrom_write16(struct map_info *map, __u16 d, unsigned long adr)
+static void amd76xrom_cleanup(struct amd76xrom_window *window)
 {
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
+	struct amd76xrom_map_info *map, *scratch;
+	u8 byte;
 
-static void amd76xrom_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__raw_writel(d, map->map_priv_1 + adr);
-	mb();
-}
+	if (window->pdev) {
+		/* Disable writes through the rom window */
+		pci_read_config_byte(window->pdev, 0x40, &byte);
+		pci_write_config_byte(window->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);
+	/* Free all of the mtd devices */
+	list_for_each_entry_safe(map, scratch, &window->maps, list) {
+		if (map->rsrc.parent) {
+			release_resource(&map->rsrc);
+		}
+		del_mtd_device(map->mtd);
+		map_destroy(map->mtd);
+		list_del(&map->list);
+		kfree(map);
+	}
+	if (window->rsrc.parent) 
+		release_resource(&window->rsrc);
+
+	if (window->virt) {
+		iounmap(window->virt);
+		window->virt = NULL;
+		window->phys = 0;
+		window->size = 0;
+		window->pdev = NULL;
+	}
 }
 
-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)
 {
-	struct rom_window {
-		u32 start;
-		u32 size;
-		u8 segen_bits;
-	};
-	static struct rom_window rom_window[] = {
-		{ 0xffb00000, 5*1024*1024, (1<<7) | (1<<6), },
-		{ 0xffc00000, 4*1024*1024, (1<<7), },
-		{ 0xffff0000, 64*1024,     0 },
-		{ 0         , 0,           0 },
-	};
-	static const u32 rom_probe_sizes[] = { 
-		5*1024*1024, 4*1024*1024, 2*1024*1024, 1024*1024, 512*1024, 
-		256*1024, 128*1024, 64*1024, 0};
-	static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", 0 };
+	static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
 	u8 byte;
-	struct amd76xrom_map_info *info = &amd76xrom_map;
-	struct rom_window *window;
-	int i;
-	u32 rom_size;
+	struct amd76xrom_window *window = &amd76xrom_window;
+	struct amd76xrom_map_info *map = 0;
+	unsigned long map_top;
 
-	window = &rom_window[0];
-#if 0
-	while(window->size) {
-		if (request_mem_region(window->start, window->size, "amd76xrom")) {
-			break;
+	/* Remember the pci dev I find the window in */
+	window->pdev = pdev;
+
+	/* Assume the rom window is properly setup, and find it's size */
+	pci_read_config_byte(pdev, 0x43, &byte);
+	if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) {
+		window->phys = 0xffb00000; /* 5MiB */
 		}
-		window++;
+	else if ((byte & (1<<7)) == (1<<7)) {
+		window->phys = 0xffc00000; /* 4MiB */
 	}
-	if (!window->size) {
-		printk(KERN_ERR "amd76xrom: cannot reserve rom window\n");
-		goto err_out_none;
+	else {
+		window->phys = 0xffff0000; /* 64KiB */
+	}
+	window->size = 0xffffffffUL - window->phys + 1UL;
+	
+	/*
+	 * 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.
+	 */
+	window->rsrc.name = MOD_NAME;
+	window->rsrc.start = window->phys;
+	window->rsrc.end   = window->phys + window->size - 1;
+	window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+	if (request_resource(&iomem_resource, &window->rsrc)) {
+		window->rsrc.parent = NULL;
+		printk(KERN_ERR MOD_NAME
+			" %s(): Unable to register resource"
+			" 0x%.08lx-0x%.08lx - kernel bug?\n",
+			__func__,
+			window->rsrc.start, window->rsrc.end);
 	}
-#endif
+
+#if 0
 
 	/* Enable the selected rom window */
 	pci_read_config_byte(pdev, 0x43, &byte);
-	pci_write_config_byte(pdev, 0x43, byte | window->segen_bits);
+	pci_write_config_byte(pdev, 0x43, byte | rwindow->segen_bits);
+#endif
 
 	/* Enable writes through the rom window */
 	pci_read_config_byte(pdev, 0x40, &byte);
@@ -136,78 +141,149 @@
 
 	/* 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);
+	window->virt = ioremap_nocache(window->phys, window->size);
+	if (!window->virt) {
+		printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",
+			window->phys, window->size);
+		goto out;
+	}
+
+	/* Get the first address to look for an rom chip at */
+	map_top = window->phys;
+#if 1
+	/* The probe sequence run over the firmware hub lock
+	 * registers sets them to 0x7 (no access).
+	 * Probe at most the last 4M of the address space.
+	 */
+	if (map_top < 0xffc00000) {
+		map_top = 0xffc00000;
+	}
+#endif
+	/* Loop  through and look for rom chips */
+	while((map_top - 1) < 0xffffffffUL) {
+		struct cfi_private *cfi;
+		unsigned long offset;
+		int i;
 
-	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) {
+		if (!map) {
+			map = kmalloc(sizeof(*map), GFP_KERNEL);
+		}
+		if (!map) {
+			printk(KERN_ERR MOD_NAME ": kmalloc failed");
+			goto out;
+		}
+		memset(map, 0, sizeof(*map));
+		INIT_LIST_HEAD(&map->list);
+		map->map.name = map->map_name;
+		map->map.phys = map_top;
+		offset = map_top - window->phys;
+		map->map.virt = (void __iomem *)
+			(((unsigned long)(window->virt)) + offset);
+		map->map.size = 0xffffffffUL - map_top + 1UL;
+		/* Set the name of the map to the address I am trying */
+		sprintf(map->map_name, "%s @%08lx",
+			MOD_NAME, map->map.phys);
+
+		/* There is no generic VPP support */
+		for(map->map.bankwidth = 32; map->map.bankwidth; 
+			map->map.bankwidth >>= 1)
+		{
+			char **probe_type;
+			/* Skip bankwidths that are not supported */
+			if (!map_bankwidth_supported(map->map.bankwidth))
 			continue;
+
+			/* Setup the map methods */
+			simple_map_init(&map->map);
+
+			/* Try all of the probe methods */
+			probe_type = rom_probe_types;
+			for(; *probe_type; probe_type++) {
+				map->mtd = do_map_probe(*probe_type, &map->map);
+				if (map->mtd)
+					goto found;
 		}
-		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;
+		map_top += ROM_PROBE_STEP_SIZE;
+		continue;
+	found:
+		/* Trim the size if we are larger than the map */
+		if (map->mtd->size > map->map.size) {
+			printk(KERN_WARNING MOD_NAME
+				" rom(%u) larger than window(%lu). fixing...\n",
+				map->mtd->size, map->map.size);
+			map->mtd->size = map->map.size;
+		}
+		if (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.
+			 */
+			map->rsrc.name  = map->map_name;
+			map->rsrc.start = map->map.phys;
+			map->rsrc.end   = map->map.phys + map->mtd->size - 1;
+			map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+			if (request_resource(&window->rsrc, &map->rsrc)) {
+				printk(KERN_ERR MOD_NAME
+					": cannot reserve MTD resource\n");
+				map->rsrc.parent = NULL;
 		}
 	}
-	if (!info->mtd) {
-		goto err_out_iounmap;
+
+		/* Make the whole region visible in the map */
+		map->map.virt = window->virt;
+		map->map.phys = window->phys;
+		cfi = map->map.fldrv_priv;
+		for(i = 0; i < cfi->numchips; i++) {
+			cfi->chips[i].start += offset;
+		}
+		
+		/* Now that the mtd devices is complete claim and export it */
+		map->mtd->owner = THIS_MODULE;
+		if (add_mtd_device(map->mtd)) {
+			map_destroy(map->mtd);
+			map->mtd = 0;
+			goto out;
 	}
-	printk(KERN_NOTICE "amd76xrom chip at offset: %x\n",
-		window->size - rom_size);
 		
-	info->mtd->module = THIS_MODULE;
-	add_mtd_device(info->mtd);
-	info->window_start = window->start;
-	info->window_size = window->size;
-	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:
+		/* Calculate the new value of map_top */
+		map_top += map->mtd->size;
+
+		/* File away the map structure */
+		list_add(&map->list, &window->maps);
+		map = 0;
+	}
+
+ out:
+	/* Free any left over map structures */
+	if (map) {
+		kfree(map);
+	}
+	/* See if I have any map structures */
+	if (list_empty(&window->maps)) {
+		amd76xrom_cleanup(window);
 	return -ENODEV;
+	}
+	return 0;
 }
 
 
 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);
+	struct amd76xrom_window *window = &amd76xrom_window;
 	
-	release_mem_region(info->window_start, info->window_size);
+	amd76xrom_cleanup(window);
 }
 
-static struct pci_device_id amd76xrom_pci_tbl[] __devinitdata = {
+static struct pci_device_id amd76xrom_pci_tbl[] = {
 	{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410,  
 		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 +291,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
 
@@ -226,15 +302,14 @@
 {
 	struct pci_dev *pdev;
 	struct pci_device_id *id;
-	pdev = 0;
+	pdev = NULL;
 	for(id = amd76xrom_pci_tbl; id->vendor; id++) {
-		pdev = pci_find_device(id->vendor, id->device, 0);
+		pdev = pci_find_device(id->vendor, id->device, NULL);
 		if (pdev) {
 			break;
 		}
 	}
 	if (pdev) {
-		amd76xrom_map.pdev = pdev;
 		return amd76xrom_init_one(pdev, &amd76xrom_pci_tbl[0]);
 	}
 	return -ENXIO;
@@ -245,7 +320,7 @@
 
 static void __exit cleanup_amd76xrom(void)
 {
-	amd76xrom_remove_one(amd76xrom_map.pdev);
+	amd76xrom_remove_one(amd76xrom_window.pdev);
 }
 
 module_init(init_amd76xrom);
diff -uNrb linux-2.4.30/drivers/mtd/maps/arctic-mtd.c linux/drivers/mtd/maps/arctic-mtd.c
--- linux-2.4.30/drivers/mtd/maps/arctic-mtd.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/arctic-mtd.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,135 @@
+/*
+ * $Id: arctic-mtd.c,v 1.12 2004/09/16 23:27:12 gleixner Exp $
+ * 
+ * drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for 
+ *                              IBM 405LP Arctic boards.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright (C) 2002, International Business Machines Corporation
+ * All Rights Reserved.
+ *
+ * Bishop Brock
+ * IBM Research, Austin Center for Low-Power Computing
+ * bcbrock@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,
+	.bankwidth	= BUSWIDTH,
+	.phys		= PADDR,
+};
+
+static struct mtd_info *arctic_mtd;
+
+static struct mtd_partition arctic_partitions[PARTITIONS] = {
+	{ .name		= "Filesystem",
+	  .size		= FFS1_SIZE,
+	  .offset	= 0,},
+        { .name		= "Kernel",
+	  .size		= KERNEL_SIZE,
+	  .offset	= FFS1_SIZE,},
+	{ .name		= "Filesystem",
+	  .size		= FFS2_SIZE,
+	  .offset	= FFS1_SIZE + KERNEL_SIZE,},
+	{ .name		= "Firmware",
+	  .size		= FIRMWARE_SIZE,
+	  .offset	= SUBZERO_BOOTFLASH_SIZE - FIRMWARE_SIZE,},
+};
+
+static int __init
+init_arctic_mtd(void)
+{
+	printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
+
+	arctic_mtd_map.virt = (void __iomem *) 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 -uNrb linux-2.4.30/drivers/mtd/maps/autcpu12-nvram.c linux/drivers/mtd/maps/autcpu12-nvram.c
--- linux-2.4.30/drivers/mtd/maps/autcpu12-nvram.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/autcpu12-nvram.c	Tue Sep 28 14:38:13 2004
@@ -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.7 2004/09/16 23:27:12 gleixner 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,80 +33,27 @@
 #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,
+	.bankwidth = 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 = (void __iomem *)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 
@@ -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);
+	map_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 -uNrb linux-2.4.30/drivers/mtd/maps/bast-flash.c linux/drivers/mtd/maps/bast-flash.c
--- linux-2.4.30/drivers/mtd/maps/bast-flash.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/bast-flash.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,227 @@
+/* linux/drivers/mtd/maps/bast_flash.c
+ *
+ * Copyright (c) 2004 Simtec Electronics
+ * Ben Dooks <ben@simtec.co.uk>
+ *
+ * Simtec Bast (EB2410ITX) NOR MTD Mapping driver
+ *
+ * Changelog:
+ *	20-Sep-2004  BJD  Initial version
+ *
+ * $Id: bast-flash.c,v 1.1 2004/09/21 14:29:04 bjd 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/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/mach/flash.h>
+
+#include <asm/arch/map.h>
+#include <asm/arch/bast-map.h>
+#include <asm/arch/bast-cpld.h>
+
+#ifdef CONFIG_MTD_BAST_MAXSIZE
+#define AREA_MAXSIZE (CONFIG_MTD_BAST_MAXSIZE * (1024*1024))
+#else
+#define AREA_MAXSIZE (32*1024*1024)
+#endif
+
+#define PFX "bast-flash: "
+
+struct bast_flash_info {
+	struct mtd_info		*mtd;
+	struct map_info		 map;
+	struct mtd_partition	*partitions;
+	struct resource		*area;
+};
+
+static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+
+static struct bast_flash_info *to_bast_info(struct device *dev)
+{
+	return (struct bast_flash_info *)dev_get_drvdata(dev);
+}
+
+static void bast_flash_setrw(int to)
+{
+	unsigned int val;
+	unsigned long flags;
+
+	local_irq_save(flags);
+	val = __raw_readb(BAST_VA_CTRL3);
+	
+	if (to)
+		val |= BAST_CPLD_CTRL3_ROMWEN;
+	else
+		val &= ~BAST_CPLD_CTRL3_ROMWEN;
+
+	pr_debug("new cpld ctrl3=%02x\n", val);
+
+	__raw_writeb(val, BAST_VA_CTRL3);
+	local_irq_restore(flags);
+}
+
+static int bast_flash_remove(struct device *dev)
+{
+	struct bast_flash_info *info = to_bast_info(dev);
+
+	dev_set_drvdata(dev, NULL);
+
+	if (info == NULL) 
+		return 0;
+
+	if (info->map.virt != NULL)
+		iounmap(info->map.virt);
+
+	if (info->mtd) {
+		del_mtd_partitions(info->mtd);
+		map_destroy(info->mtd);
+	}
+
+	if (info->partitions)
+		kfree(info->partitions);
+
+	if (info->area) {
+		release_resource(info->area);
+		kfree(info->area);
+	}
+	
+	kfree(info);
+
+	return 0;
+}
+
+static int bast_flash_probe(struct device *dev)
+{
+	struct platform_device *pdev = to_platform_device(dev);
+	struct bast_flash_info *info;
+	struct resource *res;
+	int err = 0;
+
+	info = kmalloc(sizeof(*info), GFP_KERNEL);
+	if (info == NULL) {
+		printk(KERN_ERR PFX "no memory for flash info\n");
+		err = -ENOMEM;
+		goto exit_error;
+	}
+
+	memzero(info, sizeof(*info));
+	dev_set_drvdata(dev, info);
+
+	res = pdev->resource;  /* assume that the flash has one resource */
+
+	info->map.phys = res->start;
+	info->map.size = res->end - res->start + 1;
+	info->map.name = dev->bus_id;	
+	info->map.bankwidth = 2;
+	
+	if (info->map.size > AREA_MAXSIZE)
+		info->map.size = AREA_MAXSIZE;
+
+	pr_debug("%s: area %08lx, size %ld\n", __FUNCTION__,
+		 info->map.phys, info->map.size);
+	
+	info->area = request_mem_region(res->start, info->map.size,
+					pdev->name);
+	if (info->area == NULL) {
+		printk(KERN_ERR PFX "cannot reserve flash memory region\n");
+		err = -ENOENT;
+		goto exit_error;
+	}
+
+	info->map.virt = ioremap(res->start, info->map.size);
+	pr_debug("%s: virt at %08x\n", __FUNCTION__, (int)info->map.virt);
+
+	if (info->map.virt == 0) {
+		printk(KERN_ERR PFX "failed to ioremap() region\n");
+		err = -EIO;
+		goto exit_error;
+	}
+ 
+	simple_map_init(&info->map);
+
+	/* enable the write to the flash area */
+
+	bast_flash_setrw(1);
+
+	/* probe for the device(s) */
+
+	info->mtd = do_map_probe("jedec_probe", &info->map);
+	if (info->mtd == NULL)
+		info->mtd = do_map_probe("cfi_probe", &info->map);
+
+	if (info->mtd == NULL) {
+		printk(KERN_ERR PFX "map_probe() failed\n");
+		err = -ENXIO;
+		goto exit_error;
+	}
+
+	/* mark ourselves as the owner */
+	info->mtd->owner = THIS_MODULE;
+
+	err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0);
+	if (err > 0) {
+		err = add_mtd_partitions(info->mtd, info->partitions, err);
+		if (err) 
+			printk(KERN_ERR PFX "cannot add/parse partitions\n");
+	}
+
+	if (err == 0)
+		return 0;
+
+	/* fall through to exit error */
+
+ exit_error:
+	bast_flash_remove(dev);
+	return err;
+}
+
+static struct device_driver bast_flash_driver = {
+	.name		= "bast-nor",
+	.bus		= &platform_bus_type,
+	.probe		= bast_flash_probe,
+	.remove		= bast_flash_remove,
+};
+
+static int __init bast_flash_init(void)
+{
+	printk("BAST NOR-Flash Driver, (c) 2004 Simtec Electronics\n");
+	return driver_register(&bast_flash_driver);
+}
+
+static void __exit bast_flash_exit(void)
+{
+	driver_unregister(&bast_flash_driver);
+}
+
+module_init(bast_flash_init);
+module_exit(bast_flash_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
+MODULE_DESCRIPTION("BAST MTD Map driver");
diff -uNrb linux-2.4.30/drivers/mtd/maps/beech-mtd.c linux/drivers/mtd/maps/beech-mtd.c
--- linux-2.4.30/drivers/mtd/maps/beech-mtd.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/beech-mtd.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,112 @@
+/*
+ * $Id: beech-mtd.c,v 1.9 2004/09/16 23:27:12 gleixner Exp $
+ * 
+ * drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for 
+ *                              IBM 405LP Beech boards.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Copyright (C) 2002, International Business Machines Corporation
+ * All Rights Reserved.
+ *
+ * Bishop Brock
+ * IBM Research, Austin Center for Low-Power Computing
+ * bcbrock@us.ibm.com
+ * March 2002
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/ibm4xx.h>
+
+#define NAME     "Beech Linux Flash"
+#define PADDR    BEECH_BIGFLASH_PADDR
+#define SIZE     BEECH_BIGFLASH_SIZE
+#define BUSWIDTH 1
+
+/* Flash memories on these boards are memory resources, accessed big-endian. */
+
+
+static struct map_info beech_mtd_map = {
+	.name =		NAME,
+	.size =		SIZE,
+	.bankwidth =	BUSWIDTH,
+	.phys =		PADDR
+};
+
+static struct mtd_info *beech_mtd;
+
+static struct mtd_partition beech_partitions[2] = {
+	{
+	      .name = "Linux Kernel",
+	      .size = BEECH_KERNEL_SIZE,
+	      .offset = BEECH_KERNEL_OFFSET
+	}, {
+	      .name = "Free Area",
+	      .size = BEECH_FREE_AREA_SIZE,
+	      .offset = BEECH_FREE_AREA_OFFSET
+	}
+};
+
+static int __init
+init_beech_mtd(void)
+{
+	printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR);
+
+	beech_mtd_map.virt = (void __iomem *) 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 -uNrb linux-2.4.30/drivers/mtd/maps/cdb89712.c linux/drivers/mtd/maps/cdb89712.c
--- linux-2.4.30/drivers/mtd/maps/cdb89712.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/cdb89712.c	Tue Sep 28 14:38:13 2004
@@ -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.9 2004/09/16 23:27:12 gleixner 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,
+	.bankwidth = 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 = (void __iomem *)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,
+	.bankwidth = 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 = (void __iomem *)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,
+	.bankwidth = 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 = (void __iomem *)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 -uNrb linux-2.4.30/drivers/mtd/maps/ceiva.c linux/drivers/mtd/maps/ceiva.c
--- linux-2.4.30/drivers/mtd/maps/ceiva.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/ceiva.c	Tue Sep 28 14:38:13 2004
@@ -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.11 2004/09/16 23:27:12 gleixner 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
@@ -115,23 +64,23 @@
 
 static struct mtd_partition ceiva_partitions[] = {
 	{
-		name: "Ceiva BOOT partition",
-		size:   BOOT_PARTITION_SIZE_KiB*1024,
-		offset: 0,
+		.name = "Ceiva BOOT partition",
+		.size   = BOOT_PARTITION_SIZE_KiB*1024,
+		.offset = 0,
 
 	},{
-		name: "Ceiva parameters partition",
-		size:   PARAMS_PARTITION_SIZE_KiB*1024,
-		offset: (16 + 8) * 1024,
+		.name = "Ceiva parameters partition",
+		.size   = PARAMS_PARTITION_SIZE_KiB*1024,
+		.offset = (16 + 8) * 1024,
 	},{
-		name: "Ceiva kernel partition",
-		size: (KERNEL_PARTITION_SIZE_KiB)*1024,
-		offset: 0x20000,
+		.name = "Ceiva kernel partition",
+		.size = (KERNEL_PARTITION_SIZE_KiB)*1024,
+		.offset = 0x20000,
 
 	},{
-		name: "Ceiva root filesystem partition",
-		offset: MTDPART_OFS_APPEND,
-		size: (ROOT_PARTITION_SIZE_KiB)*1024,
+		.name = "Ceiva root filesystem partition",
+		.offset = MTDPART_OFS_APPEND,
+		.size = (ROOT_PARTITION_SIZE_KiB)*1024,
 	}
 };
 #endif
@@ -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->buswidth = clps[i].width;
+		clps[i].map->virt = (void __iomem *)clps[i].vbase;
+		clps[i].map->bankwidth = 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 -uNrb linux-2.4.30/drivers/mtd/maps/cfi_flagadm.c linux/drivers/mtd/maps/cfi_flagadm.c
--- linux-2.4.30/drivers/mtd/maps/cfi_flagadm.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/cfi_flagadm.c	Tue Sep 28 14:38:13 2004
@@ -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.13 2004/09/16 23:27:12 gleixner 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,
+		.bankwidth =	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 = (void __iomem *s)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 -uNrb linux-2.4.30/drivers/mtd/maps/cstm_mips_ixx.c linux/drivers/mtd/maps/cstm_mips_ixx.c
--- linux-2.4.30/drivers/mtd/maps/cstm_mips_ixx.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/cstm_mips_ixx.c	Tue Sep 28 14:38:13 2004
@@ -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.11 2004/09/16 23:27:12 gleixner 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,10 +55,17 @@
 #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)
 {
+	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 defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+		if (!vpp_count++) {
         __u16	data;
         __u8	data1;
 	static u8 first = 1;
@@ -116,10 +81,9 @@
 		   enabling vpp after powerup */
 		udelay(40);
 	}
-#endif /* CONFIG_MIPS_ITE8172 */
   }
-  else {
-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR)
+	} else {
+		if (!--vpp_count) {
         __u16	data;
 
 	// Set GPIO port B pin3 to high
@@ -127,26 +91,11 @@
 	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 */
 
@@ -155,7 +104,7 @@
 	char *name;
 	unsigned long window_addr;
 	unsigned long window_size;
-	int buswidth;
+	int bankwidth;
 	int num_partitions;
 };
 
@@ -167,7 +116,7 @@
         "big flash",     // name
 	0x08000000,      // window_addr
 	0x02000000,      // window_size
-        4,               // buswidth
+        4,               // bankwidth
 	1,               // num_partitions
     }
 
@@ -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,
 	},
 },
 };
@@ -189,7 +138,7 @@
         "MTD flash",                   // name
 	CONFIG_MTD_CSTM_MIPS_IXX_START,      // window_addr
 	CONFIG_MTD_CSTM_MIPS_IXX_LEN,        // window_size
-        CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH,   // buswidth
+        CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH,   // bankwidth
 	1,                             // num_partitions
     },
 
@@ -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 = (void __iomem *)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));
+		cstm_mips_ixx_map[i].bankwidth = cstm_mips_ixx_board_desc[i].bankwidth;
+#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 -uNrb linux-2.4.30/drivers/mtd/maps/db1550-flash.c linux/drivers/mtd/maps/db1550-flash.c
--- linux-2.4.30/drivers/mtd/maps/db1550-flash.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/db1550-flash.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,187 @@
+/*
+ * Flash memory access on Alchemy Db1550 board
+ * 
+ * $Id: db1550-flash.c,v 1.5 2004/09/19 00:12:00 ppopov 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
+
+static unsigned long window_addr;
+static unsigned long window_size;
+
+
+static struct map_info db1550_map = {
+	.name =	"Db1550 flash",
+};
+
+static unsigned char flash_bankwidth = 4;
+
+/* 
+ * Support only 64MB NOR Flash parts
+ */
+
+#if defined(CONFIG_MTD_DB1550_BOOT) && defined(CONFIG_MTD_DB1550_USER)
+#define DB1550_BOTH_BANKS
+#elif defined(CONFIG_MTD_DB1550_BOOT) && !defined(CONFIG_MTD_DB1550_USER)
+#define DB1550_BOOT_ONLY
+#elif !defined(CONFIG_MTD_DB1550_BOOT) && defined(CONFIG_MTD_DB1550_USER)
+#define DB1550_USER_ONLY
+#endif
+
+#ifdef DB1550_BOTH_BANKS
+/* both banks will be used. Combine the first bank and the first 
+ * part of the second bank together into a single jffs/jffs2
+ * partition.
+ */
+static struct mtd_partition db1550_partitions[] = {
+	/* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+	 * 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash
+	 * 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash
+	 */
+        {
+                .name = "User FS",
+                .size =   (0x1FC00000 - 0x18000000),
+                .offset = 0x0000000
+        },{
+                .name = "yamon",
+                .size = 0x0100000,
+		.offset = MTDPART_OFS_APPEND,
+                .mask_flags = MTD_WRITEABLE
+        },{
+                .name = "raw kernel",
+		.size = (0x300000 - 0x40000), /* last 256KB is yamon env */
+		.offset = MTDPART_OFS_APPEND,
+        }
+};
+#elif defined(DB1550_BOOT_ONLY)
+static struct mtd_partition db1550_partitions[] = {
+	/* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+	 * 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash
+	 */
+        {
+                .name = "User FS",
+                .size =   0x03c00000,
+                .offset = 0x0000000
+        },{
+                .name = "yamon",
+                .size = 0x0100000,
+		.offset = MTDPART_OFS_APPEND,
+                .mask_flags = MTD_WRITEABLE
+        },{
+                .name = "raw kernel",
+		.size = (0x300000-0x40000), /* last 256KB is yamon env */
+		.offset = MTDPART_OFS_APPEND,
+        }
+};
+#elif defined(DB1550_USER_ONLY)
+static struct mtd_partition db1550_partitions[] = {
+	/* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+	 * 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash
+	 */
+        {
+                .name = "User FS",
+                .size = (0x4000000 - 0x200000), /* reserve 2MB for raw kernel */
+                .offset = 0x0000000
+        },{
+                .name = "raw kernel",
+		.size = MTDPART_SIZ_FULL,
+		.offset = MTDPART_OFS_APPEND,
+        }
+};
+#else
+#error MTD_DB1550 define combo error /* should never happen */
+#endif
+
+#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
+
+static struct mtd_info *mymtd;
+
+/*
+ * Probe the flash density and setup window address and size
+ * based on user CONFIG options. There are times when we don't
+ * want the MTD driver to be probing the boot or user flash,
+ * so having the option to enable only one bank is important.
+ */
+int setup_flash_params(void)
+{
+#if defined(DB1550_BOTH_BANKS)
+			window_addr = 0x18000000;
+			window_size = 0x8000000; 
+#elif defined(DB1550_BOOT_ONLY)
+			window_addr = 0x1C000000;
+			window_size = 0x4000000; 
+#else /* USER ONLY */
+			window_addr = 0x1E000000;
+			window_size = 0x4000000; 
+#endif
+	return 0;
+}
+
+int __init db1550_mtd_init(void)
+{
+	struct mtd_partition *parts;
+	int nb_parts = 0;
+	
+	/* Default flash bankwidth */
+	db1550_map.bankwidth = flash_bankwidth;
+
+	if (setup_flash_params()) 
+		return -ENXIO;
+
+	/*
+	 * Static partition definition selection
+	 */
+	parts = db1550_partitions;
+	nb_parts = NB_OF(db1550_partitions);
+	db1550_map.size = window_size;
+
+	/*
+	 * Now let's probe for the actual flash.  Do it here since
+	 * specific machine settings might have been set above.
+	 */
+	printk(KERN_NOTICE "Pb1550 flash: probing %d-bit flash bus\n", 
+			db1550_map.bankwidth*8);
+	db1550_map.virt = 
+		(void __iomem *)ioremap(window_addr, window_size);
+	mymtd = do_map_probe("cfi_probe", &db1550_map);
+	if (!mymtd) return -ENXIO;
+	mymtd->owner = THIS_MODULE;
+
+	add_mtd_partitions(mymtd, parts, nb_parts);
+	return 0;
+}
+
+static void __exit db1550_mtd_cleanup(void)
+{
+	if (mymtd) {
+		del_mtd_partitions(mymtd);
+		map_destroy(mymtd);
+	}
+}
+
+module_init(db1550_mtd_init);
+module_exit(db1550_mtd_cleanup);
+
+MODULE_AUTHOR("Embedded Edge, LLC");
+MODULE_DESCRIPTION("Db1550 mtd map driver");
+MODULE_LICENSE("GPL");
diff -uNrb linux-2.4.30/drivers/mtd/maps/db1x00-flash.c linux/drivers/mtd/maps/db1x00-flash.c
--- linux-2.4.30/drivers/mtd/maps/db1x00-flash.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/db1x00-flash.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,226 @@
+/*
+ * Flash memory access on Alchemy Db1xxx boards
+ * 
+ * $Id: db1x00-flash.c,v 1.5 2004/09/18 23:22:35 ppopov Exp $
+ *
+ * (C) 2003 Pete Popov <ppopov@embeddedalley.com>
+ * 
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+
+#ifdef 	DEBUG_RW
+#define	DBG(x...)	printk(x)
+#else
+#define	DBG(x...)	
+#endif
+
+/* MTD CONFIG OPTIONS */
+#if defined(CONFIG_MTD_DB1X00_BOOT) && defined(CONFIG_MTD_DB1X00_USER)
+#define DB1X00_BOTH_BANKS
+#elif defined(CONFIG_MTD_DB1X00_BOOT) && !defined(CONFIG_MTD_DB1X00_USER)
+#define DB1X00_BOOT_ONLY
+#elif !defined(CONFIG_MTD_DB1X00_BOOT) && defined(CONFIG_MTD_DB1X00_USER)
+#define DB1X00_USER_ONLY
+#endif
+
+static unsigned long window_addr;
+static unsigned long window_size;
+static unsigned long flash_size;
+
+static unsigned short *bcsr = (unsigned short *)0xAE000000;
+static unsigned char flash_bankwidth = 4;
+
+/* 
+ * The Db1x boards support different flash densities. We setup
+ * the mtd_partition structures below for default of 64Mbit 
+ * flash densities, and override the partitions sizes, if
+ * necessary, after we check the board status register.
+ */
+
+#ifdef DB1X00_BOTH_BANKS
+/* both banks will be used. Combine the first bank and the first 
+ * part of the second bank together into a single jffs/jffs2
+ * partition.
+ */
+static struct mtd_partition db1x00_partitions[] = {
+        {
+                .name         =  "User FS",
+                .size         =  0x1c00000,
+                .offset       =  0x0000000
+        },{
+                .name         =  "yamon",
+                .size         =  0x0100000,
+		.offset       =  MTDPART_OFS_APPEND,
+                .mask_flags   =  MTD_WRITEABLE
+        },{
+                .name         =  "raw kernel",
+		.size         =  (0x300000-0x40000), /* last 256KB is env */
+		.offset       =  MTDPART_OFS_APPEND,
+        }
+};
+#elif defined(DB1X00_BOOT_ONLY)
+static struct mtd_partition db1x00_partitions[] = {
+        {
+                .name         =  "User FS",
+                .size         =  0x00c00000,
+                .offset       =  0x0000000
+        },{
+                .name         =  "yamon",
+                .size         =  0x0100000,
+		.offset       =  MTDPART_OFS_APPEND,
+                .mask_flags   =  MTD_WRITEABLE
+        },{
+                .name         =  "raw kernel",
+		.size         =  (0x300000-0x40000), /* last 256KB is env */
+		.offset       =  MTDPART_OFS_APPEND,
+        }
+};
+#elif defined(DB1X00_USER_ONLY)
+static struct mtd_partition db1x00_partitions[] = {
+        {
+                .name         =  "User FS",
+                .size         =  0x0e00000,
+                .offset       =  0x0000000
+        },{
+                .name         =  "raw kernel",
+		.size         =  MTDPART_SIZ_FULL,
+		.offset       =  MTDPART_OFS_APPEND,
+        }
+};
+#else
+#error MTD_DB1X00 define combo error /* should never happen */
+#endif
+#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
+
+#define NAME     	"Db1x00 Linux Flash"
+
+static struct map_info db1xxx_mtd_map = {
+	.name		= NAME,
+};
+
+static struct mtd_partition *parsed_parts;
+static struct mtd_info *db1xxx_mtd;
+
+/*
+ * Probe the flash density and setup window address and size
+ * based on user CONFIG options. There are times when we don't
+ * want the MTD driver to be probing the boot or user flash,
+ * so having the option to enable only one bank is important.
+ */
+int setup_flash_params(void)
+{
+	switch ((bcsr[2] >> 14) & 0x3) {
+		case 0: /* 64Mbit devices */
+			flash_size = 0x800000; /* 8MB per part */
+#if defined(DB1X00_BOTH_BANKS)
+			window_addr = 0x1E000000;
+			window_size = 0x2000000; 
+#elif defined(DB1X00_BOOT_ONLY)
+			window_addr = 0x1F000000;
+			window_size = 0x1000000; 
+#else /* USER ONLY */
+			window_addr = 0x1E000000;
+			window_size = 0x1000000; 
+#endif
+			break;
+		case 1:
+			/* 128 Mbit devices */
+			flash_size = 0x1000000; /* 16MB per part */
+#if defined(DB1X00_BOTH_BANKS)
+			window_addr = 0x1C000000;
+			window_size = 0x4000000;
+			/* USERFS from 0x1C00 0000 to 0x1FC0 0000 */
+			db1x00_partitions[0].size = 0x3C00000;
+#elif defined(DB1X00_BOOT_ONLY)
+			window_addr = 0x1E000000;
+			window_size = 0x2000000;
+			/* USERFS from 0x1E00 0000 to 0x1FC0 0000 */
+			db1x00_partitions[0].size = 0x1C00000;
+#else /* USER ONLY */
+			window_addr = 0x1C000000;
+			window_size = 0x2000000;
+			/* USERFS from 0x1C00 0000 to 0x1DE00000 */
+			db1x00_partitions[0].size = 0x1DE0000;
+#endif
+			break;
+		case 2:
+			/* 256 Mbit devices */
+			flash_size = 0x4000000; /* 64MB per part */
+#if defined(DB1X00_BOTH_BANKS)
+			return 1;
+#elif defined(DB1X00_BOOT_ONLY)
+			/* Boot ROM flash bank only; no user bank */
+			window_addr = 0x1C000000;
+			window_size = 0x4000000;
+			/* USERFS from 0x1C00 0000 to 0x1FC00000 */
+			db1x00_partitions[0].size = 0x3C00000;
+#else /* USER ONLY */
+			return 1;
+#endif
+			break;
+		default:
+			return 1;
+	}
+	db1xxx_mtd_map.size = window_size;
+	db1xxx_mtd_map.bankwidth = flash_bankwidth;
+	db1xxx_mtd_map.phys = window_addr;
+	db1xxx_mtd_map.bankwidth = flash_bankwidth;
+	return 0;
+}
+
+int __init db1x00_mtd_init(void)
+{
+	struct mtd_partition *parts;
+	int nb_parts = 0;
+	
+	if (setup_flash_params()) 
+		return -ENXIO;
+
+	/*
+	 * Static partition definition selection
+	 */
+	parts = db1x00_partitions;
+	nb_parts = NB_OF(db1x00_partitions);
+
+	/*
+	 * Now let's probe for the actual flash.  Do it here since
+	 * specific machine settings might have been set above.
+	 */
+	printk(KERN_NOTICE "Db1xxx flash: probing %d-bit flash bus\n", 
+			db1xxx_mtd_map.bankwidth*8);
+	db1xxx_mtd_map.virt = (void __iomem *)ioremap(window_addr, window_size);
+	db1xxx_mtd = do_map_probe("cfi_probe", &db1xxx_mtd_map);
+	if (!db1xxx_mtd) return -ENXIO;
+	db1xxx_mtd->owner = THIS_MODULE;
+
+	add_mtd_partitions(db1xxx_mtd, parts, nb_parts);
+	return 0;
+}
+
+static void __exit db1x00_mtd_cleanup(void)
+{
+	if (db1xxx_mtd) {
+		del_mtd_partitions(db1xxx_mtd);
+		map_destroy(db1xxx_mtd);
+		if (parsed_parts)
+			kfree(parsed_parts);
+	}
+}
+
+module_init(db1x00_mtd_init);
+module_exit(db1x00_mtd_cleanup);
+
+MODULE_AUTHOR("Pete Popov");
+MODULE_DESCRIPTION("Db1x00 mtd map driver");
+MODULE_LICENSE("GPL");
diff -uNrb linux-2.4.30/drivers/mtd/maps/dbox2-flash.c linux/drivers/mtd/maps/dbox2-flash.c
--- linux-2.4.30/drivers/mtd/maps/dbox2-flash.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/dbox2-flash.c	Tue Sep 28 14:38:13 2004
@@ -1,37 +1,61 @@
 /*
- * $Id: dbox2-flash.c,v 1.4 2001/10/02 15:05:14 dwmw2 Exp $
+ * $Id: dbox2-flash.c,v 1.12 2004/09/16 23:27:12 gleixner 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>
 #include <linux/mtd/partitions.h>
 #include <linux/config.h>
+#include <linux/errno.h>
 
 /* 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 (U-Boot)",
+	.size		= 128 * 1024, 
+	.offset		= MTDPART_OFS_APPEND, 
+	.mask_flags	= 0
+	},
+	{
+	.name		= "Root (SquashFS)",	
+	.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,84 +64,36 @@
 
 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,
+	.bankwidth	= 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 = (void __iomem *)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);
 	if (!mymtd) {
 	    // Probe for single Intel 28F640
-	    dbox2_flash_map.buswidth = 2;
+	    dbox2_flash_map.bankwidth = 2;
 	
 	    mymtd = do_map_probe("cfi_probe", &dbox2_flash_map);
 	}
 	    
 	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 +101,7 @@
 		return 0;
 	}
 
-	iounmap((void *)dbox2_flash_map.map_priv_1);
+	iounmap((void *)dbox2_flash_map.virt);
 	return -ENXIO;
 }
 
@@ -135,9 +111,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 +122,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 -uNrb linux-2.4.30/drivers/mtd/maps/dc21285.c linux/drivers/mtd/maps/dc21285.c
--- linux-2.4.30/drivers/mtd/maps/dc21285.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/dc21285.c	Tue Sep 28 14:38:13 2004
@@ -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.21 2004/09/16 23:27:13 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,143 +20,189 @@
 
 #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;
 
-__u8 dc21285_read8(struct map_info *map, unsigned long ofs)
+#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
+
+static map_word dc21285_read8(struct map_info *map, unsigned long ofs)
 {
-	return *(__u8*)(map->map_priv_1 + ofs);
+	return *(uint8_t*)(map->map_priv_1 + ofs);
 }
 
-__u16 dc21285_read16(struct map_info *map, unsigned long ofs)
+static map_word dc21285_read16(struct map_info *map, unsigned long ofs)
 {
-	return *(__u16*)(map->map_priv_1 + ofs);
+	return *(uint16_t*)(map->map_priv_1 + ofs);
 }
 
-__u32 dc21285_read32(struct map_info *map, unsigned long ofs)
+static map_word dc21285_read32(struct map_info *map, unsigned long ofs)
 {
-	return *(__u32*)(map->map_priv_1 + ofs);
+	return *(uint32_t*)(map->map_priv_1 + ofs);
 }
 
-void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+static void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
 {
 	memcpy(to, (void*)(map->map_priv_1 + from), len);
 }
 
-void dc21285_write8(struct map_info *map, __u8 d, unsigned long adr)
+static void dc21285_write(struct map_info *map, map_word d, unsigned long adr)
 {
+	if (machine_is_netwinder())
+		nw_en_write();
 	*CSR_ROMWRITEREG = adr & 3;
 	adr &= ~3;
-	*(__u8*)(map->map_priv_1 + adr) = d;
+	*(uint8_t*)(map->map_priv_1 + adr) = d.x[0];
 }
 
-void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr)
+static void dc21285_write16(struct map_info *map, map_word d, unsigned long adr)
 {
+	if (machine_is_netwinder())
+		nw_en_write();
 	*CSR_ROMWRITEREG = adr & 3;
 	adr &= ~3;
-	*(__u16*)(map->map_priv_1 + adr) = d;
+	*(uint16_t*)(map->map_priv_1 + adr) = d.x[0];
 }
 
-void dc21285_write32(struct map_info *map, __u32 d, unsigned long adr)
+static void dc21285_write32(struct map_info *map, map_word d, unsigned long adr)
 {
-	*(__u32*)(map->map_priv_1 + adr) = d;
+	if (machine_is_netwinder())
+		nw_en_write();
+	*(uint32_t*)(map->map_priv_1 + adr) = d.x[0];
 }
 
-void dc21285_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+static void dc21285_copy_to_32(struct map_info *map, unsigned long to, const void *from, ssize_t len)
 {
-	switch (map->buswidth) {
-		case 4:
 			while (len > 0) {
-				__u32 d = *((__u32*)from)++;
+		uint32_t d = *((uint32_t*)from)++;
 				dc21285_write32(map, d, to);
 				to += 4;
 				len -= 4;
 			}
-			break;
-		case 2:
+}
+
+static void dc21285_copy_to_16(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+{
 			while (len > 0) {
-				__u16 d = *((__u16*)from)++;
+		uint16_t d = *((uint16_t*)from)++;
 				dc21285_write16(map, d, to);
 				to += 2;
 				len -= 2;
 			}
-			break;
-		case 1:
-			while (len > 0) {
-				__u8 d = *((__u8*)from)++;
+}
+
+static void dc21285_copy_to_8(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+{
+	uint8_t d = *((uint8_t*)from)++;
 				dc21285_write8(map, d, to);
 				to++;
 				len--;
-			}
-			break;
-	}
 }
 
-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
+static struct map_info dc21285_map = {
+	.name = "DC21285 flash",
+	.phys = NO_XIP,
+	.size = 16*1024*1024,
+	.copy_from = dc21285_copy_from,
 };
 
 
 /* Partition stuff */
 static struct mtd_partition *dc21285_parts;
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+#endif
 		      
-extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **);
-
-int __init init_dc21285(void)
+static int __init init_dc21285(void)
 {
-	/* Determine buswidth */
+
+#ifdef CONFIG_MTD_PARTITIONS
+	int nrparts;
+#endif
+
+	/* Determine bankwidth */
 	switch (*CSR_SA110_CNTL & (3<<14)) {
 		case SA110_CNTL_ROMWIDTH_8: 
-			dc21285_map.buswidth = 1;
+			dc21285_map.bankwidth = 1;
+			dc21285_map.read = dc21285_read8;
+			dc21285_map.write = dc21285_write8;
+			dc21285_map.copy_to = dc21285_copy_to_8;
 			break;
 		case SA110_CNTL_ROMWIDTH_16: 
-			dc21285_map.buswidth = 2; 
+			dc21285_map.bankwidth = 2; 
+			dc21285_map.read = dc21285_read16;
+			dc21285_map.write = dc21285_write16;
+			dc21285_map.copy_to = dc21285_copy_to_16;
 			break;
 		case SA110_CNTL_ROMWIDTH_32: 
-			dc21285_map.buswidth = 4; 
+			dc21285_map.bankwidth = 4; 
 			break;
+			dc21285_map.read = dc21285_read32;
+			dc21285_map.write = dc21285_write32;
+			dc21285_map.copy_to = dc21285_copy_to_32;
 		default:
-			printk (KERN_ERR "DC21285 flash: undefined buswidth\n");
+			printk (KERN_ERR "DC21285 flash: undefined bankwidth\n");
 			return -ENXIO;
 	}
-	printk (KERN_NOTICE "DC21285 flash support (%d-bit buswidth)\n",
-		dc21285_map.buswidth*8);
+	printk (KERN_NOTICE "DC21285 flash support (%d-bit bankwidth)\n",
+		dc21285_map.bankwidth*8);
 
 	/* Let's map the flash area */
-	dc21285_map.map_priv_1 = (unsigned long)ioremap(DC21285_FLASH, 16*1024*1024);
+	dc21285_map.map_priv_1 = (void __iomem *)ioremap(DC21285_FLASH, 16*1024*1024);
 	if (!dc21285_map.map_priv_1) {
 		printk("Failed to ioremap\n");
 		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;
+	}	
 			
-		/* partition fixup */
+	dc21285_mtd->owner = THIS_MODULE;
 
-#ifdef CONFIG_MTD_REDBOOT_PARTS
-		nrparts = parse_redboot_partitions(mymtd, &dc21285_parts);
+#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
-		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);
-		}
+		add_mtd_device(dc21285_mtd);
 
+	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 -uNrb linux-2.4.30/drivers/mtd/maps/dilnetpc.c linux/drivers/mtd/maps/dilnetpc.c
--- linux-2.4.30/drivers/mtd/maps/dilnetpc.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/dilnetpc.c	Tue Sep 28 14:38:13 2004
@@ -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.14 2004/09/16 23:27:13 gleixner 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);
-}
 
 /*
 ************************************************************
@@ -288,19 +250,11 @@
 #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,
+	.bankwidth = 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 = (void __iomem *)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 -uNrb linux-2.4.30/drivers/mtd/maps/dmv182.c linux/drivers/mtd/maps/dmv182.c
--- linux-2.4.30/drivers/mtd/maps/dmv182.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/dmv182.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,150 @@
+
+/*
+ * drivers/mtd/maps/svme182.c
+ * 
+ * Flash map driver for the Dy4 SVME182 board
+ * 
+ * $Id: dmv182.c,v 1.4 2004/09/16 23:27:13 gleixner Exp $
+ *
+ * Copyright 2003-2004, TimeSys Corporation
+ *
+ * Based on the SVME181 flash map, by Tom Nelson, Dot4, Inc. for TimeSys Corp.
+ *
+ * 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/init.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/errno.h>
+
+/*
+ * This driver currently handles only the 16MiB user flash bank 1 on the
+ * board.  It does not provide access to bank 0 (contains the Dy4 FFW), bank 2
+ * (VxWorks boot), or the optional 48MiB expansion flash.
+ *
+ * scott.wood@timesys.com: On the newer boards with 128MiB flash, it
+ * now supports the first 96MiB (the boot flash bank containing FFW
+ * is excluded).  The VxWorks loader is in partition 1.
+ */
+
+#define FLASH_BASE_ADDR 0xf0000000
+#define FLASH_BANK_SIZE (128*1024*1024)
+
+MODULE_AUTHOR("Scott Wood, TimeSys Corporation <scott.wood@timesys.com>");
+MODULE_DESCRIPTION("User-programmable flash device on the Dy4 SVME182 board");
+MODULE_LICENSE("GPL");
+
+static struct map_info svme182_map = {
+	.name		= "Dy4 SVME182",
+	.bankwidth	= 32,
+	.size		=  128 * 1024 * 1024
+};
+
+#define BOOTIMAGE_PART_SIZE		((6*1024*1024)-RESERVED_PART_SIZE)
+
+// Allow 6MiB for the kernel
+#define NEW_BOOTIMAGE_PART_SIZE  (6 * 1024 * 1024)
+// Allow 1MiB for the bootloader
+#define NEW_BOOTLOADER_PART_SIZE (1024 * 1024)
+// Use the remaining 9MiB at the end of flash for the RFS
+#define NEW_RFS_PART_SIZE        (0x01000000 - NEW_BOOTLOADER_PART_SIZE - \
+                                  NEW_BOOTIMAGE_PART_SIZE)
+
+static struct mtd_partition svme182_partitions[] = {
+	// The Lower PABS is only 128KiB, but the partition code doesn't
+	// like partitions that don't end on the largest erase block
+	// size of the device, even if all of the erase blocks in the
+	// partition are small ones.  The hardware should prevent
+	// writes to the actual PABS areas.
+	{
+		name:       "Lower PABS and CPU 0 bootloader or kernel",
+		size:       6*1024*1024,
+		offset:     0,
+	},
+	{
+		name:       "Root Filesystem",
+		size:       10*1024*1024,
+		offset:     MTDPART_OFS_NXTBLK
+	},
+	{
+		name:       "CPU1 Bootloader",
+		size:       1024*1024,
+		offset:     MTDPART_OFS_NXTBLK,
+	},
+	{
+		name:       "Extra",
+		size:       110*1024*1024,
+		offset:     MTDPART_OFS_NXTBLK
+	},
+	{
+		name:       "Foundation Firmware and Upper PABS",
+		size:       1024*1024,
+		offset:     MTDPART_OFS_NXTBLK,
+		mask_flags: MTD_WRITEABLE // read-only
+	}
+};
+
+static struct mtd_info *this_mtd;
+
+static int __init init_svme182(void)
+{
+	struct mtd_partition *partitions;
+	int num_parts = sizeof(svme182_partitions) / sizeof(struct mtd_partition);
+
+	partitions = svme182_partitions;
+
+	svme182_map.virt = 
+		(void __iomem *)ioremap(FLASH_BASE_ADDR, svme182_map.size);
+		
+	if (svme182_map.virt == 0) {
+		printk("Failed to ioremap FLASH memory area.\n");
+		return -EIO;
+	}
+
+	simple_map_init(&svme182_map);
+
+	this_mtd = do_map_probe("cfi_probe", &svme182_map);
+	if (!this_mtd)
+	{
+		iounmap((void *)svme182_map.virt);
+		return -ENXIO;
+	}
+
+	printk(KERN_NOTICE "SVME182 flash device: %dMiB at 0x%08x\n",
+		   this_mtd->size >> 20, FLASH_BASE_ADDR);
+
+	this_mtd->owner = THIS_MODULE;
+	add_mtd_partitions(this_mtd, partitions, num_parts);
+
+	return 0;
+}
+
+static void __exit cleanup_svme182(void)
+{
+	if (this_mtd)
+	{
+		del_mtd_partitions(this_mtd);
+		map_destroy(this_mtd);
+	}
+
+	if (svme182_map.virt)
+	{
+		iounmap((void *)svme182_map.virt);
+		svme182_map.virt = 0;
+	}
+
+	return;
+}
+
+module_init(init_svme182);
+module_exit(cleanup_svme182);
diff -uNrb linux-2.4.30/drivers/mtd/maps/ebony.c linux/drivers/mtd/maps/ebony.c
--- linux-2.4.30/drivers/mtd/maps/ebony.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/ebony.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,165 @@
+/*
+ * $Id: ebony.c,v 1.12 2004/09/16 23:27:13 gleixner Exp $
+ * 
+ * Mapping for Ebony user flash
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * Copyright 2002-2004 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 <linux/version.h>
+#include <asm/io.h>
+#include <asm/ibm44x.h>
+#include <platforms/4xx/ebony.h>
+
+static struct mtd_info *flash;
+
+static struct map_info ebony_small_map = {
+	.name =		"Ebony small flash",
+	.size =		EBONY_SMALL_FLASH_SIZE,
+	.bankwidth =	1,
+};
+
+static struct map_info ebony_large_map = {
+	.name =		"Ebony large flash",
+	.size =		EBONY_LARGE_FLASH_SIZE,
+	.bankwidth =	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;
+	u8 *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 =
+		(void __iomem *)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@kernel.crashing.org>");
+MODULE_DESCRIPTION("MTD map and partitions for IBM 440GP Ebony boards");
diff -uNrb linux-2.4.30/drivers/mtd/maps/edb7312.c linux/drivers/mtd/maps/edb7312.c
--- linux-2.4.30/drivers/mtd/maps/edb7312.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/edb7312.c	Tue Sep 28 14:38:13 2004
@@ -1,5 +1,5 @@
 /*
- * $Id: edb7312.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $
+ * $Id: edb7312.c,v 1.12 2004/09/16 23:27:13 gleixner 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>
@@ -27,69 +28,19 @@
 #define BUSWIDTH    2
 #define FLASH_BLOCKSIZE_MAIN	0x20000
 #define FLASH_NUMBLOCKS_MAIN	128
-/* can be "cfi_probe", "jedec_probe", "map_rom", 0 }; */
-#define PROBETYPES { "cfi_probe", 0 }
+/* can be "cfi_probe", "jedec_probe", "map_rom", NULL }; */
+#define PROBETYPES { "cfi_probe", NULL }
 
 #define MSG_PREFIX "EDB7312-NOR:"   /* prefix for our printk()'s */
 #define MTDID      "edb7312-nor"    /* for mtdparts= partitioning */
 
 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,
+	.bankwidth = BUSWIDTH,
+	.phys = WINDOW_ADDR,
 };
 
 #ifdef CONFIG_MTD_PARTITIONS
@@ -100,29 +51,23 @@
 static struct mtd_partition static_partitions[3] =
 {
     {
-	name: "ARMboot",
-	  size: 0x40000,
-	  offset: 0
+		.name = "ARMboot",
+		.size = 0x40000,
+		.offset = 0
     },
     {
-	name: "Kernel",
-	  size: 0x200000,
-	  offset: 0x40000
+		.name = "Kernel",
+		.size = 0x200000,
+		.offset = 0x40000
     },
     {
-	name: "RootFS",
-	  size: 0xDC0000,
-	  offset: 0x240000
+		.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,32 +82,33 @@
 
        	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 = (void __iomem *)
 	  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;
 	for(; !mymtd && *type; type++) {
 		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 -uNrb linux-2.4.30/drivers/mtd/maps/elan-104nc.c linux/drivers/mtd/maps/elan-104nc.c
--- linux-2.4.30/drivers/mtd/maps/elan-104nc.c	Fri Nov 19 12:46:58 2004
+++ linux/drivers/mtd/maps/elan-104nc.c	Tue Sep 28 14:38:13 2004
@@ -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.22 2004/09/16 23:27:13 gleixner 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.
@@ -27,7 +27,7 @@
    
    16 bit I/O port (0x22) for some sort of paging.
 
-The single flash device is divided into 3 partition which appear as seperate
+The single flash device is divided into 3 partition which appear as separate
 MTD devices.
 
 Linux thinks that the I/O port is used by the PIC and hence check_region() will
@@ -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
@@ -52,21 +53,21 @@
 #define PAGE_IO_SIZE 2
 
 static volatile int page_in_window = -1; // Current page in window.
-static unsigned long iomapadr;
+static void __iomem *iomapadr;
 static spinlock_t elan_104nc_spin = SPIN_LOCK_UNLOCKED;
 
 /* 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: "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]))
 
@@ -106,39 +107,19 @@
 }
 
 
-static __u8 elan_104nc_read8(struct map_info *map, unsigned long ofs)
+static map_word elan_104nc_read16(struct map_info *map, unsigned long ofs)
 {
-	__u8 ret;
+	map_word ret;
 	spin_lock(&elan_104nc_spin);
 	elan_104nc_page(map, ofs);
-	ret = readb(iomapadr + (ofs & WINDOW_MASK));
-	spin_unlock(&elan_104nc_spin);
-	return ret;
-}
-
-static __u16 elan_104nc_read16(struct map_info *map, unsigned long ofs)
-{
-	__u16 ret;
-	spin_lock(&elan_104nc_spin);
-	elan_104nc_page(map, ofs);
-	ret = readw(iomapadr + (ofs & WINDOW_MASK));
-	spin_unlock(&elan_104nc_spin);
-	return ret;
-}
-
-static __u32 elan_104nc_read32(struct map_info *map, unsigned long ofs)
-{
-	__u32 ret;
-	spin_lock(&elan_104nc_spin);
-	elan_104nc_page(map, ofs);
-	ret = readl(iomapadr + (ofs & WINDOW_MASK));
+	ret.x[0] = readw(iomapadr + (ofs & WINDOW_MASK));
 	spin_unlock(&elan_104nc_spin);
 	return ret;
 }
 
 static void elan_104nc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
 {
-	while(len) {
+	while (len) {
 		unsigned long thislen = len;
 		if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
 			thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
@@ -153,27 +134,11 @@
 	}
 }
 
-static void elan_104nc_write8(struct map_info *map, __u8 d, unsigned long adr)
+static void elan_104nc_write16(struct map_info *map, map_word d, unsigned long adr)
 {
 	spin_lock(&elan_104nc_spin);
 	elan_104nc_page(map, adr);
-	writeb(d, iomapadr + (adr & WINDOW_MASK));
-	spin_unlock(&elan_104nc_spin);
-}
-
-static void elan_104nc_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	spin_lock(&elan_104nc_spin);
-	elan_104nc_page(map, adr);
-	writew(d, iomapadr + (adr & WINDOW_MASK));
-	spin_unlock(&elan_104nc_spin);
-}
-
-static void elan_104nc_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	spin_lock(&elan_104nc_spin);
-	elan_104nc_page(map, adr);
-	writel(d, iomapadr + (adr & WINDOW_MASK));
+	writew(d.x[0], iomapadr + (adr & WINDOW_MASK));
 	spin_unlock(&elan_104nc_spin);
 }
 
@@ -195,19 +160,16 @@
 }
 
 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
+	.bankwidth = 2,
+	.read = elan_104nc_read16,
+	.copy_from = elan_104nc_copy_from,
+	.write = elan_104nc_write16,
+	.copy_to = elan_104nc_copy_to
 };
 
 /* MTD device for all of the flash. */
@@ -221,31 +183,20 @@
 	}
 
 	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;
-	}
-	*/
-  	iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH);
+	/* Urg! We use I/O port 0x22 without request_region()ing it,
+	   because it's already allocated to the PIC. */
+
+  	iomapadr = (void __iomem *)ioremap(WINDOW_START, WINDOW_LENGTH);
 	if (!iomapadr) {
 		printk( KERN_ERR"%s: failed to ioremap memory region\n",
 			elan_104nc_map.name );
 		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 +211,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 -uNrb linux-2.4.30/drivers/mtd/maps/epxa10db-flash.c linux/drivers/mtd/maps/epxa10db-flash.c
--- linux-2.4.30/drivers/mtd/maps/epxa10db-flash.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/epxa10db-flash.c	Tue Sep 28 14:38:13 2004
@@ -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.12 2004/09/16 23:27:13 gleixner 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,
+	.bankwidth =	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 = (void __iomem *)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 -uNrb linux-2.4.30/drivers/mtd/maps/fortunet.c linux/drivers/mtd/maps/fortunet.c
--- linux-2.4.30/drivers/mtd/maps/fortunet.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/fortunet.c	Tue Sep 28 14:38:13 2004
@@ -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.8 2004/09/16 23:27:13 gleixner 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,8 +24,8 @@
 
 struct map_region
 {
-	int			window_addr_phyical;
-	int			altbuswidth;
+	int			window_addr_physical;
+	int			altbankwidth;
 	struct map_info		map_info;
 	struct mtd_info		*mymtd;
 	struct mtd_partition	parts[MAX_NUM_PARTITIONS];
@@ -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,
+	.bankwidth = 4,
 };
 
 static char * __init get_string_option(char *dest,int dest_size,char *sor)
@@ -147,8 +101,8 @@
 	get_options (get_string_option(string,sizeof(string),line),6,params);
 	if(params[0]<1)
 	{
-		printk(MTD_FORTUNET_PK "Bad paramters for MTD Region "
-			" name,region-number[,base,size,buswidth,altbuswidth]\n");
+		printk(MTD_FORTUNET_PK "Bad parameters for MTD Region "
+			" name,region-number[,base,size,bankwidth,altbankwidth]\n");
 		return 1;
 	}
 	if((params[1]<0)||(params[1]>=MAX_NUM_REGIONS))
@@ -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]].altbuswidth = 2;
+        map_regions[params[1]].window_addr_physical = DEF_WINDOW_ADDR_PHY;
+        map_regions[params[1]].altbankwidth = 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)
 	{
@@ -176,23 +130,23 @@
 	}
 	if(params[0]>3)
 	{
-		map_regions[params[1]].map_info.buswidth = params[4];
+		map_regions[params[1]].map_info.bankwidth = params[4];
 	}
 	if(params[0]>4)
 	{
-		map_regions[params[1]].altbuswidth = params[5];
+		map_regions[params[1]].altbankwidth = params[5];
 	}
 	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,14 +186,14 @@
 	{
 		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].altbuswidth = 2;
+			map_regions[ix].window_addr_physical = DEF_WINDOW_ADDR_PHY;
+			map_regions[ix].altbankwidth = 2;
 			map_regions[ix].mymtd = NULL;
 			map_regions[ix].map_info.name = map_regions[ix].map_name;
 			strcpy(map_regions[ix].map_info.name,"FORTUNET");
@@ -244,38 +201,43 @@
 		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 =
-				(int)ioremap_nocache(
-				map_regions[ix].window_addr_phyical,
+
+			map_regions[ix].map_info.phys =	map_regions[ix].window_addr_physical,
+
+			map_regions[ix].map_info.virt =
+				(void __iomem *)ioremap_nocache(
+				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))
+				map_regions[ix].altbankwidth!=map_regions[ix].map_info.bankwidth))
 			{
-				printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternet buswidth "
+				printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternate bankwidth "
 					"for %s flash.\n",
 					map_regions[ix].map_info.name);
-				map_regions[ix].map_info.buswidth =
-					map_regions[ix].altbuswidth;
+				map_regions[ix].map_info.bankwidth =
+					map_regions[ix].altbankwidth;
 				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 -uNrb linux-2.4.30/drivers/mtd/maps/h720x-flash.c linux/drivers/mtd/maps/h720x-flash.c
--- linux-2.4.30/drivers/mtd/maps/h720x-flash.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/h720x-flash.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,144 @@
+/*
+ * Flash memory access on Hynix GMS30C7201/HMS30C7202 based 
+ * evaluation boards
+ * 
+ * $Id: h720x-flash.c,v 1.10 2004/09/16 23:27:13 gleixner Exp $
+ *
+ * (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",
+	.bankwidth =	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 = (void __iomem *)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 bankwidth 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 bankwidth 2
+	    h720x_map.bankwidth = 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 -uNrb linux-2.4.30/drivers/mtd/maps/ichxrom.c linux/drivers/mtd/maps/ichxrom.c
--- linux-2.4.30/drivers/mtd/maps/ichxrom.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/ichxrom.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,383 @@
+/*
+ * ichxrom.c
+ *
+ * Normal mappings of chips in physical memory
+ * $Id: ichxrom.c,v 1.14 2004/09/18 01:59:56 eric 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/cfi.h>
+#include <linux/mtd/flashchip.h>
+#include <linux/config.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/list.h>
+
+#define xstr(s) str(s)
+#define str(s) #s
+#define MOD_NAME xstr(KBUILD_BASENAME)
+
+#define ADDRESS_NAME_LEN 18
+
+#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */
+
+#define BIOS_CNTL	0x4e
+#define FWH_DEC_EN1	0xE3
+#define FWH_DEC_EN2	0xF0
+#define FWH_SEL1	0xE8
+#define FWH_SEL2	0xEE
+
+struct ichxrom_window {
+	void __iomem* virt;
+	unsigned long phys;
+	unsigned long size;
+	struct list_head maps;
+	struct resource rsrc;
+	struct pci_dev *pdev;
+};
+
+struct ichxrom_map_info {
+	struct list_head list;
+	struct map_info map;
+	struct mtd_info *mtd;
+	struct resource rsrc;
+	char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
+};
+
+static struct ichxrom_window ichxrom_window = {
+	.maps = LIST_HEAD_INIT(ichxrom_window.maps),
+};
+
+static void ichxrom_cleanup(struct ichxrom_window *window)
+{
+	struct ichxrom_map_info *map, *scratch;
+	u16 word;
+
+	/* Disable writes through the rom window */
+	pci_read_config_word(window->pdev, BIOS_CNTL, &word);
+	pci_write_config_word(window->pdev, BIOS_CNTL, word & ~1);
+
+	/* Free all of the mtd devices */
+	list_for_each_entry_safe(map, scratch, &window->maps, list) {
+		if (map->rsrc.parent)
+			release_resource(&map->rsrc);
+		del_mtd_device(map->mtd);
+		map_destroy(map->mtd);
+		list_del(&map->list);
+		kfree(map);
+	}
+	if (window->rsrc.parent)
+		release_resource(&window->rsrc);
+	if (window->virt) {
+		iounmap(window->virt);
+		window->virt = NULL;
+		window->phys = 0;
+		window->size = 0;
+		window->pdev = NULL;
+	}
+}
+
+
+static int __devinit ichxrom_init_one (struct pci_dev *pdev,
+	const struct pci_device_id *ent)
+{
+	static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
+	struct ichxrom_window *window = &ichxrom_window;
+	struct ichxrom_map_info *map = 0;
+	unsigned long map_top;
+	u8 byte;
+	u16 word;
+
+	/* 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.
+	 */
+	window->pdev = pdev;
+
+	/* Find a region continuous to the end of the ROM window  */
+	window->phys = 0;
+	pci_read_config_byte(pdev, FWH_DEC_EN1, &byte);
+	if (byte == 0xff) {
+		window->phys = 0xffc00000;
+		pci_read_config_byte(pdev, FWH_DEC_EN2, &byte);
+		if ((byte & 0x0f) == 0x0f) {
+			window->phys = 0xff400000;
+		}
+		else if ((byte & 0x0e) == 0x0e) {
+			window->phys = 0xff500000;
+		}
+		else if ((byte & 0x0c) == 0x0c) {
+			window->phys = 0xff600000;
+		}
+		else if ((byte & 0x08) == 0x08) {
+			window->phys = 0xff700000;
+		}
+	}
+	else if ((byte & 0xfe) == 0xfe) {
+		window->phys = 0xffc80000;
+	}
+	else if ((byte & 0xfc) == 0xfc) {
+		window->phys = 0xffd00000;
+	}
+	else if ((byte & 0xf8) == 0xf8) {
+		window->phys = 0xffd80000;
+	}
+	else if ((byte & 0xf0) == 0xf0) {
+		window->phys = 0xffe00000;
+	}
+	else if ((byte & 0xe0) == 0xe0) {
+		window->phys = 0xffe80000;
+	}
+	else if ((byte & 0xc0) == 0xc0) {
+		window->phys = 0xfff00000;
+	}
+	else if ((byte & 0x80) == 0x80) {
+		window->phys = 0xfff80000; 
+	}
+
+	if (window->phys == 0) {
+		printk(KERN_ERR MOD_NAME ": Rom window is closed\n");
+		goto out;
+	}
+	window->phys -= 0x400000UL;
+	window->size = (0xffffffffUL - window->phys) + 1UL;
+
+	/* 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 out;
+	}
+	pci_write_config_word(pdev, BIOS_CNTL, word | 1);
+
+	/*
+	 * Try to reserve the window mem region.  If this fails then
+	 * it is likely due to the window being "reseved" by the BIOS.
+	 */
+	window->rsrc.name = MOD_NAME;
+	window->rsrc.start = window->phys;
+	window->rsrc.end   = window->phys + window->size - 1;
+	window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+	if (request_resource(&iomem_resource, &window->rsrc)) {
+		window->rsrc.parent = NULL;
+		printk(KERN_DEBUG MOD_NAME
+			": %s(): Unable to register resource"
+			" 0x%.08lx-0x%.08lx - kernel bug?\n",
+			__func__,
+			window->rsrc.start, window->rsrc.end);
+	}
+
+	/* Map the firmware hub into my address space. */
+	window->virt = ioremap_nocache(window->phys, window->size);
+	if (!window->virt) {
+		printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",
+			window->phys, window->size);
+		goto out;
+	}
+
+	/* Get the first address to look for an rom chip at */
+	map_top = window->phys;
+	if ((window->phys & 0x3fffff) != 0) {
+		map_top = window->phys + 0x400000;
+	}
+#if 1
+	/* The probe sequence run over the firmware hub lock
+	 * registers sets them to 0x7 (no access).
+	 * Probe at most the last 4M of the address space.
+	 */
+	if (map_top < 0xffc00000) {
+		map_top = 0xffc00000;
+	}
+#endif
+	/* Loop through and look for rom chips */
+	while((map_top - 1) < 0xffffffffUL) {
+		struct cfi_private *cfi;
+		unsigned long offset;
+		int i;
+
+		if (!map) {
+			map = kmalloc(sizeof(*map), GFP_KERNEL);
+		}
+		if (!map) {
+			printk(KERN_ERR MOD_NAME ": kmalloc failed");
+			goto out;
+		}
+		memset(map, 0, sizeof(*map));
+		INIT_LIST_HEAD(&map->list);
+		map->map.name = map->map_name;
+		map->map.phys = map_top;
+		offset = map_top - window->phys;
+		map->map.virt = (void __iomem *)
+			(((unsigned long)(window->virt)) + offset);
+		map->map.size = 0xffffffffUL - map_top + 1UL;
+		/* Set the name of the map to the address I am trying */
+		sprintf(map->map_name, "%s @%08lx",
+			MOD_NAME, map->map.phys);
+
+		/* Firmware hubs only use vpp when being programmed
+		 * in a factory setting.  So in-place programming
+		 * needs to use a different method.
+		 */
+		for(map->map.bankwidth = 32; map->map.bankwidth; 
+			map->map.bankwidth >>= 1)
+		{
+			char **probe_type;
+			/* Skip bankwidths that are not supported */
+			if (!map_bankwidth_supported(map->map.bankwidth))
+				continue;
+
+			/* Setup the map methods */
+			simple_map_init(&map->map);
+
+			/* Try all of the probe methods */
+			probe_type = rom_probe_types;
+			for(; *probe_type; probe_type++) {
+				map->mtd = do_map_probe(*probe_type, &map->map);
+				if (map->mtd)
+					goto found;
+			}
+		}
+		map_top += ROM_PROBE_STEP_SIZE;
+		continue;
+	found:
+		/* Trim the size if we are larger than the map */
+		if (map->mtd->size > map->map.size) {
+			printk(KERN_WARNING MOD_NAME
+				" rom(%u) larger than window(%lu). fixing...\n",
+				map->mtd->size, map->map.size);
+			map->mtd->size = map->map.size;
+		}
+		if (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.
+			 */
+			map->rsrc.name  = map->map_name;
+			map->rsrc.start = map->map.phys;
+			map->rsrc.end   = map->map.phys + map->mtd->size - 1;
+			map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
+			if (request_resource(&window->rsrc, &map->rsrc)) {
+				printk(KERN_ERR MOD_NAME
+					": cannot reserve MTD resource\n");
+				map->rsrc.parent = NULL;
+			}
+		}
+
+		/* Make the whole region visible in the map */
+		map->map.virt = window->virt;
+		map->map.phys = window->phys;
+		cfi = map->map.fldrv_priv;
+		for(i = 0; i < cfi->numchips; i++) {
+			cfi->chips[i].start += offset;
+		}
+		
+		/* Now that the mtd devices is complete claim and export it */
+		map->mtd->owner = THIS_MODULE;
+		if (add_mtd_device(map->mtd)) {
+			map_destroy(map->mtd);
+			map->mtd = 0;
+			goto out;
+		}
+
+
+		/* Calculate the new value of map_top */
+		map_top += map->mtd->size;
+
+		/* File away the map structure */
+		list_add(&map->list, &window->maps);
+		map = 0;
+	}
+
+ out:
+	/* Free any left over map structures */
+	if (map) {
+		kfree(map);
+	}
+	/* See if I have any map structures */
+	if (list_empty(&window->maps)) {
+		ichxrom_cleanup(window);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+
+static void __devexit ichxrom_remove_one (struct pci_dev *pdev)
+{
+	struct ichxrom_window *window = &ichxrom_window;
+	ichxrom_cleanup(window);
+}
+
+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, },
+	{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1,
+	  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
+
+int __init init_ichxrom(void)
+{
+	struct pci_dev *pdev;
+	struct pci_device_id *id;
+
+	pdev = NULL;
+	for (id = ichxrom_pci_tbl; id->vendor; id++) {
+		pdev = pci_find_device(id->vendor, id->device, NULL);
+		if (pdev) {
+			break;
+		}
+	}
+	if (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(ichxrom_window.pdev);
+}
+
+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 -uNrb linux-2.4.30/drivers/mtd/maps/impa7.c linux/drivers/mtd/maps/impa7.c
--- linux-2.4.30/drivers/mtd/maps/impa7.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/impa7.c	Tue Sep 28 14:38:13 2004
@@ -1,5 +1,5 @@
 /*
- * $Id: impa7.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $
+ * $Id: impa7.c,v 1.12 2004/09/16 23:27:13 gleixner 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>
@@ -29,83 +30,25 @@
 #define NUM_FLASHBANKS 2
 #define BUSWIDTH     4
 
-/* can be { "cfi_probe", "jedec_probe", "map_rom", 0 }; */
-#define PROBETYPES { "jedec_probe", 0 }
+/* can be { "cfi_probe", "jedec_probe", "map_rom", NULL } */
+#define PROBETYPES { "jedec_probe", NULL }
 
 #define MSG_PREFIX "impA7:"   /* prefix for our printk()'s */
 #define MTDID      "impa7-%d"  /* for mtdparts= partitioning */
 
-static struct mtd_info *impa7_mtd[NUM_FLASHBANKS] = { 0 };
+static struct mtd_info *impa7_mtd[NUM_FLASHBANKS];
 
-__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,
+		.bankwidth = 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,
+		.bankwidth = BUSWIDTH,
 	},
 };
 
@@ -117,24 +60,18 @@
 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 = (void __iomem *)
+		  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)
+			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";
-#endif
-			if (mtd_parts_nb <= 0)
-			{
-				mtd_parts = static_partitions;
-				mtd_parts_nb = NB_OF(static_partitions);
+			} 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);
-			}
+					   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 -uNrb linux-2.4.30/drivers/mtd/maps/integrator-flash-v24.c linux/drivers/mtd/maps/integrator-flash-v24.c
--- linux-2.4.30/drivers/mtd/maps/integrator-flash-v24.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/integrator-flash-v24.c	Tue Sep 28 14:38:13 2004
@@ -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.14 2004/09/16 23:27:13 gleixner 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.bankwidth   = 4;
+	armflash_map.virt = (void __iomem *) 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 -uNrb linux-2.4.30/drivers/mtd/maps/integrator-flash.c linux/drivers/mtd/maps/integrator-flash.c
--- linux-2.4.30/drivers/mtd/maps/integrator-flash.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/integrator-flash.c	Tue Sep 28 14:38:13 2004
@@ -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.17 2004/09/16 23:27:13 gleixner 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);
-}
+struct armflash_info {
+	struct flash_platform_data *plat;
+	struct resource		*res;
+	struct mtd_partition	*parts;
+	struct mtd_info		*mtd;
+	struct map_info		map;
+};
 
-static __u32 armflash_read32(struct map_info *map, unsigned long ofs)
+static void armflash_set_vpp(struct map_info *map, int on)
 {
-	return readl(ofs + map->map_priv_2);
-}
+	struct armflash_info *info = container_of(map, struct armflash_info, map);
 
-static void armflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
-{
-	memcpy(to, (void *) (from + map->map_priv_2), len);
+	if (info->plat && info->plat->set_vpp)
+		info->plat->set_vpp(on);
 }
 
-static void armflash_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	writeb(d, adr + map->map_priv_2);
-}
-
-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.bankwidth	= plat->width;
+	info->map.phys		= res->start;
+	info->map.virt		= (void __iomem *) 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);
+	if (err < 0) {
+		if (info->mtd) {
+			del_mtd_partitions(info->mtd);
+			map_destroy(info->mtd);
 	}
+		if (info->parts)
+			kfree(info->parts);
 
-	return ret;
-}
-
-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 -uNrb linux-2.4.30/drivers/mtd/maps/ipaq-flash.c linux/drivers/mtd/maps/ipaq-flash.c
--- linux-2.4.30/drivers/mtd/maps/ipaq-flash.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/ipaq-flash.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,464 @@
+/*
+ * Flash memory access on iPAQ Handhelds (either SA1100 or PXA250 based)
+ * 
+ * (C) 2000 Nicolas Pitre <nico@cam.org>
+ * (C) 2002 Hewlett-Packard Company <jamey.hicks@hp.com>
+ * (C) 2003 Christian Pellegrin <chri@ascensit.com>, <chri@infis.univ.ts.it>: concatenation of multiple flashes
+ * 
+ * $Id: ipaq-flash.c,v 1.2 2004/09/16 23:27:13 gleixner Exp $
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <asm/page.h>
+#include <asm/mach-types.h>
+#include <asm/system.h>
+#include <asm/errno.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#ifdef CONFIG_MTD_CONCAT
+#include <linux/mtd/concat.h>
+#endif
+
+#include <asm/hardware.h>
+#include <asm/arch-sa1100/h3600.h>
+#include <asm/io.h>
+
+
+#ifndef CONFIG_IPAQ_HANDHELD
+#error This is for iPAQ Handhelds only
+#endif
+#ifdef CONFIG_SA1100_JORNADA56X
+
+static void jornada56x_set_vpp(struct map_info *map, int vpp)
+{
+	if (vpp)
+		GPSR = GPIO_GPIO26;
+	else
+		GPCR = GPIO_GPIO26;
+	GPDR |= GPIO_GPIO26;
+}
+
+#endif
+
+#ifdef CONFIG_SA1100_JORNADA720
+
+static void jornada720_set_vpp(struct map_info *map, int vpp)
+{
+	if (vpp)
+		PPSR |= 0x80;
+	else
+		PPSR &= ~0x80;
+	PPDR |= 0x80;
+}
+
+#endif
+
+#define MAX_IPAQ_CS 2		/* Number of CS we are going to test */
+
+#define IPAQ_MAP_INIT(X) \
+	{ \
+		name:		"IPAQ flash " X, \
+	}
+
+
+static struct map_info ipaq_map[MAX_IPAQ_CS] = {
+	IPAQ_MAP_INIT("bank 1"),
+	IPAQ_MAP_INIT("bank 2")
+};
+
+static struct mtd_info *my_sub_mtd[MAX_IPAQ_CS] = {
+	NULL,
+	NULL
+};
+
+/*
+ * Here are partition information for all known IPAQ-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 keep these in alphabetical order, and formatted as per existing
+ * entries.  Thanks.
+ */
+
+#ifdef CONFIG_IPAQ_HANDHELD
+static unsigned long h3xxx_max_flash_size = 0x04000000;
+static struct mtd_partition h3xxx_partitions[] = {
+	{
+		name:		"H3XXX boot firmware",
+#ifndef CONFIG_LAB
+		size:		0x00040000,
+#else
+		size:		0x00080000,
+#endif
+		offset:		0,
+#ifndef CONFIG_LAB
+		mask_flags:	MTD_WRITEABLE,  /* force read-only */
+#endif
+	}, 
+	{
+		name:		"H3XXX root jffs2",
+#ifndef CONFIG_LAB
+		size:		0x2000000 - 2*0x40000, /* Warning, this is fixed later */
+		offset:		0x00040000,
+#else
+		size:		0x2000000 - 0x40000 - 0x80000, /* Warning, this is fixed later */
+		offset:		0x00080000,
+#endif
+	},
+	{
+		name:		"asset",
+		size:		0x40000,
+		offset:		0x2000000 - 0x40000, /* Warning, this is fixed later */
+		mask_flags:	MTD_WRITEABLE,  /* force read-only */
+	}
+};
+
+#ifndef CONFIG_MTD_CONCAT
+static struct mtd_partition h3xxx_partitions_bank2[] = {
+	/* this is used only on 2 CS machines when concat is not present */
+	{
+		name:		"second H3XXX root jffs2",
+		size:		0x1000000 - 0x40000, /* Warning, this is fixed later */
+		offset:		0x00000000,
+	},
+	{
+		name:		"second asset",
+		size:		0x40000,
+		offset:		0x1000000 - 0x40000, /* Warning, this is fixed later */
+		mask_flags:	MTD_WRITEABLE,  /* force read-only */
+	}
+};
+#endif
+
+static spinlock_t ipaq_vpp_lock = SPIN_LOCK_UNLOCKED;
+
+static void h3xxx_set_vpp(struct map_info *map, int vpp)
+{
+	static int nest = 0;
+	
+	spin_lock(&ipaq_vpp_lock);
+	if (vpp)
+		nest++;
+	else
+		nest--;
+	if (nest)
+		assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 1);
+	else
+		assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, 0);
+	spin_unlock(&ipaq_vpp_lock);
+}
+
+#endif
+
+#if defined(CONFIG_SA1100_JORNADA56X) || defined(CONFIG_SA1100_JORNADA720)
+static unsigned long jornada_max_flash_size = 0x02000000;
+static struct mtd_partition jornada_partitions[] = {
+	{
+		name:		"Jornada boot firmware",
+		size:		0x00040000,
+		offset:		0,
+		mask_flags:	MTD_WRITEABLE,  /* force read-only */
+	}, {
+		name:		"Jornada root jffs2",
+		size:		MTDPART_SIZ_FULL,
+		offset:		0x00040000,
+	}
+};
+#endif
+
+
+static struct mtd_partition *parsed_parts;
+static struct mtd_info *mymtd;
+
+static unsigned long cs_phys[] = {
+#ifdef CONFIG_ARCH_SA1100
+	SA1100_CS0_PHYS,
+	SA1100_CS1_PHYS,
+	SA1100_CS2_PHYS,
+	SA1100_CS3_PHYS,
+	SA1100_CS4_PHYS,
+	SA1100_CS5_PHYS,
+#else 
+	PXA_CS0_PHYS,
+	PXA_CS1_PHYS,
+	PXA_CS2_PHYS,
+	PXA_CS3_PHYS,
+	PXA_CS4_PHYS,
+	PXA_CS5_PHYS,
+#endif
+};
+
+static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
+
+static int __init h1900_special_case(void);
+
+int __init ipaq_mtd_init(void)
+{
+	struct mtd_partition *parts = NULL;
+	int nb_parts = 0;
+	int parsed_nr_parts = 0;
+	const char *part_type;
+	int i; /* used when we have >1 flash chips */
+	unsigned long tot_flashsize = 0; /* used when we have >1 flash chips */
+
+	/* Default flash bankwidth */
+	// ipaq_map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4;
+	
+	if (machine_is_h1900())
+	{
+		/* For our intents, the h1900 is not a real iPAQ, so we special-case it. */
+		return h1900_special_case();
+	}
+
+	if (machine_is_h3100() || machine_is_h1900())
+		for(i=0; i<MAX_IPAQ_CS; i++)
+			ipaq_map[i].bankwidth = 2;
+	else
+		for(i=0; i<MAX_IPAQ_CS; i++)
+			ipaq_map[i].bankwidth = 4;
+			
+	/*
+	 * Static partition definition selection
+	 */
+	part_type = "static";
+
+	simple_map_init(&ipaq_map[0]);
+	simple_map_init(&ipaq_map[1]);
+
+#ifdef CONFIG_IPAQ_HANDHELD
+	if (machine_is_ipaq()) {
+		parts = h3xxx_partitions;
+		nb_parts = ARRAY_SIZE(h3xxx_partitions);
+		for(i=0; i<MAX_IPAQ_CS; i++) {
+			ipaq_map[i].size = h3xxx_max_flash_size;
+			ipaq_map[i].set_vpp = h3xxx_set_vpp;
+			ipaq_map[i].phys = cs_phys[i];
+			ipaq_map[i].virt = (void __iomem *)__ioremap(cs_phys[i], 0x04000000, 0, 1);
+			if (machine_is_h3100 () || machine_is_h1900())
+				ipaq_map[i].bankwidth = 2;
+		}
+		if (machine_is_h3600()) {
+			/* No asset partition here */
+			h3xxx_partitions[1].size += 0x40000;
+			nb_parts--;
+		}
+	}
+#endif
+#ifdef CONFIG_ARCH_H5400
+	if (machine_is_h5400()) {
+		ipaq_map[0].size = 0x02000000;
+		ipaq_map[1].size = 0x02000000;
+		ipaq_map[1].phys = 0x02000000;
+		ipaq_map[1].virt = ipaq_map[0].virt + 0x02000000;
+	}
+#endif
+#ifdef CONFIG_ARCH_H1900
+	if (machine_is_h1900()) {
+		ipaq_map[0].size = 0x00400000;
+		ipaq_map[1].size = 0x02000000;
+		ipaq_map[1].phys = 0x00080000;
+		ipaq_map[1].virt = ipaq_map[0].virt + 0x00080000;
+	}
+#endif
+
+#ifdef CONFIG_SA1100_JORNADA56X
+	if (machine_is_jornada56x()) {
+		parts = jornada_partitions;
+		nb_parts = ARRAY_SIZE(jornada_partitions);
+		ipaq_map[0].size = jornada_max_flash_size;
+		ipaq_map[0].set_vpp = jornada56x_set_vpp;
+		ipaq_map[0].virt = (__u32)__ioremap(0x0, 0x04000000, 0, 1);
+	}
+#endif
+#ifdef CONFIG_SA1100_JORNADA720
+	if (machine_is_jornada720()) {
+		parts = jornada_partitions;
+		nb_parts = ARRAY_SIZE(jornada_partitions);
+		ipaq_map[0].size = jornada_max_flash_size;
+		ipaq_map[0].set_vpp = jornada720_set_vpp;
+	}
+#endif
+
+
+	if (machine_is_ipaq()) { /* for iPAQs only */
+		for(i=0; i<MAX_IPAQ_CS; i++) {
+			printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with CFI.\n", ipaq_map[i].bankwidth*8, ipaq_map[i].virt);
+			my_sub_mtd[i] = do_map_probe("cfi_probe", &ipaq_map[i]);
+			if (!my_sub_mtd[i]) {
+				printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with JEDEC.\n", ipaq_map[i].bankwidth*8, ipaq_map[i].virt);
+				my_sub_mtd[i] = do_map_probe("jedec_probe", &ipaq_map[i]);
+			}
+			if (!my_sub_mtd[i]) {
+				printk(KERN_NOTICE "iPAQ flash: failed to find flash.\n");
+				if (i)
+					break;
+				else
+					return -ENXIO;
+			} else
+				printk(KERN_NOTICE "iPAQ flash: found %d bytes\n", my_sub_mtd[i]->size);
+			
+			/* do we really need this debugging? --joshua 20030703 */
+			// printk("my_sub_mtd[%d]=%p\n", i, my_sub_mtd[i]);
+			my_sub_mtd[i]->owner = THIS_MODULE;
+			tot_flashsize += my_sub_mtd[i]->size;
+		}
+#ifdef CONFIG_MTD_CONCAT
+		/* fix the asset location */
+#	ifdef CONFIG_LAB
+		h3xxx_partitions[1].size = tot_flashsize - 0x40000 - 0x80000 /* extra big boot block */;
+#	else
+		h3xxx_partitions[1].size = tot_flashsize - 2 * 0x40000;
+#	endif
+		h3xxx_partitions[2].offset = tot_flashsize - 0x40000;
+		/* and concat the devices */
+		mymtd = mtd_concat_create(&my_sub_mtd[0], i,
+					  "ipaq");
+		if (!mymtd) {
+			printk("Cannot create iPAQ concat device\n");
+			return -ENXIO;
+		}
+#else
+		mymtd = my_sub_mtd[0];
+
+		/* 
+		 *In the very near future, command line partition parsing
+		 * will use the device name as 'mtd-id' instead of a value
+		 * passed to the parse_cmdline_partitions() routine. Since
+		 * the bootldr says 'ipaq', make sure it continues to work. 
+		 */
+		mymtd->name = "ipaq";
+
+		if ((machine_is_h3600())) {
+#	ifdef CONFIG_LAB
+			h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x80000;
+#	else
+			h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000;
+#	endif
+			nb_parts = 2;
+		} else {
+#	ifdef CONFIG_LAB
+			h3xxx_partitions[1].size = my_sub_mtd[0]->size - 0x40000 - 0x80000; /* extra big boot block */
+#	else
+			h3xxx_partitions[1].size = my_sub_mtd[0]->size - 2*0x40000;
+#	endif
+			h3xxx_partitions[2].offset = my_sub_mtd[0]->size - 0x40000;
+		}
+
+		if (my_sub_mtd[1]) {
+#	ifdef CONFIG_LAB
+			h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x80000;
+#	else
+			h3xxx_partitions_bank2[0].size = my_sub_mtd[1]->size - 0x40000;
+#	endif
+			h3xxx_partitions_bank2[1].offset = my_sub_mtd[1]->size - 0x40000;
+		}
+#endif
+	}
+	else {
+		/*
+		 * Now let's probe for the actual flash.  Do it here since
+		 * specific machine settings might have been set above.
+		 */
+		printk(KERN_NOTICE "IPAQ flash: probing %d-bit flash bus, window=%lx\n", ipaq_map[0].bankwidth*8, ipaq_map[0].virt);
+		mymtd = do_map_probe("cfi_probe", &ipaq_map[0]);
+		if (!mymtd)
+			return -ENXIO;
+		mymtd->owner = THIS_MODULE;
+	}
+
+
+	/*
+	 * Dynamic partition selection stuff (might override the static ones)
+	 */
+
+	 i = parse_mtd_partitions(mymtd, part_probes, &parsed_parts, 0);
+			
+	 if (i > 0) {
+		 nb_parts = parsed_nr_parts = i;
+		 parts = parsed_parts;
+		 part_type = "dynamic";
+	 }
+
+	 if (!parts) {
+		 printk(KERN_NOTICE "IPAQ flash: no partition info available, registering whole flash at once\n");
+		 add_mtd_device(mymtd);
+#ifndef CONFIG_MTD_CONCAT
+		 if (my_sub_mtd[1])
+			 add_mtd_device(my_sub_mtd[1]);
+#endif
+	 } else {
+		 printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+		 add_mtd_partitions(mymtd, parts, nb_parts);
+#ifndef CONFIG_MTD_CONCAT
+		 if (my_sub_mtd[1])
+			 add_mtd_partitions(my_sub_mtd[1], h3xxx_partitions_bank2, ARRAY_SIZE(h3xxx_partitions_bank2));
+#endif
+	 }
+
+	 return 0;
+}
+
+static void __exit ipaq_mtd_cleanup(void)
+{
+	int i;
+
+	if (mymtd) {
+		del_mtd_partitions(mymtd);
+#ifndef CONFIG_MTD_CONCAT
+		if (my_sub_mtd[1])
+			del_mtd_partitions(my_sub_mtd[1]);
+#endif
+		map_destroy(mymtd);
+#ifdef CONFIG_MTD_CONCAT
+		for(i=0; i<MAX_IPAQ_CS; i++) 
+#else
+			for(i=1; i<MAX_IPAQ_CS; i++) 
+#endif		  
+			{
+				if (my_sub_mtd[i])
+					map_destroy(my_sub_mtd[i]);
+			}
+		if (parsed_parts)
+			kfree(parsed_parts);
+	}
+}
+
+static int __init h1900_special_case(void)
+{
+	/* The iPAQ h1900 is a special case - it has weird ROM. */
+	simple_map_init(&ipaq_map[0]);
+	ipaq_map[0].size = 0x80000;
+	ipaq_map[0].set_vpp = h3xxx_set_vpp;
+	ipaq_map[0].phys = 0x0;
+	ipaq_map[0].virt = (void __iomem *)__ioremap(0x0, 0x04000000, 0, 1);
+	ipaq_map[0].bankwidth = 2;
+	
+	printk(KERN_NOTICE "iPAQ flash: probing %d-bit flash bus, window=%lx with JEDEC.\n", ipaq_map[0].bankwidth*8, ipaq_map[0].virt);
+	mymtd = do_map_probe("jedec_probe", &ipaq_map[0]);
+	if (!mymtd)
+		return -ENODEV;
+	add_mtd_device(mymtd);
+	printk(KERN_NOTICE "iPAQ flash: registered h1910 flash\n");
+	
+	return 0;
+}
+
+module_init(ipaq_mtd_init);
+module_exit(ipaq_mtd_cleanup);
+
+MODULE_AUTHOR("Jamey Hicks");
+MODULE_DESCRIPTION("IPAQ CFI map driver");
+MODULE_LICENSE("MIT");
diff -uNrb linux-2.4.30/drivers/mtd/maps/iq80310.c linux/drivers/mtd/maps/iq80310.c
--- linux-2.4.30/drivers/mtd/maps/iq80310.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/iq80310.c	Tue Sep 28 14:38:13 2004
@@ -1,5 +1,5 @@
 /*
- * $Id: iq80310.c,v 1.9 2002/01/01 22:45:02 rmk Exp $
+ * $Id: iq80310.c,v 1.19 2004/09/16 23:27:13 gleixner 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,
+	.bankwidth = 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 = (void __iomem *)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);
+	ret = parse_mtd_partitions(mymtd, probes, &parsed_parts, 0);
 
-		if (ret > 0) {
-			part_type = "RedBoot";
+	if (ret > 0)
 			parsed_nr_parts = ret;
-		}
-	}
-#endif
 
 	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 -uNrb linux-2.4.30/drivers/mtd/maps/ixp2000.c linux/drivers/mtd/maps/ixp2000.c
--- linux-2.4.30/drivers/mtd/maps/ixp2000.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/ixp2000.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,281 @@
+/*
+ * $Id: ixp2000.c,v 1.3 2004/09/16 23:27:13 gleixner Exp $
+ *
+ * drivers/mtd/maps/ixp2000.c
+ *
+ * Mapping for the Intel XScale IXP2000 based systems
+ *
+ * Copyright (C) 2002 Intel Corp.
+ * Copyright (C) 2003-2004 MontaVista Software, Inc.
+ *
+ * Original Author: Naeem M Afzal <naeem.m.afzal@intel.com>
+ * Maintainer: Deepak Saxena <dsaxena@plexity.net>
+ *
+ * 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/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 <linux/device.h>
+
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/flash.h>
+
+#include <linux/reboot.h>
+
+struct ixp2000_flash_info {
+	struct		mtd_info *mtd;
+	struct		map_info map;
+	struct		mtd_partition *partitions;
+	struct		resource *res;
+	int		nr_banks;
+};
+
+static inline unsigned long flash_bank_setup(struct map_info *map, unsigned long ofs)
+{	
+	unsigned long (*set_bank)(unsigned long) = 
+		(unsigned long(*)(unsigned long))map->map_priv_2;
+
+	return (set_bank ? set_bank(ofs) : ofs);
+}
+
+#ifdef __ARMEB__
+/*
+ * Rev A0 and A1 of IXP2400 silicon have a broken addressing unit which 
+ * causes the lower address bits to be XORed with 0x11 on 8 bit accesses 
+ * and XORed with 0x10 on 16 bit accesses. See the spec update, erratum 44.
+ */
+static int erratum44_workaround = 0;
+
+static inline unsigned long address_fix8_write(unsigned long addr)
+{
+	if (erratum44_workaround) {
+		return (addr ^ 3);
+	}
+	return addr;
+}
+#else
+
+#define address_fix8_write(x)	(x)
+#endif
+
+static map_word ixp2000_flash_read8(struct map_info *map, unsigned long ofs)
+{
+	map_word val;
+
+	val.x[0] =  *((u8 *)(map->map_priv_1 + flash_bank_setup(map, ofs)));
+	return val;
+}
+
+/*
+ * We can't use the standard memcpy due to the broken SlowPort
+ * address translation on rev A0 and A1 silicon and the fact that
+ * we have banked flash.
+ */
+static void ixp2000_flash_copy_from(struct map_info *map, void *to,
+			      unsigned long from, ssize_t len)
+{
+	from = flash_bank_setup(map, from);
+	while(len--) 
+		*(__u8 *) to++ = *(__u8 *)(map->map_priv_1 + from++);
+}
+
+static void ixp2000_flash_write8(struct map_info *map, map_word d, unsigned long ofs)
+{
+	*(__u8 *) (address_fix8_write(map->map_priv_1 +
+				      flash_bank_setup(map, ofs))) = d.x[0];
+}
+
+static void ixp2000_flash_copy_to(struct map_info *map, unsigned long to,
+			    const void *from, ssize_t len)
+{
+	to = flash_bank_setup(map, to);
+	while(len--) {
+		unsigned long tmp = address_fix8_write(map->map_priv_1 + to++);
+		*(__u8 *)(tmp) = *(__u8 *)(from++);
+	}
+}
+
+
+static int ixp2000_flash_remove(struct device *_dev)
+{
+	struct platform_device *dev = to_platform_device(_dev);
+	struct flash_platform_data *plat = dev->dev.platform_data;
+	struct ixp2000_flash_info *info = dev_get_drvdata(&dev->dev);
+
+	dev_set_drvdata(&dev->dev, NULL);
+
+	if(!info)
+		return 0;
+
+	if (info->mtd) {
+		del_mtd_partitions(info->mtd);
+		map_destroy(info->mtd);
+	}
+	if (info->map.map_priv_1)
+		iounmap((void *) info->map.map_priv_1);
+
+	if (info->partitions) {
+		kfree(info->partitions); }
+
+	if (info->res) {
+		release_resource(info->res);
+		kfree(info->res);
+	}
+
+	if (plat->exit)
+		plat->exit();
+
+	return 0;
+}
+
+
+static int ixp2000_flash_probe(struct device *_dev)
+{
+	static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+	struct platform_device *dev = to_platform_device(_dev);
+	struct ixp2000_flash_data *ixp_data = dev->dev.platform_data;
+	struct flash_platform_data *plat; 
+	struct ixp2000_flash_info *info;
+	unsigned long window_size;
+	int err = -1;
+	
+	if (!ixp_data)
+		return -ENODEV;
+
+	plat = ixp_data->platform_data;
+	if (!plat)
+		return -ENODEV;
+
+	window_size = dev->resource->end - dev->resource->start + 1;
+	dev_info(_dev, "Probe of IXP2000 flash(%d banks x %dMiB)\n", 
+			ixp_data->nr_banks, ((u32)window_size >> 20));
+
+	if (plat->width != 1) {
+		dev_err(_dev, "IXP2000 MTD map only supports 8-bit mode, asking for %d\n",
+				plat->width * 8);
+		return -EIO;
+	}
+
+	info = kmalloc(sizeof(struct ixp2000_flash_info), GFP_KERNEL);
+	if(!info) {
+		err = -ENOMEM;
+		goto Error;
+	}	
+	memzero(info, sizeof(struct ixp2000_flash_info));
+
+	dev_set_drvdata(&dev->dev, info);
+
+	/*
+	 * Tell the MTD layer we're not 1:1 mapped so that it does
+	 * not attempt to do a direct access on us.
+	 */
+	info->map.phys = NO_XIP;
+	
+	info->nr_banks = ixp_data->nr_banks;
+	info->map.size = ixp_data->nr_banks * window_size;
+	info->map.bankwidth = 1;
+
+	/*
+ 	 * map_priv_2 is used to store a ptr to to the bank_setup routine
+ 	 */
+	info->map.map_priv_2 = (void __iomem *) ixp_data->bank_setup;
+
+	info->map.name = dev->dev.bus_id;
+	info->map.read = ixp2000_flash_read8;
+	info->map.write = ixp2000_flash_write8;
+	info->map.copy_from = ixp2000_flash_copy_from;
+	info->map.copy_to = ixp2000_flash_copy_to;
+
+	info->res = request_mem_region(dev->resource->start, 
+			dev->resource->end - dev->resource->start + 1, 
+			dev->dev.bus_id);
+	if (!info->res) {
+		dev_err(_dev, "Could not reserve memory region\n");
+		err = -ENOMEM;
+		goto Error;
+	}
+
+	info->map.map_priv_1 =
+	    (void __iomem *) ioremap(dev->resource->start, 
+				    dev->resource->end - dev->resource->start + 1);
+	if (!info->map.map_priv_1) {
+		dev_err(_dev, "Failed to ioremap flash region\n");
+		err = -EIO;
+		goto Error;
+	}
+
+	/*
+	 * Setup read mode for FLASH
+	 */
+	*IXP2000_SLOWPORT_FRM = 1;
+
+#if defined(__ARMEB__)
+	/*
+	 * Enable erratum 44 workaround for NPUs with broken slowport
+	 */
+
+	errata44_workaround = ixp2000_has_broken_slowport();
+	dev_info(_dev, "Erratum 44 workaround %s\n",
+	       erratum44_workaround ? "enabled" : "disabled");
+#endif
+
+	info->mtd = do_map_probe(plat->map_name, &info->map);
+	if (!info->mtd) {
+		dev_err(_dev, "map_probe failed\n");
+		err = -ENXIO;
+		goto Error;
+	}
+	info->mtd->owner = THIS_MODULE;
+
+	err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0);
+	if (err > 0) {
+		err = add_mtd_partitions(info->mtd, info->partitions, err);
+		if(err)
+			dev_err(_dev, "Could not parse partitions\n");
+	}
+
+	if (err)
+		goto Error;
+
+	return 0;
+
+Error:
+	ixp2000_flash_remove(_dev);
+	return err;
+}
+
+static struct device_driver ixp2000_flash_driver = {
+	.name		= "IXP2000-Flash",
+	.bus		= &platform_bus_type,
+	.probe		= &ixp2000_flash_probe,
+	.remove		= &ixp2000_flash_remove
+};
+
+static int __init ixp2000_flash_init(void)
+{
+	return driver_register(&ixp2000_flash_driver);
+}
+
+static void __exit ixp2000_flash_exit(void)
+{
+	driver_unregister(&ixp2000_flash_driver);
+}
+
+module_init(ixp2000_flash_init);
+module_exit(ixp2000_flash_exit);
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net>");
+
diff -uNrb linux-2.4.30/drivers/mtd/maps/ixp4xx.c linux/drivers/mtd/maps/ixp4xx.c
--- linux-2.4.30/drivers/mtd/maps/ixp4xx.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/ixp4xx.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,260 @@
+/*
+ * $Id: ixp4xx.c,v 1.6 2004/09/17 00:25:06 gleixner Exp $
+ *
+ * drivers/mtd/maps/ixp4xx.c
+ *
+ * MTD Map file for IXP4XX based systems. Please do not make per-board
+ * changes in here. If your board needs special setup, do it in your
+ * platform level code in arch/arm/mach-ixp4xx/board-setup.c
+ *
+ * Original Author: Intel Corporation
+ * Maintainer: Deepak Saxena <dsaxena@mvista.com>
+ *
+ * Copyright (C) 2002 Intel Corporation
+ * Copyright (C) 2003-2004 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 <linux/device.h>
+#include <asm/io.h>
+#include <asm/mach-types.h>
+#include <asm/mach/flash.h>
+
+#include <linux/reboot.h>
+
+#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 map_word ixp4xx_read16(struct map_info *map, unsigned long ofs)
+{
+	map_word val;
+	val.x[0] = *(__u16 *) (map->map_priv_1 + ofs);
+	return val;
+}
+
+/*
+ * The IXP4xx 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 ixp4xx_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]);
+}
+
+/* 
+ * Unaligned writes are ignored, causing the 8-bit
+ * probe to fail and proceed to the 16-bit probe (which succeeds).
+ */
+static void ixp4xx_probe_write16(struct map_info *map, map_word d, unsigned long adr)
+{
+	if (!(adr & 1))
+	       *(__u16 *) (map->map_priv_1 + adr) = d.x[0];
+}
+
+/* 
+ * Fast write16 function without the probing check above
+ */
+static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr)
+{
+       *(__u16 *) (map->map_priv_1 + adr) = d.x[0];
+}
+
+struct ixp4xx_flash_info {
+	struct mtd_info *mtd;
+	struct map_info map;
+	struct mtd_partition *partitions;
+	struct resource *res;
+};
+
+static const char *probes[] = { "RedBoot", "cmdlinepart", NULL };
+
+static int ixp4xx_flash_remove(struct device *_dev)
+{
+	struct platform_device *dev = to_platform_device(_dev);
+	struct flash_platform_data *plat = dev->dev.platform_data;
+	struct ixp4xx_flash_info *info = dev_get_drvdata(&dev->dev);
+	map_word d;
+
+	dev_set_drvdata(&dev->dev, NULL);
+
+	if(!info)
+		return 0;
+
+	/*
+	 * This is required for a soft reboot to work.
+	 */
+	d.x[0] = 0xff;
+	ixp4xx_write16(&info->map, d, 0x55 * 0x2);
+
+	if (info->mtd) {
+		del_mtd_partitions(info->mtd);
+		map_destroy(info->mtd);
+	}
+	if (info->map.map_priv_1)
+		iounmap((void *) info->map.map_priv_1);
+
+	if (info->partitions)
+		kfree(info->partitions);
+
+	if (info->res) {
+		release_resource(info->res);
+		kfree(info->res);
+	}
+
+	if (plat->exit)
+		plat->exit();
+
+	/* Disable flash write */
+	*IXP4XX_EXP_CS0 &= ~IXP4XX_FLASH_WRITABLE;
+
+	return 0;
+}
+
+static int ixp4xx_flash_probe(struct device *_dev)
+{
+	struct platform_device *dev = to_platform_device(_dev);
+	struct flash_platform_data *plat = dev->dev.platform_data;
+	struct ixp4xx_flash_info *info;
+	int err = -1;
+
+	if (!plat)
+		return -ENODEV;
+
+	if (plat->init) {
+		err = plat->init();
+		if (err)
+			return err;
+	}
+
+	info = kmalloc(sizeof(struct ixp4xx_flash_info), GFP_KERNEL);
+	if(!info) {
+		err = -ENOMEM;
+		goto Error;
+	}	
+	memzero(info, sizeof(struct ixp4xx_flash_info));
+
+	dev_set_drvdata(&dev->dev, info);
+
+	/* 
+	 * Enable flash write 
+	 * TODO: Move this out to board specific code
+	 */
+	*IXP4XX_EXP_CS0 |= IXP4XX_FLASH_WRITABLE;
+
+	/*
+	 * Tell the MTD layer we're not 1:1 mapped so that it does
+	 * not attempt to do a direct access on us.
+	 */
+	info->map.phys = NO_XIP;
+	info->map.size = dev->resource->end - dev->resource->start + 1;
+
+	/*
+	 * We only support 16-bit accesses for now. If and when
+	 * any board use 8-bit access, we'll fixup the driver to
+	 * handle that.
+	 */
+	info->map.bankwidth = 2;
+	info->map.name = dev->dev.bus_id;
+	info->map.read = ixp4xx_read16,
+	info->map.write = ixp4xx_probe_write16,
+	info->map.copy_from = ixp4xx_copy_from,
+
+	info->res = request_mem_region(dev->resource->start, 
+			dev->resource->end - dev->resource->start + 1, 
+			"IXP4XXFlash");
+	if (!info->res) {
+		printk(KERN_ERR "IXP4XXFlash: Could not reserve memory region\n");
+		err = -ENOMEM;
+		goto Error;
+	}
+
+	info->map.map_priv_1 =
+	    (void __iomem *) ioremap(dev->resource->start, 
+				    dev->resource->end - dev->resource->start + 1);
+	if (!info->map.map_priv_1) {
+		printk(KERN_ERR "IXP4XXFlash: Failed to ioremap region\n");
+		err = -EIO;
+		goto Error;
+	}
+
+	info->mtd = do_map_probe(plat->map_name, &info->map);
+	if (!info->mtd) {
+		printk(KERN_ERR "IXP4XXFlash: map_probe failed\n");
+		err = -ENXIO;
+		goto Error;
+	}
+	info->mtd->owner = THIS_MODULE;
+	
+	/* Use the fast version */
+	info->map.write = ixp4xx_write16,
+
+	err = parse_mtd_partitions(info->mtd, probes, &info->partitions, 0);
+	if (err > 0) {
+		err = add_mtd_partitions(info->mtd, info->partitions, err);
+		if(err)
+			printk(KERN_ERR "Could not parse partitions\n");
+	}
+
+	if (err)
+		goto Error;
+
+	return 0;
+
+Error:
+	ixp4xx_flash_remove(_dev);
+	return err;
+}
+
+static struct device_driver ixp4xx_flash_driver = {
+	.name		= "IXP4XX-Flash",
+	.bus		= &platform_bus_type,
+	.probe		= ixp4xx_flash_probe,
+	.remove		= ixp4xx_flash_remove,
+};
+
+static int __init ixp4xx_flash_init(void)
+{
+	return driver_register(&ixp4xx_flash_driver);
+}
+
+static void __exit ixp4xx_flash_exit(void)
+{
+	driver_unregister(&ixp4xx_flash_driver);
+}
+
+
+module_init(ixp4xx_flash_init);
+module_exit(ixp4xx_flash_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MTD map driver for Intel IXP4xx systems")
+MODULE_AUTHOR("Deepak Saxena");
+
diff -uNrb linux-2.4.30/drivers/mtd/maps/l440gx.c linux/drivers/mtd/maps/l440gx.c
--- linux-2.4.30/drivers/mtd/maps/l440gx.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/l440gx.c	Tue Sep 28 14:38:13 2004
@@ -1,5 +1,5 @@
 /*
- * $Id: l440gx.c,v 1.8 2002/01/10 20:27:40 eric Exp $
+ * $Id: l440gx.c,v 1.14 2004/09/16 23:27:13 gleixner 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,
+	.bankwidth = 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 = (void __iomem *)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 -uNrb linux-2.4.30/drivers/mtd/maps/lasat.c linux/drivers/mtd/maps/lasat.c
--- linux-2.4.30/drivers/mtd/maps/lasat.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/lasat.c	Tue Sep 28 14:38:13 2004
@@ -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.8 2004/09/16 23:27:13 gleixner 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",
-	.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
+static struct map_info lasat_map = {
+	.name = "LASAT flash",
+	.bankwidth = 4,
+	.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 */
+	/* 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");
-	*lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit;
+	ENABLE_VPP((&lasat_map));
 
-	sp_map.map_priv_1 = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER);
-	sp_map.size = lasat_board_info.li_flash_size;
+	lasat_map.phys = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER);
+	lasat_map.virt = (void __iomem *)ioremap_nocache(
+		        lasat_map.phys, lasat_board_info.li_flash_size);
+	lasat_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 -uNrb linux-2.4.30/drivers/mtd/maps/lubbock-flash.c linux/drivers/mtd/maps/lubbock-flash.c
--- linux-2.4.30/drivers/mtd/maps/lubbock-flash.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/lubbock-flash.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,166 @@
+/*
+ * $Id: lubbock-flash.c,v 1.17 2004/09/16 23:27:13 gleixner 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 <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+#include <asm/hardware.h>
+#include <asm/arch/lubbock.h>
+
+
+#define ROM_ADDR	0x00000000
+#define FLASH_ADDR	0x04000000
+
+#define WINDOW_SIZE 	64*1024*1024
+
+static void lubbock_map_inval_cache(struct map_info *map, unsigned long from, ssize_t len)
+{
+	consistent_sync((char *)map->cached + from, len, DMA_FROM_DEVICE);
+}
+
+static struct map_info lubbock_maps[2] = { {
+	.size =		WINDOW_SIZE,
+	.phys =		0x00000000,
+	.inval_cache = 	lubbock_map_inval_cache,
+}, {
+	.size =		WINDOW_SIZE,
+	.phys =		0x04000000,
+	.inval_cache = 	lubbock_map_inval_cache,
+} };
+
+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].bankwidth = lubbock_maps[1].bankwidth = 
+		(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 = (void __iomem *)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;
+		}
+		lubbock_maps[i].cached = ioremap_cached(lubbock_maps[i].phys, WINDOW_SIZE);
+		if (!lubbock_maps[i].cached)
+			printk(KERN_WARNING "Failed to ioremap cached %s\n", lubbock_maps[i].name);
+		simple_map_init(&lubbock_maps[i]);
+
+		printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit bankwidth)\n",
+		       lubbock_maps[i].name, lubbock_maps[i].phys, 
+		       lubbock_maps[i].bankwidth * 8);
+
+		mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]);
+		
+		if (!mymtds[i]) {
+			iounmap((void *)lubbock_maps[i].virt);
+			if (lubbock_maps[i].cached)
+				iounmap(lubbock_maps[i].cached);
+			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 (lubbock_maps[i].cached)
+			iounmap(lubbock_maps[i].cached);
+
+		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 -uNrb linux-2.4.30/drivers/mtd/maps/map_funcs.c linux/drivers/mtd/maps/map_funcs.c
--- linux-2.4.30/drivers/mtd/maps/map_funcs.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/map_funcs.c	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,44 @@
+/*
+ * $Id: map_funcs.c,v 1.9 2004/07/13 22:33:15 dwmw2 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/mtd/map.h>
+
+static map_word simple_map_read(struct map_info *map, unsigned long ofs)
+{
+	return inline_map_read(map, ofs);
+}
+
+static void simple_map_write(struct map_info *map, const map_word datum, unsigned long ofs)
+{
+	inline_map_write(map, datum, ofs);
+}
+
+static void simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+{
+	inline_map_copy_from(map, to, from, len);
+}
+
+static void simple_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+{
+	inline_map_copy_to(map, to, from, len);
+}
+
+void simple_map_init(struct map_info *map)
+{
+	BUG_ON(!map_bankwidth_supported(map->bankwidth));
+
+	map->read = simple_map_read;
+	map->write = simple_map_write;
+	map->copy_from = simple_map_copy_from;
+	map->copy_to = simple_map_copy_to;
+}
+
+EXPORT_SYMBOL(simple_map_init);
+MODULE_LICENSE("GPL");
diff -uNrb linux-2.4.30/drivers/mtd/maps/mbx860.c linux/drivers/mtd/maps/mbx860.c
--- linux-2.4.30/drivers/mtd/maps/mbx860.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/mbx860.c	Tue Sep 28 14:38:13 2004
@@ -1,5 +1,5 @@
 /*
- * $Id: mbx860.c,v 1.1 2001/11/18 19:43:09 dwmw2 Exp $
+ * $Id: mbx860.c,v 1.7 2004/09/16 23:27:13 gleixner 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,
+	.bankwidth = 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 = (void __iomem *)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 -uNrb linux-2.4.30/drivers/mtd/maps/mpc1211.c linux/drivers/mtd/maps/mpc1211.c
--- linux-2.4.30/drivers/mtd/maps/mpc1211.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/mpc1211.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,81 @@
+/*
+ * Flash on MPC-1211
+ *
+ * $Id: mpc1211.c,v 1.4 2004/09/16 23:27:13 gleixner Exp $
+ *
+ * (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,
+	.bankwidth	= 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 = (void __iomem *)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 -uNrb linux-2.4.30/drivers/mtd/maps/netsc520.c linux/drivers/mtd/maps/netsc520.c
--- linux-2.4.30/drivers/mtd/maps/netsc520.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/netsc520.c	Tue Sep 28 14:38:13 2004
@@ -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.11 2004/09/16 23:27:13 gleixner 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,
+	.bankwidth = 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 = (void __iomem *)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 -uNrb linux-2.4.30/drivers/mtd/maps/nettel.c linux/drivers/mtd/maps/nettel.c
--- linux-2.4.30/drivers/mtd/maps/nettel.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/nettel.c	Tue Sep 28 14:38:13 2004
@@ -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.6 2004/09/16 23:27:13 gleixner 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,
+	.bankwidth = 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,
+	.bankwidth = 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,19 @@
 	*amdpar = SC520_PAR(SC520_PAR_BOOTCS, amdaddr, maxsize);
 	__asm__ ("wbinvd");
 
-	nettel_amd_map.map_priv_1 = (unsigned long)
-		ioremap_nocache(amdaddr, maxsize);
-	if (!nettel_amd_map.map_priv_1) {
+	nettel_amd_map.phys = amdaddr;
+	nettel_amd_map.virt = (void __iomem *) ioremap_nocache(amdaddr, maxsize);
+	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 +332,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 +356,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 +388,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 +415,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 +470,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 -uNrb linux-2.4.30/drivers/mtd/maps/ocelot.c linux/drivers/mtd/maps/ocelot.c
--- linux-2.4.30/drivers/mtd/maps/ocelot.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/ocelot.c	Tue Sep 28 14:38:13 2004
@@ -1,5 +1,5 @@
 /*
- * $Id: ocelot.c,v 1.6 2001/10/02 15:05:14 dwmw2 Exp $
+ * $Id: ocelot.c,v 1.14 2004/09/16 23:27:13 gleixner 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,
+	.bankwidth = 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,
+	.bankwidth = 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 = (void __iomem *)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 = (void __iomem *)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 -uNrb linux-2.4.30/drivers/mtd/maps/ocotea.c linux/drivers/mtd/maps/ocotea.c
--- linux-2.4.30/drivers/mtd/maps/ocotea.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/ocotea.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,156 @@
+/*
+ * Mapping for Ocotea user flash
+ *
+ * Matt Porter <mporter@kernel.crashing.org>
+ *
+ * Copyright 2002-2004 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 <linux/version.h>
+#include <asm/io.h>
+#include <asm/ibm44x.h>
+#include <platforms/4xx/ocotea.h>
+
+static struct mtd_info *flash;
+
+static struct map_info ocotea_small_map = {
+	.name =		"Ocotea small flash",
+	.size =		OCOTEA_SMALL_FLASH_SIZE,
+	.buswidth =	1,
+};
+
+static struct map_info ocotea_large_map = {
+	.name =		"Ocotea large flash",
+	.size =		OCOTEA_LARGE_FLASH_SIZE,
+	.buswidth =	1,
+};
+
+static struct mtd_partition ocotea_small_partitions[] = {
+	{
+		.name =   "pibs",
+		.offset = 0x0,
+		.size =   0x100000,
+	}
+};
+
+static struct mtd_partition ocotea_large_partitions[] = {
+	{
+		.name =   "fs",
+		.offset = 0,
+		.size =   0x300000,
+	},
+	{
+		.name =   "firmware",
+		.offset = 0x300000,
+		.size =   0x100000,
+	}
+};
+
+#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
+
+int __init init_ocotea(void)
+{
+	u8 fpga0_reg;
+	u8 *fpga0_adr;
+	unsigned long long small_flash_base, large_flash_base;
+
+	fpga0_adr = ioremap64(OCOTEA_FPGA_ADDR, 16);
+	if (!fpga0_adr)
+		return -ENOMEM;
+
+	fpga0_reg = readb((unsigned long)fpga0_adr);
+	iounmap(fpga0_adr);
+
+	if (OCOTEA_BOOT_LARGE_FLASH(fpga0_reg)) {
+		small_flash_base = OCOTEA_SMALL_FLASH_HIGH;
+		large_flash_base = OCOTEA_LARGE_FLASH_LOW;
+	}
+	else {
+		small_flash_base = OCOTEA_SMALL_FLASH_LOW;
+		large_flash_base = OCOTEA_LARGE_FLASH_HIGH;
+	}
+
+	ocotea_small_map.phys = small_flash_base;
+	ocotea_small_map.virt =
+		(void __iomem *)ioremap64(small_flash_base,
+					 ocotea_small_map.size);
+
+	if (!ocotea_small_map.virt) {
+		printk("Failed to ioremap flash\n");
+		return -EIO;
+	}
+
+	simple_map_init(&ocotea_small_map);
+
+	flash = do_map_probe("map_rom", &ocotea_small_map);
+	if (flash) {
+		flash->owner = THIS_MODULE;
+		add_mtd_partitions(flash, ocotea_small_partitions,
+					NB_OF(ocotea_small_partitions));
+	} else {
+		printk("map probe failed for flash\n");
+		return -ENXIO;
+	}
+
+	ocotea_large_map.phys = large_flash_base;
+	ocotea_large_map.virt =
+		(void __iomem *)ioremap64(large_flash_base,
+					 ocotea_large_map.size);
+
+	if (!ocotea_large_map.virt) {
+		printk("Failed to ioremap flash\n");
+		return -EIO;
+	}
+
+	simple_map_init(&ocotea_large_map);
+
+	flash = do_map_probe("cfi_probe", &ocotea_large_map);
+	if (flash) {
+		flash->owner = THIS_MODULE;
+		add_mtd_partitions(flash, ocotea_large_partitions,
+					NB_OF(ocotea_large_partitions));
+	} else {
+		printk("map probe failed for flash\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static void __exit cleanup_ocotea(void)
+{
+	if (flash) {
+		del_mtd_partitions(flash);
+		map_destroy(flash);
+	}
+
+	if (ocotea_small_map.virt) {
+		iounmap((void *)ocotea_small_map.virt);
+		ocotea_small_map.virt = 0;
+	}
+
+	if (ocotea_large_map.virt) {
+		iounmap((void *)ocotea_large_map.virt);
+		ocotea_large_map.virt = 0;
+	}
+}
+
+module_init(init_ocotea);
+module_exit(cleanup_ocotea);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Matt Porter <mporter@kernel.crashing.org>");
+MODULE_DESCRIPTION("MTD map and partitions for IBM 440GX Ocotea boards");
diff -uNrb linux-2.4.30/drivers/mtd/maps/octagon-5066.c linux/drivers/mtd/maps/octagon-5066.c
--- linux-2.4.30/drivers/mtd/maps/octagon-5066.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/octagon-5066.c	Fri Sep 10 15:15:04 2004
@@ -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.26 2004/07/12 22:38:29 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
@@ -61,32 +62,12 @@
 }
 
 
-static __u8 oct5066_read8(struct map_info *map, unsigned long ofs)
+static map_word oct5066_read8(struct map_info *map, unsigned long ofs)
 {
-	__u8 ret;
+	map_word ret;
 	spin_lock(&oct5066_spin);
 	oct5066_page(map, ofs);
-	ret = readb(iomapadr + (ofs & WINDOW_MASK));
-	spin_unlock(&oct5066_spin);
-	return ret;
-}
-
-static __u16 oct5066_read16(struct map_info *map, unsigned long ofs)
-{
-	__u16 ret;
-	spin_lock(&oct5066_spin);
-	oct5066_page(map, ofs);
-	ret = readw(iomapadr + (ofs & WINDOW_MASK));
-	spin_unlock(&oct5066_spin);
-	return ret;
-}
-
-static __u32 oct5066_read32(struct map_info *map, unsigned long ofs)
-{
-	__u32 ret;
-	spin_lock(&oct5066_spin);
-	oct5066_page(map, ofs);
-	ret = readl(iomapadr + (ofs & WINDOW_MASK));
+	ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK));
 	spin_unlock(&oct5066_spin);
 	return ret;
 }
@@ -108,27 +89,11 @@
 	}
 }
 
-static void oct5066_write8(struct map_info *map, __u8 d, unsigned long adr)
+static void oct5066_write8(struct map_info *map, map_word d, unsigned long adr)
 {
 	spin_lock(&oct5066_spin);
 	oct5066_page(map, adr);
-	writeb(d, iomapadr + (adr & WINDOW_MASK));
-	spin_unlock(&oct5066_spin);
-}
-
-static void oct5066_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	spin_lock(&oct5066_spin);
-	oct5066_page(map, adr);
-	writew(d, iomapadr + (adr & WINDOW_MASK));
-	spin_unlock(&oct5066_spin);
-}
-
-static void oct5066_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	spin_lock(&oct5066_spin);
-	oct5066_page(map, adr);
-	writel(d, iomapadr + (adr & WINDOW_MASK));
+	writeb(d.x[0], iomapadr + (adr & WINDOW_MASK));
 	spin_unlock(&oct5066_spin);
 }
 
@@ -151,32 +116,26 @@
 
 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,
+		.bankwidth = 1,
+		.read = oct5066_read8,
+		.copy_from = oct5066_copy_from,
+		.write = oct5066_write8,
+		.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,
+		.bankwidth = 1,
+		.read = oct5066_read8,
+		.copy_from = oct5066_copy_from,
+		.write = oct5066_write8,
+		.copy_to = oct5066_copy_to,
+		.map_priv_1 = 2<<6
 	}
 };
 
@@ -244,6 +203,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 +221,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 -uNrb linux-2.4.30/drivers/mtd/maps/omap-toto-flash.c linux/drivers/mtd/maps/omap-toto-flash.c
--- linux-2.4.30/drivers/mtd/maps/omap-toto-flash.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/omap-toto-flash.c	Tue Sep 28 14:38:13 2004
@@ -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.3 2004/09/16 23:27:13 gleixner 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",
+	.bankwidth =	2,
+	.virt =		(void __iomem *)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.bankwidth*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 -uNrb linux-2.4.30/drivers/mtd/maps/pb1550-flash.c linux/drivers/mtd/maps/pb1550-flash.c
--- linux-2.4.30/drivers/mtd/maps/pb1550-flash.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/pb1550-flash.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,204 @@
+/*
+ * Flash memory access on Alchemy Pb1550 board
+ * 
+ * $Id: pb1550-flash.c,v 1.5 2004/09/16 23:27:13 gleixner Exp $
+ *
+ * (C) 2004 Embedded Edge, LLC, based on pb1550-flash.c:
+ * (C) 2003 Pete Popov <ppopov@pacbell.net>
+ * 
+ */
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+
+#include <asm/io.h>
+#include <asm/au1000.h>
+#include <asm/pb1550.h>
+
+#ifdef 	DEBUG_RW
+#define	DBG(x...)	printk(x)
+#else
+#define	DBG(x...)	
+#endif
+
+static unsigned long window_addr;
+static unsigned long window_size;
+
+
+static struct map_info pb1550_map = {
+	.name =	"Pb1550 flash",
+};
+
+static unsigned char flash_bankwidth = 4;
+
+/* 
+ * Support only 64MB NOR Flash parts
+ */
+
+#ifdef PB1550_BOTH_BANKS
+/* both banks will be used. Combine the first bank and the first 
+ * part of the second bank together into a single jffs/jffs2
+ * partition.
+ */
+static struct mtd_partition pb1550_partitions[] = {
+	/* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+	 * 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash
+	 * 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash
+	 */
+        {
+                .name = "User FS",
+                .size =   (0x1FC00000 - 0x18000000),
+                .offset = 0x0000000
+        },{
+                .name = "yamon",
+                .size = 0x0100000,
+		.offset = MTDPART_OFS_APPEND,
+                .mask_flags = MTD_WRITEABLE
+        },{
+                .name = "raw kernel",
+		.size = (0x300000 - 0x40000), /* last 256KB is yamon env */
+		.offset = MTDPART_OFS_APPEND,
+        }
+};
+#elif defined(PB1550_BOOT_ONLY)
+static struct mtd_partition pb1550_partitions[] = {
+	/* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+	 * 1C00 0000 1FFF FFFF CE0 64MB Boot NOR Flash
+	 */
+        {
+                .name = "User FS",
+                .size =   0x03c00000,
+                .offset = 0x0000000
+        },{
+                .name = "yamon",
+                .size = 0x0100000,
+		.offset = MTDPART_OFS_APPEND,
+                .mask_flags = MTD_WRITEABLE
+        },{
+                .name = "raw kernel",
+		.size = (0x300000-0x40000), /* last 256KB is yamon env */
+		.offset = MTDPART_OFS_APPEND,
+        }
+};
+#elif defined(PB1550_USER_ONLY)
+static struct mtd_partition pb1550_partitions[] = {
+	/* assume boot[2:0]:swap is '0000' or '1000', which translates to:
+	 * 1800 0000 1BFF FFFF CE0 64MB Param NOR Flash
+	 */
+        {
+                .name = "User FS",
+                .size = (0x4000000 - 0x200000), /* reserve 2MB for raw kernel */
+                .offset = 0x0000000
+        },{
+                .name = "raw kernel",
+		.size = MTDPART_SIZ_FULL,
+		.offset = MTDPART_OFS_APPEND,
+        }
+};
+#else
+#error MTD_PB1550 define combo error /* should never happen */
+#endif
+
+#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
+
+static struct mtd_info *mymtd;
+
+/*
+ * Probe the flash density and setup window address and size
+ * based on user CONFIG options. There are times when we don't
+ * want the MTD driver to be probing the boot or user flash,
+ * so having the option to enable only one bank is important.
+ */
+int setup_flash_params(void)
+{
+	u16 boot_swapboot;
+	boot_swapboot = (au_readl(MEM_STSTAT) & (0x7<<1)) | 
+		((bcsr->status >> 6)  & 0x1);
+	printk("Pb1550 MTD: boot:swap %d\n", boot_swapboot);
+
+	switch (boot_swapboot) {
+		case 0: /* 512Mbit devices, both enabled */
+		case 1: 
+		case 8:
+		case 9: 
+#if defined(PB1550_BOTH_BANKS)
+			window_addr = 0x18000000;
+			window_size = 0x8000000; 
+#elif defined(PB1550_BOOT_ONLY)
+			window_addr = 0x1C000000;
+			window_size = 0x4000000; 
+#else /* USER ONLY */
+			window_addr = 0x1E000000;
+			window_size = 0x4000000; 
+#endif
+			break;
+		case 0xC:
+		case 0xD:
+		case 0xE:
+		case 0xF: 
+			/* 64 MB Boot NOR Flash is disabled */
+			/* and the start address is moved to 0x0C00000 */
+			window_addr = 0x0C000000;
+			window_size = 0x4000000; 
+		default:
+			printk("Pb1550 MTD: unsupported boot:swap setting\n");
+			return 1;
+	}
+	return 0;
+}
+
+int __init pb1550_mtd_init(void)
+{
+	struct mtd_partition *parts;
+	int nb_parts = 0;
+	
+	/* Default flash bankwidth */
+	pb1550_map.bankwidth = flash_bankwidth;
+
+	if (setup_flash_params()) 
+		return -ENXIO;
+
+	/*
+	 * Static partition definition selection
+	 */
+	parts = pb1550_partitions;
+	nb_parts = NB_OF(pb1550_partitions);
+	pb1550_map.size = window_size;
+
+	/*
+	 * Now let's probe for the actual flash.  Do it here since
+	 * specific machine settings might have been set above.
+	 */
+	printk(KERN_NOTICE "Pb1550 flash: probing %d-bit flash bus\n", 
+			pb1550_map.bankwidth*8);
+	pb1550_map.virt = 
+		(void __iomem *)ioremap(window_addr, window_size);
+	mymtd = do_map_probe("cfi_probe", &pb1550_map);
+	if (!mymtd) return -ENXIO;
+	mymtd->owner = THIS_MODULE;
+
+	add_mtd_partitions(mymtd, parts, nb_parts);
+	return 0;
+}
+
+static void __exit pb1550_mtd_cleanup(void)
+{
+	if (mymtd) {
+		del_mtd_partitions(mymtd);
+		map_destroy(mymtd);
+	}
+}
+
+module_init(pb1550_mtd_init);
+module_exit(pb1550_mtd_cleanup);
+
+MODULE_AUTHOR("Embedded Edge, LLC");
+MODULE_DESCRIPTION("Pb1550 mtd map driver");
+MODULE_LICENSE("GPL");
diff -uNrb linux-2.4.30/drivers/mtd/maps/pb1xxx-flash.c linux/drivers/mtd/maps/pb1xxx-flash.c
--- linux-2.4.30/drivers/mtd/maps/pb1xxx-flash.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/pb1xxx-flash.c	Tue Sep 28 14:38:13 2004
@@ -3,12 +3,13 @@
  * 
  * (C) 2001 Pete Popov <ppopov@mvista.com>
  * 
- * $Id: pb1xxx-flash.c,v 1.4 2002/09/13 13:51:54 dwmw2 Exp $
+ * $Id: pb1xxx-flash.c,v 1.12 2004/09/16 23:27:13 gleixner Exp $
  */
 
 #include <linux/config.h>
 #include <linux/module.h>
 #include <linux/types.h>
+#include <linux/init.h>
 #include <linux/kernel.h>
 
 #include <linux/mtd/mtd.h>
@@ -25,168 +26,87 @@
 #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
-        }
+                .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
+/* both 32MB banks will be used. Combine the first 32MB bank and the
+ * first 28MB of the second bank together into a single jffs/jffs2
  * partition.
  */
-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
+                .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
+                .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,
+                .name         =   "User FS",
+                .size         =    0x1e00000,
+                .offset       =    0x0000000
+        },{
+                .name         =    "raw kernel",
+                .size         =    0x0200000,
+                .offset       =    0x1e00000,
         }
 };
 #else
@@ -196,11 +116,20 @@
 #error Unsupported board
 #endif
 
+#define NAME     	"Pb1x00 Linux Flash"
+#define PADDR    	WINDOW_ADDR
+#define BUSWIDTH	4
+#define SIZE		WINDOW_SIZE
+#define PARTITIONS	4
+
+static struct map_info pb1xxx_mtd_map = {
+	.name		= NAME,
+	.size		= SIZE,
+	.bankwidth	= BUSWIDTH,
+	.phys		= PADDR,
+};
 
-#define NB_OF(x)  (sizeof(x)/sizeof(x[0]))
-
-static struct mtd_partition *parsed_parts;
-static struct mtd_info *mymtd;
+static struct mtd_info *pb1xxx_mtd;
 
 int __init pb1xxx_mtd_init(void)
 {
@@ -208,40 +137,37 @@
 	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;
+	nb_parts = ARRAY_SIZE(pb1xxx_partitions);
 
 	/*
 	 * Now let's probe for the actual flash.  Do it here since
 	 * specific machine settings might have been set above.
 	 */
 	printk(KERN_NOTICE "Pb1xxx flash: probing %d-bit flash bus\n", 
-			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;
+			BUSWIDTH*8);
+	pb1xxx_mtd_map.virt = (void __iomem *)ioremap(WINDOW_ADDR, WINDOW_SIZE);
+
+	simple_map_init(&pb1xxx_mtd_map);
+
+	pb1xxx_mtd = do_map_probe("cfi_probe", &pb1xxx_mtd_map);
+	if (!pb1xxx_mtd) return -ENXIO;
+	pb1xxx_mtd->owner = THIS_MODULE;
 
-	add_mtd_partitions(mymtd, parts, nb_parts);
+	add_mtd_partitions(pb1xxx_mtd, 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);
+	if (pb1xxx_mtd) {
+		del_mtd_partitions(pb1xxx_mtd);
+		map_destroy(pb1xxx_mtd);
+		iounmap((void *) pb1xxx_mtd_map.virt);
 	}
 }
 
diff -uNrb linux-2.4.30/drivers/mtd/maps/pci.c linux/drivers/mtd/maps/pci.c
--- linux-2.4.30/drivers/mtd/maps/pci.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/pci.c	Fri Sep 10 15:15:04 2004
@@ -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.8 2004/07/12 22:38:29 dwmw2 Exp $
  * 
  * Generic PCI memory map driver.  We support the following boards:
  *  - Intel IQ80310 ATU.
@@ -39,6 +39,74 @@
 	struct pci_dev *dev;
 };	
 
+static map_word mtd_pci_read8(struct map_info *_map, unsigned long ofs)
+{
+	struct map_pci_info *map = (struct map_pci_info *)_map;
+	map_word val;
+	val.x[0]= readb(map->base + map->translate(map, ofs));
+//	printk("read8 : %08lx => %02x\n", ofs, val.x[0]);
+	return val;
+}
+
+#if 0
+static map_word mtd_pci_read16(struct map_info *_map, unsigned long ofs)
+{
+	struct map_pci_info *map = (struct map_pci_info *)_map;
+	map_word val;
+	val.x[0] = readw(map->base + map->translate(map, ofs));
+//	printk("read16: %08lx => %04x\n", ofs, val.x[0]);
+	return val;
+}
+#endif
+static map_word mtd_pci_read32(struct map_info *_map, unsigned long ofs)
+{
+	struct map_pci_info *map = (struct map_pci_info *)_map;
+	map_word val;
+	val.x[0] = readl(map->base + map->translate(map, ofs));
+//	printk("read32: %08lx => %08x\n", ofs, val.x[0]);
+	return val;
+}
+
+static void mtd_pci_copyfrom(struct map_info *_map, void *to, unsigned long from, ssize_t len)
+{
+	struct map_pci_info *map = (struct map_pci_info *)_map;
+	memcpy_fromio(to, map->base + map->translate(map, from), len);
+}
+
+static void mtd_pci_write8(struct map_info *_map, map_word val, unsigned long ofs)
+{
+	struct map_pci_info *map = (struct map_pci_info *)_map;
+//	printk("write8 : %08lx <= %02x\n", ofs, val.x[0]);
+	writeb(val.x[0], map->base + map->translate(map, ofs));
+}
+
+#if 0
+static void mtd_pci_write16(struct map_info *_map, map_word val, unsigned long ofs)
+{
+	struct map_pci_info *map = (struct map_pci_info *)_map;
+//	printk("write16: %08lx <= %04x\n", ofs, val.x[0]);
+	writew(val.x[0], map->base + map->translate(map, ofs));
+}
+#endif
+static void mtd_pci_write32(struct map_info *_map, map_word val, unsigned long ofs)
+{
+	struct map_pci_info *map = (struct map_pci_info *)_map;
+//	printk("write32: %08lx <= %08x\n", ofs, val.x[0]);
+	writel(val.x[0], map->base + map->translate(map, ofs));
+}
+
+static void mtd_pci_copyto(struct map_info *_map, unsigned long to, const void *from, ssize_t len)
+{
+	struct map_pci_info *map = (struct map_pci_info *)_map;
+	memcpy_toio(map->base + map->translate(map, to), from, len);
+}
+
+static struct map_info mtd_pci_map = {
+	.phys =		NO_XIP,
+	.copy_from =	mtd_pci_copyfrom,
+	.copy_to =	mtd_pci_copyto,
+};
+
 /*
  * Intel IOP80310 Flash driver
  */
@@ -48,7 +116,10 @@
 {
 	u32 win_base;
 
-	map->map.buswidth = 1;
+	map->map.bankwidth = 1;
+	map->map.read = mtd_pci_read8,
+	map->map.write = mtd_pci_write8,
+
 	map->map.size     = 0x00800000;
 	map->base         = ioremap_nocache(pci_resource_start(dev, 0),
 					    pci_resource_len(dev, 0));
@@ -98,10 +169,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",
 };
 
 /*
@@ -140,14 +211,16 @@
 			pci_read_config_dword(dev, PCI_ROM_ADDRESS, &val);
 			val |= PCI_ROM_ADDRESS_ENABLE;
 			pci_write_config_dword(dev, PCI_ROM_ADDRESS, val);
-			printk("%s: enabling expansion ROM\n", dev->slot_name);
+			printk("%s: enabling expansion ROM\n", pci_name(dev));
 		}
 	}
 
 	if (!len || !base)
 		return -ENXIO;
 
-	map->map.buswidth = 4;
+	map->map.bankwidth = 4;
+	map->map.read = mtd_pci_read32,
+	map->map.write = mtd_pci_write32,
 	map->map.size     = len;
 	map->base         = ioremap_nocache(base, len);
 
@@ -181,34 +254,32 @@
 }
 
 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",
 };
 
 /*
  * PCI device ID table
  */
 
-static struct pci_device_id mtd_pci_ids[] __devinitdata = {
+static struct pci_device_id mtd_pci_ids[] = {
 	{
-		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, }
 };
@@ -217,74 +288,6 @@
  * Generic code follows.
  */
 
-static u8 mtd_pci_read8(struct map_info *_map, unsigned long ofs)
-{
-	struct map_pci_info *map = (struct map_pci_info *)_map;
-	u8 val = readb(map->base + map->translate(map, ofs));
-//	printk("read8 : %08lx => %02x\n", ofs, val);
-	return val;
-}
-
-static u16 mtd_pci_read16(struct map_info *_map, unsigned long ofs)
-{
-	struct map_pci_info *map = (struct map_pci_info *)_map;
-	u16 val = readw(map->base + map->translate(map, ofs));
-//	printk("read16: %08lx => %04x\n", ofs, val);
-	return val;
-}
-
-static u32 mtd_pci_read32(struct map_info *_map, unsigned long ofs)
-{
-	struct map_pci_info *map = (struct map_pci_info *)_map;
-	u32 val = readl(map->base + map->translate(map, ofs));
-//	printk("read32: %08lx => %08x\n", ofs, val);
-	return val;
-}
-
-static void mtd_pci_copyfrom(struct map_info *_map, void *to, unsigned long from, ssize_t len)
-{
-	struct map_pci_info *map = (struct map_pci_info *)_map;
-	memcpy_fromio(to, map->base + map->translate(map, from), len);
-}
-
-static void mtd_pci_write8(struct map_info *_map, u8 val, unsigned long ofs)
-{
-	struct map_pci_info *map = (struct map_pci_info *)_map;
-//	printk("write8 : %08lx <= %02x\n", ofs, val);
-	writeb(val, map->base + map->translate(map, ofs));
-}
-
-static void mtd_pci_write16(struct map_info *_map, u16 val, unsigned long ofs)
-{
-	struct map_pci_info *map = (struct map_pci_info *)_map;
-//	printk("write16: %08lx <= %04x\n", ofs, val);
-	writew(val, map->base + map->translate(map, ofs));
-}
-
-static void mtd_pci_write32(struct map_info *_map, u32 val, unsigned long ofs)
-{
-	struct map_pci_info *map = (struct map_pci_info *)_map;
-//	printk("write32: %08lx <= %08x\n", ofs, val);
-	writel(val, map->base + map->translate(map, ofs));
-}
-
-static void mtd_pci_copyto(struct map_info *_map, unsigned long to, const void *from, ssize_t len)
-{
-	struct map_pci_info *map = (struct map_pci_info *)_map;
-	memcpy_toio(map->base + map->translate(map, to), from, len);
-}
-
-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,
-};
-
 static int __devinit
 mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
 {
@@ -307,7 +310,7 @@
 		goto release;
 
 	map->map       = mtd_pci_map;
-	map->map.name  = dev->slot_name;
+	map->map.name  = pci_name(dev);
 	map->dev       = dev;
 	map->exit      = info->exit;
 	map->translate = info->translate;
@@ -322,7 +325,7 @@
 	if (!mtd)
 		goto release;
 
-	mtd->module = THIS_MODULE;
+	mtd->owner = THIS_MODULE;
 	add_mtd_device(mtd);
 
 	pci_set_drvdata(dev, mtd);
@@ -359,10 +362,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 -uNrb linux-2.4.30/drivers/mtd/maps/pcmciamtd.c linux/drivers/mtd/maps/pcmciamtd.c
--- linux-2.4.30/drivers/mtd/maps/pcmciamtd.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/pcmciamtd.c	Fri Sep 10 15:15:04 2004
@@ -1,5 +1,5 @@
 /*
- * $Id: pcmciamtd.c,v 1.39 2003/01/06 17:51:38 spse Exp $
+ * $Id: pcmciamtd.c,v 1.51 2004/07/12 22:38:29 dwmw2 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.51 $"
 
 /* Size of the PCMCIA address space: 26 bits = 64 MB */
 #define MAX_PCMCIA_ADDR	0x4000000
@@ -71,7 +73,7 @@
 /* Module parameters */
 
 /* 2 = do 16-bit transfers, 1 = do 8-bit transfers */
-static int buswidth = 2;
+static int bankwidth = 2;
 
 /* Speed of memory accesses, in ns */
 static int mem_speed;
@@ -91,12 +93,12 @@
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("Simon Evans <spse@secret.org.uk>");
 MODULE_DESCRIPTION(DRIVER_DESC);
-MODULE_PARM(buswidth, "i");
-MODULE_PARM_DESC(buswidth, "Set buswidth (1=8 bit, 2=16 bit, default=2)");
+MODULE_PARM(bankwidth, "i");
+MODULE_PARM_DESC(bankwidth, "Set bankwidth (1=8 bit, 2=16 bit, default=2)");
 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");
@@ -105,16 +107,7 @@
 MODULE_PARM_DESC(mem_type, "Set Memory type (0=Flash, 1=RAM, 2=ROM, default=0)");
 
 
-
-static inline void cs_error(client_handle_t handle, int func, int ret)
-{
-	error_info_t err = { func, ret };
-	CardServices(ReportError, handle, &err);
-}
-
-
 /* read/write{8,16} copy_{from,to} routines with window remapping to access whole card */
-
 static caddr_t remap_window(struct map_info *map, unsigned long to)
 {
 	struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1;
@@ -132,7 +125,7 @@
 		DEBUG(2, "Remapping window from 0x%8.8x to 0x%8.8x",
 		      dev->offset, mrq.CardOffset);
 		mrq.Page = 0;
-		if( (ret = CardServices(MapMemPage, win, &mrq)) != CS_SUCCESS) {
+		if( (ret = pcmcia_map_mem_page(win, &mrq)) != CS_SUCCESS) {
 			cs_error(dev->link.handle, MapMemPage, ret);
 			return NULL;
 		}
@@ -142,32 +135,32 @@
 }
 
 
-static u8 pcmcia_read8_remap(struct map_info *map, unsigned long ofs)
+static map_word pcmcia_read8_remap(struct map_info *map, unsigned long ofs)
 {
 	caddr_t addr;
-	u8 d;
+	map_word d = {{0}};
 
 	addr = remap_window(map, ofs);
 	if(!addr)
-		return 0;
+		return d;
 
-	d = readb(addr);
-	DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, addr, d);
+	d.x[0] = readb(addr);
+	DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, addr, d.x[0]);
 	return d;
 }
 
 
-static u16 pcmcia_read16_remap(struct map_info *map, unsigned long ofs)
+static map_word pcmcia_read16_remap(struct map_info *map, unsigned long ofs)
 {
 	caddr_t addr;
-	u16 d;
+	map_word d = {{0}};
 
 	addr = remap_window(map, ofs);
 	if(!addr)
-		return 0;
+		return d;
 
-	d = readw(addr);
-	DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, addr, d);
+	d.x[0] = readw(addr);
+	DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, addr, d.x[0]);
 	return d;
 }
 
@@ -198,26 +191,26 @@
 }
 
 
-static void pcmcia_write8_remap(struct map_info *map, u8 d, unsigned long adr)
+static void pcmcia_write8_remap(struct map_info *map, map_word d, unsigned long adr)
 {
 	caddr_t addr = remap_window(map, adr);
 
 	if(!addr)
 		return;
 
-	DEBUG(3, "adr = 0x%08lx (%p)  data = 0x%02x", adr, addr, d);
-	writeb(d, addr);
+	DEBUG(3, "adr = 0x%08lx (%p)  data = 0x%02x", adr, addr, d.x[0]);
+	writeb(d.x[0], addr);
 }
 
 
-static void pcmcia_write16_remap(struct map_info *map, u16 d, unsigned long adr)
+static void pcmcia_write16_remap(struct map_info *map, map_word d, unsigned long adr)
 {
 	caddr_t addr = remap_window(map, adr);
 	if(!addr)
 		return;
 
-	DEBUG(3, "adr = 0x%08lx (%p)  data = 0x%04x", adr, addr, d);
-	writew(d, addr);
+	DEBUG(3, "adr = 0x%08lx (%p)  data = 0x%04x", adr, addr, d.x[0]);
+	writew(d.x[0], addr);
 }
 
 
@@ -251,30 +244,30 @@
 
 #define DEV_REMOVED(x)  (!(*(u_int *)x->map_priv_1 & DEV_PRESENT))
 
-static u8 pcmcia_read8(struct map_info *map, unsigned long ofs)
+static map_word pcmcia_read8(struct map_info *map, unsigned long ofs)
 {
 	caddr_t win_base = (caddr_t)map->map_priv_2;
-	u8 d;
+	map_word d = {{0}};
 
 	if(DEV_REMOVED(map))
-		return 0;
+		return d;
 
-	d = readb(win_base + ofs);
-	DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, win_base + ofs, d);
+	d.x[0] = readb(win_base + ofs);
+	DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, win_base + ofs, d.x[0]);
 	return d;
 }
 
 
-static u16 pcmcia_read16(struct map_info *map, unsigned long ofs)
+static map_word pcmcia_read16(struct map_info *map, unsigned long ofs)
 {
 	caddr_t win_base = (caddr_t)map->map_priv_2;
-	u16 d;
+	map_word d = {{0}};
 
 	if(DEV_REMOVED(map))
-		return 0;
+		return d;
 
-	d = readw(win_base + ofs);
-	DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, win_base + ofs, d);
+	d.x[0] = readw(win_base + ofs);
+	DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, win_base + ofs, d.x[0]);
 	return d;
 }
 
@@ -339,7 +332,7 @@
 	mod.Vpp1 = mod.Vpp2 = on ? dev->vpp : 0;
 
 	DEBUG(2, "dev = %p on = %d vpp = %d\n", dev, on, dev->vpp);
-	ret = CardServices(ModifyConfiguration, link->handle, &mod);
+	ret = pcmcia_modify_configuration(link->handle, &mod);
 	if(ret != CS_SUCCESS) {
 		cs_error(link->handle, ModifyConfiguration, ret);
 	}
@@ -351,9 +344,8 @@
  * still open, this will be postponed until it is closed.
  */
 
-static void pcmciamtd_release(u_long arg)
+static void pcmciamtd_release(dev_link_t *link)
 {
-	dev_link_t *link = (dev_link_t *)arg;
 	struct pcmciamtd_dev *dev = link->priv;
 
 	DEBUG(3, "link = 0x%p", link);
@@ -363,9 +355,9 @@
 			iounmap(dev->win_base);
 			dev->win_base = NULL;
 		}
-		CardServices(ReleaseWindow, link->win);
+		pcmcia_release_window(link->win);
 	}
-	CardServices(ReleaseConfiguration, link->handle);
+	pcmcia_release_configuration(link->handle);
 	link->state &= ~DEV_CONFIG;
 }
 
@@ -383,14 +375,14 @@
 	tuple.TupleOffset = 0;
 	tuple.DesiredTuple = RETURN_FIRST_TUPLE;
 
-	rc = CardServices(GetFirstTuple, link->handle, &tuple);
+	rc = pcmcia_get_first_tuple(link->handle, &tuple);
 	while(rc == CS_SUCCESS) {
-		rc = CardServices(GetTupleData, link->handle, &tuple);
+		rc = pcmcia_get_tuple_data(link->handle, &tuple);
 		if(rc != CS_SUCCESS) {
 			cs_error(link->handle, GetTupleData, rc);
 			break;
 		}
-		rc = CardServices(ParseTuple, link->handle, &tuple, &parse);
+		rc = pcmcia_parse_tuple(link->handle, &tuple, &parse);
 		if(rc != CS_SUCCESS) {
 			cs_error(link->handle, ParseTuple, rc);
 			break;
@@ -447,9 +439,9 @@
 		case CISTPL_DEVICE_GEO: {
 			cistpl_device_geo_t *t = &parse.device_geo;
 			int i;
-			dev->pcmcia_map.buswidth = t->geo[0].buswidth;
+			dev->pcmcia_map.bankwidth = t->geo[0].buswidth;
 			for(i = 0; i < t->ngeo; i++) {
-				DEBUG(2, "region: %d buswidth = %u", i, t->geo[i].buswidth);
+				DEBUG(2, "region: %d bankwidth = %u", i, t->geo[i].buswidth);
 				DEBUG(2, "region: %d erase_block = %u", i, t->geo[i].erase_block);
 				DEBUG(2, "region: %d read_block = %u", i, t->geo[i].read_block);
 				DEBUG(2, "region: %d write_block = %u", i, t->geo[i].write_block);
@@ -463,22 +455,22 @@
 			DEBUG(2, "Unknown tuple code %d", tuple.TupleCode);
 		}
 		
-		rc = CardServices(GetNextTuple, link->handle, &tuple, &parse);
+		rc = pcmcia_get_next_tuple(link->handle, &tuple);
 	}
 	if(!dev->pcmcia_map.size)
 		dev->pcmcia_map.size = MAX_PCMCIA_ADDR;
 
-	if(!dev->pcmcia_map.buswidth)
-		dev->pcmcia_map.buswidth = 2;
+	if(!dev->pcmcia_map.bankwidth)
+		dev->pcmcia_map.bankwidth = 2;
 
 	if(force_size) {
 		dev->pcmcia_map.size = force_size << 20;
 		DEBUG(2, "size forced to %dM", force_size);
 	}
 
-	if(buswidth) {
-		dev->pcmcia_map.buswidth = buswidth;
-		DEBUG(2, "buswidth forced to %d", buswidth);
+	if(bankwidth) {
+		dev->pcmcia_map.bankwidth = bankwidth;
+		DEBUG(2, "bankwidth forced to %d", bankwidth);
 	}		
 
 	dev->pcmcia_map.name = dev->mtd_name;
@@ -488,7 +480,7 @@
 	}
 
 	DEBUG(1, "Device: Size: %lu Width:%d Name: %s",
-	      dev->pcmcia_map.size, dev->pcmcia_map.buswidth << 3, dev->mtd_name);
+	      dev->pcmcia_map.size, dev->pcmcia_map.bankwidth << 3, dev->mtd_name);
 }
 
 
@@ -497,8 +489,8 @@
  * MTD device available to the system.
  */
 
-#define CS_CHECK(fn, args...) \
-while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed
+#define CS_CHECK(fn, ret) \
+do { last_fn = (fn); if ((last_ret = (ret)) != 0) goto cs_failed; } while (0)
 
 static void pcmciamtd_config(dev_link_t *link)
 {
@@ -520,7 +512,7 @@
 	link->state |= DEV_CONFIG;
 
 	DEBUG(2, "Validating CIS");
-	ret = CardServices(ValidateCIS, link->handle, &cisinfo);
+	ret = pcmcia_validate_cis(link->handle, &cisinfo);
 	if(ret != CS_SUCCESS) {
 		cs_error(link->handle, GetTupleData, ret);
 	} else {
@@ -529,21 +521,25 @@
 
 	card_settings(dev, link, &new_name);
 
-	dev->pcmcia_map.read8 = pcmcia_read8_remap;
-	dev->pcmcia_map.read16 = pcmcia_read16_remap;
+	dev->pcmcia_map.phys = NO_XIP;
 	dev->pcmcia_map.copy_from = pcmcia_copy_from_remap;
-	dev->pcmcia_map.write8 = pcmcia_write8_remap;
-	dev->pcmcia_map.write16 = pcmcia_write16_remap;
 	dev->pcmcia_map.copy_to = pcmcia_copy_to_remap;
+	if (dev->pcmcia_map.bankwidth == 1) {
+		dev->pcmcia_map.read = pcmcia_read8_remap;
+		dev->pcmcia_map.write = pcmcia_write8_remap;
+	} else {
+		dev->pcmcia_map.read = pcmcia_read16_remap;
+		dev->pcmcia_map.write = pcmcia_write16_remap;
+	}
 	if(setvpp == 1)
 		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;
-	req.Attributes |= (dev->pcmcia_map.buswidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16;
+	req.Attributes |= (dev->pcmcia_map.bankwidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16;
 	req.Base = 0;
 	req.AccessSpeed = mem_speed;
 	link->win = (window_handle_t)link->handle;
@@ -552,15 +548,14 @@
 
 	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);
+		ret = pcmcia_request_window(&link->handle, &req, &link->win);
 		DEBUG(2, "ret = %d dev->win_size = %d", ret, dev->win_size);
 		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;
 		}
@@ -570,19 +565,19 @@
 
 	if(!dev->win_size) {
 		err("Cant allocate memory window");
-		pcmciamtd_release((u_long)link);
+		pcmciamtd_release(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);
+	CS_CHECK(GetStatus, pcmcia_get_status(link->handle, &status));
 	DEBUG(2, "status value: 0x%x window handle = 0x%8.8lx",
 	      status.CardState, (unsigned long)link->win);
 	dev->win_base = ioremap(req.Base, req.Size);
 	if(!dev->win_base) {
 		err("ioremap(%lu, %u) failed", req.Base, req.Size);
-		pcmciamtd_release((u_long)link);
+		pcmciamtd_release(link);
 		return;
 	}
 	DEBUG(1, "mapped window dev = %p req.base = 0x%lx base = %p size = 0x%x",
@@ -593,7 +588,7 @@
 	dev->pcmcia_map.map_priv_2 = (unsigned long)link->win;
 
 	DEBUG(2, "Getting configuration");
-	CS_CHECK(GetConfigurationInfo, link->handle, &t);
+	CS_CHECK(GetConfigurationInfo, pcmcia_get_configuration_info(link->handle, &t));
 	DEBUG(2, "Vcc = %d Vpp1 = %d Vpp2 = %d", t.Vcc, t.Vpp1, t.Vpp2);
 	dev->vpp = (vpp) ? vpp : t.Vpp1;
 	link->conf.Attributes = 0;
@@ -615,7 +610,7 @@
 	link->conf.ConfigIndex = 0;
 	link->conf.Present = t.Present;
 	DEBUG(2, "Setting Configuration");
-	ret = CardServices(RequestConfiguration, link->handle, &link->conf);
+	ret = pcmcia_request_configuration(link->handle, &link->conf);
 	if(ret != CS_SUCCESS) {
 		cs_error(link->handle, RequestConfiguration, ret);
 	}
@@ -637,26 +632,26 @@
 	
 	if(!mtd) {
 		DEBUG(1, "Cant find an MTD");
-		pcmciamtd_release((u_long)link);
+		pcmciamtd_release(link);
 		return;
 	}
 
 	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,
@@ -665,11 +660,14 @@
 		DEBUG(1, "Using non remapping memory functions");
 		dev->pcmcia_map.map_priv_1 = (unsigned long)&(dev->link.state);
 		dev->pcmcia_map.map_priv_2 = (unsigned long)dev->win_base;
-		dev->pcmcia_map.read8 = pcmcia_read8;
-		dev->pcmcia_map.read16 = pcmcia_read16;
+		if (dev->pcmcia_map.bankwidth == 1) {
+			dev->pcmcia_map.read = pcmcia_read8;
+			dev->pcmcia_map.write = pcmcia_write8;
+		} else {
+			dev->pcmcia_map.read = pcmcia_read16;
+			dev->pcmcia_map.write = pcmcia_write16;
+		}
 		dev->pcmcia_map.copy_from = pcmcia_copy_from;
-		dev->pcmcia_map.write8 = pcmcia_write8;
-		dev->pcmcia_map.write16 = pcmcia_write16;
 		dev->pcmcia_map.copy_to = pcmcia_copy_to;
 	}
 
@@ -677,7 +675,7 @@
 		map_destroy(mtd);
 		dev->mtd_info = NULL;
 		err("Couldnt register MTD device");
-		pcmciamtd_release((u_long)link);
+		pcmciamtd_release(link);
 		return;
 	}
 	snprintf(dev->node.dev_name, sizeof(dev->node.dev_name), "mtd%d", mtd->index);
@@ -689,7 +687,7 @@
  cs_failed:
 	cs_error(link->handle, last_fn, last_ret);
 	err("CS Error, exiting");
-	pcmciamtd_release((u_long)link);
+	pcmciamtd_release(link);
 	return;
 }
 
@@ -716,7 +714,7 @@
 				del_mtd_device(dev->mtd_info);
 				info("mtd%d: Removed", dev->mtd_info->index);
 			}
-			mod_timer(&link->release, jiffies + HZ/20);
+			pcmciamtd_release(link);
 		}
 		break;
 	case CS_EVENT_CARD_INSERTION:
@@ -757,16 +755,14 @@
 {
 	DEBUG(3, "link=0x%p", link);
 
-	del_timer(&link->release);
-
 	if(link->state & DEV_CONFIG) {
-		pcmciamtd_release((u_long)link);
+		pcmciamtd_release(link);
 	}
 
 	if (link->handle) {
 		int ret;
 		DEBUG(2, "Deregistering with card services");
-		ret = CardServices(DeregisterClient, link->handle);
+		ret = pcmcia_deregister_client(link->handle);
 		if (ret != CS_SUCCESS)
 			cs_error(link->handle, DeregisterClient, ret);
 	}
@@ -796,10 +792,6 @@
 	link = &dev->link;
 	link->priv = dev;
 
-	init_timer(&link->release);
-	link->release.function = &pcmciamtd_release;
-	link->release.data = (u_long)link;
-
 	link->conf.Attributes = 0;
 	link->conf.IntType = INT_MEMORY;
 
@@ -817,7 +809,7 @@
 	client_reg.Version = 0x0210;
 	client_reg.event_callback_args.client_data = link;
 	DEBUG(2, "Calling RegisterClient");
-	ret = CardServices(RegisterClient, &link->handle, &client_reg);
+	ret = pcmcia_register_client(&link->handle, &client_reg);
 	if (ret != 0) {
 		cs_error(link->handle, RegisterClient, ret);
 		pcmciamtd_detach(link);
@@ -828,20 +820,23 @@
 }
 
 
+static struct pcmcia_driver pcmciamtd_driver = {
+	.drv		= {
+		.name	= "pcmciamtd"
+	},
+	.attach		= pcmciamtd_attach,
+	.detach		= pcmciamtd_detach,
+	.owner		= THIS_MODULE
+};
+
+
 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);
-		buswidth = 2;
+	if(bankwidth && bankwidth != 1 && bankwidth != 2) {
+		info("bad bankwidth (%d), using default", bankwidth);
+		bankwidth = 2;
 	}
 	if(force_size && (force_size < 1 || force_size > 64)) {
 		info("bad force_size (%d), using default", force_size);
@@ -851,15 +846,14 @@
 		info("bad mem_type (%d), using default", mem_type);
 		mem_type = 0;
 	}
-	register_pccard_driver(&dev_info, &pcmciamtd_attach, &pcmciamtd_detach);
-	return 0;
+	return pcmcia_register_driver(&pcmciamtd_driver);
 }
 
 
 static void __exit exit_pcmciamtd(void)
 {
 	DEBUG(1, DRIVER_DESC " unloading");
-	unregister_pccard_driver(&dev_info);
+	pcmcia_unregister_driver(&pcmciamtd_driver);
 
 	while(dev_list) {
 		dev_link_t *link = dev_list;
diff -uNrb linux-2.4.30/drivers/mtd/maps/physmap.c linux/drivers/mtd/maps/physmap.c
--- linux-2.4.30/drivers/mtd/maps/physmap.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/physmap.c	Tue Sep 28 14:38:13 2004
@@ -1,179 +1,119 @@
 /*
- * $Id: physmap.c,v 1.21 2002/09/05 05:12:54 acurtis Exp $
+ * $Id: physmap.c,v 1.35 2004/09/16 23:27:13 gleixner 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);
-}
-
-__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();
-}
-
-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	__raw_writew(d, map->map_priv_1 + adr);
-	mb();
-}
-
-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-	__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)
-{
-	memcpy_toio(map->map_priv_1 + to, from, len);
-}
-
 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
+	.name = "phys_mapped_flash",
+	.phys = CONFIG_MTD_PHYSMAP_START,
+	.size = CONFIG_MTD_PHYSMAP_LEN,
+	.bankwidth = CONFIG_MTD_PHYSMAP_BANKWIDTH,
 };
 
 #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
-};
+static struct mtd_partition *mtd_parts;
+static int                   mtd_parts_nb;
 
-#define NUM_PARTITIONS	(sizeof(physmap_partitions)/sizeof(struct mtd_partition))
+static int num_physmap_partitions;
+static struct mtd_partition *physmap_partitions;
 
-#endif
-#endif
+static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL};
 
-int __init init_physmap(void)
+void physmap_set_partitions(struct mtd_partition *parts, int num_parts)
 {
-	static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", 0 };
+	physmap_partitions=parts;
+	num_physmap_partitions=num_parts;
+}
+#endif /* CONFIG_MTD_PARTITIONS */
+
+static int __init init_physmap(void)
+{
+	static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL };
 	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 = (void __iomem *)ioremap(physmap_map.phys, physmap_map.size);
 
-	if (!physmap_map.map_priv_1) {
+	if (!physmap_map.virt) {
 		printk("Failed to ioremap\n");
 		return -EIO;
 	}
 	
-	mymtd = 0;
+	simple_map_init(&physmap_map);
+
+	mymtd = NULL;
 	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 -uNrb linux-2.4.30/drivers/mtd/maps/pnc2000.c linux/drivers/mtd/maps/pnc2000.c
--- linux-2.4.30/drivers/mtd/maps/pnc2000.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/pnc2000.c	Tue Sep 28 14:38:13 2004
@@ -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.16 2004/09/16 23:27:13 gleixner 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,
+	.bankwidth = 4,
+	.phys = 0xFFFFFFFF,
+	.virt = (void __iomem *)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 -uNrb linux-2.4.30/drivers/mtd/maps/redwood.c linux/drivers/mtd/maps/redwood.c
--- linux-2.4.30/drivers/mtd/maps/redwood.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/redwood.c	Tue Sep 28 14:38:13 2004
@@ -1,38 +1,23 @@
 /*
- * $Id:
+ * $Id: redwood.c,v 1.9 2004/09/16 23:27:13 gleixner Exp $
  *
- * redwood.c - mapper for IBM Redwood-4/5 board.
+ * drivers/mtd/maps/redwood.c
  *	
- * Copyright 2001 MontaVista Softare Inc. 
+ * FLASH map for the IBM Redwood 4/5/6 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  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.
- *
- *  History: 12/17/2001 - Armin
- *  		migrated to use do_map_probe
+ * 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,96 +25,102 @@
 
 #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);
-}
-
-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;
-}
-
-void redwood_flash_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-        *(__u16 *)(map->map_priv_1 + adr) = d;
-}
-
-void redwood_flash_write32(struct map_info *map, __u32 d, unsigned long adr)
-{
-        *(__u32 *)(map->map_priv_1 + adr) = d;
-}
-
-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);
-}
+#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
 
-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
+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 */
+	}
 };
 
+#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
 
 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 filesystem",
+		.offset = RW_PART0_OF,
+		.size = RW_PART0_SZ
         },
         {
-                name: "Redwood kernel",
-                offset: 0x10000,
-                size:   0x200000 - 0x10000
+		.name = "Redwood OpenBIOS Vital Product Data",
+		.offset = RW_PART1_OF,
+		.size = RW_PART1_SZ,
+		.mask_flags = MTD_WRITEABLE	/* force read-only */
         },
         {
-                name: "Redwood OpenBIOS non-volatile storage",
-                offset: 0x200000,
-                size:   0x10000,
-                mask_flags: MTD_WRITEABLE       /* force read-only */
+		.name = "Redwood kernel",
+		.offset = RW_PART2_OF,
+		.size = RW_PART2_SZ
         },
         {
-                name: "Redwood filesystem",
-                offset: 0x210000,
-                size:   0x200000 - (0x10000 + 0x20000)
-        },
-        {
-                name: "Redwood OpenBIOS",
-                offset: 0x3e0000,
-                size:   0x20000,
-                mask_flags: MTD_WRITEABLE       /* force read-only */
+		.name = "Redwood OpenBIOS",
+		.offset = RW_PART3_OF,
+		.size = RW_PART3_SZ,
+		.mask_flags = MTD_WRITEABLE	/* force read-only */
         }
 };
+
+#endif /* CONFIG_REDWOOD_6 */
+
+struct map_info redwood_flash_map = {
+	.name = "IBM Redwood",
+	.size = WINDOW_SIZE,
+	.bankwidth = 2,
+	.phys = WINDOW_ADDR,
+};
+
+
 #define NUM_REDWOOD_FLASH_PARTITIONS \
         (sizeof(redwood_flash_partitions)/sizeof(redwood_flash_partitions[0]))
 
@@ -140,18 +131,19 @@
         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 =
+		(void __iomem *)ioremap(WINDOW_ADDR, WINDOW_SIZE);
 
-        if (!redwood_flash_map.map_priv_1) {
+	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->module = THIS_MODULE;
+		redwood_mtd->owner = THIS_MODULE;
                 return add_mtd_partitions(redwood_mtd,
                                           redwood_flash_partitions,
                                           NUM_REDWOOD_FLASH_PARTITIONS);
@@ -164,10 +156,15 @@
 {
         if (redwood_mtd) {
                 del_mtd_partitions(redwood_mtd);
-                iounmap((void *)redwood_flash_map.map_priv_1);
+		/* 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 -uNrb linux-2.4.30/drivers/mtd/maps/rpxlite.c linux/drivers/mtd/maps/rpxlite.c
--- linux-2.4.30/drivers/mtd/maps/rpxlite.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/rpxlite.c	Tue Sep 28 14:38:13 2004
@@ -1,5 +1,5 @@
 /*
- * $Id: rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp $
+ * $Id: rpxlite.c,v 1.21 2004/09/16 23:27:13 gleixner 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,
+	.bankwidth = 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 = (void __iomem *)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 -uNrb linux-2.4.30/drivers/mtd/maps/sa1100-flash.c linux/drivers/mtd/maps/sa1100-flash.c
--- linux-2.4.30/drivers/mtd/maps/sa1100-flash.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/sa1100-flash.c	Tue Sep 28 14:38:13 2004
@@ -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.41 2004/09/16 23:27:13 gleixner 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,241 @@
 
 #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,
+	}
+};
+#endif
+
+#ifdef CONFIG_SA1100_JORNADA56X
+static struct mtd_partition jornada56x_partitions[] = {
+	{
+		.name		= "bootldr",
+		.size		= 0x00040000,
+		.offset		= 0,
+		.mask_flags	= MTD_WRITEABLE,
 	}, {
-		size:		MTDPART_SIZ_FULL,
-		offset:		MTDPART_OFS_APPEND,
+		.name		= "rootfs",
+		.size		= MTDPART_SIZ_FULL,
+		.offset		= MTDPART_OFS_APPEND,
 	}
 };
+
+static void jornada56x_set_vpp(struct map_info *map, int vpp)
+{
+	if (vpp)
+		GPSR = GPIO_GPIO26;
+	else
+		GPCR = GPIO_GPIO26;
+	GPDR |= GPIO_GPIO26;
+}
+#else
+#define jornada56x_set_vpp NULL
 #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 +564,823 @@
 		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;
+		*parts       = adsbitsy_partitions;
 		nb_parts = ARRAY_SIZE(adsbitsy_partitions);
-		sa1100_map.size = ADSBITSY_FLASH_SIZE;
-		sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4;
 	}
 #endif
 #ifdef CONFIG_SA1100_ASSABET
 	if (machine_is_assabet()) {
-		parts = assabet_partitions;
+		*parts       = assabet_partitions;
 		nb_parts = ARRAY_SIZE(assabet_partitions);
-		sa1100_map.size = ASSABET_FLASH_SIZE;
 	}
 #endif
 #ifdef CONFIG_SA1100_BADGE4
 	if (machine_is_badge4()) {
-		parts = badge4_partitions;
+		*parts       = badge4_partitions;
 		nb_parts = ARRAY_SIZE(badge4_partitions);
-		sa1100_map.size = BADGE4_FLASH_SIZE;
 	}
 #endif
 #ifdef CONFIG_SA1100_CERF
 	if (machine_is_cerf()) {
-		parts = cerf_partitions;
+		*parts       = cerf_partitions;
 		nb_parts = ARRAY_SIZE(cerf_partitions);
-		sa1100_map.size = CERF_FLASH_SIZE;
 	}
 #endif
 #ifdef CONFIG_SA1100_CONSUS
 	if (machine_is_consus()) {
-		parts = consus_partitions;
+		*parts       = consus_partitions;
 		nb_parts = ARRAY_SIZE(consus_partitions);
-		sa1100_map.size = CONSUS_FLASH_SIZE;
 	}
 #endif
 #ifdef CONFIG_SA1100_FLEXANET
 	if (machine_is_flexanet()) {
-		parts = flexanet_partitions;
+		*parts       = flexanet_partitions;
 		nb_parts = ARRAY_SIZE(flexanet_partitions);
-		sa1100_map.size = FLEXANET_FLASH_SIZE;
 	}
 #endif
 #ifdef CONFIG_SA1100_FREEBIRD
 	if (machine_is_freebird()) {
-		parts = freebird_partitions;
+		*parts       = freebird_partitions;
 		nb_parts = ARRAY_SIZE(freebird_partitions);
-		sa1100_map.size = FREEBIRD_FLASH_SIZE;
 	}
 #endif
 #ifdef CONFIG_SA1100_FRODO
 	if (machine_is_frodo()) {
-		parts = frodo_partitions;
+		*parts       = frodo_partitions;
 		nb_parts = ARRAY_SIZE(frodo_partitions);
-		sa1100_map.size = FRODO_FLASH_SIZE;
-		base = 0x00000000;
 	}
 #endif
 #ifdef CONFIG_SA1100_GRAPHICSCLIENT
 	if (machine_is_graphicsclient()) {
-		parts = graphicsclient_partitions;
+		*parts       = graphicsclient_partitions;
 		nb_parts = ARRAY_SIZE(graphicsclient_partitions);
-		sa1100_map.size = GRAPHICSCLIENT_FLASH_SIZE;
-		sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4;
 	}
 #endif
 #ifdef CONFIG_SA1100_GRAPHICSMASTER
 	if (machine_is_graphicsmaster()) {
-		parts = graphicsmaster_partitions;
+		*parts       = graphicsmaster_partitions;
 		nb_parts = ARRAY_SIZE(graphicsmaster_partitions);
-		sa1100_map.size = GRAPHICSMASTER_FLASH_SIZE;
-		sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4;
 	}
 #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;
+		*parts       = huw_webpanel_partitions;
 		nb_parts = ARRAY_SIZE(huw_webpanel_partitions);
-		sa1100_map.size = HUW_WEBPANEL_FLASH_SIZE;
+	}
+#endif
+#ifdef CONFIG_SA1100_JORNADA56X
+	if (machine_is_jornada56x()) {
+		*parts       = jornada56x_partitions;
+		nb_parts     = ARRAY_SIZE(jornada56x_partitions);
 	}
 #endif
 #ifdef CONFIG_SA1100_JORNADA720
 	if (machine_is_jornada720()) {
-		parts = jornada720_partitions;
+		*parts       = jornada720_partitions;
 		nb_parts = ARRAY_SIZE(jornada720_partitions);
-		sa1100_map.size = JORNADA720_FLASH_SIZE;
-		sa1100_map.set_vpp = jornada720_set_vpp;
 	}
 #endif
 #ifdef CONFIG_SA1100_PANGOLIN
 	if (machine_is_pangolin()) {
-		parts = pangolin_partitions;
+		*parts       = pangolin_partitions;
 		nb_parts = ARRAY_SIZE(pangolin_partitions);
-		sa1100_map.size = PANGOLIN_FLASH_SIZE;
 	}
 #endif
 #ifdef CONFIG_SA1100_PT_SYSTEM3
 	if (machine_is_pt_system3()) {
-		parts = system3_partitions;
+		*parts       = system3_partitions;
 		nb_parts = ARRAY_SIZE(system3_partitions);
-		sa1100_map.size = SYSTEM3_FLASH_SIZE;
 	}
 #endif
 #ifdef CONFIG_SA1100_SHANNON
 	if (machine_is_shannon()) {
-		parts = shannon_partitions;
+		*parts       = shannon_partitions;
 		nb_parts = ARRAY_SIZE(shannon_partitions);
-		sa1100_map.size = SHANNON_FLASH_SIZE;
 	}
 #endif
 #ifdef CONFIG_SA1100_SHERMAN
 	if (machine_is_sherman()) {
-		parts = sherman_partitions;
+		*parts       = sherman_partitions;
 		nb_parts = ARRAY_SIZE(sherman_partitions);
-		sa1100_map.size = SHERMAN_FLASH_SIZE;
 	}
 #endif
 #ifdef CONFIG_SA1100_SIMPAD
 	if (machine_is_simpad()) {
-		parts = simpad_partitions;
+		*parts       = simpad_partitions;
 		nb_parts = ARRAY_SIZE(simpad_partitions);
-		sa1100_map.size = SIMPAD_FLASH_SIZE;
 	}
 #endif
 #ifdef CONFIG_SA1100_STORK
 	if (machine_is_stork()) {
-		parts = stork_partitions;
+		*parts       = stork_partitions;
 		nb_parts = ARRAY_SIZE(stork_partitions);
-		sa1100_map.size = STORK_FLASH_SIZE;
+	}
+#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;
+		*parts       = yopy_partitions;
 		nb_parts = ARRAY_SIZE(yopy_partitions);
-		sa1100_map.size = YOPY_FLASH_SIZE;
 	}
 #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);
+	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.
+	 */
+	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;
-		if (!sa1100_map.map_priv_1)
-			goto out_err;
+			break;
 	}
 
+		sa[i].map->virt = (void __iomem *)sa[i].vbase;
+		sa[i].map->phys = sa[i].base;
+		sa[i].map->set_vpp = sa[i].set_vpp;
+		sa[i].map->bankwidth = 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.
 	 */
-	printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8);
-	mymtd = do_map_probe("cfi_probe", &sa1100_map);
+		sa[i].mtd = do_map_probe("cfi_probe", sa[i].map);
+		if (sa[i].mtd == NULL) {
 	ret = -ENXIO;
-	if (!mymtd)
-		goto out_err;
-	mymtd->module = THIS_MODULE;
+			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 == -ENXIO) {
+		iounmap(sa[i].vbase);
+		sa[i].vbase = NULL;
+		release_resource(sa[i].res);
+		sa[i].res = NULL;
+	}
 
-		if (ret > 0) {
-			part_type = "RedBoot";
-			parsed_nr_parts = ret;
+	/*
+	 * 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
+		}
 		}
+
+	/*
+	 * 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
-#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;
+
+	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);
+}
+
+/*
+ * 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;
 	}
-#endif
 
-	if (parsed_nr_parts > 0) {
-		parts = parsed_parts;
-		nb_parts = parsed_nr_parts;
+	if ((msc & 3) == 1) {
+		printk("wrong type\n");
+		return;
 	}
 
-	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);
+	sa1100_probe_map.bankwidth = msc & MSC_RBW ? 2 : 4;
+	sa1100_probe_map.size = SZ_1M;
+	sa1100_probe_map.phys = phys;
+	sa1100_probe_map.virt = (void __iomem *)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_ipaq()) {
+		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_jornada56x()) {
+		info[0].set_vpp = jornada56x_set_vpp;
+		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 bankwidth 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 ret;
+	}
+
+	return nr;
 }
 
-static void __exit sa1100_mtd_cleanup(void)
+static struct mtd_partition *parsed_parts;
+const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
+
+static void __init sa1100_locate_partitions(struct mtd_info *mtd)
 {
-	if (mymtd) {
-		del_mtd_partitions(mymtd);
-		map_destroy(mymtd);
-		if (parsed_parts)
-			kfree(parsed_parts);
+	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;
 	}
-	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);
+#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)
+{
+	sa1100_destroy_mtd(info, mymtd);
+	sa1100_destroy_partitions();
 }
 
 module_init(sa1100_mtd_init);
diff -uNrb linux-2.4.30/drivers/mtd/maps/sbc8240.c linux/drivers/mtd/maps/sbc8240.c
--- linux-2.4.30/drivers/mtd/maps/sbc8240.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/sbc8240.c	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,247 @@
+/*
+ * 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.4 2004/07/12 22:38:29 dwmw2 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 struct map_info sbc8240_map[2] = {
+	{
+		.name           = "sbc8240 Flash Bank #0",
+		.size           = WINDOW_SIZE0,
+		.bankwidth       = BUSWIDTH0,
+	},
+	{
+		.name           = "sbc8240 Flash Bank #1",
+		.size           = WINDOW_SIZE1,
+		.bankwidth       = BUSWIDTH1,
+	}
+};
+
+#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;
+		}
+		simple_map_init(&sbc8240_mtd[i]);
+
+		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 -uNrb linux-2.4.30/drivers/mtd/maps/sbc_gxx.c linux/drivers/mtd/maps/sbc_gxx.c
--- linux-2.4.30/drivers/mtd/maps/sbc_gxx.c	Fri Nov 19 12:46:58 2004
+++ linux/drivers/mtd/maps/sbc_gxx.c	Tue Sep 28 14:38:13 2004
@@ -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.30 2004/09/16 23:27:14 gleixner Exp $
 
 The SBC-MediaGX / SBC-GXx has up to 16 MiB of 
 Intel StrataFlash (28F320/28F640) in x8 mode.  
@@ -84,21 +84,21 @@
 // Globals
 
 static volatile int page_in_window = -1; // Current page in window.
-static unsigned long iomapadr;
+static void __iomem *iomapadr;
 static spinlock_t sbc_gxx_spin = SPIN_LOCK_UNLOCKED;
 
 /* 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: "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
@@ -114,32 +114,12 @@
 }
 
 
-static __u8 sbc_gxx_read8(struct map_info *map, unsigned long ofs)
+static map_word sbc_gxx_read8(struct map_info *map, unsigned long ofs)
 {
-	__u8 ret;
+	map_word ret;
 	spin_lock(&sbc_gxx_spin);
 	sbc_gxx_page(map, ofs);
-	ret = readb(iomapadr + (ofs & WINDOW_MASK));
-	spin_unlock(&sbc_gxx_spin);
-	return ret;
-}
-
-static __u16 sbc_gxx_read16(struct map_info *map, unsigned long ofs)
-{
-	__u16 ret;
-	spin_lock(&sbc_gxx_spin);
-	sbc_gxx_page(map, ofs);
-	ret = readw(iomapadr + (ofs & WINDOW_MASK));
-	spin_unlock(&sbc_gxx_spin);
-	return ret;
-}
-
-static __u32 sbc_gxx_read32(struct map_info *map, unsigned long ofs)
-{
-	__u32 ret;
-	spin_lock(&sbc_gxx_spin);
-	sbc_gxx_page(map, ofs);
-	ret = readl(iomapadr + (ofs & WINDOW_MASK));
+	ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK));
 	spin_unlock(&sbc_gxx_spin);
 	return ret;
 }
@@ -161,27 +141,11 @@
 	}
 }
 
-static void sbc_gxx_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	spin_lock(&sbc_gxx_spin);
-	sbc_gxx_page(map, adr);
-	writeb(d, iomapadr + (adr & WINDOW_MASK));
-	spin_unlock(&sbc_gxx_spin);
-}
-
-static void sbc_gxx_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	spin_lock(&sbc_gxx_spin);
-	sbc_gxx_page(map, adr);
-	writew(d, iomapadr + (adr & WINDOW_MASK));
-	spin_unlock(&sbc_gxx_spin);
-}
-
-static void sbc_gxx_write32(struct map_info *map, __u32 d, unsigned long adr)
+static void sbc_gxx_write8(struct map_info *map, map_word d, unsigned long adr)
 {
 	spin_lock(&sbc_gxx_spin);
 	sbc_gxx_page(map, adr);
-	writel(d, iomapadr + (adr & WINDOW_MASK));
+	writeb(d.x[0], iomapadr + (adr & WINDOW_MASK));
 	spin_unlock(&sbc_gxx_spin);
 }
 
@@ -203,19 +167,16 @@
 }
 
 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
+	.bankwidth = 1,
+	.read = sbc_gxx_read8,
+	.copy_from = sbc_gxx_copy_from,
+	.write = sbc_gxx_write8,
+	.copy_to = sbc_gxx_copy_to
 };
 
 /* MTD device for all of the flash. */
@@ -234,20 +195,21 @@
 
 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);
+  	iomapadr = (void __iomem *)ioremap(WINDOW_START, WINDOW_LENGTH);
 	if (!iomapadr) {
 		printk( KERN_ERR"%s: failed to ioremap memory region\n",
 			sbc_gxx_map.name );
 		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 +223,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 -uNrb linux-2.4.30/drivers/mtd/maps/sc520cdp.c linux/drivers/mtd/maps/sc520cdp.c
--- linux-2.4.30/drivers/mtd/maps/sc520cdp.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/sc520cdp.c	Tue Sep 28 14:38:13 2004
@@ -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.17 2004/09/16 23:27:14 gleixner 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,
+		.bankwidth = 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,
+		.bankwidth = 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,
+		.bankwidth = 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 = (void __iomem *)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 -uNrb linux-2.4.30/drivers/mtd/maps/scb2_flash.c linux/drivers/mtd/maps/scb2_flash.c
--- linux-2.4.30/drivers/mtd/maps/scb2_flash.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/scb2_flash.c	Tue Sep 28 14:38:13 2004
@@ -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.9 2004/09/16 23:27:14 gleixner 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
@@ -31,23 +31,24 @@
  *
  * The actual BIOS layout has been mostly reverse engineered.  Intel BIOS
  * updates for this board include 10 related (*.bio - &.bi9) binary files and
- * another seperate (*.bbo) binary file.  The 10 files are 64k of data + a
+ * another separate (*.bbo) binary file.  The 10 files are 64k of data + a
  * small header.  If the headers are stripped off, the 10 64k files can be
  * concatenated into a 640k image.  This is your BIOS image, proper.  The
- * seperate .bbo file also has a small header.  It is the 'Boot Block'
+ * separate .bbo file also has a small header.  It is the 'Boot Block'
  * recovery BIOS.  Once the header is stripped, no further prep is needed.
  * As best I can tell, the BIOS is arranged as such:
  * offset 0x00000 to 0x4ffff (320k):  unknown - SCSI BIOS, etc?
  * offset 0x50000 to 0xeffff (640k):  BIOS proper
  * offset 0xf0000 ty 0xfffff (64k):   Boot Block region
  *
- * Intel's BIOS update program flashes the BIOS and Boot Block in seperate
+ * Intel's BIOS update program flashes the BIOS and Boot Block in separate
  * steps.  Probably a wise thing to do.
  */
 
 #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,
+	.bankwidth =  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 = (void __iomem *)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);
@@ -264,21 +218,21 @@
 	pci_set_drvdata(dev, NULL);
 }
 
-static struct pci_device_id scb2_flash_pci_ids[] __devinitdata = {
+static struct pci_device_id scb2_flash_pci_ids[] = {
 	{
-	  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 -uNrb linux-2.4.30/drivers/mtd/maps/scx200_docflash.c linux/drivers/mtd/maps/scx200_docflash.c
--- linux-2.4.30/drivers/mtd/maps/scx200_docflash.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/scx200_docflash.c	Tue Sep 28 14:38:13 2004
@@ -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.7 2004/09/16 23:27:14 gleixner 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)
@@ -209,12 +173,15 @@
 
 	scx200_docflash_map.size = size;
 	if (width == 8)
-		scx200_docflash_map.buswidth = 1;
+		scx200_docflash_map.bankwidth = 1;
 	else
-		scx200_docflash_map.buswidth = 2;
+		scx200_docflash_map.bankwidth = 2;
+
+	simple_map_init(&scx200_docflash_map);
 
-	scx200_docflash_map.map_priv_1 = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size);
-	if (!scx200_docflash_map.map_priv_1) {
+	scx200_docflash_map.phys = docmem.start;
+	scx200_docflash_map.virt = (void __iomem *)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 -uNrb linux-2.4.30/drivers/mtd/maps/solutionengine.c linux/drivers/mtd/maps/solutionengine.c
--- linux-2.4.30/drivers/mtd/maps/solutionengine.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/solutionengine.c	Tue Sep 28 14:38:13 2004
@@ -1,5 +1,5 @@
 /*
- * $Id: solutionengine.c,v 1.4 2001/11/07 01:20:59 jsiegel Exp $
+ * $Id: solutionengine.c,v 1.14 2004/09/16 23:27:14 gleixner 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,
+	.bankwidth = 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,
+	.bankwidth = 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 = (void __iomem *)P2SEGADDR(0);
+	soleng_eprom_map.phys = 0x01000000;
+	soleng_eprom_map.virt = (void __iomem *)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 */
-#if CONFIG_MTD_SUPERH_RESERVE
-	if (nr_parts == 0) {
+	nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0);
+
+#ifdef CONFIG_MTD_SUPERH_RESERVE
+	if (nr_parts <= 0) {
 		printk(KERN_NOTICE "Using configured partition at 0x%08x.\n",
 		       CONFIG_MTD_SUPERH_RESERVE);
 		parsed_parts = superh_se_partitions;
diff -uNrb linux-2.4.30/drivers/mtd/maps/sun_uflash.c linux/drivers/mtd/maps/sun_uflash.c
--- linux-2.4.30/drivers/mtd/maps/sun_uflash.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/sun_uflash.c	Tue Sep 28 14:38:13 2004
@@ -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.10 2004/09/16 23:27:14 gleixner Exp $
  *
  * sun_uflash - Driver implementation for user-programmable flash
  * present on many Sun Microsystems SME boardsets.
@@ -12,7 +12,6 @@
 
 #include <linux/kernel.h>
 #include <linux/module.h>
-#include <linux/version.h>
 #include <linux/fs.h>
 #include <linux/errno.h>
 #include <linux/init.h>
@@ -48,60 +47,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,
+		.bankwidth =	UFLASH_BUSWIDTH,
 };
 
 int uflash_devinit(struct linux_ebus_device* edev)
@@ -145,20 +95,22 @@
 	if(0 != pdev->name && 0 < strlen(pdev->name)) {
 		pdev->map.name = pdev->name;
 	}
-
-	pdev->map.map_priv_1 = 
-		(unsigned long)ioremap_nocache(edev->resource[0].start, pdev->map.size);
-	if(0 == pdev->map.map_priv_1) {
+	pdev->map.phys = edev->resource[0].start;
+	pdev->map.virt = 
+		(void __iomem *)ioremap_nocache(edev->resource[0].start, pdev->map.size);
+	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 +118,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 +163,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 -uNrb linux-2.4.30/drivers/mtd/maps/tqm8xxl.c linux/drivers/mtd/maps/tqm8xxl.c
--- linux-2.4.30/drivers/mtd/maps/tqm8xxl.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/tqm8xxl.c	Tue Sep 28 14:38:13 2004
@@ -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.12 2004/09/16 23:27:14 gleixner 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]->bankwidth = 4;
+
+		simple_map_init(map_banks[idx]);
+
+		map_banks[idx]->virt = (void __iomem *)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 -uNrb linux-2.4.30/drivers/mtd/maps/ts5500_flash.c linux/drivers/mtd/maps/ts5500_flash.c
--- linux-2.4.30/drivers/mtd/maps/ts5500_flash.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/ts5500_flash.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,141 @@
+/*
+ * ts5500_flash.c -- MTD map driver for Technology Systems TS-5500 board
+ *
+ * Copyright (C) 2004 Sean Young <sean@mess.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
+ *
+ * Note:
+ * - In order for detection to work, jumper 3 must be set.
+ * - Drive A and B use a proprietary FTL from General Software which isn't 
+ *   supported as of yet so standard drives can't be mounted; you can create 
+ *   your own (e.g. jffs) file system.
+ * - If you have created your own jffs file system and the bios overwrites 
+ *   it during boot, try disabling Drive A: and B: in the boot order.
+ *
+ * $Id: ts5500_flash.c,v 1.1 2004/09/20 15:33:26 sean Exp $
+ */
+
+#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>
+
+#ifdef CONFIG_MTD_PARTITIONS
+#include <linux/mtd/partitions.h>
+#endif
+
+#define WINDOW_ADDR	0x09400000
+#define WINDOW_SIZE	0x00200000
+
+static struct map_info ts5500_map = {
+	.name = "TS-5500 Flash",
+	.size = WINDOW_SIZE,
+	.bankwidth = 1,
+	.phys = WINDOW_ADDR
+};
+
+#ifdef CONFIG_MTD_PARTITIONS
+static struct mtd_partition ts5500_partitions[] = {
+	{
+		.name = "Drive A",
+		.offset = 0,
+		.size = 0x0e0000
+	},
+	{
+		.name = "BIOS",
+		.offset = 0x0e0000,
+		.size = 0x020000,
+	},
+	{
+		.name = "Drive B",
+		.offset = 0x100000,
+		.size = 0x100000
+	}
+};
+
+#define NUM_PARTITIONS (sizeof(ts5500_partitions)/sizeof(struct mtd_partition))
+
+#endif
+
+static struct mtd_info *mymtd;
+
+static int __init init_ts5500_map(void)
+{
+	int rc = 0;
+
+	ts5500_map.virt = (void*)ioremap_nocache(ts5500_map.phys, ts5500_map.size);
+
+	if(!ts5500_map.virt) {
+		printk(KERN_ERR "Failed to ioremap_nocache\n");
+		rc = -EIO;
+		goto err_out_ioremap;
+	}
+
+	simple_map_init(&ts5500_map);
+
+	mymtd = do_map_probe("jedec_probe", &ts5500_map);
+	if(!mymtd)
+		mymtd = do_map_probe("map_rom", &ts5500_map);
+
+	if(!mymtd) {
+		rc = -ENXIO;
+		goto err_out_map;
+	}
+
+	mymtd->owner = THIS_MODULE;
+#ifdef CONFIG_MTD_PARTITIONS
+	add_mtd_partitions(mymtd, ts5500_partitions, NUM_PARTITIONS);
+#else	
+	add_mtd_device(mymtd);
+#endif
+
+	return 0;
+
+err_out_map:
+	map_destroy(mymtd);
+err_out_ioremap:
+	iounmap((void *)ts5500_map.virt);
+
+	return rc;
+}
+
+static void __exit cleanup_ts5500_map(void)
+{
+	if (mymtd) {
+#ifdef CONFIG_MTD_PARTITIONS
+		del_mtd_partitions(mymtd);
+#else
+		del_mtd_device(mymtd);
+#endif
+		map_destroy(mymtd);
+	}
+
+	if (ts5500_map.virt) {
+		iounmap((void *)ts5500_map.virt);
+		ts5500_map.virt = 0;
+	}
+}
+
+module_init(init_ts5500_map);
+module_exit(cleanup_ts5500_map);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Sean Young <sean@mess.org>");
+MODULE_DESCRIPTION("MTD map driver for Techology Systems TS-5500 board");
+
diff -uNrb linux-2.4.30/drivers/mtd/maps/tsunami_flash.c linux/drivers/mtd/maps/tsunami_flash.c
--- linux-2.4.30/drivers/mtd/maps/tsunami_flash.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/tsunami_flash.c	Fri Sep 10 15:15:04 2004
@@ -2,25 +2,29 @@
  * 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.9 2004/07/14 09:52:55 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
 #define FLASH_DISABLE_BYTE 0x00
 
 #define MAX_TIG_FLASH_SIZE (12*1024*1024)
-static inline  __u8 tsunami_flash_read8(struct map_info *map, unsigned long offset)
+static inline map_word tsunami_flash_read8(struct map_info *map, unsigned long offset)
 {
-	return tsunami_tig_readb(offset);
+	map_word val;
+	val.x[0] = tsunami_tig_readb(offset);
+	return val;
 }
 
-static void tsunami_flash_write8(struct map_info *map, __u8 value, unsigned long offset)
+static void tsunami_flash_write8(struct map_info *map, map_word value, unsigned long offset)
 {
-	tsunami_tig_writeb(value, offset);
+	tsunami_tig_writeb(value.x[0], offset);
 }
 
 static void tsunami_flash_copy_from(
@@ -58,18 +62,12 @@
 static struct map_info tsunami_flash_map = {
 	.name = "flash chip on the Tsunami TIG bus",
 	.size = MAX_TIG_FLASH_SIZE,
-	.buswidth = 1,
-	.read8 = tsunami_flash_read8,
-	.read16 = 0,
-	.read32 = 0, 
+	.phys = NO_XIP;
+	.bankwidth = 1,
+	.read = tsunami_flash_read8,
 	.copy_from = tsunami_flash_copy_from,
-	.write8 = tsunami_flash_write8,
-	.write16 = 0,
-	.write32 = 0,
+	.write = tsunami_flash_write8,
 	.copy_to = tsunami_flash_copy_to,
-	.set_vpp = 0,
-	.map_priv_1 = 0,
-
 };
 
 static struct mtd_info *tsunami_flash_mtd;
@@ -88,7 +86,7 @@
 
 static int __init init_tsunami_flash(void)
 {
-	static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", 0 };
+	static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", NULL };
 	char **type;
 
 	tsunami_tig_writeb(FLASH_ENABLE_BYTE, FLASH_ENABLE_PORT);
@@ -99,7 +97,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 -uNrb linux-2.4.30/drivers/mtd/maps/uclinux.c linux/drivers/mtd/maps/uclinux.c
--- linux-2.4.30/drivers/mtd/maps/uclinux.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/uclinux.c	Tue Sep 28 14:38:13 2004
@@ -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.8 2004/09/16 23:27:14 gleixner Exp $
  */
 
 /****************************************************************************/
@@ -17,6 +17,7 @@
 #include <linux/kernel.h>
 #include <linux/fs.h>
 #include <linux/major.h>
+#include <linux/root_dev.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/map.h>
 #include <linux/mtd/partitions.h>
@@ -24,58 +25,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 +37,7 @@
 /****************************************************************************/
 
 struct mtd_partition uclinux_romfs[] = {
-	{ name: "ROMfs", offset: 0 }
+	{ .name = "ROMfs" }
 };
 
 #define	NUM_PARTITIONS	(sizeof(uclinux_romfs) / sizeof(uclinux_romfs[0]))
@@ -94,7 +48,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 +62,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;
+	mapp->bankwidth = 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 = (void __iomem *)
+		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 +111,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 -uNrb linux-2.4.30/drivers/mtd/maps/vmax301.c linux/drivers/mtd/maps/vmax301.c
--- linux-2.4.30/drivers/mtd/maps/vmax301.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/maps/vmax301.c	Fri Sep 10 15:15:04 2004
@@ -1,4 +1,4 @@
-// $Id: vmax301.c,v 1.24 2001/10/02 15:05:14 dwmw2 Exp $
+// $Id: vmax301.c,v 1.30 2004/07/12 22:38:29 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
@@ -53,32 +54,12 @@
 		__vmax301_page(map, page);
 }
 
-static __u8 vmax301_read8(struct map_info *map, unsigned long ofs)
+static map_word vmax301_read8(struct map_info *map, unsigned long ofs)
 {
-	__u8 ret;
+	map_word ret;
 	spin_lock(&vmax301_spin);
 	vmax301_page(map, ofs);
-	ret = readb(map->map_priv_2 + (ofs & WINDOW_MASK));
-	spin_unlock(&vmax301_spin);
-	return ret;
-}
-
-static __u16 vmax301_read16(struct map_info *map, unsigned long ofs)
-{
-	__u16 ret;
-	spin_lock(&vmax301_spin);
-	vmax301_page(map, ofs);
-	ret = readw(map->map_priv_2 + (ofs & WINDOW_MASK));
-	spin_unlock(&vmax301_spin);
-	return ret;
-}
-
-static __u32 vmax301_read32(struct map_info *map, unsigned long ofs)
-{
-	__u32 ret;
-	spin_lock(&vmax301_spin);
-	vmax301_page(map, ofs);
-	ret =  readl(map->map_priv_2 + (ofs & WINDOW_MASK));
+	ret.x[0] = readb(map->map_priv_2 + (ofs & WINDOW_MASK));
 	spin_unlock(&vmax301_spin);
 	return ret;
 }
@@ -99,27 +80,11 @@
 	}
 }
 
-static void vmax301_write8(struct map_info *map, __u8 d, unsigned long adr)
-{
-	spin_lock(&vmax301_spin);
-	vmax301_page(map, adr);
-	writeb(d, map->map_priv_2 + (adr & WINDOW_MASK));
-	spin_unlock(&vmax301_spin);
-}
-
-static void vmax301_write16(struct map_info *map, __u16 d, unsigned long adr)
-{
-	spin_lock(&vmax301_spin);
-	vmax301_page(map, adr);
-	writew(d, map->map_priv_2 + (adr & WINDOW_MASK));
-	spin_unlock(&vmax301_spin);
-}
-
-static void vmax301_write32(struct map_info *map, __u32 d, unsigned long adr)
+static void vmax301_write8(struct map_info *map, map_word d, unsigned long adr)
 {
 	spin_lock(&vmax301_spin);
 	vmax301_page(map, adr);
-	writel(d, map->map_priv_2 + (adr & WINDOW_MASK));
+	writeb(d.x[0], map->map_priv_2 + (adr & WINDOW_MASK));
 	spin_unlock(&vmax301_spin);
 }
 
@@ -142,34 +107,28 @@
 
 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,
+		.bankwidth = 1,
+		.read = vmax301_read8,
+		.copy_from = vmax301_copy_from,
+		.write = vmax301_write8,
+		.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,
+		.bankwidth = 1,
+		.read = vmax301_read8,
+		.copy_from = vmax301_copy_from,
+		.write = vmax301_write8,
+		.copy_to = vmax301_copy_to,
+		.map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH),
+		.map_priv_2 = 0xFFFFFFFF
 	}
 };
 
@@ -206,8 +165,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 +177,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 -uNrb linux-2.4.30/drivers/mtd/maps/wr_sbc82xx_flash.c linux/drivers/mtd/maps/wr_sbc82xx_flash.c
--- linux-2.4.30/drivers/mtd/maps/wr_sbc82xx_flash.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/maps/wr_sbc82xx_flash.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,181 @@
+/*
+ * $Id: wr_sbc82xx_flash.c,v 1.6 2004/09/16 23:27:14 gleixner Exp $
+ *
+ * Map for flash chips on Wind River PowerQUICC II SBC82xx board.
+ *
+ * Copyright (C) 2004 Red Hat, Inc.
+ *
+ * Author: David Woodhouse <dwmw2@infradead.org>
+ *
+ */
+
+#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>
+#include <linux/mtd/partitions.h>
+
+#include <asm/immap_cpm2.h>
+
+static struct mtd_info *sbcmtd[3];
+static struct mtd_partition *sbcmtd_parts[3];
+
+struct map_info sbc82xx_flash_map[3] = {
+	{.name = "Boot flash"},
+	{.name = "Alternate boot flash"},
+	{.name = "User flash"}
+};
+
+static struct mtd_partition smallflash_parts[] = {
+	{
+		.name =		"space",
+		.size =		0x100000,
+		.offset =	0,
+	}, {
+		.name =		"bootloader",
+		.size =		MTDPART_SIZ_FULL,
+		.offset =	MTDPART_OFS_APPEND,
+	}
+};
+
+static struct mtd_partition bigflash_parts[] = {
+	{
+		.name =		"bootloader",
+		.size =		0x00100000,
+		.offset =	0,
+	}, {
+		.name =		"file system",
+		.size =		0x01f00000,
+		.offset =	MTDPART_OFS_APPEND,
+	}, {
+		.name =		"boot config",
+		.size =		0x00100000,
+		.offset =	MTDPART_OFS_APPEND,
+	}, {
+		.name =		"space",
+		.size =		0x01f00000,
+		.offset =	MTDPART_OFS_APPEND,
+	}
+};
+
+static const char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL};
+
+#define init_sbc82xx_one_flash(map, br, or)			\
+do {								\
+	(map).phys = (br & 1) ? (br & 0xffff8000) : 0;		\
+	(map).size = (br & 1) ? (~(or & 0xffff8000) + 1) : 0;	\
+	switch (br & 0x00001800) {				\
+	case 0x00000000:					\
+	case 0x00000800:	(map).bankwidth = 1;	break;	\
+	case 0x00001000:	(map).bankwidth = 2;	break;	\
+	case 0x00001800:	(map).bankwidth = 4;	break;	\
+	}							\
+} while (0);
+
+int __init init_sbc82xx_flash(void)
+{
+	volatile memctl_cpm2_t *mc = &cpm2_immr->im_memctl;
+	int bigflash;
+	int i;
+
+#ifdef CONFIG_SBC8560
+	mc = ioremap(0xff700000 + 0x5000, sizeof(memctl_cpm2_t));
+#else
+	mc = &cpm2_immr->im_memctl;
+#endif
+
+	bigflash = 1;
+	if ((mc->memc_br0 & 0x00001800) == 0x00001800)
+		bigflash = 0;
+
+	init_sbc82xx_one_flash(sbc82xx_flash_map[0], mc->memc_br0, mc->memc_or0);
+	init_sbc82xx_one_flash(sbc82xx_flash_map[1], mc->memc_br6, mc->memc_or6);
+	init_sbc82xx_one_flash(sbc82xx_flash_map[2], mc->memc_br1, mc->memc_or1);
+
+#ifdef CONFIG_SBC8560
+	iounmap((void *) mc);
+#endif
+
+	for (i=0; i<3; i++) {
+		int8_t flashcs[3] = { 0, 6, 1 };
+		int nr_parts;
+
+		printk(KERN_NOTICE "PowerQUICC II %s (%ld MiB on CS%d",
+		       sbc82xx_flash_map[i].name,
+		       (sbc82xx_flash_map[i].size >> 20),
+		       flashcs[i]);
+		if (!sbc82xx_flash_map[i].phys) {
+			/* We know it can't be at zero. */
+			printk("): disabled by bootloader.\n");
+			continue;
+		}
+		printk(" at %08lx)\n",  sbc82xx_flash_map[i].phys);
+
+		sbc82xx_flash_map[i].virt = (void __iomem *)ioremap(sbc82xx_flash_map[i].phys, sbc82xx_flash_map[i].size);
+
+		if (!sbc82xx_flash_map[i].virt) {
+			printk("Failed to ioremap\n");
+			continue;
+		}
+
+		simple_map_init(&sbc82xx_flash_map[i]);
+
+		sbcmtd[i] = do_map_probe("cfi_probe", &sbc82xx_flash_map[i]);
+
+		if (!sbcmtd[i])
+			continue;
+
+		sbcmtd[i]->owner = THIS_MODULE;
+
+		nr_parts = parse_mtd_partitions(sbcmtd[i], part_probes,
+						&sbcmtd_parts[i], 0);
+		if (nr_parts > 0) {
+			add_mtd_partitions (sbcmtd[i], sbcmtd_parts[i], nr_parts);
+			continue;
+		}
+
+		/* No partitioning detected. Use default */
+		if (i == 2) {
+			add_mtd_device(sbcmtd[i]);
+		} else if (i == bigflash) {
+			add_mtd_partitions (sbcmtd[i], bigflash_parts, ARRAY_SIZE(bigflash_parts));
+		} else {
+			add_mtd_partitions (sbcmtd[i], smallflash_parts, ARRAY_SIZE(smallflash_parts));
+		}
+	}
+	return 0;
+}
+
+static void __exit cleanup_sbc82xx_flash(void)
+{
+	int i;
+
+	for (i=0; i<3; i++) {
+		if (!sbcmtd[i])
+			continue;
+
+		if (i<2 || sbcmtd_parts[i])
+			del_mtd_partitions(sbcmtd[i]);
+		else
+			del_mtd_device(sbcmtd[i]);
+			
+		kfree(sbcmtd_parts[i]);
+		map_destroy(sbcmtd[i]);
+		
+		iounmap((void *)sbc82xx_flash_map[i].virt);
+		sbc82xx_flash_map[i].virt = 0;
+	}
+}
+
+module_init(init_sbc82xx_flash);
+module_exit(cleanup_sbc82xx_flash);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("Flash map driver for WindRiver PowerQUICC II");
diff -uNrb linux-2.4.30/drivers/mtd/mtd_blkdevs-24.c linux/drivers/mtd/mtd_blkdevs-24.c
--- linux-2.4.30/drivers/mtd/mtd_blkdevs-24.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/mtd_blkdevs-24.c	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,692 @@
+/*
+ * $Id: mtd_blkdevs-24.c,v 1.16 2004/08/11 15:29:24 dmarlin 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. */
+	spin_lock_irq(&current->sigmask_lock);
+	sigfillset(&current->blocked);
+	recalc_sigpending();
+	spin_unlock_irq(&current->sigmask_lock);
+
+	daemonize("%sd", tr->name);
+
+	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 -uNrb linux-2.4.30/drivers/mtd/mtd_blkdevs.c linux/drivers/mtd/mtd_blkdevs.c
--- linux-2.4.30/drivers/mtd/mtd_blkdevs.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/mtd_blkdevs.c	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,478 @@
+/*
+ * $Id: mtd_blkdevs.c,v 1.23 2004/08/19 01:54:36 tpoynor 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 | PF_NOFREEZE;
+
+	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 __user *)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_KERNEL);
+	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 -uNrb linux-2.4.30/drivers/mtd/mtdblock.c linux/drivers/mtd/mtdblock.c
--- linux-2.4.30/drivers/mtd/mtdblock.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/mtdblock.c	Fri Sep 10 15:15:04 2004
@@ -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");
+	int dev = mbd->devnum;
+	struct mtdblk_dev *mtdblk = mtdblks[dev];
 
-	if (inode == NULL)
-		release_return(-ENODEV);
-
-	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);
-}  
-
-
-/* 
- * 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)
-{
-	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;
-		}
-
-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);
-		}
-	}
-
-	up(&thread_sem);
 	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 int mtdblock_flush(struct mtd_blktrans_dev *dev)
 {
-	/* Don't do anything, except wake the thread if necessary */
-	wake_up(&thr_wq);
-}
+	struct mtdblk_dev *mtdblk = mtdblks[dev->devnum];
 
-
-static int mtdblock_ioctl(struct inode * inode, struct file * file,
-		      unsigned int cmd, unsigned long arg)
-{
-	struct mtdblk_dev *mtdblk;
-
-	mtdblk = mtdblks[MINOR(inode->i_rdev)];
-
-#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;
-
-	default:
-		return -EINVAL;
-	}
 }
 
-#if LINUX_VERSION_CODE < 0x20326
-static struct file_operations mtd_fops =
+static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 {
-	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
-};
-#endif
+	struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
 
-#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)
+	if (!dev)
                 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);
-}
-
-static void mtd_notify_remove(struct mtd_info* mtd)
-{
-        if (!mtd || mtd->type == MTD_ABSENT)
-                return;
+	memset(dev, 0, sizeof(*dev));
 
-        devfs_unregister(devfs_rw_handle[mtd->index]);
-}
-#endif
+	dev->mtd = mtd;
+	dev->devnum = mtd->index;
+	dev->blksize = 512;
+	dev->size = mtd->size >> 9;
+	dev->tr = tr;
+
+	if (!(mtd->flags & MTD_WRITEABLE))
+		dev->readonly = 1;
+
+	add_mtd_blktrans_dev(dev);
+}
+
+static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev)
+{
+	del_mtd_blktrans_dev(dev);
+	kfree(dev);
+}
+
+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 -uNrb linux-2.4.30/drivers/mtd/mtdblock_ro.c linux/drivers/mtd/mtdblock_ro.c
--- linux-2.4.30/drivers/mtd/mtdblock_ro.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/mtdblock_ro.c	Fri Sep 10 15:15:04 2004
@@ -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>
-
-#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];
+#include <linux/mtd/blktrans.h>
 
-
-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;
-
-   	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);
-
-	put_mtd_device(mtd);
-
-	DEBUG(1, "ok\n");
+	size_t retlen;
 
-	BLK_DEC_USE_COUNT;
-	release_return(0);
+	if (dev->mtd->write(dev->mtd, (block * 512), 512, &retlen, buf))
+		return 1;
+	return 0;
 }  
 
-
-static void mtdblock_request(RQFUNC_ARG)
+static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd)
 {
-   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;
+	struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL);
 
-	 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;
+	if (!dev)
+		return;
 	 
-	 case WRITE:
+	memset(dev, 0, sizeof(*dev));
 
-	 /* printk("mtdblock_request WRITE sector=%d(%d)\n",current_request->sector,
-		current_request->current_nr_sectors);
-	 */
+	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;
 
-	 // 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 -uNrb linux-2.4.30/drivers/mtd/mtdchar.c linux/drivers/mtd/mtdchar.c
--- linux-2.4.30/drivers/mtd/mtdchar.c	Fri Nov 19 12:46:58 2004
+++ linux/drivers/mtd/mtdchar.c	Fri Nov 19 13:44:03 2004
@@ -1,8 +1,7 @@
 /*
- * $Id: mtdchar.c,v 1.49 2003/01/24 12:02:58 dwmw2 Exp $
+ * $Id: mtdchar.c,v 1.64 2004/08/09 13:59:46 dwmw2 Exp $
  *
  * Character-device access to raw MTD devices.
- * Pure 2.4 version - compatibility cruft removed to mtdchar-compat.c
  *
  */
 
@@ -10,21 +9,54 @@
 #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>
-static void mtd_notify_add(struct mtd_info* mtd);
-static void mtd_notify_remove(struct mtd_info* mtd);
+
+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);
+	devfs_remove("mtd/%dro", mtd->index);
+}
 
 static struct mtd_notifier notifier = {
-	add:	mtd_notify_add,
-	remove:	mtd_notify_remove,
+	.add	= mtd_notify_add,
+	.remove	= mtd_notify_remove,
 };
 
-static devfs_handle_t devfs_dir_handle;
-static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES];
-static devfs_handle_t devfs_ro_handle[MAX_MTD_DEVICES];
+static inline void mtdchar_devfs_init(void)
+{
+	devfs_mk_dir("mtd");
+	register_mtd_user(&notifier);
+}
+
+static inline void mtdchar_devfs_exit(void)
+{
+	unregister_mtd_user(&notifier);
+	devfs_remove("mtd");
+}
+#else /* !DEVFS */
+#define mtdchar_devfs_init() do { } while(0)
+#define mtdchar_devfs_exit() do { } while(0)
 #endif
 
 static loff_t mtd_lseek (struct file *file, loff_t offset, int orig)
@@ -60,7 +92,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;
 
@@ -117,7 +149,7 @@
 */
 #define MAX_KMALLOC_SIZE 0x20000
 
-static ssize_t mtd_read(struct file *file, char *buf, size_t count,loff_t *ppos)
+static ssize_t mtd_read(struct file *file, char __user *buf, size_t count,loff_t *ppos)
 {
 	struct mtd_info *mtd = (struct mtd_info *)file->private_data;
 	size_t retlen=0;
@@ -176,7 +208,7 @@
 	return total_retlen;
 } /* mtd_read */
 
-static ssize_t mtd_write(struct file *file, const char *buf, size_t count,loff_t *ppos)
+static ssize_t mtd_write(struct file *file, const char __user *buf, size_t count,loff_t *ppos)
 {
 	struct mtd_info *mtd = (struct mtd_info *)file->private_data;
 	char *kbuf;
@@ -238,7 +270,7 @@
     IOCTL calls for getting device parameters.
 
 ======================================================================*/
-static void mtd_erase_callback (struct erase_info *instr)
+static void mtdchar_erase_callback (struct erase_info *instr)
 {
 	wake_up((wait_queue_head_t *)instr->priv);
 }
@@ -247,6 +279,7 @@
 		     u_int cmd, u_long arg)
 {
 	struct mtd_info *mtd = (struct mtd_info *)file->private_data;
+	void __user *argp = (void __user *)arg;
 	int ret = 0;
 	u_long size;
 	
@@ -254,17 +287,17 @@
 
 	size = (cmd & IOCSIZE_MASK) >> IOCSIZE_SHIFT;
 	if (cmd & IOC_IN) {
-		ret = verify_area(VERIFY_READ, (char *)arg, size);
+		ret = verify_area(VERIFY_READ, argp, size);
 		if (ret) return ret;
 	}
 	if (cmd & IOC_OUT) {
-		ret = verify_area(VERIFY_WRITE, (char *)arg, size);
+		ret = verify_area(VERIFY_WRITE, argp, size);
 		if (ret) return ret;
 	}
 	
 	switch (cmd) {
 	case MEMGETREGIONCOUNT:
-		if (copy_to_user((int *) arg, &(mtd->numeraseregions), sizeof(int)))
+		if (copy_to_user(argp, &(mtd->numeraseregions), sizeof(int)))
 			return -EFAULT;
 		break;
 
@@ -272,24 +305,19 @@
 	{
 		struct region_info_user ur;
 
-		if (copy_from_user(	&ur, 
-					(struct region_info_user *)arg, 
-					sizeof(struct region_info_user))) {
+		if (copy_from_user(&ur, argp, sizeof(struct region_info_user)))
 			return -EFAULT;
-		}
 
 		if (ur.regionindex >= mtd->numeraseregions)
 			return -EINVAL;
-		if (copy_to_user((struct mtd_erase_region_info *) arg, 
-				&(mtd->eraseregions[ur.regionindex]),
+		if (copy_to_user(argp, &(mtd->eraseregions[ur.regionindex]),
 				sizeof(struct mtd_erase_region_info)))
 			return -EFAULT;
 		break;
 	}
 
 	case MEMGETINFO:
-		if (copy_to_user((struct mtd_info *)arg, mtd,
-				 sizeof(struct mtd_info_user)))
+		if (copy_to_user(argp, mtd, sizeof(struct mtd_info_user)))
 			return -EFAULT;
 		break;
 
@@ -310,13 +338,13 @@
 			init_waitqueue_head(&waitq);
 
 			memset (erase,0,sizeof(struct erase_info));
-			if (copy_from_user(&erase->addr, (u_long *)arg,
-					   2 * sizeof(u_long))) {
+			if (copy_from_user(&erase->addr, argp,
+				    sizeof(struct erase_info_user))) {
 				kfree(erase);
 				return -EFAULT;
 			}
 			erase->mtd = mtd;
-			erase->callback = mtd_erase_callback;
+			erase->callback = mtdchar_erase_callback;
 			erase->priv = (unsigned long)&waitq;
 			
 			/*
@@ -354,7 +382,7 @@
 		if(!(file->f_mode & 2))
 			return -EPERM;
 
-		if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
+		if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
 			return -EFAULT;
 		
 		if (buf.length > 0x4096)
@@ -363,7 +391,7 @@
 		if (!mtd->write_oob)
 			ret = -EOPNOTSUPP;
 		else
-			ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length);
+			ret = verify_area(VERIFY_READ, buf.ptr, buf.length);
 
 		if (ret)
 			return ret;
@@ -379,7 +407,7 @@
 
 		ret = (mtd->write_oob)(mtd, buf.start, buf.length, &retlen, databuf);
 
-		if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
+		if (copy_to_user(argp + sizeof(uint32_t), &retlen, sizeof(uint32_t)))
 			ret = -EFAULT;
 
 		kfree(databuf);
@@ -393,7 +421,7 @@
 		void *databuf;
 		ssize_t retlen;
 
-		if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
+		if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf)))
 			return -EFAULT;
 		
 		if (buf.length > 0x4096)
@@ -402,7 +430,7 @@
 		if (!mtd->read_oob)
 			ret = -EOPNOTSUPP;
 		else
-			ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length);
+			ret = verify_area(VERIFY_WRITE, buf.ptr, buf.length);
 
 		if (ret)
 			return ret;
@@ -413,7 +441,7 @@
 		
 		ret = (mtd->read_oob)(mtd, buf.start, buf.length, &retlen, databuf);
 
-		if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t)))
+		if (put_user(retlen, (uint32_t __user *)argp))
 			ret = -EFAULT;
 		else if (retlen && copy_to_user(buf.ptr, databuf, retlen))
 			ret = -EFAULT;
@@ -424,109 +452,73 @@
 
 	case MEMLOCK:
 	{
-		unsigned long adrs[2];
+		struct erase_info_user info;
 
-		if (copy_from_user(adrs ,(void *)arg, 2* sizeof(unsigned long)))
+		if (copy_from_user(&info, argp, sizeof(info)))
 			return -EFAULT;
 
 		if (!mtd->lock)
 			ret = -EOPNOTSUPP;
 		else
-			ret = mtd->lock(mtd, adrs[0], adrs[1]);
+			ret = mtd->lock(mtd, info.start, info.length);
 		break;
 	}
 
 	case MEMUNLOCK:
 	{
-		unsigned long adrs[2];
+		struct erase_info_user info;
 
-		if (copy_from_user(adrs, (void *)arg, 2* sizeof(unsigned long)))
+		if (copy_from_user(&info, argp, sizeof(info)))
 			return -EFAULT;
 
 		if (!mtd->unlock)
 			ret = -EOPNOTSUPP;
 		else
-			ret = mtd->unlock(mtd, adrs[0], adrs[1]);
+			ret = mtd->unlock(mtd, info.start, info.length);
 		break;
 	}
 
-	case 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, argp, sizeof(struct nand_oobinfo)))
 			return -EFAULT;
+		break;
+	}
 		
-		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);
+	case MEMGETOOBSEL:
+	{
+		if (copy_to_user(argp, &(mtd->oobinfo), sizeof(struct nand_oobinfo)))
 			return -EFAULT;
+		break;
 		}
 
-		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;
+	case MEMGETBADBLOCK:
+	{
+		loff_t offs;
 
-		kfree(databuf);
+		if (copy_from_user(&offs, argp, sizeof(loff_t)))
+			return -EFAULT;
+		if (!mtd->block_isbad)
+			ret = -EOPNOTSUPP;
+		else
+			return mtd->block_isbad(mtd, offs);
 		break;
-
 	}
 
-	case MEMREADDATA:
+	case MEMSETBADBLOCK:
 	{
-		struct mtd_oob_buf buf;
-		void *databuf;
-		ssize_t retlen = 0;
+		loff_t offs;
 
-		if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf)))
+		if (copy_from_user(&offs, argp, sizeof(loff_t)))
 			return -EFAULT;
-		
-		if (buf.length > 0x4096)
-			return -EINVAL;
-
-		if (!mtd->read_ecc)
+		if (!mtd->block_markbad)
 			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);
+			return mtd->block_markbad(mtd, offs);
 		break;
 	}
 
-		
 	default:
-		DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO);
 		ret = -ENOTTY;
 	}
 
@@ -534,84 +526,31 @@
 } /* 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
-/* Notification that a new device has been added. Create the devfs entry for
- * it. */
-
-static void mtd_notify_add(struct mtd_info* mtd)
-{
-	char name[8];
-
-	if (!mtd)
-		return;
-
-	sprintf(name, "%d", mtd->index);
-	devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
-			DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2,
-			S_IFCHR | S_IRUGO | S_IWUGO,
-			&mtd_fops, NULL);
-
-	sprintf(name, "%dro", mtd->index);
-	devfs_ro_handle[mtd->index] = devfs_register(devfs_dir_handle, name,
-			DEVFS_FL_DEFAULT, MTD_CHAR_MAJOR, mtd->index*2+1,
-			S_IFCHR | S_IRUGO,
-			&mtd_fops, NULL);
-}
-
-static void mtd_notify_remove(struct mtd_info* mtd)
-{
-	if (!mtd)
-		return;
-
-	devfs_unregister(devfs_rw_handle[mtd->index]);
-	devfs_unregister(devfs_ro_handle[mtd->index]);
-}
-#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;
 	}
 
-	devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL);
-
-	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
-
+	mtdchar_devfs_init();
 	return 0;
 }
 
 static void __exit cleanup_mtdchar(void)
 {
-#ifdef CONFIG_DEVFS_FS
-	unregister_mtd_user(&notifier);
-	devfs_unregister(devfs_dir_handle);
-	devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
-#else
+	mtdchar_devfs_exit();
 	unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
-#endif
 }
 
 module_init(init_mtdchar);
diff -uNrb linux-2.4.30/drivers/mtd/mtdconcat.c linux/drivers/mtd/mtdconcat.c
--- linux-2.4.30/drivers/mtd/mtdconcat.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/mtdconcat.c	Fri Sep 10 15:15:04 2004
@@ -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.9 2004/06/30 15:17:41 dbrown Exp $
  */
 
 #include <linux/module.h>
@@ -35,21 +37,20 @@
 #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;
@@ -57,43 +58,43 @@
 
 	*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)
-		{
+		if (from >= subdev->size) {
+			/* Not destined for this subdev */
 			size  = 0;
 			from -= subdev->size;
+			continue;
 		}
-		else
-		{
 			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)
+		if (err)
 				break;
 
 			*retlen += retsize;
 			len -= size;
-			if(len == 0)
+		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)
+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;
@@ -104,18 +105,15 @@
 
 	*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)
-		{
+		if (to >= subdev->size) {
 			size  = 0;
 			to -= subdev->size;
+			continue;
 		}
-		else
-		{
 			if (to + len > subdev->size)
 				size = subdev->size - to;
 			else
@@ -126,25 +124,232 @@
 			else
 				err = subdev->write(subdev, to, size, &retsize, buf);
 
-			if(err)
+		if (err)
 				break;
 
 			*retlen += retsize;
 			len -= size;
-			if(len == 0)
+		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;
+	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;
+
+		if (subdev->read_ecc)
+			err = subdev->read_ecc(subdev, from, 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;
+			/* 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_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;
+	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_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
+			/* Entire transaction goes into this subdev */
+			size = len;
+
+		if (subdev->read_oob)
+			err = subdev->read_oob(subdev, from, size,
+					       &retsize, buf);
+		else
+			err = -EINVAL;
+
+		if (err)
+			break;
+
+		*retlen += retsize;
+		len -= size;
+		if (len == 0)
+			break;
+
+		err = -EINVAL;
+		buf += size;
+		from = 0;
 	}
 	return err;
 }
 
-static void concat_erase_callback (struct erase_info *instr)
+static int
+concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len,
+		 size_t * retlen, const u_char * buf)
 {
-	wake_up((wait_queue_head_t *)instr->priv);
+	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)
+{
+	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,21 +386,21 @@
 	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;
 	int i, err;
-	u_int32_t length;
+	u_int32_t length, offset = 0;
 	struct erase_info *erase;
 
 	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,28 @@
 		 * 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;
 	}
 
+	instr->fail_addr = 0xffffffff;
+
 	/* make a local copy of instr to avoid modifying the caller's struct */
-	erase = kmalloc(sizeof(struct erase_info),GFP_KERNEL);
+	erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL);
 
 	if (!erase)
 		return -ENOMEM;
@@ -258,39 +465,44 @@
 	 * 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
+			offset += 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 */
-	{
+	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();
+			if (erase->fail_addr != 0xffffffff)
+				instr->fail_addr = erase->fail_addr + offset;
 			break;
 		}
 		/*
@@ -302,18 +514,19 @@
 		 * current subdevice, i.e. at offset zero.
 		 */
 		erase->addr = 0;
+		offset += subdev->size;
 	}
+	instr->state = erase->state;
 	kfree(erase);
 	if (err)
 		return err;
 
-	instr->state = MTD_ERASE_DONE;
 	if (instr->callback)
 		instr->callback(instr);
 	return 0;
 }
 
-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;
@@ -321,18 +534,15 @@
 	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)
-		{
+		if (ofs >= subdev->size) {
 			size  = 0;
 			ofs -= subdev->size;
+			continue;
 		}
-		else
-		{
 			if (ofs + len > subdev->size)
 				size = subdev->size - ofs;
 			else
@@ -340,21 +550,21 @@
 
 			err = subdev->lock(subdev, ofs, size);
 
-			if(err)
+		if (err)
 				break;
 
 			len -= size;
-			if(len == 0)
+		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;
@@ -362,18 +572,15 @@
 	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)
-		{
+		if (ofs >= subdev->size) {
 			size  = 0;
 			ofs -= subdev->size;
+			continue;
 		}
-		else
-		{
 			if (ofs + len > subdev->size)
 				size = subdev->size - ofs;
 			else
@@ -381,17 +588,17 @@
 
 			err = subdev->unlock(subdev, ofs, size);
 
-			if(err)
+		if (err)
 				break;
 
 			len -= size;
-			if(len == 0)
+		if (len == 0)
 				break;
 
 			err = -EINVAL;
 			ofs = 0;
 		}
-	}
+
 	return err;
 }
 
@@ -400,8 +607,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 +618,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 +631,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 +643,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 */
+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   */
-{
+				   char *name)
+{				/* name for the new device   */
 	int i;
 	size_t size;
 	struct mtd_concat *concat;
@@ -451,21 +654,21 @@
 	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",
+	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
@@ -479,39 +682,53 @@
 	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)
-		{
+	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 ||
+		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.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];
@@ -535,7 +752,6 @@
 	concat->mtd.suspend = concat_suspend;
 	concat->mtd.resume  = concat_resume;
 
-
 	/*
 	 * Combine the erase block size info of the subdevices:
 	 *
@@ -544,44 +760,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,12 +806,13 @@
 
 		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"
+			printk
+			    ("memory allocation error while creating erase region list"
 			        " for device \"%s\"\n", name);
 			return NULL;
 		}
@@ -606,41 +823,48 @@
 		 */
 		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->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)
-					{
+				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;
+						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;
 				}
 			}
 		}
@@ -660,15 +884,13 @@
 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>");
diff -uNrb linux-2.4.30/drivers/mtd/mtdcore.c linux/drivers/mtd/mtdcore.c
--- linux-2.4.30/drivers/mtd/mtdcore.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/mtdcore.c	Fri Sep 10 15:15:04 2004
@@ -1,5 +1,5 @@
 /*
- * $Id: mtdcore.c,v 1.34 2003/01/24 23:32:25 dwmw2 Exp $
+ * $Id: mtdcore.c,v 1.43 2004/07/23 15:20:46 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;
+	module_put(THIS_MODULE);
 
 			for (i=0; i< MAX_MTD_DEVICES; i++)
 				if (mtd_table[i])
 					old->remove(mtd_table[i]);
 			
+	list_del(&old->list);
 			up(&mtd_table_mutex);
 			return 0;
-		}
-		prev = &cur->next;
-	}
-	up(&mtd_table_mutex);
-	return 1;
 }
 
 
 /**
- *	__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;
@@ -198,16 +205,33 @@
 			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
  */
 
-int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs,
+int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
 		       unsigned long count, loff_t to, size_t *retlen)
 {
 	unsigned long i;
@@ -237,7 +261,7 @@
  *		       implement their own
  */
 
-int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
+int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs,
 		      unsigned long count, loff_t from, size_t *retlen)
 {
 	unsigned long i;
@@ -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);
@@ -308,10 +333,7 @@
 /* Support for /proc/mtd */
 
 #ifdef CONFIG_PROC_FS
-
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
 static struct proc_dir_entry *proc_mtd;
-#endif
 
 static inline int mtd_proc_info (char *buf, int i)
 {
@@ -324,13 +346,8 @@
 		       this->erasesize, this->name);
 }
 
-static int mtd_read_proc ( char *page, char **start, off_t off,int count
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-                       ,int *eof, void *data_unused
-#else
-                        ,int unused
-#endif
-			)
+static int mtd_read_proc (char *page, char **start, off_t off, int count,
+			  int *eof, void *data_unused)
 {
 	int len, l, i;
         off_t   begin = 0;
@@ -350,9 +367,7 @@
                 }
         }
 
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
         *eof = 1;
-#endif
 
 done:
 	up(&mtd_table_mutex);
@@ -362,18 +377,6 @@
         return ((count < begin+len-off) ? count : begin+len-off);
 }
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0)
-struct proc_dir_entry mtd_proc_entry = {
-        0,                 /* low_ino: the inode -- dynamic */
-        3, "mtd",     /* len of name and name */
-        S_IFREG | S_IRUGO, /* mode */
-        1, 0, 0,           /* nlinks, owner, group */
-        0, NULL,           /* size - unused; operations -- use default */
-        &mtd_read_proc,   /* function used to read data */
-        /* nothing more */
-    };
-#endif
-
 #endif /* CONFIG_PROC_FS */
 
 /*====================================================================*/
@@ -382,16 +385,8 @@
 int __init init_mtd(void)
 {
 #ifdef CONFIG_PROC_FS
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
-	if ((proc_mtd = create_proc_entry( "mtd", 0, 0 )))
+	if ((proc_mtd = create_proc_entry( "mtd", 0, NULL )))
 	  proc_mtd->read_proc = mtd_read_proc;
-#else
-        proc_register_dynamic(&proc_root,&mtd_proc_entry);
-#endif
-#endif
-
-#if LINUX_VERSION_CODE < 0x20212
-	init_mtd_devices();
 #endif
 
 #ifdef CONFIG_PM
@@ -410,12 +405,8 @@
 #endif
 
 #ifdef CONFIG_PROC_FS
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
         if (proc_mtd)
-          remove_proc_entry( "mtd", 0);
-#else
-        proc_unregister(&proc_root,mtd_proc_entry.low_ino);
-#endif
+		remove_proc_entry( "mtd", NULL);
 #endif
 }
 
diff -uNrb linux-2.4.30/drivers/mtd/mtdpart.c linux/drivers/mtd/mtdpart.c
--- linux-2.4.30/drivers/mtd/mtdpart.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/mtdpart.c	Fri Sep 10 15:15:04 2004
@@ -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.50 2004/08/10 16:18:34 dwmw2 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;
+	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;
+	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)
@@ -168,41 +182,55 @@
 					len, retlen, buf);
 }
 
-static int part_writev (struct mtd_info *mtd,  const struct iovec *vecs,
+static int part_writev (struct mtd_info *mtd,  const struct kvec *vecs,
 			 unsigned long count, loff_t to, size_t *retlen)
 {
 	struct mtd_part *part = PART(mtd);
 	if (!(mtd->flags & MTD_WRITEABLE))
 		return -EROFS;
+	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,
+static int part_readv (struct mtd_info *mtd,  struct kvec *vecs,
 			 unsigned long count, loff_t from, size_t *retlen)
 {
 	struct mtd_part *part = PART(mtd);
+	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,
+static int part_writev_ecc (struct mtd_info *mtd,  const struct kvec *vecs,
 			 unsigned long count, loff_t to, size_t *retlen,
-			 u_char *eccbuf, 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);
 }
 
-static int part_readv_ecc (struct mtd_info *mtd,  struct iovec *vecs,
+static int part_readv_ecc (struct mtd_info *mtd,  struct kvec *vecs,
 			 unsigned long count, loff_t from, size_t *retlen,
-			 u_char *eccbuf, 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);
@@ -211,14 +239,30 @@
 static int part_erase (struct mtd_info *mtd, struct erase_info *instr)
 {
 	struct mtd_part *part = PART(mtd);
+	int ret;
 	if (!(mtd->flags & MTD_WRITEABLE))
 		return -EROFS;
 	if (instr->addr >= mtd->size)
 		return -EINVAL;
 	instr->addr += part->offset;
-	return part->master->erase(part->master, instr);
+	ret = part->master->erase(part->master, instr);
+	return ret;
 }
 
+void mtd_erase_callback(struct erase_info *instr)
+{
+	if (instr->mtd->erase == part_erase) {
+		struct mtd_part *part = PART(instr->mtd);
+
+		if (instr->fail_addr != 0xffffffff)
+			instr->fail_addr -= part->offset;
+		instr->addr -= part->offset;
+	}
+	if (instr->callback)
+		instr->callback(instr);
+}
+EXPORT_SYMBOL_GPL(mtd_erase_callback);
+
 static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
 {
 	struct mtd_part *part = PART(mtd);
@@ -253,6 +297,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 +352,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 +385,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 +427,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 +501,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 +523,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 -uNrb linux-2.4.30/drivers/mtd/nand/Config.in linux/drivers/mtd/nand/Config.in
--- linux-2.4.30/drivers/mtd/nand/Config.in	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/nand/Config.in	Tue Sep 28 14:38:13 2004
@@ -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.22 2004/09/16 23:23:42 gleixner Exp $
 
 mainmenu_option next_comment
 
@@ -11,26 +11,51 @@
    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
+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" ]; then
+   dep_tristate '  NAND Flash Driver for Au1550 controller' CONFIG_MTD_NAND_AU1550 $CONFIG_MTD_NAND 
 fi
+
+if [ "$CONFIG_SH_SOLUTION_ENGINE" = "y" ]; then
+   dep_tristate '  Renesas Flash ROM 4-slot interface board (FROM_BOARD4)' CONFIG_MTD_NAND_RTC_FROM4 $CONFIG_MTD_NAND
+fi
+
+dep_tristate '  DiskOnChip 2000, Millennium and Millennium Plus (NAND reimplementation) (EXPERIMENTAL)' CONFIG_MTD_NAND_DISKONCHIP $CONFIG_MTD_NAND $CONFIG_EXPERIMENTAL
+if [ "$CONFIG_MTD_NAND_DISKONCHIP" = "y" -o "$CONFIG_MTD_NAND_DISKONCHIP" = "m" ]; then
+   bool '    Advanced detection options for DiskOnChip' CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADVANCED
+   hex  '    Physical address of DiskOnChip' CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 
+   bool '    Probe high addresses' CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH $CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADVANCED
+   bool '    Allow BBT write on DiskOnChip Millennium and 2000TSOP' CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE
+fi
+
+if [ "$CONFIG_MIKROTIK_RB500" = "y" ]; then
+   dep_tristate '  NAND Flash device on RouterBoard5xx board' CONFIG_MTD_NAND_RBMIPS $CONFIG_MTD_NAND
 fi
 
 endmenu
diff -uNrb linux-2.4.30/drivers/mtd/nand/Makefile linux/drivers/mtd/nand/Makefile
--- linux-2.4.30/drivers/mtd/nand/Makefile	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/nand/Makefile	Fri Sep 10 15:15:04 2004
@@ -1,16 +1,16 @@
 #
-# linux/drivers/nand/Makefile
+# linux/drivers/nand/Makefile.24
+# Makefile for obsolete kernels.
 #
-# $Id: Makefile,v 1.10 2002/12/01 13:23:05 gleixner Exp $
+# $Id: Makefile.24,v 1.1 2004/07/12 16:08:17 dwmw2 Exp $
 
 O_TARGET	:= nandlink.o
+export-objs	:= nand_base.o nand_bbt.o nand_ecc.o nand_ids.o
+list-multi	:= nand.o
 
-export-objs	:= nand.o nand_ecc.o nand_ids.o
-
-obj-$(CONFIG_MTD_NAND)		+= nand.o nand_ecc.o
-obj-$(CONFIG_MTD_NAND_SPIA)	+= spia.o
-obj-$(CONFIG_MTD_NAND_AUTCPU12)	+= autcpu12.o
-obj-$(CONFIG_MTD_NAND_EDB7312)  += edb7312.o
-obj-$(CONFIG_MTD_NAND_IDS)	+= nand_ids.o
+include Makefile.common
 
 include $(TOPDIR)/Rules.make
+
+nand.o: $(nand-objs)
+	$(LD) -r -o $@ $(nand-objs)
diff -uNrb linux-2.4.30/drivers/mtd/nand/Makefile.common linux/drivers/mtd/nand/Makefile.common
--- linux-2.4.30/drivers/mtd/nand/Makefile.common	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/nand/Makefile.common	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,23 @@
+#
+# linux/drivers/nand/Makefile
+#
+# $Id: Makefile.common,v 1.12 2004/09/20 08:35:49 dmarlin Exp $
+
+obj-$(CONFIG_MTD_NAND)			+= nand.o nand_ecc.o
+obj-$(CONFIG_MTD_NAND_IDS)		+= nand_ids.o
+
+obj-$(CONFIG_MTD_NAND_SPIA)		+= spia.o
+obj-$(CONFIG_MTD_NAND_TOTO)		+= toto.o
+obj-$(CONFIG_MTD_NAND_AUTCPU12)		+= autcpu12.o
+obj-$(CONFIG_MTD_NAND_EDB7312)		+= edb7312.o
+obj-$(CONFIG_MTD_NAND_TX4925NDFMC)	+= tx4925ndfmc.o
+obj-$(CONFIG_MTD_NAND_TX4938NDFMC)	+= tx4938ndfmc.o
+obj-$(CONFIG_MTD_NAND_AU1550)		+= au1550nd.o
+obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB)	+= ppchameleonevb.o
+obj-$(CONFIG_MTD_NAND_DISKONCHIP)	+= diskonchip.o
+obj-$(CONFIG_MTD_NAND_H1900)		+= h1910.o
+obj-$(CONFIG_MTD_NAND_RTC_FROM4)	+= rtc_from4.o
+obj-$(CONFIG_MTD_NAND_RBMIPS)		+= rbmipsnand.o
+
+
+nand-objs = nand_base.o nand_bbt.o
diff -uNrb linux-2.4.30/drivers/mtd/nand/au1550nd.c linux/drivers/mtd/nand/au1550nd.c
--- linux-2.4.30/drivers/mtd/nand/au1550nd.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/nand/au1550nd.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,481 @@
+/*
+ *  drivers/mtd/nand/au1550nd.c
+ *
+ *  Copyright (C) 2004 Embedded Edge, LLC
+ *
+ * $Id: au1550nd.c,v 1.8 2004/09/16 23:27:14 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.
+ *
+ */
+
+#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> 
+#endif
+#ifdef CONFIG_MIPS_DB1550
+#include <asm/db1x00.h> 
+#endif
+
+
+/*
+ * MTD structure for NAND controller
+ */
+static struct mtd_info *au1550_mtd = NULL;
+static void __iomem *p_nand;
+static int nand_width = 1; /* default x8*/
+
+/*
+ * Define partitions for flash device
+ */
+const static struct mtd_partition partition_info[] = {
+#ifdef CONFIG_MIPS_PB1550
+#define NUM_PARTITIONS            2
+	{ 
+		.name = "Pb1550 NAND FS 0",
+	  	.offset = 0,
+	  	.size = 8*1024*1024 
+	},
+	{ 
+		.name = "Pb1550 NAND FS 1",
+		.offset =  MTDPART_OFS_APPEND,
+ 		.size =    MTDPART_SIZ_FULL
+	}
+#endif
+#ifdef CONFIG_MIPS_DB1550
+#define NUM_PARTITIONS            2
+	{ 
+		.name = "Db1550 NAND FS 0",
+	  	.offset = 0,
+	  	.size = 8*1024*1024 
+	},
+	{ 
+		.name = "Db1550 NAND FS 1",
+		.offset =  MTDPART_OFS_APPEND,
+ 		.size =    MTDPART_SIZ_FULL
+	}
+#endif
+};
+
+
+/**
+ * au_read_byte -  read one byte from the chip
+ * @mtd:	MTD device structure
+ *
+ *  read function for 8bit buswith
+ */
+static u_char au_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	u_char ret = readb(this->IO_ADDR_R);
+	au_sync();
+	return ret;
+}
+
+/**
+ * au_write_byte -  write one byte to the chip
+ * @mtd:	MTD device structure
+ * @byte:	pointer to data byte to write
+ *
+ *  write function for 8it buswith
+ */
+static void au_write_byte(struct mtd_info *mtd, u_char byte)
+{
+	struct nand_chip *this = mtd->priv;
+	writeb(byte, this->IO_ADDR_W);
+	au_sync();
+}
+
+/**
+ * au_read_byte16 -  read one byte endianess aware from the chip
+ * @mtd:	MTD device structure
+ *
+ *  read function for 16bit buswith with 
+ * endianess conversion
+ */
+static u_char au_read_byte16(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	u_char ret = (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
+	au_sync();
+	return ret;
+}
+
+/**
+ * au_write_byte16 -  write one byte endianess aware to the chip
+ * @mtd:	MTD device structure
+ * @byte:	pointer to data byte to write
+ *
+ *  write function for 16bit buswith with
+ * endianess conversion
+ */
+static void au_write_byte16(struct mtd_info *mtd, u_char byte)
+{
+	struct nand_chip *this = mtd->priv;
+	writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
+	au_sync();
+}
+
+/**
+ * au_read_word -  read one word from the chip
+ * @mtd:	MTD device structure
+ *
+ *  read function for 16bit buswith without 
+ * endianess conversion
+ */
+static u16 au_read_word(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	u16 ret = readw(this->IO_ADDR_R);
+	au_sync();
+	return ret;
+}
+
+/**
+ * au_write_word -  write one word to the chip
+ * @mtd:	MTD device structure
+ * @word:	data word to write
+ *
+ *  write function for 16bit buswith without 
+ * endianess conversion
+ */
+static void au_write_word(struct mtd_info *mtd, u16 word)
+{
+	struct nand_chip *this = mtd->priv;
+	writew(word, this->IO_ADDR_W);
+	au_sync();
+}
+
+/**
+ * au_write_buf -  write buffer to chip
+ * @mtd:	MTD device structure
+ * @buf:	data buffer
+ * @len:	number of bytes to write
+ *
+ *  write function for 8bit buswith
+ */
+static void au_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+
+	for (i=0; i<len; i++) {
+		writeb(buf[i], this->IO_ADDR_W);
+		au_sync();
+	}
+}
+
+/**
+ * au_read_buf -  read chip data into buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer to store date
+ * @len:	number of bytes to read
+ *
+ *  read function for 8bit buswith
+ */
+static void au_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+
+	for (i=0; i<len; i++) {
+		buf[i] = readb(this->IO_ADDR_R);
+		au_sync();	
+	}
+}
+
+/**
+ * au_verify_buf -  Verify chip data against buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer containing the data to compare
+ * @len:	number of bytes to compare
+ *
+ *  verify function for 8bit buswith
+ */
+static int au_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+
+	for (i=0; i<len; i++) {
+		if (buf[i] != readb(this->IO_ADDR_R))
+			return -EFAULT;
+		au_sync();
+	}
+
+	return 0;
+}
+
+/**
+ * au_write_buf16 -  write buffer to chip
+ * @mtd:	MTD device structure
+ * @buf:	data buffer
+ * @len:	number of bytes to write
+ *
+ *  write function for 16bit buswith
+ */
+static void au_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+	
+	for (i=0; i<len; i++) {
+		writew(p[i], this->IO_ADDR_W);
+		au_sync();
+	}
+		
+}
+
+/**
+ * au_read_buf16 -  read chip data into buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer to store date
+ * @len:	number of bytes to read
+ *
+ *  read function for 16bit buswith
+ */
+static void au_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+
+	for (i=0; i<len; i++) {
+		p[i] = readw(this->IO_ADDR_R);
+		au_sync();
+	}
+}
+
+/**
+ * au_verify_buf16 -  Verify chip data against buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer containing the data to compare
+ * @len:	number of bytes to compare
+ *
+ *  verify function for 16bit buswith
+ */
+static int au_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+
+	for (i=0; i<len; i++) {
+		if (p[i] != readw(this->IO_ADDR_R))
+			return -EFAULT;
+		au_sync();
+	}
+	return 0;
+}
+
+
+static void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+	register struct nand_chip *this = mtd->priv;
+
+	switch(cmd){
+
+	case NAND_CTL_SETCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_CMD; break;
+	case NAND_CTL_CLRCLE: this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; break;
+
+	case NAND_CTL_SETALE: this->IO_ADDR_W = p_nand + MEM_STNAND_ADDR; break;
+	case NAND_CTL_CLRALE: 
+		this->IO_ADDR_W = p_nand + MEM_STNAND_DATA; 
+		/* FIXME: Nobody knows why this is neccecary, 
+		 * but it works onlythat way */
+		udelay(1); 
+		break;
+
+	case NAND_CTL_SETNCE: 
+		/* assert (force assert) chip enable */
+		au_writel(au_readl(MEM_STNDCTL) | 0x20 , MEM_STNDCTL);
+		break;
+
+	case NAND_CTL_CLRNCE: 
+ 		/* deassert chip enable */
+		au_writel(au_readl(MEM_STNDCTL) & ~0x20 , MEM_STNDCTL);
+		break;
+	}
+
+	this->IO_ADDR_R = this->IO_ADDR_W;
+	
+	/* Drain the writebuffer */
+	au_sync();
+}
+
+int au1550_device_ready(struct mtd_info *mtd)
+{
+	int ret = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0;
+	au_sync();
+	return ret;
+}
+
+/*
+ * Main initialization routine
+ */
+int __init au1550_init (void)
+{
+	struct nand_chip *this;
+	u16 boot_swapboot = 0; /* default value */
+	u32 mem_time;
+	int retval;
+
+	/* Allocate memory for MTD device structure and private data */
+	au1550_mtd = kmalloc (sizeof(struct mtd_info) + 
+			sizeof (struct nand_chip), GFP_KERNEL);
+	if (!au1550_mtd) {
+		printk ("Unable to allocate NAND MTD dev structure.\n");
+		return -ENOMEM;
+	}
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *) (&au1550_mtd[1]);
+
+	/* Initialize structures */
+	memset((char *) au1550_mtd, 0, sizeof(struct mtd_info));
+	memset((char *) this, 0, sizeof(struct nand_chip));
+
+	/* Link the private data with the MTD structure */
+	au1550_mtd->priv = this;
+
+	/* disable interrupts */
+	au_writel(au_readl(MEM_STNDCTL) & ~(1<<8), MEM_STNDCTL);
+
+	/* disable NAND boot */
+	au_writel(au_readl(MEM_STNDCTL) & ~(1<<0), MEM_STNDCTL);
+
+#ifdef CONFIG_MIPS_PB1550
+	/* set gpio206 high */
+	au_writel(au_readl(GPIO2_DIR) & ~(1<<6), GPIO2_DIR);
+
+	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;
+			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");
+			retval = -EINVAL;
+			goto outmem;
+	}
+
+	/* Configure RCE1 - should be done by YAMON */
+	au_writel(0x5 | (nand_width << 22), MEM_STCFG1);
+	au_writel(NAND_TIMING, MEM_STTIME1);
+	mem_time = au_readl(MEM_STTIME1);
+	au_sync();
+
+	/* setup and enable chip select */
+	/* we really need to decode offsets only up till 0x20 */
+	au_writel((1<<28) | (NAND_PHYS_ADDR>>4) | 
+			(((NAND_PHYS_ADDR + 0x1000)-1) & (0x3fff<<18)>>18), 
+			MEM_STADDR1);
+	au_sync();
+#endif
+
+#ifdef CONFIG_MIPS_DB1550
+	/* FIXME: should be done by the bootloader
+	 *  
+	 * tglx: stcfg1 was set to 0x00400005. I changed
+	 * this as it does not work with all chips.
+	 * someone should look into the correct timing
+	 * values, as bit 8 does a clock / 4 prescale
+	*/
+	au_writel(0x00400105, MEM_STCFG1);
+	au_writel(0x00007774, MEM_STTIME1);
+	au_writel(0x12003FFF, MEM_STADDR1);
+#endif
+
+	p_nand = (void __iomem *)ioremap(NAND_PHYS_ADDR, 0x1000);
+
+	/* Set address of hardware control function */
+	this->hwcontrol = au1550_hwcontrol;
+	this->dev_ready = au1550_device_ready;
+	/* 30 us command delay time */
+	this->chip_delay = 30;		
+	this->eccmode = NAND_ECC_SOFT;
+
+	this->options = NAND_NO_AUTOINCR;
+
+	if (!nand_width)
+		this->options |= NAND_BUSWIDTH_16;
+
+	this->read_byte = (!nand_width) ? au_read_byte16 : au_read_byte;
+	this->write_byte = (!nand_width) ? au_write_byte16 : au_write_byte;
+	this->write_word = au_write_word;
+	this->read_word = au_read_word;
+	this->write_buf = (!nand_width) ? au_write_buf16 : au_write_buf;
+	this->read_buf = (!nand_width) ? au_read_buf16 : au_read_buf;
+	this->verify_buf = (!nand_width) ? au_verify_buf16 : au_verify_buf;
+
+	/* Scan to find existence of the device */
+	if (nand_scan (au1550_mtd, 1)) {
+		retval = -ENXIO;
+		goto outio;
+	}
+
+	/* Register the partitions */
+	add_mtd_partitions(au1550_mtd, partition_info, NUM_PARTITIONS);
+
+	return 0;
+
+ outio:
+	iounmap ((void *)p_nand);
+	
+ outmem:
+	kfree (au1550_mtd);
+	return retval;
+}
+
+module_init(au1550_init);
+
+/*
+ * Clean up routine
+ */
+#ifdef MODULE
+static void __exit au1550_cleanup (void)
+{
+	struct nand_chip *this = (struct nand_chip *) &au1550_mtd[1];
+
+	/* Release resources, unregister device */
+	nand_release (au1550_mtd);
+
+	/* Free the MTD device structure */
+	kfree (au1550_mtd);
+
+	/* Unmap */
+	iounmap ((void *)p_nand);
+}
+module_exit(au1550_cleanup);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Embedded Edge, LLC");
+MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Pb1550 board");
diff -uNrb linux-2.4.30/drivers/mtd/nand/autcpu12.c linux/drivers/mtd/nand/autcpu12.c
--- linux-2.4.30/drivers/mtd/nand/autcpu12.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/nand/autcpu12.c	Tue Sep 28 14:38:13 2004
@@ -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.21 2004/09/16 23:27:14 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
@@ -15,7 +15,7 @@
  *  Overview:
  *   This is a device driver for the NAND flash device found on the
  *   autronix autcpu12 board, which is a SmartMediaCard. It supports 
- *   16MB, 32MB and 64MB cards.
+ *   16MiB, 32MiB and 64MiB cards.
  *
  *
  *	02-12-2002 TG	Cleanup of module params
@@ -25,10 +25,11 @@
  *			added page_cache
  *
  *	10-06-2002 TG	128K card support added
- *
  */
 
+#include <linux/version.h>
 #include <linux/slab.h>
+#include <linux/init.h>
 #include <linux/module.h>
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/nand.h>
@@ -43,19 +44,11 @@
  */
 static struct mtd_info *autcpu12_mtd = NULL;
 
-/*
- * Module stuff
- */
-#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
-#define autcpu12_init init_module
-#define autcpu12_cleanup cleanup_module
-#endif
-
 static int autcpu12_io_base = CS89712_VIRT_BASE;
 static int autcpu12_fio_pbase = AUTCPU12_PHYS_SMC;
 static int autcpu12_fio_ctrl = AUTCPU12_SMC_SELECT_OFFSET;
 static int autcpu12_pedr = AUTCPU12_SMC_PORT_OFFSET;
-static int autcpu12_fio_base;
+static void __iomem * autcpu12_fio_base;
 
 #ifdef MODULE
 MODULE_PARM(autcpu12_fio_pbase, "i");
@@ -70,41 +63,40 @@
 /*
  * Define partitions for flash devices
  */
-
 static struct mtd_partition partition_info16k[] = {
-	{ name: "AUTCPU12 flash partition 1",
-	  offset:  0,
-	  size:    8 * SZ_1M },
-	{ name: "AUTCPU12 flash partition 2",
-	  offset:  8 * SZ_1M,
-	  size:    8 * SZ_1M },
+	{ .name		= "AUTCPU12 flash partition 1",
+	  .offset	= 0,
+	  .size		= 8 * SZ_1M },
+	{ .name		= "AUTCPU12 flash partition 2",
+	  .offset	= 8 * SZ_1M,
+	  .size		= 8 * SZ_1M },
 };
 
 static struct mtd_partition partition_info32k[] = {
-	{ name: "AUTCPU12 flash partition 1",
-	  offset:  0,
-	  size:    8 * SZ_1M },
-	{ name: "AUTCPU12 flash partition 2",
-	  offset:  8 * SZ_1M,
-	  size:   24 * SZ_1M },
+	{ .name		= "AUTCPU12 flash partition 1",
+	  .offset	= 0,
+	  .size		= 8 * SZ_1M },
+	{ .name		= "AUTCPU12 flash partition 2",
+	  .offset	= 8 * SZ_1M,
+	  .size		= 24 * SZ_1M },
 };
 
 static struct mtd_partition partition_info64k[] = {
-	{ name: "AUTCPU12 flash partition 1",
-	  offset:  0,
-	  size:   16 * SZ_1M },
-	{ name: "AUTCPU12 flash partition 2",
-	  offset: 16 * SZ_1M,
-	  size:   48 * SZ_1M},
+	{ .name		= "AUTCPU12 flash partition 1",
+	  .offset	= 0,
+	  .size		= 16 * SZ_1M },
+	{ .name		= "AUTCPU12 flash partition 2",
+	  .offset	= 16 * SZ_1M,
+	  .size		= 48 * SZ_1M },
 };
 
 static struct mtd_partition partition_info128k[] = {
-	{ name: "AUTCPU12 flash partition 1",
-	  offset:  0,
-	  size:   16 * SZ_1M },
-	{ name: "AUTCPU12 flash partition 2",
-	  offset: 16 * SZ_1M,
-	  size:   112 * SZ_1M},
+	{ .name		= "AUTCPU12 flash partition 1",
+	  .offset	= 0,
+	  .size		= 16 * SZ_1M },
+	{ .name		= "AUTCPU12 flash partition 2",
+	  .offset	= 16 * SZ_1M,
+	  .size		= 112 * SZ_1M },
 };
 
 #define NUM_PARTITIONS16K 2
@@ -114,7 +106,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 +125,13 @@
 /*
 *	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;
 
 }
+
 /*
  * Main initialization routine
  */
@@ -157,7 +150,7 @@
 	}
 
 	/* map physical adress */
-	autcpu12_fio_base=(unsigned long)ioremap(autcpu12_fio_pbase,SZ_1K);
+	autcpu12_fio_base=(void __iomem *)ioremap(autcpu12_fio_pbase,SZ_1K);
 	if(!autcpu12_fio_base){
 		printk("Ioremap autcpu12 SmartMedia Card failed\n");
 		err = -EIO;
@@ -183,29 +176,18 @@
 	this->chip_delay = 20;		
 	this->eccmode = NAND_ECC_SOFT;
 
+	/* Enable the following for a flash based bad block table */
+	/*
+	this->options = NAND_USE_FLASH_BBT;
+	*/
+	this->options = NAND_USE_FLASH_BBT;
+	
 	/* 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 +197,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:
@@ -240,17 +218,8 @@
 #ifdef MODULE
 static void __exit autcpu12_cleanup (void)
 {
-	struct nand_chip *this = (struct nand_chip *) &autcpu12_mtd[1];
-
-	/* Unregister partitions */
-	del_mtd_partitions(autcpu12_mtd);
-	
-	/* Unregister the device */
-	del_mtd_device (autcpu12_mtd);
-
-	/* Free internal data buffers */
-	kfree (this->data_buf);
-	kfree (this->data_cache);
+	/* Release resources, unregister device */
+	nand_release (autcpu12_mtd);
 
 	/* unmap physical adress */
 	iounmap((void *)autcpu12_fio_base);
diff -uNrb linux-2.4.30/drivers/mtd/nand/diskonchip.c linux/drivers/mtd/nand/diskonchip.c
--- linux-2.4.30/drivers/mtd/nand/diskonchip.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/nand/diskonchip.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,1637 @@
+/* 
+ * drivers/mtd/nand/diskonchip.c
+ *
+ * (C) 2003 Red Hat, Inc.
+ * (C) 2004 Dan Brown <dan_brown@ieee.org>
+ * (C) 2004 Kalev Lember <kalev@smartlink.ee>
+ *
+ * Author: David Woodhouse <dwmw2@infradead.org>
+ * Additional Diskonchip 2000 and Millennium support by Dan Brown <dan_brown@ieee.org>
+ * Diskonchip Millennium Plus support by Kalev Lember <kalev@smartlink.ee>
+ *
+ * Interface to generic NAND code for M-Systems DiskOnChip devices
+ *
+ * $Id: diskonchip.c,v 1.35 2004/09/16 23:27:14 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>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/inftl.h>
+
+/* Where to look for the devices? */
+#ifndef CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS
+#define CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS 0
+#endif
+
+static unsigned long __initdata doc_locations[] = {
+#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
+#ifdef CONFIG_MTD_DISKONCHIP_PROBE_HIGH
+	0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, 
+	0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
+	0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, 
+	0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000, 
+	0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
+#else /*  CONFIG_MTD_DOCPROBE_HIGH */
+	0xc8000, 0xca000, 0xcc000, 0xce000, 
+	0xd0000, 0xd2000, 0xd4000, 0xd6000,
+	0xd8000, 0xda000, 0xdc000, 0xde000, 
+	0xe0000, 0xe2000, 0xe4000, 0xe6000, 
+	0xe8000, 0xea000, 0xec000, 0xee000,
+#endif /*  CONFIG_MTD_DOCPROBE_HIGH */
+#elif defined(__PPC__)
+	0xe4000000,
+#elif defined(CONFIG_MOMENCO_OCELOT)
+	0x2f000000,
+        0xff000000,
+#elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C)
+        0xff000000,
+##else
+#warning Unknown architecture for DiskOnChip. No default probe locations defined
+#endif
+	0xffffffff };
+
+static struct mtd_info *doclist = NULL;
+
+struct doc_priv {
+	void __iomem *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;
+	int mh0_page;
+	int mh1_page;
+	struct mtd_info *nextdoc;
+};
+
+/* Max number of eraseblocks to scan (from start of device) for the (I)NFTL
+   MediaHeader.  The spec says to just keep going, I think, but that's just
+   silly. */
+#define MAX_MEDIAHEADER_SCAN 8
+
+/* This is the syndrome computed by the HW ecc generator upon reading an empty
+   page, one with all 0xff for data and stored ecc code. */
+static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a };
+/* This is the ecc value computed by the HW ecc generator upon writing an empty
+   page, one with all 0xff for data. */
+static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };
+
+#define INFTL_BBT_RESERVED_BLOCKS 4
+
+#define DoC_is_MillenniumPlus(doc) ((doc)->ChipID == DOC_ChipID_DocMilPlus16 || (doc)->ChipID == DOC_ChipID_DocMilPlus32)
+#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 int no_ecc_failures=0;
+MODULE_PARM(no_ecc_failures, "i");
+
+#ifdef CONFIG_MTD_PARTITIONS
+static int no_autopart=0;
+MODULE_PARM(no_autopart, "i");
+#endif
+
+#ifdef MTD_NAND_DISKONCHIP_BBTWRITE
+static int inftl_bbt_write=1;
+#else
+static int inftl_bbt_write=0;
+#endif
+MODULE_PARM(inftl_bbt_write, "i");
+
+static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS;
+MODULE_PARM(doc_config_location, "l");
+MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
+
+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 if (DoC_is_MillenniumPlus(doc))
+			dummy = ReadDOC(doc->virtadr, Mplus_NOP);
+		else
+			dummy = ReadDOC(doc->virtadr, DOCStatus);
+	}
+	
+}
+
+#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(struct doc_priv *doc)
+{
+        void __iomem *docptr = doc->virtadr;
+	unsigned long timeo = jiffies + (HZ * 10);
+
+	if(debug) printk("_DoC_WaitReady...\n");
+	/* Out-of-line routine to wait for chip response */
+	if (DoC_is_MillenniumPlus(doc)) {
+		while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
+			if (time_after(jiffies, timeo)) {
+				printk("_DoC_WaitReady timed out.\n");
+				return -EIO;
+			}
+			udelay(1);
+			cond_resched();
+		}
+	} else {
+		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)
+{
+        void __iomem *docptr = doc->virtadr;
+	int ret = 0;
+
+	if (DoC_is_MillenniumPlus(doc)) {
+		DoC_Delay(doc, 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(doc);
+	} else {
+		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;
+        void __iomem *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;
+        void __iomem *docptr = doc->virtadr;
+	u_char ret;
+
+	ReadDOC(docptr, CDSNSlowIO);
+	DoC_Delay(doc, 2);
+	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;
+        void __iomem *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;
+        void __iomem *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;
+        void __iomem *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;
+        void __iomem *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 __init 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;
+		void __iomem *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 __init 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;
+	printk(KERN_DEBUG "Detected %d chips per floor.\n", 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;
+        void __iomem *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;
+        void __iomem *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);
+	return ReadDOC(docptr, LastDataRead);
+}
+
+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;
+        void __iomem *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;
+        void __iomem *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 + (i & 0xff));
+
+	/* 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;
+        void __iomem *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 u_char doc2001plus_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+        void __iomem *docptr = doc->virtadr;
+	u_char ret;
+
+        ReadDOC(docptr, Mplus_ReadPipeInit);
+        ReadDOC(docptr, Mplus_ReadPipeInit);
+        ret = ReadDOC(docptr, Mplus_LastDataRead);
+	if (debug) printk("read_byte returns %02x\n", ret);
+	return ret;
+}
+
+static void doc2001plus_writebuf(struct mtd_info *mtd, 
+			     const u_char *buf, int len)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+        void __iomem *docptr = doc->virtadr;
+	int i;
+
+	if (debug)printk("writebuf of %d bytes: ", len);
+	for (i=0; i < len; i++) {
+		WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
+		if (debug && i < 16)
+			printk("%02x ", buf[i]);
+	}
+	if (debug) printk("\n");
+}
+
+static void doc2001plus_readbuf(struct mtd_info *mtd, 
+			    u_char *buf, int len)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+        void __iomem *docptr = doc->virtadr;
+	int i;
+
+	if (debug)printk("readbuf of %d bytes: ", len);
+
+	/* Start read pipeline */
+	ReadDOC(docptr, Mplus_ReadPipeInit);
+	ReadDOC(docptr, Mplus_ReadPipeInit);
+
+	for (i=0; i < len-2; i++) {
+		buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
+		if (debug && i < 16)
+			printk("%02x ", buf[i]);
+	}
+
+	/* Terminate read pipeline */
+	buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead);
+	if (debug && i < 16)
+		printk("%02x ", buf[len-2]);
+	buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead);
+	if (debug && i < 16)
+		printk("%02x ", buf[len-1]);
+	if (debug) printk("\n");
+}
+
+static int doc2001plus_verifybuf(struct mtd_info *mtd, 
+			     const u_char *buf, int len)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+        void __iomem *docptr = doc->virtadr;
+	int i;
+
+	if (debug)printk("verifybuf of %d bytes: ", len);
+
+	/* Start read pipeline */
+	ReadDOC(docptr, Mplus_ReadPipeInit);
+	ReadDOC(docptr, Mplus_ReadPipeInit);
+
+	for (i=0; i < len-2; i++)
+		if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
+			ReadDOC(docptr, Mplus_LastDataRead);
+			ReadDOC(docptr, Mplus_LastDataRead);
+			return i;
+		}
+	if (buf[len-2] != ReadDOC(docptr, Mplus_LastDataRead))
+		return len-2;
+	if (buf[len-1] != ReadDOC(docptr, Mplus_LastDataRead))
+		return len-1;
+	return 0;
+}
+
+static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+        void __iomem *docptr = doc->virtadr;
+	int floor = 0;
+
+	if(debug)printk("select chip (%d)\n", chip);
+
+	if (chip == -1) {
+		/* Disable flash internally */
+		WriteDOC(0, docptr, Mplus_FlashSelect);
+		return;
+	}
+
+	floor = chip / doc->chips_per_floor;
+	chip -= (floor *  doc->chips_per_floor);
+
+	/* Assert ChipEnable and deassert WriteProtect */
+	WriteDOC((DOC_FLASH_CE), docptr, Mplus_FlashSelect);
+	this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+	doc->curchip = chip;
+	doc->curfloor = floor;
+}
+
+static void doc200x_select_chip(struct mtd_info *mtd, int chip)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+        void __iomem *docptr = doc->virtadr;
+	int floor = 0;
+
+	if(debug)printk("select chip (%d)\n", chip);
+
+	if (chip == -1)
+		return;
+
+	floor = chip / doc->chips_per_floor;
+	chip -= (floor *  doc->chips_per_floor);
+
+	/* 11.4.4 -- deassert CE before changing chip */
+	doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE);
+
+	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;
+        void __iomem *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 void doc2001plus_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+        void __iomem *docptr = doc->virtadr;
+
+	/*
+	 * Must terminate write pipeline before sending any commands
+	 * to the device.
+	 */
+	if (command == NAND_CMD_PAGEPROG) {
+		WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
+		WriteDOC(0x00, docptr, Mplus_WritePipeTerm);
+	}
+
+	/*
+	 * 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;
+		}
+		WriteDOC(readcmd, docptr, Mplus_FlashCmd);
+	}
+	WriteDOC(command, docptr, Mplus_FlashCmd);
+	WriteDOC(0, docptr, Mplus_WritePipeTerm);
+	WriteDOC(0, docptr, Mplus_WritePipeTerm);
+
+	if (column != -1 || page_addr != -1) {
+		/* Serially input address */
+		if (column != -1) {
+			/* Adjust columns for 16 bit buswidth */
+			if (this->options & NAND_BUSWIDTH_16)
+				column >>= 1;
+			WriteDOC(column, docptr, Mplus_FlashAddress);
+		}
+		if (page_addr != -1) {
+			WriteDOC((unsigned char) (page_addr & 0xff), docptr, Mplus_FlashAddress);
+			WriteDOC((unsigned char) ((page_addr >> 8) & 0xff), docptr, Mplus_FlashAddress);
+			/* One more address cycle for higher density devices */
+			if (this->chipsize & 0x0c000000) {
+				WriteDOC((unsigned char) ((page_addr >> 16) & 0x0f), docptr, Mplus_FlashAddress);
+				printk("high density\n");
+			}
+		}
+		WriteDOC(0, docptr, Mplus_WritePipeTerm);
+		WriteDOC(0, docptr, Mplus_WritePipeTerm);
+		/* deassert ALE */
+		if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || command == NAND_CMD_READOOB || command == NAND_CMD_READID)
+			WriteDOC(0, docptr, Mplus_FlashControl);
+	}
+
+	/* 
+	 * 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);
+		WriteDOC(NAND_CMD_STATUS, docptr, Mplus_FlashCmd);
+		WriteDOC(0, docptr, Mplus_WritePipeTerm);
+		WriteDOC(0, docptr, Mplus_WritePipeTerm);
+		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;
+		}
+	}
+
+	/* Apply this short delay always to ensure that we do wait tWB in
+	 * any case on any machine. */
+	ndelay (100);
+	/* wait until command is processed */
+	while (!this->dev_ready(mtd));
+}
+
+static int doc200x_dev_ready(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+        void __iomem *docptr = doc->virtadr;
+
+	if (DoC_is_MillenniumPlus(doc)) {
+		/* 11.4.2 -- must NOP four times before checking FR/B# */
+		DoC_Delay(doc, 4);
+		if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
+			if(debug)
+				printk("not ready\n");
+			return 0;
+		}
+		if (debug)printk("was ready\n");
+		return 1;
+	} else {
+		/* 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, loff_t ofs, int getchip)
+{
+	/* This is our last resort if we couldn't find or create a BBT.  Just
+	   pretend all blocks are good. */
+	return 0;
+}
+
+static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+        void __iomem *docptr = doc->virtadr;
+
+	/* Prime the ECC engine */
+	switch(mode) {
+	case NAND_ECC_READ:
+		WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+		WriteDOC(DOC_ECC_EN, docptr, ECCConf);
+		break;
+	case NAND_ECC_WRITE:
+		WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
+		WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
+		break;
+	}
+}
+
+static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+        void __iomem *docptr = doc->virtadr;
+
+	/* Prime the ECC engine */
+	switch(mode) {
+	case NAND_ECC_READ:
+		WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
+		WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf);
+		break;
+	case NAND_ECC_WRITE:
+		WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf);
+		WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf);
+		break;
+	}
+}
+
+/* This code is only called on write */
+static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
+				 unsigned char *ecc_code)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+        void __iomem *docptr = doc->virtadr;
+	int i;
+	int emptymatch = 1;
+
+	/* flush the pipeline */
+	if (DoC_is_2000(doc)) {
+		WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl);
+		WriteDOC(0, docptr, 2k_CDSN_IO);
+		WriteDOC(0, docptr, 2k_CDSN_IO);
+		WriteDOC(0, docptr, 2k_CDSN_IO);
+		WriteDOC(doc->CDSNControl, docptr, CDSNControl);
+	} else if (DoC_is_MillenniumPlus(doc)) {
+		WriteDOC(0, docptr, Mplus_NOP);
+		WriteDOC(0, docptr, Mplus_NOP);
+		WriteDOC(0, docptr, Mplus_NOP);
+	} else {
+		WriteDOC(0, docptr, NOP);
+		WriteDOC(0, docptr, NOP);
+		WriteDOC(0, docptr, NOP);
+	}
+
+	for (i = 0; i < 6; i++) {
+		if (DoC_is_MillenniumPlus(doc))
+			ecc_code[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i);
+		else 
+			ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
+		if (ecc_code[i] != empty_write_ecc[i])
+			emptymatch = 0;
+	}
+	if (DoC_is_MillenniumPlus(doc))
+		WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
+	else
+		WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+#if 0
+	/* If emptymatch=1, we might have an all-0xff data buffer.  Check. */
+	if (emptymatch) {
+		/* Note: this somewhat expensive test should not be triggered
+		   often.  It could be optimized away by examining the data in
+		   the writebuf routine, and remembering the result. */
+		for (i = 0; i < 512; i++) {
+			if (dat[i] == 0xff) continue;
+			emptymatch = 0;
+			break;
+		}
+	}
+	/* If emptymatch still =1, we do have an all-0xff data buffer.
+	   Return all-0xff ecc value instead of the computed one, so
+	   it'll look just like a freshly-erased page. */
+	if (emptymatch) memset(ecc_code, 0xff, 6);
+#endif
+	return 0;
+}
+
+static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
+{
+	int i, ret = 0;
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+        void __iomem *docptr = doc->virtadr;
+	volatile u_char dummy;
+	int emptymatch = 1;
+	
+	/* flush the pipeline */
+	if (DoC_is_2000(doc)) {
+		dummy = ReadDOC(docptr, 2k_ECCStatus);
+		dummy = ReadDOC(docptr, 2k_ECCStatus);
+		dummy = ReadDOC(docptr, 2k_ECCStatus);
+	} else if (DoC_is_MillenniumPlus(doc)) {
+		dummy = ReadDOC(docptr, Mplus_ECCConf);
+		dummy = ReadDOC(docptr, Mplus_ECCConf);
+		dummy = ReadDOC(docptr, Mplus_ECCConf);
+	} else {
+		dummy = ReadDOC(docptr, ECCConf);
+		dummy = ReadDOC(docptr, ECCConf);
+		dummy = ReadDOC(docptr, ECCConf);
+	}
+	
+	/* Error occured ? */
+	if (dummy & 0x80) {
+		for (i = 0; i < 6; i++) {
+			if (DoC_is_MillenniumPlus(doc))
+				calc_ecc[i] = ReadDOC_(docptr, DoC_Mplus_ECCSyndrome0 + i);
+			else
+				calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
+			if (calc_ecc[i] != empty_read_syndrome[i])
+				emptymatch = 0;
+		}
+		/* If emptymatch=1, the read syndrome is consistent with an
+		   all-0xff data and stored ecc block.  Check the stored ecc. */
+		if (emptymatch) {
+			for (i = 0; i < 6; i++) {
+				if (read_ecc[i] == 0xff) continue;
+				emptymatch = 0;
+				break;
+			}
+		}
+		/* If emptymatch still =1, check the data block. */
+		if (emptymatch) {
+		/* Note: this somewhat expensive test should not be triggered
+		   often.  It could be optimized away by examining the data in
+		   the readbuf routine, and remembering the result. */
+			for (i = 0; i < 512; i++) {
+				if (dat[i] == 0xff) continue;
+				emptymatch = 0;
+				break;
+			}
+		}
+		/* If emptymatch still =1, this is almost certainly a freshly-
+		   erased block, in which case the ECC will not come out right.
+		   We'll suppress the error and tell the caller everything's
+		   OK.  Because it is. */
+		if (!emptymatch) ret = doc_decode_ecc (dat, calc_ecc);
+		if (ret > 0)
+			printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
+	}	
+	if (DoC_is_MillenniumPlus(doc))
+		WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf);
+	else
+		WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
+	if (no_ecc_failures && (ret == -1)) {
+		printk(KERN_ERR "suppressing ECC failure\n");
+		ret = 0;
+	}
+	return ret;
+}
+		
+//u_char mydatabuf[528];
+
+static struct nand_oobinfo doc200x_oobinfo = {
+        .useecc = MTD_NANDECC_AUTOPLACE,
+        .eccbytes = 6,
+        .eccpos = {0, 1, 2, 3, 4, 5},
+        .oobfree = { {8, 8} }
+};
+ 
+/* Find the (I)NFTL Media Header, and optionally also the mirror media header.
+   On sucessful return, buf will contain a copy of the media header for
+   further processing.  id is the string to scan for, and will presumably be
+   either "ANAND" or "BNAND".  If findmirror=1, also look for the mirror media
+   header.  The page #s of the found media headers are placed in mh0_page and
+   mh1_page in the DOC private structure. */
+static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,
+				     const char *id, int findmirror)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift);
+	int ret;
+	size_t retlen;
+
+	end = min(end, mtd->size); // paranoia
+	for (offs = 0; offs < end; offs += mtd->erasesize) {
+		ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf);
+		if (retlen != mtd->oobblock) continue;
+		if (ret) {
+			printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n",
+				offs);
+		}
+		if (memcmp(buf, id, 6)) continue;
+		printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs);
+		if (doc->mh0_page == -1) {
+			doc->mh0_page = offs >> this->page_shift;
+			if (!findmirror) return 1;
+			continue;
+		}
+		doc->mh1_page = offs >> this->page_shift;
+		return 2;
+	}
+	if (doc->mh0_page == -1) {
+		printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id);
+		return 0;
+	}
+	/* Only one mediaheader was found.  We want buf to contain a
+	   mediaheader on return, so we'll have to re-read the one we found. */
+	offs = doc->mh0_page << this->page_shift;
+	ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf);
+	if (retlen != mtd->oobblock) {
+		/* Insanity.  Give up. */
+		printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n");
+		return 0;
+	}
+	return 1;
+}
+
+static inline int __init nftl_partscan(struct mtd_info *mtd,
+				struct mtd_partition *parts)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	int ret = 0;
+	u_char *buf;
+	struct NFTLMediaHeader *mh;
+	const unsigned psize = 1 << this->page_shift;
+	unsigned blocks, maxblocks;
+	int offs, numheaders;
+
+	buf = (u_char *) kmalloc(mtd->oobblock, GFP_KERNEL);
+	if (!buf) {
+		printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n");
+		return 0;
+	}
+	if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out;
+	mh = (struct NFTLMediaHeader *) buf;
+
+//#ifdef CONFIG_MTD_DEBUG_VERBOSE
+//	if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
+	printk(KERN_INFO "    DataOrgID        = %s\n"
+			 "    NumEraseUnits    = %d\n"
+			 "    FirstPhysicalEUN = %d\n"
+			 "    FormattedSize    = %d\n"
+			 "    UnitSizeFactor   = %d\n",
+		mh->DataOrgID, mh->NumEraseUnits,
+		mh->FirstPhysicalEUN, mh->FormattedSize,
+		mh->UnitSizeFactor);
+//#endif
+
+	blocks = mtd->size >> this->phys_erase_shift;
+	maxblocks = min(32768U, mtd->erasesize - psize);
+
+	if (mh->UnitSizeFactor == 0x00) {
+		/* Auto-determine UnitSizeFactor.  The constraints are:
+		   - There can be at most 32768 virtual blocks.
+		   - There can be at most (virtual block size - page size)
+		     virtual blocks (because MediaHeader+BBT must fit in 1).
+		*/
+		mh->UnitSizeFactor = 0xff;
+		while (blocks > maxblocks) {
+			blocks >>= 1;
+			maxblocks = min(32768U, (maxblocks << 1) + psize);
+			mh->UnitSizeFactor--;
+		}
+		printk(KERN_WARNING "UnitSizeFactor=0x00 detected.  Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor);
+	}
+
+	/* NOTE: The lines below modify internal variables of the NAND and MTD
+	   layers; variables with have already been configured by nand_scan.
+	   Unfortunately, we didn't know before this point what these values
+	   should be.  Thus, this code is somewhat dependant on the exact
+	   implementation of the NAND layer.  */
+	if (mh->UnitSizeFactor != 0xff) {
+		this->bbt_erase_shift += (0xff - mh->UnitSizeFactor);
+		mtd->erasesize <<= (0xff - mh->UnitSizeFactor);
+		printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize);
+		blocks = mtd->size >> this->bbt_erase_shift;
+		maxblocks = min(32768U, mtd->erasesize - psize);
+	}
+
+	if (blocks > maxblocks) {
+		printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size.  Aborting.\n", mh->UnitSizeFactor);
+		goto out;
+	}
+
+	/* Skip past the media headers. */
+	offs = max(doc->mh0_page, doc->mh1_page);
+	offs <<= this->page_shift;
+	offs += mtd->erasesize;
+
+	//parts[0].name = " DiskOnChip Boot / Media Header partition";
+	//parts[0].offset = 0;
+	//parts[0].size = offs;
+
+	parts[0].name = " DiskOnChip BDTL partition";
+	parts[0].offset = offs;
+	parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift;
+
+	offs += parts[0].size;
+	if (offs < mtd->size) {
+		parts[1].name = " DiskOnChip Remainder partition";
+		parts[1].offset = offs;
+		parts[1].size = mtd->size - offs;
+		ret = 2;
+		goto out;
+	}
+	ret = 1;
+out:
+	kfree(buf);
+	return ret;
+}
+
+/* This is a stripped-down copy of the code in inftlmount.c */
+static inline int __init inftl_partscan(struct mtd_info *mtd,
+				 struct mtd_partition *parts)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	int ret = 0;
+	u_char *buf;
+	struct INFTLMediaHeader *mh;
+	struct INFTLPartition *ip;
+	int numparts = 0;
+	int blocks;
+	int vshift, lastvunit = 0;
+	int i;
+	int end = mtd->size;
+
+	if (inftl_bbt_write)
+		end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift);
+
+	buf = (u_char *) kmalloc(mtd->oobblock, GFP_KERNEL);
+	if (!buf) {
+		printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n");
+		return 0;
+	}
+
+	if (!find_media_headers(mtd, buf, "BNAND", 0)) goto out;
+	doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift);
+	mh = (struct INFTLMediaHeader *) buf;
+
+	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(KERN_INFO "    bootRecordID          = %s\n"
+			 "    NoOfBootImageBlocks   = %d\n"
+			 "    NoOfBinaryPartitions  = %d\n"
+			 "    NoOfBDTLPartitions    = %d\n"
+			 "    BlockMultiplerBits    = %d\n"
+			 "    FormatFlgs            = %d\n"
+			 "    OsakVersion           = %d.%d.%d.%d\n"
+			 "    PercentUsed           = %d\n",
+		mh->bootRecordID, mh->NoOfBootImageBlocks,
+		mh->NoOfBinaryPartitions,
+		mh->NoOfBDTLPartitions,
+		mh->BlockMultiplierBits, mh->FormatFlags,
+		((unsigned char *) &mh->OsakVersion)[0] & 0xf,
+		((unsigned char *) &mh->OsakVersion)[1] & 0xf,
+		((unsigned char *) &mh->OsakVersion)[2] & 0xf,
+		((unsigned char *) &mh->OsakVersion)[3] & 0xf,
+		mh->PercentUsed);
+//#endif
+
+	vshift = this->phys_erase_shift + mh->BlockMultiplierBits;
+
+	blocks = mtd->size >> vshift;
+	if (blocks > 32768) {
+		printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size.  Aborting.\n", mh->BlockMultiplierBits);
+		goto out;
+	}
+
+	blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift);
+	if (inftl_bbt_write && (blocks > mtd->erasesize)) {
+		printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported.  FIX ME!\n");
+		goto out;
+	}
+
+	/* 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(KERN_INFO	"    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 ((i == 0) && (ip->firstUnit > 0)) {
+			parts[0].name = " DiskOnChip IPL / Media Header partition";
+			parts[0].offset = 0;
+			parts[0].size = mtd->erasesize * ip->firstUnit;
+			numparts = 1;
+		}
+*/
+
+		if (ip->flags & INFTL_BINARY)
+			parts[numparts].name = " DiskOnChip BDK partition";
+		else
+			parts[numparts].name = " DiskOnChip BDTL partition";
+		parts[numparts].offset = ip->firstUnit << vshift;
+		parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift;
+		numparts++;
+		if (ip->lastUnit > lastvunit) lastvunit = ip->lastUnit;
+		if (ip->flags & INFTL_LAST) break;
+	}
+	lastvunit++;
+	if ((lastvunit << vshift) < end) {
+		parts[numparts].name = " DiskOnChip Remainder partition";
+		parts[numparts].offset = lastvunit << vshift;
+		parts[numparts].size = end - parts[numparts].offset;
+		numparts++;
+	}
+	ret = numparts;
+out:
+	kfree(buf);
+	return ret;
+}
+
+static int __init nftl_scan_bbt(struct mtd_info *mtd)
+{
+	int ret, numparts;
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	struct mtd_partition parts[2];
+
+	memset((char *) parts, 0, sizeof(parts));
+	/* On NFTL, we have to find the media headers before we can read the
+	   BBTs, since they're stored in the media header eraseblocks. */
+	numparts = nftl_partscan(mtd, parts);
+	if (!numparts) return -EIO;
+	this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
+				NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
+				NAND_BBT_VERSION;
+	this->bbt_td->veroffs = 7;
+	this->bbt_td->pages[0] = doc->mh0_page + 1;
+	if (doc->mh1_page != -1) {
+		this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
+					NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
+					NAND_BBT_VERSION;
+		this->bbt_md->veroffs = 7;
+		this->bbt_md->pages[0] = doc->mh1_page + 1;
+	} else {
+		this->bbt_md = NULL;
+	}
+
+	/* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
+	   At least as nand_bbt.c is currently written. */
+	if ((ret = nand_scan_bbt(mtd, NULL)))
+		return ret;
+	add_mtd_device(mtd);
+#ifdef CONFIG_MTD_PARTITIONS
+	if (!no_autopart)
+		add_mtd_partitions(mtd, parts, numparts);
+#endif
+	return 0;
+}
+
+static int __init inftl_scan_bbt(struct mtd_info *mtd)
+{
+	int ret, numparts;
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+	struct mtd_partition parts[5];
+
+	if (this->numchips > doc->chips_per_floor) {
+		printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n");
+		return -EIO;
+	}
+
+	if (DoC_is_MillenniumPlus(doc)) {
+		this->bbt_td->options = NAND_BBT_2BIT | NAND_BBT_ABSPAGE;
+		if (inftl_bbt_write)
+			this->bbt_td->options |= NAND_BBT_WRITE;
+		this->bbt_td->pages[0] = 2;
+		this->bbt_md = NULL;
+	} else {
+		this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
+					NAND_BBT_VERSION;
+		if (inftl_bbt_write)
+			this->bbt_td->options |= NAND_BBT_WRITE;
+		this->bbt_td->offs = 8;
+		this->bbt_td->len = 8;
+		this->bbt_td->veroffs = 7;
+		this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
+		this->bbt_td->reserved_block_code = 0x01;
+		this->bbt_td->pattern = "MSYS_BBT";
+
+		this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
+					NAND_BBT_VERSION;
+		if (inftl_bbt_write)
+			this->bbt_md->options |= NAND_BBT_WRITE;
+		this->bbt_md->offs = 8;
+		this->bbt_md->len = 8;
+		this->bbt_md->veroffs = 7;
+		this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
+		this->bbt_md->reserved_block_code = 0x01;
+		this->bbt_md->pattern = "TBB_SYSM";
+	}
+
+	/* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
+	   At least as nand_bbt.c is currently written. */
+	if ((ret = nand_scan_bbt(mtd, NULL)))
+		return ret;
+	memset((char *) parts, 0, sizeof(parts));
+	numparts = inftl_partscan(mtd, parts);
+	/* At least for now, require the INFTL Media Header.  We could probably
+	   do without it for non-INFTL use, since all it gives us is
+	   autopartitioning, but I want to give it more thought. */
+	if (!numparts) return -EIO;
+	add_mtd_device(mtd);
+#ifdef CONFIG_MTD_PARTITIONS
+	if (!no_autopart)
+		add_mtd_partitions(mtd, parts, numparts);
+#endif
+	return 0;
+}
+
+static inline int __init doc2000_init(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+
+	this->write_byte = doc2000_write_byte;
+	this->read_byte = doc2000_read_byte;
+	this->write_buf = doc2000_writebuf;
+	this->read_buf = doc2000_readbuf;
+	this->verify_buf = doc2000_verifybuf;
+	this->scan_bbt = nftl_scan_bbt;
+
+	doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO;
+	doc2000_count_chips(mtd);
+	mtd->name = "DiskOnChip 2000 (NFTL Model)";
+	return (4 * doc->chips_per_floor);
+}
+
+static inline int __init doc2001_init(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+
+	this->write_byte = doc2001_write_byte;
+	this->read_byte = doc2001_read_byte;
+	this->write_buf = doc2001_writebuf;
+	this->read_buf = doc2001_readbuf;
+	this->verify_buf = doc2001_verifybuf;
+
+	ReadDOC(doc->virtadr, ChipID);
+	ReadDOC(doc->virtadr, ChipID);
+	ReadDOC(doc->virtadr, ChipID);
+	if (ReadDOC(doc->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(mtd);
+		mtd->name = "DiskOnChip 2000 (INFTL Model)";
+		this->scan_bbt = inftl_scan_bbt;
+		return (4 * doc->chips_per_floor);
+	} else {
+		/* Bog-standard Millennium */
+		doc->chips_per_floor = 1;
+		mtd->name = "DiskOnChip Millennium";
+		this->scan_bbt = nftl_scan_bbt;
+		return 1;
+	}
+}
+
+static inline int __init doc2001plus_init(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	struct doc_priv *doc = (void *)this->priv;
+
+	this->write_byte = NULL;
+	this->read_byte = doc2001plus_read_byte;
+	this->write_buf = doc2001plus_writebuf;
+	this->read_buf = doc2001plus_readbuf;
+	this->verify_buf = doc2001plus_verifybuf;
+	this->scan_bbt = inftl_scan_bbt;
+	this->hwcontrol = NULL;
+	this->select_chip = doc2001plus_select_chip;
+	this->cmdfunc = doc2001plus_command;
+	this->enable_hwecc = doc2001plus_enable_hwecc;
+
+	doc->chips_per_floor = 1;
+	mtd->name = "DiskOnChip Millennium Plus";
+
+	return 1;
+}
+
+static inline int __init doc_probe(unsigned long physadr)
+{
+	unsigned char ChipID;
+	struct mtd_info *mtd;
+	struct nand_chip *nand;
+	struct doc_priv *doc;
+	void __iomem *virtadr;
+	unsigned char save_control;
+	unsigned char tmp, tmpb, tmpc;
+	int reg, len, numchips;
+	int ret = 0;
+
+	virtadr = (void __iomem *)ioremap(physadr, DOC_IOREMAP_LEN);
+	if (!virtadr) {
+		printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr);
+		return -EIO;
+	}
+
+	/* It's not possible to cleanly detect the DiskOnChip - the
+	 * bootup procedure will put the device into reset mode, and
+	 * it's not possible to talk to it without actually writing
+	 * to the DOCControl register. So we store the current contents
+	 * of the DOCControl register's location, in case we later decide
+	 * that it's not a DiskOnChip, and want to put it back how we
+	 * found it. 
+	 */
+	save_control = ReadDOC(virtadr, DOCControl);
+
+	/* Reset the DiskOnChip ASIC */
+	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, 
+		 virtadr, DOCControl);
+	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, 
+		 virtadr, DOCControl);
+
+	/* Enable the DiskOnChip ASIC */
+	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, 
+		 virtadr, DOCControl);
+	WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, 
+		 virtadr, DOCControl);
+
+	ChipID = ReadDOC(virtadr, ChipID);
+
+	switch(ChipID) {
+	case DOC_ChipID_Doc2k:
+		reg = DoC_2k_ECCStatus;
+		break;
+	case DOC_ChipID_DocMil:
+		reg = DoC_ECCConf;
+		break;
+	case DOC_ChipID_DocMilPlus16:
+	case DOC_ChipID_DocMilPlus32:
+	case 0:
+		/* Possible Millennium Plus, need to do more checks */
+		/* Possibly release from power down mode */
+		for (tmp = 0; (tmp < 4); tmp++)
+			ReadDOC(virtadr, Mplus_Power);
+
+		/* Reset the Millennium Plus ASIC */
+		tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
+			DOC_MODE_BDECT;
+		WriteDOC(tmp, virtadr, Mplus_DOCControl);
+		WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
+
+		mdelay(1);
+		/* Enable the Millennium Plus ASIC */
+		tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT |
+			DOC_MODE_BDECT;
+		WriteDOC(tmp, virtadr, Mplus_DOCControl);
+		WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
+		mdelay(1);
+
+		ChipID = ReadDOC(virtadr, ChipID);
+
+		switch (ChipID) {
+		case DOC_ChipID_DocMilPlus16:
+			reg = DoC_Mplus_Toggle;
+			break;
+		case DOC_ChipID_DocMilPlus32:
+			printk(KERN_ERR "DiskOnChip Millennium Plus 32MB is not supported, ignoring.\n");
+		default:
+			ret = -ENODEV;
+			goto notfound;
+		}
+		break;
+
+	default:
+		ret = -ENODEV;
+		goto notfound;
+	}
+	/* Check the TOGGLE bit in the ECC register */
+	tmp  = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+	tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+	tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
+	if ((tmp == tmpb) || (tmp != tmpc)) {
+		printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr);
+		ret = -ENODEV;
+		goto notfound;
+	}
+
+	for (mtd = doclist; mtd; mtd = doc->nextdoc) {
+		unsigned char oldval;
+		unsigned char newval;
+		nand = mtd->priv;
+		doc = (void *)nand->priv;
+		/* Use the alias resolution register to determine if this is
+		   in fact the same DOC aliased to a new address.  If writes
+		   to one chip's alias resolution register change the value on
+		   the other chip, they're the same chip. */
+		if (ChipID == DOC_ChipID_DocMilPlus16) {
+			oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
+			newval = ReadDOC(virtadr, Mplus_AliasResolution);
+		} else {
+			oldval = ReadDOC(doc->virtadr, AliasResolution);
+			newval = ReadDOC(virtadr, AliasResolution);
+		}
+		if (oldval != newval)
+			continue;
+		if (ChipID == DOC_ChipID_DocMilPlus16) {
+			WriteDOC(~newval, virtadr, Mplus_AliasResolution);
+			oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
+			WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it
+		} else {
+			WriteDOC(~newval, virtadr, AliasResolution);
+			oldval = ReadDOC(doc->virtadr, AliasResolution);
+			WriteDOC(newval, virtadr, AliasResolution); // restore it
+		}
+		newval = ~newval;
+		if (oldval == newval) {
+			printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr);
+			goto notfound;
+		}
+	}
+
+	printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr);
+
+	len = sizeof(struct mtd_info) +
+	      sizeof(struct nand_chip) +
+	      sizeof(struct doc_priv) +
+	      (2 * sizeof(struct nand_bbt_descr));
+	mtd = (struct mtd_info *) kmalloc(len, GFP_KERNEL);
+	if (!mtd) {
+		printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len);
+		ret = -ENOMEM;
+		goto fail;
+	}
+	memset(mtd, 0, len);
+
+	nand			= (struct nand_chip *) (mtd + 1);
+	doc			= (struct doc_priv *) (nand + 1);
+	nand->bbt_td		= (struct nand_bbt_descr *) (doc + 1);
+	nand->bbt_md		= nand->bbt_td + 1;
+
+	mtd->priv		= (void *) nand;
+	mtd->owner		= THIS_MODULE;
+
+	nand->priv		= (void *) doc;
+	nand->select_chip	= doc200x_select_chip;
+	nand->hwcontrol		= doc200x_hwcontrol;
+	nand->dev_ready		= doc200x_dev_ready;
+	nand->waitfunc		= doc200x_wait;
+	nand->block_bad		= doc200x_block_bad;
+	nand->enable_hwecc	= doc200x_enable_hwecc;
+	nand->calculate_ecc	= doc200x_calculate_ecc;
+	nand->correct_data	= doc200x_correct_data;
+
+	nand->autooob		= &doc200x_oobinfo;
+	nand->eccmode		= NAND_ECC_HW6_512;
+	nand->options		= NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME;
+
+	doc->physadr		= physadr;
+	doc->virtadr		= virtadr;
+	doc->ChipID		= ChipID;
+	doc->curfloor		= -1;
+	doc->curchip		= -1;
+	doc->mh0_page		= -1;
+	doc->mh1_page		= -1;
+	doc->nextdoc		= doclist;
+
+	if (ChipID == DOC_ChipID_Doc2k)
+		numchips = doc2000_init(mtd);
+	else if (ChipID == DOC_ChipID_DocMilPlus16)
+		numchips = doc2001plus_init(mtd);
+	else
+		numchips = doc2001_init(mtd);
+
+	if ((ret = nand_scan(mtd, numchips))) {
+		/* DBB note: i believe nand_release is necessary here, as
+		   buffers may have been allocated in nand_base.  Check with
+		   Thomas. FIX ME! */
+		/* nand_release will call del_mtd_device, but we haven't yet
+		   added it.  This is handled without incident by
+		   del_mtd_device, as far as I can tell. */
+		nand_release(mtd);
+		kfree(mtd);
+		goto fail;
+	}
+
+	/* Success! */
+	doclist = mtd;
+	return 0;
+
+notfound:
+	/* Put back the contents of the DOCControl register, in case it's not
+	   actually a DiskOnChip.  */
+	WriteDOC(save_control, virtadr, DOCControl);
+fail:
+	iounmap((void *)virtadr);
+	return ret;
+}
+
+int __init init_nanddoc(void)
+{
+	int i;
+
+	if (doc_config_location) {
+		printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location);
+		return doc_probe(doc_config_location);
+	} else {
+		for (i=0; (doc_locations[i] != 0xffffffff); i++) {
+			doc_probe(doc_locations[i]);
+		}
+	}
+	/* No banner message any more. Print a message if no DiskOnChip
+	   found, so the user knows we at least tried. */
+	if (!doclist) {
+		printk(KERN_INFO "No valid DiskOnChip devices found\n");
+		return -ENODEV;
+	}
+	return 0;
+}
+
+void __exit cleanup_nanddoc(void)
+{
+	struct mtd_info *mtd, *nextmtd;
+	struct nand_chip *nand;
+	struct doc_priv *doc;
+
+	for (mtd = doclist; mtd; mtd = nextmtd) {
+		nand = mtd->priv;
+		doc = (void *)nand->priv;
+
+		nextmtd = doc->nextdoc;
+		nand_release(mtd);
+		iounmap((void *)doc->virtadr);
+		kfree(mtd);
+	}
+}
+
+module_init(init_nanddoc);
+module_exit(cleanup_nanddoc);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
+MODULE_DESCRIPTION("M-Systems DiskOnChip 2000, Millennium and Millennium Plus device driver\n");
diff -uNrb linux-2.4.30/drivers/mtd/nand/edb7312.c linux/drivers/mtd/nand/edb7312.c
--- linux-2.4.30/drivers/mtd/nand/edb7312.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/nand/edb7312.c	Tue Sep 28 14:38:13 2004
@@ -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.9 2004/09/16 23:27:14 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
@@ -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>
@@ -52,9 +53,9 @@
  * Module stuff
  */
 
-static int ep7312_fio_pbase = EP7312_FIO_PBASE;
-static int ep7312_pxdr = EP7312_PXDR;
-static int ep7312_pxddr = EP7312_PXDDR;
+static unsigned long ep7312_fio_pbase = EP7312_FIO_PBASE;
+static void __iomem * ep7312_pxdr = (void __iomem *) EP7312_PXDR;
+static void __iomem * ep7312_pxddr = (void __iomem *) EP7312_PXDDR;
 
 #ifdef MODULE
 MODULE_PARM(ep7312_fio_pbase, "i");
@@ -71,22 +72,19 @@
  * Define static partitions for flash device
  */
 static struct mtd_partition partition_info[] = {
-	{ name: "EP7312 Nand Flash",
-		  offset: 0,
-		  size: 8*1024*1024 }
+	{ .name = "EP7312 Nand Flash",
+		  .offset = 0,
+		  .size = 8*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 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
@@ -130,10 +131,10 @@
 	const char *part_type = 0;
 	int mtd_parts_nb = 0;
 	struct mtd_partition *mtd_parts = 0;
-	int ep7312_fio_base;
+	void __iomem * ep7312_fio_base;
 	
 	/* Allocate memory for MTD device structure and private data */
-	ep7312_mtd = kmalloc(sizeof(struct mtd_info) + 
+	ep7312_mtd = (struct mtd_info *) kmalloc(sizeof(struct mtd_info) + 
 			     sizeof(struct nand_chip),
 			     GFP_KERNEL);
 	if (!ep7312_mtd) {
@@ -142,7 +143,7 @@
 	}
 	
 	/* map physical adress */
-	ep7312_fio_base = (unsigned long)ioremap(ep7312_fio_pbase, SZ_1K);
+	ep7312_fio_base = (void __iomem *)ioremap(ep7312_fio_pbase, SZ_1K);
 	if(!ep7312_fio_base) {
 		printk("ioremap EDB7312 NAND flash failed\n");
 		kfree(ep7312_mtd);
@@ -174,42 +175,22 @@
 	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;
 	}
 	
-	/* Allocate memory for internal data buffer */
-	this->data_buf = kmalloc (sizeof(u_char) * (ep7312_mtd->oobblock + ep7312_mtd->oobsize), GFP_KERNEL);
-	if (!this->data_buf) {
-		printk("Unable to allocate NAND data buffer for EDB7312.\n");
-		iounmap((void *)ep7312_fio_base);
-		kfree (ep7312_mtd);
-		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_MTD_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";
 	else
 	  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";
@@ -231,12 +212,11 @@
 {
 	struct nand_chip *this = (struct nand_chip *) &ep7312_mtd[1];
 	
-	/* Unregister the device */
-	del_mtd_device (ep7312_mtd);
+	/* Release resources, unregister device */
+	nand_release (ap7312_mtd);
 	
 	/* Free internal data buffer */
 	kfree (this->data_buf);
-	kfree (this->data_cache);
 	
 	/* Free the MTD device structure */
 	kfree (ep7312_mtd);
diff -uNrb linux-2.4.30/drivers/mtd/nand/h1910.c linux/drivers/mtd/nand/h1910.c
--- linux-2.4.30/drivers/mtd/nand/h1910.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/nand/h1910.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,208 @@
+/*
+ *  drivers/mtd/nand/h1910.c
+ *
+ *  Copyright (C) 2003 Joshua Wise (joshua@joshuawise.com)
+ *
+ *  Derived from drivers/mtd/nand/edb7312.c
+ *       Copyright (C) 2002 Marius Gröger (mag@sysgo.de)
+ *       Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
+ *
+ * $Id: h1910.c,v 1.3 2004/09/16 23:27:14 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 device found on the
+ *   iPAQ h1910 board which utilizes the Samsung K9F2808 part. This is
+ *   a 128Mibit (16MiB x 8 bits) NAND flash device.
+ */
+
+#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/arch/hardware.h> /* for CLPS7111_VIRT_BASE */
+#include <asm/sizes.h>
+#include <asm/arch/h1900-gpio.h>
+#include <asm/arch/ipaq.h>
+
+/*
+ * MTD structure for EDB7312 board
+ */
+static struct mtd_info *h1910_nand_mtd = NULL;
+
+/*
+ * Module stuff
+ */
+
+#ifdef CONFIG_MTD_PARTITIONS
+/*
+ * Define static partitions for flash device
+ */
+static struct mtd_partition partition_info[] = {
+	{ name: "h1910 NAND Flash",
+		  offset: 0,
+		  size: 16*1024*1024 }
+};
+#define NUM_PARTITIONS 1
+
+#endif
+
+
+/* 
+ *	hardware specific access to control-lines
+ */
+static void h1910_hwcontrol(struct mtd_info *mtd, int cmd) 
+{
+	struct nand_chip* this = (struct nand_chip *) (mtd->priv);
+	
+	switch(cmd) {
+		
+	case NAND_CTL_SETCLE: 
+		this->IO_ADDR_R |= (1 << 2);
+		this->IO_ADDR_W |= (1 << 2);
+		break;
+	case NAND_CTL_CLRCLE: 
+		this->IO_ADDR_R &= ~(1 << 2);
+		this->IO_ADDR_W &= ~(1 << 2);
+		break;
+		
+	case NAND_CTL_SETALE:
+		this->IO_ADDR_R |= (1 << 3);
+		this->IO_ADDR_W |= (1 << 3);
+		break;
+	case NAND_CTL_CLRALE:
+		this->IO_ADDR_R &= ~(1 << 3);
+		this->IO_ADDR_W &= ~(1 << 3);
+		break;
+		
+	case NAND_CTL_SETNCE:
+		break;
+	case NAND_CTL_CLRNCE:
+		break;
+	}
+}
+
+/*
+ *	read device ready pin
+ */
+#if 0
+static int h1910_device_ready(struct mtd_info *mtd)
+{
+	return (GPLR(55) & GPIO_bit(55));
+}
+#endif
+
+/*
+ * Main initialization routine
+ */
+static int __init h1910_init (void)
+{
+	struct nand_chip *this;
+	const char *part_type = 0;
+	int mtd_parts_nb = 0;
+	struct mtd_partition *mtd_parts = 0;
+	void __iomem *nandaddr;
+	
+	if (!machine_is_h1900())
+		return -ENODEV;
+		
+	nandaddr = (void __iomem *)__ioremap(0x08000000, 0x1000, 0, 1);
+	if (!nandaddr) {
+		printk("Failed to ioremap nand flash.\n");
+		return -ENOMEM;
+	}
+	
+	/* Allocate memory for MTD device structure and private data */
+	h1910_nand_mtd = (struct mtd_info *) kmalloc(sizeof(struct mtd_info) + 
+			     sizeof(struct nand_chip),
+			     GFP_KERNEL);
+	if (!h1910_nand_mtd) {
+		printk("Unable to allocate h1910 NAND MTD device structure.\n");
+		iounmap ((void *) nandaddr);
+		return -ENOMEM;
+	}
+	
+	/* Get pointer to private data */
+	this = (struct nand_chip *) (&h1910_nand_mtd[1]);
+	
+	/* Initialize structures */
+	memset((char *) h1910_nand_mtd, 0, sizeof(struct mtd_info));
+	memset((char *) this, 0, sizeof(struct nand_chip));
+	
+	/* Link the private data with the MTD structure */
+	h1910_nand_mtd->priv = this;
+	
+	/*
+	 * Enable VPEN
+	 */
+	GPSR(37) = GPIO_bit(37);
+	
+	/* insert callbacks */
+	this->IO_ADDR_R = nandaddr;
+	this->IO_ADDR_W = nandaddr;
+	this->hwcontrol = h1910_hwcontrol;
+	this->dev_ready = NULL;	/* unknown whether that was correct or not so we will just do it like this */
+	/* 15 us command delay time */
+	this->chip_delay = 50;
+	this->eccmode = NAND_ECC_SOFT;
+	this->options = NAND_NO_AUTOINCR;
+	
+	/* Scan to find existence of the device */
+	if (nand_scan (h1910_nand_mtd, 1)) {
+		printk(KERN_NOTICE "No NAND device - returning -ENXIO\n");
+		kfree (h1910_nand_mtd);
+		iounmap ((void *) nandaddr);
+		return -ENXIO;
+	}
+	
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+	mtd_parts_nb = parse_cmdline_partitions(h1910_nand_mtd, &mtd_parts, 
+						"h1910-nand");
+	if (mtd_parts_nb > 0)
+	  part_type = "command line";
+	else
+	  mtd_parts_nb = 0;
+#endif
+	if (mtd_parts_nb == 0)
+	{
+		mtd_parts = partition_info;
+		mtd_parts_nb = NUM_PARTITIONS;
+		part_type = "static";
+	}
+	
+	/* Register the partitions */
+	printk(KERN_NOTICE "Using %s partition definition\n", part_type);
+	add_mtd_partitions(h1910_nand_mtd, mtd_parts, mtd_parts_nb);
+	
+	/* Return happy */
+	return 0;
+}
+module_init(h1910_init);
+
+/*
+ * Clean up routine
+ */
+static void __exit h1910_cleanup (void)
+{
+	struct nand_chip *this = (struct nand_chip *) &h1910_nand_mtd[1];
+	
+	/* Release resources, unregister device */
+	nand_release (h1910_nand_mtd);
+
+	/* Release io resource */
+	iounmap ((void *) this->IO_ADDR_W);
+
+	/* Free the MTD device structure */
+	kfree (h1910_nand_mtd);
+}
+module_exit(h1910_cleanup);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Joshua Wise <joshua at joshuawise dot com>");
+MODULE_DESCRIPTION("NAND flash driver for iPAQ h1910");
diff -uNrb linux-2.4.30/drivers/mtd/nand/idt434.c linux/drivers/mtd/nand/idt434.c
--- linux-2.4.30/drivers/mtd/nand/idt434.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/nand/idt434.c	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,224 @@
+/*
+ *  drivers/mtd/nand/434.c, derived from ./spia.c, look there for blahblah
+ *
+ */
+
+#include <linux/kernel.h>
+#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>
+
+static struct mtd_info *idt434_mtd = NULL;
+
+
+#define NAND_BASE_VAL 0x0200
+#define NAND_BASE_ADDR 0xA2000000
+
+#define IDT434_REG_BASE 0xB8000000
+#define DEV0BASE 0x010000
+#define DEV0MASK 0x010004
+#define DEV0C 0x010008
+#define DEV0TC 0x01000C
+#define DEV1BASE 0x010010
+#define DEV1MASK 0x010014
+#define DEV1C 0x010018
+#define DEV1TC 0x01001C
+#define DEV2BASE 0x010020
+#define DEV2MASK 0x010024
+#define DEV2C 0x010028
+#define DEV2TC 0x01002C
+#define DEV3BASE 0x010030
+#define DEV3MASK 0x010034
+#define DEV3C 0x010038
+#define DEV3TC 0x01003C
+#define BTCS 0x010040
+#define BTCOMPARE 0x010044
+#define GPIOFUNC 0x050000
+#define GPIOCFG 0x050004
+#define GPIOD 0x050008
+#define GPIOILEVEL 0x05000C
+#define GPIOISTAT 0x050010
+#define GPIONMIEN 0x050014
+#define IMASK6 0x038038
+
+static int dummybbtscan(struct mtd_info *mtd) { return 0; }
+static int dummyblockbad(struct mtd_info *mtd, loff_t ofs, int getchip) { return 0; }
+static int dummyblockmarkbad(struct mtd_info *mtd, loff_t ofs) { return 0; }
+
+static struct nand_bbt_descr funnydescr = {
+        .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+                | NAND_BBT_2BIT | NAND_BBT_PERCHIP,
+        .offs = 0,
+        .len = 0,
+        .veroffs = 12,
+        .maxblocks = 4,
+        .pattern = 0
+};
+
+void setID434Reg(unsigned regOffs, unsigned bit, unsigned len, unsigned val) {
+	unsigned data = *(volatile unsigned *) (IDT434_REG_BASE + regOffs);
+	unsigned i = 0;
+	for (i = 0; i != len; ++i) {
+		if (val & (1 << i)) data |= (1 << (i + bit));
+		else data &= ~(1 << (i + bit));
+	}
+	*(volatile unsigned *) (IDT434_REG_BASE + regOffs) = data;
+}
+
+
+static void idt434_hwcontrol(struct mtd_info *mtd, int cmd){
+	switch(cmd) {
+	case NAND_CTL_SETCLE: setID434Reg(GPIOD, 9, 1, 1); break;
+	case NAND_CTL_CLRCLE: setID434Reg(GPIOD, 9, 1, 0); break;
+
+	case NAND_CTL_SETALE: setID434Reg(GPIOD, 10, 1, 1); break;
+	case NAND_CTL_CLRALE: setID434Reg(GPIOD, 10, 1, 0); break;
+
+	case NAND_CTL_SETNCE: setID434Reg(GPIOD, 8, 1, 0); break;
+	case NAND_CTL_CLRNCE: setID434Reg(GPIOD, 8, 1, 1); break;
+	}
+}
+
+static struct mtd_partition partition_info[] = {
+        { name: "IDT434 NAND Boot", offset: 0, size: 4 * 1024 * 1024 },
+        { name: "IDT434 NAND Main", offset: MTDPART_OFS_NXTBLK, size: MTDPART_SIZ_FULL }
+};
+
+#define NUM_PARTITIONS 2
+
+/*
+ * Main initialization routine
+ */
+int idt434_i(void) {
+	struct nand_chip *this;
+/*
+	printk("IDT434: %d\n", __LINE__);
+	*(volatile unsigned char *) NAND_BASE_ADDR = 0x00;
+	printk("IDT434: %d\n", __LINE__);
+	*(volatile unsigned char *) NAND_BASE_ADDR = 0x00;
+	printk("IDT434: %d\n", __LINE__);
+	printk("%d\n", *(volatile unsigned char *) NAND_BASE_ADDR);
+	printk("IDT434: %d\n", __LINE__);
+	printk("%d\n", *(volatile unsigned char *) NAND_BASE_ADDR);
+	printk("IDT434: %d\n", __LINE__);
+	*(volatile unsigned char *) NAND_BASE_ADDR = 0x00;
+	printk("IDT434: %d\n", __LINE__);
+	*(volatile unsigned char *) NAND_BASE_ADDR = 0x00;
+	printk("IDT434: %d\n", __LINE__);
+	printk("%d\n", *(volatile unsigned char *) NAND_BASE_ADDR);
+	printk("IDT434: %d\n", __LINE__);
+	printk("%d\n", *(volatile unsigned char *) NAND_BASE_ADDR);
+	printk("IDT434: %d\n", __LINE__);
+*/	
+	setID434Reg(DEV3BASE, 16, 16, NAND_BASE_VAL);
+	setID434Reg(DEV3MASK, 16, 16, 0xffff);
+/*
+	setID434Reg(DEV3C, 0, 2, 0);
+	setID434Reg(DEV3C, 2, 1, 1);
+	setID434Reg(DEV3C, 3, 1, 0);
+	setID434Reg(DEV3C, 4, 4, 0);
+	setID434Reg(DEV3C, 8, 4, 0);
+	setID434Reg(DEV3C, 12, 4, 0);
+	setID434Reg(DEV3C, 16, 6, 5);
+	setID434Reg(DEV3C, 22, 6, 5);
+	setID434Reg(DEV3C, 28, 1, 0);
+	setID434Reg(DEV3C, 29, 1, 0);
+	setID434Reg(DEV3C, 30, 1, 0);
+*/
+	setID434Reg(DEV3C, 0, 32, 0x04108324);
+
+	setID434Reg(DEV3TC, 0, 4, 4);
+	setID434Reg(DEV3TC, 4, 4, 4);
+	setID434Reg(DEV3TC, 8, 3, 2);
+	setID434Reg(DEV3TC, 11, 2, 2);
+	setID434Reg(IMASK6, 8, 3, 0);
+	setID434Reg(GPIOFUNC, 8, 1, 0);
+	setID434Reg(GPIOCFG, 8, 1, 1);
+	setID434Reg(GPIOD, 8, 1, 1);
+	setID434Reg(GPIOFUNC, 9, 1, 0);
+	setID434Reg(GPIOCFG, 9, 1, 1);
+	setID434Reg(GPIOFUNC, 10, 1, 0);
+	setID434Reg(GPIOCFG, 10, 1, 1);	
+
+	/* Allocate memory for MTD device structure and private data */
+	idt434_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
+				GFP_KERNEL);
+	if (!idt434_mtd) {
+		printk ("Unable to allocate IDT434 NAND MTD device structure.\n");
+		return -ENOMEM;
+	}
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *) (&idt434_mtd[1]);
+
+	/* Initialize structures */
+	memset((char *) idt434_mtd, 0, sizeof(struct mtd_info));
+	memset((char *) this, 0, sizeof(struct nand_chip));
+
+	/* Link the private data with the MTD structure */
+	idt434_mtd->priv = this;
+
+	/* Set address of NAND IO lines */
+	this->IO_ADDR_R = NAND_BASE_ADDR;
+	this->IO_ADDR_W = NAND_BASE_ADDR;
+	/* Set address of hardware control function */
+	this->hwcontrol = idt434_hwcontrol;
+	this->eccmode = NAND_ECC_SOFT;
+	/* 15 us command delay time */
+	this->chip_delay = 25;
+//	this->scan_bbt = dummybbtscan;
+//	this->block_bad = dummyblockbad;
+//	this->block_markbad = dummyblockmarkbad;
+
+	/* Scan to find existence of the device */
+	if (nand_scan (idt434_mtd, 1)) {
+		kfree (idt434_mtd);
+		return -ENXIO;
+	}
+
+	/* Allocate memory for internal data buffer */
+	this->data_buf = kmalloc (sizeof(u_char) * (idt434_mtd->oobblock + idt434_mtd->oobsize), GFP_KERNEL);
+	if (!this->data_buf) {
+		printk ("Unable to allocate NAND data buffer for IDT434.\n");
+		kfree (idt434_mtd);
+		return -ENOMEM;
+	}
+
+	/* Register the partitions */
+	add_mtd_partitions(idt434_mtd, partition_info, NUM_PARTITIONS);
+
+	/* Return happy */
+	return 0;
+}
+
+int __init idt434_init(void) {
+    if (idt434_i() < 0) printk("first try failed\n");
+    if (idt434_i() < 0) printk("second try failed\n");
+    printk("jok\n");
+}
+module_init(idt434_init);
+
+
+#ifdef MODULE
+static void __exit idt434_cleanup(void) {
+	struct nand_chip *this = (struct nand_chip *) &idt434_mtd[1];
+
+	/* Unregister the device */
+	del_mtd_device(idt434_mtd);
+
+	/* Free internal data buffer */
+	kfree (this->data_buf);
+
+	/* Free the MTD device structure */
+	kfree (idt434_mtd);
+}
+module_exit(idt434_cleanup);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com");
+MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on SPIA^H^H^H^H IDT434 board");
diff -uNrb linux-2.4.30/drivers/mtd/nand/nand_base.c linux/drivers/mtd/nand/nand_base.c
--- linux-2.4.30/drivers/mtd/nand/nand_base.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/nand/nand_base.c	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,2581 @@
+/*
+ *  drivers/mtd/nand.c
+ *
+ *  Overview:
+ *   This is the generic MTD driver for NAND flash devices. It should be
+ *   capable of working with almost all NAND chips currently available.
+ *   Basic support for AG-AND chips is provided.
+ *   
+ *	Additional technical information is available on
+ *	http://www.linux-mtd.infradead.org/tech/nand.html
+ *	
+ *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ * 		  2002 Thomas Gleixner (tglx@linutronix.de)
+ *
+ *  02-08-2004  tglx: support for strange chips, which cannot auto increment 
+ *		pages on read / read_oob
+ *
+ *  03-17-2004  tglx: Check ready before auto increment check. Simon Bayes
+ *		pointed this out, as he marked an auto increment capable chip
+ *		as NOAUTOINCR in the board driver.
+ *		Make reads over block boundaries work too
+ *
+ *  04-14-2004	tglx: first working version for 2k page size chips
+ *  
+ *  05-19-2004  tglx: Basic support for Renesas AG-AND chips
+ *
+ * Credits:
+ *	David Woodhouse for adding multichip support  
+ *	
+ *	Aleph One Ltd. and Toby Churchill Ltd. for supporting the
+ *	rework for 2K page size chips
+ *
+ * TODO:
+ *	Enable cached programming for 2k page size chips
+ *	Check, if mtd->ecctype should be set to MTD_ECC_HW
+ *	if we have HW ecc support.
+ *	The AG-AND chips have nice features for speed improvement,
+ *	which are not supported yet. Read / program 4 pages in one go.
+ *
+ * $Id: nand_base.c,v 1.116 2004/08/30 18:00:45 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.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/compatmac.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_MTD_PARTITIONS
+#include <linux/mtd/partitions.h>
+#endif
+
+/* Define default oob placement schemes for large and small page devices */
+static struct nand_oobinfo nand_oob_8 = {
+	.useecc = MTD_NANDECC_AUTOPLACE,
+	.eccbytes = 3,
+	.eccpos = {0, 1, 2},
+	.oobfree = { {3, 2}, {6, 2} }
+};
+
+static struct nand_oobinfo nand_oob_16 = {
+	.useecc = MTD_NANDECC_AUTOPLACE,
+	.eccbytes = 6,
+	.eccpos = {0, 1, 2, 3, 6, 7},
+	.oobfree = { {8, 8} }
+};
+
+static struct nand_oobinfo nand_oob_64 = {
+	.useecc = MTD_NANDECC_AUTOPLACE,
+	.eccbytes = 24,
+	.eccpos = {
+		40, 41, 42, 43, 44, 45, 46, 47, 
+		48, 49, 50, 51, 52, 53, 54, 55, 
+		56, 57, 58, 59, 60, 61, 62, 63},
+	.oobfree = { {2, 38} }
+};
+
+/* This is used for padding purposes in nand_write_oob */
+static u_char ffchars[] = {
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
+};
+
+/*
+ * NAND low-level MTD interface functions
+ */
+static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len);
+static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len);
+static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len);
+
+static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
+static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+			  size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
+static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
+static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf);
+static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+			   size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel);
+static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf);
+static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs,
+			unsigned long count, loff_t to, size_t * retlen);
+static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs,
+			unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
+static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
+static void nand_sync (struct mtd_info *mtd);
+
+/* Some internal functions */
+static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,
+		struct nand_oobinfo *oobsel, int mode);
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, 
+	u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode);
+#else
+#define nand_verify_pages(...) (0)
+#endif
+		
+static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state);
+
+/**
+ * nand_release_chip - [GENERIC] release chip
+ * @mtd:	MTD device structure
+ * 
+ * Deselect, release chip lock 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);
+}
+
+/**
+ * nand_read_byte - [DEFAULT] read one byte from the chip
+ * @mtd:	MTD device structure
+ *
+ * Default read function for 8bit buswith
+ */
+static u_char nand_read_byte(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	return readb(this->IO_ADDR_R);
+}
+
+/**
+ * nand_write_byte - [DEFAULT] write one byte to the chip
+ * @mtd:	MTD device structure
+ * @byte:	pointer to data byte to write
+ *
+ * Default write function for 8it buswith
+ */
+static void nand_write_byte(struct mtd_info *mtd, u_char byte)
+{
+	struct nand_chip *this = mtd->priv;
+	writeb(byte, this->IO_ADDR_W);
+}
+
+/**
+ * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip
+ * @mtd:	MTD device structure
+ *
+ * Default read function for 16bit buswith with 
+ * endianess conversion
+ */
+static u_char nand_read_byte16(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	return (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
+}
+
+/**
+ * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip
+ * @mtd:	MTD device structure
+ * @byte:	pointer to data byte to write
+ *
+ * Default write function for 16bit buswith with
+ * endianess conversion
+ */
+static void nand_write_byte16(struct mtd_info *mtd, u_char byte)
+{
+	struct nand_chip *this = mtd->priv;
+	writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
+}
+
+/**
+ * nand_read_word - [DEFAULT] read one word from the chip
+ * @mtd:	MTD device structure
+ *
+ * Default read function for 16bit buswith without 
+ * endianess conversion
+ */
+static u16 nand_read_word(struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	return readw(this->IO_ADDR_R);
+}
+
+/**
+ * nand_write_word - [DEFAULT] write one word to the chip
+ * @mtd:	MTD device structure
+ * @word:	data word to write
+ *
+ * Default write function for 16bit buswith without 
+ * endianess conversion
+ */
+static void nand_write_word(struct mtd_info *mtd, u16 word)
+{
+	struct nand_chip *this = mtd->priv;
+	writew(word, this->IO_ADDR_W);
+}
+
+/**
+ * nand_select_chip - [DEFAULT] control CE line
+ * @mtd:	MTD device structure
+ * @chip:	chipnumber to select, -1 for deselect
+ *
+ * Default select function for 1 chip devices.
+ */
+static void nand_select_chip(struct mtd_info *mtd, int chip)
+{
+	struct nand_chip *this = mtd->priv;
+	switch(chip) {
+	case -1:
+		this->hwcontrol(mtd, NAND_CTL_CLRNCE);	
+		break;
+	case 0:
+		this->hwcontrol(mtd, NAND_CTL_SETNCE);
+		break;
+
+	default:
+		BUG();
+	}
+}
+
+/**
+ * nand_write_buf - [DEFAULT] write buffer to chip
+ * @mtd:	MTD device structure
+ * @buf:	data buffer
+ * @len:	number of bytes to write
+ *
+ * Default write function for 8bit buswith
+ */
+static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+
+	for (i=0; i<len; i++)
+		writeb(buf[i], this->IO_ADDR_W);
+}
+
+/**
+ * nand_read_buf - [DEFAULT] read chip data into buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer to store date
+ * @len:	number of bytes to read
+ *
+ * Default read function for 8bit buswith
+ */
+static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+
+	for (i=0; i<len; i++)
+		buf[i] = readb(this->IO_ADDR_R);
+}
+
+/**
+ * nand_verify_buf - [DEFAULT] Verify chip data against buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer containing the data to compare
+ * @len:	number of bytes to compare
+ *
+ * Default verify function for 8bit buswith
+ */
+static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+
+	for (i=0; i<len; i++)
+		if (buf[i] != readb(this->IO_ADDR_R))
+			return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * nand_write_buf16 - [DEFAULT] write buffer to chip
+ * @mtd:	MTD device structure
+ * @buf:	data buffer
+ * @len:	number of bytes to write
+ *
+ * Default write function for 16bit buswith
+ */
+static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+	
+	for (i=0; i<len; i++)
+		writew(p[i], this->IO_ADDR_W);
+		
+}
+
+/**
+ * nand_read_buf16 - [DEFAULT] read chip data into buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer to store date
+ * @len:	number of bytes to read
+ *
+ * Default read function for 16bit buswith
+ */
+static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+
+	for (i=0; i<len; i++)
+		p[i] = readw(this->IO_ADDR_R);
+}
+
+/**
+ * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer 
+ * @mtd:	MTD device structure
+ * @buf:	buffer containing the data to compare
+ * @len:	number of bytes to compare
+ *
+ * Default verify function for 16bit buswith
+ */
+static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
+{
+	int i;
+	struct nand_chip *this = mtd->priv;
+	u16 *p = (u16 *) buf;
+	len >>= 1;
+
+	for (i=0; i<len; i++)
+		if (p[i] != readw(this->IO_ADDR_R))
+			return -EFAULT;
+
+	return 0;
+}
+
+/**
+ * nand_block_bad - [DEFAULT] Read bad block marker from the chip
+ * @mtd:	MTD device structure
+ * @ofs:	offset from device start
+ * @getchip:	0, if the chip is already selected
+ *
+ * Check, if the block is bad. 
+ */
+static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
+{
+	int page, chipnr, res = 0;
+	struct nand_chip *this = mtd->priv;
+	u16 bad;
+
+	if (getchip) {
+		page = (int)(ofs >> this->page_shift);
+		chipnr = (int)(ofs >> this->chip_shift);
+
+		/* Grab the lock and see if the device is available */
+		nand_get_chip (this, mtd, FL_READING);
+
+		/* Select the NAND device */
+		this->select_chip(mtd, chipnr);
+	} else 
+		page = (int) ofs;	
+
+	if (this->options & NAND_BUSWIDTH_16) {
+		this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask);
+		bad = cpu_to_le16(this->read_word(mtd));
+		if (this->badblockpos & 0x1)
+			bad >>= 1;
+		if ((bad & 0xFF) != 0xff)
+			res = 1;
+	} else {
+		this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask);
+		if (this->read_byte(mtd) != 0xff)
+			res = 1;
+	}
+		
+	if (getchip) {
+		/* Deselect and wake up anyone waiting on the device */
+		nand_release_chip(mtd);
+	}	
+	
+	return res;
+}
+
+/**
+ * nand_default_block_markbad - [DEFAULT] mark a block bad
+ * @mtd:	MTD device structure
+ * @ofs:	offset from device start
+ *
+ * This is the default implementation, which can be overridden by
+ * a hardware specific driver.
+*/
+static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+	struct nand_chip *this = mtd->priv;
+	u_char buf[2] = {0, 0};
+	size_t	retlen;
+	int block;
+	
+	/* Get block number */
+	block = ((int) ofs) >> this->bbt_erase_shift;
+	this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+	/* Do we have a flash based bad block table ? */
+	if (this->options & NAND_USE_FLASH_BBT)
+		return nand_update_bbt (mtd, ofs);
+		
+	/* We write two bytes, so we dont have to mess with 16 bit access */
+	ofs += mtd->oobsize + (this->badblockpos & ~0x01);
+	return nand_write_oob (mtd, ofs , 2, &retlen, buf);
+}
+
+/** 
+ * nand_check_wp - [GENERIC] check if the chip is write protected
+ * @mtd:	MTD device structure
+ * Check, if the device is write protected 
+ *
+ * The function expects, that the device is already selected 
+ */
+static int nand_check_wp (struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	/* Check the WP bit */
+	this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+	return (this->read_byte(mtd) & 0x80) ? 0 : 1; 
+}
+
+/**
+ * nand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * @mtd:	MTD device structure
+ * @ofs:	offset from device start
+ * @getchip:	0, if the chip is already selected
+ * @allowbbt:	1, if its allowed to access the bbt area
+ *
+ * Check, if the block is bad. Either by reading the bad block table or
+ * calling of the scan function.
+ */
+static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt)
+{
+	struct nand_chip *this = mtd->priv;
+	
+	if (!this->bbt)
+		return this->block_bad(mtd, ofs, getchip);
+	
+	/* Return info from the table */
+	return nand_isbad_bbt (mtd, ofs, allowbbt);
+}
+
+/**
+ * nand_command - [DEFAULT] Send command to NAND device
+ * @mtd:	MTD device structure
+ * @command:	the command to be sent
+ * @column:	the column address for this command, -1 if none
+ * @page_addr:	the page address for this command, -1 if none
+ *
+ * Send command to NAND device. This function is used for small page
+ * devices (256/512 Bytes per page)
+ */
+static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+	register struct nand_chip *this = mtd->priv;
+
+	/* Begin command latch cycle */
+	this->hwcontrol(mtd, NAND_CTL_SETCLE);
+	/*
+	 * Write out the command to the device.
+	 */
+	if (command == NAND_CMD_SEQIN) {
+		int readcmd;
+
+		if (column >= mtd->oobblock) {
+			/* OOB area */
+			column -= mtd->oobblock;
+			readcmd = NAND_CMD_READOOB;
+		} else if (column < 256) {
+			/* First 256 bytes --> READ0 */
+			readcmd = NAND_CMD_READ0;
+		} else {
+			column -= 256;
+			readcmd = NAND_CMD_READ1;
+		}
+		this->write_byte(mtd, readcmd);
+	}
+	this->write_byte(mtd, command);
+
+	/* Set ALE and clear CLE to start address cycle */
+	this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+
+	if (column != -1 || page_addr != -1) {
+		this->hwcontrol(mtd, NAND_CTL_SETALE);
+
+		/* Serially input address */
+		if (column != -1) {
+			/* Adjust columns for 16 bit buswidth */
+			if (this->options & NAND_BUSWIDTH_16)
+				column >>= 1;
+			this->write_byte(mtd, column);
+		}
+		if (page_addr != -1) {
+			this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
+			this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
+			/* One more address cycle for higher density devices */
+			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;
+		}	
+	}
+	
+	/* Apply this short delay always to ensure that we do wait tWB in
+	 * any case on any machine. */
+	ndelay (100);
+	/* wait until command is processed */
+	while (!this->dev_ready(mtd));
+}
+
+/**
+ * nand_command_lp - [DEFAULT] Send command to NAND large page device
+ * @mtd:	MTD device structure
+ * @command:	the command to be sent
+ * @column:	the column address for this command, -1 if none
+ * @page_addr:	the page address for this command, -1 if none
+ *
+ * Send command to NAND device. This is the version for the new large page devices
+ * We dont have the seperate regions as we have in the small page devices.
+ * We must emulate NAND_CMD_READOOB to keep the code compatible.
+ *
+ */
+static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr)
+{
+	register struct nand_chip *this = mtd->priv;
+
+	/* Emulate NAND_CMD_READOOB */
+	if (command == NAND_CMD_READOOB) {
+		column += mtd->oobblock;
+		command = NAND_CMD_READ0;
+	}
+	
+		
+	/* Begin command latch cycle */
+	this->hwcontrol(mtd, NAND_CTL_SETCLE);
+	/* Write out the command to the device. */
+	this->write_byte(mtd, command);
+	/* End command latch cycle */
+	this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+
+	if (column != -1 || page_addr != -1) {
+		this->hwcontrol(mtd, NAND_CTL_SETALE);
+
+		/* Serially input address */
+		if (column != -1) {
+			/* Adjust columns for 16 bit buswidth */
+			if (this->options & NAND_BUSWIDTH_16)
+				column >>= 1;
+			this->write_byte(mtd, column & 0xff);
+			this->write_byte(mtd, column >> 8);
+		}	
+		if (page_addr != -1) {
+			this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
+			this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
+			/* One more address cycle for devices > 128MiB */
+			if (this->chipsize > (128 << 20))
+				this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff));
+		}
+		/* Latch in address */
+		this->hwcontrol(mtd, NAND_CTL_CLRALE);
+	}
+	
+	/* 
+	 * program and erase have their own busy handlers 
+	 * status and sequential in needs no delay
+	*/
+	switch (command) {
+			
+	case NAND_CMD_CACHEDPROG:
+	case NAND_CMD_PAGEPROG:
+	case NAND_CMD_ERASE1:
+	case NAND_CMD_ERASE2:
+	case NAND_CMD_SEQIN:
+	case NAND_CMD_STATUS:
+		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;
+
+	case NAND_CMD_READ0:
+		/* Begin command latch cycle */
+		this->hwcontrol(mtd, NAND_CTL_SETCLE);
+		/* Write out the start read command */
+		this->write_byte(mtd, NAND_CMD_READSTART);
+		/* End command latch cycle */
+		this->hwcontrol(mtd, NAND_CTL_CLRCLE);
+		/* Fall through into ready check */
+		
+	/* This applies to read commands */	
+	default:
+		/* 
+		 * If we don't have access to the busy pin, we apply the given
+		 * command delay
+		*/
+		if (!this->dev_ready) {
+			udelay (this->chip_delay);
+			return;
+		}	
+	}
+	
+	/* Apply this short delay always to ensure that we do wait tWB in
+	 * any case on any machine. */
+	ndelay (100);
+	/* wait until command is processed */
+	while (!this->dev_ready(mtd));
+}
+
+/**
+ * nand_get_chip - [GENERIC] Get chip for selected access
+ * @this:	the nand chip descriptor
+ * @mtd:	MTD device structure
+ * @new_state:	the state which is requested 
+ *
+ * Get the device and lock it for exclusive access
+ */
+static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state)
+{
+
+	DECLARE_WAITQUEUE (wait, current);
+
+	/* 
+	 * Grab the lock and see if the device is available 
+	*/
+retry:
+	spin_lock_bh (&this->chip_lock);
+
+	if (this->state == FL_READY) {
+		this->state = new_state;
+		spin_unlock_bh (&this->chip_lock);
+		return;
+	}
+
+	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 retry;
+}
+
+/**
+ * nand_wait - [DEFAULT]  wait until the command is done
+ * @mtd:	MTD device structure
+ * @this:	NAND chip structure
+ * @state:	state to select the max. timeout value
+ *
+ * Wait for command done. This applies to erase and program only
+ * Erase can take up to 400ms and program up to 20ms according to 
+ * general NAND and SmartMedia specs
+ *
+*/
+static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
+{
+
+	unsigned long	timeo = jiffies;
+	int	status;
+	
+	if (state == FL_ERASING)
+		 timeo += (HZ * 400) / 1000;
+	else
+		 timeo += (HZ * 20) / 1000;
+
+	/* Apply this short delay always to ensure that we do wait tWB in
+	 * any case on any machine. */
+	ndelay (100);
+
+	spin_lock_bh (&this->chip_lock);
+	if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
+		this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1);
+	else	
+		this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
+
+	while (time_before(jiffies, timeo)) {		
+		/* Check, if we were interrupted */
+		if (this->state != state) {
+			spin_unlock_bh (&this->chip_lock);
+			return 0;
+		}
+		if (this->dev_ready) {
+			if (this->dev_ready(mtd))
+				break;	
+		} else {
+			if (this->read_byte(mtd) & NAND_STATUS_READY)
+				break;
+		}
+		spin_unlock_bh (&this->chip_lock);
+		yield ();
+		spin_lock_bh (&this->chip_lock);
+	}
+	status = (int) this->read_byte(mtd);
+	spin_unlock_bh (&this->chip_lock);
+
+	return status;
+}
+
+/**
+ * nand_write_page - [GENERIC] write one page
+ * @mtd:	MTD device structure
+ * @this:	NAND chip structure
+ * @page: 	startpage inside the chip, must be called with (page & this->pagemask)
+ * @oob_buf:	out of band data buffer
+ * @oobsel:	out of band selecttion structre
+ * @cached:	1 = enable cached programming if supported by chip
+ *
+ * Nand_page_program function is used for write and writev !
+ * This function will always program a full page of data
+ * If you call it with a non page aligned buffer, you're lost :)
+ *
+ * Cached programming is not supported yet.
+ */
+static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, 
+	u_char *oob_buf,  struct nand_oobinfo *oobsel, int cached)
+{
+	int 	i, status;
+	u_char	ecc_code[8];
+	int	eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
+	int  	*oob_config = oobsel->eccpos;
+	int	datidx = 0, eccidx = 0, eccsteps = this->eccsteps;
+	int	eccbytes = 0;
+	
+	/* FIXME: Enable cached programming */
+	cached = 0;
+	
+	/* Send command to begin auto page programming */
+	this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);
+
+	/* Write out complete page of data, take care of eccmode */
+	switch (eccmode) {
+	/* No ecc, write all */
+	case NAND_ECC_NONE:
+		printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
+		this->write_buf(mtd, this->data_poi, mtd->oobblock);
+		break;
+		
+	/* Software ecc 3/256, write all */
+	case NAND_ECC_SOFT:
+		for (; eccsteps; eccsteps--) {
+			this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
+			for (i = 0; i < 3; i++, eccidx++)
+				oob_buf[oob_config[eccidx]] = ecc_code[i];
+			datidx += this->eccsize;
+		}
+		this->write_buf(mtd, this->data_poi, mtd->oobblock);
+		break;
+		
+	/* Hardware ecc 8 byte / 512 byte data */	
+	case NAND_ECC_HW8_512:	
+		eccbytes += 2;
+	/* Hardware ecc 6 byte / 512 byte data */	
+	case NAND_ECC_HW6_512:	
+		eccbytes += 3;
+	/* Hardware ecc 3 byte / 256 data */	
+	/* Hardware ecc 3 byte / 512 byte data */	
+	case NAND_ECC_HW3_256:		
+	case NAND_ECC_HW3_512:
+		eccbytes += 3;
+		for (; eccsteps; eccsteps--) {
+			/* enable hardware ecc logic for write */
+			this->enable_hwecc(mtd, NAND_ECC_WRITE);
+			this->write_buf(mtd, &this->data_poi[datidx], this->eccsize);
+			this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
+			for (i = 0; i < eccbytes; i++, eccidx++)
+				oob_buf[oob_config[eccidx]] = ecc_code[i];
+			/* If the hardware ecc provides syndromes then
+			 * the ecc code must be written immidiately after
+			 * the data bytes (words) */
+			if (this->options & NAND_HWECC_SYNDROME)
+				this->write_buf(mtd, ecc_code, eccbytes);
+
+			datidx += this->eccsize;
+		}
+		break;
+
+	default:
+		printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
+		BUG();	
+	}
+										
+	/* Write out OOB data */
+	if (this->options & NAND_HWECC_SYNDROME)
+		this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes);
+	else 
+		this->write_buf(mtd, oob_buf, mtd->oobsize);
+
+	/* Send command to actually program the data */
+	this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1);
+
+	if (!cached) {
+		/* call wait ready function */
+		status = this->waitfunc (mtd, this, FL_WRITING);
+		/* See if 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
+/**
+ * nand_verify_pages - [GENERIC] verify the chip contents after a write
+ * @mtd:	MTD device structure
+ * @this:	NAND chip structure
+ * @page: 	startpage inside the chip, must be called with (page & this->pagemask)
+ * @numpages:	number of pages to verify
+ * @oob_buf:	out of band data buffer
+ * @oobsel:	out of band selecttion structre
+ * @chipnr:	number of the current chip
+ * @oobmode:	1 = full buffer verify, 0 = ecc only
+ *
+ * The NAND device assumes that it is always writing to a cleanly erased page.
+ * Hence, it performs its internal write verification only on bits that 
+ * transitioned from 1 to 0. The device does NOT verify the whole page on a
+ * byte by byte basis. It is possible that the page was not completely erased 
+ * or the page is becoming unusable due to wear. The read with ECC would catch 
+ * the error later when the ECC page check fails, but we would rather catch 
+ * it early in the page write stage. Better to write no data than invalid data.
+ */
+static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages, 
+	u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode)
+{
+	int 	i, j, datidx = 0, oobofs = 0, res = -EIO;
+	int	eccsteps = this->eccsteps;
+	int	hweccbytes; 
+	u_char 	oobdata[64];
+
+	hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0;
+
+	/* Send command to read back the first page */
+	this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
+
+	for(;;) {
+		for (j = 0; j < eccsteps; j++) {
+			/* Loop through and verify the data */
+			if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) {
+				DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+				goto out;
+			}
+			datidx += mtd->eccsize;
+			/* Have we a hw generator layout ? */
+			if (!hweccbytes)
+				continue;
+			if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) {
+				DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+				goto out;
+			}
+			oobofs += hweccbytes;
+		}
+
+		/* check, if we must compare all data or if we just have to
+		 * compare the ecc bytes
+		 */
+		if (oobmode) {
+			if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) {
+				DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
+				goto out;
+			}
+		} else {
+			/* Read always, else autoincrement fails */
+			this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps);
+
+			if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) {
+				int ecccnt = oobsel->eccbytes;
+		
+				for (i = 0; i < ecccnt; i++) {
+					int idx = oobsel->eccpos[i];
+					if (oobdata[idx] != oob_buf[oobofs + idx] ) {
+						DEBUG (MTD_DEBUG_LEVEL0,
+					       	"%s: Failed ECC write "
+						"verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i);
+						goto out;
+					}
+				}
+			}	
+		}
+		oobofs += mtd->oobsize - hweccbytes * eccsteps;
+		page++;
+		numpages--;
+
+		/* Apply delay or wait for ready/busy pin 
+		 * Do this before the AUTOINCR check, so no problems
+		 * arise if a chip which does auto increment
+		 * is marked as NOAUTOINCR by the board driver.
+		 * Do this also before returning, so the chip is
+		 * ready for the next command.
+		*/
+		if (!this->dev_ready) 
+			udelay (this->chip_delay);
+		else
+			while (!this->dev_ready(mtd));	
+
+		/* All done, return happy */
+		if (!numpages)
+			return 0;
+		
+			
+		/* Check, if the chip supports auto page increment */ 
+		if (!NAND_CANAUTOINCR(this))
+			this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+	}
+	/* 
+	 * Terminate the read command. We come here in case of an error
+	 * So we must issue a reset command.
+	 */
+out:	 
+	this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1);
+	return res;
+}
+#endif
+
+/**
+ * nand_read - [MTD Interface] MTD compability function for nand_read_ecc
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @len:	number of bytes to read
+ * @retlen:	pointer to variable to store the number of read bytes
+ * @buf:	the databuffer to put data
+ *
+ * This function simply calls nand_read_ecc with oob buffer and oobsel = NULL
+*/
+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, NULL);
+}			   
+
+
+/**
+ * nand_read_ecc - [MTD Interface] Read data with ECC
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @len:	number of bytes to read
+ * @retlen:	pointer to variable to store the number of read bytes
+ * @buf:	the databuffer to put data
+ * @oob_buf:	filesystem supplied oob data buffer
+ * @oobsel:	oob selection structure
+ *
+ * NAND read with ECC
+ */
+static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
+			  size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
+{
+	int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
+	int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
+	struct nand_chip *this = mtd->priv;
+	u_char *data_poi, *oob_data = oob_buf;
+	u_char ecc_calc[32];
+	u_char ecc_code[32];
+        int eccmode, eccsteps;
+	int	*oob_config, datidx;
+	int	blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+	int	eccbytes = 3;
+	int	compareecc = 1;
+	int	oobreadlen;
+
+
+	DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+
+	/* Do not allow reads past end of device */
+	if ((from + len) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n");
+		*retlen = 0;
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	nand_get_chip (this, mtd ,FL_READING);
+
+	/* 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 */
+	chipnr = (int)(from >> this->chip_shift);
+	this->select_chip(mtd, chipnr);
+
+	/* First we calculate the starting page */
+	realpage = (int) (from >> this->page_shift);
+	page = realpage & this->pagemask;
+
+	/* Get raw starting column */
+	col = from & (mtd->oobblock - 1);
+
+	end = mtd->oobblock;
+	ecc = this->eccsize;
+	switch (eccmode) {
+	case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data  */
+		eccbytes = 6;
+		break;						
+	case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data  */
+		eccbytes = 8;
+		break;
+	case NAND_ECC_NONE:
+		compareecc = 0;
+		break;						
+	}	 
+
+	if (this->options & NAND_HWECC_SYNDROME)
+		compareecc = 0;
+
+	oobreadlen = mtd->oobsize;
+	if (this->options & NAND_HWECC_SYNDROME) 
+		oobreadlen -= oobsel->eccbytes;
+
+	/* Loop until all data read */
+	while (read < len) {
+		
+		int aligned = (!col && (len - read) >= end);
+		/* 
+		 * If the read is not page aligned, we have to read into data buffer
+		 * due to ecc, else we read into return buffer direct
+		 */
+		if (aligned)
+			data_poi = &buf[read];
+		else 
+			data_poi = this->data_buf;
+		
+		/* Check, if we have this page in the buffer 
+		 *
+		 * FIXME: Make it work when we must provide oob data too,
+		 * check the usage of data_buf oob field
+		 */
+		if (realpage == this->pagebuf && !oob_buf) {
+			/* aligned read ? */
+			if (aligned)
+				memcpy (data_poi, this->data_buf, end);
+			goto readdata;
+		}
+
+		/* Check, if we must send the read command */
+		if (sndcmd) {
+			this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
+			sndcmd = 0;
+		}	
+
+		/* get oob area, if we have no oob buffer from fs-driver */
+		if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE)
+			oob_data = &this->data_buf[end];
+
+		eccsteps = this->eccsteps;
+		
+		switch (eccmode) {
+		case NAND_ECC_NONE: {	/* No ECC, Read in a page */
+			static unsigned long lastwhinge = 0;
+			if ((lastwhinge / HZ) != (jiffies / HZ)) {
+				printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n");
+				lastwhinge = jiffies;
+			}
+			this->read_buf(mtd, data_poi, end);
+			break;
+		}
+			
+		case NAND_ECC_SOFT:	/* Software ECC 3/256: Read in a page + oob data */
+			this->read_buf(mtd, data_poi, end);
+			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc) 
+				this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
+			break;	
+			
+		case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data */
+		case NAND_ECC_HW3_512: /* Hardware ECC 3 byte /512 byte data */	
+		case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data  */
+		case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data  */
+			for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) {
+				this->enable_hwecc(mtd, NAND_ECC_READ);	
+				this->read_buf(mtd, &data_poi[datidx], ecc);
+
+				/* HW ecc with syndrome calculation must read the
+				 * syndrome from flash immidiately after the data */
+				if (!compareecc) {
+					/* Some hw ecc generators need to know when the
+					 * syndrome is read from flash */
+					this->enable_hwecc(mtd, NAND_ECC_READSYN);
+					this->read_buf(mtd, &oob_data[i], eccbytes);
+					/* We calc error correction directly, it checks the hw
+					 * generator for an error, reads back the syndrome and
+					 * does the error correction on the fly */
+					if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) {
+						DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " 
+							"Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
+						ecc_failed++;
+					}
+				} else {
+					this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
+				}	
+			}
+			break;						
+
+		default:
+			printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
+			BUG();	
+		}
+
+		/* read oobdata */
+		this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen);
+
+		/* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */
+		if (!compareecc)
+			goto readoob;	
+		
+		/* Pick the ECC bytes out of the oob data */
+		for (j = 0; j < oobsel->eccbytes; j++)
+			ecc_code[j] = oob_data[oob_config[j]];
+
+		/* correct data, if neccecary */
+		for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) {
+			ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);
+			
+			/* Get next chunk of ecc bytes */
+			j += eccbytes;
+			
+			/* Check, if we have a fs supplied oob-buffer, 
+			 * This is the legacy mode. Used by YAFFS1
+			 * Should go away some day
+			 */
+			if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) { 
+				int *p = (int *)(&oob_data[mtd->oobsize]);
+				p[i] = ecc_status;
+			}
+			
+			if (ecc_status == -1) {	
+				DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
+				ecc_failed++;
+			}
+		}		
+
+	readoob:
+		/* check, if we have a fs supplied oob-buffer */
+		if (oob_buf) {
+			/* without autoplace. Legacy mode used by YAFFS1 */
+			switch(oobsel->useecc) {
+			case MTD_NANDECC_AUTOPLACE:
+				/* 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;
+				break;
+			case MTD_NANDECC_PLACE:
+				/* YAFFS1 legacy mode */
+				oob_data += this->eccsteps * sizeof (int);
+			default:
+				oob_data += mtd->oobsize;
+			}
+		}
+	readdata:
+		/* Partial page read, transfer data into fs buffer */
+		if (!aligned) { 
+			for (j = col; j < end && read < len; j++)
+				buf[read++] = data_poi[j];
+			this->pagebuf = realpage;	
+		} else		
+			read += mtd->oobblock;
+
+		/* Apply delay or wait for ready/busy pin 
+		 * Do this before the AUTOINCR check, so no problems
+		 * arise if a chip which does auto increment
+		 * is marked as NOAUTOINCR by the board driver.
+		*/
+		if (!this->dev_ready) 
+			udelay (this->chip_delay);
+		else
+			while (!this->dev_ready(mtd));	
+			
+		if (read == len)
+			break;	
+
+		/* For subsequent reads align to page boundary. */
+		col = 0;
+		/* Increment page address */
+		realpage++;
+
+		page = realpage & this->pagemask;
+		/* Check, if we cross a chip boundary */
+		if (!page) {
+			chipnr++;
+			this->select_chip(mtd, -1);
+			this->select_chip(mtd, chipnr);
+		}
+		/* Check, if the chip supports auto page increment 
+		 * or if we have hit a block boundary. 
+		*/ 
+		if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+			sndcmd = 1;				
+	}
+
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_chip(mtd);
+
+	/*
+	 * Return success, if no ECC failures, else -EBADMSG
+	 * fs driver will take care of that, because
+	 * retlen == desired len and result == -EBADMSG
+	 */
+	*retlen = read;
+	return ecc_failed ? -EBADMSG : 0;
+}
+
+/**
+ * nand_read_oob - [MTD Interface] NAND read out-of-band
+ * @mtd:	MTD device structure
+ * @from:	offset to read from
+ * @len:	number of bytes to read
+ * @retlen:	pointer to variable to store the number of read bytes
+ * @buf:	the databuffer to put data
+ *
+ * NAND read out-of-band data from the spare area
+ */
+static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
+{
+	int i, col, page, chipnr;
+	struct nand_chip *this = mtd->priv;
+	int	blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+
+	DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
+
+	/* Shift to get page */
+	page = (int)(from >> this->page_shift);
+	chipnr = (int)(from >> this->chip_shift);
+	
+	/* Mask to get column */
+	col = from & (mtd->oobsize - 1);
+
+	/* Initialize return length value */
+	*retlen = 0;
+
+	/* Do not allow reads past end of device */
+	if ((from + len) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n");
+		*retlen = 0;
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	nand_get_chip (this, mtd , FL_READING);
+
+	/* Select the NAND device */
+	this->select_chip(mtd, chipnr);
+
+	/* Send the read command */
+	this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask);
+	/* 
+	 * Read the data, if we read more than one page
+	 * oob data, let the device transfer the data !
+	 */
+	i = 0;
+	while (i < len) {
+		int thislen = mtd->oobsize - col;
+		thislen = min_t(int, thislen, len);
+		this->read_buf(mtd, &buf[i], thislen);
+		i += thislen;
+		
+		/* 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));	
+
+		/* Read more ? */
+		if (i < len) {
+			page++;
+			col = 0;
+
+			/* Check, if we cross a chip boundary */
+			if (!(page & this->pagemask)) {
+				chipnr++;
+				this->select_chip(mtd, -1);
+				this->select_chip(mtd, chipnr);
+			}
+				
+			/* Check, if the chip supports auto page increment 
+			 * or if we have hit a block boundary. 
+			*/ 
+			if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) {
+				/* For subsequent page reads set offset to 0 */
+			        this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask);
+			}
+		}
+	}
+
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_chip(mtd);
+
+	/* Return happy */
+	*retlen = len;
+	return 0;
+}
+
+/**
+ * nand_read_raw - [GENERIC] Read raw data including oob into buffer
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @from:	offset to read from
+ * @len:	number of bytes to read
+ * @ooblen:	number of oob data bytes to read
+ *
+ * Read raw data including oob into buffer
+ */
+int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen)
+{
+	struct nand_chip *this = mtd->priv;
+	int page = (int) (from >> this->page_shift);
+	int chip = (int) (from >> this->chip_shift);
+	int sndcmd = 1;
+	int cnt = 0;
+	int pagesize = mtd->oobblock + mtd->oobsize;
+	int	blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
+
+	/* Do not allow reads past end of device */
+	if ((from + len) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt read beyond end of device\n");
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	nand_get_chip (this, mtd , FL_READING);
+
+	this->select_chip (mtd, chip);
+	
+	/* Add requested oob length */
+	len += ooblen;
+	
+	while (len) {
+		if (sndcmd)
+			this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask);
+		sndcmd = 0;	
+
+		this->read_buf (mtd, &buf[cnt], pagesize);
+
+		len -= pagesize;
+		cnt += pagesize;
+		page++;
+		
+		if (!this->dev_ready) 
+			udelay (this->chip_delay);
+		else
+			while (!this->dev_ready(mtd));	
+			
+		/* Check, if the chip supports auto page increment */ 
+		if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
+			sndcmd = 1;
+	}
+
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_chip(mtd);
+	return 0;
+}
+
+
+/** 
+ * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer 
+ * @mtd:	MTD device structure
+ * @fsbuf:	buffer given by fs driver
+ * @oobsel:	out of band selection structre
+ * @autoplace:	1 = place given buffer into the oob bytes
+ * @numpages:	number of pages to prepare
+ *
+ * Return:
+ * 1. Filesystem buffer available and autoplacement is off,
+ *    return filesystem buffer
+ * 2. No filesystem buffer or autoplace is off, return internal
+ *    buffer
+ * 3. Filesystem buffer is given and autoplace selected
+ *    put data from fs buffer into internal buffer and
+ *    retrun internal buffer
+ *
+ * Note: The internal buffer is filled with 0xff. This must
+ * be done only once, when no autoplacement happens
+ * Autoplacement sets the buffer dirty flag, which
+ * forces the 0xff fill before using the buffer again.
+ *
+*/
+static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel,
+		int autoplace, int numpages)
+{
+	struct nand_chip *this = mtd->priv;
+	int i, len, ofs;
+
+	/* Zero copy fs supplied buffer */
+	if (fsbuf && !autoplace) 
+		return fsbuf;
+
+	/* Check, if the buffer must be filled with ff again */
+	if (this->oobdirty) {	
+		memset (this->oob_buf, 0xff, 
+			mtd->oobsize << (this->phys_erase_shift - this->page_shift));
+		this->oobdirty = 0;
+	}	
+	
+	/* If we have no autoplacement or no fs buffer use the internal one */
+	if (!autoplace || !fsbuf)
+		return this->oob_buf;
+	
+	/* Walk through the pages and place the data */
+	this->oobdirty = 1;
+	ofs = 0;
+	while (numpages--) {
+		for (i = 0, len = 0; len < mtd->oobavail; i++) {
+			int to = ofs + oobsel->oobfree[i][0];
+			int num = oobsel->oobfree[i][1];
+			memcpy (&this->oob_buf[to], fsbuf, num);
+			len += num;
+			fsbuf += num;
+		}
+		ofs += mtd->oobavail;
+	}
+	return this->oob_buf;
+}
+
+#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0
+
+/**
+ * nand_write - [MTD Interface] compability function for nand_write_ecc
+ * @mtd:	MTD device structure
+ * @to:		offset to write to
+ * @len:	number of bytes to write
+ * @retlen:	pointer to variable to store the number of written bytes
+ * @buf:	the data to write
+ *
+ * This function simply calls nand_write_ecc with oob buffer and oobsel = NULL
+ *
+*/
+static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
+{
+	return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
+}
+			   
+/**
+ * nand_write_ecc - [MTD Interface] NAND write with ECC
+ * @mtd:	MTD device structure
+ * @to:		offset to write to
+ * @len:	number of bytes to write
+ * @retlen:	pointer to variable to store the number of written bytes
+ * @buf:	the data to write
+ * @eccbuf:	filesystem supplied oob data buffer
+ * @oobsel:	oob selection structure
+ *
+ * NAND write with ECC
+ */
+static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len,
+			   size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel)
+{
+	int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr;
+	int autoplace = 0, numpages, totalpages;
+	struct nand_chip *this = mtd->priv;
+	u_char *oobbuf, *bufstart;
+	int	ppblock = (1 << (this->phys_erase_shift - this->page_shift));
+
+	DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+
+	/* Initialize retlen, in case of early exit */
+	*retlen = 0;
+
+	/* Do not allow write past end of device */
+	if ((to + len) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n");
+		return -EINVAL;
+	}
+
+	/* reject writes, which are not page aligned */	
+	if (NOTALIGNED (to) || NOTALIGNED(len)) {
+		printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	nand_get_chip (this, mtd, FL_WRITING);
+
+	/* Calculate chipnr */
+	chipnr = (int)(to >> this->chip_shift);
+	/* Select the NAND device */
+	this->select_chip(mtd, chipnr);
+
+	/* Check, if it is write protected */
+	if (nand_check_wp(mtd))
+		goto out;
+
+	/* if oobsel is NULL, use chip defaults */
+	if (oobsel == NULL) 
+		oobsel = &mtd->oobinfo;		
+		
+	/* Autoplace of oob data ? Use the default placement scheme */
+	if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
+		oobsel = this->autooob;
+		autoplace = 1;
+	}	
+
+	/* Setup variables and oob buffer */
+	totalpages = len >> this->page_shift;
+	page = (int) (to >> this->page_shift);
+	/* Invalidate the page cache, if we write to the cached page */
+	if (page <= this->pagebuf && this->pagebuf < (page + totalpages))  
+		this->pagebuf = -1;
+	
+	/* Set it relative to chip */
+	page &= this->pagemask;
+	startpage = page;
+	/* Calc number of pages we can write in one go */
+	numpages = min (ppblock - (startpage  & (ppblock - 1)), totalpages);
+	oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages);
+	bufstart = (u_char *)buf;
+
+	/* Loop until all data is written */
+	while (written < len) {
+
+		this->data_poi = (u_char*) &buf[written];
+		/* Write one page. If this is the last page to write
+		 * or the last page in this block, then use the
+		 * real pageprogram command, else select cached programming
+		 * if supported by the chip.
+		 */
+		ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0));
+		if (ret) {
+			DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret);
+			goto out;
+		}	
+		/* Next oob page */
+		oob += mtd->oobsize;
+		/* Update written bytes count */
+		written += mtd->oobblock;
+		if (written == len) 
+			goto cmp;
+		
+		/* Increment page address */
+		page++;
+
+		/* Have we hit a block boundary ? Then we have to verify and
+		 * if verify is ok, we have to setup the oob buffer for
+		 * the next pages.
+		*/
+		if (!(page & (ppblock - 1))){
+			int ofs;
+			this->data_poi = bufstart;
+			ret = nand_verify_pages (mtd, this, startpage, 
+				page - startpage,
+				oobbuf, oobsel, chipnr, (eccbuf != NULL));
+			if (ret) {
+				DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);
+				goto out;
+			}	
+			*retlen = written;
+
+			ofs = autoplace ? mtd->oobavail : mtd->oobsize;
+			if (eccbuf)
+				eccbuf += (page - startpage) * ofs;
+			totalpages -= page - startpage;
+			numpages = min (totalpages, ppblock);
+			page &= this->pagemask;
+			startpage = page;
+			oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, 
+					autoplace, numpages);
+			/* Check, if we cross a chip boundary */
+			if (!page) {
+				chipnr++;
+				this->select_chip(mtd, -1);
+				this->select_chip(mtd, chipnr);
+			}
+		}
+	}
+	/* Verify the remaining pages */
+cmp:
+	this->data_poi = bufstart;
+ 	ret = nand_verify_pages (mtd, this, startpage, totalpages,
+		oobbuf, oobsel, chipnr, (eccbuf != NULL));
+	if (!ret)
+		*retlen = written;
+	else	
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);
+
+out:
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_chip(mtd);
+
+	return ret;
+}
+
+
+/**
+ * nand_write_oob - [MTD Interface] NAND write out-of-band
+ * @mtd:	MTD device structure
+ * @to:		offset to write to
+ * @len:	number of bytes to write
+ * @retlen:	pointer to variable to store the number of written bytes
+ * @buf:	the data to write
+ *
+ * NAND write out-of-band
+ */
+static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
+{
+	int column, page, status, ret = -EIO, chipnr;
+	struct nand_chip *this = mtd->priv;
+
+	DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
+
+	/* Shift to get page */
+	page = (int) (to >> this->page_shift);
+	chipnr = (int) (to >> this->chip_shift);
+
+	/* Mask to get column */
+	column = to & (mtd->oobsize - 1);
+
+	/* Initialize return length value */
+	*retlen = 0;
+
+	/* Do not allow write past end of page */
+	if ((column + len) > mtd->oobsize) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n");
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	nand_get_chip (this, mtd, FL_WRITING);
+
+	/* Select the NAND device */
+	this->select_chip(mtd, chipnr);
+
+	/* Reset the chip. Some chips (like the Toshiba TC5832DC found
+	   in one of my DiskOnChip 2000 test units) will clear the whole
+	   data page too if we don't do this. I have no clue why, but
+	   I seem to have 'fixed' it in the doc2000 driver in
+	   August 1999.  dwmw2. */
+	this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
+
+	/* Check, if it is write protected */
+	if (nand_check_wp(mtd))
+		goto out;
+	
+	/* Invalidate the page cache, if we write to the cached page */
+	if (page == this->pagebuf)
+		this->pagebuf = -1;
+
+	if (NAND_MUST_PAD(this)) {
+		/* Write out desired data */
+		this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask);
+		/* prepad 0xff for partial programming */
+		this->write_buf(mtd, ffchars, column);
+		/* write data */
+		this->write_buf(mtd, buf, len);
+		/* postpad 0xff for partial programming */
+		this->write_buf(mtd, ffchars, mtd->oobsize - (len+column));
+	} else {
+		/* Write out desired data */
+		this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask);
+		/* write data */
+		this->write_buf(mtd, buf, len);
+	}
+	/* Send command to program the OOB data */
+	this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
+
+	status = this->waitfunc (mtd, this, FL_WRITING);
+
+	/* See if device thinks it succeeded */
+	if (status & 0x01) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page);
+		ret = -EIO;
+		goto out;
+	}
+	/* Return happy */
+	*retlen = len;
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+	/* Send command to read back the data */
+	this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask);
+
+	if (this->verify_buf(mtd, buf, len)) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page);
+		ret = -EIO;
+		goto out;
+	}
+#endif
+	ret = 0;
+out:
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_chip(mtd);
+
+	return ret;
+}
+
+
+/**
+ * nand_writev - [MTD Interface] compabilty function for nand_writev_ecc
+ * @mtd:	MTD device structure
+ * @vecs:	the iovectors to write
+ * @count:	number of vectors
+ * @to:		offset to write to
+ * @retlen:	pointer to variable to store the number of written bytes
+ *
+ * NAND write with kvec. This just calls the ecc function
+ */
+static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, 
+		loff_t to, size_t * retlen)
+{
+	return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL));	
+}
+
+/**
+ * nand_writev_ecc - [MTD Interface] write with iovec with ecc
+ * @mtd:	MTD device structure
+ * @vecs:	the iovectors to write
+ * @count:	number of vectors
+ * @to:		offset to write to
+ * @retlen:	pointer to variable to store the number of written bytes
+ * @eccbuf:	filesystem supplied oob data buffer
+ * @oobsel:	oob selection structure
+ *
+ * NAND write with iovec with ecc
+ */
+static int nand_writev_ecc (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, 
+		loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel)
+{
+	int i, page, len, total_len, ret = -EIO, written = 0, chipnr;
+	int oob, numpages, autoplace = 0, startpage;
+	struct nand_chip *this = mtd->priv;
+	int	ppblock = (1 << (this->phys_erase_shift - this->page_shift));
+	u_char *oobbuf, *bufstart;
+
+	/* Preset written len for early exit */
+	*retlen = 0;
+
+	/* Calculate total length of data */
+	total_len = 0;
+	for (i = 0; i < count; i++)
+		total_len += (int) vecs[i].iov_len;
+
+	DEBUG (MTD_DEBUG_LEVEL3,
+	       "nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count);
+
+	/* Do not allow write past end of page */
+	if ((to + total_len) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n");
+		return -EINVAL;
+	}
+
+	/* reject writes, which are not page aligned */	
+	if (NOTALIGNED (to) || NOTALIGNED(total_len)) {
+		printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
+		return -EINVAL;
+	}
+
+	/* Grab the lock and see if the device is available */
+	nand_get_chip (this, mtd, FL_WRITING);
+
+	/* Get the current chip-nr */
+	chipnr = (int) (to >> this->chip_shift);
+	/* Select the NAND device */
+	this->select_chip(mtd, chipnr);
+
+	/* Check, if it is write protected */
+	if (nand_check_wp(mtd))
+		goto out;
+
+	/* if oobsel is NULL, use chip defaults */
+	if (oobsel == NULL) 
+		oobsel = &mtd->oobinfo;		
+
+	/* Autoplace of oob data ? Use the default placement scheme */
+	if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
+		oobsel = this->autooob;
+		autoplace = 1;
+	}	
+
+	/* Setup start page */
+	page = (int) (to >> this->page_shift);
+	/* Invalidate the page cache, if we write to the cached page */
+	if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift))  
+		this->pagebuf = -1;
+
+	startpage = page & this->pagemask;
+
+	/* Loop until all kvec' data has been written */
+	len = 0;
+	while (count) {
+		/* If the given tuple is >= pagesize then
+		 * write it out from the iov
+		 */
+		if ((vecs->iov_len - len) >= mtd->oobblock) {
+			/* Calc number of pages we can write
+			 * out of this iov in one go */
+			numpages = (vecs->iov_len - len) >> this->page_shift;
+			/* Do not cross block boundaries */
+			numpages = min (ppblock - (startpage & (ppblock - 1)), numpages);
+			oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
+			bufstart = (u_char *)vecs->iov_base;
+			bufstart += len;
+			this->data_poi = bufstart;
+			oob = 0;
+			for (i = 1; i <= numpages; i++) {
+				/* Write one page. If this is the last page to write
+				 * then use the real pageprogram command, else select 
+				 * cached programming if supported by the chip.
+				 */
+				ret = nand_write_page (mtd, this, page & this->pagemask, 
+					&oobbuf[oob], oobsel, i != numpages);
+				if (ret)
+					goto out;
+				this->data_poi += mtd->oobblock;
+				len += mtd->oobblock;
+				oob += mtd->oobsize;
+				page++;
+			}
+			/* Check, if we have to switch to the next tuple */
+			if (len >= (int) vecs->iov_len) {
+				vecs++;
+				len = 0;
+				count--;
+			}
+		} else {
+			/* We must use the internal buffer, read data out of each 
+			 * tuple until we have a full page to write
+			 */
+			int cnt = 0;
+			while (cnt < mtd->oobblock) {
+				if (vecs->iov_base != NULL && vecs->iov_len) 
+					this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++];
+				/* Check, if we have to switch to the next tuple */
+				if (len >= (int) vecs->iov_len) {
+					vecs++;
+					len = 0;
+					count--;
+				}
+			}
+			this->pagebuf = page;	
+			this->data_poi = this->data_buf;	
+			bufstart = this->data_poi;
+			numpages = 1;		
+			oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
+			ret = nand_write_page (mtd, this, page & this->pagemask,
+				oobbuf, oobsel, 0);
+			if (ret)
+				goto out;
+			page++;
+		}
+
+		this->data_poi = bufstart;
+		ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0);
+		if (ret)
+			goto out;
+			
+		written += mtd->oobblock * numpages;
+		/* All done ? */
+		if (!count)
+			break;
+
+		startpage = page & this->pagemask;
+		/* Check, if we cross a chip boundary */
+		if (!startpage) {
+			chipnr++;
+			this->select_chip(mtd, -1);
+			this->select_chip(mtd, chipnr);
+		}
+	}
+	ret = 0;
+out:
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_chip(mtd);
+
+	*retlen = written;
+	return ret;
+}
+
+/**
+ * single_erease_cmd - [GENERIC] NAND standard block erase command function
+ * @mtd:	MTD device structure
+ * @page:	the page address of the block which will be erased
+ *
+ * Standard erase command for NAND chips
+ */
+static void single_erase_cmd (struct mtd_info *mtd, int page)
+{
+	struct nand_chip *this = mtd->priv;
+	/* Send commands to erase a block */
+	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
+	this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
+}
+
+/**
+ * multi_erease_cmd - [GENERIC] AND specific block erase command function
+ * @mtd:	MTD device structure
+ * @page:	the page address of the block which will be erased
+ *
+ * AND multi block erase command function
+ * Erase 4 consecutive blocks
+ */
+static void multi_erase_cmd (struct mtd_info *mtd, int page)
+{
+	struct nand_chip *this = mtd->priv;
+	/* Send commands to erase a block */
+	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
+	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
+	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
+	this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
+	this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
+}
+
+/**
+ * nand_erase - [MTD Interface] erase block(s)
+ * @mtd:	MTD device structure
+ * @instr:	erase instruction
+ *
+ * Erase one ore more blocks
+ */
+static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
+{
+	return nand_erase_nand (mtd, instr, 0);
+}
+ 
+/**
+ * nand_erase_intern - [NAND Interface] erase block(s)
+ * @mtd:	MTD device structure
+ * @instr:	erase instruction
+ * @allowbbt:	allow erasing the bbt area
+ *
+ * Erase one ore more blocks
+ */
+int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt)
+{
+	int page, len, status, pages_per_block, ret, chipnr;
+	struct nand_chip *this = mtd->priv;
+
+	DEBUG (MTD_DEBUG_LEVEL3,
+	       "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
+
+	/* Start address must align on block boundary */
+	if (instr->addr & ((1 << this->phys_erase_shift) - 1)) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
+		return -EINVAL;
+	}
+
+	/* Length must align on block boundary */
+	if (instr->len & ((1 << this->phys_erase_shift) - 1)) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n");
+		return -EINVAL;
+	}
+
+	/* Do not allow erase past end of device */
+	if ((instr->len + instr->addr) > mtd->size) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n");
+		return -EINVAL;
+	}
+
+	instr->fail_addr = 0xffffffff;
+
+	/* Grab the lock and see if the device is available */
+	nand_get_chip (this, mtd, FL_ERASING);
+
+	/* Shift to get first page */
+	page = (int) (instr->addr >> this->page_shift);
+	chipnr = (int) (instr->addr >> this->chip_shift);
+
+	/* Calculate pages in each block */
+	pages_per_block = 1 << (this->phys_erase_shift - this->page_shift);
+
+	/* Select the NAND device */
+	this->select_chip(mtd, chipnr);
+
+	/* Check the WP bit */
+	/* Check, if it is write protected */
+	if (nand_check_wp(mtd)) {
+		DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");
+		instr->state = MTD_ERASE_FAILED;
+		goto erase_exit;
+	}
+
+	/* Loop through the pages */
+	len = instr->len;
+
+	instr->state = MTD_ERASING;
+
+	while (len) {
+		/* Check if we have a bad block, we do not erase bad blocks ! */
+		if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) {
+			printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);
+			instr->state = MTD_ERASE_FAILED;
+			goto erase_exit;
+		}
+		
+		/* Invalidate the page cache, if we erase the block which contains 
+		   the current cached page */
+		if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block))
+			this->pagebuf = -1;
+
+		this->erase_cmd (mtd, page & this->pagemask);
+		
+		status = this->waitfunc (mtd, this, FL_ERASING);
+
+		/* See if block erase succeeded */
+		if (status & 0x01) {
+			DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
+			instr->state = MTD_ERASE_FAILED;
+			instr->fail_addr = (page << this->page_shift);
+			goto erase_exit;
+		}
+		
+		/* Increment page address and decrement length */
+		len -= (1 << this->phys_erase_shift);
+		page += pages_per_block;
+
+		/* Check, if we cross a chip boundary */
+		if (len && !(page & this->pagemask)) {
+			chipnr++;
+			this->select_chip(mtd, -1);
+			this->select_chip(mtd, chipnr);
+		}
+	}
+	instr->state = MTD_ERASE_DONE;
+
+erase_exit:
+
+	ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
+	/* Do call back function */
+	if (!ret)
+		mtd_erase_callback(instr);
+
+	/* Deselect and wake up anyone waiting on the device */
+	nand_release_chip(mtd);
+
+	/* Return more or less happy */
+	return ret;
+}
+
+/**
+ * nand_sync - [MTD Interface] sync
+ * @mtd:	MTD device structure
+ *
+ * Sync is actually a wait for chip ready function
+ */
+static void nand_sync (struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	DECLARE_WAITQUEUE (wait, current);
+
+	DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
+
+retry:
+	/* Grab the spinlock */
+	spin_lock_bh (&this->chip_lock);
+
+	/* See what's going on */
+	switch (this->state) {
+	case FL_READY:
+	case FL_SYNCING:
+		this->state = FL_SYNCING;
+		spin_unlock_bh (&this->chip_lock);
+		break;
+
+	default:
+		/* Not an idle state */
+		add_wait_queue (&this->wq, &wait);
+		spin_unlock_bh (&this->chip_lock);
+		schedule ();
+
+		remove_wait_queue (&this->wq, &wait);
+		goto retry;
+	}
+
+	/* Lock the device */
+	spin_lock_bh (&this->chip_lock);
+
+	/* Set the device to be ready again */
+	if (this->state == FL_SYNCING) {
+		this->state = FL_READY;
+		wake_up (&this->wq);
+	}
+
+	/* Unlock the device */
+	spin_unlock_bh (&this->chip_lock);
+}
+
+
+/**
+ * nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
+ * @mtd:	MTD device structure
+ * @ofs:	offset relative to mtd start
+ */
+static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs)
+{
+	/* Check for invalid offset */
+	if (ofs > mtd->size) 
+		return -EINVAL;
+	
+	return nand_block_checkbad (mtd, ofs, 1, 0);
+}
+
+/**
+ * nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
+ * @mtd:	MTD device structure
+ * @ofs:	offset relative to mtd start
+ */
+static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs)
+{
+	struct nand_chip *this = mtd->priv;
+	int ret;
+
+        if ((ret = nand_block_isbad(mtd, ofs))) {
+        	/* If it was bad already, return success and do nothing. */
+		if (ret > 0)
+			return 0;
+        	return ret;
+        }
+
+	return this->block_markbad(mtd, ofs);
+}
+
+/**
+ * nand_scan - [NAND Interface] Scan for the NAND device
+ * @mtd:	MTD device structure
+ * @maxchips:	Number of chips to scan for
+ *
+ * This fills out all the not initialized function pointers
+ * with the defaults.
+ * The flash ID is read and the mtd/chip structures are
+ * filled with the appropriate values. Buffers are allocated if
+ * they are not provided by the board driver
+ *
+ */
+int nand_scan (struct mtd_info *mtd, int maxchips)
+{
+	int i, j, 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;
+
+	/* check, if a user supplied command function given */
+	if (this->cmdfunc == NULL)
+		this->cmdfunc = nand_command;
+
+	/* check, if a user supplied wait function given */
+	if (this->waitfunc == NULL)
+		this->waitfunc = nand_wait;
+
+	if (!this->select_chip)
+		this->select_chip = nand_select_chip;
+	if (!this->write_byte)
+		this->write_byte = busw ? nand_write_byte16 : nand_write_byte;
+	if (!this->read_byte)
+		this->read_byte = busw ? nand_read_byte16 : nand_read_byte;
+	if (!this->write_word)
+		this->write_word = nand_write_word;
+	if (!this->read_word)
+		this->read_word = nand_read_word;
+	if (!this->block_bad)
+		this->block_bad = nand_block_bad;
+	if (!this->block_markbad)
+		this->block_markbad = nand_default_block_markbad;
+	if (!this->write_buf)
+		this->write_buf = busw ? nand_write_buf16 : nand_write_buf;
+	if (!this->read_buf)
+		this->read_buf = busw ? nand_read_buf16 : nand_read_buf;
+	if (!this->verify_buf)
+		this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
+	if (!this->scan_bbt)
+		this->scan_bbt = nand_default_bbt;
+
+	/* Select the device */
+	this->select_chip(mtd, 0);
+
+	/* Send the command for reading device ID */
+	this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
+
+	/* Read manufacturer and device IDs */
+	nand_maf_id = this->read_byte(mtd);
+	nand_dev_id = this->read_byte(mtd);
+
+	/* Print and store flash device information */
+	for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+				
+		if (nand_dev_id != nand_flash_ids[i].id) 
+			continue;
+
+		if (!mtd->name) mtd->name = nand_flash_ids[i].name;
+		this->chipsize = nand_flash_ids[i].chipsize << 20;
+		
+		/* New devices have all the information in additional id bytes */
+		if (!nand_flash_ids[i].pagesize) {
+			int extid;
+			/* The 3rd id byte contains non relevant data ATM */
+			extid = this->read_byte(mtd);
+			/* The 4th id byte is the important one */
+			extid = this->read_byte(mtd);
+			/* Calc pagesize */
+			mtd->oobblock = 1024 << (extid & 0x3);
+			extid >>= 2;
+			/* Calc oobsize */
+			mtd->oobsize = (8 << (extid & 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;
+		
+		} else {
+			/* Old devices have this data hardcoded in the
+			 * device id table */
+			mtd->erasesize = nand_flash_ids[i].erasesize;
+			mtd->oobblock = nand_flash_ids[i].pagesize;
+			mtd->oobsize = mtd->oobblock / 32;
+			busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;
+		}
+
+		/* 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->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;
+		this->chip_shift = ffs(this->chipsize) - 1;
+
+		/* Set the bad block position */
+		this->badblockpos = mtd->oobblock > 512 ? 
+			NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
+
+		/* Get chip options, preserve non chip based options */
+		this->options &= ~NAND_CHIPOPTIONS_MSK;
+		this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;
+		/* Set this as a default. Board drivers can override it, if neccecary */
+		this->options |= NAND_NO_AUTOINCR;
+		/* Check if this is a not a samsung device. Do not clear the options
+		 * for chips which are not having an extended id.
+		 */	
+		if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize)
+			this->options &= ~NAND_SAMSUNG_LP_OPTIONS;
+		
+		/* Check for AND chips with 4 page planes */
+		if (this->options & NAND_4PAGE_ARRAY)
+			this->erase_cmd = multi_erase_cmd;
+		else
+			this->erase_cmd = single_erase_cmd;
+
+		/* Do not replace user supplied command function ! */
+		if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
+			this->cmdfunc = nand_command_lp;
+				
+		/* Try to identify manufacturer */
+		for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
+			if (nand_manuf_ids[j].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[j].name , nand_flash_ids[i].name);
+		break;
+	}
+
+	if (!nand_flash_ids[i].name) {
+		printk (KERN_WARNING "No NAND device found!!!\n");
+		this->select_chip(mtd, -1);
+		return 1;
+	}
+
+	for (i=1; i < maxchips; i++) {
+		this->select_chip(mtd, i);
+
+		/* Send the command for reading device ID */
+		this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
+
+		/* Read manufacturer and device IDs */
+		if (nand_maf_id != this->read_byte(mtd) ||
+		    nand_dev_id != this->read_byte(mtd))
+			break;
+	}
+	if (i > 1)
+		printk(KERN_INFO "%d NAND chips detected\n", i);
+	
+	/* Allocate buffers, if neccecary */
+	if (!this->oob_buf) {
+		size_t len;
+		len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);
+		this->oob_buf = kmalloc (len, GFP_KERNEL);
+		if (!this->oob_buf) {
+			printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n");
+			return -ENOMEM;
+		}
+		this->options |= NAND_OOBBUF_ALLOC;
+	}
+	
+	if (!this->data_buf) {
+		size_t len;
+		len = mtd->oobblock + mtd->oobsize;
+		this->data_buf = kmalloc (len, GFP_KERNEL);
+		if (!this->data_buf) {
+			if (this->options & NAND_OOBBUF_ALLOC)
+				kfree (this->oob_buf);
+			printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n");
+			return -ENOMEM;
+		}
+		this->options |= NAND_DATABUF_ALLOC;
+	}
+
+	/* Store the number of chips and calc total size for mtd */
+	this->numchips = i;
+	mtd->size = i * this->chipsize;
+	/* Convert chipsize to number of pages per chip -1. */
+	this->pagemask = (this->chipsize >> this->page_shift) - 1;
+	/* Preset the internal oob buffer */
+	memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift));
+
+	/* If no default placement scheme is given, select an
+	 * appropriate one */
+	if (!this->autooob) {
+		/* Select the appropriate default oob placement scheme for
+		 * placement agnostic filesystems */
+		switch (mtd->oobsize) { 
+		case 8:
+			this->autooob = &nand_oob_8;
+			break;
+		case 16:
+			this->autooob = &nand_oob_16;
+			break;
+		case 64:
+			this->autooob = &nand_oob_64;
+			break;
+		default:
+			printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",
+				mtd->oobsize);
+			BUG();
+		}
+	}
+	
+	/* The number of bytes available for the filesystem to place fs dependend
+	 * oob data */
+	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
+	 * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
+	 * fallback to software ECC 
+	*/
+	this->eccsize = 256;	/* set default eccsize */	
+
+	switch (this->eccmode) {
+
+	case NAND_ECC_HW3_512: 
+	case NAND_ECC_HW6_512: 
+	case NAND_ECC_HW8_512: 
+		if (mtd->oobblock == 256) {
+			printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");
+			this->eccmode = NAND_ECC_SOFT;
+			this->calculate_ecc = nand_calculate_ecc;
+			this->correct_data = nand_correct_data;
+			break;		
+		} else 
+			this->eccsize = 512; /* set eccsize to 512 and fall through for function check */
+
+	case NAND_ECC_HW3_256:
+		if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
+			break;
+		printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
+		BUG();	
+
+	case NAND_ECC_NONE: 
+		printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
+		this->eccmode = NAND_ECC_NONE;
+		break;
+
+	case NAND_ECC_SOFT:	
+		this->calculate_ecc = nand_calculate_ecc;
+		this->correct_data = nand_correct_data;
+		break;
+
+	default:
+		printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
+		BUG();	
+	}	
+	
+	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:
+	case NAND_ECC_HW8_512:
+		this->eccsteps = mtd->oobblock / 512;
+		break;
+	case NAND_ECC_HW3_256:
+	case NAND_ECC_SOFT:	
+		this->eccsteps = mtd->oobblock / 256;
+		break;
+		
+	case NAND_ECC_NONE: 
+		this->eccsteps = 1;
+		break;
+	}
+	
+	/* Initialize state, waitqueue and spinlock */
+	this->state = FL_READY;
+	init_waitqueue_head (&this->wq);
+	spin_lock_init (&this->chip_lock);
+
+	/* De-select the device */
+	this->select_chip(mtd, -1);
+
+	/* Invalidate the pagebuffer reference */
+	this->pagebuf = -1;
+
+	/* Fill in remaining MTD driver data */
+	mtd->type = MTD_NANDFLASH;
+	mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
+	mtd->ecctype = MTD_ECC_SW;
+	mtd->erase = nand_erase;
+	mtd->point = NULL;
+	mtd->unpoint = NULL;
+	mtd->read = nand_read;
+	mtd->write = nand_write;
+	mtd->read_ecc = nand_read_ecc;
+	mtd->write_ecc = nand_write_ecc;
+	mtd->read_oob = nand_read_oob;
+	mtd->write_oob = nand_write_oob;
+	mtd->readv = NULL;
+	mtd->writev = nand_writev;
+	mtd->writev_ecc = nand_writev_ecc;
+	mtd->sync = nand_sync;
+	mtd->lock = NULL;
+	mtd->unlock = NULL;
+	mtd->suspend = 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;
+
+	/* Build bad block table */
+	return this->scan_bbt (mtd);
+}
+
+/**
+ * nand_release - [NAND Interface] Free resources held by the NAND device 
+ * @mtd:	MTD device structure
+*/
+void nand_release (struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+
+#ifdef CONFIG_MTD_PARTITIONS
+	/* Deregister partitions */
+	del_mtd_partitions (mtd);
+#endif
+	/* Deregister the device */
+	del_mtd_device (mtd);
+
+	/* Free bad block table memory, if allocated */
+	if (this->bbt)
+		kfree (this->bbt);
+	/* Buffer allocated by nand_scan ? */
+	if (this->options & NAND_OOBBUF_ALLOC)
+		kfree (this->oob_buf);
+	/* Buffer allocated by nand_scan ? */
+	if (this->options & NAND_DATABUF_ALLOC)
+		kfree (this->data_buf);
+}
+
+EXPORT_SYMBOL (nand_scan);
+EXPORT_SYMBOL (nand_release);
+
+MODULE_LICENSE ("GPL");
+MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
+MODULE_DESCRIPTION ("Generic NAND flash driver code");
diff -uNrb linux-2.4.30/drivers/mtd/nand/nand_bbt.c linux/drivers/mtd/nand/nand_bbt.c
--- linux-2.4.30/drivers/mtd/nand/nand_bbt.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/nand/nand_bbt.c	Fri Oct  1 19:15:22 2004
@@ -0,0 +1,1057 @@
+/*
+ *  drivers/mtd/nand_bbt.c
+ *
+ *  Overview:
+ *   Bad block table support for the NAND driver
+ *   
+ *  Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * $Id: nand_bbt.c,v 1.24 2004/06/28 08:25:35 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.
+ *
+ * Description:
+ *
+ * When nand_scan_bbt is called, then it tries to find the bad block table 
+ * depending on the options in the bbt descriptor(s). If a bbt is found 
+ * then the contents are read and the memory based bbt is created. If a 
+ * mirrored bbt is selected then the mirror is searched too and the
+ * versions are compared. If the mirror has a greater version number 
+ * than the mirror bbt is used to build the memory based bbt.
+ * If the tables are not versioned, then we "or" the bad block information.
+ * If one of the bbt's is out of date or does not exist it is (re)created. 
+ * If no bbt exists at all then the device is scanned for factory marked 
+ * good / bad blocks and the bad block tables are created. 
+ *
+ * For manufacturer created bbts like the one found on M-SYS DOC devices 
+ * the bbt is searched and read but never created
+ *
+ * The autogenerated bad block table is located in the last good blocks 
+ * of the device. The table is mirrored, so it can be updated eventually. 
+ * The table is marked in the oob area with an ident pattern and a version 
+ * number which indicates which of both tables is more up to date.
+ *
+ * The table uses 2 bits per block
+ * 11b: 	block is good
+ * 00b: 	block is factory marked bad
+ * 01b, 10b: 	block is marked bad due to wear
+ *
+ * The memory bad block table uses the following scheme:
+ * 00b:		block is good
+ * 01b:		block is marked bad due to wear
+ * 10b:		block is reserved (to protect the bbt area)
+ * 11b:		block is factory marked bad
+ * 
+ * Multichip devices like DOC store the bad block info per floor.
+ *
+ * Following assumptions are made:
+ * - bbts start at a page boundary, if autolocated on a block boundary
+ * - the space neccecary for a bbt in FLASH does not exceed a block boundary
+ * 
+ */
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/compatmac.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+
+
+/** 
+ * check_pattern - [GENERIC] check if a pattern is in the buffer
+ * @buf:	the buffer to search
+ * @len:	the length of buffer to search
+ * @paglen:	the pagelength
+ * @td:		search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block
+ * tables and good / bad block identifiers.
+ * If the SCAN_EMPTY option is set then check, if all bytes except the
+ * pattern area contain 0xff
+ *
+*/
+static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
+{
+	int i, end;
+	uint8_t *p = buf;
+
+	end = paglen + td->offs;
+	if (td->options & NAND_BBT_SCANEMPTY) {
+		for (i = 0; i < end; i++) {
+			if (p[i] != 0xff)
+				return -1;
+		}
+	}	
+	p += end;
+	
+	/* Compare the pattern */
+	for (i = 0; i < td->len; i++) {
+		if (p[i] != td->pattern[i])
+			return -1;
+	}
+
+	p += td->len;
+	end += td->len;
+	if (td->options & NAND_BBT_SCANEMPTY) {
+		for (i = end; i < len; i++) {
+			if (*p++ != 0xff)
+				return -1;
+		}
+	}
+	return 0;
+}
+
+/**
+ * read_bbt - [GENERIC] Read the bad block table starting from page
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @page:	the starting page
+ * @num:	the number of bbt descriptors to read
+ * @bits:	number of bits per block
+ * @offs:	offset in the memory table
+ *
+ * Read the bad block table starting from page.
+ *
+ */
+static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num, 
+	int bits, int offs, int reserved_block_code)
+{
+	int res, i, j, act = 0;
+	struct nand_chip *this = mtd->priv;
+	size_t retlen, len, totlen;
+	loff_t from;
+	uint8_t msk = (uint8_t) ((1 << bits) - 1);
+
+	totlen = (num * bits) >> 3;
+	from = ((loff_t)page) << this->page_shift;
+	
+	while (totlen) {
+		len = min (totlen, (size_t) (1 << this->bbt_erase_shift));
+		res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob);
+		if (res < 0) {
+			if (retlen != len) {
+				printk (KERN_INFO "nand_bbt: Error reading bad block table\n");
+				return res;
+			}
+			printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
+		}	
+
+		/* Analyse data */
+		for (i = 0; i < len; i++) {
+			uint8_t dat = buf[i];
+			for (j = 0; j < 8; j += bits, act += 2) {
+				uint8_t tmp = (dat >> j) & msk;
+				if (tmp == msk)
+					continue;
+				if (reserved_block_code &&
+				    (tmp == reserved_block_code)) {
+					printk (KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n",
+						((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+					this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
+					continue;
+				}
+				/* Leave it for now, if its matured we can move this
+				 * message to MTD_DEBUG_LEVEL0 */
+				printk (KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n",
+					((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
+				/* Factory marked bad or worn out ? */	
+				if (tmp == 0)
+					this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
+				else
+					this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
+			}	
+		}
+		totlen -= len;
+		from += len;
+	}
+	return 0;
+}
+
+/**
+ * read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table 
+ * @chip:	read the table for a specific chip, -1 read all chips.
+ *		Applies only if NAND_BBT_PERCHIP option is set
+ *
+ * Read the bad block table for all chips starting at a given page
+ * We assume that the bbt bits are in consecutive order.
+*/
+static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
+{
+	struct nand_chip *this = mtd->priv;
+	int res = 0, i;
+	int bits;
+
+	bits = td->options & NAND_BBT_NRBITS_MSK;
+	if (td->options & NAND_BBT_PERCHIP) {
+		int offs = 0;
+		for (i = 0; i < this->numchips; i++) {
+			if (chip == -1 || chip == i)
+				res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code);
+			if (res)
+				return res;
+			offs += this->chipsize >> (this->bbt_erase_shift + 2);
+		}
+	} else {
+		res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code);
+		if (res)
+			return res;
+	}
+	return 0;
+}
+
+/**
+ * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table 
+ * @md:		descriptor for the bad block table mirror
+ *
+ * Read the bad block table(s) for all chips starting at a given page
+ * We assume that the bbt bits are in consecutive order.
+ *
+*/
+static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td,
+	struct nand_bbt_descr *md)
+{
+	struct nand_chip *this = mtd->priv;
+
+	/* Read the primary version, if available */	
+	if (td->options & NAND_BBT_VERSION) {
+		nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); 
+		td->version[0] = buf[mtd->oobblock + td->veroffs];
+		printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]);
+	}
+
+	/* Read the mirror version, if available */	
+	if (md && (md->options & NAND_BBT_VERSION)) {
+		nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); 
+		md->version[0] = buf[mtd->oobblock + md->veroffs];
+		printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]);
+	}
+
+	return 1;
+}
+
+/**
+ * create_bbt - [GENERIC] Create a bad block table by scanning the device
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @bd:		descriptor for the good/bad block search pattern
+ * @chip:	create the table for a specific chip, -1 read all chips.
+ *		Applies only if NAND_BBT_PERCHIP option is set
+ *
+ * Create a bad block table by scanning the device
+ * for the given good/bad block identify pattern
+ */
+static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
+{
+	struct nand_chip *this = mtd->priv;
+	int i, j, numblocks, len, scanlen;
+	int startblock;
+	loff_t from;
+	size_t readlen, ooblen;
+	static int max_bblock_reports = 10;
+
+	printk (KERN_INFO "Scanning device for bad blocks\n");
+
+	if (bd->options & NAND_BBT_SCANALLPAGES)
+		len = 1 << (this->bbt_erase_shift - this->page_shift);
+	else {
+		if (bd->options & NAND_BBT_SCAN2NDPAGE)
+			len = 2;
+		else	
+			len = 1;
+	}
+	scanlen	= mtd->oobblock + mtd->oobsize;
+	readlen = len * mtd->oobblock;
+	ooblen = len * mtd->oobsize;
+
+	if (chip == -1) {
+		/* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it
+		 * makes shifting and masking less painful */
+		numblocks = mtd->size >> (this->bbt_erase_shift - 1);
+		startblock = 0;
+		from = 0;
+	} else {
+		if (chip >= this->numchips) {
+			printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
+				chip + 1, this->numchips);
+			return;	
+		}
+		numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
+		startblock = chip * numblocks;
+		numblocks += startblock;
+		from = startblock << (this->bbt_erase_shift - 1);
+	}
+	
+	for (i = startblock; i < numblocks;) {
+		nand_read_raw (mtd, buf, from, readlen, ooblen);
+		for (j = 0; j < len; j++) {
+			if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
+				this->bbt[i >> 3] |= 0x03 << (i & 0x6);
+				if (max_bblock_reports) {
+				    printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n", 
+					    i >> 1, (unsigned int) from);
+				    --max_bblock_reports;
+				}
+				break;
+			}
+		}
+		i += 2;
+		from += (1 << this->bbt_erase_shift);
+	}
+}
+
+/**
+ * search_bbt - [GENERIC] scan the device for a specific bad block table
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table
+ *
+ * Read the bad block table by searching for a given ident pattern.
+ * Search is preformed either from the beginning up or from the end of 
+ * the device downwards. The search starts always at the start of a
+ * block.
+ * If the option NAND_BBT_PERCHIP is given, each chip is searched 
+ * for a bbt, which contains the bad block information of this chip.
+ * This is neccecary to provide support for certain DOC devices.
+ *
+ * The bbt ident pattern resides in the oob area of the first page 
+ * in a block. 
+ */
+static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
+{
+	struct nand_chip *this = mtd->priv;
+	int i, chips;
+	int bits, startblock, block, dir;
+	int scanlen = mtd->oobblock + mtd->oobsize;
+	int bbtblocks;
+
+	/* Search direction top -> down ? */
+	if (td->options & NAND_BBT_LASTBLOCK) {
+		startblock = (mtd->size >> this->bbt_erase_shift) -1;
+		dir = -1;
+	} else {
+		startblock = 0;	
+		dir = 1;
+	}	
+	
+	/* Do we have a bbt per chip ? */
+	if (td->options & NAND_BBT_PERCHIP) {
+		chips = this->numchips;
+		bbtblocks = this->chipsize >> this->bbt_erase_shift;
+		startblock &= bbtblocks - 1;
+	} else {
+		chips = 1;
+		bbtblocks = mtd->size >> this->bbt_erase_shift;
+	}
+	
+	/* Number of bits for each erase block in the bbt */
+	bits = td->options & NAND_BBT_NRBITS_MSK;
+	
+	for (i = 0; i < chips; i++) {
+		/* Reset version information */
+		td->version[i] = 0;	
+		td->pages[i] = -1;
+		/* Scan the maximum number of blocks */
+		for (block = 0; block < td->maxblocks; block++) {
+			int actblock = startblock + dir * block;
+			/* Read first page */
+			nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize); 
+			if (!check_pattern(buf, scanlen, mtd->oobblock, td)) {
+				td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift);
+				if (td->options & NAND_BBT_VERSION) {
+					td->version[i] = buf[mtd->oobblock + td->veroffs];
+				}
+				break;
+			}
+		}
+		startblock += this->chipsize >> this->bbt_erase_shift;
+	}
+	/* Check, if we found a bbt for each requested chip */
+	for (i = 0; i < chips; i++) {
+		if (td->pages[i] == -1)
+			printk (KERN_WARNING "Bad block table not found for chip %d\n", i);
+		else
+			printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], td->version[i]);
+	}
+	return 0;	
+}
+
+/**
+ * search_read_bbts - [GENERIC] scan the device for bad block table(s)
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table 
+ * @md:		descriptor for the bad block table mirror
+ *
+ * Search and read the bad block table(s)
+*/
+static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf, 
+	struct nand_bbt_descr *td, struct nand_bbt_descr *md)
+{
+	/* Search the primary table */
+	search_bbt (mtd, buf, td);
+		
+	/* Search the mirror table */
+	if (md)
+		search_bbt (mtd, buf, md);
+	
+	/* Force result check */
+	return 1;	
+}
+	
+
+/** 
+ * write_bbt - [GENERIC] (Re)write the bad block table
+ *
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @td:		descriptor for the bad block table 
+ * @md:		descriptor for the bad block table mirror
+ * @chipsel:	selector for a specific chip, -1 for all
+ *
+ * (Re)write the bad block table
+ *
+*/
+static int write_bbt (struct mtd_info *mtd, uint8_t *buf, 
+	struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel)
+{
+	struct nand_chip *this = mtd->priv;
+	struct nand_oobinfo oobinfo;
+	struct erase_info einfo;
+	int i, j, res, chip = 0;
+	int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
+	int nrchips, bbtoffs, pageoffs;
+	uint8_t msk[4];
+	uint8_t rcode = td->reserved_block_code;
+	size_t retlen, len = 0;
+	loff_t to;
+
+	if (!rcode)
+		rcode = 0xff;
+	/* Write bad block table per chip rather than per device ? */
+	if (td->options & NAND_BBT_PERCHIP) {
+		numblocks = (int) (this->chipsize >> this->bbt_erase_shift);
+		/* Full device write or specific chip ? */	
+		if (chipsel == -1) {
+			nrchips = this->numchips;
+		} else {
+			nrchips = chipsel + 1;
+			chip = chipsel;
+		}
+	} else {
+		numblocks = (int) (mtd->size >> this->bbt_erase_shift);
+		nrchips = 1;
+	}	
+	
+	/* Loop through the chips */
+	for (; chip < nrchips; chip++) {
+		
+		/* There was already a version of the table, reuse the page 
+		 * This applies for absolute placement too, as we have the 
+		 * page nr. in td->pages.
+		 */
+		if (td->pages[chip] != -1) {
+			page = td->pages[chip];
+			goto write;
+		}	
+
+		/* Automatic placement of the bad block table */
+		/* Search direction top -> down ? */
+		if (td->options & NAND_BBT_LASTBLOCK) {
+			startblock = numblocks * (chip + 1) - 1;
+			dir = -1;
+		} else {
+			startblock = chip * numblocks;
+			dir = 1;
+		}	
+
+		for (i = 0; i < td->maxblocks; i++) {
+			int block = startblock + dir * i;
+			/* Check, if the block is bad */
+			switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) {
+			case 0x01:
+			case 0x03:
+				continue;
+			}
+			page = block << (this->bbt_erase_shift - this->page_shift);
+			/* Check, if the block is used by the mirror table */
+			if (!md || md->pages[chip] != page)
+				goto write;
+		}
+		printk (KERN_ERR "No space left to write bad block table\n");
+		return -ENOSPC;
+write:	
+
+		/* Set up shift count and masks for the flash table */
+		bits = td->options & NAND_BBT_NRBITS_MSK;
+		switch (bits) {
+		case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break;
+		case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break;
+		case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break;
+		case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break;
+		default: return -EINVAL;
+		}
+		
+		bbtoffs = chip * (numblocks >> 2);
+		
+		to = ((loff_t) page) << this->page_shift;
+
+		memcpy (&oobinfo, this->autooob, sizeof(oobinfo));
+		oobinfo.useecc = MTD_NANDECC_PLACEONLY;
+		
+		/* Must we save the block contents ? */
+		if (td->options & NAND_BBT_SAVECONTENT) {
+			/* Make it block aligned */
+			to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));
+			len = 1 << this->bbt_erase_shift;
+			res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
+			if (res < 0) {
+				if (retlen != len) {
+					printk (KERN_INFO "nand_bbt: Error reading block for writing the bad block table\n");
+					return res;
+				}
+				printk (KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n");
+			}
+			/* Calc the byte offset in the buffer */
+			pageoffs = page - (int)(to >> this->page_shift);
+			offs = pageoffs << this->page_shift;
+			/* Preset the bbt area with 0xff */
+			memset (&buf[offs], 0xff, (size_t)(numblocks >> sft));
+			/* Preset the bbt's oob area with 0xff */
+			memset (&buf[len + pageoffs * mtd->oobsize], 0xff,
+				((len >> this->page_shift) - pageoffs) * mtd->oobsize);
+			if (td->options & NAND_BBT_VERSION) {
+				buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip];
+			}
+		} else {
+			/* Calc length */
+			len = (size_t) (numblocks >> sft);
+			/* Make it page aligned ! */
+			len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1);
+			/* Preset the buffer with 0xff */
+			memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize);
+			offs = 0;
+			/* Pattern is located in oob area of first page */
+			memcpy (&buf[len + td->offs], td->pattern, td->len);
+			if (td->options & NAND_BBT_VERSION) {
+				buf[len + td->veroffs] = td->version[chip];
+			}
+		}
+	
+		/* walk through the memory table */
+		for (i = 0; i < numblocks; ) {
+			uint8_t dat;
+			dat = this->bbt[bbtoffs + (i >> 2)];
+			for (j = 0; j < 4; j++ , i++) {
+				int sftcnt = (i << (3 - sft)) & sftmsk;
+				/* Do not store the reserved bbt blocks ! */
+				buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt);
+				dat >>= 2;
+			}
+		}
+		
+		memset (&einfo, 0, sizeof (einfo));
+		einfo.mtd = mtd;
+		einfo.addr = (unsigned long) to;
+		einfo.len = 1 << this->bbt_erase_shift;
+		res = nand_erase_nand (mtd, &einfo, 1);
+		if (res < 0) {
+			printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n", res);
+			return res;
+		}
+	
+		res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
+		if (res < 0) {
+			printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res);
+			return res;
+		}
+		printk (KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n", 
+			(unsigned int) to, td->version[chip]);
+	
+		/* Mark it as used */
+		td->pages[chip] = page;
+	}	
+	return 0;
+}
+
+/**
+ * nand_memory_bbt - [GENERIC] create a memory based bad block table
+ * @mtd:	MTD device structure
+ * @bd:		descriptor for the good/bad block search pattern
+ *
+ * The function creates a memory based bbt by scanning the device 
+ * for manufacturer / software marked good / bad blocks
+*/
+static int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+	struct nand_chip *this = mtd->priv;
+
+	/* Ensure that we only scan for the pattern and nothing else */
+	bd->options = 0;
+	create_bbt (mtd, this->data_buf, bd, -1);
+	return 0;
+}
+
+/**
+ * check_create - [GENERIC] create and write bbt(s) if neccecary
+ * @mtd:	MTD device structure
+ * @buf:	temporary buffer
+ * @bd:		descriptor for the good/bad block search pattern
+ *
+ * The function checks the results of the previous call to read_bbt
+ * and creates / updates the bbt(s) if neccecary
+ * Creation is neccecary if no bbt was found for the chip/device
+ * Update is neccecary if one of the tables is missing or the
+ * version nr. of one table is less than the other
+*/
+static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
+{
+	int i, chips, writeops, chipsel, res;
+	struct nand_chip *this = mtd->priv;
+	struct nand_bbt_descr *td = this->bbt_td;
+	struct nand_bbt_descr *md = this->bbt_md;
+	struct nand_bbt_descr *rd, *rd2;
+
+	/* Do we have a bbt per chip ? */
+	if (td->options & NAND_BBT_PERCHIP) 
+		chips = this->numchips;
+	else 
+		chips = 1;
+	
+	for (i = 0; i < chips; i++) {
+		writeops = 0;
+		rd = NULL;
+		rd2 = NULL;
+		/* Per chip or per device ? */
+		chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
+		/* Mirrored table avilable ? */
+		if (md) {
+			if (td->pages[i] == -1 && md->pages[i] == -1) {
+				writeops = 0x03;
+				goto create;
+			}
+
+			if (td->pages[i] == -1) {
+				rd = md;				
+				td->version[i] = md->version[i];
+				writeops = 1;
+				goto writecheck;
+			}
+
+			if (md->pages[i] == -1) {
+				rd = td;
+				md->version[i] = td->version[i];
+				writeops = 2;
+				goto writecheck;
+			}
+
+			if (td->version[i] == md->version[i]) {
+				rd = td;
+				if (!(td->options & NAND_BBT_VERSION))
+					rd2 = md;
+				goto writecheck;
+			}	
+
+			if (((int8_t) (td->version[i] - md->version[i])) > 0) {
+				rd = td;
+				md->version[i] = td->version[i];
+				writeops = 2;
+			} else {
+				rd = md;
+				td->version[i] = md->version[i];
+				writeops = 1;
+			}
+
+			goto writecheck;
+
+		} else {
+			if (td->pages[i] == -1) {
+				writeops = 0x01;
+				goto create;
+			}
+			rd = td;
+			goto writecheck;
+		}
+create:
+		/* Create the bad block table by scanning the device ? */
+		if (!(td->options & NAND_BBT_CREATE))
+			continue;	
+		
+		/* Create the table in memory by scanning the chip(s) */
+		create_bbt (mtd, buf, bd, chipsel);
+		
+		td->version[i] = 1;
+		if (md)
+			md->version[i] = 1;	
+writecheck:	
+		/* read back first ? */
+		if (rd)
+			read_abs_bbt (mtd, buf, rd, chipsel);
+		/* If they weren't versioned, read both. */
+		if (rd2)
+			read_abs_bbt (mtd, buf, rd2, chipsel);
+
+		/* Write the bad block table to the device ? */
+		if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+			res = write_bbt (mtd, buf, td, md, chipsel);
+			if (res < 0)
+				return res;
+		}
+		
+		/* Write the mirror bad block table to the device ? */
+		if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
+			res = write_bbt (mtd, buf, md, td, chipsel);
+			if (res < 0)
+				return res;
+		}
+	}
+	return 0;	
+}
+
+/**
+ * mark_bbt_regions - [GENERIC] mark the bad block table regions 
+ * @mtd:	MTD device structure
+ * @td:		bad block table descriptor
+ *
+ * The bad block table regions are marked as "bad" to prevent
+ * accidental erasures / writes. The regions are identified by
+ * the mark 0x02.
+*/
+static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
+{
+	struct nand_chip *this = mtd->priv;
+	int i, j, chips, block, nrblocks, update;
+	uint8_t oldval, newval;
+
+	/* Do we have a bbt per chip ? */
+	if (td->options & NAND_BBT_PERCHIP) {
+		chips = this->numchips;
+		nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
+	} else {
+		chips = 1;
+		nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
+	}	
+	
+	for (i = 0; i < chips; i++) {
+		if ((td->options & NAND_BBT_ABSPAGE) ||
+		    !(td->options & NAND_BBT_WRITE)) {
+		    	if (td->pages[i] == -1) continue;
+			block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
+			block <<= 1;		
+			oldval = this->bbt[(block >> 3)];
+			newval = oldval | (0x2 << (block & 0x06));
+			this->bbt[(block >> 3)] = newval;
+			if ((oldval != newval) && td->reserved_block_code)
+				nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1));
+			continue;
+		}
+		update = 0;
+		if (td->options & NAND_BBT_LASTBLOCK)
+			block = ((i + 1) * nrblocks) - td->maxblocks;
+		else	
+			block = i * nrblocks;
+		block <<= 1;	
+		for (j = 0; j < td->maxblocks; j++) {
+			oldval = this->bbt[(block >> 3)];
+			newval = oldval | (0x2 << (block & 0x06));
+			this->bbt[(block >> 3)] = newval;
+			if (oldval != newval) update = 1;
+			block += 2;
+		}	
+		/* If we want reserved blocks to be recorded to flash, and some
+		   new ones have been marked, then we need to update the stored
+		   bbts.  This should only happen once. */
+		if (update && td->reserved_block_code)
+			nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1));
+	}
+}
+
+/**
+ * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
+ * @mtd:	MTD device structure
+ * @bd:		descriptor for the good/bad block search pattern
+ *
+ * The function checks, if a bad block table(s) is/are already 
+ * available. If not it scans the device for manufacturer
+ * marked good / bad blocks and writes the bad block table(s) to
+ * the selected place.
+ *
+ * The bad block table memory is allocated here. It must be freed
+ * by calling the nand_free_bbt function.
+ *
+*/
+int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+	struct nand_chip *this = mtd->priv;
+	int len, res = 0;
+	uint8_t *buf;
+	struct nand_bbt_descr *td = this->bbt_td;
+	struct nand_bbt_descr *md = this->bbt_md;
+
+	len = mtd->size >> (this->bbt_erase_shift + 2);
+	/* Allocate memory (2bit per block) */
+	this->bbt = (uint8_t *) kmalloc (len, GFP_KERNEL);
+	if (!this->bbt) {
+		printk (KERN_ERR "nand_scan_bbt: Out of memory\n");
+		return -ENOMEM;
+	}
+	/* Clear the memory bad block table */
+	memset (this->bbt, 0x00, len);
+
+	/* If no primary table decriptor is given, scan the device
+	 * to build a memory based bad block table
+	 */
+	if (!td)
+		return nand_memory_bbt(mtd, bd);
+
+	/* Allocate a temporary buffer for one eraseblock incl. oob */
+	len = (1 << this->bbt_erase_shift);
+	len += (len >> this->page_shift) * mtd->oobsize;
+	buf = kmalloc (len, GFP_KERNEL);
+	if (!buf) {
+		printk (KERN_ERR "nand_bbt: Out of memory\n");
+		kfree (this->bbt);
+		this->bbt = NULL;
+		return -ENOMEM;
+	}
+	
+	/* Is the bbt at a given page ? */
+	if (td->options & NAND_BBT_ABSPAGE) {
+		res = read_abs_bbts (mtd, buf, td, md);
+	} else {	
+		/* Search the bad block table using a pattern in oob */
+		res = search_read_bbts (mtd, buf, td, md);
+	}	
+
+	if (res) 
+		res = check_create (mtd, buf, bd);
+	
+	/* Prevent the bbt regions from erasing / writing */
+	mark_bbt_region (mtd, td);
+	if (md)
+		mark_bbt_region (mtd, md);
+	
+	kfree (buf);
+	return res;
+}
+
+
+/**
+ * nand_update_bbt - [NAND Interface] update bad block table(s) 
+ * @mtd:	MTD device structure
+ * @offs:	the offset of the newly marked block
+ *
+ * The function updates the bad block table(s)
+*/
+int nand_update_bbt (struct mtd_info *mtd, loff_t offs)
+{
+	struct nand_chip *this = mtd->priv;
+	int len, res = 0, writeops = 0;
+	int chip, chipsel;
+	uint8_t *buf;
+	struct nand_bbt_descr *td = this->bbt_td;
+	struct nand_bbt_descr *md = this->bbt_md;
+
+	if (!this->bbt || !td)
+		return -EINVAL;
+
+	len = mtd->size >> (this->bbt_erase_shift + 2);
+	/* Allocate a temporary buffer for one eraseblock incl. oob */
+	len = (1 << this->bbt_erase_shift);
+	len += (len >> this->page_shift) * mtd->oobsize;
+	buf = kmalloc (len, GFP_KERNEL);
+	if (!buf) {
+		printk (KERN_ERR "nand_update_bbt: Out of memory\n");
+		return -ENOMEM;
+	}
+	
+	writeops = md != NULL ? 0x03 : 0x01;
+
+	/* Do we have a bbt per chip ? */
+	if (td->options & NAND_BBT_PERCHIP) {
+		chip = (int) (offs >> this->chip_shift);
+		chipsel = chip;
+	} else {
+		chip = 0;
+		chipsel = -1;
+	}
+
+	td->version[chip]++;
+	if (md)
+		md->version[chip]++;	
+
+	/* Write the bad block table to the device ? */
+	if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
+		res = write_bbt (mtd, buf, td, md, chipsel);
+		if (res < 0)
+			goto out;
+	}
+	/* Write the mirror bad block table to the device ? */
+	if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
+		res = write_bbt (mtd, buf, md, td, chipsel);
+	}
+
+out:	
+	kfree (buf);
+	return res;
+}
+
+/* Define some generic bad / good block scan pattern which are used 
+ * while scanning a device for factory marked good / bad blocks
+ * 
+ * The memory based patterns just 
+ */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr smallpage_memorybased = {
+	.options = 0,
+	.offs = 5,
+	.len = 1,
+	.pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr largepage_memorybased = {
+	.options = 0,
+	.offs = 0,
+	.len = 2,
+	.pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr smallpage_flashbased = {
+	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+	.offs = 5,
+	.len = 1,
+	.pattern = scan_ff_pattern
+};
+
+static struct nand_bbt_descr largepage_flashbased = {
+	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+	.offs = 0,
+	.len = 2,
+	.pattern = scan_ff_pattern
+};
+
+static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 };
+
+static struct nand_bbt_descr agand_flashbased = {
+	.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
+	.offs = 0x20,
+	.len = 6,
+	.pattern = scan_agand_pattern
+};
+
+/* Generic flash bbt decriptors
+*/
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr bbt_main_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	8,
+	.len = 4,
+	.veroffs = 12,
+	.maxblocks = 4,
+	.pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr bbt_mirror_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE 
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs =	8,
+	.len = 4,
+	.veroffs = 12,
+	.maxblocks = 4,
+	.pattern = mirror_pattern
+};
+
+/**
+ * nand_default_bbt - [NAND Interface] Select a default bad block table for the device 
+ * @mtd:	MTD device structure
+ *
+ * This function selects the default bad block table
+ * support for the device and calls the nand_scan_bbt function
+ *
+*/
+int nand_default_bbt (struct mtd_info *mtd)
+{
+	struct nand_chip *this = mtd->priv;
+	
+	/* Default for AG-AND. We must use a flash based 
+	 * bad block table as the devices have factory marked
+	 * _good_ blocks. Erasing those blocks leads to loss
+	 * of the good / bad information, so we _must_ store
+	 * this information in a good / bad table during 
+	 * startup
+	*/
+	if (this->options & NAND_IS_AND) {
+		/* Use the default pattern descriptors */
+		if (!this->bbt_td) {	
+			this->bbt_td = &bbt_main_descr;
+			this->bbt_md = &bbt_mirror_descr;
+		}	
+		this->options |= NAND_USE_FLASH_BBT;
+		return nand_scan_bbt (mtd, &agand_flashbased);
+	}
+	
+	/* Is a flash based bad block table requested ? */
+	if (this->options & NAND_USE_FLASH_BBT) {
+		/* Use the default pattern descriptors */	
+		if (!this->bbt_td) {	
+			this->bbt_td = &bbt_main_descr;
+			this->bbt_md = &bbt_mirror_descr;
+		}	
+		if (mtd->oobblock > 512)
+			return nand_scan_bbt (mtd, &largepage_flashbased);
+		else	
+			return nand_scan_bbt (mtd, &smallpage_flashbased);			
+	} else {
+		this->bbt_td = NULL;
+		this->bbt_md = NULL;
+		if (mtd->oobblock > 512)
+			return nand_scan_bbt (mtd, &largepage_memorybased);
+		else
+			return nand_scan_bbt (mtd, &smallpage_memorybased);
+	}
+}
+
+/**
+ * nand_isbad_bbt - [NAND Interface] Check if a block is bad 
+ * @mtd:	MTD device structure
+ * @offs:	offset in the device
+ * @allowbbt:	allow access to bad block table region
+ *
+*/
+int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
+{
+	struct nand_chip *this = mtd->priv;
+	int block;
+	uint8_t	res;
+	
+	/* Get block number * 2 */
+	block = (int) (offs >> (this->bbt_erase_shift - 1));
+	res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
+
+	DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n", 
+		(unsigned int)offs, res, block >> 1);
+
+	switch ((int)res) {
+	case 0x00:	return 0;
+	case 0x01:	return 1;
+	case 0x02:	return allowbbt ? 0 : 1;
+	}
+	return 1;
+}
+
+EXPORT_SYMBOL (nand_scan_bbt);
+EXPORT_SYMBOL (nand_default_bbt);
diff -uNrb linux-2.4.30/drivers/mtd/nand/nand_ecc.c linux/drivers/mtd/nand/nand_ecc.c
--- linux-2.4.30/drivers/mtd/nand/nand_ecc.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/nand/nand_ecc.c	Tue Sep 28 14:38:13 2004
@@ -1,22 +1,45 @@
 /*
- *  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)
+ * drivers/mtd/nand/nand_ecc.c
+ *
+ * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
  *                     Toshiba America Electronics Components, Inc.
  *
- * $Id: nand_ecc.c,v 1.8 2002/09/16 09:19:53 dwmw2 Exp $
+ * $Id: nand_ecc.c,v 1.14 2004/06/16 15:34:37 gleixner Exp $
  *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU Lesser General Public License
- * version 2.1 as published by the Free Software Foundation.
+ * This 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 contains an ECC algorithm from Toshiba that detects and
- * corrects 1 bit errors in a 256 byte block of data.
+ * 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>
+#include <linux/mtd/mtd.h>
 
 /*
  * Pre-calculated 256-way 1 byte column parity
@@ -41,7 +64,12 @@
 };
 
 
-/*
+/**
+ * nand_trans_result - [GENERIC] create non-inverted ECC
+ * @reg2:	line parity reg 2
+ * @reg3:	line parity reg 3
+ * @ecc_code:	ecc 
+ *
  * Creates non-inverted ECC code from line parity
  */
 static void nand_trans_result(u_char reg2, u_char reg3,
@@ -81,10 +109,13 @@
 	ecc_code[1] = tmp2;
 }
 
-/*
- * Calculate 3 byte ECC code for 256 byte block
+/**
+ * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for 256 byte block
+ * @mtd:	MTD block structure
+ * @dat:	raw data
+ * @ecc_code:	buffer for ECC
  */
-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code)
+int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
 {
 	u_char idx, reg1, reg2, reg3;
 	int j;
@@ -114,12 +145,19 @@
 	ecc_code[0] = ~ecc_code[0];
 	ecc_code[1] = ~ecc_code[1];
 	ecc_code[2] = ((~reg1) << 2) | 0x03;
+	return 0;
 }
 
-/*
+/**
+ * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
+ * @mtd:	MTD block structure
+ * @dat:	raw data read from the chip
+ * @read_ecc:	ECC from the chip
+ * @calc_ecc:	the ECC calculated from raw data
+ *
  * Detect and correct a 1 bit error for 256 byte block
  */
-int nand_correct_data (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;
 	
@@ -133,6 +171,8 @@
 		return 0;
 	}
 	else {
+                DEBUG(MTD_DEBUG_LEVEL2, "nand_correct_data ecc mismatch: calc %x %x %x read %x %x %x\n",
+                        calc_ecc[0], calc_ecc[1], calc_ecc[2], read_ecc[0], read_ecc[1], read_ecc[2]);
 		a = (d1 ^ (d1 >> 1)) & 0x55;
 		b = (d2 ^ (d2 >> 1)) & 0x55;
 		c = (d3 ^ (d3 >> 1)) & 0x54;
@@ -209,5 +249,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 -uNrb linux-2.4.30/drivers/mtd/nand/nand_ids.c linux/drivers/mtd/nand/nand_ids.c
--- linux-2.4.30/drivers/mtd/nand/nand_ids.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/nand/nand_ids.c	Fri Sep 10 15:15:04 2004
@@ -3,8 +3,7 @@
  *
  *  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.10 2004/05/26 13:40:12 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,99 @@
  */
 #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},
+
+	/* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout ! 
+	 * The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes
+	 * 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7
+	 * Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go
+	 * There are more speed improvements for reads and writes possible, but not implemented now 
+	 */
+	{"AND 128MiB 3,3V 8-bit",	0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY},
+
 	{NULL,}
 };
 
@@ -44,9 +116,10 @@
 	{NAND_MFR_SAMSUNG, "Samsung"},
 	{NAND_MFR_FUJITSU, "Fujitsu"},
 	{NAND_MFR_NATIONAL, "National"},
+	{NAND_MFR_RENESAS, "Renesas"},
+	{NAND_MFR_STMICRO, "ST Micro"},
 	{0x0, "Unknown"}
 };
-
 
 EXPORT_SYMBOL (nand_manuf_ids);
 EXPORT_SYMBOL (nand_flash_ids);
diff -uNrb linux-2.4.30/drivers/mtd/nand/ppchameleonevb.c linux/drivers/mtd/nand/ppchameleonevb.c
--- linux-2.4.30/drivers/mtd/nand/ppchameleonevb.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/nand/ppchameleonevb.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,419 @@
+/*
+ *  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.3 2004/09/16 23:27:14 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
+
+#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;
+	void __iomem *ppchameleon_fio_base;
+	void __iomem *ppchameleonevb_fio_base;
+
+
+	/*********************************
+	* Processor module NAND (if any) *
+	*********************************/
+	/* Allocate memory for MTD device structure and private data */
+	ppchameleon_mtd = (struct mtd_info *) 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 = (void __iomem *) 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;
+
+	/* 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 = (struct mtd_info *) 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 = (void __iomem *)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;
+
+	/* 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;
+
+	/* Release resources, unregister device(s) */
+	nand_release (ppchameleon_mtd);
+	nand_release (ppchameleonevb_mtd);
+	
+	/* Release iomaps */
+	this = (struct nand_chip *) &ppchameleon_mtd[1];
+	iounmap((void *) this->IO_ADDR_R;
+	this = (struct nand_chip *) &ppchameleonevb_mtd[1];
+	iounmap((void *) this->IO_ADDR_R;
+
+	/* Free the MTD device structure */
+	kfree (ppchameleon_mtd);
+	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 -uNrb linux-2.4.30/drivers/mtd/nand/rbmipsnand.c linux/drivers/mtd/nand/rbmipsnand.c
--- linux-2.4.30/drivers/mtd/nand/rbmipsnand.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/nand/rbmipsnand.c	Wed Mar 23 16:40:18 2005
@@ -0,0 +1,248 @@
+#include <linux/init.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <asm/rc32434/rb.h>
+#include <linux/delay.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+
+#define IDT434_REG_BASE ((volatile void *) KSEG1ADDR(0x18000000))
+#define GPIOF 0x050000
+#define GPIOC 0x050004
+#define GPIOD 0x050008
+
+#define GPIO_RDY (1 << 0x08)
+#define GPIO_WPX (1 << 0x09)
+#define GPIO_ALE (1 << 0x0a)
+#define GPIO_CLE (1 << 0x0b)
+
+extern unsigned int board_type;
+
+static int rbmips_dev_ready(struct mtd_info *mtd) {
+    return (*(volatile unsigned *) (IDT434_REG_BASE + GPIOD)) & GPIO_RDY;
+}
+
+/*
+static int dummybbtscan(struct mtd_info *mtd) { return 0; }
+static int dummyblockbad(struct mtd_info *mtd, loff_t ofs, int getchip) { return 0; }
+static int dummyblockmarkbad(struct mtd_info *mtd, loff_t ofs) { return 0; }
+
+static void rbmips_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
+{
+        int i;
+        struct nand_chip *this = mtd->priv;
+	printk("RBW:");
+        for (i=0; i<len; i++) {
+                printk(" %x%x", buf[i] >> 4, buf[i] & 15);
+                writeb(buf[i], this->IO_ADDR_W);
+	}
+	printk("\n");
+}
+
+static void rbmips_read_buf(struct mtd_info *mtd, u_char *buf, int len)
+{
+        int i;
+        struct nand_chip *this = mtd->priv;
+	printk("RB<:");
+        for (i=0; i<len; i++) {
+                buf[i] = readb(this->IO_ADDR_R);
+                printk(" %x%x", buf[i] >> 4, buf[i] & 15);
+	}
+	printk("\n");
+}
+
+static u_char rbmips_read_byte(struct mtd_info *mtd) {
+    u_char ret;
+    struct nand_chip *this = mtd->priv;
+    ret = readb(this->IO_ADDR_R);
+    printk("rbmips_read_byte %x %p\n", ret, this->IO_ADDR_R);
+    return ret;
+}
+
+static void rbmips_write_byte(struct mtd_info *mtd, u_char byte) {
+    struct nand_chip *this = mtd->priv;
+    writeb(byte, this->IO_ADDR_W);
+    printk("rbmips_write_byte %x %p\n", byte, this->IO_ADDR_W);
+}
+*/
+
+static unsigned long iflags = 0;
+static int ioff = 0;
+
+static void rbmips_select_chip(struct mtd_info *mtd, int chip)
+{
+    // struct nand_chip *this = mtd->priv;
+        switch(chip) {
+        case -1:
+                if (ioff) {
+			ioff = 0;
+                        restore_flags(iflags);
+//	                DEBUG (MTD_DEBUG_LEVEL3, "unselect chip, restore interrupts\n");
+                }
+                break;
+        case 0:
+                if (!ioff) {
+//	                DEBUG (MTD_DEBUG_LEVEL3, "select chip, disable interrupts\n");
+			ioff = 1;
+			save_flags(iflags);
+			cli();
+                }
+                break;
+
+        default:
+                BUG();
+        }
+}
+
+static void rbmips_hwcontrol400(struct mtd_info *mtd, int cmd) {
+    switch (cmd) {
+    case NAND_CTL_SETCLE:
+	// printk("NAND_CTL_SETCLE\n");
+	(*(volatile unsigned *) (IDT434_REG_BASE + GPIOD)) |= GPIO_CLE;
+        break;
+    case NAND_CTL_CLRCLE:
+	// printk("NAND_CTL_CLRCLE\n");
+	(*(volatile unsigned *) (IDT434_REG_BASE + GPIOD)) &= ~GPIO_CLE;
+        break;
+    case NAND_CTL_SETALE:
+	// printk("NAND_CTL_SETALE\n");
+	(*(volatile unsigned *) (IDT434_REG_BASE + GPIOD)) |= GPIO_ALE;
+        break;
+    case NAND_CTL_CLRALE:
+	// printk("NAND_CTL_CLRALE\n");
+	(*(volatile unsigned *) (IDT434_REG_BASE + GPIOD)) &= ~GPIO_ALE;
+        break;
+    default:
+	break;
+    }
+}
+
+static void rbmips_hwcontrol500(struct mtd_info *mtd, int cmd) {
+    switch (cmd) {
+    case NAND_CTL_SETCLE:
+	changeLatchU5(LO_CLE, 0);
+//        printk("rbmips_setcle\n");
+        break;
+    case NAND_CTL_CLRCLE:
+	changeLatchU5(0, LO_CLE);
+//        printk("rbmips_clrcle\n");
+        break;
+    case NAND_CTL_SETALE:
+	changeLatchU5(LO_ALE, 0);
+//        printk("rbmips_setale\n");
+        break;
+    case NAND_CTL_CLRALE:
+	changeLatchU5(0, LO_ALE);
+//        printk("rbmips_clrale\n");
+        break;
+    case NAND_CTL_SETNCE: {
+//        unsigned n = (*(volatile unsigned *) (IDT434_REG_BASE + GPIOD));
+//        n &= ~(1 << 13);
+//	(*(volatile unsigned *) (IDT434_REG_BASE + GPIOD)) = n;
+//        changeLatchU5(0, LO_CEX);
+//        changeLatchU5(0, 8 + 16);
+//        udelay(200);
+//        printk("rbmips_setnce\n");
+    }
+        break;
+    case NAND_CTL_CLRNCE: {
+//	unsigned n = (*(volatile unsigned *) (IDT434_REG_BASE + GPIOD));
+//        n |= ~(1 << 13);
+//	(*(volatile unsigned *) (IDT434_REG_BASE + GPIOD)) = n;
+//        changeLatchU5(LO_CEX, 0);
+//        changeLatchU5(8 + 16, 0);
+//        udelay(200);
+//        printk("rbmips_clrnce\n");
+    }
+        break;
+    }
+}
+
+static struct mtd_partition partition_info[] = {
+    {
+        name: "RouterBoard NAND Boot",
+        offset: 0,
+	size: 4 * 1024 * 1024
+    },
+    {
+        name: "RouterBoard NAND Main",
+	offset: MTDPART_OFS_NXTBLK,
+	size: MTDPART_SIZ_FULL
+    }
+};
+
+static struct mtd_info rmtd;
+static struct nand_chip rnand;
+
+int rbmips_init(void) {
+    if (board_type == 400) {
+	printk("RB400 nand\n");
+	(*(volatile unsigned *) (IDT434_REG_BASE + GPIOD)) |= GPIO_WPX;
+	(*(volatile unsigned *) (IDT434_REG_BASE + GPIOD)) &= ~GPIO_CLE;
+	(*(volatile unsigned *) (IDT434_REG_BASE + GPIOD)) &= ~GPIO_ALE;
+    }
+    if (board_type == 500) {
+	printk("RB500 nand\n");
+	changeLatchU5(LO_WPX | LO_FOFF | LO_CEX, LO_ULED | LO_ALE | LO_CLE);
+    }
+//    set434Reg(IMASK6, 5, 1, 1);
+//    set434Reg(IMASK6, 8, 1, 1);
+//    set434Reg(IMASK6, 9, 1, 1);
+//    set434Reg(IMASK6, 10, 1, 1);
+//    set434Reg(IMASK6, 11, 1, 1);
+//    set434Reg(IMASK6, 12, 1, 1);
+//    set434Reg(IMASK6, 13, 1, 1);
+
+//    set434Reg(GPIOFUNC, 8, 1, 0);
+//    set434Reg(GPIOCFG, 8, 1, 0);
+//    set434Reg(GPIOFUNC, 13, 1, 0);
+//    set434Reg(GPIOCFG, 13, 1, 1);
+    memset(&rmtd, 0, sizeof(rmtd));
+    memset(&rnand, 0, sizeof(rnand));
+    rnand.IO_ADDR_W = (unsigned char *) KSEG1ADDR(get434Reg(DEV2BASE));
+    rnand.IO_ADDR_R = rnand.IO_ADDR_W;
+    if (board_type == 400) rnand.hwcontrol = rbmips_hwcontrol400;
+    if (board_type == 500) rnand.hwcontrol = rbmips_hwcontrol500;
+    rnand.eccmode = NAND_ECC_SOFT;
+    rnand.chip_delay = 25; //TODO, really?
+    rnand.dev_ready = rbmips_dev_ready;
+    rnand.select_chip = rbmips_select_chip;
+//    rnand.read_byte = rbmips_read_byte;
+//    rnand.write_byte = rbmips_write_byte;
+//    rnand.read_buf = rbmips_read_buf;
+//    rnand.write_buf = rbmips_write_buf;
+//    rnand.scan_bbt = dummybbtscan;
+//    rnand.block_bad = dummyblockbad;
+//    rnand.block_markbad = dummyblockmarkbad;
+
+    rmtd.priv = &rnand;
+/*     printk("0x10000=%x \n", get434Reg(0x10000)); */
+/*     printk("0x10004=%x \n", get434Reg(0x10004)); */
+/*     printk("0x10008=%x \n", get434Reg(0x10008)); */
+/*     printk("0x1000C=%x \n", get434Reg(0x1000C)); */
+/*     printk("0x10010=%x \n", get434Reg(0x10010)); */
+/*     printk("0x10014=%x \n", get434Reg(0x10014)); */
+/*     printk("0x10018=%x \n", get434Reg(0x10018)); */
+/*     printk("0x1001C=%x \n", get434Reg(0x1001C)); */
+/*     printk("0x10020=%x \n", get434Reg(0x10020)); */
+/*     printk("0x10024=%x \n", get434Reg(0x10024)); */
+/*     printk("0x10028=%x \n", get434Reg(0x10028)); */
+/*     printk("0x1002C=%x \n", get434Reg(0x1002C)); */
+/*     printk("0x10030=%x \n", get434Reg(0x10030)); */
+/*     printk("0x10034=%x \n", get434Reg(0x10034)); */
+/*     printk("0x10038=%x \n", get434Reg(0x10038)); */
+/*     printk("0x1003C=%x \n", get434Reg(0x1003C)); */
+/*     printk("base=%p %p \n", rnand.IO_ADDR_R, rnand.IO_ADDR_W); */
+    if (nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1)
+	    && nand_scan(&rmtd, 1) && nand_scan(&rmtd, 1)) {
+	printk("RBxxx nand device not found");
+	return -ENXIO;
+    }
+    add_mtd_partitions(&rmtd, partition_info, 2);
+    return 0;
+}
+
+module_init(rbmips_init);
+
+
diff -uNrb linux-2.4.30/drivers/mtd/nand/rtc_from4.c linux/drivers/mtd/nand/rtc_from4.c
--- linux-2.4.30/drivers/mtd/nand/rtc_from4.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/nand/rtc_from4.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,547 @@
+/*
+ *  drivers/mtd/nand/rtc_from4.c
+ *
+ *  Copyright (C) 2004  Red Hat, Inc.
+ * 
+ *  Derived from drivers/mtd/nand/spia.c
+ *       Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+ *
+ * $Id: rtc_from4.c,v 1.4 2004/09/20 11:41:52 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 AG-AND flash device found on the
+ *   Renesas Technology Corp. Flash ROM 4-slot interface board (FROM_BOARD4), 
+ *   which utilizes the Renesas HN29V1G91T-30 part. 
+ *   This chip is a 1 GBibit (128MiB x 8 bits) AG-AND flash device.
+ */
+
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/rslib.h>
+#include <linux/module.h>
+#include <linux/mtd/compatmac.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <asm/io.h>
+
+/*
+ * MTD structure for Renesas board
+ */
+static struct mtd_info *rtc_from4_mtd = NULL;
+
+#define RTC_FROM4_MAX_CHIPS	2
+
+/* HS77x9 processor register defines */
+#define SH77X9_BCR1	((volatile unsigned short *)(0xFFFFFF60))
+#define SH77X9_BCR2	((volatile unsigned short *)(0xFFFFFF62))
+#define SH77X9_WCR1	((volatile unsigned short *)(0xFFFFFF64))
+#define SH77X9_WCR2	((volatile unsigned short *)(0xFFFFFF66))
+#define SH77X9_MCR	((volatile unsigned short *)(0xFFFFFF68))
+#define SH77X9_PCR	((volatile unsigned short *)(0xFFFFFF6C))
+#define SH77X9_FRQCR	((volatile unsigned short *)(0xFFFFFF80))
+
+/*
+ * Values specific to the Renesas Technology Corp. FROM_BOARD4 (used with HS77x9 processor)
+ */
+/* Address where flash is mapped */
+#define RTC_FROM4_FIO_BASE	0x14000000
+
+/* CLE and ALE are tied to address lines 5 & 4, respectively */
+#define RTC_FROM4_CLE		(1 << 5)
+#define RTC_FROM4_ALE		(1 << 4)
+
+/* address lines A24-A22 used for chip selection */
+#define RTC_FROM4_NAND_ADDR_SLOT3	(0x00800000)
+#define RTC_FROM4_NAND_ADDR_SLOT4	(0x00C00000)
+#define RTC_FROM4_NAND_ADDR_FPGA	(0x01000000)
+/* mask address lines A24-A22 used for chip selection */
+#define RTC_FROM4_NAND_ADDR_MASK	(RTC_FROM4_NAND_ADDR_SLOT3 | RTC_FROM4_NAND_ADDR_SLOT4 | RTC_FROM4_NAND_ADDR_FPGA)
+
+/* FPGA status register for checking device ready (bit zero) */
+#define RTC_FROM4_FPGA_SR		(RTC_FROM4_NAND_ADDR_FPGA | 0x00000002)
+#define RTC_FROM4_DEVICE_READY		0x0001
+
+/* FPGA Reed-Solomon ECC Control register */
+
+#define RTC_FROM4_RS_ECC_CTL		(RTC_FROM4_NAND_ADDR_FPGA | 0x00000050)
+#define RTC_FROM4_RS_ECC_CTL_CLR	(1 << 7)
+#define RTC_FROM4_RS_ECC_CTL_GEN	(1 << 6)
+#define RTC_FROM4_RS_ECC_CTL_FD_E	(1 << 5)
+
+/* FPGA Reed-Solomon ECC code base */
+#define RTC_FROM4_RS_ECC		(RTC_FROM4_NAND_ADDR_FPGA | 0x00000060)
+#define RTC_FROM4_RS_ECCN		(RTC_FROM4_NAND_ADDR_FPGA | 0x00000080)
+
+/* FPGA Reed-Solomon ECC check register */
+#define RTC_FROM4_RS_ECC_CHK		(RTC_FROM4_NAND_ADDR_FPGA | 0x00000070)
+#define RTC_FROM4_RS_ECC_CHK_ERROR	(1 << 7)
+
+/* Undefine for software ECC */
+#define RTC_FROM4_HWECC	1
+
+/*
+ * Module stuff
+ */
+static void __iomem *rtc_from4_fio_base = P2SEGADDR(RTC_FROM4_FIO_BASE);
+
+MODULE_PARM(rtc_from4_fio_base, "i");
+
+const static struct mtd_partition partition_info[] = {
+        {
+                .name   = "Renesas flash partition 1",
+                .offset = 0,
+                .size   = MTDPART_SIZ_FULL
+        },
+};
+#define NUM_PARTITIONS 1
+
+/* 
+ *	hardware specific flash bbt decriptors
+ *	Note: this is to allow debugging by disabling 
+ *		NAND_BBT_CREATE and/or NAND_BBT_WRITE
+ *
+ */
+static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
+static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
+
+static struct nand_bbt_descr rtc_from4_bbt_main_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs = 40,
+	.len = 4,
+	.veroffs = 44,
+	.maxblocks = 4,
+	.pattern = bbt_pattern
+};
+
+static struct nand_bbt_descr rtc_from4_bbt_mirror_descr = {
+	.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
+		| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
+	.offs = 40,
+	.len = 4,
+	.veroffs = 44,
+	.maxblocks = 4,
+	.pattern = mirror_pattern
+};
+
+
+
+#ifdef RTC_FROM4_HWECC
+
+/* the Reed Solomon control structure */
+static struct rs_control *rs_decoder;
+
+/* 
+ *      hardware specific Out Of Band information
+ */
+static struct nand_oobinfo rtc_from4_nand_oobinfo = {
+	.useecc = MTD_NANDECC_AUTOPLACE,
+	.eccbytes = 32,
+	.eccpos = {
+		 0,  1,  2,  3,  4,  5,  6,  7,
+		 8,  9, 10, 11, 12, 13, 14, 15,
+		16, 17, 18, 19, 20, 21, 22, 23,
+		24, 25, 26, 27, 28, 29, 30, 31},
+	.oobfree = { {32, 32} }
+};
+
+/* Aargh. I missed the reversed bit order, when I
+ * was talking to Renesas about the FPGA.
+ *
+ * The table is used for bit reordering and inversion
+ * of the ecc byte which we get from the FPGA
+ */
+static uint8_t revbits[256] = {
+	0xff, 0x7f, 0xbf, 0x3f, 0xdf, 0x5f, 0x9f, 0x1f,
+	0xef, 0x6f, 0xaf, 0x2f, 0xcf, 0x4f, 0x8f, 0x0f,
+	0xf7, 0x77, 0xb7, 0x37, 0xd7, 0x57, 0x97, 0x17,
+	0xe7, 0x67, 0xa7, 0x27, 0xc7, 0x47, 0x87, 0x07,
+	0xfb, 0x7b, 0xbb, 0x3b, 0xdb, 0x5b, 0x9b, 0x1b,
+	0xeb, 0x6b, 0xab, 0x2b, 0xcb, 0x4b, 0x8b, 0x0b,
+	0xf3, 0x73, 0xb3, 0x33, 0xd3, 0x53, 0x93, 0x13,
+	0xe3, 0x63, 0xa3, 0x23, 0xc3, 0x43, 0x83, 0x03,
+	0xfd, 0x7d, 0xbd, 0x3d, 0xdd, 0x5d, 0x9d, 0x1d,
+	0xed, 0x6d, 0xad, 0x2d, 0xcd, 0x4d, 0x8d, 0x0d,
+	0xf5, 0x75, 0xb5, 0x35, 0xd5, 0x55, 0x95, 0x15,
+	0xe5, 0x65, 0xa5, 0x25, 0xc5, 0x45, 0x85, 0x05,
+	0xf9, 0x79, 0xb9, 0x39, 0xd9, 0x59, 0x99, 0x19,
+	0xe9, 0x69, 0xa9, 0x29, 0xc9, 0x49, 0x89, 0x09,
+	0xf1, 0x71, 0xb1, 0x31, 0xd1, 0x51, 0x91, 0x11,
+	0xe1, 0x61, 0xa1, 0x21, 0xc1, 0x41, 0x81, 0x01,
+	0xfe, 0x7e, 0xbe, 0x3e, 0xde, 0x5e, 0x9e, 0x1e,
+	0xee, 0x6e, 0xae, 0x2e, 0xce, 0x4e, 0x8e, 0x0e,
+	0xf6, 0x76, 0xb6, 0x36, 0xd6, 0x56, 0x96, 0x16,
+	0xe6, 0x66, 0xa6, 0x26, 0xc6, 0x46, 0x86, 0x06,
+	0xfa, 0x7a, 0xba, 0x3a, 0xda, 0x5a, 0x9a, 0x1a,
+	0xea, 0x6a, 0xaa, 0x2a, 0xca, 0x4a, 0x8a, 0x0a,
+	0xf2, 0x72, 0xb2, 0x32, 0xd2, 0x52, 0x92, 0x12,
+	0xe2, 0x62, 0xa2, 0x22, 0xc2, 0x42, 0x82, 0x02,
+	0xfc, 0x7c, 0xbc, 0x3c, 0xdc, 0x5c, 0x9c, 0x1c,
+	0xec, 0x6c, 0xac, 0x2c, 0xcc, 0x4c, 0x8c, 0x0c,
+	0xf4, 0x74, 0xb4, 0x34, 0xd4, 0x54, 0x94, 0x14,
+	0xe4, 0x64, 0xa4, 0x24, 0xc4, 0x44, 0x84, 0x04,
+	0xf8, 0x78, 0xb8, 0x38, 0xd8, 0x58, 0x98, 0x18,
+	0xe8, 0x68, 0xa8, 0x28, 0xc8, 0x48, 0x88, 0x08,
+	0xf0, 0x70, 0xb0, 0x30, 0xd0, 0x50, 0x90, 0x10,
+	0xe0, 0x60, 0xa0, 0x20, 0xc0, 0x40, 0x80, 0x00,
+};
+
+#endif
+
+
+
+/* 
+ * rtc_from4_hwcontrol - hardware specific access to control-lines
+ * @mtd:	MTD device structure
+ * @cmd:	hardware control command
+ *
+ * Address lines (A5 and A4) are used to control Command and Address Latch 
+ * Enable on this board, so set the read/write address appropriately.
+ *
+ * Chip Enable is also controlled by the Chip Select (CS5) and 
+ * Address lines (A24-A22), so no action is required here.
+ *
+ */
+static void rtc_from4_hwcontrol(struct mtd_info *mtd, int cmd)
+{
+	struct nand_chip* this = (struct nand_chip *) (mtd->priv);
+	
+	switch(cmd) {
+		
+	case NAND_CTL_SETCLE: 
+		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_CLE);
+		break;
+	case NAND_CTL_CLRCLE: 
+		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_CLE);
+		break;
+		
+	case NAND_CTL_SETALE:
+		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_ALE);
+		break;
+	case NAND_CTL_CLRALE:
+		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_ALE);
+		break;
+		
+	case NAND_CTL_SETNCE:
+		break;
+	case NAND_CTL_CLRNCE:
+		break;
+
+	}
+}
+
+
+/*
+ * rtc_from4_nand_select_chip - hardware specific chip select
+ * @mtd:	MTD device structure
+ * @chip:	Chip to select (0 == slot 3, 1 == slot 4)
+ *
+ * The chip select is based on address lines A24-A22.
+ * This driver uses flash slots 3 and 4 (A23-A22).
+ *
+ */
+static void rtc_from4_nand_select_chip(struct mtd_info *mtd, int chip)
+{
+        struct nand_chip *this = mtd->priv;
+
+	this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R & ~RTC_FROM4_NAND_ADDR_MASK);
+	this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~RTC_FROM4_NAND_ADDR_MASK);
+
+        switch(chip) {
+
+        case 0:		/* select slot 3 chip */
+		this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT3);
+		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT3);
+                break;
+        case 1:		/* select slot 4 chip */
+		this->IO_ADDR_R = (void __iomem *)((unsigned long)this->IO_ADDR_R | RTC_FROM4_NAND_ADDR_SLOT4);
+		this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | RTC_FROM4_NAND_ADDR_SLOT4);
+                break;
+
+        }
+}
+
+
+
+/*
+ * rtc_from4_nand_device_ready - hardware specific ready/busy check
+ * @mtd:	MTD device structure
+ *
+ * This board provides the Ready/Busy state in the status register
+ * of the FPGA.  Bit zero indicates the RDY(1)/BSY(0) signal.
+ *
+ */
+static int rtc_from4_nand_device_ready(struct mtd_info *mtd)
+{
+	unsigned short status;
+
+	status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_FPGA_SR));
+
+	return (status & RTC_FROM4_DEVICE_READY);
+
+}
+
+#ifdef RTC_FROM4_HWECC
+/*
+ * rtc_from4_enable_hwecc - hardware specific hardware ECC enable function
+ * @mtd:	MTD device structure
+ * @mode:	I/O mode; read or write
+ *
+ * enable hardware ECC for data read or write 
+ *
+ */
+static void rtc_from4_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+	volatile unsigned short * rs_ecc_ctl = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CTL);
+	unsigned short status;
+
+	switch (mode) {
+	    case NAND_ECC_READ :
+		status =  RTC_FROM4_RS_ECC_CTL_CLR 
+			| RTC_FROM4_RS_ECC_CTL_FD_E;
+
+		*rs_ecc_ctl = status;
+		break;
+
+	    case NAND_ECC_READSYN :
+		status =  0x00;
+
+		*rs_ecc_ctl = status;
+		break;
+
+	    case NAND_ECC_WRITE :
+		status =  RTC_FROM4_RS_ECC_CTL_CLR 
+			| RTC_FROM4_RS_ECC_CTL_GEN 
+			| RTC_FROM4_RS_ECC_CTL_FD_E;
+
+		*rs_ecc_ctl = status;
+		break;
+
+	    default:
+		BUG();
+		break;
+	}
+
+}
+
+/*
+ * rtc_from4_calculate_ecc - hardware specific code to read ECC code
+ * @mtd:	MTD device structure
+ * @dat:	buffer containing the data to generate ECC codes
+ * @ecc_code	ECC codes calculated
+ *
+ * The ECC code is calculated by the FPGA.  All we have to do is read the values
+ * from the FPGA registers.
+ *
+ * Note: We read from the inverted registers, since data is inverted before
+ * the code is calculated. So all 0xff data (blank page) results in all 0xff rs code
+ *
+ */
+static void rtc_from4_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
+{
+	volatile unsigned short * rs_eccn = (volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECCN);
+	unsigned short value;
+	int i;
+
+	for (i = 0; i < 8; i++) {
+		value = *rs_eccn;
+		ecc_code[i] = (unsigned char)value;
+		rs_eccn++;
+	}
+	ecc_code[7] |= 0x0f;	/* set the last four bits (not used) */
+}
+
+
+/*
+ * rtc_from4_correct_data - hardware specific code to correct data using ECC code
+ * @mtd:	MTD device structure
+ * @buf:	buffer containing the data to generate ECC codes
+ * @ecc1	ECC codes read
+ * @ecc2	ECC codes calculated
+ *
+ * The FPGA tells us fast, if there's an error or not. If no, we go back happy
+ * else we read the ecc results from the fpga and call the rs library to decode
+ * and hopefully correct the error
+ *
+ * For now I use the code, which we read from the FLASH to use the RS lib,
+ * as the syndrom conversion has a unresolved issue.
+ */
+static int rtc_from4_correct_data(struct mtd_info *mtd, const u_char *buf, u_char *ecc1, u_char *ecc2)
+{
+	int i, res;
+	unsigned short status; 
+	uint16_t rpar[6];
+	uint8_t ecc[8];
+
+	status = *((volatile unsigned short *)(rtc_from4_fio_base + RTC_FROM4_RS_ECC_CHK));
+
+	if (!(status & RTC_FROM4_RS_ECC_CHK_ERROR)) {
+		return 0;
+	}
+
+	/* Reverse bits and invert the codes */
+	for (i = 0; i < 8; i++) {
+		ecc[i] = revbits[ecc1[i]];
+	}
+
+	/* convert into 6 10bit parity fields */
+	rpar[0] =  ((uint16_t)ecc[0])               | (((uint16_t)ecc[1] << 8) & 0x300);
+	rpar[1] = (((uint16_t)ecc[1] >> 2) & 0x03f) | (((uint16_t)ecc[2] << 6) & 0x3c0);
+	rpar[2] = (((uint16_t)ecc[2] >> 4) & 0x00f) | (((uint16_t)ecc[3] << 4) & 0x3f0);
+	rpar[3] = (((uint16_t)ecc[3] >> 6) & 0x003) | (((uint16_t)ecc[4] << 2) & 0x3fc);
+	rpar[4] =  ((uint16_t)ecc[5])               | (((uint16_t)ecc[6] << 8) & 0x300);
+	rpar[5] = (((uint16_t)ecc[6] >> 2) & 0x03f) | (((uint16_t)ecc[7] << 6) & 0x3c0);
+
+	/* Let the library code do its magic. 
+	 * Set the data inversion mask to 0xff, as the FPGA inverts data on read
+	 * except for the virtual bits 9 /10 which are tied low
+	 */
+	res = decode_rs8 (rs_decoder, buf, rpar, 512, NULL, 0, NULL, 0xff);
+	if (res > 0) {
+		DEBUG (MTD_DEBUG_LEVEL0, "rtc_from4_correct_data: " 
+			"ECC corrected %d errors on read\n", res);
+	}
+
+	return res;
+}
+#endif
+
+/*
+ * Main initialization routine
+ */
+int __init rtc_from4_init (void)
+{
+	struct nand_chip *this;
+	unsigned short bcr1, bcr2, wcr2;
+
+	/* Allocate memory for MTD device structure and private data */
+	rtc_from4_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
+				GFP_KERNEL);
+	if (!rtc_from4_mtd) {
+		printk ("Unable to allocate Renesas NAND MTD device structure.\n");
+		return -ENOMEM;
+	}
+
+	/* Get pointer to private data */
+	this = (struct nand_chip *) (&rtc_from4_mtd[1]);
+
+	/* Initialize structures */
+	memset((char *) rtc_from4_mtd, 0, sizeof(struct mtd_info));
+	memset((char *) this, 0, sizeof(struct nand_chip));
+
+	/* Link the private data with the MTD structure */
+	rtc_from4_mtd->priv = this;
+
+	/* set area 5 as PCMCIA mode to clear the spec of tDH(Data hold time;9ns min) */
+	bcr1 = *SH77X9_BCR1 & ~0x0002;
+	bcr1 |= 0x0002;
+	*SH77X9_BCR1 = bcr1;
+
+	/* set */
+	bcr2 = *SH77X9_BCR2 & ~0x0c00;
+	bcr2 |= 0x0800;
+	*SH77X9_BCR2 = bcr2;
+
+	/* set area 5 wait states */
+	wcr2 = *SH77X9_WCR2 & ~0x1c00;
+	wcr2 |= 0x1c00;
+	*SH77X9_WCR2 = wcr2;
+
+	/* Set address of NAND IO lines */
+	this->IO_ADDR_R = rtc_from4_fio_base;
+	this->IO_ADDR_W = rtc_from4_fio_base;
+	/* Set address of hardware control function */
+	this->hwcontrol = rtc_from4_hwcontrol;
+	/* Set address of chip select function */
+        this->select_chip = rtc_from4_nand_select_chip;
+	/* command delay time (in us) */
+	this->chip_delay = 100;
+	/* return the status of the Ready/Busy line */
+	this->dev_ready = rtc_from4_nand_device_ready;
+
+#ifdef RTC_FROM4_HWECC
+	printk(KERN_INFO "rtc_from4_init: using hardware ECC detection.\n");
+
+        this->eccmode = NAND_ECC_HW8_512;
+	this->options |= NAND_HWECC_SYNDROME;
+	/* set the nand_oobinfo to support FPGA H/W error detection */
+	this->autooob = &rtc_from4_nand_oobinfo;
+	this->enable_hwecc = rtc_from4_enable_hwecc;
+	this->calculate_ecc = rtc_from4_calculate_ecc;
+	this->correct_data = rtc_from4_correct_data;
+#else
+	printk(KERN_INFO "rtc_from4_init: using software ECC detection.\n");
+
+	this->eccmode = NAND_ECC_SOFT;
+#endif
+
+	/* set the bad block tables to support debugging */
+	this->bbt_td = &rtc_from4_bbt_main_descr;
+	this->bbt_md = &rtc_from4_bbt_mirror_descr;
+
+	/* Scan to find existence of the device */
+	if (nand_scan (rtc_from4_mtd, RTC_FROM4_MAX_CHIPS)) {
+		kfree (rtc_from4_mtd);
+		return -ENXIO;
+	}
+
+	/* Register the partitions */
+	add_mtd_partitions(rtc_from4_mtd, partition_info, NUM_PARTITIONS);
+
+#ifdef RTC_FROM4_HWECC
+	/* We could create the decoder on demand, if memory is a concern.
+	 * This way we have it handy, if an error happens 
+	 *
+	 * Symbolsize is 10 (bits)
+	 * Primitve polynomial is x^10+x^3+1
+	 * first consecutive root is 0
+	 * primitve element to generate roots = 1
+	 * generator polinomial degree = 6
+	 */
+	rs_decoder = init_rs (10, 0x409, 0, 1, 6);
+	if (!rs_decoder) {
+		printk (KERN_ERR "Could not create a RS decoder\n");
+		nand_release(rtc_from4_mtd);
+		kfree (rtc_from4_mtd);
+		return -ENOMEM;
+	}
+#endif
+	/* Return happy */
+	return 0;
+}
+module_init(rtc_from4_init);
+
+
+/*
+ * Clean up routine
+ */
+#ifdef MODULE
+static void __exit rtc_from4_cleanup (void)
+{
+	/* Release resource, unregister partitions */
+	nand_release(rtc_from4_mtd);
+
+	/* Free the MTD device structure */
+	kfree (rtc_from4_mtd);
+
+#ifdef RTC_FROM4_HWECC
+	/* Free the reed solomon resources */
+	if (rs_decoder) {
+		free_rs(rs_decoder);
+	}
+#endif
+}
+module_exit(rtc_from4_cleanup);
+#endif
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("d.marlin <dmarlin@redhat.com");
+MODULE_DESCRIPTION("Board-specific glue layer for AG-AND flash on Renesas FROM_BOARD4");
+
diff -uNrb linux-2.4.30/drivers/mtd/nand/spia.c linux/drivers/mtd/nand/spia.c
--- linux-2.4.30/drivers/mtd/nand/spia.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/nand/spia.c	Tue Sep 28 14:38:13 2004
@@ -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.22 2004/09/16 23:27:14 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
@@ -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){
 
@@ -107,7 +108,7 @@
 	struct nand_chip *this;
 
 	/* Allocate memory for MTD device structure and private data */
-	spia_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
+	spia_mtd = (struct mtd_info *) kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
 				GFP_KERNEL);
 	if (!spia_mtd) {
 		printk ("Unable to allocate SPIA NAND MTD device structure.\n");
@@ -131,37 +132,19 @@
 	(*(volatile unsigned char *) (spia_io_base + spia_peddr)) = 0x07;
 
 	/* Set address of NAND IO lines */
-	this->IO_ADDR_R = spia_fio_base;
-	this->IO_ADDR_W = spia_fio_base;
+	this->IO_ADDR_R = (void __iomem *) spia_fio_base;
+	this->IO_ADDR_W = (void __iomem *) spia_fio_base;
 	/* Set address of hardware control function */
 	this->hwcontrol = spia_hwcontrol;
 	/* 15 us command delay time */
 	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;
 	}
 
-	/* Allocate memory for internal data buffer */
-	this->data_buf = kmalloc (sizeof(u_char) * (spia_mtd->oobblock + spia_mtd->oobsize), GFP_KERNEL);
-	if (!this->data_buf) {
-		printk ("Unable to allocate NAND data buffer for SPIA.\n");
-		kfree (spia_mtd);
-		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);
 
@@ -176,14 +159,8 @@
 #ifdef MODULE
 static void __exit spia_cleanup (void)
 {
-	struct nand_chip *this = (struct nand_chip *) &spia_mtd[1];
-
-	/* Unregister the device */
-	del_mtd_device (spia_mtd);
-
-	/* Free internal data buffer */
-	kfree (this->data_buf);
-	kfree (this->page_cache);
+	/* Release resources, unregister device */
+	nand_release (spia_mtd);
 
 	/* Free the MTD device structure */
 	kfree (spia_mtd);
@@ -192,5 +169,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 -uNrb linux-2.4.30/drivers/mtd/nand/toto.c linux/drivers/mtd/nand/toto.c
--- linux-2.4.30/drivers/mtd/nand/toto.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/nand/toto.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,205 @@
+/*
+ *  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.3 2004/09/16 23:27:15 gleixner 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 unsigned long 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 = (struct mtd_info *) 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;
+	}
+
+	/* 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)
+{
+	/* Release resources, unregister device */
+	nand_release (toto_mtd);
+
+	/* 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 -uNrb linux-2.4.30/drivers/mtd/nand/tx4925ndfmc.c linux/drivers/mtd/nand/tx4925ndfmc.c
--- linux-2.4.30/drivers/mtd/nand/tx4925ndfmc.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/nand/tx4925ndfmc.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,416 @@
+/*
+ *  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.4 2004/09/16 23:27:15 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;
+
+/*
+ * 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 = (struct mtd_info *) 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 = (void __iomem *)&(tx4925_ndfmcptr->dtr);
+	this->IO_ADDR_W = (void __iomem *)&(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;
+	}
+
+	/* 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_ior;
+		}
+	}
+#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */
+	goto out;
+
+out_ior:
+out:
+	return err;
+}
+
+module_init(tx4925ndfmc_init);
+
+/*
+ * Clean up routine
+ */
+#ifdef MODULE
+static void __exit tx4925ndfmc_cleanup (void)
+{
+	/* Release resources, unregister device */
+	nand_release (tx4925ndfmc_mtd);
+
+	/* 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 -uNrb linux-2.4.30/drivers/mtd/nand/tx4938ndfmc.c linux/drivers/mtd/nand/tx4938ndfmc.c
--- linux-2.4.30/drivers/mtd/nand/tx4938ndfmc.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/nand/tx4938ndfmc.c	Tue Sep 28 14:38:13 2004
@@ -0,0 +1,406 @@
+/*
+ * 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.3 2004/09/16 23:27:15 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 = (struct mtd_info *) 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;
+	}
+
+	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)
+{
+	/* Release resources, unregister device */
+	nand_release (tx4938ndfmc_mtd);
+
+	/* Free the MTD device structure */
+	kfree (tx4938ndfmc_mtd);
+}
+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 -uNrb linux-2.4.30/drivers/mtd/nftlcore.c linux/drivers/mtd/nftlcore.c
--- linux-2.4.30/drivers/mtd/nftlcore.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/nftlcore.c	Fri Sep 10 15:15:04 2004
@@ -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.96 2004/06/28 13:52:55 dbrown 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,187 +37,107 @@
 
 #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");
+	if (mtd->type != MTD_NANDFLASH)
 			return;
-		}
-	}
-        if (firstfree == -1) {
-		printk(KERN_WARNING "No more NFTL slot available\n");
+	/* OK, this is moderately ugly.  But probably safe.  Alternatives? */
+	if (memcmp(mtd->name, "DiskOnChip", 10))
+		return;
+
+	if (!mtd->block_isbad) {
+		printk(KERN_ERR
+"NFTL no longer supports the old DiskOnChip drivers loaded via docprobe.\n"
+"Please use the new diskonchip driver under the NAND subsystem.\n");
 		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;
+	memcpy(&nftl->oobinfo, &mtd->oobinfo, sizeof(struct nand_oobinfo));
+	nftl->oobinfo.useecc = MTD_NANDECC_PLACEONLY;
 
         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", 
+	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; */
+			(long)nftl->cylinders * (long)nftl->heads *
+			(long)nftl->sectors );
 	}
-	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);
-#endif
-}
-
-static void NFTL_unsetup(int i)
-{
-	struct NFTLrecord *nftl = NFTLs[i];
-
-	DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i);
-	
-	NFTLs[i] = NULL;
 	
+	if (add_mtd_blktrans_dev(&nftl->mbd)) {
 	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);
-	}
+#ifdef PSYCHO_DEBUG
+	printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a');
+#endif
 }
 
-static void NFTL_notify_remove(struct mtd_info *mtd)
+static void nftl_remove_dev(struct mtd_blktrans_dev *dev)
 {
-	int i;
+	struct NFTLrecord *nftl = (void *)dev;
 
-	for (i = 0; i < MAX_NFTLS; i++) {
-		if (NFTLs[i] && NFTLs[i]->mtd == mtd)
-			NFTL_unsetup(i);
-	}
+	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);
+	kfree(nftl);
 }
 
 #ifdef CONFIG_NFTL_RW
@@ -303,7 +221,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 +338,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,17 +362,19 @@
                 if (BlockMap[block] == BLOCK_NIL)
                         continue;
                 
-                ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
-				  512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); 
+                ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512),
+				  512, &retlen, movebuf); 
                 if (ret < 0) {
-                    ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block])
+                    ret = MTD_READ(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block])
                                       + (block * 512), 512, &retlen,
-                                      movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); 
+                                      movebuf); 
                     if (ret != -EIO) 
                         printk("Error went away on retry.\n");
                 }
-                MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512),
-                             512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP);
+		memset(&oob, 0xff, sizeof(struct nftl_oob));
+		oob.b.Status = oob.b.Status1 = SECTOR_USED;
+                MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512),
+                             512, &retlen, movebuf, (char *)&oob, &nftl->oobinfo);
 	}
         
         /* add the header so that it is now a valid chain */
@@ -462,7 +382,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. */
@@ -484,7 +404,6 @@
 
                 if (NFTL_formatblock(nftl, thisEUN) < 0) {
 			/* could not erase : mark block as reserved
-			 * FixMe: Update Bad Unit Table on disk
 			 */
 			nftl->ReplUnitTable[thisEUN] = BLOCK_RESERVED;
                 } else {
@@ -582,7 +501,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 +589,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 +604,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,12 +623,14 @@
 	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;
-	u8 eccbuf[6];
+	struct nftl_oob oob;
 
 	writeEUN = NFTL_findwriteunit(nftl, block);
 
@@ -720,16 +641,20 @@
 		return 1;
 	}
 
-	MTD_WRITEECC(nftl->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 */
+	memset(&oob, 0xff, sizeof(struct nftl_oob));
+	oob.b.Status = oob.b.Status1 = SECTOR_USED;
+	MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs,
+		     512, &retlen, (char *)buffer, (char *)&oob, &nftl->oobinfo);
+        /* need to write SECTOR_USED flags since they are not written in mtd_writeecc */
 
 	return 0;
 }
 #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 +667,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 +686,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;
 			}
@@ -782,265 +707,22 @@
 	} else {
 		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_READ(nftl->mbd.mtd, ptr, 512, &retlen, buffer))
 			return -EIO;
 	}
 	return 0;
 }
 
-static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
+static int nftl_getgeo(struct mtd_blktrans_dev *dev,  struct hd_geometry *geo)
 {
-	struct NFTLrecord *nftl;
-	int p;
-
-	nftl = NFTLs[MINOR(inode->i_rdev) >> NFTL_PARTN_BITS];
+	struct NFTLrecord *nftl = (void *)dev;
 
-	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)
-{
-	struct NFTLrecord *thisNFTL;
-
-	thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16];
-
-	DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n");
-
-	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 +730,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.96 $, 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 -uNrb linux-2.4.30/drivers/mtd/nftlmount.c linux/drivers/mtd/nftlmount.c
--- linux-2.4.30/drivers/mtd/nftlmount.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/nftlmount.c	Tue Sep 28 14:38:13 2004
@@ -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.37 2004/09/16 23:32:37 gleixner 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.37 $";
 
 /* 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
@@ -50,7 +41,6 @@
 static int find_boot_record(struct NFTLrecord *nftl)
 {
 	struct nftl_uci1 h1;
-	struct nftl_oob oob;
 	unsigned int block, boot_record_count = 0;
 	size_t retlen;
 	u8 buf[SECTORSIZE];
@@ -59,8 +49,12 @@
 
         /* 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;
+	/* Actually, we won't.  The new DiskOnChip driver has already scanned
+	   the MediaHeader and adjusted the virtual erasesize it presents in
+	   the mtd device accordingly.  We could even get rid of
+	   nftl->EraseSize if there were any point in doing so. */
+	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 +65,15 @@
 
 		/* 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))) {
+		ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf);
+		/* We ignore ret in case the ECC of the MediaHeader is invalid
+		   (which is apparently acceptable) */
+		if (retlen != SECTORSIZE) {
 			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 +84,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 +103,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, NULL) < 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,8 +134,12 @@
 				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 */
+				if (boot_record_count < 2) {
+					/* We haven't yet seen two real ones */
 				return -1;
 			}
+				continue;
+			}
 			if (boot_record_count == 1)
 				nftl->SpareMediaUnit = block;
 
@@ -154,6 +155,10 @@
 		memcpy(mh, buf, sizeof(struct NFTLMediaHeader));
 
 		/* Do some sanity checks on it */
+#if 0
+The new DiskOnChip driver scans the MediaHeader itself, and presents a virtual
+erasesize based on UnitSizeFactor.  So the erasesize we read from the mtd
+device is already correct.
 		if (mh->UnitSizeFactor == 0) {
 			printk(KERN_NOTICE "NFTL: UnitSizeFactor 0x00 detected. This violates the spec but we think we know what it means...\n");
 		} else if (mh->UnitSizeFactor < 0xfc) {
@@ -163,9 +168,10 @@
 		} 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;
 		}
+#endif
 		nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN);
 		if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) {
 			printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n");
@@ -182,7 +188,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 */
@@ -218,11 +224,13 @@
 
 		/* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */
 		for (i = 0; i < nftl->nb_blocks; i++) {
+#if 0
+The new DiskOnChip driver already scanned the bad block table.  Just query it.
 			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) {
+						       (char *)&oob, NULL)) < 0) {
 					printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n",
 					       ret);
 					kfree(nftl->ReplUnitTable);
@@ -233,6 +241,9 @@
 			/* mark the Bad Erase Unit as RESERVED in ReplUnitTable */
 			if (buf[i & (SECTORSIZE - 1)] != 0xff)
 				nftl->ReplUnitTable[i] = BLOCK_RESERVED;
+#endif
+			if (nftl->mbd.mtd->block_isbad(nftl->mbd.mtd, i * nftl->EraseSize))
+				nftl->ReplUnitTable[i] = BLOCK_RESERVED;
 		}
 		
 		nftl->MediaUnit = block;
@@ -258,21 +269,16 @@
 			      int check_oob)
 {
 	int i, retlen;
-	u8 buf[SECTORSIZE];
+	u8 buf[SECTORSIZE + nftl->mbd.mtd->oobsize];
 
 	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_READECC(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf, &buf[SECTORSIZE], &nftl->oobinfo) < 0)
 			return -1;
 		if (memcmpb(buf, 0xff, SECTORSIZE) != 0)
 			return -1;
 
 		if (check_oob) {
-			if (MTD_READOOB(nftl->mtd, address, nftl->mtd->oobsize,
-					&retlen, buf) < 0)
-				return -1;
-			if (memcmpb(buf, 0xff, nftl->mtd->oobsize) != 0)
+			if (memcmpb(buf + SECTORSIZE, 0xff, nftl->mbd.mtd->oobsize) != 0)
 				return -1;
 		}
 		address += SECTORSIZE;
@@ -287,7 +293,6 @@
  * Return: 0 when succeed, -1 on error.
  *
  *  ToDo: 1. Is it neceressary to check_free_sector after erasing ?? 
- *        2. UnitSizeFactor != 0xFF
  */
 int NFTL_formatblock(struct NFTLrecord *nftl, int block)
 {
@@ -297,7 +302,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,14 +319,13 @@
 	/* 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 
-		   both in memory and on disk */
 		printk("Error while formatting block %d\n", block);
-		return -1;
-	} else {
+		goto fail;
+	}
+
 		/* increase and write Wear-Leveling info */
 		nb_erases = le32_to_cpu(uci.WearInfo);
 		nb_erases++;
@@ -334,14 +338,18 @@
 		 * FixMe:  is this check really necessary ? since we have check the
 		 *         return code after the erase operation. */
 		if (check_free_sectors(nftl, instr->addr, nftl->EraseSize, 1) != 0)
-			return -1;
+			goto fail;
 
 		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;
+			goto fail;
 		return 0;
-	}
+fail:
+	/* could not format, update the bad block table (caller is responsible
+	   for setting the ReplUnitTable to BLOCK_RESERVED on failure) */
+	nftl->mbd.mtd->block_markbad(nftl->mbd.mtd, instr->addr);
+	return -1;
 }
 
 /* check_sectors_in_chain: Check that each sector of a Virtual Unit Chain is correct.
@@ -363,7 +371,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 +391,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);
 				}
@@ -446,8 +454,7 @@
 
 		printk("Formatting block %d\n", block);
 		if (NFTL_formatblock(nftl, block) < 0) {
-			/* cannot format !!!! Mark it as Bad Unit,
-			   FixMe: update the BadUnitTable on disk */
+			/* cannot format !!!! Mark it as Bad Unit */
 			nftl->ReplUnitTable[block] = BLOCK_RESERVED;
 		} else {
 			nftl->ReplUnitTable[block] = BLOCK_FREE;
@@ -476,7 +483,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 +498,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 +510,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 +540,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 +579,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 -uNrb linux-2.4.30/drivers/mtd/redboot.c linux/drivers/mtd/redboot.c
--- linux-2.4.30/drivers/mtd/redboot.c	Fri Nov 19 12:32:15 2004
+++ linux/drivers/mtd/redboot.c	Fri Sep 10 15:15:04 2004
@@ -1,5 +1,5 @@
 /*
- * $Id: redboot.c,v 1.6 2001/10/25 09:16:06 dwmw2 Exp $
+ * $Id: redboot.c,v 1.15 2004/08/10 07:55:16 dwmw2 Exp $
  *
  * Parse RedBoot-style Flash Image System (FIS) tables and
  * produce a Linux partition array to match.
@@ -7,6 +7,8 @@
 
 #include <linux/kernel.h>
 #include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/vmalloc.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/partitions.h>
@@ -34,7 +36,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 +47,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 = vmalloc(master->erasesize);
 
 	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 +84,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 +99,11 @@
 			goto out;
 		}
 		new_fl->img = &buf[i];
+                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 +116,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);
@@ -151,11 +191,28 @@
 		fl = fl->next;
 		kfree(old);
 	}
-	kfree(buf);
+	vfree(buf);
 	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 -uNrb linux-2.4.30/drivers/mtd/ssfdc.c linux/drivers/mtd/ssfdc.c
--- linux-2.4.30/drivers/mtd/ssfdc.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/mtd/ssfdc.c	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,1132 @@
+/*
+ *  drivers/mtd/ssfdc.c
+ *
+ *  Copyright (C) 2003 Simon Haynes (simon@baydel.con)
+ *                     Baydel Ltd
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public License
+ * version 2.1 as published by the Free Software Foundation.
+ *
+ * This module provides a translation layer, via mtd, for smart
+ * media card access. It essentially enables the possibility 
+ * of using cards on a hardware which does not have a hardware translation
+ * layer and interchanging them with hardware that does ie: PC card readers
+ *
+ * I had to write this module for a specific task and in a short timeframe
+ * for this reason I have imposed some restricions to make the job easier.
+ *
+ * To build an compile the driver I added the following lines
+ * to mtd/Config.in
+ *
+ *  dep_tristate '  SSFDC support' CONFIG_SSFDC $CONFIG_MTD
+ *
+ * to /mtd/Makefile
+ *
+ * obj-$(CONFIG_SSFDC)             += ssfdc.o
+ *
+ * and compiled the kernel via the usual methods.
+ *
+ * I am sure that there are many problems I don't know about but here are
+ * some that I know of
+ *
+ * Currently the driver uses MAJOR number 44 which I think is FTL or NFTL
+ * I did this because I wanted a static number and I didn't know
+ * how to go about getting a new one. This needs addressing
+ * The dev nodes required are like standard. I only use minor 0
+ * (/dev/ssfdca), and minor 1 (/dev/ssfdca1).
+ * You should be able to run fdisk on /dev/ssfdca and the first partition
+ * is /dev/ssfdca1. There is no working code in the module for changing the
+ * SMC and rebuilding the maps so the card should not be changed once the
+ * module is loaded. At present I only look for 1 partition. But this is a
+ * small commented hack.
+ *
+ * There is no support cards which do not have a 512 byte page size with 16
+ * bytes of oob and an erase size of 16K.
+ * There are no checks for this at present. In addition the MTD reported size
+ * must be 16M or a multiple.
+ *
+ * Code to handle multiple partitions or multiple cards is incomplete
+ * Need to allocate data buffer and oob buffer on a per partition basis.
+ * As I am only concerned with one partition I will do this if I ever need to.
+ * The cached physical address variable also needs this attention.
+ *
+ * Recently I have started to work on media changes. Some of this is specific
+ * to my hardware and you will see references to pt_ssfdc_smc and smc_status.
+ * This code is incomplete and does not work. I have commented it for the moment
+ * but it should give an indication of what I think is required. Maybe there is
+ * something it mtd that can help
+ *
+ * 17th August 2004 MHB
+ *
+ * Following updating CVS I noticed some single bit data corruption. I believe
+ * that this was down to the fact that I was using mtd->read instead of mtd->read_ecc
+ * and that mtd->read was applying it's own error corretion from the wrong ecc bytes
+ * I have now corrected this.
+ *
+ * During this time I noticed that while in allocate new I only seem to look for blocks
+ * in 1 zone. So this limits the partition size to 16MB with all the other SMC size
+ * restrictions
+
+
+*/
+
+#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/blktrans.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <linux/timer.h>
+#include <linux/major.h>
+#include <linux/ioctl.h>
+#include <linux/hdreg.h>
+#include <linux/list.h>
+#include <asm/semaphore.h>
+#include <asm/uaccess.h>
+
+
+#if (LINUX_VERSION_CODE >= 0x20100)
+#include <linux/vmalloc.h>
+#endif
+#if (LINUX_VERSION_CODE >= 0x20303)
+#include <linux/blkpg.h>
+#endif
+
+#include <asm/semaphore.h>
+
+#define SSFDC_FORMAT 1
+
+#define PDEBUG(fmt, args...)
+
+#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT
+#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT
+
+#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
+
+#define TRUE 1
+#define FALSE 0
+
+#define SSFDC_MAJOR	44
+
+#define MAJOR_NR		SSFDC_MAJOR
+#define DEVICE_NAME		"ssfdc"
+#define DEVICE_REQUEST		do_ssfdc_request
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#include <linux/blk.h>
+
+#include "/home/simon/ebony/dbwhatu/dbwhatu/smccontrol.h"
+
+
+
+#define ZONE_SIZE 		(16 * 1024 * 1024)
+#define SMC_BLOCK_SIZE 		(16 * 1024)
+#define SECTOR_SIZE 		512
+#define SECTORS_PER_ZONE  	(ZONE_SIZE / SECTOR_SIZE)
+#define BLOCKS_PER_ZONE	    	(ZONE_SIZE / SMC_BLOCK_SIZE)
+#define SECTORS_PER_BLOCK	(SMC_BLOCK_SIZE / SECTOR_SIZE)
+#define OOB_SIZE		16
+
+
+#define MAX_DEVICES 	4
+#define MAX_PARTITIONS 	8
+#define PARTITION_BITS 	3
+#define MAX_ZONES 	8
+
+
+int ssfdc_major = SSFDC_MAJOR;
+unsigned int ssfdc_cached = 0xFFFFFFFF;
+static unsigned char ssfdc_scratch[16384];
+static unsigned char ssfdc_buffer[16];
+static unsigned char ssfdc_ffoob_buf[OOB_SIZE * SECTORS_PER_BLOCK];
+static unsigned char ssfdc_oob_buf[OOB_SIZE * SECTORS_PER_BLOCK];
+
+
+static struct nand_oobinfo ssfdc_ffoob_info = {
+	.useecc = 0,
+};
+
+
+typedef struct minor_t {
+	atomic_t open;
+	int cached;
+	unsigned char * pt_data;
+	unsigned char * pt_oob;
+} minor_t;
+
+
+
+typedef struct partition_t {
+	int type;
+    	struct mtd_info	*mtd;
+    	int count;
+	unsigned int *zone;
+	unsigned int zoneCount;
+	minor_t minor[MAX_PARTITIONS];
+	unsigned int last_written[MAX_ZONES];
+} partition_t;
+
+partition_t SMCParts[MAX_DEVICES];
+
+
+static unsigned char ssfdc_ecc[] = {14, 13, 15, 9, 8, 10};
+
+static struct hd_struct ssfdc_hd[MAX_DEVICES * MAX_PARTITIONS];
+static int ssfdc_sizes[MAX_DEVICES * MAX_PARTITIONS];
+static int ssfdc_blocksizes[MAX_DEVICES * MAX_PARTITIONS];
+smc_control * pt_ssfdc_smc;
+
+
+static struct gendisk ssfdc_gendisk = {
+    major:		SSFDC_MAJOR,
+    major_name:		"ssfdc",
+    minor_shift:	PARTITION_BITS,
+    max_p:		MAX_PARTITIONS,
+    part:		ssfdc_hd,
+    sizes:		ssfdc_sizes,
+};
+
+
+static int	ssfdc_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg);
+static int 	ssfdc_open(struct inode *inode, struct file *file);
+static int 	ssfdc_close(struct inode *inode, struct file *file);
+static int 	ssfdc_write(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks);
+static int 	ssfdc_read(partition_t *part, caddr_t buffer, u_long sector, u_long nblocks);
+static int 	ssfdc_physical(partition_t * pt_smcpart, int zone, int block);
+static int 	ssfdc_erase(partition_t *pt_smcpart, unsigned int offset);
+static int 	ssfdc_read_partitions(partition_t * pt_smcpart);
+static void 	ssfdc_notify_add(struct mtd_info *mtd);
+static void 	ssfdc_notify_remove(struct mtd_info *mtd);
+static void 	ssfdc_tables(partition_t * pt_smcpart);
+static int 	ssfdc_sector_blank(partition_t * pt_smcpart, int sc);
+static int  	ssfdc_allocate_new(partition_t * pt_smcpart, int zone);
+int 		ssfdc_parity(int number);
+static void 	ssfdc_erase_callback(struct erase_info *erase);
+
+
+
+static DECLARE_WAIT_QUEUE_HEAD(ssfdc_wq);
+
+
+static struct mtd_notifier ssfdc_notifier = {
+	add:		ssfdc_notify_add,
+	remove:		ssfdc_notify_remove,
+};
+
+
+
+static struct block_device_operations ssfdc_fops = {
+    open:	ssfdc_open,
+    release:	ssfdc_close,
+    ioctl:	ssfdc_ioctl,
+};
+                                               
+static struct semaphore ssfdc_semaphore;
+
+static void ssfdc_notify_add(struct mtd_info *mtd) {
+
+
+
+	
+	if(mtd->index >= 1) return;   // Hack to limit SSFDC to 1 partition
+
+	if( ((mtd->size % ZONE_SIZE) != 0) && (mtd->size < (ZONE_SIZE * MAX_ZONES)) ){
+		PDEBUG("ssfdc_notify_add : mtd partition %d is not modulus 16M, not SSFDC\n", mtd->index);	
+	}
+	else {
+		memset((void *)&SMCParts[mtd->index].type, 0, sizeof(partition_t));	
+		SMCParts[mtd->index].mtd = mtd;
+		SMCParts[mtd->index].count = mtd->index;
+		SMCParts[mtd->index].type = 1;
+		SMCParts[mtd->index].zoneCount = mtd->size / ZONE_SIZE;
+		SMCParts[mtd->index].zone = kmalloc(SMCParts[mtd->index].zoneCount * 8192, GFP_KERNEL);
+		
+
+		if(!SMCParts[mtd->index].zone) {
+			printk(KERN_NOTICE "ssfdc_notify_add : mtd partition %d, failed to allocate mapping table\n", mtd->index);
+			SMCParts[mtd->index].type = 0;
+		}
+		else {
+			memset((void *)SMCParts[mtd->index].zone, 0xFF, SMCParts[mtd->index].zoneCount * 8192);
+		}
+	
+		ssfdc_read_partitions((partition_t *)&SMCParts[mtd->index].type);
+	}
+	return;
+
+}
+static int ssfdc_read_partitions(partition_t * pt_smcpart) {
+
+	int whole, i, j, size;
+
+//=printk("ssfdc_read_partitions : start\n");
+
+	for(i=0; i<MAX_PARTITIONS; i++)
+	   	if ((atomic_read(&pt_smcpart->minor[i].open) > 1)) {
+//=printk("ssfdc_read_partitions : part %d busy\n", i);
+
+    		return -EBUSY;
+   		}
+
+
+//=printk("ssfdc_read_partitions : tables start\n");
+	ssfdc_tables(pt_smcpart);
+//=printk("ssfdc_read_partitions : tables end\n");
+
+   	whole = pt_smcpart->count << PARTITION_BITS;         		
+
+
+   	j = MAX_PARTITIONS - 1;
+   	while (j-- > 0) {
+		if (ssfdc_hd[whole+j].nr_sects > 0) {
+    			kdev_t rdev = MKDEV(SSFDC_MAJOR, whole+j);
+    			invalidate_device(rdev, 1);
+		}
+		ssfdc_hd[whole+j].start_sect = 0;
+		ssfdc_hd[whole+j].nr_sects = 0;
+   	}
+
+
+	size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32;
+	size /= (0x8 * 0x20);
+	size = size * (0x8 * 0x20);
+
+//=printk("ssfdc_read_partitions : register start\n");
+
+    register_disk(&ssfdc_gendisk, whole >> PARTITION_BITS, MAX_PARTITIONS,
+		  &ssfdc_fops, size);
+
+//=printk("ssfdc_read_partitions : register end\n");
+
+
+    	return 0;
+}
+
+
+static void ssfdc_notify_remove(struct mtd_info *mtd) {
+int i, j, whole;
+
+	i=mtd->index;
+	whole = i << PARTITION_BITS;
+   	if(SMCParts[i].mtd == mtd) {
+       		if(SMCParts[i].zone)kfree(SMCParts[i].zone);
+		memset((void *)&SMCParts[i].type, 0, sizeof(partition_t));
+    		for (j = 0; j < MAX_PARTITIONS; j++) {
+			if (ssfdc_hd[whole+j].nr_sects > 0) {
+		   		ssfdc_hd[whole+j].start_sect = 0;
+	   			ssfdc_hd[whole+j].nr_sects=0;
+			}
+    		}
+		return;
+	}
+	return;
+}
+
+
+
+static int ssfdc_ioctl(struct inode *inode, struct file *file,
+		u_int cmd, u_long arg) {
+
+    int minor = MINOR(inode->i_rdev);
+    int ret = -EINVAL;
+    partition_t * pt_smcpart = (partition_t *)&SMCParts[(minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS].type;
+    struct hd_geometry geo;
+    int size;
+/*
+	unsigned char smc_status;
+
+  	smc_status = in_8((void *)&pt_ssfdc_smc->smc_status);
+  	if(!(smc_status & SMC_PRESENT)) {
+		printk("ssfdc : media not present\n");
+		ret = 1;
+		goto ssfdc_ioctl_error;
+	}
+
+  	if(smc_status & SMC_CHANGED) {
+   		out_8((void *)&pt_ssfdc_smc->smc_status, smc_status);
+		if(minor & ((1<< PARTITION_BITS) - 1)) return -ENOTTY;
+			ssfdc_read_partitions(pt_smcpart);
+		printk("ssfdc : media change\n");
+  	}
+*/
+	switch(cmd) {
+
+	    case HDIO_GETGEO:
+		   	memset(&geo, 0, sizeof(geo));
+			size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32;
+			size /= (0x8 * 0x20);
+			geo.heads = 0x8;
+			geo.sectors = 0x20;
+			geo.cylinders = size;
+			geo.start = ssfdc_hd[minor].start_sect;
+//			printk(KERN_WARNING "ssfdc : HDIO_GETGEO heads %d, sectors %d, cylinders %d, start %lu\n",
+//				geo.heads, geo.sectors, geo.cylinders, geo.start);
+			copy_to_user((void *)arg, &geo, sizeof(geo));
+			ret = 0;
+		break;
+
+	    case BLKGETSIZE64:
+   	 	case BLKGETSIZE:
+			size = (((pt_smcpart->mtd->size / 16384) * 1000) / 1024) * 32;
+			//=printk(KERN_WARNING "ssfdc : BLKGETSIZE %d, minor %d\n", size, minor);
+			ret = copy_to_user((unsigned long *)arg, &size, sizeof(size));
+		break;
+		case BLKSSZGET:
+			size = 512;
+			ret = copy_to_user((unsigned long *)arg, &size, sizeof(size));
+		break;
+		break;
+
+    	case BLKRRPART:
+				if(minor & ((1<< PARTITION_BITS) - 1)) return -ENOTTY;
+				ssfdc_read_partitions(pt_smcpart);
+				ret=0;
+		break;
+   		case BLKFLSBUF:
+			printk(KERN_WARNING "ssfdc : block ioctl 0x%x\n", cmd);
+		break;
+
+   		default:
+			printk(KERN_WARNING "ssfdc: unknown ioctl 0x%x\n", cmd);
+    }
+
+//ssfdc_ioctl_error:
+    return(ret);
+
+}
+static int ssfdc_open(struct inode *inode, struct file *file)
+{
+    int minor = MINOR(inode->i_rdev);
+    partition_t *pt_smcpart;
+	int index;
+
+    if (minor >= MAX_MTD_DEVICES)
+	return -ENODEV;
+
+    index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS;
+
+
+    if(SMCParts[index].type != SSFDC_FORMAT)
+	return -ENXIO;
+
+	pt_smcpart = &SMCParts[index];
+
+
+	if(!pt_smcpart->zone)
+	return -ENXIO;
+ 
+
+    BLK_INC_USE_COUNT;
+
+    if (!get_mtd_device(pt_smcpart->mtd, -1)) {
+	    BLK_DEC_USE_COUNT;
+	    return -ENXIO;
+    }
+
+    if ((file->f_mode & 2) && !(pt_smcpart->mtd->flags & MTD_CLEAR_BITS) ) {
+	    put_mtd_device(pt_smcpart->mtd);
+	    BLK_DEC_USE_COUNT;
+            return -EROFS;
+    }
+
+
+    atomic_inc(&pt_smcpart->minor[minor & ~(MAX_PARTITIONS -1)].open);
+
+	PDEBUG("ssfdc_open : device %d\n", minor);
+
+	return(0);
+}
+
+static void ssfdc_tables(partition_t * pt_smcpart) {
+
+	int * logical, * physical;
+	int offset = 0;
+	int zone, block;
+	int i, retlen;
+	int block_address, parity;
+	int h, l;
+
+	for(zone=0; zone<pt_smcpart->zoneCount; zone++) {	
+		logical  =  pt_smcpart->zone + (2048 * zone);
+		memset((void *)logical, 0xFF, 1024 * sizeof(int));
+		physical =  pt_smcpart->zone + (2048 * zone) + 1024;
+		memset((void *)physical, 0xFF, 1024 * sizeof(int));
+
+		for(block=0; block < 1024; block++) {
+			offset = (zone * ZONE_SIZE) + (block * SMC_BLOCK_SIZE);
+	    		pt_smcpart->mtd->read_oob(pt_smcpart->mtd, offset, sizeof(ssfdc_buffer), &retlen, ssfdc_buffer);
+			if(retlen != sizeof(ssfdc_buffer)) {
+				printk(KERN_WARNING "ssfdc_tables : failed to read OOB\n");
+				pt_smcpart->type = 0;
+				return;
+			}
+
+			l = (ssfdc_buffer[7] & 0xFF);
+			h = (ssfdc_buffer[6] & 0xFF);
+            		block_address = l + (h << 8L);
+
+			if((block_address & ~0x7FF) != 0x1000) {
+					continue;
+			}
+
+			parity = block_address & 0x01;
+			
+			block_address &= 0x7FF;
+			block_address >>= 1;
+
+
+			if(ssfdc_parity(block_address) != parity) {
+ 				printk(KERN_WARNING "ssfdc_tables : parity error offset 0x%x, block 0x%x, parity 0x%x\nOOB : "
+						, offset, block_address, parity);
+			    	for(i=0; i<16; i++) {
+                 			printk("0x%02x ", (unsigned char)ssfdc_buffer[i]);
+			    	}
+				printk("\n");
+				pt_smcpart->type = 0;
+				return;          		
+			}
+
+
+            		/* Ok we have a valid block number so insert it */
+            		*(logical + block_address) = (offset/SMC_BLOCK_SIZE);
+			PDEBUG("ssfdc_tables : logical 0x%x + 0x%x = 0x%x\n", 
+					(unsigned int)logical, block_address, (offset/SMC_BLOCK_SIZE));
+			*(physical + block) = block_address;
+			PDEBUG("ssfdc_tables : physical 0x%x + 0x%x = 0x%x\n", (unsigned int)physical, block, block_address);
+			
+
+    		}
+    	}
+    	return;
+}
+int ssfdc_parity(int number) {
+ 	int i;
+	int parity = 1; // the 0x1000 bit
+
+	for(i=0; i<10; i++) {
+      		parity += ((number >> i) & 1);
+	}
+	PDEBUG("ssfdc_parity : number 0x%x, parity 0x%x\n", number, parity);
+	return(parity % 2);
+}
+static int ssfdc_physical(partition_t * pt_smcpart, int zone, int block) {
+
+	unsigned int * logical;
+
+	logical = pt_smcpart->zone + (zone * 2048);
+
+	logical += block;
+
+	if(*logical == 0xFFFFFFFF) {
+		PDEBUG("ssfdc_physical : physical for zone %d, block %d invalid\n", zone, block);
+		return(-1);
+	}
+
+	PDEBUG("ssfdc_physical : physical for zone %d, block %d, 0x%x\n", zone, block, (*logical * SMC_BLOCK_SIZE));
+	return(*logical * SMC_BLOCK_SIZE);
+}
+
+static int ssfdc_close(struct inode *inode, struct file *file)
+{                       
+    int minor = MINOR(inode->i_rdev);
+    partition_t *pt_smcpart;
+	int index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS;
+
+    if (minor >= MAX_MTD_DEVICES)
+	return -ENODEV;
+
+    if(SMCParts[index].type != SSFDC_FORMAT)
+	return -ENXIO;
+
+    pt_smcpart = &SMCParts[index];
+    atomic_dec(&pt_smcpart->minor[minor & ~(MAX_PARTITIONS -1)].open);
+    put_mtd_device(pt_smcpart->mtd);
+    BLK_DEC_USE_COUNT;
+
+    return(0);
+} 
+
+
+static void do_ssfdc_request(request_arg_t)
+{
+    int ret, minor;
+    partition_t *pt_smcpart;
+	int index;
+    do {
+
+	INIT_REQUEST;
+
+
+
+	minor = MINOR(CURRENT->rq_dev);
+	index = (minor & ~(MAX_PARTITIONS -1)) >> PARTITION_BITS;
+
+	pt_smcpart = &SMCParts[index];
+	if (pt_smcpart->type == SSFDC_FORMAT) {
+ 		ret = 0;
+		switch (CURRENT->cmd) {
+			case READ:
+	  			ret = ssfdc_read(pt_smcpart, CURRENT->buffer,
+					CURRENT->sector + ssfdc_hd[minor].start_sect,
+					CURRENT->current_nr_sectors);
+		    break;
+
+		  	case WRITE:
+		    	ret = ssfdc_write(pt_smcpart, CURRENT->buffer,
+				    CURRENT->sector	+ ssfdc_hd[minor].start_sect,
+				    CURRENT->current_nr_sectors);
+		    break;
+
+		  default:
+		    panic("do_ssfdc_request : unknown block command!\n");
+		  }
+	
+	} else {
+	  ret = 1;
+	  PDEBUG("not ssfdc partition type\n");
+	}
+
+	if (!ret) {
+	  CURRENT->sector += CURRENT->current_nr_sectors;
+	}
+
+	end_request((ret == 0) ? 1 : 0);
+    } while (1);
+}
+
+static int ssfdc_write(partition_t *pt_smcpart, caddr_t buffer,
+		     u_long sector, u_long nblocks)
+{
+	int zone, block, offset;
+	int sectors_written = 0;
+    	int physical;
+	int * pt_logical;
+	int * pt_physical;
+	int new = -1;
+	int size;
+	int retlen;
+	int i;
+	int sc;
+	int ptr_done = 0;
+	unsigned char * ptr = (unsigned char *)buffer;
+	unsigned char ecc_code[6], ecc_calc[6];
+	int do_erase;
+//	unsigned char smc_status;
+
+
+
+	offset = (sector % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ;
+
+    	PDEBUG("write device %d, sector %d, count %d\n",
+			pt_smcpart->count, sector, nblocks);
+/*
+  	smc_status = in_8((void *)&pt_ssfdc_smc->smc_status);
+	if(!(smc_status & SMC_PRESENT)) {
+		printk("ssfdc : media not present\n");
+		return -ENXIO;
+	}
+
+    if(smc_status & SMC_CHANGED) {
+   		out_8((void *)&pt_ssfdc_smc->smc_status, smc_status);
+		ssfdc_read_partitions(pt_smcpart);
+		printk("ssfdc : media change\n");
+	}
+*/
+	while(sectors_written < nblocks) {
+
+		new = -1;
+		do_erase = FALSE;
+    
+		zone = (sector + sectors_written) / SECTORS_PER_ZONE;
+		block = ((sector + sectors_written) % SECTORS_PER_ZONE) / SECTORS_PER_BLOCK ;
+		offset = ((sector + sectors_written) % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ;
+
+		pt_logical = pt_smcpart->zone + (zone * 2048);
+		pt_physical = pt_smcpart->zone + (zone * 2048) + 1024;
+
+		size = ((SECTORS_PER_BLOCK - offset) < (nblocks - sectors_written)) ?
+				(SECTORS_PER_BLOCK - offset) : (nblocks - sectors_written);
+		size *= SECTOR_SIZE;
+
+		PDEBUG("write device %d, sector %d, count %d, zone %d, block %d, offset %d, done %d, size %d, address 0x%x\n",
+				pt_smcpart->count, sector, nblocks, zone, block, offset, sectors_written, size, (unsigned int)ptr);
+
+		physical = ssfdc_physical(pt_smcpart, zone, block);
+
+
+		if(physical >= 0) {
+			if(ssfdc_cached != physical) {
+	   			pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, physical, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch,
+					 ssfdc_oob_buf, &ssfdc_ffoob_info);
+				if(retlen != SMC_BLOCK_SIZE) {
+					printk(KERN_WARNING "ssfdc_write : failed to read physical\n");
+					return -ENXIO;
+				}
+
+				for(sc=0; sc<SECTORS_PER_BLOCK; sc++) {
+	   				pt_smcpart->mtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer);
+					if(retlen != sizeof(ssfdc_buffer)) {
+						printk(KERN_WARNING "ssfdc_write : failed to read physical oob\n");
+						return -ENXIO;
+					}
+
+					nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]);
+					nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]);
+					for(i=0; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]];
+					nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]);
+					nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]);
+				}
+
+			}
+			
+			for(sc=0; sc<SECTORS_PER_BLOCK; sc++) {
+				if(offset > sc) {
+					PDEBUG("offset %d, sector %d\n", offset, sc);
+					continue;
+				}
+   				pt_smcpart->mtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer);
+				if(retlen != sizeof(ssfdc_buffer)) {
+					printk(KERN_WARNING "ssfdc_write : failed to read physical oob\n");
+					return -ENXIO;
+				}
+
+				nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]);
+				nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]);
+				for(i=0; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]];
+				nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]);
+				nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]);
+				
+				/* find out if the block is being used */
+
+
+				if(ssfdc_sector_blank(pt_smcpart, sc)) {
+					PDEBUG("ssfdc_write : zone %d, block %d, sector %d, lbn %d, blank, physical 0x%x\n",
+						zone, block, sc, sector, physical);
+					memcpy(&ssfdc_scratch[(sc * SECTOR_SIZE)], ptr+ptr_done, SECTOR_SIZE);
+					nand_calculate_ecc (pt_smcpart->mtd, (ptr + ptr_done), &ecc_calc[0]);
+					nand_calculate_ecc (pt_smcpart->mtd, (ptr + ptr_done + 256), &ecc_calc[3]);
+					for(i=0; i<6; i++) ssfdc_buffer[ssfdc_ecc[i]] = ecc_calc[i];
+					i = (block << 1) | 0x1000;
+					i |= ssfdc_parity(block);
+       				        ssfdc_buffer[7] = ssfdc_buffer[12] = i & 0xFF;
+                    			ssfdc_buffer[6] = ssfdc_buffer[11] = (i & 0xFF00) >> 0x08;
+
+                    pt_smcpart->mtd->write_ecc(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), SECTOR_SIZE, &retlen,
+								ptr + ptr_done, ssfdc_buffer, &ssfdc_ffoob_info);
+					if(retlen != SECTOR_SIZE) {
+						printk(KERN_WARNING "ssfdc_write : failed to write physical 0x%x, sector 0x%x, blank, retlen %d\n"
+								, physical, sc, retlen);
+						return -ENXIO;
+					}
+
+                    ptr_done += SECTOR_SIZE;
+					if(ptr_done >= size) break;
+				}
+				else {
+					new = ssfdc_allocate_new(pt_smcpart, zone);
+					/* erase the old block */
+		            *(pt_physical + ((physical % ZONE_SIZE) / SMC_BLOCK_SIZE)) = 0xFFFFFFFF;
+
+					PDEBUG("ssfdc_write : physical 0x%x + 0x%x = 0x%x\n",
+						(unsigned int)pt_physical, ((physical % ZONE_SIZE) / SMC_BLOCK_SIZE), 0xFFFFFFFF);
+					do_erase = TRUE;
+					PDEBUG("ssfdc_write : zone %d, block %d, sector %d, lbn %d, written, physical 0x%x, new 0x%x\n",
+						zone, block, sc, sector, physical, new);
+					break;
+				}
+			}
+		}
+		else {
+			ssfdc_cached = 0xFFFFFFFF;
+			memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch));
+			new = ssfdc_allocate_new(pt_smcpart, zone);
+			PDEBUG("ssfdc_write : zone %d, block %d, lbn %d, physical 0x%x, unallocated, new 0x%x\n",
+				zone, block, sector, physical, new);
+		}
+
+
+
+		if(new != -1) {
+
+
+			memcpy(&ssfdc_scratch[(offset * SECTOR_SIZE)], ptr, size);
+			PDEBUG("ssfdc_write : new 0x%x, offset 0x%x, size 0x%x, block 0x%x\n", new, offset, size, block);
+       		for(sc=0; sc<SECTORS_PER_BLOCK; sc++) {
+				memset(ssfdc_buffer, 0xFF, OOB_SIZE);
+				nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]);
+				nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]);
+				for(i=0; i<6; i++) ssfdc_buffer[ssfdc_ecc[i]] = ecc_calc[i];
+				i = (block << 1) | 0x1000;
+				i |= ssfdc_parity(block);
+                		ssfdc_buffer[7] = ssfdc_buffer[12] = i & 0xFF;
+                		ssfdc_buffer[6] = ssfdc_buffer[11] = (i & 0xFF00) >> 0x08;
+                memcpy(&ssfdc_oob_buf[sc * OOB_SIZE], ssfdc_buffer, OOB_SIZE);
+			}
+
+
+	   		pt_smcpart->mtd->write_ecc(pt_smcpart->mtd, new, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch,
+                        ssfdc_oob_buf, &ssfdc_ffoob_info);
+			if(retlen != SMC_BLOCK_SIZE) {
+				printk(KERN_WARNING "ssfdc_write : failed to write block, physical 0x%x, returned 0x%x\n", new, retlen);
+				return -ENXIO;
+			}
+			/* change the mapping table to reflect the new block placement */
+
+			*(pt_logical + block) = (new % ZONE_SIZE) / SMC_BLOCK_SIZE;
+			PDEBUG("ssfdc_write : logical 0x%x + 0x%x = 0x%x\n",
+						(unsigned int)pt_logical, block, (new % ZONE_SIZE) / SMC_BLOCK_SIZE);
+
+            		*(pt_physical + ((new % ZONE_SIZE) / SMC_BLOCK_SIZE)) = block;
+			PDEBUG("ssfdc_write : physical 0x%x + 0x%x = 0x%x\n",
+				(unsigned int)pt_physical, ((new % ZONE_SIZE) / SMC_BLOCK_SIZE), block);
+
+
+			ssfdc_cached = new;
+   	    }
+
+
+		ptr += size;
+		ptr_done = 0;
+		sectors_written += (size / SECTOR_SIZE);
+		if(do_erase) ssfdc_erase(pt_smcpart, physical);
+
+	}
+
+
+
+
+	return(0);
+}
+static int ssfdc_sector_blank(partition_t * pt_smcpart, int sc) {
+int b;
+
+	for(b=0; b<SECTOR_SIZE; b++) {
+		if(ssfdc_scratch[b + (sc * SECTOR_SIZE)] != 0xFF) return(0);
+	}
+	for(b=0; b<OOB_SIZE; b++) {
+		if((b==6) || (b==7) || (b==11) || (b==12)) continue;   // Block address fields
+		if(ssfdc_buffer[b] != 0xFF) return(0);
+	}
+    return(1);
+}
+static int ssfdc_allocate_new(partition_t * pt_smcpart, int zone) {
+
+	int new = pt_smcpart->last_written[zone] + 1;
+	int * pt_physical;
+	int physical;
+    	int block;
+	int retlen;
+	unsigned char oob[16];
+	
+
+	if(new >= BLOCKS_PER_ZONE) new = 0;
+
+
+	while (new != pt_smcpart->last_written[zone]) {
+       	block = new % BLOCKS_PER_ZONE;
+		pt_physical = pt_smcpart->zone + (zone * 2048) + 1024 + block;
+		physical = (zone * ZONE_SIZE) + (block * SMC_BLOCK_SIZE);
+
+	  	PDEBUG("ssfdc_allocate_new : zone %d, block %d, address 0x%08x, data 0x%08x\n",
+			zone, block, (unsigned int)pt_physical, *pt_physical);
+     		if(*pt_physical == 0xFFFFFFFF) {
+			PDEBUG("ssfdc_allocate_new : physical 0x%x = 0x%x\n", (unsigned int)pt_physical, *pt_physical);
+			memset(oob, 0, OOB_SIZE);
+			pt_smcpart->mtd->read_oob(pt_smcpart->mtd, physical, OOB_SIZE, &retlen, oob);
+			if((oob[5] == 0xFF) && (retlen == OOB_SIZE)) {   // If not a bad block
+				pt_smcpart->last_written[zone] = new;
+				return((new * SMC_BLOCK_SIZE) + (zone * ZONE_SIZE));
+			}
+			else {
+             			PDEBUG("ssfdc_allocate_new : new 0x%x, physical 0x%x, block status 0x%x, oob length 0x%x\n", new, physical, oob[5], retlen);
+			}
+		}
+		new++;
+		if(new >= BLOCKS_PER_ZONE) new = 0;
+	}
+
+   	panic("ssfdc_allocate_new : cant find free block\n");
+
+}
+	
+
+
+static int ssfdc_read(partition_t *pt_smcpart, caddr_t buffer,
+		    u_long sector, u_long nblocks)
+{
+	int zone, block, offset;
+	int sectors_read = 0;
+    int physical;
+	int size;
+	int retlen;
+	int i;
+	int sc;
+	unsigned char * ptr = (unsigned char *)buffer;
+	unsigned char ecc_code[6], ecc_calc[6];
+/*
+    unsigned char smc_status;
+
+  	smc_status = in_8((void *)&pt_ssfdc_smc->smc_status);
+	if(!(smc_status & SMC_PRESENT)) {
+		printk("ssfdc : media not present\n");
+		return -ENXIO;
+	}
+
+
+
+    if(smc_status & SMC_CHANGED) {
+   		out_8((void *)&pt_ssfdc_smc->smc_status, smc_status);
+		ssfdc_read_partitions(pt_smcpart);
+		printk("ssfdc : media change\n");
+	}
+*/
+	while(sectors_read < nblocks) {
+
+		zone = (sector + sectors_read) / SECTORS_PER_ZONE;
+		block = ((sector + sectors_read) % SECTORS_PER_ZONE) / SECTORS_PER_BLOCK ;
+		offset = ((sector + sectors_read) % SECTORS_PER_ZONE) % SECTORS_PER_BLOCK ;
+
+
+		if(offset) {
+			size = ((SECTORS_PER_BLOCK - offset) < (nblocks - sectors_read)) ?
+					(SECTORS_PER_BLOCK - offset) : (nblocks - sectors_read);
+		}
+		else {
+			size = (SECTORS_PER_BLOCK < (nblocks - sectors_read)) ? SECTORS_PER_BLOCK : nblocks - sectors_read;
+		}
+		size *= SECTOR_SIZE;
+
+	    PDEBUG("ssfdc_read :  device %d, sector %d, count %d, zone %d, block %d, offset %d, done %d, size %d, address 0x%x\n",
+			pt_smcpart->count, sector, nblocks, zone, block, offset, sectors_read, size, (unsigned int)ptr);
+
+			
+		physical = ssfdc_physical(pt_smcpart, zone, block);
+		if(physical >=  0) {
+			if(ssfdc_cached != physical) {
+           		pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, physical, SMC_BLOCK_SIZE, &retlen, ssfdc_scratch,
+													 ssfdc_oob_buf, &ssfdc_ffoob_info);
+				if(retlen != SMC_BLOCK_SIZE) {
+					printk(KERN_WARNING "ssfdc_read : failed to read physical\n");
+					return -ENXIO;
+				}
+				for(sc=0; sc<SECTORS_PER_BLOCK; sc++) {
+	    			pt_smcpart->mtd->read_oob(pt_smcpart->mtd, physical + (sc * SECTOR_SIZE), sizeof(ssfdc_buffer), &retlen, ssfdc_buffer);
+					if(retlen != sizeof(ssfdc_buffer)) {
+						printk(KERN_WARNING "ssfdc_read : failed to read physical oob\n");
+						return -ENXIO;
+					}
+					nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_calc[0]);
+					nand_calculate_ecc (pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_calc[3]);
+					for(i=0; i<3; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]];
+					for(i=3; i<6; i++) ecc_code[i] = ssfdc_buffer[ssfdc_ecc[i]];
+					nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[sc * SECTOR_SIZE], &ecc_code[0], &ecc_calc[0]);
+					nand_correct_data(pt_smcpart->mtd, &ssfdc_scratch[(sc * SECTOR_SIZE) + 256], &ecc_code[3], &ecc_calc[3]);
+				}
+
+				/* Get the ecc bytes and check that they are ok */
+
+
+			}
+			ssfdc_cached = physical;
+			
+			
+		}
+		else {
+			memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch));
+			ssfdc_cached = 0xFFFFFFFF;
+		}
+			
+
+		memcpy(ptr, &ssfdc_scratch[(offset * SECTOR_SIZE)], size);
+		ptr += size;
+		sectors_read += (size / SECTOR_SIZE);	
+	}
+
+
+	                                                    
+	return(0);
+}
+
+static void ssfdc_erase_callback(struct erase_info *erase) {
+
+	PDEBUG("ssfdc_erase_callback : wake erase\n");
+    	up(&ssfdc_semaphore);
+	PDEBUG("ssfdc_erase_callback : woken erase\n");
+}
+
+static int ssfdc_erase(partition_t *pt_smcpart, unsigned int offset)
+{
+	int ret = 0;
+    	struct erase_info *erase;
+	unsigned char * junk;
+	unsigned char * oob;
+	int retlen;
+	int b, sc;
+
+
+	PDEBUG("ssfdc_erase : offset 0x%08x\n", offset);
+
+	erase=kmalloc(sizeof(struct erase_info), GFP_KERNEL);
+    	junk=kmalloc(pt_smcpart->mtd->erasesize + 16, GFP_KERNEL);
+    	oob = junk + pt_smcpart->mtd->erasesize;
+
+    	if (!erase)
+        	 return -ENOMEM;
+    	if (!junk)
+        	 return -ENOMEM;
+
+    	erase->addr = offset;
+    	erase->len = pt_smcpart->mtd->erasesize;
+	erase->callback = ssfdc_erase_callback;
+    	ret = pt_smcpart->mtd->erase(pt_smcpart->mtd, erase);
+	if(ret) {
+     		printk(KERN_WARNING "ssfdc_erase : failed status 0x%x\n", ret);
+		goto end;
+
+	}
+
+	down(&ssfdc_semaphore);
+
+	pt_smcpart->mtd->read_ecc(pt_smcpart->mtd, offset, SMC_BLOCK_SIZE, &retlen, junk,
+							 ssfdc_oob_buf, &ssfdc_ffoob_info);
+	if(retlen != SMC_BLOCK_SIZE) {
+           	printk(KERN_WARNING "ssfdc_erase : offset 0x%x, read returned length %d\n", offset, retlen);
+		goto end;
+	}
+
+
+	for(sc=0; sc < SECTORS_PER_BLOCK; sc++) {
+		for(b=0; b<SECTOR_SIZE; b++) {
+			if(*(junk + (b + (sc * SECTOR_SIZE))) != 0xFF) {
+             			printk(KERN_WARNING "ssfdc_erase : offset 0x%x, sector 0x%x, byte 0x%x, data 0x%02x, expected 0xff\n"
+						, offset, sc, b, *(junk + (b + (sc * SECTOR_SIZE))));
+				goto end;
+			}
+		}
+		pt_smcpart->mtd->read_oob(pt_smcpart->mtd, offset + (sc * SECTOR_SIZE), OOB_SIZE, &retlen, oob);
+		if(retlen != OOB_SIZE) {
+           		printk(KERN_WARNING "ssfdc_erase : offset 0x%x, read oob returned length %d\n", offset, retlen);
+			goto end;
+		}
+		for(b=0; b<OOB_SIZE; b++) {
+			if(*(oob+b) != 0xFF) {
+             			printk(KERN_WARNING "ssfdc_erase : offset 0x%x, byte 0x%x, oob got 0x%02x, expected 0xff\n", 
+						offset, b, *(oob+b));
+				goto end;
+			}
+		}
+	}
+
+end:
+	
+    kfree(erase);
+	kfree(junk);
+
+    return ret;
+} /* erase_xfer */
+
+
+
+
+
+int init_ssfdc(void)
+{
+	int result, i;
+
+//	unsigned char smc_status;
+//	#define B01159_FIO_PBASE 0x0000000148000000  /* Physical Base address of SMC control chip  */
+
+	printk(KERN_INFO "SSFDC block device translation layer V1.0\n");
+/*
+	pt_ssfdc_smc = ioremap64(B01159_FIO_PBASE, 1024);
+	if(!pt_ssfdc_smc){
+     	printk("ssfdc : failed to map SMC control device\n");
+        return(-EFAULT);
+	}
+	
+  	smc_status = in_8((void *)&pt_ssfdc_smc->smc_status);
+*/	
+    memset(ssfdc_ffoob_buf, 0xFF, sizeof(ssfdc_ffoob_buf));
+    
+	for (i = 0; i < MAX_DEVICES*MAX_PARTITIONS; i++) {
+		ssfdc_hd[i].nr_sects = 0;
+		ssfdc_hd[i].start_sect = 0;
+		ssfdc_blocksizes[i] = 4096;
+    	}
+   	blksize_size[SSFDC_MAJOR] = ssfdc_blocksizes;
+   	ssfdc_gendisk.major = SSFDC_MAJOR;
+
+
+	memset(ssfdc_scratch, 0xFF, sizeof(ssfdc_scratch));
+
+	result = register_blkdev(ssfdc_major, "ssfdc", &ssfdc_fops);
+	if(result != 0) {
+		printk(KERN_WARNING "ssfdc : failed to get a major number\n");
+		return(result);
+	}
+//	if(ssfdc_major == 0) ssfdc_major = result;
+	
+    	blk_init_queue(BLK_DEFAULT_QUEUE(ssfdc_major), &do_ssfdc_request);
+
+    	add_gendisk(&ssfdc_gendisk);
+
+
+
+    	register_mtd_user(&ssfdc_notifier);
+
+
+	init_MUTEX_LOCKED(&ssfdc_semaphore);
+
+
+
+    	return 0;
+}
+
+static void __exit cleanup_ssfdc(void)
+{
+	int i;
+
+	for(i=0; i<MAX_DEVICES; i++) {
+       		if(SMCParts[i].zone)kfree(SMCParts[i].zone);
+	}
+
+
+    	unregister_mtd_user(&ssfdc_notifier);
+    	unregister_blkdev(ssfdc_major, "ssfdc");
+    	blk_cleanup_queue(BLK_DEFAULT_QUEUE(ssfdc_major));
+
+
+
+    	blksize_size[SSFDC_MAJOR] = NULL;
+    	del_gendisk(&ssfdc_gendisk);
+
+}
+
+module_init(init_ssfdc);
+module_exit(cleanup_ssfdc);
+
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Simon Haynes <simon@baydel.com>");
+MODULE_DESCRIPTION("SSFDC translation layer support for MTD");
+
+
+
+
diff -uNrb linux-2.4.30/drivers/net/korina.c linux/drivers/net/korina.c
--- linux-2.4.30/drivers/net/korina.c	Thu Jan  1 03:00:00 1970
+++ linux/drivers/net/korina.c	Fri Mar 11 17:21:42 2005
@@ -0,0 +1,1541 @@
+/*
+************************************************************************************
+korina.c
+
+Copyright 2003 Integrated Device Technology, Inc.
+Author: Integrated Device Technology, Inc.
+
+Copyright 2002 MontaVista Software Inc.
+Author: MontaVista Software, Inc.
+	stevel@mvista.com or source@mvista.com
+  
+A driver for the IDT RC32434 on-chip ethernet controller.
+************************************************************************************
+B Maruthanayagam: 	Ported from sonic.c 
+************************************************************************************
+cgg: 			Modified code. 
+************************************************************************************
+P Sadik: 		Applied filters.
+************************************************************************************
+Haofeng Kou			6/7/03
+
+Using the IOD COD and DMA chaining mode to handle the Transmit. 
+In order to prevent the DMA Race Condition, make software access NDPTR only for tansmit.
+Using the IOF COF and NDPTR of DMA chaining to handle the Receive.
+In order to prevent the DMA Race Condition, use NDPTR 
+ trigger DMA chaining once overwrite happen for receive.
+Modify korina_restart() to make it recover Bridge mode setting after reset.
+Adjust the dev_kfree_skb_any() and dev_kfree_skb_irq() usage.
+Adjust the netif_wake_queue() and netif_start_queue() usage.
+Add in code to handle the case once there is no memory in system(only for special situations).
+************************************************************************************
+Haofeng Kou			6/16/03
+Add in code to handle the TX Underflow.
+************************************************************************************
+Kiran Rao 
+Ported to Korina.
+************************************************************************************
+Haofeng Kou			10/09/03
+Trigger the waiting queue in the TX ISR
+Finish the Korina porting.
+*************************************************************************************
+P. Sadik  Oct 10, 2003
+'Management Clock Prescaler Divisor' is no longer determined at compile time, but
+is caculated at run time, based on idt_cpu_freq.
+************************************************************************************
+Haofeng Kou			10/27/03
+Change the ISR to use Tasklet for the TX and RX.
+Add in the DMA Abort code and use DMA Abort instead of the DMA halt. (DMA abort finished 
+faster than DMA halt, so replace the halt code by abort)
+Optimize the interrupt disable/enable actions to reduce the number of interrupt.
+Improve the CPU usage.
+*************************************************************************************
+Haofeng Kou			11/18/03
+Update all the debug features(ASSERT, DBG, ERR, INFO and WARN).
+Add the MII-PHY LXT972 duplex mode auto-negotiation support.(Not enabled at this time)
+*************************************************************************************
+*/
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ctype.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/init.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/in.h>
+#include <linux/slab.h> 
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/errno.h>
+#include <linux/ethtool.h>
+#include <linux/mii.h>
+#include <asm/bootinfo.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/pgtable.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include "korina.h"
+#include  <asm/rc32434/rc32434.h>
+
+
+/*
+#define PROFILE_START \
+	unsigned long __x1, __x2; \
+	static unsigned long __total; \
+	static unsigned long __lastcheck = 0; \
+	__x1 = read_c0_count();
+
+#define PROFILE_END \
+	__x2 = read_c0_count(); \
+	__total += __x2 - __x1; \
+    if (!__lastcheck) __lastcheck = jiffies; \
+	if (jiffies > __lastcheck + (HZ * 10)) { \
+		printk("%s: %lu\n", __FUNCTION__, __total / 1330); \
+		__total = 0; \
+		__lastcheck += HZ * 10; \
+	}
+*/
+#define PROFILE_START
+#define PROFILE_END
+
+#define STATION_ADDRESS_HIGH(dev) (((dev)->dev_addr[0] << 8) | \
+			           ((dev)->dev_addr[1]))
+#define STATION_ADDRESS_LOW(dev)  (((dev)->dev_addr[2] << 24) | \
+				   ((dev)->dev_addr[3] << 16) | \
+				   ((dev)->dev_addr[4] << 8)  | \
+				   ((dev)->dev_addr[5]))
+
+static unsigned rx_ints = 0, tx_ints = 0;
+static unsigned rx_tasks = 0, tx_tasks = 0;
+static unsigned thr_packs = 0, thr_time = 0;
+#define MY_ARRAY_SIZE 20
+static unsigned array[MY_ARRAY_SIZE];
+static unsigned array_idx = 0;
+static unsigned last_check = 0;
+
+#define MII_CLOCK 1250000 				/* no more than 2.5MHz */
+
+extern char korina_mac_address[18];
+
+MODULE_PARM(mac, "c18");
+MODULE_PARM(mac1, "c18");
+MODULE_PARM_DESC(mac, "MAC address for RC32434 ethernet");
+MODULE_PARM_DESC(mac1, "MAC address for RC32434 ethernet1");
+
+static struct korina_if_t {
+	struct net_device *dev;
+	char* mac_str;
+	u32 iobase;
+	int rx_dma_irq;
+	int tx_dma_irq;
+	int rx_ovr_irq;
+  	int tx_und_irq;		
+} korina_iflist[] = {
+	{NULL, korina_mac_address, ETH_PhysicalAddress,
+	 ETH_DMA_RX_IRQ, ETH_DMA_TX_IRQ, ETH_RX_OVR_IRQ, ETH_TX_UND_IRQ},
+	{NULL, 0, 0}
+};
+
+
+static int mdio_read(struct net_device *dev, int phy_id, int regnum)
+{
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+//	unsigned flags;
+	int value;
+
+//	spin_lock_irqsave(&lp->lock, flags);
+
+//	local_writel(0, &lp->eth_regs->miimcfg);  
+	local_writel(regnum, &lp->eth_regs->miimaddr);  
+	local_writel(MIIMCMD_rd_m, &lp->eth_regs->miimcmd);  
+	while (local_readl(&lp->eth_regs->miimind) & MIIMIND_bsy_m);
+//	while (local_readl(&lp->eth_regs->miimind) & MIIMIND_nv_m);
+
+	if (local_readl(&lp->eth_regs->miimind) & MIIMIND_nv_m) {
+//		printk("korina rd %02x: not valid\n", regnum);
+		return 0;
+	}
+
+	value = local_readl(&lp->eth_regs->miimrdd);
+	local_writel(0, &lp->eth_regs->miimcmd);  
+
+//	printk("korina rd %02x: %04x\n", regnum, value);
+
+//     	spin_unlock_irqrestore(&lp->lock, flags);
+	return value;
+}
+
+static void mdio_write(struct net_device *dev, int phy_id, int regnum,
+		       int value)
+{
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+//      unsigned flags;
+
+//	printk("korina wr %02x: %04x\n", regnum, value);
+//	spin_lock_irqsave(&lp->lock, flags);
+
+//	local_writel(0, &lp->eth_regs->miimcfg);  
+//	local_writel(0, &lp->eth_regs->miimcmd);  
+	local_writel(regnum, &lp->eth_regs->miimaddr);  
+	local_writel(value, &lp->eth_regs->miimwtd);  
+	while(local_readl(&lp->eth_regs->miimind) & MIIMIND_bsy_m);
+
+//     	spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+
+static void korina_get_drvinfo (struct net_device *dev,
+				struct ethtool_drvinfo *info)
+{
+	strcpy(info->driver, "korina");
+	strcpy(info->version, "1.0");
+}
+
+static int korina_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct korina_local *np = dev->priv;
+	int rc;
+
+//	spin_lock_irq(&np->lock);
+	rc = mii_ethtool_gset(&np->mii_if, cmd);
+//	spin_unlock_irq(&np->lock);
+
+	return rc;
+}
+
+static int korina_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct korina_local *np = dev->priv;
+	int rc;
+
+	spin_lock_irq(&np->lock);
+	rc = mii_ethtool_sset(&np->mii_if, cmd);
+	spin_unlock_irq(&np->lock);
+
+	return rc;
+}
+
+static int korina_nway_reset(struct net_device *dev)
+{
+	struct korina_local *np = dev->priv;
+
+	return mii_nway_restart(&np->mii_if);
+}
+
+static u32 korina_get_link(struct net_device *dev)
+{
+	struct korina_local *np = dev->priv;
+
+	return mii_link_ok(&np->mii_if);
+}
+
+
+static struct ethtool_ops korina_ethtool_ops = {
+	.get_drvinfo		= korina_get_drvinfo,
+	.get_settings		= korina_get_settings,
+	.set_settings		= korina_set_settings,
+	.nway_reset		= korina_nway_reset,
+	.get_link		= korina_get_link,
+
+	.get_sg			= ethtool_op_get_sg,
+	.set_sg			= ethtool_op_set_sg,
+	.get_tx_csum		= ethtool_op_get_tx_csum,
+	.set_tx_csum		= ethtool_op_set_tx_csum,
+};
+
+static int korina_ioctl(struct net_device *dev, struct ifreq *rq, int cmd)
+{
+	struct korina_local *lp = dev->priv;
+	struct mii_ioctl_data *data = (struct mii_ioctl_data *) & rq->ifr_data;
+	int rc;
+
+	if (!netif_running(dev))
+		return -EINVAL;
+
+	spin_lock_irq(&lp->lock);
+	rc = generic_mii_ioctl(&lp->mii_if, data, cmd, NULL);
+	spin_unlock_irq(&lp->lock);
+
+	return rc;
+}
+
+
+static int
+parse_mac_addr(struct net_device *dev, char* macstr)    
+{    
+	int i, j;
+	unsigned char result, value;
+
+	for (i=0; i<6; i++) {
+		result = 0;
+		if (i != 5 && *(macstr+2) != ':') {
+			ERR(__FILE__ "invalid mac address format: %d %c\n",
+			    i, *(macstr+2));
+			return -EINVAL;
+		}
+				
+		for (j=0; j<2; j++) {
+			if (isxdigit(*macstr) &&
+			    (value = isdigit(*macstr) ? *macstr-'0' : 
+			     toupper(*macstr)-'A'+10) < 16) {
+				result = result*16 + value;
+				macstr++;
+			} else {
+				ERR(__FILE__ "invalid mac address "
+				    "character: %c\n", *macstr);
+				return -EINVAL;
+			}
+		}
+				
+		macstr++; 
+		dev->dev_addr[i] = result;
+	}
+		
+	return 0;
+}
+
+static inline void korina_abort_tx(struct net_device *dev)
+{
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+//	printk("korina_abort_tx\n");
+
+	rc32434_abort_dma(dev, lp->tx_dma_regs);
+	
+}
+static inline void korina_abort_rx(struct net_device *dev)
+{
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+//	printk("korina_abort_rx\n");
+
+	rc32434_abort_dma(dev, lp->rx_dma_regs);
+	
+}
+static inline void korina_halt_tx(struct net_device *dev)
+{
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+//	printk("korina_halt_tx\n");
+	if (rc32434_halt_dma(lp->tx_dma_regs))
+		ERR(__FUNCTION__ ": timeout!\n");
+}
+static inline void korina_halt_rx(struct net_device *dev)
+{
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+//	printk("korina_halt_rx\n");
+	if (rc32434_halt_dma(lp->rx_dma_regs))
+		ERR(__FUNCTION__ ": timeout!\n");
+}
+
+static inline void korina_start_tx(struct korina_local *lp,
+				   volatile DMAD_t td)
+{
+//	printk("korina_start_tx\n");
+	rc32434_start_dma(lp->tx_dma_regs, virt_to_phys(td));
+}
+static inline void korina_start_rx(struct korina_local *lp,
+				   volatile DMAD_t rd)
+{
+//	printk("korina_start_rx: %p\n", virt_to_phys(rd));
+	rc32434_start_dma(lp->rx_dma_regs, virt_to_phys(rd));
+}
+
+static inline void korina_chain_tx(struct korina_local *lp,
+				   volatile DMAD_t td)
+{
+//	printk("korina_chain_tx\n");
+	rc32434_chain_dma(lp->tx_dma_regs, virt_to_phys(td));
+}
+static inline void korina_chain_rx(struct korina_local *lp,
+				   volatile DMAD_t rd)
+{
+//	printk("korina_chain_rx\n");
+	rc32434_chain_dma(lp->rx_dma_regs, virt_to_phys(rd));
+}
+
+#ifdef KORINA_PROC_DEBUG
+static int korina_read_proc(char *buf, char **start, off_t fpos,
+			    int length, int *eof, void *data)
+{
+	struct net_device *dev = (struct net_device *)data;
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+	int len = 0;
+
+	/* print out header */
+	len += sprintf(buf + len, "\n\tRC32434 Ethernet Debug\n\n");
+
+	len += sprintf (buf + len,
+			"DMA halt count      = %10d, total pkt cnt = %10d\n",
+			lp->dma_halt_cnt, lp->halt_tx_count);
+	len += sprintf (buf + len,
+			"DMA run count       = %10d, total pkt cnt = %10d\n",
+			lp->dma_run_cnt, lp->run_tx_count);
+	len += sprintf (buf + len,
+			"DMA race count      = %10d, total pkt cnt = %10d\n",
+			lp->dma_race_cnt, lp->race_tx_count);
+	len += sprintf (buf + len,
+			"DMA collision count = %10d, total pkt cnt = %10d\n",
+			lp->dma_collide_cnt, lp->collide_tx_count);
+
+	if (fpos >= len) {
+		*start = buf;
+		*eof = 1;
+		return 0;
+	}
+	*start = buf + fpos;
+	if ((len -= fpos) > length)
+		return length;
+	*eof = 1;
+	return len;
+
+}
+#endif
+
+/*
+ * Restart the KORINA ethernet controller. 
+ */
+static int korina_restart(struct net_device *dev)
+{
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+//	printk("korina_restart\n");
+	/*
+	 * Disable interrupts
+	 */
+	disable_irq(lp->rx_irq);
+	disable_irq(lp->tx_irq);
+	disable_irq(lp->und_irq);
+
+	/* Mask F E bit in Tx DMA */
+	local_writel(local_readl(&lp->tx_dma_regs->dmasm) | DMASM_f_m | DMASM_e_m, &lp->tx_dma_regs->dmasm);
+	/* Mask D H E bit in Rx DMA */
+	local_writel(local_readl(&lp->rx_dma_regs->dmasm) | DMASM_d_m | DMASM_h_m | DMASM_e_m, &lp->rx_dma_regs->dmasm);
+
+	korina_init(dev);
+	korina_multicast_list(dev);
+
+	enable_irq(lp->und_irq);
+	enable_irq(lp->tx_irq);
+	enable_irq(lp->rx_irq);
+
+	return 0;
+}
+
+int korina_init_module(void)
+{
+	int i, retval=0;
+
+	for (i = 0; korina_iflist[i].iobase; i++) { 
+		retval |= korina_probe(i);
+	} 
+
+	return retval;
+}
+
+static int korina_probe(int port_num)
+{
+	struct korina_local *lp = NULL;
+	struct korina_if_t *bif = NULL;
+	struct net_device *dev = NULL;
+	int i, retval;
+//	printk("korina_probe\n");
+	bif = &korina_iflist[port_num];
+           
+	request_region(bif->iobase, 0x24C, "KORINA");
+	
+	/* Allocate a new 'dev' if needed */
+	dev = init_etherdev(0, sizeof(struct korina_local));
+	bif->dev = dev;
+	
+        INFO("RC32434 ethernet found at 0x%08x\n", bif->iobase);
+
+	/* Fill in the 'dev' fields. */
+	dev->base_addr = bif->iobase;
+	/* just use the rx dma irq */
+	dev->irq = bif->rx_dma_irq; 
+	
+	if ((retval = parse_mac_addr(dev, bif->mac_str))) {
+		ERR(__FUNCTION__ ": MAC address parse failed\n");
+		retval = -EINVAL;
+		goto probe_err_out;
+	}
+
+	INFO("HW Address ");
+	for (i = 0; i < 6; i++) {
+		printk("%2.2x", dev->dev_addr[i]);
+		if (i<5)
+			printk(":");
+	}
+	printk("\n");
+    
+	INFO("Rx IRQ %d, Tx IRQ %d\n", bif->rx_dma_irq, bif->tx_dma_irq);
+    
+	/* Initialize the device structure. */
+//	printk("dev->priv: %p\n", dev->priv);
+	if (dev->priv == NULL) {
+		lp = (struct korina_local *)kmalloc(sizeof(*lp), GFP_KERNEL);
+		memset(lp, 0, sizeof(struct korina_local));
+	} else {
+		lp = (struct korina_local *)dev->priv;
+	}
+
+	dev->priv = lp;
+	lp->rx_irq = bif->rx_dma_irq;
+	lp->tx_irq = bif->tx_dma_irq;
+	lp->ovr_irq = bif->rx_ovr_irq;
+	lp->und_irq = bif->tx_und_irq;
+
+	
+	lp->eth_regs = ioremap_nocache(bif->iobase,
+				       sizeof(*lp->eth_regs));
+
+	if (!lp->eth_regs) {
+		ERR("Can't remap eth registers\n");
+		retval = -ENXIO;
+		goto probe_err_out;
+	}
+//	printk("bif->iobase: %x\n", bif->iobase);
+//	printk("lp->eth_regs: %p\n", lp->eth_regs);
+
+	
+        lp->rx_dma_regs =
+		ioremap_nocache(DMA0_PhysicalAddress + 0*DMA_CHAN_OFFSET,
+	 			sizeof(struct DMA_Chan_s));
+
+	if (!lp->rx_dma_regs) {
+		ERR("Can't remap Rx DMA registers\n");
+		retval = -ENXIO;
+		goto probe_err_out;
+	}		
+//	printk("lp->rx_dma_regs: %p\n", lp->rx_dma_regs);
+	lp->tx_dma_regs =
+		ioremap_nocache(DMA0_PhysicalAddress  + 1*DMA_CHAN_OFFSET,
+				sizeof(struct DMA_Chan_s));
+
+        if (!lp->tx_dma_regs) {
+		ERR("Can't remap Tx DMA registers\n");
+		retval = -ENXIO;
+		goto probe_err_out;
+	}
+//	printk("lp->tx_dma_regs: %p\n", lp->tx_dma_regs);
+        
+
+	lp->td_ring =
+		(DMAD_t)kmalloc(TD_RING_SIZE + RD_RING_SIZE,
+					      GFP_KERNEL);
+	if (!lp->td_ring) {
+		ERR("Can't allocate descriptors\n");
+		retval = -ENOMEM;
+		goto probe_err_out;
+	}
+//	printk("lp->td_ring: %p\n", lp->td_ring);
+
+	dma_cache_inv((unsigned long)(lp->td_ring), TD_RING_SIZE + RD_RING_SIZE);
+
+	/* now convert TD_RING pointer to KSEG1 */
+	lp->td_ring = (DMAD_t )KSEG1ADDR(lp->td_ring);
+//	printk("lp->td_ring: %p\n", lp->td_ring);
+	lp->rd_ring = &lp->td_ring[KORINA_NUM_TDS];
+//	printk("lp->rd_ring: %p\n", lp->rd_ring);
+
+	spin_lock_init(&lp->lock);
+
+	dev->open = korina_open;
+	dev->stop = korina_close;
+	dev->hard_start_xmit = korina_send_packet;
+	dev->get_stats	= korina_get_stats;
+	dev->set_multicast_list = &korina_multicast_list;
+	dev->tx_timeout = korina_tx_timeout;
+	dev->watchdog_timeo = KORINA_TX_TIMEOUT;
+
+	lp->rx_tasklet = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
+	tasklet_init(lp->rx_tasklet, korina_rx_tasklet, (unsigned long)dev);
+	lp->tx_tasklet = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
+	tasklet_init(lp->tx_tasklet, korina_tx_tasklet, (unsigned long)dev);
+
+	dev->do_ioctl = korina_ioctl;
+	dev->ethtool_ops = &korina_ethtool_ops;
+	lp->mii_if.dev = dev;
+	lp->mii_if.mdio_read = mdio_read;
+	lp->mii_if.mdio_write = mdio_write;
+	lp->mii_if.phy_id_mask = 0x1f;
+	lp->mii_if.reg_num_mask = 0x1f;
+
+#ifdef KORINA_PROC_DEBUG
+	lp->ps = create_proc_read_entry ("net/rc32434", 0, NULL,
+					 korina_read_proc, dev);
+#endif
+
+	/* Fill in the fields of the device structure with ethernet values. */
+	ether_setup(dev);
+	return 0;
+
+ probe_err_out:
+	korina_cleanup_module();
+	ERR(__FUNCTION__ " failed.  Returns %d\n", retval);
+	return retval;
+}
+
+
+/*
+ * Open/initialize the KORINA controller.
+ *
+ * This routine should set everything up anew at each open, even
+ *  registers that "should" only need to be set once at boot, so that
+ *  there is non-reboot way to recover if something goes wrong.
+ */
+
+static int korina_open(struct net_device *dev)
+{
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+//	printk("korina_open in\n");
+
+	MOD_INC_USE_COUNT;
+	
+	/*
+	 * Initialize
+	 */
+	if (korina_init(dev)) {
+	  ERR("Erroe: cannot open the Ethernet device\n");
+	 return -EAGAIN;
+	}
+	
+	/*
+	 * Install the interrupt handler that handles the dma Done and
+	 * Finished Events.
+	 */	
+	if (request_irq(lp->rx_irq, &korina_rx_dma_interrupt,
+			SA_SHIRQ | SA_INTERRUPT,
+			"korina ethernet Rx", dev)) {
+		ERR(__FUNCTION__ ": unable to get Rx DMA IRQ %d\n",
+		    lp->rx_irq);
+		MOD_DEC_USE_COUNT;
+		return -EAGAIN;
+	}
+	if (request_irq(lp->tx_irq, &korina_tx_dma_interrupt,
+			SA_SHIRQ | SA_INTERRUPT,
+			"korina ethernet Tx", dev)) {
+		ERR(__FUNCTION__ ": unable to get Tx DMA IRQ %d\n",
+		    lp->tx_irq);
+		free_irq(lp->rx_irq, dev);
+		MOD_DEC_USE_COUNT;
+		return -EAGAIN;
+	}
+
+	
+	/* Install handler for underflow error. */
+	if (request_irq(lp->und_irq, &korina_und_interrupt,
+			SA_SHIRQ | SA_INTERRUPT,
+			"Ethernet Underflow", dev)) {
+		ERR(__FUNCTION__ ": unable to get UND IRQ %d\n",
+		    lp->und_irq);
+		free_irq(lp->rx_irq, dev);
+		free_irq(lp->tx_irq, dev);
+		MOD_DEC_USE_COUNT;
+		return -EAGAIN;
+	}
+
+	/*Start MII-PHY Timer*/
+	//Not enabled this feature at this time.
+	/*
+	init_timer(&lp->mii_phy_timer);
+   	lp->mii_phy_timer.expires = jiffies + 10 * HZ;	
+   	lp->mii_phy_timer.data = (unsigned long)dev;
+   	lp->mii_phy_timer.function	 = korina_mii_handler;
+   	add_timer(&lp->mii_phy_timer);
+	*/
+	
+//	printk("korina_open out\n");
+	return 0;
+}
+
+
+
+/*
+ * Close the KORINA device
+ */
+static int
+korina_close(struct net_device *dev)
+{
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+	u32 tmp;
+//	printk("korina_close in\n");
+    
+	/*
+	 * Disable interrupts
+	 */
+	disable_irq(lp->rx_irq);
+	disable_irq(lp->tx_irq);
+	disable_irq(lp->und_irq);
+
+
+		
+	tmp = local_readl(&lp->tx_dma_regs->dmasm);
+	tmp = tmp | DMASM_f_m | DMASM_e_m;
+	local_writel(tmp, &lp->tx_dma_regs->dmasm);
+     
+	tmp = local_readl(&lp->rx_dma_regs->dmasm);
+	tmp = tmp | DMASM_d_m | DMASM_h_m | DMASM_e_m;
+	local_writel(tmp, &lp->rx_dma_regs->dmasm);
+
+	free_irq(lp->rx_irq, dev);
+	free_irq(lp->tx_irq, dev);
+	free_irq(lp->und_irq, dev);
+
+	//Not enabled this feature at this time.
+	//del_timer(&lp->mii_phy_timer);
+
+	MOD_DEC_USE_COUNT;
+//	printk("korina_close out\n");
+	return 0;
+}
+
+
+/* transmit packet */
+static int korina_send_packet(struct sk_buff *skb, struct net_device *dev)
+{
+ 	struct korina_local	*lp = (struct korina_local *)dev->priv;
+  	unsigned long 	flags;
+  	u32			length;
+ 	DMAD_t		td;
+	PROFILE_START;
+
+//	printk("korina_send_packet\n");
+	spin_lock_irqsave(&lp->lock, flags);
+
+	td = &lp->td_ring[lp->tx_chain_tail];
+
+ 	/* stop queue when full, drop pkts if queue already full */
+  	if(lp->tx_count >= (KORINA_NUM_TDS - 2))
+  	{
+
+		lp->tx_full = 1;
+
+	  	if(lp->tx_count == (KORINA_NUM_TDS - 2))
+	  	{
+			/* this pkt is about to fill the queue*/
+			ERR("Tx Ring now full, queue stopped.\n");			
+			netif_stop_queue(dev);
+	  	}else{
+			/* this pkt cannot be added to the full queue*/
+			ERR("Tx ring full, packet dropped\n");
+			lp->stats.tx_dropped++;
+			dev_kfree_skb_any(skb);
+			spin_unlock_irqrestore(&lp->lock, flags);
+			return 1;
+	  	}	   
+  	}	   
+  	lp->tx_count ++;
+  
+ 	if (lp->tx_skb[lp->tx_chain_tail] != NULL)
+  		dev_kfree_skb_any(lp->tx_skb[lp->tx_chain_tail]);
+
+	lp->tx_skb[lp->tx_chain_tail] = skb;
+  
+  	length = skb->len;
+
+  	/*Setup the transmit descriptor. */
+  	td->ca = virt_to_phys(skb->data);
+
+	/* Using the NDPTR to handle the DMA Race Condition */
+   	if(local_readl(&(lp->tx_dma_regs->dmandptr)) == 0) 		
+  	{
+		if( lp->tx_chain_status == empty ) 
+		{
+			td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m; 
+			lp->tx_chain_tail = (lp->tx_chain_tail + 1) & KORINA_TDS_MASK;
+			local_writel(virt_to_phys(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr));			
+			lp->tx_chain_head = lp->tx_chain_tail; 
+		}
+		else
+		{
+			lp->td_ring[(lp->tx_chain_tail-1)& KORINA_TDS_MASK].control &=  ~(DMAD_cof_m); 	
+			lp->td_ring[(lp->tx_chain_tail-1)& KORINA_TDS_MASK].link =  virt_to_phys(td);
+			td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m;
+			lp->tx_chain_tail = (lp->tx_chain_tail + 1) & KORINA_TDS_MASK;
+			lp->tx_chain_status = empty;
+			local_writel(virt_to_phys(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr));			
+			
+			lp->tx_chain_head = lp->tx_chain_tail; 
+		}
+   	}
+	else
+	{
+		if( lp->tx_chain_status == empty )
+		{
+			td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m;
+			lp->td_ring[(lp->tx_chain_tail-1)& KORINA_TDS_MASK].link =  virt_to_phys(td);	
+			lp->tx_chain_tail = (lp->tx_chain_tail + 1) & KORINA_TDS_MASK;
+			lp->tx_chain_status = filled;		
+		}
+		else
+		{
+			lp->td_ring[(lp->tx_chain_tail-1)& KORINA_TDS_MASK].control &=  ~(DMAD_cof_m); 	
+			lp->td_ring[(lp->tx_chain_tail-1)& KORINA_TDS_MASK].link =  virt_to_phys(td);	
+			td->control = DMA_COUNT(length) |DMAD_cof_m |DMAD_iof_m; 
+			lp->tx_chain_tail = (lp->tx_chain_tail + 1) & KORINA_TDS_MASK;
+		}		
+	}	   
+
+	dev->trans_start = jiffies;
+  
+     	spin_unlock_irqrestore(&lp->lock, flags);
+
+  
+	PROFILE_END;
+  	return 0;
+}
+
+/* Ethernet MII-PHY Handler */
+/*
+static void korina_mii_handler(unsigned long data)
+{
+	struct net_device *dev = (struct net_device *)data;		
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+	unsigned long duplex_status;
+  	unsigned long 	flags;
+
+	spin_lock_irqsave(&lp->lock, flags);
+
+	local_writel(0, &lp->eth_regs->miimcfg);  
+	local_writel(0, &lp->eth_regs->miimcmd);  
+	local_writel(0x05, &lp->eth_regs->miimaddr);  
+	local_writel(MIIMCMD_scn_m, &lp->eth_regs->miimcmd);  
+	while(local_readl(&lp->eth_regs->miimind) & MIIMIND_nv_m);
+	
+	duplex_status = (local_readl(&lp->eth_regs->miimrdd) & 0x140)? ETHMAC2_fd_m: 0;
+	if(duplex_status != lp->duplex_mode)
+	{
+		ERR("The MII-PHY is Auto-negotiated to %s-Duplex mode\n", duplex_status? "Full":"Half");		
+		lp->duplex_mode = duplex_status;
+		korina_restart(dev);		
+	}
+
+   	lp->mii_phy_timer.expires = jiffies + 10 * HZ;	
+   	add_timer(&lp->mii_phy_timer);
+
+     	spin_unlock_irqrestore(&lp->lock, flags);
+
+}
+*/
+/* Ethernet Tx Underflow interrupt */
+static void korina_und_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	struct korina_local *lp;
+	unsigned int i;
+	
+	ASSERT(dev != NULL);
+	
+	netif_stop_queue(dev);	
+	lp = (struct korina_local *)dev->priv;
+
+	ERR("Tx underflow - i/f reset\n");
+	spin_lock(&lp->lock);
+
+	i = local_readl(&lp->eth_regs->ethintfc);
+  	i &= ~ETHINTFC_und_m;
+	local_writel(i, &lp->eth_regs->ethintfc);
+
+	/* Restart interface */
+	korina_restart(dev);      
+	
+	spin_unlock(&lp->lock);
+
+}
+
+
+/* Ethernet Rx DMA interrupt */
+static void
+korina_rx_dma_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	struct korina_local *lp;
+	u32 dmas;
+	PROFILE_START;
+//	u32 bla;
+//	printk("korina_rx_dma_interrupt\n");
+
+	ASSERT(dev != NULL);
+	++rx_ints;
+	lp = (struct korina_local *)dev->priv;
+
+	spin_lock(&lp->lock);
+
+  	/* Mask D H E bit in Rx DMA */
+	local_writel(local_readl(&lp->rx_dma_regs->dmasm) | (DMASM_d_m | DMASM_h_m | DMASM_e_m), &lp->rx_dma_regs->dmasm);
+  	dmas = local_readl(&lp->rx_dma_regs->dmas);
+
+//	printk("rx dmas: %p\n", dmas);
+
+//	bla = local_readl(0xb8030030);
+//	printk("errcs: %p\n", bla);
+
+	if(dmas & (DMAS_d_m | DMAS_h_m | DMAS_e_m))
+    	{
+      		tasklet_hi_schedule(lp->rx_tasklet);
+    	}
+
+	spin_unlock(&lp->lock);
+	PROFILE_END;
+}
+
+
+/*
+ * We have a good packet(s), get it/them out of the buffers.
+ *
+ * hkou: this driver works by creating (once) a circular list of receiver
+ *       DMA descriptors that will be used serially by the Korina.
+ *       Because the descriptors are never unlinked from the list _they
+ *       are always live_.  We are counting on Linux (and the chosen number
+ *	 of buffers) to keep ahead of the hardware otherwise the same
+ *	 descriptor might be used for more than one reception.
+ */
+
+static void korina_rx_tasklet(unsigned long rx_data_dev)
+{
+	struct net_device *dev = (struct net_device *)rx_data_dev;	
+	struct korina_local* lp = (struct korina_local *)dev->priv;
+	volatile DMAD_t  rd = &lp->rd_ring[lp->rx_next_done];
+	struct sk_buff *skb, *skb_new;
+//	struct sk_buff 	 *skb_new_1, *skb_new_2;	
+	u8* pkt_buf;
+	u32 devcs, count, pkt_len, pktuncrc_len;
+  	unsigned long 	flags;
+	volatile u32 dmas;
+	unsigned cnt = 0;
+	PROFILE_START;
+
+	if (!last_check) last_check = jiffies;
+	if (jiffies >= last_check + HZ) {
+		last_check += HZ;
+/*
+//		printk("k rx: %u %u (%u), tx: %u %u (%u), %u\n",
+//			   rx_ints, rx_tasks, rx_tasks * 100 / (rx_ints ? rx_ints : 1),
+//			   tx_ints, tx_tasks, tx_tasks * 100 / (tx_ints ? tx_ints : 1));
+
+
+		printk("tx: ");
+		for (i = 0; i < array_idx; ++i) {
+			printk("%u ", array[i]);
+		}
+		printk("\n");
+*/
+
+//		printk("p: %u, t: %u\n", thr_packs,
+//			   thr_time / (thr_packs ? thr_packs : 1));
+
+		rx_ints = 0;
+		tx_ints = 0;
+		rx_tasks = 0;
+		tx_tasks = 0;
+		thr_time = 0;
+		thr_packs = 0;
+
+		array_idx = 0;
+	}
+
+//	printk("korina_rx_tasklet in: %s\n", dev->name);
+	spin_lock_irqsave(&lp->lock, flags);
+	
+	/* keep going while we have received into more descriptors */
+	while (IS_DMA_DONE(rd->control)) 
+	{
+	
+		/* init the var. used for the later operations within the while loop */
+		skb_new = NULL;
+	     	devcs = rd->devcs;
+	     	pkt_len = RCVPKT_LENGTH(devcs);
+		pktuncrc_len = pkt_len -4;			
+      		skb = lp->rx_skb[lp->rx_next_done];
+
+	      	count = KORINA_RBSIZE - (u32)DMA_COUNT(rd->control);
+
+
+	     	if( count != pkt_len) 
+		{	
+	          /*
+	             * Due to a bug in korina processor, the packet length
+	             * given by devcs field and count field sometimes differ.
+	             * If that is the case, report Error.
+	           */				
+//			printk("Packet size mismatch\n");
+	        	lp->stats.rx_errors++;
+			lp->stats.rx_dropped++;				  
+	      	}
+		else if (count < 64) 
+		{
+			lp->stats.rx_errors++;
+			lp->stats.rx_dropped++;			
+		} 
+		else if ((devcs & ( ETHRX_ld_m)) !=	ETHRX_ld_m)
+		{
+			/* check that this is a whole packet */
+			/* WARNING: DMA_FD bit incorrectly set in Korina (errata ref #077) */
+			lp->stats.rx_errors++;
+			lp->stats.rx_dropped++;
+	    	}
+		else if (devcs & ETHRX_rok_m) 
+		{		      		
+		   		/* must be the (first and) last descriptor then */
+	                     pkt_buf = (u8*)lp->rx_skb[lp->rx_next_done]->data;
+				
+		              /* invalidate the cache before copying the buffer */
+	  			dma_cache_inv((unsigned long)pkt_buf, pktuncrc_len);
+
+		             	/* Malloc up new buffer. */					  
+				skb_new = dev_alloc_skb(KORINA_RBSIZE + 2);					             	
+//				skb_new = dev_alloc_skb(KORINA_RBSIZE);					             	
+				
+			    	if (skb_new != NULL)
+				{
+			              /* Make room */
+		  			skb_put(skb, pktuncrc_len);		    
+
+			              skb->protocol = eth_type_trans(skb, dev);
+				
+					/* pass the packet to upper layers */
+			              netif_rx(skb);
+				      ++rx_tasks;
+				      ++cnt;
+
+			              dev->last_rx = jiffies;
+			              lp->stats.rx_packets++;
+	      				lp->stats.rx_bytes += pktuncrc_len;
+
+			              if (IS_RCV_MP(devcs))
+				   		lp->stats.multicast++;
+
+					/* 16 bit align */						  
+		          		skb_reserve(skb_new, 2);	
+
+					skb_new->dev = dev;
+					lp->rx_skb[lp->rx_next_done] = skb_new;
+
+					/* reset descriptor's curr_addr */
+		             		rd->ca = virt_to_phys(skb_new->data); 
+					
+				}
+		   		else
+				{
+					ERR("no memory, dropping rx packet.\n");
+				  	lp->stats.rx_errors++;		
+					lp->stats.rx_dropped++;					
+				}
+                       	
+		}				
+		else 
+		{
+		 	/* This should only happen if we enable accepting broken packets */
+		  	lp->stats.rx_errors++;
+			lp->stats.rx_dropped++;
+
+		   	/* add statistics counters */
+		   	if (IS_RCV_CRC_ERR(devcs)) 
+			{
+		        	DBG(2, "RX CRC error\n");
+		        	lp->stats.rx_crc_errors++;
+		     	} 
+			else if (IS_RCV_LOR_ERR(devcs)) 
+			{
+		          	DBG(2, "RX LOR error\n");
+		          	lp->stats.rx_length_errors++;
+		       }				
+		       else if (IS_RCV_LE_ERR(devcs)) 
+			{
+		       	DBG(2, "RX LE error\n");
+		         	lp->stats.rx_length_errors++;
+		      	}
+		   	else if (IS_RCV_OVR_ERR(devcs)) 
+			{
+		            	/*
+		             	* The overflow errors are handled through
+		              * an interrupt handler.
+		              */
+		              lp->stats.rx_over_errors++;
+		     	}
+			else if (IS_RCV_CV_ERR(devcs)) 
+			{		        
+				/* code violation */
+		            	DBG(2, "RX CV error\n");
+		             	lp->stats.rx_frame_errors++;
+			}
+	          	else if (IS_RCV_CES_ERR(devcs)) 
+			{
+		             	DBG(2, "RX Preamble error\n");
+		 	}
+		}
+		
+		rd->control = DMA_COUNT(KORINA_RBSIZE) |DMAD_cod_m |DMAD_iod_m;
+		lp->rd_ring[(lp->rx_next_done-1)& KORINA_RDS_MASK].control &=  ~(DMAD_cod_m); 	
+		rd->devcs = 0;
+
+		/* restore descriptor's curr_addr */
+	    	if (skb_new == NULL)
+    		{
+	          	rd->ca = virt_to_phys(skb->data);
+    		}					
+			
+		lp->rx_next_done = (lp->rx_next_done + 1) & KORINA_RDS_MASK;
+		rd = &lp->rd_ring[lp->rx_next_done];
+
+		/*
+		 * we'll deal with all possible interrupts up to the last
+		 * used descriptor - so cancel any interrupts that may have
+		 * arrisen while we've been processing.
+		 */
+	}	
+
+	if (array_idx < MY_ARRAY_SIZE) array[array_idx++] = cnt;
+	
+  	dmas = local_readl(&lp->rx_dma_regs->dmas);
+	if(dmas & DMAS_h_m)
+	{
+		korina_start_rx(lp,rd);
+		lp->dma_halt_cnt++;
+	}
+	else if(dmas & DMAS_e_m)
+	{
+		ERR(__FUNCTION__ ": DMAS_e_m\n");
+	    	lp->stats.rx_errors++;	
+	}
+	local_writel( ~dmas, &lp->rx_dma_regs->dmas);
+
+    	/* Enable D E H bit in Rx DMA */
+  	local_writel(local_readl(&lp->rx_dma_regs->dmasm) & ~(DMASM_d_m | DMASM_h_m |DMASM_e_m), &lp->rx_dma_regs->dmasm); 
+   	spin_unlock_irqrestore(&lp->lock, flags);
+	
+//	printk("korina_rx_tasklet out\n");
+	PROFILE_END;
+}
+
+
+/* Ethernet Tx DMA interrupt */
+static void
+korina_tx_dma_interrupt(int irq, void *dev_id, struct pt_regs * regs)
+{
+	struct net_device *dev = (struct net_device *)dev_id;
+	struct korina_local *lp;
+	u32 dmas;
+	PROFILE_START;
+//	printk("korina_tx_dma_interrupt\n");
+	
+	
+	ASSERT(dev != NULL);
+
+	++tx_ints;
+	lp = (struct korina_local *)dev->priv;
+
+	spin_lock(&lp->lock);
+
+	/* Mask F E bit in Tx DMA */
+	local_writel(local_readl(&lp->tx_dma_regs->dmasm) | (DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm);
+	dmas = local_readl(&lp->tx_dma_regs->dmas);
+	if (dmas & (DMAS_f_m | DMAS_e_m))
+	{
+		tasklet_hi_schedule(lp->tx_tasklet);
+
+		if (dmas & DMAS_e_m)
+	  		ERR(__FUNCTION__ ": DMA error\n");
+		
+	}
+	local_writel(~dmas, &lp->tx_dma_regs->dmas); 
+	
+   	if(lp->tx_chain_status == filled && (local_readl(&(lp->tx_dma_regs->dmandptr)) == 0)) 
+	{
+		local_writel(virt_to_phys(&lp->td_ring[lp->tx_chain_head]), &(lp->tx_dma_regs->dmandptr));			
+		lp->tx_chain_status = empty;
+		lp->tx_chain_head = lp->tx_chain_tail;
+	}
+	spin_unlock(&lp->lock);
+	PROFILE_END;
+}
+
+static void korina_tx_tasklet(unsigned long tx_data_dev)
+{
+	struct net_device *dev = (struct net_device *)tx_data_dev;	
+  	struct korina_local* lp = (struct korina_local *)dev->priv;
+  	volatile DMAD_t td = &lp->td_ring[lp->tx_next_done];
+    	u32			devcs;
+    	unsigned long 	flags;
+//	volatile u32 dmas;
+	PROFILE_START;
+
+//	printk("korina_tx_tasklet in\n");
+	spin_lock_irqsave(&lp->lock, flags);
+
+  	/* process all desc that are done */
+  	while(IS_DMA_FINISHED(td->control)) 
+  	{
+
+  		if (	lp->tx_full == 1)
+  		{
+			netif_wake_queue(dev);
+			lp->tx_full = 0;
+		}
+
+		devcs = lp->td_ring[lp->tx_next_done].devcs;    
+		if ((devcs & (ETHTX_fd_m | ETHTX_ld_m)) != (ETHTX_fd_m | ETHTX_ld_m)) 
+		{
+	      		lp->stats.tx_errors++;
+	      		lp->stats.tx_dropped++;				
+
+	      		/* should never happen */
+	      		DBG(1, __FUNCTION__ ": split tx ignored\n");
+	    	} 
+		else if (IS_TX_TOK(devcs)) 
+		{
+	      		/* transmit OK */
+	      		lp->stats.tx_packets++;
+			++tx_tasks;
+			lp->stats.tx_bytes +=
+			    lp->tx_skb[lp->tx_next_done]->len;
+	    	} 
+		else 
+		{
+	      		lp->stats.tx_errors++;
+	      		lp->stats.tx_dropped++;				
+			  
+		      	/* underflow */
+		  	if (IS_TX_UND_ERR(devcs)) 
+				lp->stats.tx_fifo_errors++;
+
+		      	/* oversized frame */
+		      	if (IS_TX_OF_ERR(devcs))
+				lp->stats.tx_aborted_errors++;
+
+		      	/* excessive deferrals */
+		      	if (IS_TX_ED_ERR(devcs))
+				lp->stats.tx_carrier_errors++;
+
+		      	/* collisions: medium busy */
+		      	if (IS_TX_EC_ERR(devcs))
+				lp->stats.collisions++;
+
+		      	/* late collision */
+		      	if (IS_TX_LC_ERR(devcs))
+				lp->stats.tx_window_errors++;
+
+    		}
+
+	    	/* We must always free the original skb */
+	    	if (lp->tx_skb[lp->tx_next_done] != NULL) 
+		{
+	      		dev_kfree_skb_any(lp->tx_skb[lp->tx_next_done]);
+	      		lp->tx_skb[lp->tx_next_done] = NULL;
+	    	}
+
+		lp->td_ring[lp->tx_next_done].control = DMAD_iof_m;
+		lp->td_ring[lp->tx_next_done].devcs = ETHTX_fd_m | ETHTX_ld_m;	
+		lp->td_ring[lp->tx_next_done].link = 0;
+		lp->td_ring[lp->tx_next_done].ca = 0;
+	    	lp->tx_count --;
+
+	    	/* go on to next transmission */
+	    	lp->tx_next_done = (lp->tx_next_done + 1) & KORINA_TDS_MASK;
+	    	td = &lp->td_ring[lp->tx_next_done];
+			
+  	}
+
+
+  	/* Enable F E bit in Tx DMA */
+  	local_writel(local_readl(&lp->tx_dma_regs->dmasm) & ~(DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm); 
+   	spin_unlock_irqrestore(&lp->lock, flags);
+	
+//	printk("korina_tx_tasklet out\n");
+	PROFILE_END;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the device open or closed.
+ */
+static struct net_device_stats *
+korina_get_stats(struct net_device *dev)
+{
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+	return &lp->stats;
+}
+
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+static void
+korina_multicast_list(struct net_device *dev)
+{   
+	/* listen to broadcasts always and to treat 	*/
+	/*       IFF bits independantly	*/
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+	unsigned long flags;
+	u32 recognise = ETHARC_ab_m; 		/* always accept broadcasts */
+
+	if (dev->flags & IFF_PROMISC)         		/* set promiscuous mode */
+		recognise |= ETHARC_pro_m;
+
+	if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 15))
+		recognise |= ETHARC_am_m;    	  	/* all multicast & bcast */
+	
+	else if (dev->mc_count > 0) {
+		DBG(2, __FUNCTION__ ": mc_count %d\n", dev->mc_count);
+		
+		recognise |= ETHARC_am_m;    	  	/* for the time being */
+	}
+	
+	spin_lock_irqsave(&lp->lock, flags);
+	local_writel(recognise, &lp->eth_regs->etharc);
+	spin_unlock_irqrestore(&lp->lock, flags);
+}
+
+
+static void
+korina_tx_timeout(struct net_device *dev)
+{
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+	unsigned long flags;
+//	printk("korina_tx_timeout\n");
+	
+	spin_lock_irqsave(&lp->lock, flags);
+	korina_restart(dev);
+	spin_unlock_irqrestore(&lp->lock, flags);
+
+	
+}
+
+
+/*
+ * Initialize the KORINA ethernet controller.
+ */
+static int korina_init(struct net_device *dev)
+{
+	struct korina_local *lp = (struct korina_local *)dev->priv;
+	int i, j;
+//	printk("korina_init in\n");
+
+	/* Disable DMA */       
+	korina_abort_tx(dev);
+	korina_abort_rx(dev); 
+
+	/* reset ethernet logic */ 
+	local_writel(0, &lp->eth_regs->ethintfc);
+	while((local_readl(&lp->eth_regs->ethintfc) & ETHINTFC_rip_m))
+		dev->trans_start = jiffies;		
+	
+	/* Enable Ethernet Interface */ 
+	local_writel(ETHINTFC_en_m, &lp->eth_regs->ethintfc); 
+
+	tasklet_disable(lp->rx_tasklet);
+	tasklet_disable(lp->tx_tasklet);
+
+	/* Initialize the transmit Descriptors */
+	for (i = 0; i < KORINA_NUM_TDS; i++) {
+    		lp->td_ring[i].control = DMAD_iof_m;
+    		lp->td_ring[i].devcs = ETHTX_fd_m | ETHTX_ld_m;
+    		lp->td_ring[i].ca = 0;
+    		lp->td_ring[i].link = 0;
+    		if (lp->tx_skb[i] != NULL) 
+		{
+      			/* free dangling skb */
+      			dev_kfree_skb_any(lp->tx_skb[i]);
+      			lp->tx_skb[i] = NULL;
+    		}
+	}
+    	lp->tx_next_done = lp->tx_chain_head = lp->tx_chain_tail = 
+    		lp->tx_count = lp->tx_full = 0;
+	
+	lp->	tx_chain_status = empty;
+	
+	/*
+	 * Initialize the receive descriptors so that they
+	 * become a circular linked list, ie. let the last
+	 * descriptor point to the first again.
+	 */
+  	for (i=0; i<KORINA_NUM_RDS; i++) 
+  	{
+		struct sk_buff *skb = lp->rx_skb[i];
+
+		if (lp->rx_skb[i] == NULL) 
+		{
+			skb = dev_alloc_skb(KORINA_RBSIZE + 2);
+//			skb = dev_alloc_skb(KORINA_RBSIZE);
+			if (skb == NULL)
+			{
+		 		ERR("No memory in the system\n");
+				for (j = 0; j < KORINA_NUM_RDS; j ++)
+					if (lp->rx_skb[j] != NULL) 
+						dev_kfree_skb_any(lp->rx_skb[j]);
+				
+				return 1;
+			}
+			else
+			{
+				skb->dev = dev;
+				skb_reserve(skb, 2);
+				/*skb->ip_summed = CHECKSUM_HW;*/
+				lp->rx_skb[i] = skb;
+		             	lp->rd_ring[i].ca = virt_to_phys(skb->data); 
+				
+			}
+		}
+		lp->rd_ring[i].control = DMAD_iod_m | DMA_COUNT(KORINA_RBSIZE);
+		lp->rd_ring[i].devcs = 0;
+                lp->rd_ring[i].ca = virt_to_phys(skb->data);
+		lp->rd_ring[i].link = virt_to_phys(&lp->rd_ring[i+1]);
+
+  	}
+    	/* loop back */
+  	lp->rd_ring[KORINA_NUM_RDS-1].link = virt_to_phys(&lp->rd_ring[0]);
+    	lp->rx_next_done = 0;
+
+  	lp->rd_ring[KORINA_NUM_RDS-1].control |= DMAD_cod_m;
+  	lp->rx_chain_head = 0;
+  	lp->rx_chain_tail = 0;
+  	lp->rx_chain_status = empty;
+/*
+	printk("control: %p, devcs: %p, ca: %p, link: %p\n",
+	       lp->rd_ring[0].control,
+	       lp->rd_ring[0].devcs,
+	       lp->rd_ring[0].ca,
+	       lp->rd_ring[0].link);
+*/
+	local_writel(0, &lp->rx_dma_regs->dmas);
+	/* Start Rx DMA */
+	korina_start_rx(lp, &lp->rd_ring[0]);
+	
+  	/* Enable F E bit in Tx DMA */
+  	local_writel(local_readl(&lp->tx_dma_regs->dmasm) & ~(DMASM_f_m | DMASM_e_m), &lp->tx_dma_regs->dmasm); 
+    	/* Enable D H E bit in Rx DMA */
+  	local_writel(local_readl(&lp->rx_dma_regs->dmasm) & ~(DMASM_d_m | DMASM_h_m | DMASM_e_m), &lp->rx_dma_regs->dmasm); 
+
+
+	/* Accept only packets destined for this Ethernet device address */
+	local_writel(ETHARC_ab_m, &lp->eth_regs->etharc); 
+ 
+	/* Set all Ether station address registers to their initial values */ 
+	local_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal0); 
+	local_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah0);
+	
+	local_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal1); 
+	local_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah1);
+	
+	local_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal2); 
+	local_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah2);
+	
+	local_writel(STATION_ADDRESS_LOW(dev), &lp->eth_regs->ethsal3); 
+	local_writel(STATION_ADDRESS_HIGH(dev), &lp->eth_regs->ethsah3); 
+
+	/* Frame Length Checking, Pad Enable, CRC Enable, Full Duplex set */ 
+	local_writel(ETHMAC2_pe_m | ETHMAC2_cen_m | ETHMAC2_fd_m, &lp->eth_regs->ethmac2);  
+	//ETHMAC2_flc_m		ETHMAC2_fd_m	lp->duplex_mode
+
+	/* Back to back inter-packet-gap */ 
+	local_writel(0x15, &lp->eth_regs->ethipgt); 
+	/* Non - Back to back inter-packet-gap */ 
+	local_writel(0x12, &lp->eth_regs->ethipgr); 
+
+	local_writel(0x8000, &lp->eth_regs->miimcfg);
+	udelay(1000);
+	local_writel(0, &lp->eth_regs->miimcfg);
+
+	/* Management Clock Prescaler Divisor */
+	/* Clock independent setting */
+	local_writel(((idt_cpu_freq)/MII_CLOCK+1) & ~1,
+	       &lp->eth_regs->ethmcp);
+
+	/* don't transmit until fifo contains 48b */
+	local_writel(48, &lp->eth_regs->ethfifott);
+
+	local_writel(ETHMAC1_re_m, &lp->eth_regs->ethmac1);
+
+	tasklet_enable(lp->rx_tasklet);
+	tasklet_enable(lp->tx_tasklet);
+
+	netif_start_queue(dev);
+
+
+//	printk("korina_init out\n");
+	return 0; 
+
+}
+
+
+static void korina_cleanup_module(void)
+{
+	int i;
+
+	for (i = 0; korina_iflist[i].iobase; i++) {
+		struct korina_if_t * bif = &korina_iflist[i];
+		if (bif->dev != NULL) {
+			struct korina_local *lp =
+				(struct korina_local *)bif->dev->priv;
+			if (lp != NULL) {
+				if (lp->eth_regs)
+					iounmap((void*)lp->eth_regs);
+				if (lp->rx_dma_regs)
+					iounmap((void*)lp->rx_dma_regs);
+				if (lp->tx_dma_regs)
+					iounmap((void*)lp->tx_dma_regs);
+				if (lp->td_ring)
+					kfree((void*)KSEG0ADDR(lp->td_ring));
+
+#ifdef KORINA_PROC_DEBUG
+				if (lp->ps)
+					remove_proc_entry("net/rc32434",
+							  NULL);
+#endif
+				kfree(lp);
+			}
+
+			unregister_netdev(bif->dev);
+			kfree(bif->dev);
+			release_region(bif->iobase, 0x24C);
+		}
+	}
+}
+
+
+#ifndef MODULE
+
+static int __init korina_setup(char *options)
+{
+	/* no options yet */
+	return 1;
+}
+
+static int __init korina_setup_ethaddr(char *options)
+{
+	memcpy(korina_mac_address, options, 17);
+	korina_mac_address[17]= '\0';
+	return 1;
+}
+
+
+__setup("rc32434eth=", korina_setup);
+__setup("ethaddr=", korina_setup_ethaddr);
+
+#endif /* !MODULE */
+
+module_init(korina_init_module);
+module_exit(korina_cleanup_module);
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -uNrb linux-2.4.30/drivers/net/korina.h linux/drivers/net/korina.h
--- linux-2.4.30/drivers/net/korina.h	Thu Jan  1 03:00:00 1970
+++ linux/drivers/net/korina.h	Fri Sep 24 13:40:52 2004
@@ -0,0 +1,212 @@
+/*
+ *
+ * BRIEF MODULE DESCRIPTION
+ *      Helpfile for korina.c
+ *
+ * Copyright 2002 MontaVista Software Inc.
+ * Author: MontaVista Software, Inc.
+ *         	stevel@mvista.com or source@mvista.com
+ *
+ * Heavily modified from original version by:
+ *
+ * (C) 2001, IDT 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.
+ *
+ * ########################################################################
+ */
+
+#ifndef KORINA_H
+#define KORINA_H
+
+#include <linux/config.h>
+#include  <asm/rc32434/types.h> 
+#include  <asm/rc32434/dma_v.h>
+#include  <asm/rc32434/eth_v.h>
+
+//#define 	KORINA_DEBUG	100
+//#define 	KORINA_PROC_DEBUG
+#undef	KORINA_DEBUG
+//#undef	KORINA_PROC_DEBUG
+
+#ifdef KORINA_DEBUG
+
+/* use 0 for production, 1 for verification, >2 for debug */
+static int korina_debug = KORINA_DEBUG;
+/*ASSERT*/
+#define ASSERT(expr) \
+	if(!(expr)) {	\
+		printk( "Assertion failed! %s,%s,%s,line=%d\n",	\
+		#expr,__FILE__,__FUNCTION__,__LINE__);		}
+/*DBG*/
+#define DBG(lvl, format, arg...) if (korina_debug > lvl) printk(KERN_INFO "%s: " format, dev->name , ## arg)
+#define ERR(format, arg...) printk(KERN_ERR "%s: " format, dev->name , ## arg)
+#define WARN(format, arg...) printk(KERN_WARNING "%s: " format, dev->name , ## arg)		
+				    
+#else
+
+#define ASSERT(expr) do {} while (0)
+#define DBG(lvl, format, arg...) do {} while (0)
+#define ERR(format, arg...) do {} while (0)
+#define WARN(format, arg...) do {} while (0)
+
+#endif
+
+/* INFO */
+#define INFO(format, arg...) printk(KERN_INFO "%s: " format, dev->name , ## arg)
+
+
+#define ETH_DMA_RX_IRQ   	GROUP1_IRQ_BASE + 0
+#define ETH_DMA_TX_IRQ   	GROUP1_IRQ_BASE + 1 
+#define ETH_RX_OVR_IRQ   	GROUP3_IRQ_BASE + 9
+#define ETH_TX_UND_IRQ   	GROUP3_IRQ_BASE + 10
+
+extern unsigned int idt_cpu_freq;
+
+/* Index to functions, as function prototypes. */
+static int korina_open(struct net_device *dev);
+static int korina_send_packet(struct sk_buff *skb, struct net_device *dev);
+//static void korina_mii_handler(unsigned long data);
+//static void korina_ovr_interrupt(int irq, void *dev_id,
+//			      struct pt_regs * regs);
+static void korina_und_interrupt(int irq, void *dev_id,
+			      struct pt_regs * regs);
+static void korina_rx_dma_interrupt(int irq, void *dev_id,
+				 struct pt_regs * regs);
+static void korina_tx_dma_interrupt(int irq, void *dev_id,
+				 struct pt_regs * regs);
+//static void korina_rx(struct net_device *dev);
+//static void korina_tx(struct net_device *dev);
+static int  korina_close(struct net_device *dev);
+static struct net_device_stats *korina_get_stats(struct net_device *dev);
+static void korina_multicast_list(struct net_device *dev);
+static void korina_tx_timeout(struct net_device *dev);
+static void korina_tx_tasklet(unsigned long tx_data_dev);
+static void korina_rx_tasklet(unsigned long rx_data_dev);
+static void korina_cleanup_module(void);
+static int korina_probe(int port_num);
+static int  korina_init(struct net_device *dev);
+int korina_init_module(void);
+
+
+
+/* cgg - the following must be powers of two */
+#define KORINA_NUM_RDS    128    		/* number of receive descriptors */
+#define KORINA_NUM_TDS    128    		/* number of transmit descriptors */
+
+#define KORINA_RBSIZE     1536  		/* size of one resource buffer = Ether MTU */
+#define KORINA_RDS_MASK   (KORINA_NUM_RDS-1)
+#define KORINA_TDS_MASK   (KORINA_NUM_TDS-1)
+#define RD_RING_SIZE (KORINA_NUM_RDS * sizeof(struct DMAD_s))
+#define TD_RING_SIZE (KORINA_NUM_TDS * sizeof(struct DMAD_s))
+
+#define KORINA_TX_TIMEOUT HZ * 100
+
+#define rc32434_eth_regs ((ETH_t)(ETH_VirtualAddress);
+
+enum status	{ filled,	empty};
+#define IS_DMA_FINISHED(X)   (((X) & (DMAD_f_m)) != 0)
+#define IS_DMA_DONE(X)   (((X) & (DMAD_d_m)) != 0)
+
+
+
+/* Information that need to be kept for each board. */
+struct korina_local {
+	ETH_t  eth_regs;
+	DMA_Chan_t  rx_dma_regs;
+	DMA_Chan_t  tx_dma_regs;
+ 	volatile DMAD_t   td_ring;  					/* transmit descriptor ring */ 
+	volatile DMAD_t   rd_ring;  					/* receive descriptor ring  */
+
+	struct sk_buff* tx_skb[KORINA_NUM_TDS]; 	/* skbuffs for pkt to trans */
+	struct sk_buff* rx_skb[KORINA_NUM_RDS]; 	/* skbuffs for pkt to trans */
+
+	struct tasklet_struct * rx_tasklet;
+	struct tasklet_struct * tx_tasklet;
+
+	int	rx_next_done;
+	int	rx_chain_head;
+	int	rx_chain_tail;
+	enum status	rx_chain_status;
+
+	int	tx_next_done;
+	int	tx_chain_head;
+	int	tx_chain_tail;
+	enum status	tx_chain_status;
+	int	tx_count;	   						/* current # of pkts waiting to be sent */
+	int	tx_full;
+	
+	struct timer_list    mii_phy_timer;
+	unsigned long duplex_mode;
+
+	int    rx_irq;
+	int    tx_irq;
+	int    ovr_irq;
+	int    und_irq;		
+
+	
+	struct net_device_stats stats;
+	spinlock_t lock; 							/* Serialise access to device */
+
+	struct mii_if_info mii_if;
+
+	/* debug /proc entry */
+	struct proc_dir_entry *ps;
+	int dma_halt_cnt;    u32 halt_tx_count;
+	int dma_collide_cnt; u32 collide_tx_count;
+	int dma_run_cnt;     u32 run_tx_count;
+	int dma_race_cnt;    u32 race_tx_count;
+};
+
+
+static inline void rc32434_abort_dma(struct net_device *dev, DMA_Chan_t ch)
+{
+	
+	if (local_readl(&ch->dmac) & DMAC_run_m) 
+	{
+		local_writel(0x10, &ch->dmac); 
+		while (!(local_readl(&ch->dmas) & DMAS_h_m))
+			dev->trans_start = jiffies;			
+		local_writel(0, &ch->dmas); 
+	}
+
+	local_writel(0, &ch->dmadptr); 
+	local_writel(0, &ch->dmandptr); 
+
+}
+
+
+#endif /* KORINA_H */
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -uNrb linux-2.4.30/drivers/net/via-rhine.c linux/drivers/net/via-rhine.c
--- linux-2.4.30/drivers/net/via-rhine.c	Fri Nov 19 12:46:58 2004
+++ linux/drivers/net/via-rhine.c	Thu Apr 28 16:57:23 2005
@@ -171,9 +171,9 @@
    Making the Tx ring too large decreases the effectiveness of channel
    bonding and packet priority.
    There are no ill effects from too-large receive rings. */
-#define TX_RING_SIZE	16
-#define TX_QUEUE_LEN	10		/* Limit ring entries actually used.  */
-#define RX_RING_SIZE	16
+#define TX_RING_SIZE	128
+#define TX_QUEUE_LEN	120		/* Limit ring entries actually used.  */
+#define RX_RING_SIZE	128
 
 
 /* Operational parameters that usually are not changed. */
@@ -257,6 +257,87 @@
 MODULE_PARM_DESC(options, "VIA Rhine: Bits 0-3: media type, bit 17: full duplex");
 MODULE_PARM_DESC(full_duplex, "VIA Rhine full duplex setting(s) (1)");
 
+#ifdef CONFIG_MIKROTIK_RB500
+#undef USE_MEM
+#undef USE_IO
+#define USE_IO
+
+#undef readb
+#undef readw
+#undef readl
+#undef writeb
+#undef writew
+#undef writel
+
+#include <asm/rc32434/pci.h>
+
+volatile PCI_t pci_regs = (PCI_t)PCI_VirtualAddress;
+
+unsigned get_byte(unsigned long addr) {
+	return *(volatile unsigned char *) addr;
+}
+
+unsigned get_word(unsigned long addr) {
+	return *(volatile unsigned short *) addr;
+}
+
+static unsigned xx_read(unsigned long addr, unsigned (*getval)(unsigned long)) {
+	unsigned long flags;
+	unsigned char val;
+	unsigned status;
+	unsigned i;
+	local_irq_save(flags);
+
+	pci_regs->pcidac = 1;
+	for (i = 0; i < 10; ++i) {
+		pci_regs->pcidas = 0;
+		val = getval(addr & ~3);
+
+		while (1) {
+			status = pci_regs->pcidas;
+			if (status & (PCIDAS_d_m | PCIDAS_e_m)) break;
+		}
+		if (status & PCIDAS_e_m) {
+			if (debug > 1)
+				printk("via pci decoupled read error, retrying\n");
+		}
+		else {
+			val = pci_regs->pcidad >> ((addr & 3) * 8);
+			pci_regs->pcidac = 0;
+			local_irq_restore(flags);
+			return val;
+		}
+	}
+	if (debug) printk("via pci decoupled read failed\n");
+	pci_regs->pcidac = 0;
+	local_irq_restore(flags);
+	return 0;
+}
+
+static void xx_writeb(unsigned long addr, unsigned char b) {
+	while (!(pci_regs->pcidas & PCIDAS_ofe_m)) {}
+	*(volatile unsigned char *)(addr) = b;
+}
+
+static void xx_writew(unsigned long addr, unsigned short b) {
+	while (!(pci_regs->pcidas & PCIDAS_ofe_m)) {}
+	*(volatile unsigned short *)(addr) = b;
+}
+
+static void xx_writel(unsigned long addr, unsigned int b) {
+	while (!(pci_regs->pcidas & PCIDAS_ofe_m)) {}
+	*(volatile unsigned int *)(addr) = b;
+}
+
+#define readb(addr) xx_read((unsigned long) (addr) + mips_io_port_base, get_byte)
+#define readw(addr) xx_read((unsigned long) (addr) + mips_io_port_base, get_word)
+
+#define writeb(b,addr) xx_writeb((unsigned long) (addr) + mips_io_port_base, b)
+#define writew(b,addr) xx_writew((unsigned long) (addr) + mips_io_port_base, b)
+#define writel(b,addr) xx_writel((unsigned long) (addr) + mips_io_port_base, b)
+
+#endif
+
 /*
 				Theory of Operation
 
@@ -509,6 +590,9 @@
 	unsigned char *tx_bufs;
 	dma_addr_t tx_bufs_dma;
 
+	struct tasklet_struct *rx_tasklet;
+	struct tasklet_struct *tx_tasklet;
+
 	struct pci_dev *pdev;
 	struct net_device_stats stats;
 	struct timer_list timer;	/* Media monitoring timer. */
@@ -542,7 +626,9 @@
 static int  via_rhine_start_tx(struct sk_buff *skb, struct net_device *dev);
 static irqreturn_t via_rhine_interrupt(int irq, void *dev_instance, struct pt_regs *regs);
 static void via_rhine_tx(struct net_device *dev);
+static void via_rhine_tx_tasklet(unsigned long tx_data_dev);
 static void via_rhine_rx(struct net_device *dev);
+static void via_rhine_rx_tasklet(unsigned long rx_data_dev);
 static void via_rhine_error(struct net_device *dev, int intr_status);
 static void via_rhine_set_rx_mode(struct net_device *dev);
 static struct net_device_stats *via_rhine_get_stats(struct net_device *dev);
@@ -566,7 +652,7 @@
 static void wait_for_reset(struct net_device *dev, int chip_id, char *name)
 {
 	long ioaddr = dev->base_addr;
-	int boguscnt = 20;
+	int boguscnt = 2000;
 
 	IOSYNC;
 
@@ -610,7 +696,7 @@
 	int i;
 	outb(0x20, ioaddr + MACRegEEcsr);
 	/* Typically 2 cycles to reload. */
-	for (i = 0; i < 150; i++)
+	for (i = 0; i < 3000; i++)
 		if (! (inb(ioaddr + MACRegEEcsr) & 0x20))
 			break;
 }
@@ -734,6 +820,7 @@
 	dev->base_addr = ioaddr;
 	wait_for_reset(dev, chip_id, shortname);
 
+#ifndef CONFIG_MIKROTIK_RB500
 	/* Reload the station address from the EEPROM. */
 #ifdef USE_IO
 	reload_eeprom(ioaddr);
@@ -744,6 +831,7 @@
 	   not known if that still works with the "win98-reboot" problem. */
 	enable_mmio(ioaddr0, chip_id);
 #endif
+#endif
 
 	for (i = 0; i < 6; i++)
 		dev->dev_addr[i] = readb(ioaddr + StationAddr + i);
@@ -873,6 +961,13 @@
 		}
 	}
 
+	np->rx_tasklet = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
+	tasklet_init(np->rx_tasklet, via_rhine_rx_tasklet, (unsigned long)dev);
+	tasklet_disable_nosync(np->rx_tasklet);
+	np->tx_tasklet = kmalloc(sizeof(struct tasklet_struct), GFP_KERNEL);
+	tasklet_init(np->tx_tasklet, via_rhine_tx_tasklet, (unsigned long)dev);
+	tasklet_disable_nosync(np->tx_tasklet);
+
 	return 0;
 
 err_out_unmap:
@@ -1167,6 +1262,9 @@
 			   dev->name, readw(ioaddr + ChipCmd),
 			   mdio_read(dev, np->phys[0], MII_BMSR));
 
+	tasklet_enable(np->rx_tasklet);
+	tasklet_enable(np->tx_tasklet);
+
 	netif_start_queue(dev);
 
 	/* Set the timer to check for link beat. */
@@ -1362,7 +1460,6 @@
 	struct net_device *dev = dev_instance;
 	long ioaddr;
 	u32 intr_status;
-	int boguscnt = max_interrupt_work;
 	int handled = 0;
 
 	ioaddr = dev->base_addr;
@@ -1404,13 +1501,11 @@
 				   IntrTxUnderrun | IntrTxDescRace))
 			via_rhine_error(dev, intr_status);
 
-		if (--boguscnt < 0) {
-			printk(KERN_WARNING "%s: Too much work at interrupt, "
-				   "status=%#8.8x.\n",
-				   dev->name, intr_status);
+		if (debug > 3)
+			printk(KERN_DEBUG "%s: exiting interrupt, status=%8.8x.\n",
+				   dev->name, readw(ioaddr + IntrStatus));
 			break;
 		}
-	}
 
 	if (debug > 3)
 		printk(KERN_DEBUG "%s: exiting interrupt, status=%8.8x.\n",
@@ -1423,9 +1518,28 @@
 static void via_rhine_tx(struct net_device *dev)
 {
 	struct netdev_private *np = dev->priv;
+	long ioaddr = dev->base_addr;
+
+	spin_lock(&np->lock);
+
+	/* Disable TX interrupts by setting the interrupt mask */
+	writew(readw(ioaddr + IntrEnable) & ~(IntrTxDone | IntrTxErrSummary),
+		   ioaddr + IntrEnable);
+
+	tasklet_hi_schedule(np->tx_tasklet);
+
+	spin_unlock(&np->lock);
+}
+
+static void via_rhine_tx_tasklet(unsigned long tx_data_dev)
+{
+	struct net_device *dev = (struct net_device *)tx_data_dev;
+	struct netdev_private *np = dev->priv;
+	long ioaddr = dev->base_addr;
+
 	int txstatus = 0, entry = np->dirty_tx % TX_RING_SIZE;
 
-	spin_lock (&np->lock);
+	spin_lock_irq(&np->lock);
 
 	/* find and cleanup dirty tx descriptors */
 	while (np->dirty_tx != np->cur_tx) {
@@ -1469,14 +1583,19 @@
 							 np->tx_skbuff_dma[entry],
 							 np->tx_skbuff[entry]->len, PCI_DMA_TODEVICE);
 		}
-		dev_kfree_skb_irq(np->tx_skbuff[entry]);
+		dev_kfree_skb_any(np->tx_skbuff[entry]);
 		np->tx_skbuff[entry] = NULL;
 		entry = (++np->dirty_tx) % TX_RING_SIZE;
 	}
 	if ((np->cur_tx - np->dirty_tx) < TX_QUEUE_LEN - 4)
 		netif_wake_queue (dev);
 
-	spin_unlock (&np->lock);
+	spin_unlock_irq(&np->lock);
+
+	// Enable TX interrupts by setting the interrupt mask
+	writew(readw(ioaddr + IntrEnable) |
+		   (IntrTxDone | IntrTxErrSummary),
+		   ioaddr + IntrEnable);
 }
 
 /* This routine is logically part of the interrupt handler, but isolated
@@ -1484,6 +1603,28 @@
 static void via_rhine_rx(struct net_device *dev)
 {
 	struct netdev_private *np = dev->priv;
+	long ioaddr = dev->base_addr;
+
+	spin_lock(&np->lock);
+
+	/* Disable RX interrupts by setting the interrupt mask */
+	writew(readw(ioaddr + IntrEnable) &
+		   ~(IntrRxDone | IntrRxErr | IntrRxDropped | IntrRxWakeUp |
+			 IntrRxEmpty | IntrRxNoBuf),
+		   ioaddr + IntrEnable);
+
+	tasklet_hi_schedule(np->rx_tasklet);
+
+	spin_unlock(&np->lock);
+}
+
+static void via_rhine_rx_tasklet(unsigned long rx_data_dev)
+{
+	struct net_device *dev = (struct net_device *)rx_data_dev;
+	struct netdev_private *np = dev->priv;
+	long ioaddr = dev->base_addr;
+	u32 desc_status;
+
 	int entry = np->cur_rx % RX_RING_SIZE;
 	int boguscnt = np->dirty_rx + RX_RING_SIZE - np->cur_rx;
 
@@ -1493,10 +1634,11 @@
 	}
 
 	/* If EOP is set on the next entry, it's a new packet. Send it up. */
-	while ( ! (np->rx_head_desc->rx_status & cpu_to_le32(DescOwn))) {
-		struct rx_desc *desc = np->rx_head_desc;
-		u32 desc_status = le32_to_cpu(desc->rx_status);
-		int data_size = desc_status >> 16;
+	while (1) {
+		int data_size;
+		desc_status = le32_to_cpu(np->rx_head_desc->rx_status);
+		if (desc_status & DescOwn) break;
+		data_size = desc_status >> 16;
 
 		if (debug > 4)
 			printk(KERN_DEBUG "  via_rhine_rx() status is %8.8x.\n",
@@ -1522,9 +1664,9 @@
 				if (desc_status & 0x0004) np->stats.rx_frame_errors++;
 				if (desc_status & 0x0002) {
 					/* this can also be updated outside the interrupt handler */
-					spin_lock (&np->lock);
+					spin_lock_irq(&np->lock);
 					np->stats.rx_crc_errors++;
-					spin_unlock (&np->lock);
+					spin_unlock_irq(&np->lock);
 				}
 			}
 		} else {
@@ -1591,6 +1733,12 @@
 		np->rx_ring[entry].rx_status = cpu_to_le32(DescOwn);
 	}
 
+	// Enable RX interrupts by setting the interrupt mask
+	writew(readw(ioaddr + IntrEnable) |
+		   (IntrRxDone | IntrRxErr | IntrRxDropped | IntrRxWakeUp |
+			IntrRxEmpty | IntrRxNoBuf),
+		   ioaddr + IntrEnable);
+
 	/* Pre-emptively restart Rx engine. */
 	writew(readw(dev->base_addr + ChipCmd) | CmdRxOn | CmdRxDemand,
 		   dev->base_addr + ChipCmd);
@@ -1861,6 +2009,9 @@
 
 	del_timer_sync(&np->timer);
 
+	tasklet_disable(np->rx_tasklet);
+	tasklet_disable(np->tx_tasklet);
+
 	spin_lock_irq(&np->lock);
 
 	netif_stop_queue(dev);
@@ -1892,6 +2043,7 @@
 static void __devexit via_rhine_remove_one (struct pci_dev *pdev)
 {
 	struct net_device *dev = pci_get_drvdata(pdev);
+	struct netdev_private *np = dev->priv;
 
 	unregister_netdev(dev);
 
@@ -1900,6 +2052,9 @@
 #ifdef USE_MEM
 	iounmap((char *)(dev->base_addr));
 #endif
+
+	kfree(np->rx_tasklet);
+	kfree(np->tx_tasklet);
 
 	free_netdev(dev);
 	pci_disable_device(pdev);
diff -uNrb linux-2.4.30/drivers/pci/Makefile linux/drivers/pci/Makefile
--- linux-2.4.30/drivers/pci/Makefile	Fri Nov 19 12:32:15 2004
+++ linux/drivers/pci/Makefile	Fri Sep 24 13:40:52 2004
@@ -29,6 +29,7 @@
 obj-$(CONFIG_SUPERH) += setup-bus.o setup-irq.o
 obj-$(CONFIG_PPC32) += setup-irq.o
 obj-$(CONFIG_SGI_IP27) += setup-irq.o
+obj-$(CONFIG_MIKROTIK_RB500) += setup-irq.o
 
 ifndef CONFIG_X86
 obj-y += syscall.o
diff -uNrb linux-2.4.30/drivers/pci/pci.c linux/drivers/pci/pci.c
--- linux-2.4.30/drivers/pci/pci.c	Fri Nov 19 12:46:58 2004
+++ linux/drivers/pci/pci.c	Wed Jan 19 18:24:34 2005
@@ -1541,6 +1541,11 @@
 
 	/* Go find them, Rover! */
 	for (devfn = 0; devfn < 0x100; devfn += 8) {
+#ifdef CONFIG_MIKROTIK_RB500
+	        // rb500 patch
+	        if (bus->number == 0 && (devfn >> 3) > 21) break;
+#endif
+
 		dev0.devfn = devfn;
 		pci_scan_slot(&dev0);
 	}
diff -uNrb linux-2.4.30/fs/Config.in linux/fs/Config.in
--- linux-2.4.30/fs/Config.in	Fri Nov 19 12:46:58 2004
+++ linux/fs/Config.in	Fri Nov 19 13:44:03 2004
@@ -49,7 +49,20 @@
 dep_tristate 'Journalling Flash File System v2 (JFFS2) support' CONFIG_JFFS2_FS $CONFIG_MTD
 if [ "$CONFIG_JFFS2_FS" = "y" -o "$CONFIG_JFFS2_FS" = "m" ] ; then
    int 'JFFS2 debugging verbosity (0 = quiet, 2 = noisy)' CONFIG_JFFS2_FS_DEBUG 0
+   bool 'JFFS2 support for NAND chips' CONFIG_JFFS2_FS_NAND
+   bool 'JFFS2 ZLIB compression support (recommended)' CONFIG_JFFS2_ZLIB
+   bool 'JFFS2 RTIME compression support (recommended)' CONFIG_JFFS2_RTIME
+   bool 'JFFS2 RUBIN compression support' CONFIG_JFFS2_RUBIN
+   bool 'JFFS2 LZO compression support' CONFIG_JFFS2_LZO
+   bool 'JFFS2 LZARI compression support' CONFIG_JFFS2_LZARI
+   choice 'JFFS2 default compression mode' \
+        "none                                   CONFIG_JFFS2_CMODE_NONE \
+         priority                               CONFIG_JFFS2_CMODE_PRIORITY \
+         size                                   CONFIG_JFFS2_CMODE_SIZE" priority
+
+   bool 'JFFS2 proc interface support' CONFIG_JFFS2_PROC
 fi
+tristate 'Yet Another Flash Filing System (YAFFS) Support' CONFIG_YAFFS_FS $CONFIG_MTD
 tristate 'Compressed ROM file system support' CONFIG_CRAMFS
 bool 'Virtual memory file system support (former shm fs)' CONFIG_TMPFS
 define_bool CONFIG_RAMFS y
diff -uNrb linux-2.4.30/fs/Makefile linux/fs/Makefile
--- linux-2.4.30/fs/Makefile	Fri Nov 19 12:32:15 2004
+++ linux/fs/Makefile	Fri Sep 10 15:15:04 2004
@@ -67,6 +67,7 @@
 subdir-$(CONFIG_BEFS_FS)	+= befs
 subdir-$(CONFIG_JFS_FS)		+= jfs
 subdir-$(CONFIG_XFS_FS)		+= xfs
+subdir-$(CONFIG_YAFFS_FS)	+= yaffs
 
 obj-$(CONFIG_BINFMT_AOUT)	+= binfmt_aout.o
 obj-$(CONFIG_BINFMT_EM86)	+= binfmt_em86.o
diff -uNrb linux-2.4.30/fs/jffs2/Makefile linux/fs/jffs2/Makefile
--- linux-2.4.30/fs/jffs2/Makefile	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/Makefile	Tue Sep 28 14:53:53 2004
@@ -1,25 +1,42 @@
 #
-# Makefile for the linux Journalling Flash FileSystem (JFFS) routines.
+# fs/jffs2/Makefile.24
 #
-# $Id: Makefile,v 1.25.2.1 2002/10/11 09:04:44 dwmw2 Exp $
-#
-# Note! Dependencies are done automagically by 'make dep', which also
-# removes any old dependencies. DON'T put your own dependencies here
-# unless it's something special (ie not a .c file).
-#
-# Note 2! The CFLAGS definitions are now in the main makefile...
+# $Id: Makefile,v 1.43 2003/10/06 12:54:49 dwmw2 Exp $
 
+ifdef OUT_OF_TREE_BUILD
+include $(mtd)/defconfig
 
-COMPR_OBJS	:= compr.o compr_rubin.o compr_rtime.o pushpull.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
+# This must be first in the include path, so it goes in $(CC) rather
+# then $(EXTRA_CFLAGS)
 
-O_TARGET := jffs2.o
+CC += -I$(mtd)/../../include
+EXTRA_CFLAGS := -g -Werror
+
+ifndef CONFIG_MTD
+EXTRA_CFLAGS += -DMTD_OUT_OF_TREE
+endif
+
+ifdef NONAND
+EXTRA_CFLAGS += -DNONAND
+endif
+endif
+
+obj-$(CONFIG_JFFS2_FS) += jffs2.o
 
-obj-y := $(COMPR_OBJS) $(JFFS2_OBJS)
-obj-m := $(O_TARGET)
+JFFS2_OBJS := compr.o dir.o file.o ioctl.o nodelist.o malloc.o
+JFFS2_OBJS += read.o nodemgmt.o readinode.o write.o scan.o gc.o
+JFFS2_OBJS += symlink-v24.o build.o erase.o background.o fs.o writev.o
+
+LINUX_OBJS += super-v24.o crc32.o rbtree.o
+
+NAND_OBJS-$(CONFIG_JFFS2_FS_NAND) += wbuf.o
+
+COMPR_OBJS-$(CONFIG_JFFS2_RUBIN)  += compr_rubin.o
+COMPR_OBJS-$(CONFIG_JFFS2_RTIME)  += compr_rtime.o
+COMPR_OBJS-$(CONFIG_JFFS2_ZLIB)   += compr_zlib.o compr_lzo.o compr_lzari.o
+
+obj-y := $(COMPR_OBJS-y) $(JFFS2_OBJS) $(LINUX_OBJS) $(NAND_OBJS-y)
+O_TARGET := jffs2.o
 
 include $(TOPDIR)/Rules.make
 
diff -uNrb linux-2.4.30/fs/jffs2/background.c linux/fs/jffs2/background.c
--- linux-2.4.30/fs/jffs2/background.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/background.c	Fri Sep 10 15:15:04 2004
@@ -1,61 +1,32 @@
 /*
  * 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.49 2004/07/13 08:56:40 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/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 +57,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,34 +70,37 @@
 {
 	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)) {
+		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();
+		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. 
                  */
@@ -134,9 +108,7 @@
                         siginfo_t info;
                         unsigned long signr;
 
-                        spin_lock_irq(&current->sigmask_lock);
-                        signr = dequeue_signal(&current->blocked, &info);
-                        spin_unlock_irq(&current->sigmask_lock);
+			signr = dequeue_signal_lock(current, &current->blocked, &info);
 
                         switch(signr) {
                         case SIGSTOP:
@@ -147,37 +119,27 @@
 
                         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);
+				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 -uNrb linux-2.4.30/fs/jffs2/build.c linux/fs/jffs2/build.c
--- linux-2.4.30/fs/jffs2/build.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/build.c	Fri Sep 10 15:15:04 2004
@@ -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,181 +121,226 @@
 	   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;
+	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. */
+		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);
 		} 
-	} while(ret == -EAGAIN);
 	
 	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));
+
+	/* Rotate the lists by some number to ensure wear levelling */
+	jffs2_rotate_lists(c);
 
 	return ret;
 }
 	
-int jffs2_build_inode_pass1(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_tmp_dnode_info *tn;
+	struct jffs2_raw_node_ref *raw;
 	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));
+	D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
 			
-			jffs2_free_full_dnode(metadata->fn);
-			jffs2_free_tmp_dnode_info(metadata);
-			metadata = NULL;
+	for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) {
+		D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw)));
+		jffs2_mark_node_obsolete(c, raw);
 		}
 			
-		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);
-			}
-		}
-	}
+	if (ic->scan_dents) {
+		int whinged = 0;
+		D1(printk(KERN_DEBUG "Inode #%u was a directory which may have children...\n", ic->ino));
 		
-	/* OK. Now clear up */
-	if (metadata) {
-		jffs2_free_full_dnode(metadata->fn);
-		jffs2_free_tmp_dnode_info(metadata);
-	}
-	metadata = NULL;
+		while(ic->scan_dents) {
+			struct jffs2_inode_cache *child_ic;
 	
-	while (fraglist) {
-		struct jffs2_node_frag *frag;
-		frag = fraglist;
-		fraglist = fraglist->next;
+			fd = ic->scan_dents;
+			ic->scan_dents = fd->next;
 		
-		if (frag->node && !(--frag->node->frags)) {
-			jffs2_free_full_dnode(frag->node);
+			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;
 		}
-		jffs2_free_node_frag(frag);
+			if (!whinged) {
+				whinged = 1;
+				printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
 	}
 
-	/* 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;
+			D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n",
+				  fd->name, fd->ino));
 
 		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);
+				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;
 		}
 
-		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");
+			/* 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);
 			}
-			/* 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;
+
+	/*
+	   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.
+	*/
 }
 
-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic)
+static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c)
 {
-	struct jffs2_raw_node_ref *raw;
-	struct jffs2_full_dirent *fd;
-	int ret = 0;
+	uint32_t size;
 
-	if(!ic->scan) {
-		D1(printk(KERN_DEBUG "ino #%u was already removed\n", ic->ino));
-		return 0;
-	}
+	/* 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));
+} 
 
-	D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino));
+int jffs2_do_mount_fs(struct jffs2_sb_info *c)
+{
+	int i;
 	
-	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));
-		jffs2_mark_node_obsolete(c, raw);
+	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;
 	}
 
-	if (ic->scan->dents) {
-		printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino);
-	
-		while(ic->scan->dents) {
-			struct jffs2_inode_cache *child_ic;
+	jffs2_calc_trigger_levels(c);
 
-			fd = ic->scan->dents;
-			ic->scan->dents = fd->next;
-
-			D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n",
-				  fd->name, fd->ino));
-			
-			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);
-				continue;
-			}
-			jffs2_free_full_dirent(fd);
-			child_ic->nlink--;
-		}
-		ret = -EAGAIN;
-	}
-	kfree(ic->scan);
-	ic->scan = NULL;
-	//	jffs2_del_ino_cache(c, ic);
-	//	jffs2_free_inode_cache(ic);
-	return ret;
+	return 0;
 }
diff -uNrb linux-2.4.30/fs/jffs2/compr.c linux/fs/jffs2/compr.c
--- linux-2.4.30/fs/jffs2/compr.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/compr.c	Fri Sep 10 15:15:04 2004
@@ -1,151 +1,479 @@
 /*
  * 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.
+ * Copyright (C) 2004 Ferenc Havasi <havasi@inf.u-szeged.hu>,
+ *                    University of Szeged, Hungary
  *
- * 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.
+ * For licensing information, see the file 'LICENCE' in this directory.
  *
- * $Id: compr.c,v 1.17 2001/09/23 09:56:46 dwmw2 Exp $
+ * $Id: compr.c,v 1.42 2004/08/07 21:56:08 dwmw2 Exp $
  *
  */
 
-#include <linux/kernel.h>
-#include <linux/string.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/jffs2.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);
+#include "compr.h"
+
+static spinlock_t jffs2_compressor_list_lock = SPIN_LOCK_UNLOCKED;
+
+/* Available compressors are on this list */
+static LIST_HEAD(jffs2_compressor_list);
+
+/* Actual compression mode */
+static int jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+
+void jffs2_set_compression_mode(int mode) 
+{
+        jffs2_compression_mode = mode;
+}
+
+int jffs2_get_compression_mode(void)
+{
+        return jffs2_compression_mode;
+}
 
+/* Statistics for blocks stored without compression */
+static uint32_t none_stat_compr_blocks=0,none_stat_decompr_blocks=0,none_stat_compr_size=0;
 
 /* jffs2_compress:
  * @data: Pointer to uncompressed data
- * @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
  *	data. On exit, expected to hold the actual size of the compressed
  *	data.
  *
- * Returns: Byte to be stored with data indicating compression type used.
+ * Returns: Lower byte to be stored with data indicating compression type used.
  * Zero is used to show that the data could not be compressed - the 
  * compressed version was actually larger than the original.
+ * Upper byte will be used later. (soon)
  *
  * If the cdata buffer isn't large enough to hold all the uncompressed data,
  * jffs2_compress should compress as much as will fit, and should set 
  * *datalen accordingly to show the amount of data which were compressed.
  */
-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, 
-		    __u32 *datalen, __u32 *cdatalen)
+uint16_t jffs2_compress(struct jffs2_sb_info *c, struct jffs2_inode_info *f,
+			     unsigned char *data_in, unsigned char **cpage_out, 
+			     uint32_t *datalen, uint32_t *cdatalen)
 {
-	int ret;
+	int ret = JFFS2_COMPR_NONE;
+        int compr_ret;
+        struct jffs2_compressor *this, *best=NULL;
+        unsigned char *output_buf = NULL, *tmp_buf;
+        uint32_t orig_slen, orig_dlen;
+        uint32_t best_slen=0, best_dlen=0;
 
-	ret = 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);
-	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);
-	if (!ret) {
-		return JFFS2_COMPR_RUBINMIPS;
-	}
-#endif
-	/* rtime does manage to recompress already-compressed data */
-	ret = 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)
+        switch (jffs2_compression_mode) {
+        case JFFS2_COMPR_MODE_NONE:
+                break;
+        case JFFS2_COMPR_MODE_PRIORITY:
+                output_buf = kmalloc(*cdatalen,GFP_KERNEL);
+                if (!output_buf) {
+                        printk(KERN_WARNING "JFFS2: No memory for compressor allocation. Compression failed.\n");
+                        goto out;
+                }
+                orig_slen = *datalen;
+                orig_dlen = *cdatalen;
+                spin_lock(&jffs2_compressor_list_lock);
+                list_for_each_entry(this, &jffs2_compressor_list, list) {
+                        /* Skip decompress-only backwards-compatibility and disabled modules */
+                        if ((!this->compress)||(this->disabled))
+                                continue;
+
+                        this->usecount++;
+                        spin_unlock(&jffs2_compressor_list_lock);
+                        *datalen  = orig_slen;
+                        *cdatalen = orig_dlen;
+                        compr_ret = this->compress(data_in, output_buf, datalen, cdatalen, NULL);
+                        spin_lock(&jffs2_compressor_list_lock);
+                        this->usecount--;
+                        if (!compr_ret) {
+                                ret = this->compr;
+                                this->stat_compr_blocks++;
+                                this->stat_compr_orig_size += *datalen;
+                                this->stat_compr_new_size  += *cdatalen;
+                                break;
+                        }
+                }
+                spin_unlock(&jffs2_compressor_list_lock);
+                if (ret == JFFS2_COMPR_NONE) kfree(output_buf);
+                break;
+        case JFFS2_COMPR_MODE_SIZE:
+                orig_slen = *datalen;
+                orig_dlen = *cdatalen;
+                spin_lock(&jffs2_compressor_list_lock);
+                list_for_each_entry(this, &jffs2_compressor_list, list) {
+                        /* Skip decompress-only backwards-compatibility and disabled modules */
+                        if ((!this->compress)||(this->disabled))
+                                continue;
+                        /* Allocating memory for output buffer if necessary */
+                        if ((this->compr_buf_size<orig_dlen)&&(this->compr_buf)) {
+                                spin_unlock(&jffs2_compressor_list_lock);
+                                kfree(this->compr_buf);
+                                spin_lock(&jffs2_compressor_list_lock);
+                                this->compr_buf_size=0;
+                                this->compr_buf=NULL;
+                        }
+                        if (!this->compr_buf) {
+                                spin_unlock(&jffs2_compressor_list_lock);
+                                tmp_buf = kmalloc(orig_dlen,GFP_KERNEL);
+                                spin_lock(&jffs2_compressor_list_lock);
+                                if (!tmp_buf) {
+                                        printk(KERN_WARNING "JFFS2: No memory for compressor allocation. (%d bytes)\n",orig_dlen);
+                                        continue;
+                                }
+                                else {
+                                        this->compr_buf = tmp_buf;
+                                        this->compr_buf_size = orig_dlen;
+                                }
+                        }
+                        this->usecount++;
+                        spin_unlock(&jffs2_compressor_list_lock);
+                        *datalen  = orig_slen;
+                        *cdatalen = orig_dlen;
+                        compr_ret = this->compress(data_in, this->compr_buf, datalen, cdatalen, NULL);
+                        spin_lock(&jffs2_compressor_list_lock);
+                        this->usecount--;
+                        if (!compr_ret) {
+                                if ((!best_dlen)||(best_dlen>*cdatalen)) {
+                                        best_dlen = *cdatalen;
+                                        best_slen = *datalen;
+                                        best = this;
+                                }
+                        }
+                }
+                if (best_dlen) {
+                        *cdatalen = best_dlen;
+                        *datalen  = best_slen;
+                        output_buf = best->compr_buf;
+                        best->compr_buf = NULL;
+                        best->compr_buf_size = 0;
+                        best->stat_compr_blocks++;
+                        best->stat_compr_orig_size += best_slen;
+                        best->stat_compr_new_size  += best_dlen;
+                        ret = best->compr;
+                }
+                spin_unlock(&jffs2_compressor_list_lock);
+                break;
+        default:
+                printk(KERN_ERR "JFFS2: unknow compression mode.\n");
+        }
+ out:
+        if (ret == JFFS2_COMPR_NONE) {
+	        *cpage_out = data_in;
 		*datalen = *cdatalen;
-#endif		
-	return JFFS2_COMPR_NONE; /* We failed to compress */
-
+                none_stat_compr_blocks++;
+                none_stat_compr_size += *datalen;
+        }
+        else {
+                *cpage_out = output_buf;
+        }
+	return ret;
 }
 
-
-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,
+		     uint16_t comprtype, unsigned char *cdata_in, 
+		     unsigned char *data_out, uint32_t cdatalen, uint32_t datalen)
 {
-	switch (comprtype) {
+        struct jffs2_compressor *this;
+        int ret;
+
+	/* Older code had a bug where it would write non-zero 'usercompr'
+	   fields. Deal with it. */
+	if ((comprtype & 0xff) <= JFFS2_COMPR_ZLIB)
+		comprtype &= 0xff;
+
+	switch (comprtype & 0xff) {
 	case JFFS2_COMPR_NONE:
 		/* This should be special-cased elsewhere, but we might as well deal with it */
 		memcpy(data_out, cdata_in, datalen);
+                none_stat_decompr_blocks++;
 		break;
-
 	case JFFS2_COMPR_ZERO:
 		memset(data_out, 0, datalen);
 		break;
+	default:
+                spin_lock(&jffs2_compressor_list_lock);
+                list_for_each_entry(this, &jffs2_compressor_list, list) {
+                        if (comprtype == this->compr) {
+                                this->usecount++;
+                                spin_unlock(&jffs2_compressor_list_lock);
+                                ret = this->decompress(cdata_in, data_out, cdatalen, datalen, NULL);
+                                spin_lock(&jffs2_compressor_list_lock);
+                                if (ret) {
+                                        printk(KERN_WARNING "Decompressor \"%s\" returned %d\n", this->name, ret);
+                                }
+                                else {
+                                        this->stat_decompr_blocks++;
+                                }
+                                this->usecount--;
+                                spin_unlock(&jffs2_compressor_list_lock);
+                                return ret;
+                        }
+                }
+		printk(KERN_WARNING "JFFS2 compression type 0x%02x not available.\n", comprtype);
+                spin_unlock(&jffs2_compressor_list_lock);
+		return -EIO;
+	}
+	return 0;
+}
 
-	case JFFS2_COMPR_ZLIB:
-		zlib_decompress(cdata_in, data_out, cdatalen, datalen);
-		break;
+int jffs2_register_compressor(struct jffs2_compressor *comp)
+{
+        struct jffs2_compressor *this;
 
-	case JFFS2_COMPR_RTIME:
-		rtime_decompress(cdata_in, data_out, cdatalen, datalen);
-		break;
+        if (!comp->name) {
+                printk(KERN_WARNING "NULL compressor name at registering JFFS2 compressor. Failed.\n");
+                return -1;
+        }
+        comp->compr_buf_size=0;
+        comp->compr_buf=NULL;
+        comp->usecount=0;
+        comp->stat_compr_orig_size=0;
+        comp->stat_compr_new_size=0;
+        comp->stat_compr_blocks=0;
+        comp->stat_decompr_blocks=0;
+        D1(printk(KERN_DEBUG "Registering JFFS2 compressor \"%s\"\n", comp->name));
+
+        spin_lock(&jffs2_compressor_list_lock);
+
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (this->priority < comp->priority) {
+                        list_add(&comp->list, this->list.prev);
+                        goto out;
+                }
+        }
+        list_add_tail(&comp->list, &jffs2_compressor_list);
+out:
+        D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+                printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
+        })
+
+        spin_unlock(&jffs2_compressor_list_lock);
+
+        return 0;
+}
+
+int jffs2_unregister_compressor(struct jffs2_compressor *comp)
+{
+        D2(struct jffs2_compressor *this;)
+
+        D1(printk(KERN_DEBUG "Unregistering JFFS2 compressor \"%s\"\n", comp->name));
+
+        spin_lock(&jffs2_compressor_list_lock);
+
+        if (comp->usecount) {
+                spin_unlock(&jffs2_compressor_list_lock);
+                printk(KERN_WARNING "JFFS2: Compressor modul is in use. Unregister failed.\n");
+                return -1;
+        }
+        list_del(&comp->list);
+
+        D2(list_for_each_entry(this, &jffs2_compressor_list, list) {
+                printk(KERN_DEBUG "Compressor \"%s\", prio %d\n", this->name, this->priority);
+        })
+        spin_unlock(&jffs2_compressor_list_lock);
+        return 0;
+}
+
+#ifdef CONFIG_JFFS2_PROC
+
+#define JFFS2_STAT_BUF_SIZE 16000
+
+char *jffs2_list_compressors(void)
+{
+        struct jffs2_compressor *this;
+        char *buf, *act_buf;
+
+        act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                act_buf += sprintf(act_buf, "%10s priority:%d ", this->name, this->priority);
+                if ((this->disabled)||(!this->compress))
+                        act_buf += sprintf(act_buf,"disabled");
+                else
+                        act_buf += sprintf(act_buf,"enabled");
+                act_buf += sprintf(act_buf,"\n");
+        }
+        return buf;
+}
+
+char *jffs2_stats(void)
+{
+        struct jffs2_compressor *this;
+        char *buf, *act_buf;
+
+        act_buf = buf = kmalloc(JFFS2_STAT_BUF_SIZE,GFP_KERNEL);
+
+        act_buf += sprintf(act_buf,"JFFS2 compressor statistics:\n");
+        act_buf += sprintf(act_buf,"%10s   ","none");
+        act_buf += sprintf(act_buf,"compr: %d blocks (%d)  decompr: %d blocks\n", none_stat_compr_blocks, 
+                           none_stat_compr_size, none_stat_decompr_blocks);
+        spin_lock(&jffs2_compressor_list_lock);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                act_buf += sprintf(act_buf,"%10s ",this->name);
+                if ((this->disabled)||(!this->compress))
+                        act_buf += sprintf(act_buf,"- ");
+                else
+                        act_buf += sprintf(act_buf,"+ ");
+                act_buf += sprintf(act_buf,"compr: %d blocks (%d/%d)  decompr: %d blocks ", this->stat_compr_blocks, 
+                                   this->stat_compr_new_size, this->stat_compr_orig_size, 
+                                   this->stat_decompr_blocks);
+                act_buf += sprintf(act_buf,"\n");
+        }
+        spin_unlock(&jffs2_compressor_list_lock);
+
+        return buf;
+}
+
+char *jffs2_get_compression_mode_name(void) 
+{
+        switch (jffs2_compression_mode) {
+        case JFFS2_COMPR_MODE_NONE:
+                return "none";
+        case JFFS2_COMPR_MODE_PRIORITY:
+                return "priority";
+        case JFFS2_COMPR_MODE_SIZE:
+                return "size";
+        }
+        return "unkown";
+}
+
+int jffs2_set_compression_mode_name(const char *name) 
+{
+        if (!strcmp("none",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+                return 0;
+        }
+        if (!strcmp("priority",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_PRIORITY;
+                return 0;
+        }
+        if (!strcmp("size",name)) {
+                jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+                return 0;
+        }
+        return 1;
+}
+
+static int jffs2_compressor_Xable(const char *name, int disabled)
+{
+        struct jffs2_compressor *this;
+        spin_lock(&jffs2_compressor_list_lock);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (!strcmp(this->name, name)) {
+                        this->disabled = disabled;
+                        spin_unlock(&jffs2_compressor_list_lock);
+                        return 0;                        
+                }
+        }
+        spin_unlock(&jffs2_compressor_list_lock);
+        printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);
+        return 1;
+}
+
+int jffs2_enable_compressor_name(const char *name)
+{
+        return jffs2_compressor_Xable(name, 0);
+}
+
+int jffs2_disable_compressor_name(const char *name)
+{
+        return jffs2_compressor_Xable(name, 1);
+}
+
+int jffs2_set_compressor_priority(const char *name, int priority)
+{
+        struct jffs2_compressor *this,*comp;
+        spin_lock(&jffs2_compressor_list_lock);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (!strcmp(this->name, name)) {
+                        this->priority = priority;
+                        comp = this;
+                        goto reinsert;
+                }
+        }
+        spin_unlock(&jffs2_compressor_list_lock);
+        printk(KERN_WARNING "JFFS2: compressor %s not found.\n",name);        
+        return 1;
+reinsert:
+        /* list is sorted in the order of priority, so if
+           we change it we have to reinsert it into the
+           good place */
+        list_del(&comp->list);
+        list_for_each_entry(this, &jffs2_compressor_list, list) {
+                if (this->priority < comp->priority) {
+                        list_add(&comp->list, this->list.prev);
+                        spin_unlock(&jffs2_compressor_list_lock);
+                        return 0;
+                }
+        }
+        list_add_tail(&comp->list, &jffs2_compressor_list);
+        spin_unlock(&jffs2_compressor_list_lock);
+        return 0;
+}
 
-	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
-		break;
-	case JFFS2_COMPR_DYNRUBIN:
-#if 1 /* Phase this one out */
-		dynrubin_decompress(cdata_in, data_out, cdatalen, datalen);
+
+void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig)
+{
+        if (orig != comprbuf)
+                kfree(comprbuf);
+}
+
+int jffs2_compressors_init(void) 
+{
+/* Registering compressors */
+#ifdef CONFIG_JFFS2_ZLIB
+        jffs2_zlib_init();
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+        jffs2_rtime_init();
+#endif
+#ifdef CONFIG_JFFS2_RUBIN
+        jffs2_rubinmips_init();
+        jffs2_dynrubin_init();
+#endif
+#ifdef CONFIG_JFFS2_LZARI
+        jffs2_lzari_init();
+#endif
+#ifdef CONFIG_JFFS2_LZO
+        jffs2_lzo_init();
+#endif
+/* Setting default compression mode */
+#ifdef CONFIG_JFFS2_CMODE_NONE
+        jffs2_compression_mode = JFFS2_COMPR_MODE_NONE;
+        D1(printk(KERN_INFO "JFFS2: default compression mode: none\n");)
 #else
-		printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n");
+#ifdef CONFIG_JFFS2_CMODE_SIZE
+        jffs2_compression_mode = JFFS2_COMPR_MODE_SIZE;
+        D1(printk(KERN_INFO "JFFS2: default compression mode: size\n");)
+#else
+        D1(printk(KERN_INFO "JFFS2: default compression mode: priority\n");)
 #endif
-		break;
+#endif
+        return 0;
+}
 
-	default:
-		printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype);
-		return -EIO;
-	}
+int jffs2_compressors_exit(void) 
+{
+/* Unregistering compressors */
+#ifdef CONFIG_JFFS2_LZO
+        jffs2_lzo_exit();
+#endif
+#ifdef CONFIG_JFFS2_LZARI
+        jffs2_lzari_exit();
+#endif
+#ifdef CONFIG_JFFS2_RUBIN
+        jffs2_dynrubin_exit();
+        jffs2_rubinmips_exit();
+#endif
+#ifdef CONFIG_JFFS2_RTIME
+        jffs2_rtime_exit();
+#endif
+#ifdef CONFIG_JFFS2_ZLIB
+        jffs2_zlib_exit();
+#endif
 	return 0;
 }
diff -uNrb linux-2.4.30/fs/jffs2/compr_rtime.c linux/fs/jffs2/compr_rtime.c
--- linux-2.4.30/fs/jffs2/compr_rtime.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/compr_rtime.c	Fri Sep 10 15:15:04 2004
@@ -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.14 2004/06/23 16:34:40 havasi 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
  *
@@ -49,12 +25,14 @@
 #include <linux/types.h>
 #include <linux/errno.h>
 #include <linux/string.h> 
+#include <linux/jffs2.h> 
+#include "compr.h"
 
 /* _compress returns the compressed size, -1 if bigger */
-int 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, void *model)
 {
-	int positions[256];
+	short positions[256];
 	int outpos = 0;
 	int pos=0;
 
@@ -91,10 +69,10 @@
 }		   
 
 
-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
-		      __u32 srclen, __u32 destlen)
+int jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out,
+		      uint32_t srclen, uint32_t destlen, void *model)
 {
-	int positions[256];
+	short positions[256];
 	int outpos = 0;
 	int pos=0;
 	
@@ -123,6 +101,28 @@
 			}
 		}
 	}		
+        return 0;
 }		   
 
+static struct jffs2_compressor jffs2_rtime_comp = {
+    .priority = JFFS2_RTIME_PRIORITY,
+    .name = "rtime",
+    .compr = JFFS2_COMPR_RTIME,
+    .compress = &jffs2_rtime_compress,
+    .decompress = &jffs2_rtime_decompress,
+#ifdef JFFS2_RTIME_DISABLED
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+};
 
+int jffs2_rtime_init(void)
+{
+    return jffs2_register_compressor(&jffs2_rtime_comp);
+}
+
+void jffs2_rtime_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_rtime_comp);
+}
diff -uNrb linux-2.4.30/fs/jffs2/compr_rubin.c linux/fs/jffs2/compr_rubin.c
--- linux-2.4.30/fs/jffs2/compr_rubin.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/compr_rubin.c	Fri Sep 10 15:15:04 2004
@@ -1,49 +1,25 @@
 /*
  * 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.20 2004/06/23 16:34:40 havasi Exp $
  *
  */
 
  
 #include <linux/string.h>
 #include <linux/types.h>
+#include <linux/jffs2.h>
 #include "compr_rubin.h"
 #include "histo_mips.h"
+#include "compr.h"
 
-
-
-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, void *model)
 {
 	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, void *model)
 {
 	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,15 @@
 }		   
 
 
-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, 
-		   __u32 sourcelen, __u32 dstlen)
+int jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, 
+		   uint32_t sourcelen, uint32_t dstlen, void *model)
 {
 	rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen);
+        return 0;
 }
 
-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, 
-		   __u32 sourcelen, __u32 dstlen)
+int jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, 
+		   uint32_t sourcelen, uint32_t dstlen, void *model)
 {
 	int bits[8];
 	int c;
@@ -346,4 +323,51 @@
 		bits[c] = data_in[c];
 
 	rubin_do_decompress(256, bits, data_in+8, cpage_out, sourcelen-8, dstlen);
+        return 0;
+}
+
+static struct jffs2_compressor jffs2_rubinmips_comp = {
+    .priority = JFFS2_RUBINMIPS_PRIORITY,
+    .name = "rubinmips",
+    .compr = JFFS2_COMPR_DYNRUBIN,
+    .compress = NULL, /*&jffs2_rubinmips_compress,*/
+    .decompress = &jffs2_rubinmips_decompress,
+#ifdef JFFS2_RUBINMIPS_DISABLED
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+};
+
+int jffs2_rubinmips_init(void)
+{
+    return jffs2_register_compressor(&jffs2_rubinmips_comp);
+}
+
+void jffs2_rubinmips_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_rubinmips_comp);
+}
+
+static struct jffs2_compressor jffs2_dynrubin_comp = {
+    .priority = JFFS2_DYNRUBIN_PRIORITY,
+    .name = "dynrubin",
+    .compr = JFFS2_COMPR_RUBINMIPS,
+    .compress = jffs2_dynrubin_compress,
+    .decompress = &jffs2_dynrubin_decompress,
+#ifdef JFFS2_DYNRUBIN_DISABLED
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+};
+
+int jffs2_dynrubin_init(void)
+{
+    return jffs2_register_compressor(&jffs2_dynrubin_comp);
+}
+
+void jffs2_dynrubin_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_dynrubin_comp);
 }
diff -uNrb linux-2.4.30/fs/jffs2/compr_rubin.h linux/fs/jffs2/compr_rubin.h
--- linux-2.4.30/fs/jffs2/compr_rubin.h	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/compr_rubin.h	Fri Sep 10 15:15:04 2004
@@ -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 -uNrb linux-2.4.30/fs/jffs2/compr_zlib.c linux/fs/jffs2/compr_zlib.c
--- linux-2.4.30/fs/jffs2/compr_zlib.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/compr_zlib.c	Fri Sep 10 15:15:04 2004
@@ -1,51 +1,28 @@
 /*
  * 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.28 2004/06/23 16:34:40 havasi 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"
+#include "compr.h"
 
 	/* Plan: call deflate() with avail_in == *sourcelen, 
 		avail_out = *dstlen - 12 and flush == Z_FINISH. 
@@ -58,120 +35,184 @@
 
 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;
 
-int __init jffs2_zlib_init(void)
+#ifdef __KERNEL__ /* Linux-only */
+#include <linux/vmalloc.h>
+#include <linux/init.h>
+
+static int __init alloc_workspaces(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()));
 	return 0;
 }
 
-void jffs2_zlib_exit(void)
+static void free_workspaces(void)
 {
-	vfree(deflate_workspace);
-	vfree(inflate_workspace);
+	vfree(def_strm.workspace);
+	vfree(inf_strm.workspace);
 }
+#else
+#define alloc_workspaces() (0)
+#define free_workspaces() do { } while(0)
+#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, void *model)
 {
-	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)
+int jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out,
+		      uint32_t srclen, uint32_t destlen, void *model)
 {
-	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;
+		return 1;
 	}
-	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);
+        return 0;
+}
+
+static struct jffs2_compressor jffs2_zlib_comp = {
+    .priority = JFFS2_ZLIB_PRIORITY,
+    .name = "zlib",
+    .compr = JFFS2_COMPR_ZLIB,
+    .compress = &jffs2_zlib_compress,
+    .decompress = &jffs2_zlib_decompress,
+#ifdef JFFS2_ZLIB_DISABLED
+    .disabled = 1,
+#else
+    .disabled = 0,
+#endif
+};
+
+int __init jffs2_zlib_init(void)
+{
+    int ret;
+
+    ret = alloc_workspaces();
+    if (ret)
+        return ret;
+
+    ret = jffs2_register_compressor(&jffs2_zlib_comp);
+    if (ret)
+        free_workspaces();
+
+    return ret;
+}
+
+void jffs2_zlib_exit(void)
+{
+    jffs2_unregister_compressor(&jffs2_zlib_comp);
+    free_workspaces();
 }
diff -uNrb linux-2.4.30/fs/jffs2/comprtest.c linux/fs/jffs2/comprtest.c
--- linux-2.4.30/fs/jffs2/comprtest.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/comprtest.c	Fri Sep 10 15:15:04 2004
@@ -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 -uNrb linux-2.4.30/fs/jffs2/dir.c linux/fs/jffs2/dir.c
--- linux-2.4.30/fs/jffs2/dir.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/dir.c	Fri Sep 10 15:15:04 2004
@@ -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);
+	dir_f = JFFS2_INODE_INFO(dir_i);
 
-	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);
+	ret = jffs2_do_create(c, dir_f, f, ri, 
+			      dentry->d_name.name, dentry->d_name.len);
 		
 		if (ret) {
-			/* Eep. */
-			D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n"));
 			jffs2_clear_inode(inode);
+		make_bad_inode(inode);
+		iput(inode);
+		jffs2_free_raw_inode(ri);
 			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);
-
-	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);
-		jffs2_clear_inode(inode);
-		return PTR_ERR(fd);
-	}
-
-	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);
+	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;
-	}
-	
-	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;
 }
+/***********************************************************************/
+
 
 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,13 +360,6 @@
 	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) {
@@ -595,7 +367,6 @@
 			jffs2_clear_inode(inode);
 			return ret;
 		}
-	}
 
 	rd = jffs2_alloc_raw_dirent();
 	if (!rd) {
@@ -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(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);
 	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,13 +488,6 @@
 	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) {
@@ -729,7 +495,6 @@
 			jffs2_clear_inode(inode);
 			return ret;
 		}
-	}
 	
 	rd = jffs2_alloc_raw_dirent();
 	if (!rd) {
@@ -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,13 +642,6 @@
 	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) {
@@ -883,7 +649,6 @@
 			jffs2_clear_inode(inode);
 			return ret;
 		}
-	}
 
 	rd = jffs2_alloc_raw_dirent();
 	if (!rd) {
@@ -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);
-
-	fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen);
+	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));
 	
-	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;
+	}
+
+	if (S_ISDIR(old_dentry->d_inode->i_mode))
+		old_dir_i->i_nlink--;
+
+	return 0;
 }
 
diff -uNrb linux-2.4.30/fs/jffs2/erase.c linux/fs/jffs2/erase.c
--- linux-2.4.30/fs/jffs2/erase.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/erase.c	Fri Sep 10 15:15:04 2004
@@ -1,68 +1,61 @@
 /*
  * 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.60 2004/06/30 17:26:15 dbrown 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, uint32_t bad_offset);
+static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
+static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb);
 
 void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb)
 {
-	struct erase_info *instr;
 	int ret;
+	uint32_t bad_offset;
+#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;
 	}
 
@@ -73,23 +66,29 @@
 	instr->len = c->sector_size;
 	instr->callback = jffs2_erase_callback;
 	instr->priv = (unsigned long)(&instr[1]);
+	instr->fail_addr = 0xffffffff;
 	
 	((struct erase_priv_struct *)instr->priv)->jeb = jeb;
 	((struct erase_priv_struct *)instr->priv)->c = c;
 
 	ret = c->mtd->erase(c->mtd, instr);
-	if (!ret) {
+	if (!ret)
 		return;
-	}
+
+	bad_offset = instr->fail_addr;
+	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 +96,119 @@
 		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, bad_offset);
 }
 
-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->used_size = jeb->dirty_size = jeb->free_size = 0;
+			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_bh(&c->erase_completion_lock);
+			spin_unlock(&c->erase_completion_lock);
 		
 		jffs2_erase_block(c, jeb);
+
+		} else {
+			BUG();
+		}
+
 		/* 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, uint32_t bad_offset)
+{
+	/* For NAND, if the failure did not occur at the device level for a
+	   specific physical page, don't bother updating the bad block table. */
+	if (jffs2_cleanmarker_oob(c) && (bad_offset != 0xffffffff)) {
+		/* We had a device-level failure to erase.  Let's see if we've
+		   failed too many times. */
+		if (!jffs2_write_nand_badblock(c, jeb, bad_offset)) {
+			/* We'd like to give this block another try. */
+			spin_lock(&c->erase_completion_lock);
+			list_del(&jeb->list);
+			list_add(&jeb->list, &c->erase_pending_list);
+			c->erasing_size -= c->sector_size;
+			c->dirty_size += c->sector_size;
+			jeb->dirty_size = c->sector_size;
+			spin_unlock(&c->erase_completion_lock);
+			return;
+		}
+	}
+
+	spin_lock(&c->erase_completion_lock);
+	c->erasing_size -= c->sector_size;
+	c->bad_size += c->sector_size;
+	list_del(&jeb->list);
+	list_add(&jeb->list, &c->bad_list);
+	c->nr_erasing_blocks--;
+	spin_unlock(&c->erase_completion_lock);
+	wake_up(&c->erase_wait);
+}	 
+
+#ifndef __ECOS
 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, instr->fail_addr);
 	} 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 +265,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 +300,138 @@
 	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;
+	uint32_t bad_offset;
 
-	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 {
-			__u32 ofs = jeb->offset;
+		uint32_t 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);
+			uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs);
 				int i;
 
-				ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf);
-				if (ret < 0) {
+			bad_offset = ofs;
+
+			ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf);
+			if (ret) {
 					printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret);
 					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);
+				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_offset += i;
+					printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, bad_offset);
 					bad: 
+					if (!jffs2_cleanmarker_oob(c))
 						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);
+					spin_lock(&c->erase_completion_lock);
+					/* Stick it on a list (any list) so
+					   erase_failed can take it right off
+					   again.  Silly, but shouldn't happen
+					   often. */
+					list_add(&jeb->list, &c->erasing_list);
+					spin_unlock(&c->erase_completion_lock);
+					jffs2_erase_failed(c, jeb, bad_offset);
 						return;
 					}
 				}
 				ofs += readlen;
+			cond_resched();
 			}
 			kfree(ebuf);
 		}
 					
+	bad_offset = jeb->offset;
+
 		/* Write the erase complete marker */	
 		D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset));
-		ret = c->mtd->write(c->mtd, jeb->offset, sizeof(marker), &retlen, (char *)&marker);
+	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);
+	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);
+	D1(ACCT_PARANOIA_CHECK(jeb));
 
 		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);
-	}
-	spin_unlock_bh(&c->erase_completion_lock);
 }
+
diff -uNrb linux-2.4.30/fs/jffs2/file.c linux/fs/jffs2/file.c
--- linux-2.4.30/fs/jffs2/file.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/file.c	Fri Sep 10 15:15:04 2004
@@ -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 */
+	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 */
+	ret = jffs2_read_inode_range(c, f, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
 
-			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"));
-		}
-	}
-	D2(printk(KERN_DEBUG "readpage finishing\n"));
+	} else {
 	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(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL);
 		
-		fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL);
-		jffs2_complete_reservation(c);
 		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)
+
+	if (!ri) {
+		D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n"));
 		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;
+	/* 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);
 
-		D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, file_ofs));
+	kunmap(pg);
 
-		ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL);
 		if (ret) {
+		/* There was an error writing. */
 			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);
-
-		fn = jffs2_write_dnode(inode, ri, comprbuf, cdatalen, phys_ofs, NULL);
-
-		jffs2_complete_reservation(c);
 
-		if (comprtype != JFFS2_COMPR_NONE)
-			kfree(comprbuf);
+	/* 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;
-		}
-		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;
+	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 = ri->ctime;
-		if (!datalen) {
-			printk(KERN_WARNING "Eep. We didn't actually write any bloody data\n");
-			ret = -EIO;
-			SetPageError(pg);
-			break;
+			
+			inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime));
 		}
-		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 -uNrb linux-2.4.30/fs/jffs2/gc.c linux/fs/jffs2/gc.c
--- linux-2.4.30/fs/jffs2/gc.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/gc.c	Fri Sep 10 15:15:04 2004
@@ -1,76 +1,68 @@
 /*
  * 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.137 2004/07/20 13:44:55 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"
+#include "compr.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 +72,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 */
+		D1(printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"));
 		return NULL;
 	}
 
@@ -94,6 +93,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 +113,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;
@@ -126,13 +205,15 @@
 		jeb = jffs2_find_gc_block(c);
 
 	if (!jeb) {
-		printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n");
-		spin_unlock_bh(&c->erase_completion_lock);
+		D1 (printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"));
+		spin_unlock(&c->erase_completion_lock);
 		up(&c->alloc_sem);
 		return -EIO;
 	}
 
-	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 +222,215 @@
 
 	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);
 
-	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.
+	/* 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.
 		*/
-		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) {
+		printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n",
+		       ic->ino, ic->state);
 			up(&c->alloc_sem);
-			return -ENOMEM;
+		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 */
+	}
+
+	/* 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)) {
+		ret = PTR_ERR(f);
+		goto release_sem;
 		}
+	if (!f) {
+		ret = 0;
+		goto release_sem;
 	}
-	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. */
+
+	ret = jffs2_garbage_collect_live(c, jeb, raw, f);
+
+	jffs2_gc_release_inode(c, f);
+
+ release_sem:
 		up(&c->alloc_sem);
-		iput(inode);
-		return -EIO;
+
+ 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 +441,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 +468,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 +482,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;
 	}
-	spin_unlock_bh(&c->erase_completion_lock);
+		}
+		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;
+	}
+	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 +691,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 +729,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 +773,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 +874,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 +882,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 +986,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 +1021,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 +1170,58 @@
 	 *    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;
-		char comprtype = JFFS2_COMPR_NONE;
+		uint32_t datalen;
+		uint32_t cdatalen;
+		uint16_t 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;
-		ri.compr = comprtype;
-		ri.node_crc = crc32(0, &ri, sizeof(ri)-8);
-		ri.data_crc = crc32(0, writebuf, cdatalen);
+		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 & 0xff;
+		ri.usercompr = (comprtype >> 8) & 0xff;
+		ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8));
+		ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
+	
+		new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC);
 	
-		new_fn = jffs2_write_dnode(inode, &ri, writebuf, cdatalen, phys_ofs, NULL);
+		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 +1236,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 -uNrb linux-2.4.30/fs/jffs2/ioctl.c linux/fs/jffs2/ioctl.c
--- linux-2.4.30/fs/jffs2/ioctl.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/ioctl.c	Fri Sep 10 15:15:04 2004
@@ -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 -uNrb linux-2.4.30/fs/jffs2/malloc.c linux/fs/jffs2/malloc.c
--- linux-2.4.30/fs/jffs2/malloc.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/malloc.c	Fri Sep 10 15:15:04 2004
@@ -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 -uNrb linux-2.4.30/fs/jffs2/nodelist.c linux/fs/jffs2/nodelist.c
--- linux-2.4.30/fs/jffs2/nodelist.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/nodelist.c	Fri Sep 10 15:15:04 2004
@@ -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 */
+			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 -uNrb linux-2.4.30/fs/jffs2/nodelist.h linux/fs/jffs2/nodelist.h
--- linux-2.4.30/fs/jffs2/nodelist.h	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/nodelist.h	Fri Sep 10 15:15:04 2004
@@ -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.119 2004/05/26 12:28:12 gleixner 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,31 @@
 /* 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);
-
-/* 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);
+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);
 
 /* 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);
-
-/* compr_zlib.c */
-int jffs2_zlib_init(void);
-void jffs2_zlib_exit(void);
+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);
+#endif
+
+#endif /* __JFFS2_NODELIST_H__ */
diff -uNrb linux-2.4.30/fs/jffs2/nodemgmt.c linux/fs/jffs2/nodemgmt.c
--- linux-2.4.30/fs/jffs2/nodemgmt.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/nodemgmt.c	Tue Sep 28 14:53:53 2004
@@ -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.108 2004/09/21 12:30:37 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;
+
+			/* 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);
-			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);
 				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;
+		
+		/* 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->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) {
+
+	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, blocknr, ref->flash_offset, jeb->used_size);
+			       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;
 	}
-
-	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;
+		}
+	} 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,333 @@
 		   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) {
+	} 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);
-		//		OFNI_BS_2SFFJ(c)->s_dirt = 1;
+			} 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);
+	} 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)); 
 	}
-	spin_unlock_bh(&c->erase_completion_lock);
 
-	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;
 	}
+
+	/* Nodes which have been marked obsolete no longer need to be
+	   associated with any inode. Remove them from the per-inode list */
+	if (ref->next_in_ino) {
+		struct jffs2_inode_cache *ic;
+		struct jffs2_raw_node_ref **p;
+
+		ic = jffs2_raw_ref_to_ic(ref);
+		for (p = &ic->nodes; (*p) != ref; p = &((*p)->next_in_ino))
+			;
+
+		*p = ref->next_in_ino;
+		ref->next_in_ino = NULL;
+	}
+
+
+	/* Merge with the next node in the physical list, if there is one
+	   and if it's also obsolete. */
+	if (ref->next_phys && ref_obsolete(ref->next_phys) ) {
+		struct jffs2_raw_node_ref *n = ref->next_phys;
+		
+		ref->__totlen += n->__totlen;
+		/* we don't need to check jeb->last_node */
+		ref->next_phys = n->next_phys;
+		if (jeb->gc_node == n) {
+			/* gc will be happy continuing gc on this node */
+			jeb->gc_node=ref;
+		}
+		BUG_ON(n->next_in_ino);
+		jffs2_free_raw_node_ref(n);
+	}
+	
+	/* Also merge with the previous node in the list, if there is one
+	   and that one is obsolete */
+	if (ref != jeb->first_node ) {
+		struct jffs2_raw_node_ref *p = jeb->first_node;
+		
+		while (p->next_phys != ref)
+			p = p->next_phys;
+		
+		if (ref_obsolete(p) ) {
+			p->__totlen += ref->__totlen;
+			if (jeb->last_node == ref) {
+				jeb->last_node = p;
+			}
+			if (jeb->gc_node == ref) {
+				/* gc will be happy continuing gc on this node */
+				jeb->gc_node=p;
+			}
+			p->next_phys = ref->next_phys;
+			jffs2_free_raw_node_ref(ref);
+		}
+	}
+}
+
+#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 -uNrb linux-2.4.30/fs/jffs2/pushpull.h linux/fs/jffs2/pushpull.h
--- linux-2.4.30/fs/jffs2/pushpull.h	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/pushpull.h	Fri Sep 10 15:15:04 2004
@@ -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 -uNrb linux-2.4.30/fs/jffs2/read.c linux/fs/jffs2/read.c
--- linux-2.4.30/fs/jffs2/read.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/read.c	Fri Sep 10 15:15:04 2004
@@ -1,52 +1,32 @@
 /*
  * 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.36 2004/05/25 11:12:32 havasi 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>
+#include "compr.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 +35,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 +86,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 +109,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 | (ri->usercompr << 8), readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize));
 		if (ret) {
 			printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret);
 			goto out_decomprbuf;
 		}
 	}
 
-	if (len < ri->dsize) {
+	if (len < je32_to_cpu(ri->dsize)) {
 		memcpy(buf, decomprbuf+ofs, len);
 	}
  out_decomprbuf:
@@ -160,4 +150,97 @@
 	jffs2_free_raw_inode(ri);
 
 	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 -uNrb linux-2.4.30/fs/jffs2/readinode.c linux/fs/jffs2/readinode.c
--- linux-2.4.30/fs/jffs2/readinode.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/readinode.c	Fri Sep 10 15:15:04 2004
@@ -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)
 {
+	int ret;
+	struct jffs2_node_frag *newfrag;
 	
-	struct jffs2_node_frag *this, **prev, *old;
-	struct jffs2_node_frag *newfrag, *newfrag2;
-	__u32 lastend = 0;
-
+	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));
 	
-	prev = list;
-	this = *list;
+	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));
 
-	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;
+
+	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);
+	}
+
+	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 */
-	while(this && fn->ofs >= this->ofs+this->size) {
-		lastend = this->ofs + this->size;
+	this = jffs2_lookup_node_frag(list, newfrag->node->ofs);
 
-		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 (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)
+static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, 
+					struct jffs2_inode_info *f,
+					struct jffs2_raw_inode *latest_node);
+
+int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 
+			uint32_t ino, struct jffs2_raw_inode *latest_node)
 {
-	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;
+	D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n"));
 
-	D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino));
+ 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;
 
-	f = JFFS2_INODE_INFO(inode);
-	c = JFFS2_SB_INFO(inode->i_sb);
+		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));
+		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;
-			}
-			printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n");
+				if (f->inocache->state == INO_STATE_READING)
+					jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT);
+				return -EIO;
+			}
+			printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n");
+		}
+		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;
 		}
-		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;
 
-		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;
+	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;
 	}			
 	    
-	inode->i_blksize = PAGE_SIZE;
-	inode->i_blocks = (inode->i_size + 511) >> 9;
-	
-	switch (inode->i_mode & S_IFMT) {
-		unsigned short rdev;
-
-	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;
+	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;
+	}
 		
+	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;
+		/* 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;
 		}			
-
-	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)));
+		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));
-
 	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));
+	jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL);
 
-		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);
+	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 -uNrb linux-2.4.30/fs/jffs2/scan.c linux/fs/jffs2/scan.c
--- linux-2.4.30/fs/jffs2/scan.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/scan.c	Tue Sep 28 14:53:53 2004
@@ -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.112 2004/09/12 09:56:13 gleixner 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,88 @@
 } 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;
+
+		/* Respect kmalloc limitations */
+		if (buf_size > 128*1024)
+			buf_size = 128*1024;
+
+		D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size));
+		flashbuf = kmalloc(buf_size, GFP_KERNEL);
+		if (!flashbuf)
+			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 +134,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 +151,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) && 
+                        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)
+                                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 {
+				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 && ((c->nr_free_blocks+empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { 
 			printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n");
-			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) {
+#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;
+			default: 	return ret;
+			}
+		}
+#endif
 		D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset));
-		return 1;	/* special return code */
+		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) {
@@ -196,102 +381,183 @@
 		}
 		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));
+		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;
 		}
-		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;
+
+		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;
+
+			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;
 		}
 
-		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;
+				inbuf_ofs+=4;
+				ofs += 4;
 		}
+			/* Ran off end. */
+			D1(printk(KERN_DEBUG "Empty flash to end of buffer at 0x%08x\n", ofs));
 
-		if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) {
+			/* 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 && 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 +566,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 "Empty flash detected from 0x%08x to 0x%08x\n", *startofs, curofs));
-	kfree(buf);
-	*startofs = curofs;
-	return 0;
+	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;
+	}
+
+	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 +647,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));
-
-	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;
-	}
+	uint32_t ino = je32_to_cpu(ri->ino);
 
-	/* 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;
+	D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs));
 
-	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;
-	}
-
-	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);
+
+	ic = jffs2_get_ino_cache(c, ino);
+	if (!ic) {
+		/* 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 -ENOMEM;
+			return 0;
 	}
-	ic = jffs2_scan_make_ino_cache(c, ri.ino);
+		ic = jffs2_scan_make_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;
 	}
+	}
+
+	/* Wheee. It worked */
 
-	/* Build the data structures and file them for later */
-	raw->flash_offset = *ofs;
-	raw->totlen = PAD(ri.totlen);
+	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 +726,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));
-
-	pseudo_random += 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;
+		  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)));
 
-		tn->next = NULL;
-		tn->fn = fn;
-		tn->version = ri.version;
+	pseudo_random += je32_to_cpu(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;
+	uint32_t crc;
 
-	D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", *ofs));
+	D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs));
 
-	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;
-	}
-
-	/* 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 +784,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 +802,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;
+	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);
 
-		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);
-
-		DIRTY_SPACE(PAD(rd.totlen));
-	} 
-	*ofs += PAD(rd.totlen);
 	return 0;
 }
 
@@ -731,26 +832,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));
 
-	if (c->nr_erasing_blocks)
-		rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks);
+		rotate_list((&c->erasable_list), rotateby);
 
-	if (c->nr_free_blocks) /* Not that it should ever be zero */
-		rotate_list((&c->free_list), pseudo_random % c->nr_free_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));
+
+		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 -uNrb linux-2.4.30/fs/jffs2/super.c linux/fs/jffs2/super.c
--- linux-2.4.30/fs/jffs2/super.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/super.c	Fri Sep 10 15:15:04 2004
@@ -1,291 +1,259 @@
 /*
  * 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.99 2004/08/24 07:59:57 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 "compr.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);
-	} else {
-		printk(KERN_DEBUG "nextblock: NULL\n");
-	}
-	if (c->gcblock) {
-		printk(KERN_DEBUG "gcblock: 0x%08x\n", c->gcblock->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 "gcblock: 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 (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);
-		}
-	}
-	if (list_empty(&c->bad_used_list)) {
-		printk(KERN_DEBUG "bad_used_list: empty\n");
-	} else {
-		struct list_head *this;
+	sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c);
+
+	if (IS_ERR(sb))
+		goto out_put;
 
-		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);
+	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;
 		}
+
+	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 | MS_NOATIME;
+
+	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));
+	return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd);
+}
 	
-	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;
+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);
 	}
-	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;
 	}
+			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;
 
-	sb->s_op = &jffs2_super_operations;
+			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);
+			}
+		}
+	}
 
-	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;
+	/* Try the old way - the hack where we allowed users to mount 
+	   /dev/mtdblock$(n) but didn't actually _use_ the blkdev */
+
+	err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd);
+
+	D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n",
+		  err, nd.dentry->d_inode));
+
+	if (err)
+		return ERR_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,83 +261,62 @@
 
 	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)
+static void jffs2_kill_sb(struct super_block *sb)
 {
 	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;
-}
-
-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;
-
-	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");
 
-	ret = jffs2_zlib_init();
+	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_compressors_init();
 	if (ret) {
-		printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n");
+		printk(KERN_ERR "JFFS2 error: Failed to initialise compressors\n");
 		goto out;
 	}
 	ret = jffs2_create_slab_caches();
 	if (ret) {
 		printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n");
-		goto out_zlib;
+		goto out_compressors;
 	}
 	ret = register_filesystem(&jffs2_fs_type);
 	if (ret) {
@@ -380,17 +327,19 @@
 
  out_slab:
 	jffs2_destroy_slab_caches();
- out_zlib:
-	jffs2_zlib_exit();
+ out_compressors:
+	jffs2_compressors_exit();
  out:
+	kmem_cache_destroy(jffs2_inode_cachep);
 	return ret;
 }
 
 static void __exit exit_jffs2_fs(void)
 {
-	jffs2_destroy_slab_caches();
-	jffs2_zlib_exit();
 	unregister_filesystem(&jffs2_fs_type);
+	jffs2_destroy_slab_caches();
+	jffs2_compressors_exit();
+	kmem_cache_destroy(jffs2_inode_cachep);
 }
 
 module_init(init_jffs2_fs);
diff -uNrb linux-2.4.30/fs/jffs2/symlink.c linux/fs/jffs2/symlink.c
--- linux-2.4.30/fs/jffs2/symlink.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/symlink.c	Fri Sep 10 15:15:04 2004
@@ -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.13 2004/07/13 08:59:04 dwmw2 Exp $
  *
  */
 
@@ -39,72 +15,31 @@
 #include <linux/kernel.h>
 #include <linux/slab.h>
 #include <linux/fs.h>
-#include <linux/jffs2.h>
+#include <linux/namei.h>
 #include "nodelist.h"
 
-int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen);
-int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
+static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd);
+static void jffs2_put_link(struct dentry *dentry, struct nameidata *nd);
 
 struct inode_operations jffs2_symlink_inode_operations =
 {	
-	readlink:	jffs2_readlink,
-	follow_link:	jffs2_follow_link,
-	setattr:	jffs2_setattr
+	.readlink =	generic_readlink,
+	.follow_link =	jffs2_follow_link,
+	.put_link =	jffs2_put_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)
+static int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
 {
-	unsigned char *kbuf;
-	int ret;
-
-	kbuf = jffs2_getlink(dentry);
-	if (IS_ERR(kbuf))
-		return PTR_ERR(kbuf);
-
-	ret = vfs_readlink(dentry, buffer, buflen, kbuf);
-	kfree(kbuf);
-	return ret;
+	unsigned char *buf;
+	buf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode));
+	nd_set_link(nd, buf);
+	return 0;
 }
 
-int jffs2_follow_link(struct dentry *dentry, struct nameidata *nd)
+static void jffs2_put_link(struct dentry *dentry, struct nameidata *nd)
 {
-	unsigned char *buf;
-	int ret;
-
-	buf = jffs2_getlink(dentry);
-
-	if (IS_ERR(buf))
-		return PTR_ERR(buf);
-
-	ret = vfs_follow_link(nd, buf);
-	kfree(buf);
-	return ret;
+	char *s = nd_get_link(nd);
+	if (!IS_ERR(s))
+		kfree(s);
 }
diff -uNrb linux-2.4.30/fs/jffs2/write.c linux/fs/jffs2/write.c
--- linux-2.4.30/fs/jffs2/write.c	Fri Nov 19 12:32:15 2004
+++ linux/fs/jffs2/write.c	Fri Sep 10 15:15:04 2004
@@ -1,154 +1,70 @@
 /*
  * 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.85 2004/07/13 08:58:25 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>
+#include "compr.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);
+		return -ENOMEM;
 	}
-	memset(ic, 0, sizeof(*ic));
-	
-	inode = new_inode(sb);
 	
-	if (!inode) {
-		jffs2_free_inode_cache(ic);
-		return ERR_PTR(-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);
+	f->inocache->ino = ++c->highest_ino;
+	f->inocache->state = INO_STATE_PRESENT;
 
-	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;
+	ri->ino = cpu_to_je32(f->inocache->ino);
 
-	insert_inode_hash(inode);
+	D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino));
+	jffs2_add_ino_cache(c, f->inocache);
 
-	return inode;
-}
+	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);
 
-/* 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;
+	f->highest_version = 1;
+	ri->version = cpu_to_je32(f->highest_version);
 
-	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;
-}
-
-
-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 +73,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;
-	struct iovec vecs[2];
+	size_t retlen;
+	struct kvec 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 +107,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 +121,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 +153,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;
-	struct iovec vecs[2];
+	size_t retlen;
+	struct kvec 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 +263,442 @@
 		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);
+	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 %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(*rd)+namelen, flash_ofs, ret, retlen);
 		/* Mark the space as dirtied */
 			if (retlen) {
-				jffs2_add_physical_node_ref(c, raw, sizeof(*rd)+namelen, 1);
+			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 {
+				/* 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;
+		uint16_t 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 & 0xff;
+		ri->usercompr = (comprtype >> 8 ) & 0xff;
+		ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8));
+		ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen));
+
+		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 -uNrb linux-2.4.30/fs/proc/proc_misc.c linux/fs/proc/proc_misc.c
--- linux-2.4.30/fs/proc/proc_misc.c	Fri Nov 19 12:46:58 2004
+++ linux/fs/proc/proc_misc.c	Fri Nov 19 13:44:03 2004
@@ -353,6 +353,8 @@
 			     " %u", kstat_irqs(i));
 #endif
 
+	proc_sprintf(page, &off, &len, "\nrootdrv_wblk: %llu", kstat.rootdrv_wblk);
+
 	proc_sprintf(page, &off, &len, "\ndisk_io: ");
 
 	for (major = 0; major < DK_MAX_MAJOR; major++) {
diff -uNrb linux-2.4.30/fs/yaffs/Makefile linux/fs/yaffs/Makefile
--- linux-2.4.30/fs/yaffs/Makefile	Thu Jan  1 03:00:00 1970
+++ linux/fs/yaffs/Makefile	Tue Sep 28 14:46:10 2004
@@ -0,0 +1,26 @@
+#
+# Makefile for the Linux msdos filesystem routines.
+#
+# Note! Dependencies are done automagically by 'make dep', which also
+# removes any old dependencies. DON'T put your own dependencies here
+# unless it's something special (ie not a .c file).
+#
+# Note 2! The CFLAGS definitions are now in the main makefile.
+
+
+EXTRA_CFLAGS += -DCONFIG_YAFFS_MTD_ENABLED \
+        -DCONFIG_YAFFS_USE_GENERIC_RW \
+        -DCONFIG_YAFFS_USE_NANDECC \
+
+#	-DCONFIG_YAFFS_ECC_WRONG_ORDER \
+
+
+O_TARGET := yaffs.o
+
+#export-objs := msdosfs_syms.o
+
+obj-y := yaffs_fs.o yaffs_guts.o yaffs_mtdif.o
+# yaffs_ecc.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -uNrb linux-2.4.30/fs/yaffs/devextras.h linux/fs/yaffs/devextras.h
--- linux-2.4.30/fs/yaffs/devextras.h	Thu Jan  1 03:00:00 1970
+++ linux/fs/yaffs/devextras.h	Tue Jan 11 14:08:51 2005
@@ -0,0 +1,271 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * devextras.h
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ *
+ * This file is just holds extra declarations used during development.
+ * Most of these are from kernel includes placed here so we can use them in 
+ * applications.
+ *
+ * $Id: devextras.h,v 1.5 2002/09/27 20:50:50 charles Exp $
+ *
+ */
+ 
+#ifndef __EXTRAS_H__
+#define __EXTRAS_H__
+
+#if defined WIN32
+#define __inline__ __inline
+#define new newHack
+#endif
+
+#if !(defined __KERNEL__) || (defined WIN32)
+
+// User space defines
+
+typedef unsigned char   __u8;
+typedef unsigned short  __u16;
+typedef unsigned        __u32;
+
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+ 
+ #define prefetch(x) 1
+ 
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+#define INIT_LIST_HEAD(ptr) do { \
+	(ptr)->next = (ptr); (ptr)->prev = (ptr); \
+} while (0)
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_add(struct list_head * new,
+	struct list_head * prev,
+	struct list_head * next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static __inline__ void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static __inline__ void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static __inline__ void __list_del(struct list_head * prev,
+				  struct list_head * next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty on entry does not return true after this, the entry is in an undefined state.
+ */
+static __inline__ void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static __inline__ void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static __inline__ int list_empty(struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_splice - join two lists
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static __inline__ void list_splice(struct list_head *list, struct list_head *head)
+{
+	struct list_head *first = list->next;
+
+	if (first != list) {
+		struct list_head *last = list->prev;
+		struct list_head *at = head->next;
+
+		first->prev = head;
+		head->next = first;
+
+		last->next = at;
+		at->prev = last;
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member)))
+
+/**
+ * list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next, prefetch(pos->next); pos != (head); \
+        	pos = pos->next, prefetch(pos->next))
+
+/**
+ * list_for_each_safe	-	iterate over a list safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop counter.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+
+
+
+/*
+ * File types
+ */
+#define DT_UNKNOWN	0
+#define DT_FIFO		1
+#define DT_CHR		2
+#define DT_DIR		4
+#define DT_BLK		6
+#define DT_REG		8
+#define DT_LNK		10
+#define DT_SOCK		12
+#define DT_WHT		14
+
+#ifndef WIN32
+#include <sys/stat.h>
+#endif
+
+/*
+ * Attribute flags.  These should be or-ed together to figure out what
+ * has been changed!
+ */
+#define ATTR_MODE	1
+#define ATTR_UID	2
+#define ATTR_GID	4
+#define ATTR_SIZE	8
+#define ATTR_ATIME	16
+#define ATTR_MTIME	32
+#define ATTR_CTIME	64
+#define ATTR_ATIME_SET	128
+#define ATTR_MTIME_SET	256
+#define ATTR_FORCE	512	/* Not a change, but a change it */
+#define ATTR_ATTR_FLAG	1024
+
+
+struct iattr {
+	unsigned int	ia_valid;
+	unsigned		ia_mode;
+	unsigned		ia_uid;
+	unsigned		ia_gid;
+	unsigned		ia_size;
+	unsigned		ia_atime;
+	unsigned	ia_mtime;
+	unsigned	ia_ctime;
+	unsigned int	ia_attr_flags;
+};
+
+#define KERN_DEBUG
+
+
+#else
+
+#ifndef WIN32
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/stat.h>
+#endif
+
+#endif
+
+
+
+#if defined WIN32
+#undef new
+#endif 
+
+#endif
+
diff -uNrb linux-2.4.30/fs/yaffs/yaffs_ecc.h linux/fs/yaffs/yaffs_ecc.h
--- linux-2.4.30/fs/yaffs/yaffs_ecc.h	Thu Jan  1 03:00:00 1970
+++ linux/fs/yaffs/yaffs_ecc.h	Tue Jan 11 16:51:19 2005
@@ -0,0 +1,33 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ *
+ * yaffs_ecc.c: ECC generation/correction algorithms.
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+ 
+ /*
+ * This code implements the ECC algorithm used in SmartMedia.
+ *
+ * The ECC comprises 22 bits of parity information and is stuffed into 3 bytes. 
+ * The two unused bit are set to 1.
+ * The ECC can correct single bit errors in a 256-byte page of data. Thus, two such ECC 
+ * blocks are used on a 512-byte NAND page.
+ *
+ */
+
+#ifndef __YAFFS_ECC_H__
+#define __YAFFS_ECC_H__
+extern inline void yaffs_ECCCalculate(const unsigned char *data,unsigned char *ecc) {}
+extern inline int yaffs_ECCCorrect(unsigned char *data, unsigned char *read_ecc,
+				   const unsigned char *test_ecc) {
+	return 0;
+}
+#endif
diff -uNrb linux-2.4.30/fs/yaffs/yaffs_fs.c linux/fs/yaffs/yaffs_fs.c
--- linux-2.4.30/fs/yaffs/yaffs_fs.c	Thu Jan  1 03:00:00 1970
+++ linux/fs/yaffs/yaffs_fs.c	Wed Apr 13 14:58:35 2005
@@ -0,0 +1,1715 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
+ * yaffs_fs.c
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This is the file system front-end to YAFFS that hooks it up to
+ * the VFS.
+ *
+ * Special notes: 
+ * >> sb->u.generic_sbp points to the yaffs_Device associated with this superblock
+ * >> inode->u.generic_ip points to the associated yaffs_Object.
+ *
+ *
+ * Acknowledgements:
+ * * Luc van OostenRyck for numerous patches.
+ * * Nick Bane for numerous patches.
+ * * Nick Bane for 2.5/2.6 integration.
+ * * Andras Toth for mknod rdev issue.
+ * * Michael Fischer for finding the problem with inode inconsistency.
+ * * Some code bodily lifted from JFFS2.
+ */
+
+
+const char *yaffs_fs_c_version = "$Id: yaffs_fs.c,v 1.37 2005/01/06 23:28:34 charles Exp $";
+extern const char *yaffs_guts_c_version;
+
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/version.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+#include <linux/pagemap.h>
+#include <linux/mtd/mtd.h>
+#include <linux/interrupt.h>
+#include <linux/string.h>
+
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+
+#include <linux/statfs.h>	/* Added NCB 15-8-2003 */
+#include <asm/statfs.h>
+#define UnlockPage(p) unlock_page(p)
+#define Page_Uptodate(page)	test_bit(PG_uptodate, &(page)->flags)
+//#define kdevname(x) cdevname(to_kdev_t(x))
+#define kdevname(x) "(unavailable)"	// temporary fix
+
+#else
+
+#include <linux/locks.h>
+
+#endif
+
+
+#include <asm/uaccess.h>
+
+#include "yportenv.h"
+#include "yaffs_guts.h"
+
+
+
+
+unsigned yaffs_traceMask = YAFFS_TRACE_ALWAYS | YAFFS_TRACE_BAD_BLOCKS;
+
+#ifdef CONFIG_YAFFS_RAM_ENABLED
+#include "yaffs_nandemul.h" 
+// 2 MB of RAM for emulation
+#define YAFFS_RAM_EMULATION_SIZE  0x200000
+#endif //CONFIG_YAFFS_RAM_ENABLED
+
+#ifdef CONFIG_YAFFS_MTD_ENABLED
+#include <linux/mtd/mtd.h>
+#include "yaffs_mtdif.h"
+#endif //CONFIG_YAFFS_MTD_ENABLED
+
+//#define T(x) printk x
+
+
+
+#define yaffs_InodeToObject(iptr) ((yaffs_Object *)((iptr)->u.generic_ip))
+#define yaffs_DentryToObject(dptr) yaffs_InodeToObject((dptr)->d_inode)
+//NCB #define yaffs_SuperToDevice(sb)	((yaffs_Device *)sb->u.generic_sbp)
+
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+#define yaffs_SuperToDevice(sb)	((yaffs_Device *)sb->s_fs_info)
+#else
+#define yaffs_SuperToDevice(sb)	((yaffs_Device *)sb->u.generic_sbp)
+#endif
+
+
+static void yaffs_put_super(struct super_block *sb);
+
+static ssize_t yaffs_file_read(struct file *f, char *buf, size_t n, loff_t *pos);
+static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, loff_t *pos);
+static int yaffs_file_flush(struct file* file);
+
+static int yaffs_sync_object(struct file * file, struct dentry *dentry, int datasync);
+
+static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir);
+
+//#if defined(CONFIG_KERNEL_2_5)	/* Added NCB 185-8-2003 */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *n);
+static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *n);
+#else
+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode);
+static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry);
+#endif
+static int yaffs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry);
+static int yaffs_unlink(struct inode * dir, struct dentry *dentry);
+static int yaffs_symlink(struct inode * dir, struct dentry *dentry, const char * symname);
+static int yaffs_mkdir(struct inode * dir, struct dentry * dentry, int mode);
+
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev);
+#else
+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int dev);
+#endif
+static int yaffs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry);
+static int yaffs_setattr(struct dentry *dentry, struct iattr *attr);
+
+//#if defined(CONFIG_KERNEL_2_5)	/* Added NCB 185-8-2003 */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf);
+#else
+static int yaffs_statfs(struct super_block *sb, struct statfs *buf);
+#endif
+static void yaffs_read_inode (struct inode *inode);
+
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static struct super_block *yaffs_read_super(struct file_system_type * fs, int flags, const char *dev_name, void *data);
+#else
+static struct super_block *yaffs_read_super(struct super_block * sb, void * data, int silent);
+#endif
+
+static void yaffs_put_inode (struct inode *inode);
+static void yaffs_delete_inode(struct inode *);
+static void yaffs_clear_inode(struct inode *);
+
+static int yaffs_readpage(struct file *file, struct page * page);
+static int yaffs_writepage(struct page *page);
+static int yaffs_prepare_write(struct file *f, struct page *pg, unsigned offset, unsigned to);
+static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, unsigned to);
+
+static int yaffs_readlink(struct dentry *dentry, char *buffer, int buflen);
+static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd);
+
+
+
+
+static struct address_space_operations yaffs_file_address_operations = {
+	readpage:		yaffs_readpage,
+	writepage:		yaffs_writepage,
+	prepare_write:	yaffs_prepare_write,
+	commit_write:	yaffs_commit_write
+};
+
+
+static struct file_operations yaffs_file_operations = {
+#ifdef CONFIG_YAFFS_USE_GENERIC_RW
+	read:		generic_file_read,
+	write:		generic_file_write,
+#else
+	read:		yaffs_file_read,
+	write:		yaffs_file_write,
+#endif
+	mmap:		generic_file_mmap,
+	flush:		yaffs_file_flush,
+	fsync:		yaffs_sync_object,
+	
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+	sendfile:	generic_file_sendfile,
+#endif
+};
+
+
+static struct inode_operations yaffs_file_inode_operations = {
+	setattr:	yaffs_setattr,
+};
+
+
+struct inode_operations yaffs_symlink_inode_operations =
+{	
+	readlink:	yaffs_readlink,
+	follow_link:	yaffs_follow_link,
+	setattr:	yaffs_setattr
+};
+
+static struct inode_operations yaffs_dir_inode_operations = {
+	create:		yaffs_create,
+	lookup:		yaffs_lookup,
+	link:		yaffs_link,
+	unlink:		yaffs_unlink,	
+	symlink:	yaffs_symlink,
+	mkdir:		yaffs_mkdir,
+	rmdir:		yaffs_unlink,
+	mknod:		yaffs_mknod,
+	rename:		yaffs_rename,
+	setattr:	yaffs_setattr,
+};
+
+static struct file_operations yaffs_dir_operations = {
+	read:		generic_read_dir,
+	readdir:	yaffs_readdir,
+	fsync:		yaffs_sync_object,
+};
+
+
+static struct super_operations yaffs_super_ops = {
+	statfs:			yaffs_statfs,
+	read_inode:		yaffs_read_inode,
+	put_inode:		yaffs_put_inode,
+	put_super:		yaffs_put_super,
+//	remount_fs:
+	delete_inode:		yaffs_delete_inode,
+	clear_inode:		yaffs_clear_inode,
+};
+
+
+
+static void yaffs_GrossLock(yaffs_Device *dev)
+{
+	T(YAFFS_TRACE_OS,("yaffs locking\n"));
+
+	down(&dev->grossLock);
+}
+
+static void yaffs_GrossUnlock(yaffs_Device *dev)
+{
+	T(YAFFS_TRACE_OS,("yaffs unlocking\n"));
+	up(&dev->grossLock);
+
+}
+
+static int yaffs_readlink(struct dentry *dentry, char *buffer, int buflen)
+{
+	unsigned char *alias;
+	int ret;
+
+	yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev;
+
+
+	yaffs_GrossLock(dev);
+	
+	alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry));
+	
+	yaffs_GrossUnlock(dev);
+	
+	if(!alias)
+		return -ENOMEM;
+
+	ret = vfs_readlink(dentry, buffer, buflen, alias);
+	kfree(alias);
+	return ret;
+}
+
+static int yaffs_follow_link(struct dentry *dentry, struct nameidata *nd)
+{
+	unsigned char *alias;
+	int ret;
+	yaffs_Device *dev = yaffs_DentryToObject(dentry)->myDev;
+
+
+	yaffs_GrossLock(dev);
+
+	alias = yaffs_GetSymlinkAlias(yaffs_DentryToObject(dentry));
+	
+	yaffs_GrossUnlock(dev);
+	
+	if(!alias)
+		return -ENOMEM;
+
+	ret = vfs_follow_link(nd,alias);
+	kfree(alias);
+	return ret;
+}
+
+
+struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,yaffs_Object *obj);
+
+/*
+ * Lookup is used to find objects in the fs
+ */
+//#if defined(CONFIG_KERNEL_2_5)	/* Added NCB 185-8-2003 */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+
+static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *n)
+#else
+static struct dentry * yaffs_lookup(struct inode *dir, struct dentry *dentry)
+#endif
+{
+	yaffs_Object *obj;
+	struct inode *inode = NULL; // NCB 2.5/2.6 needs NULL here
+	
+	yaffs_Device *dev = yaffs_InodeToObject(dir)->myDev;
+
+
+	yaffs_GrossLock(dev);
+
+	
+	T(YAFFS_TRACE_OS,("yaffs_lookup for %d:%s\n",yaffs_InodeToObject(dir)->objectId,dentry->d_name.name));
+	
+	obj = yaffs_FindObjectByName(yaffs_InodeToObject(dir),dentry->d_name.name);
+	
+	obj = yaffs_GetEquivalentObject(obj); // in case it was a hardlink
+	
+
+	
+	if(obj)
+	{
+		T(YAFFS_TRACE_OS,("yaffs_lookup found %d\n",obj->objectId));
+		
+		inode = yaffs_get_inode(dir->i_sb, obj->st_mode,0,obj);
+		
+		if(inode)
+		{
+			T(YAFFS_TRACE_OS,("yaffs_loookup dentry \n"));
+/* #if 0 asserted by NCB for 2.5/6 compatability - falls through to d_add even if NULL inode */	
+#if 0
+			//dget(dentry); // try to solve directory bug
+			d_add(dentry,inode);
+			
+			yaffs_GrossUnlock(dev);
+
+			// return dentry;
+			return NULL;
+#endif
+		}
+
+	}
+	else
+	{
+		T(YAFFS_TRACE_OS,("yaffs_lookup not found\n"));
+		
+	}
+	yaffs_GrossUnlock(dev);
+
+/* added NCB for 2.5/6 compatability - forces add even if inode is NULL which creates dentry hash*/	
+	d_add(dentry,inode);
+	
+	return NULL;
+	//	return (ERR_PTR(-EIO));
+	
+}
+
+// For now put inode is just for debugging
+// Put inode is called when the inode **structure** is put.
+static void yaffs_put_inode(struct inode *inode)
+{
+	T(YAFFS_TRACE_OS,("yaffs_put_inode: ino %d, count %d nlink %d\n",
+	                       (int)inode->i_ino, atomic_read(&inode->i_count), inode->i_nlink));
+	
+}
+
+// clear is called to tell the fs to release any per-inode data it holds
+static void yaffs_clear_inode(struct inode *inode)
+{
+	yaffs_Object *obj;
+	yaffs_Device *dev;
+	
+	obj = yaffs_InodeToObject(inode);
+	
+	T(YAFFS_TRACE_OS,("yaffs_clear_inode: ino %d, count %d %s\n",(int)inode->i_ino, atomic_read(&inode->i_count),
+		obj ? "object exists" : "null object"));	
+
+	if(obj)
+	{
+		dev = obj->myDev;
+		yaffs_GrossLock(dev);
+		
+		// Clear the association between the inode ant the yaffs_Object.
+		obj->myInode = NULL;
+		inode->u.generic_ip = NULL;
+		
+		// If the object freeing was deferred, then the real free happens now.
+		// This should fix the inode inconsistency problem.
+		
+		yaffs_HandleDeferedFree(obj);
+		
+		yaffs_GrossUnlock(dev);
+	}
+	
+	
+}
+
+// delete is called when the link count is zero and the inode
+// is put (ie. nobody wants to know about it anymore, time to
+// delete the file).
+// NB Must call clear_inode()
+static void yaffs_delete_inode(struct inode *inode)
+{
+	yaffs_Object *obj = yaffs_InodeToObject(inode);
+	yaffs_Device *dev;
+
+	T(YAFFS_TRACE_OS,("yaffs_delete_inode: ino %d, count %d %s\n",(int)inode->i_ino, atomic_read(&inode->i_count),
+		obj ? "object exists" : "null object"));
+	
+	if(obj)
+	{
+	 	dev = obj->myDev;
+		yaffs_GrossLock(dev);
+		yaffs_DeleteFile(obj);
+		yaffs_GrossUnlock(dev);
+	}
+	clear_inode(inode);
+}
+
+
+static int yaffs_file_flush(struct file* file)
+{
+	yaffs_Object *obj = yaffs_DentryToObject(file->f_dentry);
+	
+	yaffs_Device *dev = obj->myDev;
+	
+	T(YAFFS_TRACE_OS,("yaffs_file_flush object %d (%s)\n",obj->objectId,
+				obj->dirty ? "dirty" : "clean"));
+
+	yaffs_GrossLock(dev);
+	
+    yaffs_FlushFile(obj,1);
+
+	yaffs_GrossUnlock(dev);
+
+    return 0;
+}
+
+
+
+static int yaffs_readpage_nolock(struct file *f, struct page * pg)
+{
+	// Lifted from jffs2
+	
+	yaffs_Object *obj;
+	unsigned char *pg_buf;
+	int ret;
+
+	yaffs_Device *dev;
+
+	T(YAFFS_TRACE_OS,("yaffs_readpage at %08x, size %08x\n",
+	              (unsigned)(pg->index << PAGE_CACHE_SHIFT), (unsigned)PAGE_CACHE_SIZE));
+
+	obj  = yaffs_DentryToObject(f->f_dentry);
+
+	dev = obj->myDev;
+	
+	
+	if (!PageLocked(pg))
+                PAGE_BUG(pg);
+
+	pg_buf = kmap(pg);
+	/* FIXME: Can kmap fail? */
+
+	yaffs_GrossLock(dev);
+	
+	ret = yaffs_ReadDataFromFile(obj, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE);
+
+	yaffs_GrossUnlock(dev);
+	
+	if(ret >= 0) ret = 0;
+
+	if (ret) {
+		ClearPageUptodate(pg);
+		SetPageError(pg);
+	} else {
+		SetPageUptodate(pg);
+		ClearPageError(pg);
+	}
+
+	flush_dcache_page(pg);
+	kunmap(pg);
+
+
+	T(YAFFS_TRACE_OS,("yaffs_readpage done\n"));
+	return ret;
+}
+
+static int yaffs_readpage_unlock(struct file *f, struct page *pg)
+{
+	int ret = yaffs_readpage_nolock(f,pg);
+	UnlockPage(pg);
+	return ret;
+}
+
+static int yaffs_readpage(struct file *f, struct page * pg)
+{
+	return yaffs_readpage_unlock(f,pg);
+}
+
+// writepage inspired by/stolen from smbfs
+//
+
+static int yaffs_writepage(struct page *page)
+{
+	struct address_space *mapping = page->mapping;
+	struct inode *inode;
+	unsigned long end_index;
+	char *buffer;
+	yaffs_Object *obj;
+	int nWritten = 0;
+	unsigned nBytes;
+
+	if (!mapping)
+		BUG();
+	inode = mapping->host;
+	if (!inode)
+		BUG();
+
+	end_index = inode->i_size >> PAGE_CACHE_SHIFT;
+
+	/* easy case */
+	if (page->index < end_index)
+	{
+		nBytes = PAGE_CACHE_SIZE;
+	}
+	else
+	{
+		nBytes = inode->i_size & (PAGE_CACHE_SIZE-1);
+	}
+	//  What's happening here?
+	///* OK, are we completely out? */
+	//if (page->index >= end_index+1 || !offset)
+	//	return -EIO;
+
+	get_page(page);
+
+
+	buffer = kmap(page);
+
+	obj = yaffs_InodeToObject(inode);
+	yaffs_GrossLock(obj->myDev);
+
+
+	nWritten = yaffs_WriteDataToFile(obj,buffer,page->index << PAGE_CACHE_SHIFT,nBytes);
+
+	yaffs_GrossUnlock(obj->myDev);
+	
+	kunmap(page);
+	SetPageUptodate(page);
+	UnlockPage(page);
+	put_page(page);
+
+	return (nWritten == nBytes) ? 0  : -ENOSPC;
+}
+
+
+
+static int yaffs_prepare_write(struct file *f, struct page *pg, unsigned offset, unsigned to)
+{
+
+	T(YAFFS_TRACE_OS,("yaffs_prepair_write\n"));
+	if(!Page_Uptodate(pg) && (offset || to < PAGE_CACHE_SIZE))
+		return  yaffs_readpage_nolock(f,pg);    
+
+	return 0;
+	
+}
+
+static int yaffs_commit_write(struct file *f, struct page *pg, unsigned offset, unsigned to)
+{
+
+	void *addr = page_address(pg) + offset;
+	loff_t pos = (((loff_t)pg->index) << PAGE_CACHE_SHIFT) + offset;
+	int nBytes = to - offset;
+	int nWritten;
+	
+	unsigned spos = pos;
+	unsigned saddr = (unsigned)addr;
+
+	T(YAFFS_TRACE_OS,("yaffs_commit_write addr %x pos %x nBytes %d\n",saddr,spos,nBytes));
+	
+	nWritten = yaffs_file_write(f,addr, nBytes, &pos);
+	
+	if(nWritten != nBytes)
+	{
+		T(YAFFS_TRACE_OS,("yaffs_commit_write not same size nWritten %d  nBytes %d\n",nWritten,nBytes));
+		SetPageError(pg);
+		ClearPageUptodate(pg);
+	}
+	else
+	{
+		SetPageUptodate(pg);
+	}
+
+	T(YAFFS_TRACE_OS,("yaffs_commit_write returning %d\n",nWritten));
+	
+	return nWritten;
+
+}
+
+
+
+static void yaffs_FillInodeFromObject(struct inode *inode, yaffs_Object *obj)
+{
+	if (inode && obj) 
+	{
+		inode->i_ino = obj->objectId;
+		inode->i_mode = obj->st_mode;
+		inode->i_uid = obj->st_uid;
+		inode->i_gid = obj->st_gid;
+		inode->i_blksize = inode->i_sb->s_blocksize;
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+
+		inode->i_rdev = old_decode_dev(obj->st_rdev);
+		inode->i_atime.tv_sec = (time_t)(obj->st_atime);
+		inode->i_atime.tv_nsec = 0;
+		inode->i_mtime.tv_sec = (time_t)obj->st_mtime;
+		inode->i_mtime.tv_nsec =0;
+		inode->i_ctime.tv_sec = (time_t)obj->st_ctime;
+		inode->i_ctime.tv_nsec = 0;
+#else
+		inode->i_rdev = obj->st_rdev;
+		inode->i_atime = obj->st_atime;
+		inode->i_mtime = obj->st_mtime;
+		inode->i_ctime = obj->st_ctime;
+#endif
+		inode->i_size = yaffs_GetObjectFileLength(obj);
+		inode->i_blocks = (inode->i_size + 511) >> 9;
+
+		inode->i_nlink = yaffs_GetObjectLinkCount(obj);
+		
+		T(YAFFS_TRACE_OS,("yaffs_FillInode mode %x uid %d gid %d size %d count %d\n",
+				inode->i_mode, inode->i_uid, inode->i_gid, (int)inode->i_size, atomic_read(&inode->i_count)));
+		
+		switch (obj->st_mode & S_IFMT) 
+		{
+			default: // fifo, device or socket
+				init_special_inode(inode, obj->st_mode,(dev_t)(obj->st_rdev));
+				break;
+			case S_IFREG:	// file		
+				inode->i_op = &yaffs_file_inode_operations;
+				inode->i_fop = &yaffs_file_operations;
+				inode->i_mapping->a_ops = &yaffs_file_address_operations;
+				break;
+			case S_IFDIR:	// directory
+				inode->i_op = &yaffs_dir_inode_operations;
+				inode->i_fop = &yaffs_dir_operations;
+				break;
+			case S_IFLNK:	// symlink
+				inode->i_op = &yaffs_symlink_inode_operations;
+				break;
+		}
+		
+		
+		inode->u.generic_ip = obj;
+		obj->myInode = inode;
+		
+	}
+	else
+	{
+		T(YAFFS_TRACE_OS,("yaffs_FileInode invalid parameters\n"));
+	}
+
+}
+
+struct inode *yaffs_get_inode(struct super_block *sb, int mode, int dev,yaffs_Object *obj)
+{
+	struct inode * inode;
+	
+	T(YAFFS_TRACE_OS,("yaffs_get_inode for object %d\n",obj->objectId));
+
+	inode = iget(sb,obj->objectId);
+
+	// NB Side effect: iget calls back to yaffs_read_inode().
+	// iget also increments the inode's i_count
+	
+	return inode;
+}
+
+static ssize_t yaffs_file_read(struct file *f, char *buf, size_t n, loff_t *pos)
+{
+	yaffs_Object *obj;
+	int nRead,ipos;
+	struct inode *inode;
+	yaffs_Device *dev;
+	
+	T(YAFFS_TRACE_OS,("yaffs_file_read\n"));
+
+	obj  = yaffs_DentryToObject(f->f_dentry);
+	
+	dev = obj->myDev;
+	
+	yaffs_GrossLock(dev);
+	
+	inode = f->f_dentry->d_inode;
+	
+	if (*pos < inode->i_size) 
+	{
+			if (*pos + n > inode->i_size)
+			{
+				n = inode->i_size - *pos;
+			}
+	}
+	else
+	{
+		n = 0;
+	}
+	
+	nRead = yaffs_ReadDataFromFile(obj,buf,*pos,n);
+	if(nRead > 0)
+	{
+		f->f_pos += nRead;
+	}
+	
+	yaffs_GrossUnlock(dev);
+	
+	ipos = *pos;
+	
+	T(YAFFS_TRACE_OS,("yaffs_file_read read %d bytes, %d read at %d\n",n,nRead,ipos));
+	return nRead;
+	
+}
+
+
+static ssize_t yaffs_file_write(struct file *f, const char *buf, size_t n, loff_t *pos)
+{
+	yaffs_Object *obj;
+	int nWritten,ipos;
+	struct inode *inode;
+	yaffs_Device *dev;
+	
+	
+	obj  = yaffs_DentryToObject(f->f_dentry);
+	
+	dev = obj->myDev;
+	
+	yaffs_GrossLock(dev);
+
+	inode = f->f_dentry->d_inode;
+
+	if(!S_ISBLK(inode->i_mode) && f->f_flags & O_APPEND)
+	{
+		ipos = inode->i_size;
+	}
+	else
+	{
+		ipos = *pos;
+	}
+	
+	
+	if(!obj)
+	{
+		T(YAFFS_TRACE_OS,("yaffs_file_write: hey obj is null!\n"));
+	}
+	else
+	{
+		T(YAFFS_TRACE_OS,("yaffs_file_write about to write writing %d bytes to object %d at %d\n",n,obj->objectId,ipos));
+	}
+
+	nWritten = yaffs_WriteDataToFile(obj,buf,ipos,n);
+
+	T(YAFFS_TRACE_OS,("yaffs_file_write writing %d bytes, %d written at %d\n",n,nWritten,ipos));
+	if(nWritten > 0)
+	{
+		ipos += nWritten;
+		*pos = ipos;
+		if(ipos > inode->i_size)
+		{
+			inode->i_size = ipos;
+			inode->i_blocks = (ipos + 511)>>9;
+			
+			T(YAFFS_TRACE_OS,("yaffs_file_write size updated to %d bytes, %d blocks\n",ipos,(int)(inode->i_blocks)));
+		}
+		
+	}
+	yaffs_GrossUnlock(dev);
+	
+	return nWritten != n ? -ENOSPC : nWritten;
+}
+
+
+static int yaffs_readdir(struct file *f, void *dirent, filldir_t filldir)
+{
+	yaffs_Object *obj;
+	yaffs_Device *dev;
+	struct inode *inode = f->f_dentry->d_inode;
+	unsigned long offset, curoffs;
+	struct list_head *i;	
+	yaffs_Object *l;
+	
+	char name[YAFFS_MAX_NAME_LENGTH	+1];
+		
+	obj =  yaffs_DentryToObject(f->f_dentry);
+	dev = obj->myDev;
+	
+	yaffs_GrossLock(dev);
+	
+	offset = f->f_pos;
+	
+	T(YAFFS_TRACE_OS,("yaffs_readdir: starting at %d\n",(int)offset));
+	
+	if(offset == 0)
+	{
+        T(YAFFS_TRACE_OS,("yaffs_readdir: entry . ino %d \n",(int)inode->i_ino));
+		if(filldir(dirent,".",1,offset,inode->i_ino,DT_DIR) < 0)
+		{
+			goto out;
+		}
+		offset++;
+		f->f_pos++;
+	}
+	if(offset == 1)
+	{
+		T(YAFFS_TRACE_OS,("yaffs_readdir: entry .. ino %d \n",(int)f->f_dentry->d_parent->d_inode->i_ino));
+		if(filldir(dirent,"..",2,offset,f->f_dentry->d_parent->d_inode->i_ino,DT_DIR) < 0)
+		{
+			goto out;
+		}
+		offset++;
+		f->f_pos++;
+	}
+	
+	curoffs = 1;
+	
+	list_for_each(i,&obj->variant.directoryVariant.children)
+	{
+		curoffs++;
+		if(curoffs >= offset)
+		{		
+			l = list_entry(i, yaffs_Object,siblings);
+			
+			yaffs_GetObjectName(l,name,YAFFS_MAX_NAME_LENGTH+1); 
+			T(YAFFS_TRACE_OS,("yaffs_readdir: %s inode %d\n",name,yaffs_GetObjectInode(l)));
+			
+			if(filldir(dirent,
+					   name,
+					   strlen(name),
+					   offset,
+					   yaffs_GetObjectInode(l),
+					   yaffs_GetObjectType(l))
+					   < 0)
+			{
+				goto up_and_out;
+			}
+			
+			offset++;
+			f->f_pos++;	   
+		}
+	}
+
+  up_and_out:
+  out:
+  
+    yaffs_GrossUnlock(dev);
+    
+	return 0;
+}
+
+
+/*
+ * File creation. Allocate an inode, and we're done..
+ */
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t rdev)
+#else
+static int yaffs_mknod(struct inode *dir, struct dentry *dentry, int mode, int rdev)
+#endif
+{
+	struct inode *inode;
+	
+	yaffs_Object *obj = NULL;
+	yaffs_Device *dev;
+	
+	yaffs_Object *parent = yaffs_InodeToObject(dir);
+	
+	int error = -ENOSPC;
+
+	if(parent)
+	{
+		T(YAFFS_TRACE_OS,("yaffs_mknod: parent object %d type %d\n",
+					 parent->objectId,parent->variantType));
+	}
+	else
+	{
+		T(YAFFS_TRACE_OS,("yaffs_mknod: could not get parent object\n"));
+		return -EPERM;
+	}
+	
+	T(YAFFS_TRACE_OS,("yaffs_mknod: making oject for %s, mode %x dev %x\n",
+					dentry->d_name.name, mode,rdev));
+
+	dev = parent->myDev;
+	
+	yaffs_GrossLock(dev);
+
+	switch (mode & S_IFMT) 
+	{
+		default:
+			// Special (socket, fifo, device...)
+			T(YAFFS_TRACE_OS,("yaffs_mknod: making special\n"));
+			obj = yaffs_MknodSpecial(parent,dentry->d_name.name,mode,current->uid, current->gid,rdev);
+			break;
+		case S_IFREG:	// file		
+			T(YAFFS_TRACE_OS,("yaffs_mknod: making file\n"));
+			obj = yaffs_MknodFile(parent,dentry->d_name.name,mode,current->uid, current->gid);
+			break;
+		case S_IFDIR:	// directory
+			T(YAFFS_TRACE_OS,("yaffs_mknod: making directory\n"));
+			obj = yaffs_MknodDirectory(parent,dentry->d_name.name,mode,current->uid, current->gid);
+			break;
+		case S_IFLNK:	// symlink
+			T(YAFFS_TRACE_OS,("yaffs_mknod: making file\n"));
+			obj = NULL; // Do we ever get here?
+			break;
+	}
+	
+	if(obj)
+	{
+		inode = yaffs_get_inode(dir->i_sb, mode, rdev, obj);
+		d_instantiate(dentry, inode);
+		T(YAFFS_TRACE_OS,("yaffs_mknod created object %d count = %d\n",obj->objectId,atomic_read(&inode->i_count)));
+		error = 0;
+	}
+	else
+	{
+		T(YAFFS_TRACE_OS,("yaffs_mknod failed making object\n"));
+		error = -ENOMEM;
+	}
+
+	yaffs_GrossUnlock(dev);
+
+	return error;
+}
+
+static int yaffs_mkdir(struct inode * dir, struct dentry * dentry, int mode)
+{
+	int retVal;
+	T(YAFFS_TRACE_OS,("yaffs_mkdir\n"));
+	retVal =  yaffs_mknod(dir, dentry, mode | S_IFDIR, 0);
+#if 0
+ // attempt to fix dir bug - didn't work
+	if(!retVal)
+	{
+		dget(dentry);
+	}
+#endif
+	return retVal;
+}
+
+//#if defined(CONFIG_KERNEL_2_5)	/* Added NCB 185-8-2003 */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode, struct nameidata *n)
+#else
+static int yaffs_create(struct inode *dir, struct dentry *dentry, int mode)
+#endif
+{
+	T(YAFFS_TRACE_OS,("yaffs_create\n"));
+	return yaffs_mknod(dir, dentry, mode | S_IFREG, 0);
+}
+
+
+static int yaffs_unlink(struct inode * dir, struct dentry *dentry)
+{
+	int retVal;
+	
+	yaffs_Device *dev;
+	
+	
+	T(YAFFS_TRACE_OS,("yaffs_unlink %d:%s\n",(int)(dir->i_ino),dentry->d_name.name));
+	
+	dev = yaffs_InodeToObject(dir)->myDev;
+	
+	yaffs_GrossLock(dev);
+	
+	
+	retVal = yaffs_Unlink(yaffs_InodeToObject(dir),dentry->d_name.name);
+	
+	
+	yaffs_GrossUnlock(dev);
+	
+	if( retVal == YAFFS_OK)
+	{
+		dentry->d_inode->i_nlink--;
+		mark_inode_dirty(dentry->d_inode);
+		return 0;
+	}
+	else
+	{
+		return -ENOTEMPTY;
+	}
+}
+
+
+/*
+ * Create a link...
+ */
+static int yaffs_link(struct dentry *old_dentry, struct inode * dir, struct dentry * dentry)
+{
+	struct inode *inode = old_dentry->d_inode;
+	yaffs_Object *obj = NULL;
+	yaffs_Object *link=NULL;
+	yaffs_Device *dev;
+	
+	T(YAFFS_TRACE_OS,("yaffs_link\n"));
+	
+	obj = yaffs_InodeToObject(inode);
+	dev = obj->myDev;
+	
+	yaffs_GrossLock(dev);
+
+	if (!S_ISDIR(inode->i_mode))	// Don't link directories
+	{
+		link = yaffs_Link(yaffs_InodeToObject(dir),dentry->d_name.name,obj);
+	}
+	
+
+	if(link)
+	{
+		old_dentry->d_inode->i_nlink =  yaffs_GetObjectLinkCount(obj);
+		d_instantiate(dentry, old_dentry->d_inode);
+		atomic_inc(&old_dentry->d_inode->i_count);
+		T(YAFFS_TRACE_OS,("yaffs_link link count %d i_count %d\n",	
+			old_dentry->d_inode->i_nlink,atomic_read(&old_dentry->d_inode->i_count)));
+	
+	}
+	
+	yaffs_GrossUnlock(dev);
+	
+
+	if(link)
+	{
+	
+		return 0;
+	}
+	
+	
+	return -EPERM; 
+}
+
+
+static int yaffs_symlink(struct inode * dir, struct dentry *dentry, const char * symname)
+{
+	yaffs_Object *obj;
+	yaffs_Device *dev;
+	
+	T(YAFFS_TRACE_OS,("yaffs_symlink\n"));
+	
+	dev = yaffs_InodeToObject(dir)->myDev;
+	yaffs_GrossLock(dev);
+	obj = yaffs_MknodSymLink(yaffs_InodeToObject(dir), dentry->d_name.name, 
+							 S_IFLNK | S_IRWXUGO, current->uid, current->gid,
+							 symname);
+	yaffs_GrossUnlock(dev);
+
+	if(obj)
+	{
+
+		struct inode* inode;
+	
+		inode = yaffs_get_inode(dir->i_sb, obj->st_mode, 0, obj);
+		d_instantiate(dentry, inode);
+		T(YAFFS_TRACE_OS,("symlink created OK\n"));
+		return 0;
+	}
+	else
+	{
+		T(YAFFS_TRACE_OS,("symlink not created\n"));
+
+	}
+	
+	return -ENOMEM;
+}
+
+static int yaffs_sync_object(struct file * file, struct dentry *dentry, int datasync)
+{
+
+	yaffs_Object *obj;
+	yaffs_Device *dev;
+	
+	obj = yaffs_DentryToObject(dentry);
+
+	dev = obj->myDev;
+	
+	T(YAFFS_TRACE_OS,("yaffs_sync_object\n"));
+	yaffs_GrossLock(dev);
+	yaffs_FlushFile(obj,1);
+	yaffs_GrossUnlock(dev);
+	return 0;
+}
+
+/*
+ * The VFS layer already does all the dentry stuff for rename.
+ *
+ * NB: POSIX says you can rename an object over an old object of the same name
+ */
+static int yaffs_rename(struct inode * old_dir, struct dentry *old_dentry, struct inode * new_dir,struct dentry *new_dentry)
+{
+	yaffs_Device *dev;
+	int retVal = YAFFS_FAIL;
+	int removed = 0;
+	yaffs_Object *target;
+	
+	dev = yaffs_InodeToObject(old_dir)->myDev;
+
+	yaffs_GrossLock(dev);
+	
+	// Check if the target is an existing directory that is not empty.
+	target = yaffs_FindObjectByName(yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
+	
+	if(target &&
+	   target->variantType == YAFFS_OBJECT_TYPE_DIRECTORY &&
+	   !list_empty(&target->variant.directoryVariant.children))
+	{
+		retVal = YAFFS_FAIL;
+	}
+	else
+	{
+	   
+		// Unlink the target if it exists
+		removed = yaffs_Unlink(yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
+
+	
+		retVal =  yaffs_RenameObject(yaffs_InodeToObject(old_dir),old_dentry->d_name.name,
+									yaffs_InodeToObject(new_dir),new_dentry->d_name.name);
+									
+	}
+	yaffs_GrossUnlock(dev);
+	
+	if(retVal == YAFFS_OK)
+	{
+		if(removed == YAFFS_OK)
+		{
+			new_dentry->d_inode->i_nlink--;
+			mark_inode_dirty(new_dentry->d_inode);
+		}
+		
+		return 0;
+	}
+	else
+	{
+		return -ENOTEMPTY;
+	}
+	
+
+}
+
+static int yaffs_setattr(struct dentry *dentry, struct iattr *attr)
+{
+	struct inode *inode = dentry->d_inode;
+	int error;
+	yaffs_Device *dev;
+	
+	T(YAFFS_TRACE_OS,("yaffs_setattr of object %d\n",yaffs_InodeToObject(inode)->objectId));
+	
+	if((error = inode_change_ok(inode,attr)) == 0)
+	{
+	
+		dev = yaffs_InodeToObject(inode)->myDev;
+		yaffs_GrossLock(dev);
+		if(yaffs_SetAttributes(yaffs_InodeToObject(inode),attr) == YAFFS_OK)
+		{
+			error = 0;
+		}
+		else
+		{
+			error = -EPERM;
+		}
+		yaffs_GrossUnlock(dev);
+		inode_setattr(inode,attr);
+	}
+	return error;
+}
+
+//#if defined(CONFIG_KERNEL_2_5)	/* Added NCB 185-8-2003 */
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static int yaffs_statfs(struct super_block *sb, struct kstatfs *buf)
+#else
+static int yaffs_statfs(struct super_block *sb, struct statfs *buf)
+#endif
+{
+	yaffs_Device *dev = yaffs_SuperToDevice(sb);
+	T(YAFFS_TRACE_OS,("yaffs_statfs\n"));
+
+	yaffs_GrossLock(dev);
+	
+	buf->f_type = YAFFS_MAGIC;
+	buf->f_bsize = sb->s_blocksize;
+	buf->f_namelen = 255;
+	buf->f_blocks = (dev->endBlock - dev->startBlock + 1) * YAFFS_CHUNKS_PER_BLOCK/
+						(sb->s_blocksize/YAFFS_BYTES_PER_CHUNK);
+	buf->f_files = 0;
+	buf->f_ffree = 0;
+	buf->f_bfree = yaffs_GetNumberOfFreeChunks(dev)/
+						(sb->s_blocksize/YAFFS_BYTES_PER_CHUNK);
+	buf->f_bavail =  buf->f_bfree;
+	
+	yaffs_GrossUnlock(dev);
+	return 0;
+}
+
+static void yaffs_read_inode (struct inode *inode)
+{
+	// NB This is called as a side effect of other functions and
+	// thus gross locking should always be in place already.
+	
+	yaffs_Object *obj ; 
+	yaffs_Device *dev = yaffs_SuperToDevice(inode->i_sb);
+	
+	T(YAFFS_TRACE_OS,("yaffs_read_inode for %d\n",(int)inode->i_ino));
+
+	obj  = yaffs_FindObjectByNumber(dev,inode->i_ino);
+	
+	yaffs_FillInodeFromObject(inode,obj);
+
+}
+
+
+
+static yaffs_Device *yaffs_dev;
+static yaffs_Device *yaffsram_dev;
+
+
+
+static void yaffs_put_super(struct super_block *sb)
+{
+	yaffs_Device *dev = yaffs_SuperToDevice(sb);
+	
+	yaffs_GrossLock(dev);
+	if(dev->putSuperFunc)
+	{
+		 dev->putSuperFunc(sb);
+	}
+	yaffs_Deinitialise(dev);
+	yaffs_GrossUnlock(dev);
+
+	if(dev == yaffs_dev) yaffs_dev = NULL;
+	if(dev == yaffsram_dev) yaffsram_dev = NULL;
+		
+	kfree(dev);
+}
+
+
+#ifdef CONFIG_YAFFS_MTD_ENABLED
+
+static void  yaffs_MTDPutSuper(struct super_block *sb)
+{
+	
+	struct mtd_info *mtd = yaffs_SuperToDevice(sb)->genericDevice;
+	
+	if(mtd->sync)
+	{
+		mtd->sync(mtd);
+	}
+	
+	put_mtd_device(mtd);
+}
+
+#endif
+
+
+static struct super_block *yaffs_internal_read_super(int useRam, struct super_block * sb, void * data, int silent)
+{
+	int nBlocks;
+	struct inode * inode;
+	struct dentry * root;
+	yaffs_Device *dev;
+	
+	sb->s_magic = YAFFS_MAGIC;
+	sb->s_op = &yaffs_super_ops;
+	
+	if(!sb)
+		T(YAFFS_TRACE_OS,("yaffs: sb is NULL\n"));
+	else if(!sb->s_dev)
+		T(YAFFS_TRACE_OS,("yaffs: sb->s_dev is NULL\n"));
+	else if(! kdevname(sb->s_dev))
+		T(YAFFS_TRACE_OS,("yaffs: kdevname is NULL\n"));
+	else
+		T(YAFFS_TRACE_OS,("yaffs: dev is %d name is \"%s\"\n", sb->s_dev, kdevname(sb->s_dev)));
+
+	
+
+#ifdef CONFIG_YAFFS_USE_CHUNK_SIZE
+	sb->s_blocksize = YAFFS_BYTES_PER_CHUNK;
+	sb->s_blocksize_bits = YAFFS_CHUNK_SIZE_SHIFT;
+#else
+	sb->s_blocksize = PAGE_CACHE_SIZE;
+	sb->s_blocksize_bits = PAGE_CACHE_SHIFT;
+#endif
+	T(YAFFS_TRACE_OS,("yaffs_read_super: %s block size %d\n", useRam ? "RAM" : "MTD",(int)(sb->s_blocksize)));
+
+#ifdef CONFIG_YAFFS_DISABLE_WRITE_VERIFY
+	T(YAFFS_TRACE_OS,("yaffs: Write verification disabled. All guarantees null and void\n"));
+#endif
+
+
+	
+	if(useRam)
+	{
+
+#ifdef CONFIG_YAFFS_RAM_ENABLED
+		// Set the yaffs_Device up for ram emulation
+
+		sb->u.generic_sbp =	dev = kmalloc(sizeof(yaffs_Device),GFP_KERNEL);
+		if(!dev)
+		{
+			// Deep shit could not allocate device structure
+			T(YAFFS_TRACE_OS,("yaffs_read_super: Failed trying to allocate yaffs_Device. Terminating debug.\n"));
+			return NULL;
+		}
+
+		memset(dev,0,sizeof(yaffs_Device));
+		dev->genericDevice = NULL; // Not used for RAM emulation.
+
+		nBlocks = YAFFS_RAM_EMULATION_SIZE / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK);
+		dev->startBlock = 1;  // Don't use block 0
+		dev->endBlock = nBlocks - 1;
+		dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;
+		dev->nBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
+		dev->nReservedBlocks = 5;
+		dev->nShortOpCaches = 10; // Enable short op caching
+
+		dev->writeChunkToNAND = nandemul_WriteChunkToNAND;
+		dev->readChunkFromNAND = nandemul_ReadChunkFromNAND;
+		dev->eraseBlockInNAND = nandemul_EraseBlockInNAND;
+		dev->initialiseNAND = nandemul_InitialiseNAND;
+
+		yaffsram_dev = dev;
+		
+#endif
+
+	}
+	else
+	{	
+#ifdef CONFIG_YAFFS_MTD_ENABLED
+		struct mtd_info *mtd;
+		
+		T(YAFFS_TRACE_ALWAYS,( "yaffs: Attempting MTD mount on %u.%u, \"%s\"\n",
+		 MAJOR(sb->s_dev),MINOR(sb->s_dev),kdevname(sb->s_dev)));
+			
+		// Check it's an mtd device.....
+		if(MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR)
+		{
+			return NULL; // This isn't an mtd device
+		} 
+		
+		// Get the device
+		mtd = get_mtd_device(NULL, MINOR(sb->s_dev));
+		if (!mtd) 
+		{
+			T(YAFFS_TRACE_ALWAYS,( "yaffs: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev)));
+			return NULL;
+		}
+		
+		// Check it's NAND
+		if(mtd->type != MTD_NANDFLASH)
+		{
+			T(YAFFS_TRACE_ALWAYS,( "yaffs: MTD device is not NAND it's type %d\n", mtd->type));
+			return NULL;
+		}
+
+		//T(YAFFS_TRACE_OS,(KERN_DEBUG" erase %x\n",mtd->erase);
+		//T(YAFFS_TRACE_OS,(KERN_DEBUG" read %x\n",mtd->read);
+		//T(YAFFS_TRACE_OS,(KERN_DEBUG" write %x\n",mtd->write);
+		//T(YAFFS_TRACE_OS,(KERN_DEBUG" readoob %x\n",mtd->read_oob);
+		//T(YAFFS_TRACE_OS,(KERN_DEBUG" writeoob %x\n",mtd->write_oob);
+		//T(YAFFS_TRACE_OS,(KERN_DEBUG" oobblock %x\n",mtd->oobblock);
+		//T(YAFFS_TRACE_OS,(KERN_DEBUG" oobsize %x\n",mtd->oobsize);
+
+
+		if(!mtd->erase ||
+		   !mtd->read  ||
+		   !mtd->write ||
+#ifndef CONFIG_YAFFS_USE_OLD_MTD
+		   !mtd->write_ecc ||
+		   !mtd->read_ecc ||
+#endif
+		   !mtd->read_oob ||
+		   !mtd->write_oob )
+		{
+			T(YAFFS_TRACE_ALWAYS,( "yaffs: MTD device does not support required functions\n"));
+			return NULL;
+		}
+		
+		if(mtd->oobblock != YAFFS_BYTES_PER_CHUNK ||
+		   mtd->oobsize != YAFFS_BYTES_PER_SPARE)
+		{
+			T(YAFFS_TRACE_ALWAYS,( "yaffs: MTD device does not support have the right page sizes\n"));
+			return NULL;
+		}
+		   
+
+		// OK, so if we got here, we have an MTD that's NAND and looks 
+		// like it has the right capabilities
+		// Set the yaffs_Device up for ram emulation
+
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+		sb->s_fs_info =	dev = kmalloc(sizeof(yaffs_Device),GFP_KERNEL);
+#else
+		sb->u.generic_sbp =	dev = kmalloc(sizeof(yaffs_Device),GFP_KERNEL);
+#endif
+		if(!dev)
+		{
+			// Deep shit could not allocate device structure
+			T(YAFFS_TRACE_ALWAYS,("yaffs_read_super: Failed trying to allocate yaffs_Device. Terminating debug.\n"));
+			return NULL;
+		}
+
+		memset(dev,0,sizeof(yaffs_Device));
+		dev->genericDevice = mtd; 
+
+		// Set up the memory size parameters....
+		
+		nBlocks = mtd->size / (YAFFS_CHUNKS_PER_BLOCK * YAFFS_BYTES_PER_CHUNK);
+		dev->startBlock = 1;  // Don't use block 0
+		dev->endBlock = nBlocks - 1;
+		dev->nChunksPerBlock = YAFFS_CHUNKS_PER_BLOCK;
+		dev->nBytesPerChunk = YAFFS_BYTES_PER_CHUNK;
+		dev->nReservedBlocks = 5;
+		dev->nShortOpCaches = 10; // Enable short op caching
+		
+
+		// ... and the functions.
+		dev->writeChunkToNAND = nandmtd_WriteChunkToNAND;
+		dev->readChunkFromNAND = nandmtd_ReadChunkFromNAND;
+		dev->eraseBlockInNAND = nandmtd_EraseBlockInNAND;
+		dev->initialiseNAND = nandmtd_InitialiseNAND;
+				
+		dev->putSuperFunc = yaffs_MTDPutSuper;
+		
+#ifdef CONFIG_YAFFS_USE_NANDECC
+		dev->useNANDECC = 1;
+#endif
+
+		yaffs_dev = dev;
+		
+#endif
+	}
+
+	init_MUTEX(&dev->grossLock);
+	
+	
+	yaffs_GrossLock(dev);
+	yaffs_GutsInitialise(yaffs_SuperToDevice(sb));
+
+	T(YAFFS_TRACE_OS,("yaffs_read_super: guts initialised\n"));
+
+	// Create root inode
+	inode = yaffs_get_inode(sb, S_IFDIR | 0755, 0,yaffs_Root(yaffs_SuperToDevice(sb)));
+
+	yaffs_GrossUnlock(dev);
+
+	if (!inode)
+		return NULL;
+		
+// added NCB
+	inode->i_op = & yaffs_dir_inode_operations;
+	inode->i_fop = & yaffs_dir_operations;
+
+	T(YAFFS_TRACE_OS,("yaffs_read_super: got root inode\n"));
+		
+
+	root = d_alloc_root(inode);
+
+	T(YAFFS_TRACE_OS,("yaffs_read_super: d_alloc_root done\n"));
+
+	if (!root) {
+		iput(inode);
+		return NULL;
+	}
+	sb->s_root = root;
+
+	T(YAFFS_TRACE_OS,("yaffs_read_super: done\n"));
+	return sb;
+}
+
+static int yaffs_internal_read_super_ram(struct super_block * sb, void * data, int silent)
+{
+	 return yaffs_internal_read_super(1,sb,data,silent) ? 0 : -1;
+}
+static int yaffs_internal_read_super_mtd(struct super_block * sb, void * data, int silent)
+{
+	 return yaffs_internal_read_super(0,sb,data,silent) ? 0 : -1;
+}
+
+
+#ifdef CONFIG_YAFFS_MTD_ENABLED
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static struct super_block *yaffs_read_super(struct file_system_type * fs, int flags, const char *dev_name, void *data)
+{
+
+    return get_sb_bdev(fs, flags, dev_name, data, yaffs_internal_read_super_mtd);
+}
+
+/* changes NCB 2.5.70 */
+//static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, FS_REQUIRES_DEV);
+static struct file_system_type yaffs_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "yaffs",
+	.get_sb		= yaffs_read_super,
+	.kill_sb	= kill_block_super,
+//	.kill_sb	= kill_litter_super,
+	.fs_flags	= FS_REQUIRES_DEV,
+};
+#else
+static struct super_block *yaffs_read_super(struct super_block * sb, void * data, int silent)
+{
+	return yaffs_internal_read_super(0,sb,data,silent);
+}
+
+static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, FS_REQUIRES_DEV);
+#endif
+#endif
+
+#ifdef CONFIG_YAFFS_RAM_ENABLED
+
+//#if defined(CONFIG_KERNEL_2_5)
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+static struct super_block *yaffs_ram_read_super(struct file_system_type * fs, int flags, const char *dev_name, void *data)
+{
+
+    return get_sb_bdev(fs, flags, dev_name, data, yaffs_internal_read_super_ram);
+}
+
+/* changes NCB 2.5.70 */
+//static DECLARE_FSTYPE(yaffs_fs_type, "yaffs", yaffs_read_super, FS_REQUIRES_DEV);
+static struct file_system_type yaffs_fs_type = {
+	.owner		= THIS_MODULE,
+	.name		= "yaffsram",
+	.get_sb		= yaffs_ram_read_super,
+	.kill_sb	= kill_block_super,
+//	.kill_sb	= kill_litter_super,
+	.fs_flags	= FS_SINGLE,
+};
+#else
+static struct super_block *yaffs_ram_read_super(struct super_block * sb, void * data, int silent)
+{
+	return yaffs_internal_read_super(1,sb,data,silent);
+}
+
+static DECLARE_FSTYPE(yaffs_ram_fs_type, "yaffsram", yaffs_ram_read_super, FS_SINGLE);
+#endif
+#endif // CONFIG_YAFFS_RAM_ENABLED
+
+
+static struct proc_dir_entry *my_proc_entry;
+static struct proc_dir_entry *my_proc_ram_write_entry;
+
+static char * yaffs_dump_dev(char *buf,yaffs_Device *dev,char *name)
+{
+	buf +=sprintf(buf,"\nDevice %s\n",name);
+	buf +=sprintf(buf,"startBlock......... %d\n",dev->startBlock);
+	buf +=sprintf(buf,"endBlock........... %d\n",dev->endBlock);
+	buf +=sprintf(buf,"chunkGroupBits..... %d\n",dev->chunkGroupBits);
+	buf +=sprintf(buf,"chunkGroupSize..... %d\n",dev->chunkGroupSize);
+	buf +=sprintf(buf,"nErasedBlocks...... %d\n",dev->nErasedBlocks);
+	buf +=sprintf(buf,"nTnodesCreated..... %d\n",dev->nTnodesCreated);
+	buf +=sprintf(buf,"nFreeTnodes........ %d\n",dev->nFreeTnodes);
+	buf +=sprintf(buf,"nObjectsCreated.... %d\n",dev->nObjectsCreated);
+	buf +=sprintf(buf,"nFreeObjects....... %d\n",dev->nFreeObjects);
+	buf +=sprintf(buf,"nFreeChunks........ %d\n",dev->nFreeChunks);
+	buf +=sprintf(buf,"nPageWrites........ %d\n",dev->nPageWrites);
+	buf +=sprintf(buf,"nPageReads......... %d\n",dev->nPageReads);
+	buf +=sprintf(buf,"nBlockErasures..... %d\n",dev->nBlockErasures);
+	buf +=sprintf(buf,"nGCCopies.......... %d\n",dev->nGCCopies);
+	buf +=sprintf(buf,"garbageCollections. %d\n",dev->garbageCollections);
+	buf +=sprintf(buf,"passiveGCs......... %d\n",dev->passiveGarbageCollections);
+	buf +=sprintf(buf,"nRetriedWrites..... %d\n",dev->nRetriedWrites);
+	buf +=sprintf(buf,"nRetireBlocks...... %d\n",dev->nRetiredBlocks);
+	buf +=sprintf(buf,"eccFixed........... %d\n",dev->eccFixed);
+	buf +=sprintf(buf,"eccUnfixed......... %d\n",dev->eccUnfixed);
+	buf +=sprintf(buf,"tagsEccFixed....... %d\n",dev->tagsEccFixed);
+	buf +=sprintf(buf,"tagsEccUnfixed..... %d\n",dev->tagsEccUnfixed);
+	buf +=sprintf(buf,"cacheHits.......... %d\n",dev->cacheHits);
+	buf +=sprintf(buf,"nDeletedFiles...... %d\n",dev->nDeletedFiles);
+	buf +=sprintf(buf,"nUnlinkedFiles..... %d\n",dev->nUnlinkedFiles);
+	buf +=sprintf(buf,"nBackgroudDeletions %d\n",dev->nBackgroundDeletions);
+	buf +=sprintf(buf,"useNANDECC......... %d\n",dev->useNANDECC);
+
+	
+	return buf;	
+}
+
+static int  yaffs_proc_read(
+        char *page,
+	char **start,
+	off_t offset,
+	int count,
+	int *eof,
+	void *data
+	)
+{
+
+	char my_buffer[3000];
+	char *buf;
+	buf = my_buffer;
+
+	if (offset > 0) return 0;
+
+	/* Fill the buffer and get its length */
+	buf +=sprintf(buf,"YAFFS built:"__DATE__ " "__TIME__"\n%s\n%s\n", yaffs_fs_c_version,yaffs_guts_c_version);
+	
+	if(yaffs_dev) buf = yaffs_dump_dev(buf,yaffs_dev,"yaffs");
+	if(yaffsram_dev) buf = yaffs_dump_dev(buf,yaffsram_dev,"yaffsram");
+	
+
+	strcpy(page,my_buffer);
+	return strlen(my_buffer);
+}
+
+
+static int  yaffs_proc_ram_write(
+        char *page,
+	char **start,
+	off_t offset,
+	int count,
+	int *eof,
+	void *data
+	)
+{
+
+	T(YAFFS_TRACE_OS,( "yaffs write size %d\n",count));
+	return count;
+}
+
+static int __init init_yaffs_fs(void)
+{
+	int error = 0;
+	
+	yaffs_dev = yaffsram_dev = NULL;
+	
+	T(YAFFS_TRACE_OS,( "yaffs " __DATE__ " " __TIME__ " Initialisation\n"));
+#ifdef CONFIG_YAFFS_USE_GENERIC_RW
+	T(YAFFS_TRACE_OS,( "yaffs is using generic read/write (caching)\n"));
+#else
+	T(YAFFS_TRACE_OS,( "yaffs is using direct read/write (uncached)\n"));
+#endif
+
+
+    /* Install the proc_fs entry */
+    my_proc_entry = create_proc_read_entry("yaffs",
+                                           S_IRUGO | S_IFREG,
+					   &proc_root,
+					   yaffs_proc_read,
+					   NULL);
+    if(!my_proc_entry)
+    {
+       return -ENOMEM;
+    }
+
+#ifdef CONFIG_YAFFS_RAM_ENABLED
+#if 0
+    my_proc_ram_write_entry = create_proc_entry("yaffs_ram",
+                                           S_IRUGO | S_IFREG,
+					   &proc_root);
+ 
+    if(!my_proc_ram_write_entry)
+    {
+       return -ENOMEM;
+    }
+    else
+    {
+    	my_proc_ram_write_entry->write_proc = yaffs_proc_ram_write;
+    }
+#endif
+
+    error = register_filesystem(&yaffs_ram_fs_type);
+    if(error)
+    {
+    	return error;
+    }
+#endif //CONFIG_YAFFS_RAM_ENABLED
+
+#ifdef CONFIG_YAFFS_MTD_ENABLED
+	error = register_filesystem(&yaffs_fs_type);
+	if(error)
+	{
+#ifdef CONFIG_YAFFS_RAM_ENABLED
+		unregister_filesystem(&yaffs_ram_fs_type);
+#endif //CONFIG_YAFFS_RAM_ENABLED
+	}
+#endif // CONFIG_YAFFS_MTD_ENABLED
+
+	return error;
+}
+
+static void __exit exit_yaffs_fs(void)
+{
+	T(YAFFS_TRACE_OS,( "yaffs " __DATE__ " " __TIME__ " Clean up\n"));
+
+    remove_proc_entry("yaffs",&proc_root);
+    
+#ifdef CONFIG_YAFFS_RAM_ENABLED
+	unregister_filesystem(&yaffs_ram_fs_type);
+#endif
+#ifdef CONFIG_YAFFS_MTD_ENABLED
+	unregister_filesystem(&yaffs_fs_type);
+#endif
+
+}
+
+module_init(init_yaffs_fs)
+module_exit(exit_yaffs_fs)
+
+MODULE_DESCRIPTION("YAFFS - a NAND specific flash file system");
+MODULE_AUTHOR("Charles Manning, Aleph One Ltd., 2002");
+MODULE_LICENSE("GPL");
+
diff -uNrb linux-2.4.30/fs/yaffs/yaffs_guts.c linux/fs/yaffs/yaffs_guts.c
--- linux-2.4.30/fs/yaffs/yaffs_guts.c	Thu Jan  1 03:00:00 1970
+++ linux/fs/yaffs/yaffs_guts.c	Wed Apr 13 14:58:35 2005
@@ -0,0 +1,5019 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
+ * yaffs_guts.c  The main guts of YAFFS
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+ //yaffs_guts.c
+
+const char *yaffs_guts_c_version="$Id: yaffs_guts.c,v 1.38 2005/01/06 23:28:34 charles Exp $";
+
+#include "yportenv.h"
+
+#include "yaffsinterface.h"
+#include "yaffs_guts.h"
+
+#define YAFFS_PASSIVE_GC_CHUNKS 2
+
+#if 0
+// Use Steven Hill's ECC struff instead
+// External functions for ECC on data
+void nand_calculate_ecc (const u_char *dat, u_char *ecc_code);
+int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc);
+#define yaffs_ECCCalculate(data,ecc) nand_calculate_ecc(data,ecc)
+#define yaffs_ECCCorrect(data,read_ecc,calc_ecc) nand_correct_ecc(data,read_ecc,calc_ecc)
+#else
+#include "yaffs_ecc.h"
+#endif
+
+// countBits is a quick way of counting the number of bits in a byte.
+// ie. countBits[n] holds the number of 1 bits in a byte with the value n.
+
+static const char yaffs_countBitsTable[256] =
+{
+0,1,1,2,1,2,2,3,1,2,2,3,2,3,3,4,
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+1,2,2,3,2,3,3,4,2,3,3,4,3,4,4,5,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+2,3,3,4,3,4,4,5,3,4,4,5,4,5,5,6,
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+3,4,4,5,4,5,5,6,4,5,5,6,5,6,6,7,
+4,5,5,6,5,6,6,7,5,6,6,7,6,7,7,8
+};
+
+static int yaffs_CountBits(__u8 x)
+{
+	int retVal;
+	retVal = yaffs_countBitsTable[x];
+	return retVal;
+}
+
+
+
+// Local prototypes
+static int yaffs_CheckObjectHashSanity(yaffs_Device *dev);
+static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr);
+static void yaffs_GetTagsFromSpare(yaffs_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr);
+static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan);
+
+static yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type);
+static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj);
+static int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force);
+static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId,int markNAND);
+static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj);
+static int yaffs_CheckStructures(void);
+static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit);
+static int yaffs_DoGenericObjectDeletion(yaffs_Object *in);
+
+static yaffs_BlockInfo *yaffs_GetBlockInfo(yaffs_Device *dev,int blockNo);
+
+// Robustification
+static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND);
+static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND);
+static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND);
+static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare);
+static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_Spare *spare);
+
+static int  yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND);
+
+static int yaffs_UnlinkWorker(yaffs_Object *obj);
+static void yaffs_AbortHalfCreatedObject(yaffs_Object *obj);
+
+static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1);
+
+
+
+loff_t yaffs_GetFileSize(yaffs_Object *obj);
+
+static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted);
+static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted);
+
+
+static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve);
+
+#ifdef YAFFS_PARANOID
+static int yaffs_CheckFileSanity(yaffs_Object *in);
+#else
+#define yaffs_CheckFileSanity(in)
+#endif
+
+static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in);
+static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId);
+
+// Chunk bitmap manipulations
+
+static __inline __u8 *yaffs_BlockBits(yaffs_Device *dev, int blk)
+{
+	if(blk < dev->startBlock || blk > dev->endBlock)
+	{
+		T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: BlockBits block %d is not valid" TENDSTR),blk));
+		YBUG();
+	}
+	return dev->chunkBits + (dev->chunkBitmapStride * (blk - dev->startBlock));
+}
+
+static __inline__ void yaffs_ClearChunkBits(yaffs_Device *dev,int blk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev,blk);
+
+	 memset(blkBits,0,dev->chunkBitmapStride);
+}
+
+static __inline__ void yaffs_ClearChunkBit(yaffs_Device *dev,int blk,int chunk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev,blk);
+	
+	blkBits[chunk/8] &=  ~ (1<<(chunk & 7));
+}
+
+static __inline__ void yaffs_SetChunkBit(yaffs_Device *dev,int blk,int chunk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev,blk);
+
+	blkBits[chunk/8] |=   (1<<(chunk & 7));
+}
+
+static __inline__ int yaffs_CheckChunkBit(yaffs_Device *dev,int blk,int chunk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev,blk);
+	return (blkBits[chunk/8] &  (1<<(chunk & 7))) ? 1 :0;
+}
+
+static __inline__ int yaffs_StillSomeChunkBits(yaffs_Device *dev,int blk)
+{
+	__u8 *blkBits = yaffs_BlockBits(dev,blk);
+	int i;
+	for(i = 0; i < dev->chunkBitmapStride; i++)
+	{
+		if(*blkBits) return 1;
+		blkBits++;
+	}
+	return 0;
+}
+
+// Function to manipulate block info
+static  __inline__ yaffs_BlockInfo* yaffs_GetBlockInfo(yaffs_Device *dev, int blk)
+{
+	if(blk < dev->startBlock || blk > dev->endBlock)
+	{
+		T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs: getBlockInfo block %d is not valid" TENDSTR),blk));
+		YBUG();
+	}
+	return &dev->blockInfo[blk - dev->startBlock];
+}
+
+
+static  __inline__ int yaffs_HashFunction(int n)
+{
+	return (n % YAFFS_NOBJECT_BUCKETS);
+}
+
+
+yaffs_Object *yaffs_Root(yaffs_Device *dev)
+{
+	return dev->rootDir;
+}
+
+yaffs_Object *yaffs_LostNFound(yaffs_Device *dev)
+{
+	return dev->lostNFoundDir;
+}
+
+
+static int yaffs_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare)
+{
+	if(chunkInNAND < dev->startBlock * dev->nChunksPerBlock)
+	{
+		T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d is not valid" TENDSTR),chunkInNAND));
+		return YAFFS_FAIL;
+	}
+
+	dev->nPageWrites++;
+	return dev->writeChunkToNAND(dev,chunkInNAND,data,spare);
+}
+
+
+
+int yaffs_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,
+							int chunkInNAND, 
+							__u8 *data, 
+							yaffs_Spare *spare, 
+							int doErrorCorrection)
+{
+	int retVal;
+	yaffs_Spare localSpare;
+
+	dev->nPageReads++;
+	
+	
+
+	
+	if(!spare && data)
+	{
+		// If we don't have a real spare, then we use a local one.
+		// Need this for the calculation of the ecc
+		spare = &localSpare;
+	}
+	
+
+	if(!dev->useNANDECC)
+	{
+		retVal  = dev->readChunkFromNAND(dev,chunkInNAND,data,spare);
+		if(data && doErrorCorrection)
+		{
+			// Do ECC correction
+			//Todo handle any errors
+         	int eccResult1,eccResult2;
+        	__u8 calcEcc[3];
+                
+			yaffs_ECCCalculate(data,calcEcc);
+			eccResult1 = yaffs_ECCCorrect (data,spare->ecc1, calcEcc);
+			yaffs_ECCCalculate(&data[256],calcEcc);
+			eccResult2 = yaffs_ECCCorrect(&data[256],spare->ecc2, calcEcc);
+
+			if(eccResult1>0)
+			{
+				T(YAFFS_TRACE_ERROR, (TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));
+				dev->eccFixed++;
+			}
+			else if(eccResult1<0)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));
+				dev->eccUnfixed++;
+			}
+
+			if(eccResult2>0)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));
+				dev->eccFixed++;
+			}
+			else if(eccResult2<0)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));
+				dev->eccUnfixed++;
+			}
+
+			if(eccResult1 || eccResult2)
+			{
+				// Hoosterman, we had a data problem on this page
+				yaffs_HandleReadDataError(dev,chunkInNAND);
+			}
+		}
+	}
+	else
+	{
+        // Must allocate enough memory for spare+2*sizeof(int) for ecc results from device.
+    	struct yaffs_NANDSpare nspare;
+		retVal  = dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare*)&nspare);
+		memcpy (spare, &nspare, sizeof(yaffs_Spare));
+		if(data && doErrorCorrection)
+		{
+			if(nspare.eccres1>0)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:0" TENDSTR),chunkInNAND));
+			}
+			else if(nspare.eccres1<0)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:0" TENDSTR),chunkInNAND));
+			}
+
+			if(nspare.eccres2>0)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error fix performed on chunk %d:1" TENDSTR),chunkInNAND));
+			}
+			else if(nspare.eccres2<0)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>>ecc error unfixed on chunk %d:1" TENDSTR),chunkInNAND));
+			}
+
+			if(nspare.eccres1 || nspare.eccres2)
+			{
+				// Hoosterman, we had a data problem on this page
+				yaffs_HandleReadDataError(dev,chunkInNAND);
+			}
+
+		}
+	}
+	return retVal;
+}
+
+
+static int yaffs_CheckChunkErased(struct yaffs_DeviceStruct *dev,int chunkInNAND)
+{
+
+	static int init = 0;
+	static __u8 cmpbuf[YAFFS_BYTES_PER_CHUNK];
+	static __u8 data[YAFFS_BYTES_PER_CHUNK];
+    // Might as well always allocate the larger size for dev->useNANDECC == true;
+	static __u8 spare[sizeof(struct yaffs_NANDSpare)];	
+
+  	dev->readChunkFromNAND(dev,chunkInNAND,data,(yaffs_Spare *)spare);
+	
+	if(!init)
+	{
+		memset(cmpbuf,0xff,YAFFS_BYTES_PER_CHUNK);
+		init = 1;
+	}
+	
+	if(memcmp(cmpbuf,data,YAFFS_BYTES_PER_CHUNK)) return  YAFFS_FAIL;
+	if(memcmp(cmpbuf,spare,16)) return YAFFS_FAIL;
+
+	
+	return YAFFS_OK;
+	
+}
+
+
+
+int yaffs_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND)
+{
+	dev->nBlockErasures++;
+	return dev->eraseBlockInNAND(dev,blockInNAND);
+}
+
+int yaffs_InitialiseNAND(struct yaffs_DeviceStruct *dev)
+{
+	return dev->initialiseNAND(dev);
+}
+
+static int yaffs_WriteNewChunkToNAND(struct yaffs_DeviceStruct *dev, const __u8 *data, yaffs_Spare *spare,int useReserve)
+{
+	int chunk;
+	
+	int writeOk = 1;
+	int attempts = 0;
+	
+	unsigned char rbData[YAFFS_BYTES_PER_CHUNK];
+	yaffs_Spare rbSpare;
+	
+	do{
+		chunk = yaffs_AllocateChunk(dev,useReserve);
+	
+		if(chunk >= 0)
+		{
+
+			// First check this chunk is erased...
+#ifndef CONFIG_YAFFS_DISABLE_CHUNK_ERASED_CHECK
+			writeOk = yaffs_CheckChunkErased(dev,chunk);
+#endif		
+			if(!writeOk)
+			{
+				T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs chunk %d was not erased" TENDSTR),chunk));
+			}
+			else
+			{
+				writeOk =  yaffs_WriteChunkToNAND(dev,chunk,data,spare);
+			}
+			attempts++;
+			if(writeOk)
+			{
+				// Readback & verify
+				// If verify fails, then delete this chunk and try again
+				// To verify we compare everything except the block and 
+				// page status bytes.
+				// NB We check a raw read without ECC correction applied
+				yaffs_ReadChunkFromNAND(dev,chunk,rbData,&rbSpare,0);
+				
+#ifndef CONFIG_YAFFS_DISABLE_WRITE_VERIFY
+				if(!yaffs_VerifyCompare(data,rbData,spare,&rbSpare))
+				{
+					// Didn't verify
+					T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write verify failed on chunk %d" TENDSTR), chunk));
+
+					writeOk = 0;
+				}	
+#endif				
+				
+			}
+			if(writeOk)
+			{
+				// Copy the data into the write buffer.
+				// NB We do this at the end to prevent duplicates in the case of a write error.
+				//Todo
+				yaffs_HandleWriteChunkOk(dev,chunk,data,spare);
+			}
+			else
+			{
+				yaffs_HandleWriteChunkError(dev,chunk);
+			}
+		}
+		
+	} while(chunk >= 0 && ! writeOk);
+	
+	if(attempts > 1)
+	{
+		T(YAFFS_TRACE_ERROR,(TSTR("**>> yaffs write required %d attempts" TENDSTR),attempts));
+		dev->nRetriedWrites+= (attempts - 1);	
+	}
+	
+	return chunk;
+}
+
+///
+// Functions for robustisizing
+//
+//
+
+static void yaffs_RetireBlock(yaffs_Device *dev,int blockInNAND)
+{
+	// Ding the blockStatus in the first two pages of the block.
+	
+	yaffs_Spare spare;
+
+	memset(&spare, 0xff,sizeof(yaffs_Spare));
+
+	spare.blockStatus = 0;
+	
+	// TODO change this retirement marking for other NAND types
+	yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock, NULL , &spare);
+	yaffs_WriteChunkToNAND(dev, blockInNAND * dev->nChunksPerBlock + 1, NULL , &spare);
+	
+	yaffs_GetBlockInfo(dev,blockInNAND)->blockState = YAFFS_BLOCK_STATE_DEAD;
+	dev->nRetiredBlocks++;
+}
+
+
+
+static int yaffs_RewriteBufferedBlock(yaffs_Device *dev)
+{
+	dev->doingBufferedBlockRewrite = 1;
+	//
+	//	Remove erased chunks
+	//  Rewrite existing chunks to a new block
+	//	Set current write block to the new block
+	
+	dev->doingBufferedBlockRewrite = 0;
+	
+	return 1;
+}
+
+
+static void yaffs_HandleReadDataError(yaffs_Device *dev,int chunkInNAND)
+{
+	int blockInNAND = chunkInNAND/dev->nChunksPerBlock;
+
+	// Mark the block for retirement
+	yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
+	T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>>Block %d marked for retirement" TENDSTR),blockInNAND));
+
+
+	//TODO	
+	// Just do a garbage collection on the affected block then retire the block
+	// NB recursion
+}
+
+
+static void yaffs_CheckWrittenBlock(yaffs_Device *dev,int chunkInNAND)
+{
+}
+
+static void yaffs_HandleWriteChunkOk(yaffs_Device *dev,int chunkInNAND,const __u8 *data, const yaffs_Spare *spare)
+{
+}
+
+static void yaffs_HandleUpdateChunk(yaffs_Device *dev,int chunkInNAND, const yaffs_Spare *spare)
+{
+}
+
+static void yaffs_HandleWriteChunkError(yaffs_Device *dev,int chunkInNAND)
+{
+	int blockInNAND = chunkInNAND/dev->nChunksPerBlock;
+
+	// Mark the block for retirement
+	yaffs_GetBlockInfo(dev,blockInNAND)->needsRetiring = 1;
+	// Delete the chunk
+	yaffs_DeleteChunk(dev,chunkInNAND,1);
+}
+
+
+
+
+static int yaffs_VerifyCompare(const __u8 *d0, const __u8 * d1, const yaffs_Spare *s0, const yaffs_Spare *s1)
+{
+
+
+	if( memcmp(d0,d1,YAFFS_BYTES_PER_CHUNK) != 0 ||
+		s0->tagByte0 != s1->tagByte0 ||
+		s0->tagByte1 != s1->tagByte1 ||
+		s0->tagByte2 != s1->tagByte2 ||
+		s0->tagByte3 != s1->tagByte3 ||
+		s0->tagByte4 != s1->tagByte4 ||
+		s0->tagByte5 != s1->tagByte5 ||
+		s0->tagByte6 != s1->tagByte6 ||
+		s0->tagByte7 != s1->tagByte7 ||
+		s0->ecc1[0]  != s1->ecc1[0]  ||
+		s0->ecc1[1]  != s1->ecc1[1]  ||
+		s0->ecc1[2]  != s1->ecc1[2]  ||
+		s0->ecc2[0]  != s1->ecc2[0]  ||
+		s0->ecc2[1]  != s1->ecc2[1]  ||
+		s0->ecc2[2]  != s1->ecc2[2] )
+		{
+			return 0;
+		}
+	
+	return 1;
+}
+
+
+///////////////////////// Object management //////////////////
+// List of spare objects
+// The list is hooked together using the first pointer
+// in the object
+
+// static yaffs_Object *yaffs_freeObjects = NULL;
+
+// static int yaffs_nFreeObjects;
+
+// static yaffs_ObjectList *yaffs_allocatedObjectList = NULL;
+
+// static yaffs_ObjectBucket yaffs_objectBucket[YAFFS_NOBJECT_BUCKETS];
+
+
+static __u16 yaffs_CalcNameSum(const char *name)
+{
+	__u16 sum = 0;
+	__u16 i = 1;
+	
+	__u8 *bname = (__u8 *)name;
+	if(bname)
+	{
+		while ((*bname) && (i <=YAFFS_MAX_NAME_LENGTH))
+		{
+
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
+			sum += toupper(*bname) * i;
+#else
+			sum += (*bname) * i;
+#endif
+			i++;
+			bname++;
+		}
+	}
+	return sum;
+}
+
+void yaffs_SetObjectName(yaffs_Object *obj, const char *name)
+{
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
+					if(name && strlen(name) <= YAFFS_SHORT_NAME_LENGTH)
+					{
+						strcpy(obj->shortName,name);
+					}
+					else
+					{
+						obj->shortName[0]='\0';
+					}
+#endif
+					obj->sum = yaffs_CalcNameSum(name);
+}
+
+void yaffs_CalcECC(const __u8 *data, yaffs_Spare *spare)
+{
+	yaffs_ECCCalculate(data , spare->ecc1);
+	yaffs_ECCCalculate(&data[256] , spare->ecc2);
+}
+
+void yaffs_CalcTagsECC(yaffs_Tags *tags)
+{
+	// Calculate an ecc
+	
+	unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
+	unsigned  i,j;
+	unsigned  ecc = 0;
+	unsigned bit = 0;
+
+	tags->ecc = 0;
+	
+	for(i = 0; i < 8; i++)
+	{
+		for(j = 1; j &0xff; j<<=1)
+		{
+			bit++;
+			if(b[i] & j)
+			{
+				ecc ^= bit;
+			}
+		}
+	}
+	
+	tags->ecc = ecc;
+	
+	
+}
+
+int  yaffs_CheckECCOnTags(yaffs_Tags *tags)
+{
+	unsigned ecc = tags->ecc;
+	
+	yaffs_CalcTagsECC(tags);
+	
+	ecc ^= tags->ecc;
+	
+	if(ecc && ecc <= 64)
+	{
+		// TODO: Handle the failure better. Retire?
+		unsigned char *b = ((yaffs_TagsUnion *)tags)->asBytes;
+
+		ecc--;
+				
+		b[ecc / 8] ^= (1 << (ecc & 7));
+		
+		// Now recvalc the ecc
+		yaffs_CalcTagsECC(tags);
+		
+		return 1; // recovered error
+	}
+	else if(ecc)
+	{
+		// Wierd ecc failure value
+		// TODO Need to do somethiong here
+		return -1; //unrecovered error
+	}
+	
+	return 0;
+}
+
+
+///////////////////////// TNODES ///////////////////////
+
+// List of spare tnodes
+// The list is hooked together using the first pointer
+// in the tnode.
+
+//static yaffs_Tnode *yaffs_freeTnodes = NULL;
+
+// static int yaffs_nFreeTnodes;
+
+//static yaffs_TnodeList *yaffs_allocatedTnodeList = NULL;
+
+
+
+// yaffs_CreateTnodes creates a bunch more tnodes and
+// adds them to the tnode free list.
+// Don't use this function directly
+
+static int yaffs_CreateTnodes(yaffs_Device *dev,int nTnodes)
+{
+    int i;
+    yaffs_Tnode *newTnodes;
+    yaffs_TnodeList *tnl;
+    
+    if(nTnodes < 1) return YAFFS_OK;
+   
+	// make these things
+	
+    newTnodes = YMALLOC(nTnodes * sizeof(yaffs_Tnode));
+   
+    if (!newTnodes)
+    {
+		T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not allocate Tnodes"TENDSTR)));
+		return YAFFS_FAIL;
+    }
+    
+    // Hook them into the free list
+    for(i = 0; i < nTnodes - 1; i++)
+    {
+    	newTnodes[i].internal[0] = &newTnodes[i+1];
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+    	newTnodes[i].internal[YAFFS_NTNODES_INTERNAL] = 1;
+#endif
+    }
+    	
+	newTnodes[nTnodes - 1].internal[0] = dev->freeTnodes;
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+   	newTnodes[nTnodes - 1].internal[YAFFS_NTNODES_INTERNAL] = 1;
+#endif
+	dev->freeTnodes = newTnodes;
+	dev->nFreeTnodes+= nTnodes;
+	dev->nTnodesCreated += nTnodes;
+
+	// Now add this bunch of tnodes to a list for freeing up.
+	// NB If we can't add this to the management list it isn't fatal
+	// but it just means we can't free this bunch of tnodes later.
+	tnl = YMALLOC(sizeof(yaffs_TnodeList));
+	if(!tnl)
+	{
+		T(YAFFS_TRACE_ERROR,(TSTR("yaffs: Could not add tnodes to management list" TENDSTR)));
+		
+	}
+	else
+	{
+		tnl->tnodes = newTnodes;
+		tnl->next = dev->allocatedTnodeList;
+		dev->allocatedTnodeList = tnl;
+	}
+
+
+	T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Tnodes added" TENDSTR)));
+
+
+	return YAFFS_OK;
+}
+
+
+// GetTnode gets us a clean tnode. Tries to make allocate more if we run out
+static yaffs_Tnode *yaffs_GetTnode(yaffs_Device *dev)
+{
+	yaffs_Tnode *tn = NULL;
+	
+	// If there are none left make more
+	if(!dev->freeTnodes)
+	{
+		yaffs_CreateTnodes(dev,YAFFS_ALLOCATION_NTNODES);
+	}
+	
+	if(dev->freeTnodes)
+	{
+		tn = dev->freeTnodes;
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+    	if(tn->internal[YAFFS_NTNODES_INTERNAL] != 1)
+		{
+			// Hoosterman, this thing looks like it isn't in the list
+				T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Tnode list bug 1" TENDSTR)));
+		}
+#endif
+		dev->freeTnodes = dev->freeTnodes->internal[0];
+		dev->nFreeTnodes--;
+		// zero out
+		memset(tn,0,sizeof(yaffs_Tnode));
+	}
+	
+
+	return tn;
+}
+
+
+// FreeTnode frees up a tnode and puts it back on the free list
+static void yaffs_FreeTnode(yaffs_Device*dev, yaffs_Tnode *tn)
+{
+	if(tn)
+	{
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+    	if(tn->internal[YAFFS_NTNODES_INTERNAL] != 0)
+		{
+			// Hoosterman, this thing looks like it is already in the list
+				T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: Tnode list bug 2" TENDSTR)));
+		}
+		tn->internal[YAFFS_NTNODES_INTERNAL] = 1;
+#endif
+		tn->internal[0] = dev->freeTnodes;
+		dev->freeTnodes = tn;
+		dev->nFreeTnodes++;
+	}
+}
+
+
+static void yaffs_DeinitialiseTnodes(yaffs_Device*dev)
+{
+	// Free the list of allocated tnodes
+	yaffs_TnodeList *tmp;
+		
+	while(dev->allocatedTnodeList)
+	{
+		tmp =  dev->allocatedTnodeList->next;
+
+		YFREE(dev->allocatedTnodeList->tnodes);
+		YFREE(dev->allocatedTnodeList);
+		dev->allocatedTnodeList	= tmp;
+		
+	}
+	
+	dev->freeTnodes = NULL;
+	dev->nFreeTnodes = 0;
+}
+
+static void yaffs_InitialiseTnodes(yaffs_Device*dev)
+{
+	dev->allocatedTnodeList = NULL;
+	dev->freeTnodes = NULL;
+	dev->nFreeTnodes = 0;
+	dev->nTnodesCreated = 0;
+
+}
+
+#if 0
+void yaffs_TnodeTest(yaffs_Device *dev)
+{
+
+	int i;
+	int j;
+	yaffs_Tnode *tn[1000];
+	
+	YINFO("Testing TNodes");
+	
+	for(j = 0; j < 50; j++)
+	{
+		for(i = 0; i < 1000; i++)
+		{
+			tn[i] = yaffs_GetTnode(dev);
+			if(!tn[i])
+			{
+				YALERT("Getting tnode failed");
+			}
+		}
+		for(i = 0; i < 1000; i+=3)
+		{
+			yaffs_FreeTnode(dev,tn[i]);
+			tn[i] = NULL;
+		}
+		
+	}
+}
+#endif
+
+
+////////////////// END OF TNODE MANIPULATION ///////////////////////////
+
+/////////////// Functions to manipulate the look-up tree (made up of tnodes)
+// The look up tree is represented by the top tnode and the number of topLevel
+// in the tree. 0 means only the level 0 tnode is in the tree.
+
+
+// FindLevel0Tnode finds the level 0 tnode, if one exists.
+// Used when reading.....
+static yaffs_Tnode *yaffs_FindLevel0Tnode(yaffs_Device *dev,yaffs_FileStructure *fStruct, __u32 chunkId)
+{
+	
+	yaffs_Tnode *tn = fStruct->top;
+	__u32 i;
+	int requiredTallness;	
+	int level = fStruct->topLevel;
+	
+	// Check sane level and chunk Id
+	if(level < 0 || level > YAFFS_TNODES_MAX_LEVEL)
+	{
+//		char str[50];
+//		sprintf(str,"Bad level %d",level);
+//		YALERT(str);
+		return NULL;
+	}
+	
+	if(chunkId > YAFFS_MAX_CHUNK_ID)
+	{
+//		char str[50];
+//		sprintf(str,"Bad chunkId %d",chunkId);
+//		YALERT(str);
+		return NULL;
+	}
+
+	// First check we're tall enough (ie enough topLevel)
+	
+	i = chunkId >> (/*dev->chunkGroupBits  + */YAFFS_TNODES_LEVEL0_BITS);
+	requiredTallness = 0;
+	while(i)
+	{
+		i >>= YAFFS_TNODES_INTERNAL_BITS;
+		requiredTallness++;
+	}
+	
+	
+	if(requiredTallness > fStruct->topLevel)
+	{
+		// Not tall enough, so we can't find it, return NULL.
+		return NULL;
+	}
+		
+	
+	// Traverse down to level 0
+	while (level > 0 && tn)
+	{
+	    tn = tn->internal[(chunkId >>(/* dev->chunkGroupBits + */ YAFFS_TNODES_LEVEL0_BITS + (level-1) * YAFFS_TNODES_INTERNAL_BITS)) & 
+	                       YAFFS_TNODES_INTERNAL_MASK]; 
+		level--;
+	
+	}
+	
+	return tn;		
+}
+
+// AddOrFindLevel0Tnode finds the level 0 tnode if it exists, otherwise first expands the tree.
+// This happens in two steps:
+//  1. If the tree isn't tall enough, then make it taller.
+//  2. Scan down the tree towards the level 0 tnode adding tnodes if required.
+//
+// Used when modifying the tree.
+//
+static yaffs_Tnode *yaffs_AddOrFindLevel0Tnode(yaffs_Device *dev, yaffs_FileStructure *fStruct, __u32 chunkId)
+{
+	
+	yaffs_Tnode *tn; 
+	
+	int requiredTallness;
+	int i;
+	int l;
+	
+	__u32 x;
+		
+	
+	//T((TSTR("AddOrFind topLevel=%d, chunk=%d"),fStruct->topLevel,chunkId));
+	
+	// Check sane level and page Id
+	if(fStruct->topLevel < 0 || fStruct->topLevel > YAFFS_TNODES_MAX_LEVEL)
+	{
+//		char str[50];
+//		sprintf(str,"Bad level %d",fStruct->topLevel);
+//		YALERT(str);
+		return NULL;
+	}
+	
+	if(chunkId > YAFFS_MAX_CHUNK_ID)
+	{
+//		char str[50];
+//		sprintf(str,"Bad chunkId %d",chunkId);
+//		YALERT(str);
+		return NULL;
+	}
+	
+	// First check we're tall enough (ie enough topLevel)
+	
+	x = chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS);
+	requiredTallness = 0;
+	while(x)
+	{
+		x >>= YAFFS_TNODES_INTERNAL_BITS;
+		requiredTallness++;
+	}
+	
+	//T((TSTR(" required=%d"),requiredTallness));
+	
+	
+	if(requiredTallness > fStruct->topLevel)
+	{
+		// Not tall enough,gotta make the tree taller
+		for(i = fStruct->topLevel; i < requiredTallness; i++)
+		{
+			//T((TSTR(" add new top")));
+			
+			tn = yaffs_GetTnode(dev);
+			
+			if(tn)
+			{
+				tn->internal[0] = fStruct->top;
+				fStruct->top = tn;
+			}
+			else
+			{
+					T(YAFFS_TRACE_ERROR,(TSTR("yaffs: no more tnodes" TENDSTR)));
+			}
+		}
+		
+		fStruct->topLevel = requiredTallness;
+	}
+	
+	
+	// Traverse down to level 0, adding anything we need
+	
+	l = fStruct->topLevel;
+	tn = fStruct->top;
+	while (l > 0 && tn)
+	{
+		x = (chunkId >> (/*dev->chunkGroupBits + */YAFFS_TNODES_LEVEL0_BITS + (l-1) * YAFFS_TNODES_INTERNAL_BITS)) & 
+	                       YAFFS_TNODES_INTERNAL_MASK;
+			       
+		//T((TSTR(" [%d:%d]"),l,i));
+		
+	    if(!tn->internal[x])
+	    {
+	    	//T((TSTR(" added")));
+		
+	    	tn->internal[x] = yaffs_GetTnode(dev);
+	    }
+	    
+	    tn = 	tn->internal[x];
+		l--;
+	
+	}
+	
+	//TSTR(TENDSTR)));
+	
+	return tn;		
+}
+
+// DeleteWorker scans backwards through the tnode tree and deletes all the
+// chunks and tnodes in the file
+// Returns 1 if the tree was deleted. Returns 0 if it stopped early due to hitting the limit and the delete is incomplete.
+
+static int yaffs_DeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset,int *limit)
+{
+	int i;
+	int chunkInInode;
+	int theChunk;
+	yaffs_Tags tags;
+	int found;
+	int chunkDeleted;
+	int allDone = 1;
+	
+	
+	if(tn)
+	{
+		if(level > 0)
+		{
+		
+			for(i = YAFFS_NTNODES_INTERNAL -1; allDone && i >= 0; i--)
+			{
+			    if(tn->internal[i])
+		    	{
+					if(limit && (*limit) < 0)
+					{
+						allDone = 0;
+					}
+					else
+					{
+						allDone = yaffs_DeleteWorker(in,tn->internal[i],level - 1,
+										(chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i ,limit);
+					}
+					if(allDone)
+					{
+						yaffs_FreeTnode(in->myDev,tn->internal[i]);
+			    		tn->internal[i] = NULL;
+					}
+			    }
+		    
+			}
+			return (allDone) ? 1 : 0;
+		}
+		else if(level == 0)
+		{
+			int hitLimit = 0;
+			
+			for(i = YAFFS_NTNODES_LEVEL0 -1; i >= 0 && !hitLimit; i--)
+			{
+			    if(tn->level0[i])
+		    	{
+					int j;
+					
+					chunkInInode = (chunkOffset << YAFFS_TNODES_LEVEL0_BITS ) + i;
+					
+					theChunk =  tn->level0[i] << in->myDev->chunkGroupBits;
+
+					// Now we need to search for it
+					for(j = 0,found = 0; theChunk && j < in->myDev->chunkGroupSize && !found; j++)
+					{
+						yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,&tags,&chunkDeleted);
+						if(yaffs_TagsMatch(&tags,in->objectId,chunkInInode,chunkDeleted))
+						{
+							// found it;
+							found = 1;
+					
+						}
+						else
+						{
+							theChunk++;
+						}
+					}
+					
+					if(found)
+					{
+						yaffs_DeleteChunk(in->myDev,theChunk,1);
+						in->nDataChunks--;
+						if(limit)
+						{ 
+							*limit = *limit-1;
+							if(*limit <= 0) 
+							{ 
+								hitLimit = 1;
+							}
+						}
+					
+					}
+					
+			    	tn->level0[i] = 0;
+			    }
+		    
+			}
+			return (i < 0) ? 1 : 0;
+
+			
+		}
+		
+	}
+	
+	return 1;
+	
+}
+
+// SoftDeleteWorker scans backwards through the tnode tree and soft deletes all the chunks in the file.
+// All soft deleting does is increment the block's softdelete count and pulls the chunk out
+// of the tnode.
+// THus, essentially this is the same as DeleteWorker except that the chunks are soft deleted.
+//
+static int yaffs_SoftDeleteWorker(yaffs_Object *in, yaffs_Tnode *tn, __u32 level, int chunkOffset)
+{
+	int i;
+	int chunkInInode;
+	int theChunk;
+	yaffs_BlockInfo *theBlock;
+	yaffs_Tags tags;
+	int found;
+	int chunkDeleted;
+	int allDone = 1;
+	
+	
+	if(tn)
+	{
+		if(level > 0)
+		{
+		
+			for(i = YAFFS_NTNODES_INTERNAL -1; allDone && i >= 0; i--)
+			{
+			    if(tn->internal[i])
+		    	{
+						allDone = yaffs_SoftDeleteWorker(in,tn->internal[i],level - 1,
+										(chunkOffset << YAFFS_TNODES_INTERNAL_BITS ) + i);
+					if(allDone)
+					{
+						yaffs_FreeTnode(in->myDev,tn->internal[i]);
+			    		tn->internal[i] = NULL;
+					}
+					else
+					{
+						//Hoosterman... how could this happen.
+					}			    
+				}		    
+			}
+			return (allDone) ? 1 : 0;
+		}
+		else if(level == 0)
+		{
+			
+			for(i = YAFFS_NTNODES_LEVEL0 -1; i >=0; i--)
+			{
+			    if(tn->level0[i])
+		    	{
+					// Note this does not find the real chunk, only the chunk group.
+					// We make an assumption that a chunk group is niot larger than a block.
+					theChunk =  (tn->level0[i] << in->myDev->chunkGroupBits);
+					T(YAFFS_TRACE_SCAN,(TSTR("soft delete tch %d cgb %d chunk %d" TENDSTR),
+						tn->level0[i],in->myDev->chunkGroupBits,theChunk));
+						
+					theBlock =	yaffs_GetBlockInfo(in->myDev,  theChunk/in->myDev->nChunksPerBlock);
+					if(theBlock)
+					{
+						theBlock->softDeletions++;
+					}
+			    	tn->level0[i] = 0;
+			    }
+		    
+			}
+			return 1;
+			
+		}
+		
+	}
+	
+	return 1;
+		
+}
+
+
+
+static void yaffs_SoftDeleteFile(yaffs_Object *obj)
+{
+	if(obj->deleted &&
+	   obj->variantType == YAFFS_OBJECT_TYPE_FILE &&
+	   !obj->softDeleted)
+	{
+		if(obj->nDataChunks <= 0)
+		{
+				// Empty file, just delete it immediately
+				yaffs_FreeTnode(obj->myDev,obj->variant.fileVariant.top);
+				obj->variant.fileVariant.top = NULL;
+				T(YAFFS_TRACE_TRACING,(TSTR("yaffs: Deleting empty file %d" TENDSTR),obj->objectId));
+				yaffs_DoGenericObjectDeletion(obj);	
+		}
+		else
+		{
+			yaffs_SoftDeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0);
+			obj->softDeleted = 1;
+		}
+	}
+}
+
+
+
+
+
+// Pruning removes any part of the file structure tree that is beyond the
+// bounds of the file (ie that does not point to chunks).
+//
+// A file should only get pruned when its size is reduced.
+//
+// Before pruning, the chunks must be pulled from the tree and the
+// level 0 tnode entries must be zeroed out.
+// Could also use this for file deletion, but that's probably better handled
+// by a special case.
+
+// yaffs_PruneWorker should only be called by yaffs_PruneFileStructure()
+
+static yaffs_Tnode *yaffs_PruneWorker(yaffs_Device *dev, yaffs_Tnode *tn, __u32 level, int del0)
+{
+	int i;
+	int hasData;
+	
+	if(tn)
+	{
+		hasData = 0;
+		
+		for(i = 0; i < YAFFS_NTNODES_INTERNAL; i++)
+		{
+		    if(tn->internal[i] && level > 0)
+		    {
+		    	tn->internal[i] = yaffs_PruneWorker(dev,tn->internal[i],level - 1, ( i == 0) ? del0 : 1);
+		    }
+		    
+		    if(tn->internal[i])
+		    {
+		    	hasData++;
+			}
+		}
+		
+		if(hasData == 0 && del0)
+		{
+			// Free and return NULL
+			
+			yaffs_FreeTnode(dev,tn);
+			tn = NULL;
+		}
+		
+	}
+
+	return tn;
+	
+}
+
+static int yaffs_PruneFileStructure(yaffs_Device *dev, yaffs_FileStructure *fStruct)
+{
+	int i;
+	int hasData;
+	int done = 0;
+	yaffs_Tnode *tn;
+	
+	if(fStruct->topLevel > 0)
+	{
+		fStruct->top = yaffs_PruneWorker(dev,fStruct->top, fStruct->topLevel,0);
+		
+		// Now we have a tree with all the non-zero branches NULL but the height
+		// is the same as it was.
+		// Let's see if we can trim internal tnodes to shorten the tree.
+		// We can do this if only the 0th element in the tnode is in use 
+		// (ie all the non-zero are NULL)
+		
+		while(fStruct->topLevel && !done)
+		{
+			tn = fStruct->top;
+			
+			hasData = 0;
+			for(i = 1; i <YAFFS_NTNODES_INTERNAL; i++)
+			{
+				if(tn->internal[i])
+		    	{
+		    		hasData++;
+				}
+			}
+			
+			if(!hasData)
+			{
+				fStruct->top = tn->internal[0];
+				fStruct->topLevel--;
+				yaffs_FreeTnode(dev,tn);
+			}
+			else
+			{
+				done = 1;
+			}
+		}
+	}
+	
+	return YAFFS_OK;
+}
+
+
+
+
+
+/////////////////////// End of File Structure functions. /////////////////
+
+// yaffs_CreateFreeObjects creates a bunch more objects and
+// adds them to the object free list.
+static int yaffs_CreateFreeObjects(yaffs_Device *dev, int nObjects)
+{
+    int i;
+    yaffs_Object *newObjects;
+    yaffs_ObjectList *list;
+    
+    if(nObjects < 1) return YAFFS_OK;
+   
+	// make these things
+	
+    newObjects = YMALLOC(nObjects * sizeof(yaffs_Object));
+   
+    if (!newObjects)
+    {
+		T(YAFFS_TRACE_ALLOCATE,(TSTR("yaffs: Could not allocate more objects" TENDSTR)));
+		return YAFFS_FAIL;
+    }
+    
+    // Hook them into the free list
+    for(i = 0; i < nObjects - 1; i++)
+    {
+    	newObjects[i].siblings.next = (struct list_head *)(&newObjects[i+1]);
+    }
+    	
+	newObjects[nObjects - 1].siblings.next = (void *)dev->freeObjects;
+	dev->freeObjects = newObjects;
+	dev->nFreeObjects+= nObjects;
+	dev->nObjectsCreated+= nObjects;
+	
+	// Now add this bunch of Objects to a list for freeing up.
+	
+	list = YMALLOC(sizeof(yaffs_ObjectList));
+	if(!list)
+	{
+		T(YAFFS_TRACE_ALLOCATE,(TSTR("Could not add objects to management list" TENDSTR)));
+	}
+	else
+	{
+		list->objects = newObjects;
+		list->next = dev->allocatedObjectList;
+		dev->allocatedObjectList = list;
+	}
+	
+	
+	
+	return YAFFS_OK;
+}
+
+
+// AllocateEmptyObject gets us a clean Object. Tries to make allocate more if we run out
+static yaffs_Object *yaffs_AllocateEmptyObject(yaffs_Device *dev)
+{
+	yaffs_Object *tn = NULL;
+	
+	// If there are none left make more
+	if(!dev->freeObjects)
+	{
+		yaffs_CreateFreeObjects(dev,YAFFS_ALLOCATION_NOBJECTS);
+	}
+	
+	if(dev->freeObjects)
+	{
+		tn = dev->freeObjects;
+		dev->freeObjects = (yaffs_Object *)(dev->freeObjects->siblings.next);
+		dev->nFreeObjects--;
+		
+		// Now sweeten it up...
+	
+		memset(tn,0,sizeof(yaffs_Object));
+		tn->myDev = dev;
+		tn->chunkId = -1;
+		tn->variantType = YAFFS_OBJECT_TYPE_UNKNOWN;
+		INIT_LIST_HEAD(&(tn->hardLinks));
+		INIT_LIST_HEAD(&(tn->hashLink));
+		INIT_LIST_HEAD(&tn->siblings);
+		
+		// Add it to the lost and found directory.
+		// NB Can't put root or lostNFound in lostNFound so
+		// check if lostNFound exists first
+		if(dev->lostNFoundDir)
+		{
+			yaffs_AddObjectToDirectory(dev->lostNFoundDir,tn);	
+		}
+	}
+	
+
+	return tn;
+}
+
+static yaffs_Object *yaffs_CreateFakeDirectory(yaffs_Device *dev,int number,__u32 mode)
+{
+
+	yaffs_Object *obj = yaffs_CreateNewObject(dev,number,YAFFS_OBJECT_TYPE_DIRECTORY);		
+	if(obj)
+	{
+		obj->fake = 1;			// it is fake so it has no NAND presence...
+		obj->renameAllowed= 0;	// ... and we're not allowed to rename it...
+		obj->unlinkAllowed= 0;	// ... or unlink it
+		obj->deleted = 0;
+		obj->unlinked = 0;
+		obj->st_mode = mode;
+		obj->myDev = dev;
+		obj->chunkId = 0; // Not a valid chunk.
+	}
+	
+	return obj;
+	
+}
+
+
+static void yaffs_UnhashObject(yaffs_Object *tn)
+{
+	int bucket;
+	yaffs_Device *dev = tn->myDev;
+	
+	
+	// If it is still linked into the bucket list, free from the list
+	if(!list_empty(&tn->hashLink))
+	{
+		list_del_init(&tn->hashLink);
+		bucket =  yaffs_HashFunction(tn->objectId);
+		dev->objectBucket[bucket].count--;
+	}
+	
+}
+
+
+// FreeObject frees up a Object and puts it back on the free list
+static void yaffs_FreeObject(yaffs_Object *tn)
+{
+
+	yaffs_Device *dev = tn->myDev;
+	
+#ifdef  __KERNEL__
+	if(tn->myInode)
+	{
+		// We're still hooked up to a cached inode.
+		// Don't delete now, but mark for later deletion
+		tn->deferedFree = 1;
+		return;
+	}
+#endif
+	
+	yaffs_UnhashObject(tn);
+	
+	// Link into the free list.
+	tn->siblings.next = (struct list_head *)(dev->freeObjects);
+	dev->freeObjects = tn;
+	dev->nFreeObjects++;
+}
+
+
+#ifdef __KERNEL__
+
+void yaffs_HandleDeferedFree(yaffs_Object *obj)
+{
+	if(obj->deferedFree)
+	{
+	   yaffs_FreeObject(obj);
+	}
+}
+
+#endif
+
+
+
+static void yaffs_DeinitialiseObjects(yaffs_Device *dev)
+{
+	// Free the list of allocated Objects
+	
+	yaffs_ObjectList *tmp;
+	
+	while( dev->allocatedObjectList)
+	{
+		tmp =  dev->allocatedObjectList->next;
+		YFREE(dev->allocatedObjectList->objects);
+		YFREE(dev->allocatedObjectList);
+		
+		dev->allocatedObjectList =  tmp;
+	}
+	
+	dev->freeObjects = NULL;
+	dev->nFreeObjects = 0;
+}
+
+static void yaffs_InitialiseObjects(yaffs_Device *dev)
+{
+	int i;
+	
+	dev->allocatedObjectList = NULL;
+	dev->freeObjects = NULL;
+	dev->nFreeObjects = 0;
+	
+	for(i = 0; i < YAFFS_NOBJECT_BUCKETS; i++)
+	{
+		INIT_LIST_HEAD(&dev->objectBucket[i].list);
+		dev->objectBucket[i].count = 0;	
+	}
+
+}
+
+
+
+
+
+
+int yaffs_FindNiceObjectBucket(yaffs_Device *dev)
+{
+	static int x = 0;
+	int i;
+	int l = 999;
+	int lowest = 999999;
+
+		
+	// First let's see if we can find one that's empty.
+	
+	for(i = 0; i < 10 && lowest > 0; i++)
+	 {
+		x++;
+		x %=  YAFFS_NOBJECT_BUCKETS;
+		if(dev->objectBucket[x].count < lowest)
+		{
+			lowest = dev->objectBucket[x].count;
+			l = x;
+		}
+		
+	}
+	
+	// If we didn't find an empty list, then try
+	// looking a bit further for a short one
+	
+	for(i = 0; i < 10 && lowest > 3; i++)
+	 {
+		x++;
+		x %=  YAFFS_NOBJECT_BUCKETS;
+		if(dev->objectBucket[x].count < lowest)
+		{
+			lowest = dev->objectBucket[x].count;
+			l = x;
+		}
+		
+	}
+	
+	return l;
+}
+
+static int yaffs_CreateNewObjectNumber(yaffs_Device *dev)
+{
+	int bucket = yaffs_FindNiceObjectBucket(dev);
+	
+	// Now find an object value that has not already been taken
+	// by scanning the list.
+	
+	int found = 0;
+	struct list_head *i;
+	
+	__u32 n = (__u32)bucket;
+
+	//yaffs_CheckObjectHashSanity();	
+	
+	while(!found)
+	{
+		found = 1;
+		n +=  YAFFS_NOBJECT_BUCKETS;
+		if(1 ||dev->objectBucket[bucket].count > 0)
+		{
+			list_for_each(i,&dev->objectBucket[bucket].list)
+			{
+				// If there is already one in the list
+				if(i && list_entry(i, yaffs_Object,hashLink)->objectId == n)
+				{
+					found = 0;
+				}
+			}
+		}
+	}
+	
+	//T(("bucket %d count %d inode %d\n",bucket,yaffs_objectBucket[bucket].count,n);
+	
+	return n;	
+}
+
+void yaffs_HashObject(yaffs_Object *in)
+{
+	int bucket = yaffs_HashFunction(in->objectId);
+	yaffs_Device *dev = in->myDev;
+	
+	if(!list_empty(&in->hashLink))
+	{
+		//YINFO("!!!");
+	}
+
+	
+	list_add(&in->hashLink,&dev->objectBucket[bucket].list);
+	dev->objectBucket[bucket].count++;
+
+}
+
+yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,__u32 number)
+{
+	int bucket = yaffs_HashFunction(number);
+	struct list_head *i;
+	yaffs_Object *in;
+	
+	list_for_each(i,&dev->objectBucket[bucket].list)
+	{
+		// Look if it is in the list
+		if(i)
+		{
+			in = list_entry(i, yaffs_Object,hashLink);
+			if(in->objectId == number)
+			{
+#ifdef __KERNEL__
+				// Don't tell the VFS about this if it has been marked for freeing
+ 				if(in->deferedFree) 
+				    return NULL;
+#endif
+				return in;
+			}
+		}
+	}
+	
+	return NULL;
+}
+
+
+
+yaffs_Object *yaffs_CreateNewObject(yaffs_Device *dev,int number,yaffs_ObjectType type)
+{
+		
+	yaffs_Object *theObject;
+
+	if(number < 0)
+	{
+		number = yaffs_CreateNewObjectNumber(dev);
+	}
+	
+	theObject = yaffs_AllocateEmptyObject(dev);
+	
+	if(theObject)
+	{
+		theObject->fake = 0;
+		theObject->renameAllowed = 1;
+		theObject->unlinkAllowed = 1;
+		theObject->objectId = number;
+		yaffs_HashObject(theObject);
+		theObject->variantType = type;
+#ifdef CONFIG_YAFFS_WINCE
+		yfsd_WinFileTimeNow(theObject->win_atime);
+		theObject->win_ctime[0] = theObject->win_mtime[0] = theObject->win_atime[0];
+		theObject->win_ctime[1] = theObject->win_mtime[1] = theObject->win_atime[1];
+
+#else
+
+		theObject->st_atime = theObject->st_mtime =	theObject->st_ctime = Y_CURRENT_TIME;
+
+#endif
+		switch(type)
+		{
+			case YAFFS_OBJECT_TYPE_FILE: 
+				theObject->variant.fileVariant.fileSize = 0;
+				theObject->variant.fileVariant.scannedFileSize = 0;
+				theObject->variant.fileVariant.topLevel = 0;
+				theObject->variant.fileVariant.top  = yaffs_GetTnode(dev);
+				break;
+			case YAFFS_OBJECT_TYPE_DIRECTORY:
+				INIT_LIST_HEAD(&theObject->variant.directoryVariant.children);
+				break;
+			case YAFFS_OBJECT_TYPE_SYMLINK:
+				// No action required
+				break;
+			case YAFFS_OBJECT_TYPE_HARDLINK:
+				// No action required
+				break;
+			case YAFFS_OBJECT_TYPE_SPECIAL:
+				// No action required
+				break;
+			case YAFFS_OBJECT_TYPE_UNKNOWN:
+				// todo this should not happen
+				break;
+		}
+	}
+	
+	return theObject;
+}
+
+yaffs_Object *yaffs_FindOrCreateObjectByNumber(yaffs_Device *dev, int number,yaffs_ObjectType type)
+{
+	yaffs_Object *theObject = NULL;
+	
+	if(number > 0)
+	{
+		theObject = yaffs_FindObjectByNumber(dev,number);
+	}
+	
+	if(!theObject)
+	{
+		theObject = yaffs_CreateNewObject(dev,number,type);
+	}
+	
+	return theObject;
+
+}
+
+char *yaffs_CloneString(const char *str)
+{
+	char *newStr = NULL;
+	
+	if(str && *str)
+	{
+		newStr = YMALLOC(strlen(str) + 1);
+		strcpy(newStr,str);
+	}
+
+	return newStr;
+	
+}
+
+//
+// Mknod (create) a new object.
+// equivalentObject only has meaning for a hard link;
+// aliasString only has meaning for a sumlink.
+// rdev only has meaning for devices (a subset of special objects)
+yaffs_Object *yaffs_MknodObject( yaffs_ObjectType type,
+								 yaffs_Object *parent,
+								 const char *name, 
+								 __u32 mode,
+								 __u32 uid,
+								 __u32 gid,
+								 yaffs_Object *equivalentObject,
+								 const char *aliasString,
+								 __u32 rdev)
+{
+	yaffs_Object *in;
+
+	yaffs_Device *dev = parent->myDev;
+	
+	// Check if the entry exists. If it does then fail the call since we don't want a dup.
+	if(yaffs_FindObjectByName(parent,name))
+	{
+		return NULL;
+	}
+	
+	in = yaffs_CreateNewObject(dev,-1,type);
+	
+	if(in)
+	{
+		in->chunkId = -1;
+		in->valid = 1;
+		in->variantType = type;
+
+		in->st_mode  = mode;
+		
+#ifdef CONFIG_YAFFS_WINCE
+		yfsd_WinFileTimeNow(in->win_atime);
+		in->win_ctime[0] = in->win_mtime[0] = in->win_atime[0];
+		in->win_ctime[1] = in->win_mtime[1] = in->win_atime[1];
+		
+#else
+
+		in->st_atime = in->st_mtime = in->st_ctime = Y_CURRENT_TIME;
+		in->st_rdev  = rdev;
+		in->st_uid   = uid;
+		in->st_gid   = gid;
+#endif		
+		in->nDataChunks = 0;
+
+		yaffs_SetObjectName(in,name);
+		in->dirty = 1;
+		
+		yaffs_AddObjectToDirectory(parent,in);
+		
+		in->myDev = parent->myDev;
+		
+				
+		switch(type)
+		{
+			case YAFFS_OBJECT_TYPE_SYMLINK:
+				in->variant.symLinkVariant.alias = yaffs_CloneString(aliasString);
+				break;
+			case YAFFS_OBJECT_TYPE_HARDLINK:
+				in->variant.hardLinkVariant.equivalentObject = equivalentObject;
+				in->variant.hardLinkVariant.equivalentObjectId = equivalentObject->objectId;
+				list_add(&in->hardLinks,&equivalentObject->hardLinks);
+				break;
+			case YAFFS_OBJECT_TYPE_FILE: // do nothing
+			case YAFFS_OBJECT_TYPE_DIRECTORY: // do nothing
+			case YAFFS_OBJECT_TYPE_SPECIAL: // do nothing
+			case YAFFS_OBJECT_TYPE_UNKNOWN:
+				break;
+		}
+
+		if(/*yaffs_GetNumberOfFreeChunks(dev) <= 0 || */
+		   yaffs_UpdateObjectHeader(in,name,0) < 0)
+		{
+			// Could not create the object header, fail the creation
+			yaffs_AbortHalfCreatedObject(in);
+			in = NULL;
+		}
+
+	}
+	
+	return in;
+}
+
+yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid)
+{
+	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_FILE,parent,name,mode,uid,gid,NULL,NULL,0);
+}
+
+yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid)
+{
+	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,0);
+}
+
+yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid, __u32 rdev)
+{
+	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_DIRECTORY,parent,name,mode,uid,gid,NULL,NULL,rdev);
+}
+
+yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,const char *alias)
+{
+	return yaffs_MknodObject(YAFFS_OBJECT_TYPE_SYMLINK,parent,name,mode,uid,gid,NULL,alias,0);
+}
+
+// NB yaffs_Link returns the object id of the equivalent object.
+yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject)
+{
+	// Get the real object in case we were fed a hard link as an equivalent object
+	equivalentObject = yaffs_GetEquivalentObject(equivalentObject);
+	
+	if(yaffs_MknodObject(YAFFS_OBJECT_TYPE_HARDLINK,parent,name,0,0,0,equivalentObject,NULL,0))
+	{
+		return equivalentObject;
+	}
+	else
+	{
+		return NULL;
+	}
+	
+}
+
+
+static int yaffs_ChangeObjectName(yaffs_Object *obj, yaffs_Object *newDir, const char *newName,int force)
+{
+	int unlinkOp;
+
+	if(newDir == NULL)
+	{
+		newDir = obj->parent; // use the old directory
+	}
+
+	unlinkOp = (newDir == obj->myDev->unlinkedDir && obj->variantType == YAFFS_OBJECT_TYPE_FILE);
+	
+	// If the object is a file going into the unlinked directory, then it is OK to just stuff it in since
+	// duplicate names are allowed.
+	// Otherwise only proceed if the new name does not exist and if we're putting it into a directory.
+	if( (unlinkOp|| 
+		 force || 
+		 !yaffs_FindObjectByName(newDir,newName))  &&
+	     newDir->variantType == YAFFS_OBJECT_TYPE_DIRECTORY)
+	{
+		yaffs_SetObjectName(obj,newName);
+		obj->dirty = 1;
+		
+		yaffs_AddObjectToDirectory(newDir,obj);
+		
+		if(unlinkOp) obj->unlinked = 1;
+		
+		
+		if(yaffs_UpdateObjectHeader(obj,newName,0) >= 0)
+		{
+			return YAFFS_OK;
+		}
+	}
+	
+	return YAFFS_FAIL;
+}
+
+
+
+int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName)
+{
+	yaffs_Object *obj;
+	int force = 0;
+	
+#ifdef CONFIG_YAFFS_CASE_INSENSITIVE
+	// Special case for WinCE.
+	// While look-up is case insensitive, the name isn't.
+	// THerefore we might want to change x.txt to X.txt
+	if(oldDir == newDir && _stricmp(oldName,newName) == 0)
+	{
+		force = 1;
+	}	
+#endif
+	
+	obj = yaffs_FindObjectByName(oldDir,oldName);
+	if(obj && obj->renameAllowed)
+	{
+		return yaffs_ChangeObjectName(obj,newDir,newName,force);
+	}
+	return YAFFS_FAIL;
+}
+
+
+
+static int yaffs_CheckObjectHashSanity(yaffs_Device *dev)
+{
+	// Scan the buckets and check that the lists 
+	// have as many members as the count says there are
+	int bucket;
+	int countEm;
+	struct list_head *j;
+	int ok = YAFFS_OK;
+	
+	for(bucket = 0; bucket < YAFFS_NOBJECT_BUCKETS; bucket++)
+	{
+		countEm = 0;
+		
+		list_for_each(j,&dev->objectBucket[bucket].list)
+		{
+			countEm++;
+		}
+		
+		if(countEm != dev->objectBucket[bucket].count)
+		{
+			T(YAFFS_TRACE_ERROR,(TSTR("Inode hash inconsistency" TENDSTR)));
+			ok = YAFFS_FAIL;
+		}
+	}
+
+	return ok;
+}
+
+#if 0
+void yaffs_ObjectTest(yaffs_Device *dev)
+{
+	yaffs_Object *in[1000];
+	int inNo[1000];
+	yaffs_Object *inold[1000];
+	int i;
+	int j;
+	
+	memset(in,0,1000*sizeof(yaffs_Object *));
+	memset(inold,0,1000*sizeof(yaffs_Object *));
+	
+	yaffs_CheckObjectHashSanity(dev);
+	
+	for(j = 0; j < 10; j++)
+	{
+		//T(("%d\n",j));
+		
+		for(i = 0; i < 1000; i++)
+		{
+			in[i] = yaffs_CreateNewObject(dev,-1,YAFFS_OBJECT_TYPE_FILE);
+			if(!in[i])
+			{
+				YINFO("No more inodes");
+			}
+			else
+			{
+				inNo[i] = in[i]->objectId;
+			}
+		}
+		
+		for(i = 0; i < 1000; i++)
+		{
+			if(yaffs_FindObjectByNumber(dev,inNo[i]) != in[i])
+			{
+				//T(("Differnce in look up test\n"));
+			}
+			else
+			{
+				// T(("Look up ok\n"));
+			}
+		}
+		
+		yaffs_CheckObjectHashSanity(dev);
+	
+		for(i = 0; i < 1000; i+=3)
+		{
+			yaffs_FreeObject(in[i]);	
+			in[i] = NULL;
+		}
+		
+	
+		yaffs_CheckObjectHashSanity(dev);
+	}
+		
+}
+
+#endif
+
+/////////////////////////// Block Management and Page Allocation ///////////////////
+
+
+static int yaffs_InitialiseBlocks(yaffs_Device *dev,int nBlocks)
+{
+	dev->allocationBlock = -1; // force it to get a new one
+	//Todo we're assuming the malloc will pass.
+	dev->blockInfo = YMALLOC(nBlocks * sizeof(yaffs_BlockInfo));
+	// Set up dynamic blockinfo stuff.
+	dev->chunkBitmapStride = (dev->nChunksPerBlock+7)/8;
+	dev->chunkBits = YMALLOC(dev->chunkBitmapStride * nBlocks);
+	if(dev->blockInfo && dev->chunkBits)
+	{
+		memset(dev->blockInfo,0,nBlocks * sizeof(yaffs_BlockInfo));
+		memset(dev->chunkBits,0,dev->chunkBitmapStride * nBlocks);
+		return YAFFS_OK;
+	}
+	
+	return YAFFS_FAIL;
+	
+}
+
+static void yaffs_DeinitialiseBlocks(yaffs_Device *dev)
+{
+	YFREE(dev->blockInfo);
+	dev->blockInfo = NULL;
+	YFREE(dev->chunkBits);
+	dev->chunkBits = NULL;
+}
+
+// FindDiretiestBlock is used to select the dirtiest block (or close enough)
+// for garbage collection.
+
+static int yaffs_FindDirtiestBlock(yaffs_Device *dev,int aggressive)
+{
+
+	int b = dev->currentDirtyChecker;
+	
+	int i;
+	int iterations;
+	int dirtiest = -1;
+	int pagesInUse; 
+	yaffs_BlockInfo *bi;
+
+	// If we're doing aggressive GC then we are happy to take a less-dirty block, and
+	// search further.
+	
+	pagesInUse = (aggressive)? dev->nChunksPerBlock : YAFFS_PASSIVE_GC_CHUNKS + 1;
+	if(aggressive)
+	{
+		iterations = dev->endBlock - dev->startBlock + 1;
+	}
+	else
+	{
+		iterations = dev->endBlock - dev->startBlock + 1;
+		iterations = iterations / 16;
+		if(iterations > 200)
+		{
+			iterations = 200;
+		}
+	}
+	
+	for(i = 0; i <= iterations && pagesInUse > 0 ; i++)
+	{
+		b++;
+		if ( b < dev->startBlock || b > dev->endBlock)
+		{
+			b =  dev->startBlock;
+		}
+
+		if(b < dev->startBlock || b > dev->endBlock)
+		{
+			T(YAFFS_TRACE_ERROR,(TSTR("**>> Block %d is not valid" TENDSTR),b));
+			YBUG();
+		}
+		
+		bi = yaffs_GetBlockInfo(dev,b);
+		
+		if(bi->blockState == YAFFS_BLOCK_STATE_FULL &&
+		   (bi->pagesInUse - bi->softDeletions )< pagesInUse)
+		{
+			dirtiest = b;
+			pagesInUse = (bi->pagesInUse - bi->softDeletions);
+		}
+	}
+	
+	dev->currentDirtyChecker = b;
+	
+	return dirtiest;
+}
+
+
+static void yaffs_BlockBecameDirty(yaffs_Device *dev,int blockNo)
+{
+	yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,blockNo);
+	
+	int erasedOk = 0;
+	
+	// If the block is still healthy erase it and mark as clean.
+	// If the block has had a data failure, then retire it.
+	bi->blockState = YAFFS_BLOCK_STATE_DIRTY;
+
+	if(!bi->needsRetiring)
+	{
+		erasedOk = yaffs_EraseBlockInNAND(dev,blockNo);
+		if(!erasedOk)
+		{
+			T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Erasure failed %d" TENDSTR),blockNo));
+		}
+	}
+	
+	if( erasedOk )
+	{
+		// Clean it up...
+		bi->blockState = YAFFS_BLOCK_STATE_EMPTY;
+		dev->nErasedBlocks++;
+		bi->pagesInUse = 0;
+		bi->softDeletions = 0;
+		yaffs_ClearChunkBits(dev,blockNo);
+	
+		T(YAFFS_TRACE_ERASE,(TSTR("Erased block %d" TENDSTR),blockNo));
+	}
+	else
+	{
+		yaffs_RetireBlock(dev,blockNo);
+		T(YAFFS_TRACE_ERROR | YAFFS_TRACE_BAD_BLOCKS,(TSTR("**>> Block %d retired" TENDSTR),blockNo));
+	}
+}
+
+
+static int yaffs_FindBlockForAllocation(yaffs_Device *dev)
+{
+	int i;
+	
+	yaffs_BlockInfo *bi;
+	
+	if(dev->nErasedBlocks < 1)
+	{
+		// Hoosterman we've got a problem.
+		// Can't get space to gc
+		T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: no space during gc" TENDSTR)));
+
+		return -1;
+	}
+	
+	// Find an empty block.
+	
+	for(i = dev->startBlock; i <= dev->endBlock; i++)
+	{
+		dev->allocationBlockFinder++;
+		if(dev->allocationBlockFinder <dev->startBlock || dev->allocationBlockFinder> dev->endBlock) 
+		{
+			dev->allocationBlockFinder = dev->startBlock;
+		}
+		
+		bi = yaffs_GetBlockInfo(dev,dev->allocationBlockFinder);
+
+		if(bi->blockState == YAFFS_BLOCK_STATE_EMPTY)
+		{
+			bi->blockState = YAFFS_BLOCK_STATE_ALLOCATING;
+			dev->nErasedBlocks--;			
+			return dev->allocationBlockFinder;
+		}
+	}
+	
+	return -1;	
+}
+
+
+
+static int yaffs_AllocateChunk(yaffs_Device *dev,int useReserve)
+{
+	int retVal;
+	yaffs_BlockInfo *bi;
+	
+	if(dev->allocationBlock < 0)
+	{
+		// Get next block to allocate off
+		dev->allocationBlock = yaffs_FindBlockForAllocation(dev);
+		dev->allocationPage = 0;
+	}
+	
+	if(!useReserve &&  dev->nErasedBlocks <= dev->nReservedBlocks)
+	{
+		// Not enough space to allocate unless we're allowed to use the reserve.
+		return -1;
+	}
+	
+	// Next page please....
+	if(dev->allocationBlock >= 0)
+	{
+		bi = yaffs_GetBlockInfo(dev,dev->allocationBlock);
+		
+		retVal = (dev->allocationBlock * dev->nChunksPerBlock) + 
+			  	  dev->allocationPage;
+		bi->pagesInUse++;
+		yaffs_SetChunkBit(dev,dev->allocationBlock,dev->allocationPage);
+
+		dev->allocationPage++;
+		
+		dev->nFreeChunks--;
+		
+		// If the block is full set the state to full
+		if(dev->allocationPage >= dev->nChunksPerBlock)
+		{
+			bi->blockState = YAFFS_BLOCK_STATE_FULL;
+			dev->allocationBlock = -1;
+		}
+
+
+		return retVal;
+		
+	}
+	T(YAFFS_TRACE_ERROR,(TSTR("!!!!!!!!! Allocator out !!!!!!!!!!!!!!!!!" TENDSTR)));
+
+	return -1;	
+}
+
+// To determine if we have enough space we just look at the 
+// number of erased blocks.
+// The cache is allowed to use reserved blocks.
+
+static int yaffs_CheckSpaceForChunkCache(yaffs_Device *dev)
+{
+	return (dev->nErasedBlocks >= dev->nReservedBlocks);
+}
+
+
+static int  yaffs_GarbageCollectBlock(yaffs_Device *dev,int block)
+{
+	int oldChunk;
+	int newChunk;
+	int chunkInBlock;
+	int markNAND;
+	
+	
+	yaffs_Spare spare;
+	yaffs_Tags  tags;
+	__u8  buffer[YAFFS_BYTES_PER_CHUNK];
+	
+//	yaffs_BlockInfo *bi = yaffs_GetBlockInfo(dev,block);
+	
+	yaffs_Object *object;
+
+	//T(("Collecting block %d n %d bits %x\n",block, bi->pagesInUse, bi->pageBits));	
+	
+	for(chunkInBlock = 0,oldChunk = block * dev->nChunksPerBlock; 
+	    chunkInBlock < dev->nChunksPerBlock && yaffs_StillSomeChunkBits(dev,block);
+	    chunkInBlock++, oldChunk++ )
+	{
+		if(yaffs_CheckChunkBit(dev,block,chunkInBlock))
+		{
+			
+			// This page is in use and might need to be copied off
+			
+			markNAND = 1;
+			
+			//T(("copying page %x from %d to %d\n",mask,oldChunk,newChunk));
+			
+			yaffs_ReadChunkFromNAND(dev,oldChunk,buffer, &spare,1);
+			
+			yaffs_GetTagsFromSpare(dev,&spare,&tags);
+
+			object = yaffs_FindObjectByNumber(dev,tags.objectId);
+			
+			if(object && object->deleted && tags.chunkId != 0)
+			{
+				// Data chunk in a deleted file, throw it away
+				// It's a deleted data chunk,
+				// No need to copy this, just forget about it and fix up the
+				// object.
+				
+				//yaffs_PutChunkIntoFile(object, tags.chunkId, 0,0); 
+				object->nDataChunks--;
+				
+				if(object->nDataChunks <= 0)
+				{
+					// Time to delete the file too
+					yaffs_FreeTnode(object->myDev,object->variant.fileVariant.top);
+					object->variant.fileVariant.top = NULL;
+					T(YAFFS_TRACE_TRACING,(TSTR("yaffs: About to finally delete object %d" TENDSTR),object->objectId));
+					yaffs_DoGenericObjectDeletion(object);					
+ 				}
+				markNAND = 0;
+			}
+			else if( 0 /* Todo object && object->deleted && object->nDataChunks == 0 */)
+			{
+				// Deleted object header with no data chunks.
+				// Can be discarded and the file deleted.
+				object->chunkId = 0;
+				yaffs_FreeTnode(object->myDev,object->variant.fileVariant.top);
+				object->variant.fileVariant.top = NULL;
+				yaffs_DoGenericObjectDeletion(object);
+				
+			}
+			else if(object)
+			{
+				// It's either a data chunk in a live file or
+				// an ObjectHeader, so we're interested in it.
+				// NB Need to keep the ObjectHeaders of deleted files
+				// until the whole file has been deleted off
+				tags.serialNumber++;
+				yaffs_LoadTagsIntoSpare(&spare,&tags);
+
+				dev->nGCCopies++;
+
+				newChunk = yaffs_WriteNewChunkToNAND(dev, buffer, &spare,1);
+			
+				if(newChunk < 0)
+				{
+					return YAFFS_FAIL;
+				}
+			
+				// Ok, now fix up the Tnodes etc.
+			
+				if(tags.chunkId == 0)
+				{
+					// It's a header
+					object->chunkId = newChunk;
+					object->serial = tags.serialNumber;
+				}
+				else
+				{
+					// It's a data chunk
+					yaffs_PutChunkIntoFile(object, tags.chunkId, newChunk,0);
+				}
+			}
+			
+			yaffs_DeleteChunk(dev,oldChunk,markNAND);			
+			
+		}
+	}
+
+	return YAFFS_OK;
+}
+
+
+static yaffs_Object *yaffs_FindDeletedUnlinkedFile(yaffs_Device *dev)
+{
+	// find a file to delete
+	struct list_head *i;	
+	yaffs_Object *l;
+
+
+	//Scan the unlinked files looking for one to delete
+	list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
+	{
+		if(i)
+		{
+			l = list_entry(i, yaffs_Object,siblings);
+			if(l->deleted)
+			{
+				return l;			
+			}
+		}
+	}	
+	return NULL;
+}
+
+
+static void yaffs_DoUnlinkedFileDeletion(yaffs_Device *dev)
+{
+	// This does background deletion on unlinked files.. only deleted ones.
+	// If we don't have a file we're working on then find one
+	if(!dev->unlinkedDeletion && dev->nDeletedFiles > 0)
+	{
+		dev->unlinkedDeletion = yaffs_FindDeletedUnlinkedFile(dev);
+	}
+	
+	// OK, we're working on a file...
+	if(dev->unlinkedDeletion)
+	{
+		yaffs_Object *obj = dev->unlinkedDeletion;
+		int delresult;
+		int limit; // Number of chunks to delete in a file.
+				   // NB this can be exceeded, but not by much.
+				   
+		limit = -1;
+
+		delresult = yaffs_DeleteWorker(obj, obj->variant.fileVariant.top, obj->variant.fileVariant.topLevel, 0,&limit);
+		
+		if(obj->nDataChunks == 0)
+		{
+			// Done all the deleting of data chunks.
+			// Now dump the header and clean up
+			yaffs_FreeTnode(dev,obj->variant.fileVariant.top);
+			obj->variant.fileVariant.top = NULL;
+			yaffs_DoGenericObjectDeletion(obj);
+			dev->nDeletedFiles--;
+			dev->nUnlinkedFiles--;
+			dev->nBackgroundDeletions++;
+			dev->unlinkedDeletion = NULL;	
+		}
+	}
+}
+
+
+#if 0
+#define YAFFS_GARBAGE_COLLECT_LOW_WATER 2
+static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
+{
+	int block;
+	int aggressive=0;
+	
+	//yaffs_DoUnlinkedFileDeletion(dev);
+	
+	if(dev->nErasedBlocks <= (dev->nReservedBlocks + YAFFS_GARBAGE_COLLECT_LOW_WATER))
+	{
+		aggressive = 1;
+	}		
+	
+	if(aggressive)
+	{
+		block = yaffs_FindDirtiestBlock(dev,aggressive);
+		
+		if(block >= 0)
+		{
+			dev->garbageCollections++;
+			return yaffs_GarbageCollectBlock(dev,block);
+		}	
+		else
+		{
+			return YAFFS_FAIL;
+		}
+	}
+
+	return YAFFS_OK;
+}
+#endif
+
+// New garbage collector
+// If we're very low on erased blocks then we do aggressive garbage collection
+// otherwise we do "passive" garbage collection.
+// Aggressive gc looks further (whole array) and will accept dirtier blocks.
+// Passive gc only inspects smaller areas and will only accept cleaner blocks.
+//
+// The idea is to help clear out space in a more spread-out manner.
+// Dunno if it really does anything useful.
+//
+static int yaffs_CheckGarbageCollection(yaffs_Device *dev)
+{
+	int block;
+	int aggressive=0;
+	
+	//yaffs_DoUnlinkedFileDeletion(dev);
+	
+	if(dev->nErasedBlocks <= (dev->nReservedBlocks + 1))
+	{
+		aggressive = 1;
+	}		
+	
+	block = yaffs_FindDirtiestBlock(dev,aggressive);
+	
+	if(block >= 0)
+	{
+		dev->garbageCollections++;
+		if(!aggressive)
+		{
+			dev->passiveGarbageCollections++;
+		}
+
+		T(YAFFS_TRACE_GC,(TSTR("yaffs: GC erasedBlocks %d aggressive %d" TENDSTR),dev->nErasedBlocks,aggressive));
+
+		return yaffs_GarbageCollectBlock(dev,block);
+	}	
+
+	return aggressive ? YAFFS_FAIL : YAFFS_OK;
+}
+
+
+//////////////////////////// TAGS ///////////////////////////////////////
+
+static void yaffs_LoadTagsIntoSpare(yaffs_Spare *sparePtr, yaffs_Tags *tagsPtr)
+{
+	yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
+	
+	yaffs_CalcTagsECC(tagsPtr);
+	
+	sparePtr->tagByte0 = tu->asBytes[0];
+	sparePtr->tagByte1 = tu->asBytes[1];
+	sparePtr->tagByte2 = tu->asBytes[2];
+	sparePtr->tagByte3 = tu->asBytes[3];
+	sparePtr->tagByte4 = tu->asBytes[4];
+	sparePtr->tagByte5 = tu->asBytes[5];
+	sparePtr->tagByte6 = tu->asBytes[6];
+	sparePtr->tagByte7 = tu->asBytes[7];
+}
+
+static void yaffs_GetTagsFromSpare(yaffs_Device *dev, yaffs_Spare *sparePtr,yaffs_Tags *tagsPtr)
+{
+	yaffs_TagsUnion *tu = (yaffs_TagsUnion *)tagsPtr;
+	int result;
+
+	tu->asBytes[0]= sparePtr->tagByte0;
+	tu->asBytes[1]= sparePtr->tagByte1;
+	tu->asBytes[2]= sparePtr->tagByte2;
+	tu->asBytes[3]= sparePtr->tagByte3;
+	tu->asBytes[4]= sparePtr->tagByte4;
+	tu->asBytes[5]= sparePtr->tagByte5;
+	tu->asBytes[6]= sparePtr->tagByte6;
+	tu->asBytes[7]= sparePtr->tagByte7;
+	
+	result =  yaffs_CheckECCOnTags(tagsPtr);
+	if(result> 0)
+	{
+		dev->tagsEccFixed++;
+	}
+	else if(result <0)
+	{
+		dev->tagsEccUnfixed++;
+	}
+}
+
+static void yaffs_SpareInitialise(yaffs_Spare *spare)
+{
+	memset(spare,0xFF,sizeof(yaffs_Spare));
+}
+
+static int yaffs_ReadChunkTagsFromNAND(yaffs_Device *dev,int chunkInNAND, yaffs_Tags *tags, int *chunkDeleted)
+{
+	if(tags)
+	{
+		yaffs_Spare spare;
+		if(yaffs_ReadChunkFromNAND(dev,chunkInNAND,NULL,&spare,1) == YAFFS_OK)
+		{
+			*chunkDeleted = (yaffs_CountBits(spare.pageStatus) < 7) ? 1 : 0;
+			yaffs_GetTagsFromSpare(dev,&spare,tags);
+			return YAFFS_OK;
+		}
+		else
+		{
+			return YAFFS_FAIL;
+		}
+	}
+	
+	return YAFFS_OK;
+}
+
+#if 0
+static int yaffs_WriteChunkWithTagsToNAND(yaffs_Device *dev,int chunkInNAND, const __u8 *buffer, yaffs_Tags *tags)
+{
+	// NB There must be tags, data is optional
+	// If there is data, then an ECC is calculated on it.
+	
+	yaffs_Spare spare;
+	
+	if(!tags)
+	{
+		return YAFFS_FAIL;
+	}
+	
+	yaffs_SpareInitialise(&spare);
+	
+	if(!dev->useNANDECC && buffer)
+	{
+		yaffs_CalcECC(buffer,&spare);
+	}
+	
+	yaffs_LoadTagsIntoSpare(&spare,tags);
+	
+	return yaffs_WriteChunkToNAND(dev,chunkInNAND,buffer,&spare);
+	
+}
+#endif
+
+
+static int yaffs_WriteNewChunkWithTagsToNAND(yaffs_Device *dev, const __u8 *buffer, yaffs_Tags *tags, int useReserve)
+{
+	// NB There must be tags, data is optional
+	// If there is data, then an ECC is calculated on it.
+	
+	yaffs_Spare spare;
+	
+	if(!tags)
+	{
+		return YAFFS_FAIL;
+	}
+	
+	yaffs_SpareInitialise(&spare);
+	
+	if(!dev->useNANDECC && buffer)
+	{
+		yaffs_CalcECC(buffer,&spare);
+	}
+	
+	yaffs_LoadTagsIntoSpare(&spare,tags);
+	
+	return yaffs_WriteNewChunkToNAND(dev,buffer,&spare,useReserve);
+	
+}
+
+static int yaffs_TagsMatch(const yaffs_Tags *tags, int objectId, int chunkInObject, int chunkDeleted)
+{
+	return 	(  tags->chunkId == chunkInObject &&
+			   tags->objectId == objectId &&
+			   !chunkDeleted) ? 1 : 0;
+	
+}
+
+
+
+int yaffs_FindChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)
+{
+	//Get the Tnode, then get the level 0 offset chunk offset
+    yaffs_Tnode *tn;     
+    int theChunk = -1;
+    yaffs_Tags localTags;
+    int i;
+    int found = 0;
+    int chunkDeleted;
+    
+    yaffs_Device *dev = in->myDev;
+    
+    
+    if(!tags)
+    {
+    	// Passed a NULL, so use our own tags space
+    	tags = &localTags;
+    }
+    
+    tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
+    
+    if(tn)
+    {
+		theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
+
+		// Now we need to do the shifting etc and search for it
+		for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)
+		{
+			yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags,&chunkDeleted);
+			if(yaffs_TagsMatch(tags,in->objectId,chunkInInode,chunkDeleted))
+			{
+				// found it;
+				found = 1;
+			}
+			else
+			{
+				theChunk++;
+			}
+		}
+    }
+    return found ? theChunk : -1;
+}
+
+int yaffs_FindAndDeleteChunkInFile(yaffs_Object *in,int chunkInInode,yaffs_Tags *tags)
+{
+	//Get the Tnode, then get the level 0 offset chunk offset
+    yaffs_Tnode *tn;     
+    int theChunk = -1;
+    yaffs_Tags localTags;
+    int i;
+    int found = 0;
+    yaffs_Device *dev = in->myDev;
+    int chunkDeleted;
+    
+    if(!tags)
+    {
+    	// Passed a NULL, so use our own tags space
+    	tags = &localTags;
+    }
+    
+    tn = yaffs_FindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
+    
+    if(tn)
+    {
+    
+		theChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] << dev->chunkGroupBits;
+    
+		// Now we need to do the shifting etc and search for it
+		for(i = 0,found = 0; theChunk && i < dev->chunkGroupSize && !found; i++)
+		{
+			yaffs_ReadChunkTagsFromNAND(dev,theChunk,tags,&chunkDeleted);
+			if(yaffs_TagsMatch(tags,in->objectId,chunkInInode,chunkDeleted))
+			{
+				// found it;
+				found = 1;
+			}
+			else
+			{
+				theChunk++;
+			}
+		}
+    
+		// Delete the entry in the filestructure
+		if(found)
+		{
+			tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = 0;
+		}
+    }
+    else
+    {
+    	//T(("No level 0 found for %d\n", chunkInInode));
+    }
+    
+    if(!found)
+    {
+    	//T(("Could not find %d to delete\n",chunkInInode));
+    }
+    return found ? theChunk : -1;
+}
+
+
+#ifdef YAFFS_PARANOID
+
+static int yaffs_CheckFileSanity(yaffs_Object *in)
+{
+	int chunk;
+	int nChunks;
+	int fSize;
+	int failed = 0;
+	int objId;
+	yaffs_Tnode *tn;
+    yaffs_Tags localTags;
+    yaffs_Tags *tags = &localTags;
+    int theChunk;
+    int chunkDeleted;
+    
+    	
+	if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
+	{
+		//T(("Object not a file\n"));
+		return YAFFS_FAIL;
+	}
+	
+	objId = in->objectId;
+	fSize  = in->variant.fileVariant.fileSize;
+	nChunks = (fSize + in->myDev->nBytesPerChunk -1)/in->myDev->nBytesPerChunk;
+	
+	for(chunk = 1; chunk <= nChunks; chunk++)
+	{
+		tn = yaffs_FindLevel0Tnode(in->myDev,&in->variant.fileVariant, chunk);
+    
+		if(tn)
+		{
+    
+			theChunk = tn->level0[chunk & YAFFS_TNODES_LEVEL0_MASK] << in->myDev->chunkGroupBits;
+    
+
+				yaffs_ReadChunkTagsFromNAND(in->myDev,theChunk,tags,&chunkDeleted);
+				if(yaffs_TagsMatch(tags,in->objectId,chunk,chunkDeleted))
+				{
+					// found it;
+				
+				}
+				else
+				{
+					//T(("File problem file [%d,%d] NAND %d  tags[%d,%d]\n",
+					//		objId,chunk,theChunk,tags->chunkId,tags->objectId);
+							
+					failed = 1;
+								
+				}
+    
+		}
+		else
+		{
+			//T(("No level 0 found for %d\n", chunk));
+		}
+	}
+	
+	return failed ? YAFFS_FAIL : YAFFS_OK;
+}
+
+#endif
+
+static int yaffs_PutChunkIntoFile(yaffs_Object *in,int chunkInInode, int chunkInNAND, int inScan)
+{
+	yaffs_Tnode *tn;
+	yaffs_Device *dev = in->myDev;
+	int existingChunk;
+	yaffs_Tags existingTags;
+	yaffs_Tags newTags;
+	unsigned existingSerial, newSerial;
+	
+	int newChunkDeleted;
+	
+	
+	tn = yaffs_AddOrFindLevel0Tnode(dev,&in->variant.fileVariant, chunkInInode);
+	if(!tn)
+	{
+		return YAFFS_FAIL;
+	}
+
+	existingChunk = tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK];		
+	
+	if(inScan)
+	{
+		// If we're scanning then we need to test for duplicates
+		// NB This does not need to be efficient since it should only ever 
+		// happen when the power fails during a write, then only one
+		// chunk should ever be affected.
+	
+		
+		if(existingChunk != 0)
+		{
+			// NB Right now existing chunk will not be real chunkId if the device >= 32MB
+			//    thus we have to do a FindChunkInFile to get the real chunk id.
+			//
+			// We have a duplicate now we need to decide which one to use
+			// To do this we get both sets of tags and compare serial numbers.
+			yaffs_ReadChunkTagsFromNAND(dev,chunkInNAND, &newTags,&newChunkDeleted);
+			
+			
+			// Do a proper find
+			existingChunk = yaffs_FindChunkInFile(in,chunkInInode, &existingTags);
+
+			if(existingChunk <=0)
+			{
+				//Hoosterman - how did this happen?
+				
+				T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: existing chunk < 0 in scan" TENDSTR)));
+
+			}
+
+			
+			// NB The deleted flags should be false, otherwise the chunks will 
+			// not be loaded during a scan
+			
+			newSerial = newTags.serialNumber;
+			existingSerial = existingTags.serialNumber;
+			
+			if( existingChunk <= 0 ||
+			    ((existingSerial+1) & 3) == newSerial)
+			{
+				// Use new
+				// Delete the old one and drop through to update the tnode
+				yaffs_DeleteChunk(dev,existingChunk,1);
+			}
+			else
+			{
+				// Use existing.
+				// Delete the new one and return early so that the tnode isn't changed
+				yaffs_DeleteChunk(dev,chunkInNAND,1);
+				return YAFFS_OK;
+			}
+		}
+
+	}
+		
+	if(existingChunk == 0)
+	{
+		in->nDataChunks++;
+	}
+	
+	tn->level0[chunkInInode & YAFFS_TNODES_LEVEL0_MASK] = (chunkInNAND >> dev->chunkGroupBits);
+	
+	return YAFFS_OK;
+}
+
+
+
+int yaffs_ReadChunkDataFromObject(yaffs_Object *in,int chunkInInode, __u8 *buffer)
+{
+    int chunkInNAND = yaffs_FindChunkInFile(in,chunkInInode,NULL);
+    
+    if(chunkInNAND >= 0)
+    {
+		return yaffs_ReadChunkFromNAND(in->myDev,chunkInNAND,buffer,NULL,1);
+	}
+	else
+	{
+		memset(buffer,0,YAFFS_BYTES_PER_CHUNK);
+		return 0;
+	}
+
+}
+
+
+static void yaffs_DeleteChunk(yaffs_Device *dev,int chunkId,int markNAND)
+{
+	int block;
+	int page;
+	yaffs_Spare spare;
+	yaffs_BlockInfo *bi;
+	
+	if(chunkId <= 0) return;	
+	
+	dev->nDeletions++;
+	block = chunkId / dev->nChunksPerBlock;
+	page = chunkId % dev->nChunksPerBlock;
+	
+	if(markNAND)
+	{
+		yaffs_SpareInitialise(&spare);
+
+#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
+
+                //read data before write, to ensure correct ecc 
+                //if we're using MTD verification under Linux
+                yaffs_ReadChunkFromNAND(dev,chunkId,NULL,&spare,0);
+#endif
+
+		spare.pageStatus = 0; // To mark it as deleted.
+
+	
+		yaffs_WriteChunkToNAND(dev,chunkId,NULL,&spare);
+		yaffs_HandleUpdateChunk(dev,chunkId,&spare);
+	}
+	else
+	{
+			dev->nUnmarkedDeletions++;
+	}	
+	
+	bi = yaffs_GetBlockInfo(dev,block);
+			
+	
+	// Pull out of the management area.
+	// If the whole block became dirty, this will kick off an erasure.
+	if(	bi->blockState == YAFFS_BLOCK_STATE_ALLOCATING ||
+	    bi->blockState == YAFFS_BLOCK_STATE_FULL)
+	{
+		dev->nFreeChunks++;
+
+		yaffs_ClearChunkBit(dev,block,page);
+		bi->pagesInUse--;
+		
+		if(bi->pagesInUse == 0 &&
+	       bi->blockState == YAFFS_BLOCK_STATE_FULL)
+	    {
+	    	yaffs_BlockBecameDirty(dev,block);
+	    }
+
+	}
+	else
+	{
+		// T(("Bad news deleting chunk %d\n",chunkId));
+	}
+	
+}
+
+
+
+
+int yaffs_WriteChunkDataToObject(yaffs_Object *in,int chunkInInode, const __u8 *buffer,int nBytes,int useReserve)
+{
+	// Find old chunk Need to do this to get serial number
+	// Write new one and patch into tree.
+	// Invalidate old tags.
+
+    int prevChunkId;
+    yaffs_Tags prevTags;
+    
+    int newChunkId;
+    yaffs_Tags newTags;
+
+    yaffs_Device *dev = in->myDev;    
+
+	yaffs_CheckGarbageCollection(dev);
+
+	// Get the previous chunk at this location in the file if it exists
+    prevChunkId  = yaffs_FindChunkInFile(in,chunkInInode,&prevTags);
+    
+    // Set up new tags
+   	newTags.chunkId = chunkInInode;
+	newTags.objectId = in->objectId;
+	newTags.serialNumber = (prevChunkId >= 0) ? prevTags.serialNumber + 1 : 1;
+   	newTags.byteCount = nBytes;
+	newTags.unusedStuff = 0xFFFFFFFF;
+		
+	yaffs_CalcTagsECC(&newTags);
+
+	newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,buffer,&newTags,useReserve);
+	if(newChunkId >= 0)
+	{
+		yaffs_PutChunkIntoFile(in,chunkInInode,newChunkId,0);
+		
+		
+		if(prevChunkId >= 0)
+		{
+			yaffs_DeleteChunk(dev,prevChunkId,1);
+	
+		}
+		
+		yaffs_CheckFileSanity(in);
+	}
+	return newChunkId;
+
+
+
+
+
+}
+
+
+// UpdateObjectHeader updates the header on NAND for an object.
+// If name is not NULL, then that new name is used.
+//
+int yaffs_UpdateObjectHeader(yaffs_Object *in,const char *name, int force)
+{
+
+	yaffs_Device *dev = in->myDev;
+	
+    int prevChunkId;
+    
+    int newChunkId;
+    yaffs_Tags newTags;
+    __u8 bufferNew[YAFFS_BYTES_PER_CHUNK];
+    __u8 bufferOld[YAFFS_BYTES_PER_CHUNK];
+    
+    yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)bufferNew;
+    yaffs_ObjectHeader *ohOld = (yaffs_ObjectHeader *)bufferOld;
+
+    
+    if(!in->fake || force)
+    {
+  
+		yaffs_CheckGarbageCollection(dev);		
+    
+		memset(bufferNew,0xFF,YAFFS_BYTES_PER_CHUNK);
+    
+		prevChunkId = in->chunkId;
+    
+		if(prevChunkId >= 0)
+		{
+			yaffs_ReadChunkFromNAND(dev,prevChunkId,bufferOld,NULL,1);    	
+		}
+
+		// Header data
+		oh->type = in->variantType;
+		
+		oh->st_mode = in->st_mode;
+
+#ifdef CONFIG_YAFFS_WINCE
+		oh->win_atime[0] = in->win_atime[0];
+		oh->win_ctime[0] = in->win_ctime[0];
+		oh->win_mtime[0] = in->win_mtime[0];
+		oh->win_atime[1] = in->win_atime[1];
+		oh->win_ctime[1] = in->win_ctime[1];
+		oh->win_mtime[1] = in->win_mtime[1];
+#else
+		oh->st_uid = in->st_uid;
+		oh->st_gid = in->st_gid;
+		oh->st_atime = in->st_atime;
+		oh->st_mtime = in->st_mtime;
+		oh->st_ctime = in->st_ctime;
+		oh->st_rdev = in->st_rdev;
+#endif	
+		if(in->parent)
+		{
+			oh->parentObjectId = in->parent->objectId;
+		}
+		else
+		{
+			oh->parentObjectId = 0;
+		}
+		
+		//oh->sum = in->sum;
+		if(name && *name)
+		{
+			memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1);
+			strncpy(oh->name,name,YAFFS_MAX_NAME_LENGTH);
+		}
+		else if(prevChunkId)
+		{	
+			memcpy(oh->name, ohOld->name,YAFFS_MAX_NAME_LENGTH + 1);
+		}
+		else
+		{
+			memset(oh->name,0,YAFFS_MAX_NAME_LENGTH + 1);	
+		}
+	
+		switch(in->variantType)
+		{
+			case YAFFS_OBJECT_TYPE_UNKNOWN: 	
+				// Should not happen
+				break;
+			case YAFFS_OBJECT_TYPE_FILE:
+				oh->fileSize = in->variant.fileVariant.fileSize;
+				break;
+			case YAFFS_OBJECT_TYPE_HARDLINK:
+				oh->equivalentObjectId = in->variant.hardLinkVariant.equivalentObjectId;
+				break;
+			case YAFFS_OBJECT_TYPE_SPECIAL:	
+				// Do nothing
+				break;
+			case YAFFS_OBJECT_TYPE_DIRECTORY:	
+				// Do nothing
+				break;
+			case YAFFS_OBJECT_TYPE_SYMLINK:
+				strncpy(oh->alias,in->variant.symLinkVariant.alias,YAFFS_MAX_ALIAS_LENGTH);
+				oh->alias[YAFFS_MAX_ALIAS_LENGTH] = 0;
+				break;
+		}
+
+		// Tags
+		in->serial++;
+		newTags.chunkId = 0;
+		newTags.objectId = in->objectId;
+		newTags.serialNumber = in->serial;
+		newTags.byteCount =   0xFFFFFFFF;
+		newTags.unusedStuff = 0xFFFFFFFF;
+	
+		yaffs_CalcTagsECC(&newTags);
+	
+
+		// Create new chunk in NAND
+		newChunkId = yaffs_WriteNewChunkWithTagsToNAND(dev,bufferNew,&newTags, (prevChunkId >= 0) ? 1 : 0 );
+    
+		if(newChunkId >= 0)
+		{
+		
+			in->chunkId = newChunkId;		
+		
+			if(prevChunkId >= 0)
+			{
+				yaffs_DeleteChunk(dev,prevChunkId,1);
+			}
+		
+			in->dirty = 0;
+		}
+		
+		return newChunkId;
+
+    }
+    return 0;
+}
+
+
+/////////////////////// Short Operations Cache ////////////////////////////////
+//	In many siturations where there is no high level buffering (eg WinCE) a lot of
+//	reads might be short sequential reads, and a lot of writes may be short 
+//  sequential writes. eg. scanning/writing a jpeg file.
+//	In these cases, a short read/write cache can provide a huge perfomance benefit 
+//  with dumb-as-a-rock code.
+//  There are a limited number (~10) of cache chunks per device so that we don't
+//  need a very intelligent search.
+
+
+
+
+
+static void yaffs_FlushFilesChunkCache(yaffs_Object *obj)
+{
+	yaffs_Device *dev = obj->myDev;
+	int lowest;
+	int i;
+	yaffs_ChunkCache *cache;
+	int chunkWritten = 0;
+	int nBytes;
+	int nCaches = obj->myDev->nShortOpCaches;
+	
+	if  (nCaches > 0)
+	{
+		do{
+			cache = NULL;
+		
+			// Find the dirty cache for this object with the lowest chunk id.
+			for(i = 0; i < nCaches; i++)
+			{
+				if(dev->srCache[i].object == obj &&
+				dev->srCache[i].dirty)
+				{
+					if(!cache ||  dev->srCache[i].chunkId < lowest)
+					{
+						cache = &dev->srCache[i];
+						lowest = cache->chunkId;
+					}
+				}
+			}
+		
+			if(cache)
+			{
+				//Write it out
+
+#if 0
+				nBytes = cache->object->variant.fileVariant.fileSize - ((cache->chunkId -1) * YAFFS_BYTES_PER_CHUNK);
+			
+				if(nBytes > YAFFS_BYTES_PER_CHUNK)
+				{
+					nBytes= YAFFS_BYTES_PER_CHUNK;
+				}
+#endif			
+				chunkWritten = yaffs_WriteChunkDataToObject(cache->object,
+															cache->chunkId,
+															cache->data,
+															cache->nBytes,1);
+
+				cache->dirty = 0;
+				cache->object = NULL;
+			}
+		
+		} while(cache && chunkWritten > 0);
+	
+		if(cache)
+		{
+			//Hoosterman, disk full while writing cache out.
+			T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: no space during cache write" TENDSTR)));
+
+		}
+	}	
+		
+}
+
+
+// Grab us a chunk for use.
+// First look for an empty one. 
+// Then look for the least recently used non-dirty one.
+// Then look for the least recently used dirty one...., flush and look again.
+static yaffs_ChunkCache *yaffs_GrabChunkCacheWorker(yaffs_Device *dev)
+{
+	int i;
+	int usage;
+	int theOne;
+	
+	if(dev->nShortOpCaches > 0)
+	{
+		for(i = 0; i < dev->nShortOpCaches; i++)
+		{
+			if(!dev->srCache[i].object)
+			{
+				//T(("Grabbing empty %d\n",i));
+				
+				//printf("Grabbing empty %d\n",i);
+			
+				return &dev->srCache[i];
+			}
+		}
+		
+		return NULL;
+	
+		theOne = -1; 
+		usage = 0; // just to stop the compiler grizzling
+	
+		for(i = 0; i < dev->nShortOpCaches; i++)
+		{
+			if(!dev->srCache[i].dirty &&
+			((dev->srCache[i].lastUse < usage  && theOne >= 0)|| 
+				theOne < 0))
+			{
+				usage = dev->srCache[i].lastUse;
+				theOne = i;
+			}
+		}
+	
+		//T(("Grabbing non-empty %d\n",theOne));
+		
+		//if(theOne >= 0) printf("Grabbed non-empty cache %d\n",theOne);
+		
+		return  theOne >= 0 ?  &dev->srCache[theOne] : NULL;
+	}
+	else
+	{
+		return NULL;
+	}
+	
+}
+
+
+static yaffs_ChunkCache *yaffs_GrabChunkCache(yaffs_Device *dev)
+{
+	yaffs_ChunkCache *cache;
+	yaffs_Object *theObj;
+	int usage;
+	int i;
+	int pushout;
+	
+	if(dev->nShortOpCaches > 0)
+	{
+		// Try find a non-dirty one...
+	
+		cache = yaffs_GrabChunkCacheWorker(dev);
+	
+		if(!cache)
+		{
+			// They were all dirty, find the last recently used object and flush
+			// its cache, then  find again.
+			// NB what's here is not very accurate, we actually flush the object
+			// the last recently used page.
+		
+			theObj = dev->srCache[0].object;
+			usage = dev->srCache[0].lastUse;
+			cache = &dev->srCache[0];
+			pushout = 0;
+	
+			for(i = 1; i < dev->nShortOpCaches; i++)
+			{
+				if( dev->srCache[i].object && 
+					dev->srCache[i].lastUse < usage)
+				{
+					usage  = dev->srCache[i].lastUse;
+					theObj = dev->srCache[i].object;
+					cache = &dev->srCache[i];
+					pushout = i;
+				}
+			}
+		
+			if(!cache || cache->dirty)
+			{
+			
+				//printf("Dirty ");
+				yaffs_FlushFilesChunkCache(theObj);
+		
+				// Try again
+				cache = yaffs_GrabChunkCacheWorker(dev);
+			}
+			else
+			{
+				//printf(" pushout %d\n",pushout);
+			}
+			
+		}
+
+		return cache;
+	}
+	else
+		return NULL;
+
+}
+
+
+// Find a cached chunk
+static yaffs_ChunkCache *yaffs_FindChunkCache(const yaffs_Object *obj, int chunkId)
+{
+	yaffs_Device *dev = obj->myDev;
+	int i;
+	if(dev->nShortOpCaches > 0)
+	{
+		for(i = 0; i < dev->nShortOpCaches; i++)
+		{
+			if(dev->srCache[i].object == obj && 
+			dev->srCache[i].chunkId == chunkId)
+			{
+				dev->cacheHits++;
+			
+				return &dev->srCache[i];
+			}
+		}
+	}
+	return NULL;
+}
+
+// Mark the chunk for the least recently used algorithym
+static void yaffs_UseChunkCache(yaffs_Device *dev, yaffs_ChunkCache *cache, int isAWrite)
+{
+
+	if(dev->nShortOpCaches > 0)
+	{
+		if( dev->srLastUse < 0 || 
+			dev->srLastUse > 100000000)
+		{
+			// Reset the cache usages
+			int i;
+			for(i = 1; i < dev->nShortOpCaches; i++)
+			{
+				dev->srCache[i].lastUse = 0;
+			}
+			dev->srLastUse = 0;
+		}
+
+		dev->srLastUse++;
+	
+		cache->lastUse = dev->srLastUse;
+
+		if(isAWrite)
+		{
+			cache->dirty = 1;
+		}
+	}
+}
+
+// Invalidate a single cache page.
+// Do this when a whole page gets written,
+// ie the short cache for this page is no longer valid.
+static void yaffs_InvalidateChunkCache(yaffs_Object *object, int chunkId)
+{
+	if(object->myDev->nShortOpCaches > 0)
+	{
+		yaffs_ChunkCache *cache = yaffs_FindChunkCache(object,chunkId);
+
+		if(cache)
+		{
+			cache->object = NULL;
+		}
+	}
+}
+
+
+// Invalidate all the cache pages associated with this object
+// Do this whenever ther file is deleted or resized.
+static void yaffs_InvalidateWholeChunkCache(yaffs_Object *in)
+{
+	int i;
+	yaffs_Device *dev = in->myDev;
+	
+	if(dev->nShortOpCaches > 0)
+	{ 
+		// Now invalidate it.
+		for(i = 0; i < dev->nShortOpCaches; i++)
+		{
+			if(dev->srCache[i].object == in)
+			{
+				dev->srCache[i].object = NULL;
+			}
+		}
+	}
+}
+
+
+
+
+
+///////////////////////// File read/write ///////////////////////////////
+// Read and write have very similar structures.
+// In general the read/write has three parts to it
+// * An incomplete chunk to start with (if the read/write is not chunk-aligned)
+// * Some complete chunks
+// * An incomplete chunk to end off with
+//
+// Curve-balls: the first chunk might also be the last chunk.
+
+int yaffs_ReadDataFromFile(yaffs_Object *in, __u8 * buffer, __u32 offset, int nBytes)
+{
+	
+	
+	int chunk;
+	int start;
+	int nToCopy;
+	int n = nBytes;
+	int nDone = 0;
+	yaffs_ChunkCache *cache;
+	
+	yaffs_Device *dev;
+	
+	dev = in->myDev;
+	
+	while(n > 0)
+	{
+		chunk = offset / YAFFS_BYTES_PER_CHUNK + 1; // The first chunk is 1
+		start = offset % YAFFS_BYTES_PER_CHUNK;
+
+		// OK now check for the curveball where the start and end are in
+		// the same chunk.	
+		if(	(start + n) < YAFFS_BYTES_PER_CHUNK)
+		{
+			nToCopy = n;
+		}
+		else
+		{
+			nToCopy = YAFFS_BYTES_PER_CHUNK - start;
+		}
+	
+		cache = yaffs_FindChunkCache(in,chunk);
+		
+		// If the chunk is already in the cache or it is less than a whole chunk
+		// then use the cache (if there is caching)
+		// else bypass the cache.
+		if( cache || nToCopy != YAFFS_BYTES_PER_CHUNK)
+		{
+			if(dev->nShortOpCaches > 0)
+			{
+				
+				// If we can't find the data in the cache, then load it up.
+				
+				if(!cache)
+				{
+					cache = yaffs_GrabChunkCache(in->myDev);
+					cache->object = in;
+					cache->chunkId = chunk;
+					cache->dirty = 0;
+					yaffs_ReadChunkDataFromObject(in,chunk,cache->data);
+					cache->nBytes = 0;	
+				}
+			
+				yaffs_UseChunkCache(dev,cache,0);
+
+				memcpy(buffer,&cache->data[start],nToCopy);
+			}
+			else
+			{
+				// Read into the local buffer then copy...
+				yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);		
+				memcpy(buffer,&dev->localBuffer[start],nToCopy);
+			}
+
+		}
+		else
+		{
+#ifdef CONFIG_YAFFS_WINCE
+			
+			// Under WinCE can't do direct transfer. Need to use a local buffer.
+			// This is because we otherwise screw up WinCE's memory mapper
+			yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);
+			memcpy(buffer,dev->localBuffer,YAFFS_BYTES_PER_CHUNK);
+#else
+			// A full chunk. Read directly into the supplied buffer.
+			yaffs_ReadChunkDataFromObject(in,chunk,buffer);
+#endif
+		}
+		
+		n -= nToCopy;
+		offset += nToCopy;
+		buffer += nToCopy;
+		nDone += nToCopy;
+		
+	}
+	
+	return nDone;
+}
+
+
+
+int yaffs_WriteDataToFile(yaffs_Object *in,const __u8 * buffer, __u32 offset, int nBytes)
+{	
+	
+	int chunk;
+	int start;
+	int nToCopy;
+	int n = nBytes;
+	int nDone = 0;
+	int nToWriteBack;
+	int startOfWrite = offset;
+	int chunkWritten = 0;
+	int nBytesRead;
+	
+	yaffs_Device *dev;
+	
+	dev = in->myDev;
+	
+	
+	while(n > 0 && chunkWritten >= 0)
+	{
+		chunk = offset / YAFFS_BYTES_PER_CHUNK + 1;
+		start = offset % YAFFS_BYTES_PER_CHUNK;
+		
+
+		// OK now check for the curveball where the start and end are in
+		// the same chunk.
+		
+		if(	(start + n) < YAFFS_BYTES_PER_CHUNK)
+		{
+			nToCopy = n;
+			
+			// Now folks, to calculate how many bytes to write back....
+			// If we're overwriting and not writing to then end of file then
+			// we need to write back as much as was there before.
+			
+			nBytesRead = in->variant.fileVariant.fileSize - ((chunk -1) * YAFFS_BYTES_PER_CHUNK);
+			
+			if(nBytesRead > YAFFS_BYTES_PER_CHUNK)
+			{
+				nBytesRead = YAFFS_BYTES_PER_CHUNK;
+			}
+			
+			nToWriteBack = (nBytesRead > (start + n)) ? nBytesRead : (start +n);
+			
+		}
+		else
+		{
+			nToCopy = YAFFS_BYTES_PER_CHUNK - start;
+			nToWriteBack = YAFFS_BYTES_PER_CHUNK;
+		}
+	
+		if(nToCopy != YAFFS_BYTES_PER_CHUNK)
+		{
+			// An incomplete start or end chunk (or maybe both start and end chunk)
+			if(dev->nShortOpCaches > 0)
+			{
+				yaffs_ChunkCache *cache;
+				// If we can't find the data in the cache, then load it up.
+				cache = yaffs_FindChunkCache(in,chunk);
+				if(!cache && yaffs_CheckSpaceForChunkCache(in->myDev))
+				{
+					cache = yaffs_GrabChunkCache(in->myDev);
+					cache->object = in;
+					cache->chunkId = chunk;
+					cache->dirty = 0;
+					yaffs_ReadChunkDataFromObject(in,chunk,cache->data);		
+				}
+			
+				if(cache)
+				{	
+					yaffs_UseChunkCache(dev,cache,1);
+					memcpy(&cache->data[start],buffer,nToCopy);
+					cache->nBytes = nToWriteBack;
+				}
+				else
+				{
+					chunkWritten = -1; // fail the write
+				}
+			}
+			else
+			{
+				// An incomplete start or end chunk (or maybe both start and end chunk)
+				// Read into the local buffer then copy, then copy over and write back.
+		
+				yaffs_ReadChunkDataFromObject(in,chunk,dev->localBuffer);
+				
+				memcpy(&dev->localBuffer[start],buffer,nToCopy);
+			
+				chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,dev->localBuffer,nToWriteBack,0);
+			
+				//T(("Write with readback to chunk %d %d  start %d  copied %d wrote back %d\n",chunk,chunkWritten,start, nToCopy, nToWriteBack));
+			}
+			
+		}
+		else
+		{
+			
+#ifdef CONFIG_YAFFS_WINCE
+			// Under WinCE can't do direct transfer. Need to use a local buffer.
+			// This is because we otherwise screw up WinCE's memory mapper
+			memcpy(dev->localBuffer,buffer,YAFFS_BYTES_PER_CHUNK);
+			chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,dev->localBuffer,YAFFS_BYTES_PER_CHUNK,0);
+#else
+			// A full chunk. Write directly from the supplied buffer.
+			chunkWritten = yaffs_WriteChunkDataToObject(in,chunk,buffer,YAFFS_BYTES_PER_CHUNK,0);
+#endif
+			// Since we've overwritten the cached data, we better invalidate it.
+			yaffs_InvalidateChunkCache(in,chunk);
+			//T(("Write to chunk %d %d\n",chunk,chunkWritten));
+		}
+		
+		if(chunkWritten >= 0)
+		{
+			n -= nToCopy;
+			offset += nToCopy;
+			buffer += nToCopy;
+			nDone += nToCopy;
+		}
+		
+	}
+	
+	// Update file object
+	
+	if((startOfWrite + nDone) > in->variant.fileVariant.fileSize)
+	{
+		in->variant.fileVariant.fileSize = (startOfWrite + nDone);
+	}
+	
+	in->dirty = 1;
+	
+	return nDone;
+}
+
+
+int yaffs_ResizeFile(yaffs_Object *in, int newSize)
+{
+	int i;
+	int chunkId;
+	int oldFileSize = in->variant.fileVariant.fileSize;
+	int sizeOfPartialChunk = newSize % YAFFS_BYTES_PER_CHUNK;
+	
+	yaffs_Device *dev = in->myDev;
+	
+
+	yaffs_FlushFilesChunkCache(in);	
+	yaffs_InvalidateWholeChunkCache(in);
+	
+	if(in->variantType != YAFFS_OBJECT_TYPE_FILE)
+	{
+		return yaffs_GetFileSize(in);
+	}
+	
+	if(newSize < oldFileSize)
+	{
+		
+		int lastDel = 1 + (oldFileSize-1)/YAFFS_BYTES_PER_CHUNK;
+		
+		int startDel = 1 + (newSize + YAFFS_BYTES_PER_CHUNK - 1)/
+							YAFFS_BYTES_PER_CHUNK;
+
+		// Delete backwards so that we don't end up with holes if
+		// power is lost part-way through the operation.
+		for(i = lastDel; i >= startDel; i--)
+		{
+			// NB this could be optimised somewhat,
+			// eg. could retrieve the tags and write them without
+			// using yaffs_DeleteChunk
+
+			chunkId = yaffs_FindAndDeleteChunkInFile(in,i,NULL);
+			if(chunkId < (dev->startBlock * 32) || chunkId >= ((dev->endBlock+1) * 32))
+			{
+				//T(("Found daft chunkId %d for %d\n",chunkId,i));
+			}
+			else
+			{
+				in->nDataChunks--;
+				yaffs_DeleteChunk(dev,chunkId,1);
+			}
+		}
+		
+		
+		if(sizeOfPartialChunk != 0)
+		{
+			int lastChunk = 1+ newSize/YAFFS_BYTES_PER_CHUNK;
+			
+			// Got to read and rewrite the last chunk with its new size.
+			// NB Got to zero pad to nuke old data
+			yaffs_ReadChunkDataFromObject(in,lastChunk,dev->localBuffer);
+			memset(dev->localBuffer + sizeOfPartialChunk,0, YAFFS_BYTES_PER_CHUNK - sizeOfPartialChunk);
+
+			yaffs_WriteChunkDataToObject(in,lastChunk,dev->localBuffer,sizeOfPartialChunk,1);
+				
+		}
+		
+		in->variant.fileVariant.fileSize = newSize;
+		
+		yaffs_PruneFileStructure(dev,&in->variant.fileVariant);
+		
+		return newSize;
+		
+	}
+	else
+	{
+		return oldFileSize;
+	}
+}
+
+
+loff_t yaffs_GetFileSize(yaffs_Object *obj)
+{
+	obj = yaffs_GetEquivalentObject(obj);
+	
+	switch(obj->variantType)
+	{
+		case YAFFS_OBJECT_TYPE_FILE: 
+			return obj->variant.fileVariant.fileSize;
+		case YAFFS_OBJECT_TYPE_SYMLINK:
+			return strlen(obj->variant.symLinkVariant.alias);
+		default:
+			return 0;
+	}
+}
+
+
+
+// yaffs_FlushFile() updates the file's
+// objectId in NAND
+
+int yaffs_FlushFile(yaffs_Object *in, int updateTime)
+{
+	int retVal;
+	if(in->dirty)
+	{
+		//T(("flushing object header\n"));
+		
+		yaffs_FlushFilesChunkCache(in);
+		if(updateTime)
+		{
+#ifdef CONFIG_YAFFS_WINCE
+			yfsd_WinFileTimeNow(in->win_mtime);
+#else
+			in->st_mtime = Y_CURRENT_TIME;
+#endif
+		}
+
+		retVal = (yaffs_UpdateObjectHeader(in,NULL,0) >= 0)? YAFFS_OK : YAFFS_FAIL;
+	}
+	else
+	{
+		retVal = YAFFS_OK;
+	}
+	
+	return retVal;
+	
+}
+
+
+static int yaffs_DoGenericObjectDeletion(yaffs_Object *in)
+{
+
+	// First off, invalidate the file's data in the cache, without flushing.
+	yaffs_InvalidateWholeChunkCache(in);
+	
+	yaffs_RemoveObjectFromDirectory(in);
+	yaffs_DeleteChunk(in->myDev,in->chunkId,1);
+	in->chunkId = -1;
+#if 0
+#ifdef __KERNEL__
+	if(in->myInode)
+	{
+		in->myInode->u.generic_ip = NULL;
+		in->myInode = NULL;
+	}
+#endif
+#endif
+	yaffs_FreeObject(in);
+	return YAFFS_OK;
+
+}
+
+// yaffs_DeleteFile deletes the whole file data
+// and the inode associated with the file.
+// It does not delete the links associated with the file.
+static int yaffs_UnlinkFile(yaffs_Object *in)
+{
+
+#ifdef CONFIG_YAFFS_DISABLE_BACKGROUND_DELETION
+
+	// Delete the file data & tnodes
+
+	 yaffs_DeleteWorker(in, in->variant.fileVariant.top, in->variant.fileVariant.topLevel, 0,NULL);
+	 
+
+	yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);
+	
+	return  yaffs_DoGenericObjectDeletion(in);
+#else
+	int retVal;
+	int immediateDeletion=0;
+	retVal = yaffs_ChangeObjectName(in, in->myDev->unlinkedDir,NULL,0);
+	if(1 || // Ignore the result of the change name. This will only fail
+			// if the disk was completely full (an error condition)
+			// We ignore the error so we can delete files to recover the disk
+	   retVal == YAFFS_OK)
+	{
+		//in->unlinked = 1;
+		//in->myDev->nUnlinkedFiles++;
+		//in->renameAllowed = 0;
+#ifdef __KERNEL__
+		if(!in->myInode)
+		{
+			// Might be open at present,
+			// Caught by delete_inode in yaffs_fs.c
+			immediateDeletion = 1;
+
+		}
+#else
+		if(in->inUse <= 0)
+		{
+			immediateDeletion = 1;
+
+		}
+#endif
+		
+		if(immediateDeletion)
+		{
+			T(YAFFS_TRACE_TRACING,(TSTR("yaffs: immediate deletion of file %d" TENDSTR),in->objectId));
+			in->deleted=1;
+			in->myDev->nDeletedFiles++;
+			yaffs_SoftDeleteFile(in);
+		}
+	
+	}
+	return retVal;
+
+	
+#endif
+}
+
+int yaffs_DeleteFile(yaffs_Object *in)
+{
+	int retVal = YAFFS_OK;
+	
+	if(in->nDataChunks > 0)
+	{
+		// Use soft deletion
+		if(!in->unlinked)
+		{
+			retVal = yaffs_UnlinkFile(in);
+		}
+		if(retVal == YAFFS_OK && 
+		in->unlinked &&
+		!in->deleted)
+		{
+			in->deleted = 1;
+			in->myDev->nDeletedFiles++;
+			yaffs_SoftDeleteFile(in);
+		}
+		return in->deleted ? YAFFS_OK : YAFFS_FAIL;	
+	}
+	else
+	{
+		// The file has no data chunks so we toss it immediately
+		yaffs_FreeTnode(in->myDev,in->variant.fileVariant.top);
+		in->variant.fileVariant.top = NULL;
+		yaffs_DoGenericObjectDeletion(in);	
+		
+		return YAFFS_OK;	
+	}
+}
+
+static int yaffs_DeleteDirectory(yaffs_Object *in)
+{
+	//First check that the directory is empty.
+	if(list_empty(&in->variant.directoryVariant.children))
+	{
+		return  yaffs_DoGenericObjectDeletion(in);
+	}
+	
+	return YAFFS_FAIL;
+	
+}
+
+static int yaffs_DeleteSymLink(yaffs_Object *in)
+{
+	YFREE(in->variant.symLinkVariant.alias);
+
+	return  yaffs_DoGenericObjectDeletion(in);
+}
+
+static int yaffs_DeleteHardLink(yaffs_Object *in)
+{
+	// remove this hardlink from the list assocaited with the equivalent
+	// object
+	list_del(&in->hardLinks);
+	return  yaffs_DoGenericObjectDeletion(in);	
+}
+
+
+static void yaffs_AbortHalfCreatedObject(yaffs_Object *obj)
+{
+	switch(obj->variantType)
+	{
+		case YAFFS_OBJECT_TYPE_FILE: yaffs_DeleteFile(obj); break;
+		case YAFFS_OBJECT_TYPE_DIRECTORY: yaffs_DeleteDirectory(obj); break;
+		case YAFFS_OBJECT_TYPE_SYMLINK: yaffs_DeleteSymLink(obj); break;
+		case YAFFS_OBJECT_TYPE_HARDLINK: yaffs_DeleteHardLink(obj); break;
+		case YAFFS_OBJECT_TYPE_SPECIAL: yaffs_DoGenericObjectDeletion(obj); break;
+		case YAFFS_OBJECT_TYPE_UNKNOWN: break; // should not happen.
+	}
+}
+
+
+static int yaffs_UnlinkWorker(yaffs_Object *obj)
+{
+
+	
+	if(obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
+	{
+		return  yaffs_DeleteHardLink(obj);
+	}
+	else if(!list_empty(&obj->hardLinks))
+	{	
+		// Curve ball: We're unlinking an object that has a hardlink.
+		//
+		//	This problem arises because we are not strictly following
+		//  The Linux link/inode model.
+		//
+		// We can't really delete the object.
+		// Instead, we do the following:
+		// - Select a hardlink.
+		// - Unhook it from the hard links
+		// - Unhook it from its parent directory (so that the rename can work)
+		// - Rename the object to the hardlink's name.
+		// - Delete the hardlink
+		
+		
+		yaffs_Object *hl;
+		int retVal;
+		char name[YAFFS_MAX_NAME_LENGTH+1];
+		
+		hl = list_entry(obj->hardLinks.next,yaffs_Object,hardLinks);
+		
+		list_del_init(&hl->hardLinks);
+		list_del_init(&hl->siblings);
+		
+		yaffs_GetObjectName(hl,name,YAFFS_MAX_NAME_LENGTH+1);
+		
+		retVal = yaffs_ChangeObjectName(obj, hl->parent, name,0);
+		
+		if(retVal == YAFFS_OK)
+		{
+			retVal = yaffs_DoGenericObjectDeletion(hl);
+		}
+		return retVal;
+				
+	}
+	else
+	{
+		switch(obj->variantType)
+		{
+			case YAFFS_OBJECT_TYPE_FILE:
+				return yaffs_UnlinkFile(obj);
+				break;
+			case YAFFS_OBJECT_TYPE_DIRECTORY:
+				return yaffs_DeleteDirectory(obj);
+				break;
+			case YAFFS_OBJECT_TYPE_SYMLINK:
+				return yaffs_DeleteSymLink(obj);
+				break;
+			case YAFFS_OBJECT_TYPE_HARDLINK:
+			case YAFFS_OBJECT_TYPE_UNKNOWN:
+			default:
+				return YAFFS_FAIL;
+		}
+	}
+}
+
+int yaffs_Unlink(yaffs_Object *dir, const char *name)
+{
+	yaffs_Object *obj;
+	
+	 obj = yaffs_FindObjectByName(dir,name);
+	 
+	 if(obj && obj->unlinkAllowed)
+	 {
+	 	return yaffs_UnlinkWorker(obj);
+	 }
+	 
+	 return YAFFS_FAIL;
+	
+}
+
+//////////////// Initialisation Scanning /////////////////
+
+
+
+// For now we use the SmartMedia check.
+// We look at the blockStatus byte in the first two chunks
+// These must be 0xFF to pass as OK.
+// todo: this function needs to be modifyable foir different NAND types
+// and different chunk sizes.  Suggest make this into a per-device configurable
+// function.
+static int yaffs_IsBlockBad(yaffs_Device *dev, int blk)
+{
+	yaffs_Spare spare;
+	
+	yaffs_ReadChunkFromNAND(dev,blk * dev->nChunksPerBlock,NULL,&spare,1);
+#if 1
+	if(yaffs_CountBits(spare.blockStatus) < 7)
+	{
+		return 1;
+	}
+#else
+	if(spare.blockStatus != 0xFF)
+	{
+		return 1;
+	}
+#endif
+	yaffs_ReadChunkFromNAND(dev,blk * dev->nChunksPerBlock + 1,NULL,&spare,1);
+
+#if 1
+	if(yaffs_CountBits(spare.blockStatus) < 7)
+	{
+		return 1;
+	}
+#else
+	if(spare.blockStatus != 0xFF)
+	{
+		return 1;
+	}
+#endif
+	
+	return 0;
+	
+}
+
+static int yaffs_Scan(yaffs_Device *dev)
+{
+	yaffs_Spare spare;
+	yaffs_Tags tags;
+	int blk;
+	int chunk;
+	int c;
+	int deleted;
+	yaffs_BlockState state;
+	yaffs_Object *hardList = NULL;
+	yaffs_Object *hl;
+	yaffs_BlockInfo *bi;
+
+	
+	yaffs_ObjectHeader *oh;
+	yaffs_Object *in;
+	yaffs_Object *parent;
+	
+	__u8 chunkData[YAFFS_BYTES_PER_CHUNK];
+	
+	
+	// Scan all the blocks...
+	
+	for(blk = dev->startBlock; blk <= dev->endBlock; blk++)
+	{
+		deleted = 0;
+		bi = yaffs_GetBlockInfo(dev,blk);
+		yaffs_ClearChunkBits(dev,blk);
+		bi->pagesInUse = 0;
+		bi->softDeletions = 0;
+		state = YAFFS_BLOCK_STATE_SCANNING;
+		
+		
+		if(yaffs_IsBlockBad(dev,blk))
+		{
+			state = YAFFS_BLOCK_STATE_DEAD;
+			T(YAFFS_TRACE_BAD_BLOCKS,(TSTR("block %d is bad" TENDSTR),blk));
+		}
+		
+		// Read each chunk in the block.
+		
+		for(c = 0; c < dev->nChunksPerBlock && 
+				   state == YAFFS_BLOCK_STATE_SCANNING; c++)
+		{
+			// Read the spare area and decide what to do
+			chunk = blk * dev->nChunksPerBlock + c;
+			
+			yaffs_ReadChunkFromNAND(dev,chunk,NULL,&spare,1);
+
+			
+			// This block looks ok, now what's in this chunk?
+			yaffs_GetTagsFromSpare(dev,&spare,&tags);
+			
+			if(yaffs_CountBits(spare.pageStatus) < 6)
+			{
+				// A deleted chunk
+				deleted++;
+				dev->nFreeChunks ++;
+				//T((" %d %d deleted\n",blk,c));
+			}
+			else if(tags.objectId == YAFFS_UNUSED_OBJECT_ID)
+			{
+				// An unassigned chunk in the block
+				// This means that either the block is empty or 
+				// this is the one being allocated from
+				
+				if(c == 0)
+				{
+					// the block is unused
+					state = YAFFS_BLOCK_STATE_EMPTY;
+					dev->nErasedBlocks++;
+				}
+				else
+				{
+					// this is the block being allocated from
+					T(YAFFS_TRACE_SCAN,(TSTR(" Allocating from %d %d" TENDSTR),blk,c));
+					state = YAFFS_BLOCK_STATE_ALLOCATING;
+					dev->allocationBlock = blk;
+					dev->allocationPage = c;
+				}
+
+				dev->nFreeChunks += (dev->nChunksPerBlock - c);
+			}
+			else if(tags.chunkId > 0)
+			{
+				int endpos;
+				// A data chunk.
+				yaffs_SetChunkBit(dev,blk,c);
+				bi->pagesInUse++;
+								
+				in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,YAFFS_OBJECT_TYPE_FILE);
+				// PutChunkIntoFIle checks for a clash (two data chunks with
+				// the same chunkId).
+				yaffs_PutChunkIntoFile(in,tags.chunkId,chunk,1);
+				endpos = (tags.chunkId - 1)* YAFFS_BYTES_PER_CHUNK + tags.byteCount;
+				if(in->variant.fileVariant.scannedFileSize <endpos)
+				{
+					in->variant.fileVariant.scannedFileSize = endpos;
+#ifndef CONFIG_YAFFS_USE_HEADER_FILE_SIZE
+						in->variant.fileVariant.fileSize = 	
+							in->variant.fileVariant.scannedFileSize;
+#endif
+
+				}
+				//T((" %d %d data %d %d\n",blk,c,tags.objectId,tags.chunkId));	
+			}
+			else
+			{
+				// chunkId == 0, so it is an ObjectHeader.
+				// Thus, we read in the object header and make the object
+				yaffs_SetChunkBit(dev,blk,c);
+				bi->pagesInUse++;
+							
+				yaffs_ReadChunkFromNAND(dev,chunk,chunkData,NULL,1);
+				
+				oh = (yaffs_ObjectHeader *)chunkData;
+				
+				in = yaffs_FindOrCreateObjectByNumber(dev,tags.objectId,oh->type);
+				
+				if(in->valid)
+				{
+					// We have already filled this one. We have a duplicate and need to resolve it.
+					
+					unsigned existingSerial = in->serial;
+					unsigned newSerial = tags.serialNumber;
+					
+					if(((existingSerial+1) & 3) == newSerial)
+					{
+						// Use new one - destroy the exisiting one
+						yaffs_DeleteChunk(dev,in->chunkId,1);
+						in->valid = 0;
+					}
+					else
+					{
+						// Use existing - destroy this one.
+						yaffs_DeleteChunk(dev,chunk,1);
+					}
+				}
+				
+				if(!in->valid &&
+				   (tags.objectId == YAFFS_OBJECTID_ROOT ||
+				    tags.objectId == YAFFS_OBJECTID_LOSTNFOUND))
+				{
+					// We only load some info, don't fiddle with directory structure
+					in->valid = 1;
+					in->variantType = oh->type;
+	
+					in->st_mode  = oh->st_mode;
+#ifdef CONFIG_YAFFS_WINCE
+					in->win_atime[0] = oh->win_atime[0];
+					in->win_ctime[0] = oh->win_ctime[0];
+					in->win_mtime[0] = oh->win_mtime[0];
+					in->win_atime[1] = oh->win_atime[1];
+					in->win_ctime[1] = oh->win_ctime[1];
+					in->win_mtime[1] = oh->win_mtime[1];
+#else
+					in->st_uid   = oh->st_uid;
+					in->st_gid   = oh->st_gid;
+					in->st_atime = oh->st_atime;
+					in->st_mtime = oh->st_mtime;
+					in->st_ctime = oh->st_ctime;
+					in->st_rdev = oh->st_rdev;
+#endif
+					in->chunkId  = chunk;
+
+				}
+				else if(!in->valid)
+				{
+					// we need to load this info
+				
+					in->valid = 1;
+					in->variantType = oh->type;
+	
+					in->st_mode  = oh->st_mode;
+#ifdef CONFIG_YAFFS_WINCE
+					in->win_atime[0] = oh->win_atime[0];
+					in->win_ctime[0] = oh->win_ctime[0];
+					in->win_mtime[0] = oh->win_mtime[0];
+					in->win_atime[1] = oh->win_atime[1];
+					in->win_ctime[1] = oh->win_ctime[1];
+					in->win_mtime[1] = oh->win_mtime[1];
+#else
+					in->st_uid   = oh->st_uid;
+					in->st_gid   = oh->st_gid;
+					in->st_atime = oh->st_atime;
+					in->st_mtime = oh->st_mtime;
+					in->st_ctime = oh->st_ctime;
+					in->st_rdev = oh->st_rdev;
+#endif
+					in->chunkId  = chunk;
+
+					yaffs_SetObjectName(in,oh->name);
+					in->dirty = 0;
+							
+					// directory stuff...
+					// hook up to parent
+	
+					parent = yaffs_FindOrCreateObjectByNumber(dev,oh->parentObjectId,YAFFS_OBJECT_TYPE_DIRECTORY);
+					if(parent->variantType == YAFFS_OBJECT_TYPE_UNKNOWN)
+					{
+						// Set up as a directory
+						parent->variantType = YAFFS_OBJECT_TYPE_DIRECTORY;
+						INIT_LIST_HEAD(&parent->variant.directoryVariant.children);
+					}
+					else if(parent->variantType != YAFFS_OBJECT_TYPE_DIRECTORY)
+					{
+						// Hoosterman, another problem....
+						// We're trying to use a non-directory as a directory
+						
+						T(YAFFS_TRACE_ERROR, (TSTR("yaffs tragedy: attempting to use non-directory as a directory in scan. Put in lost+found." TENDSTR)));
+						parent = dev->lostNFoundDir;
+
+					}
+				
+					yaffs_AddObjectToDirectory(parent,in);
+					if(parent == dev->unlinkedDir)
+					{
+						in->deleted = 1; // If it is unlinked at start up then it wants deleting
+						dev->nDeletedFiles++;
+					}
+				
+					// Note re hardlinks.
+					// Since we might scan a hardlink before its equivalent object is scanned
+					// we put them all in a list.
+					// After scanning is complete, we should have all the objects, so we run through this
+					// list and fix up all the chains.		
+	
+					switch(in->variantType)
+					{
+						case YAFFS_OBJECT_TYPE_UNKNOWN: 	// Todo got a problem
+							break;
+						case YAFFS_OBJECT_TYPE_FILE:
+#ifdef CONFIG_YAFFS_USE_HEADER_FILE_SIZE
+							in->variant.fileVariant.fileSize = oh->fileSize;
+#endif
+							break;
+						case YAFFS_OBJECT_TYPE_HARDLINK:
+							in->variant.hardLinkVariant.equivalentObjectId = oh->equivalentObjectId;
+							in->hardLinks.next = (struct list_head *)hardList;
+							hardList = in;
+							break;
+						case YAFFS_OBJECT_TYPE_DIRECTORY:	// Do nothing
+							break;
+						case YAFFS_OBJECT_TYPE_SPECIAL:	// Do nothing
+							break;
+						case YAFFS_OBJECT_TYPE_SYMLINK: 	// Do nothing
+							in->variant.symLinkVariant.alias = yaffs_CloneString(oh->alias);
+							break;
+					}
+					//T((" %d %d header %d \"%s\" type %d\n",blk,c,tags.objectId,oh->name,in->variantType));	
+				}
+			}
+		}
+		
+		if(state == YAFFS_BLOCK_STATE_SCANNING)
+		{
+			// If we got this far while scanning, then the block is fully allocated.
+			state = YAFFS_BLOCK_STATE_FULL;	
+		}
+		
+		bi->blockState = state;
+		
+		// Now let's see if it was dirty
+		if(	bi->pagesInUse == 0 &&
+	        bi->blockState == YAFFS_BLOCK_STATE_FULL)
+	    {
+	    	yaffs_BlockBecameDirty(dev,blk);
+	    }
+
+	}
+	
+	// Fix up the hard link chains.
+	// We should now have scanned all the objects, now it's time to add these 
+	// hardlinks.
+	while(hardList)
+	{
+		hl = hardList;
+		hardList = (yaffs_Object *)(hardList->hardLinks.next);
+		
+		in = yaffs_FindObjectByNumber(dev,hl->variant.hardLinkVariant.equivalentObjectId);
+		
+		if(in)
+		{
+			// Add the hardlink pointers
+			hl->variant.hardLinkVariant.equivalentObject=in;
+			list_add(&hl->hardLinks,&in->hardLinks);
+		}
+		else
+		{
+			//Todo Need to report/handle this better.
+			// Got a problem... hardlink to a non-existant object
+			hl->variant.hardLinkVariant.equivalentObject=NULL;
+			INIT_LIST_HEAD(&hl->hardLinks);
+			
+		}
+		
+	}
+	
+	{
+		struct list_head *i;	
+		struct list_head *n;
+			
+		yaffs_Object *l;
+		// Soft delete all the unlinked files
+		list_for_each_safe(i,n,&dev->unlinkedDir->variant.directoryVariant.children)
+		{
+			if(i)
+			{
+				l = list_entry(i, yaffs_Object,siblings);
+				if(l->deleted)
+				{
+					yaffs_SoftDeleteFile(l);		
+				}
+			}
+		}	
+	}
+	
+	
+	return YAFFS_OK;
+}
+
+
+////////////////////////// Directory Functions /////////////////////////
+
+
+static void yaffs_AddObjectToDirectory(yaffs_Object *directory, yaffs_Object *obj)
+{
+
+	if(obj->siblings.prev == NULL)
+	{
+		// Not initialised
+		INIT_LIST_HEAD(&obj->siblings);
+		
+	}
+	else if(!list_empty(&obj->siblings))
+	{
+		// If it is holed up somewhere else, un hook it
+		list_del_init(&obj->siblings);
+	}
+	// Now add it
+	list_add(&obj->siblings,&directory->variant.directoryVariant.children);
+	obj->parent = directory;
+	
+	if(directory == obj->myDev->unlinkedDir)
+	{
+		obj->unlinked = 1;
+		obj->myDev->nUnlinkedFiles++;
+		obj->renameAllowed = 0;
+	}
+}
+
+static void yaffs_RemoveObjectFromDirectory(yaffs_Object *obj)
+{
+	list_del_init(&obj->siblings);
+	obj->parent = NULL;
+}
+
+yaffs_Object *yaffs_FindObjectByName(yaffs_Object *directory,const char *name)
+{
+	int sum;
+	
+	struct list_head *i;
+	char buffer[YAFFS_MAX_NAME_LENGTH+1];
+	
+	yaffs_Object *l;
+	
+	sum = yaffs_CalcNameSum(name);
+	
+	list_for_each(i,&directory->variant.directoryVariant.children)
+	{
+		if(i)
+		{
+			l = list_entry(i, yaffs_Object,siblings);
+		
+			// Special case for lost-n-found
+			if(l->objectId == YAFFS_OBJECTID_LOSTNFOUND)
+			{
+				if(yaffs_strcmp(name,YAFFS_LOSTNFOUND_NAME) == 0)
+				{
+					return l;
+				}
+			}
+			else if(yaffs_SumCompare(l->sum, sum))
+			{
+				// Do a real check
+				yaffs_GetObjectName(l,buffer,YAFFS_MAX_NAME_LENGTH);
+				if(yaffs_strcmp(name,buffer) == 0)
+				{
+					return l;
+				}
+			
+			}			
+		}
+	}
+	
+	return NULL;
+}
+
+
+int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *))
+{
+	struct list_head *i;	
+	yaffs_Object *l;
+	
+	
+	list_for_each(i,&theDir->variant.directoryVariant.children)
+	{
+		if(i)
+		{
+			l = list_entry(i, yaffs_Object,siblings);
+			if(l && !fn(l))
+			{
+				return YAFFS_FAIL;
+			}
+		}
+	}
+	
+	return YAFFS_OK;
+
+}
+
+
+// GetEquivalentObject dereferences any hard links to get to the
+// actual object.
+
+yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj)
+{
+	if(obj && obj->variantType == YAFFS_OBJECT_TYPE_HARDLINK)
+	{
+		// We want the object id of the equivalent object, not this one
+		obj = obj->variant.hardLinkVariant.equivalentObject;
+	}
+	return obj;
+
+}
+
+int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize)
+{
+	memset(name,0,buffSize);
+	
+	if(obj->objectId == YAFFS_OBJECTID_LOSTNFOUND)
+	{
+		strncpy(name,YAFFS_LOSTNFOUND_NAME,buffSize - 1);
+	}
+	else if(obj->chunkId <= 0)
+	{
+		char locName[20];
+		// make up a name
+		sprintf(locName,"%s%d",YAFFS_LOSTNFOUND_PREFIX,obj->objectId);
+		strncpy(name,locName,buffSize - 1);
+
+	}
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
+	else if(obj->shortName[0])
+	{
+		strcpy(name,obj->shortName);
+	}
+#endif
+	else
+	{
+		__u8 buffer[YAFFS_BYTES_PER_CHUNK];
+		yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
+
+		memset(buffer,0,YAFFS_BYTES_PER_CHUNK);
+	
+		if(obj->chunkId >= 0)
+		{
+			yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL,1);
+		}
+		strncpy(name,oh->name,buffSize - 1);
+	}
+	
+	return strlen(name);
+}
+
+int yaffs_GetObjectFileLength(yaffs_Object *obj)
+{
+	
+	// Dereference any hard linking
+	obj = yaffs_GetEquivalentObject(obj);
+	
+	if(obj->variantType == YAFFS_OBJECT_TYPE_FILE)
+	{
+		return obj->variant.fileVariant.fileSize;
+	}
+	if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
+	{
+		return strlen(obj->variant.symLinkVariant.alias);
+	}
+	else
+	{
+		// Only a directory should drop through to here
+		return YAFFS_BYTES_PER_CHUNK;
+	}	
+}
+
+int yaffs_GetObjectLinkCount(yaffs_Object *obj)
+{
+	int count = 0; 
+	struct list_head *i;
+	
+	if(!obj->unlinked)
+	{
+		count++;	// the object itself
+	}
+	list_for_each(i,&obj->hardLinks)
+	{
+		count++;	// add the hard links;
+	}
+	return count;
+	
+}
+
+
+int yaffs_GetObjectInode(yaffs_Object *obj)
+{
+	obj = yaffs_GetEquivalentObject(obj);
+	
+	return obj->objectId;
+}
+
+unsigned yaffs_GetObjectType(yaffs_Object *obj)
+{
+	obj = yaffs_GetEquivalentObject(obj);
+	
+	switch(obj->variantType)
+	{
+		case YAFFS_OBJECT_TYPE_FILE:		return DT_REG; break;
+		case YAFFS_OBJECT_TYPE_DIRECTORY:	return DT_DIR; break;
+		case YAFFS_OBJECT_TYPE_SYMLINK:		return DT_LNK; break;
+		case YAFFS_OBJECT_TYPE_HARDLINK:	return DT_REG; break;
+		case YAFFS_OBJECT_TYPE_SPECIAL:		
+			if(S_ISFIFO(obj->st_mode)) return DT_FIFO;
+			if(S_ISCHR(obj->st_mode)) return DT_CHR;
+			if(S_ISBLK(obj->st_mode)) return DT_BLK;
+			if(S_ISSOCK(obj->st_mode)) return DT_SOCK;
+		default: return DT_REG; break;
+	}
+}
+
+char *yaffs_GetSymlinkAlias(yaffs_Object *obj)
+{
+	obj = yaffs_GetEquivalentObject(obj);
+	if(obj->variantType == YAFFS_OBJECT_TYPE_SYMLINK)
+	{
+		return yaffs_CloneString(obj->variant.symLinkVariant.alias);
+	}
+	else
+	{
+		return yaffs_CloneString("");
+	}
+}
+
+#ifndef CONFIG_YAFFS_WINCE
+
+int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr)
+{
+	unsigned int valid = attr->ia_valid;
+	
+	if(valid & ATTR_MODE) obj->st_mode = attr->ia_mode;
+	if(valid & ATTR_UID) obj->st_uid = attr->ia_uid;
+	if(valid & ATTR_GID) obj->st_gid = attr->ia_gid;
+	
+	if(valid & ATTR_ATIME) obj->st_atime = Y_TIME_CONVERT(attr->ia_atime);
+	if(valid & ATTR_CTIME) obj->st_ctime = Y_TIME_CONVERT(attr->ia_ctime);
+	if(valid & ATTR_MTIME) obj->st_mtime = Y_TIME_CONVERT(attr->ia_mtime);
+
+	
+	if(valid & ATTR_SIZE) yaffs_ResizeFile(obj,attr->ia_size);
+	
+	yaffs_UpdateObjectHeader(obj,NULL,1);
+	
+	return YAFFS_OK;
+	
+}
+
+int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr)
+{
+	unsigned int valid = 0;
+	
+	attr->ia_mode = obj->st_mode;	valid |= ATTR_MODE;
+	attr->ia_uid = obj->st_uid;		valid |= ATTR_UID;
+	attr->ia_gid = obj->st_gid;		valid |= ATTR_GID;
+	
+	
+	Y_TIME_CONVERT(attr->ia_atime) = obj->st_atime;	valid |= ATTR_ATIME;
+	Y_TIME_CONVERT(attr->ia_ctime) = obj->st_ctime;	valid |= ATTR_CTIME;
+	Y_TIME_CONVERT(attr->ia_mtime) = obj->st_mtime;	valid |= ATTR_MTIME;
+
+	attr->ia_size = yaffs_GetFileSize(obj); valid |= ATTR_SIZE;
+	
+	attr->ia_valid = valid;
+	
+	return YAFFS_OK;
+	
+}
+
+#endif
+
+int yaffs_DumpObject(yaffs_Object *obj)
+{
+//	__u8 buffer[YAFFS_BYTES_PER_CHUNK];
+	char name[257];
+//	yaffs_ObjectHeader *oh = (yaffs_ObjectHeader *)buffer;
+
+//	memset(buffer,0,YAFFS_BYTES_PER_CHUNK);
+	
+//	if(obj->chunkId >= 0)
+//	{
+//		yaffs_ReadChunkFromNAND(obj->myDev,obj->chunkId,buffer,NULL);
+//	}
+	
+	yaffs_GetObjectName(obj,name,256);
+	
+	T(YAFFS_TRACE_ALWAYS,(TSTR("Object %d, inode %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d type %d size %d\n" TENDSTR),
+			obj->objectId,yaffs_GetObjectInode(obj), name, obj->dirty, obj->valid, obj->serial, 
+			obj->sum, obj->chunkId, yaffs_GetObjectType(obj), yaffs_GetObjectFileLength(obj)));
+
+#if 0
+	YPRINTF(("Object %d \"%s\"\n dirty %d valid %d serial %d sum %d chunk %d\n",
+			obj->objectId, oh->name, obj->dirty, obj->valid, obj->serial, 
+			obj->sum, obj->chunkId));
+		switch(obj->variantType)
+	{
+		case YAFFS_OBJECT_TYPE_FILE: 
+			YPRINTF((" FILE length %d\n",obj->variant.fileVariant.fileSize));
+			break;
+		case YAFFS_OBJECT_TYPE_DIRECTORY:
+			YPRINTF((" DIRECTORY\n"));
+			break;
+		case YAFFS_OBJECT_TYPE_HARDLINK: //todo
+		case YAFFS_OBJECT_TYPE_SYMLINK:
+		case YAFFS_OBJECT_TYPE_UNKNOWN:
+		default:
+	}
+#endif
+	
+	return YAFFS_OK;
+}
+
+
+///////////////////////// Initialisation code ///////////////////////////
+
+
+
+int yaffs_GutsInitialise(yaffs_Device *dev)
+{
+	unsigned x;
+	int bits;
+	int extraBits;
+	int nBlocks;
+
+	if(	dev->nBytesPerChunk != YAFFS_BYTES_PER_CHUNK ||	
+		dev->nChunksPerBlock < 2 ||
+		dev->nReservedBlocks < 2 ||
+		dev->startBlock <= 0 ||
+		dev->endBlock <= 0 ||
+		dev->endBlock <= (dev->startBlock + dev->nReservedBlocks)
+	  )
+	{
+		//these parameters must be set before stating yaffs
+		// Other parameters startBlock,
+		return YAFFS_FAIL;
+	}
+
+
+	
+	if(!yaffs_CheckStructures())
+	{
+		T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs_CheckStructures failed\n" TENDSTR)));
+		return YAFFS_FAIL;
+	}
+
+	if(dev->isMounted)
+	{
+		T(YAFFS_TRACE_ALWAYS,(TSTR("yaffs: device already mounted\n" TENDSTR)));
+		return YAFFS_FAIL;
+	}
+
+	dev->isMounted = 1;
+	
+	if(dev->startBlock <= 0 ||
+	   (dev->endBlock - dev->startBlock) < 10)
+	{
+		T(YAFFS_TRACE_ALWAYS,(TSTR("startBlock %d or endBlock %d invalid\n" TENDSTR),
+				dev->startBlock, dev->endBlock));
+		return YAFFS_FAIL;
+	}
+	
+	nBlocks = dev->endBlock - dev->startBlock + 1;
+	
+
+		
+	// OK now calculate a few things for the device
+	// Calculate chunkGroupBits. 
+	// We need to find the next power of 2 > than endBlock
+	
+	x = dev->nChunksPerBlock * (dev->endBlock+1);
+	
+	for(bits = extraBits = 0; x > 1; bits++)
+	{
+		if(x & 1) extraBits++;
+		x >>= 1;
+	}
+
+	if(extraBits > 0) bits++;
+	
+	
+	// Level0 Tnodes are 16 bits, so if the bitwidth of the
+	// chunk range we're using is greater than 16 we need 
+	// to figure out chunk shift and chunkGroupSize
+	if(bits <= 16) 
+	{
+		dev->chunkGroupBits = 0;
+	}
+	else
+	{
+		dev->chunkGroupBits = bits - 16;
+	}
+	
+	dev->chunkGroupSize = 1 << dev->chunkGroupBits;
+
+	if(dev->nChunksPerBlock < dev->chunkGroupSize)
+	{
+		// We have a problem because the soft delete won't work if
+		// the chunk group size > chunks per block.
+		// This can be remedied by using larger "virtual blocks".
+		
+		return YAFFS_FAIL;
+	}
+
+	
+	
+	
+	// More device initialisation
+	dev->garbageCollections = 0;
+	dev->passiveGarbageCollections = 0;
+	dev->currentDirtyChecker = 0;
+	dev->bufferedBlock = -1;
+	dev->doingBufferedBlockRewrite = 0;
+	dev->nDeletedFiles = 0;
+	dev->nBackgroundDeletions=0;
+	dev->nUnlinkedFiles = 0;
+	dev->eccFixed=0;
+	dev->eccUnfixed=0;
+	dev->tagsEccFixed=0;
+	dev->tagsEccUnfixed=0;
+	dev->nErasedBlocks=0;
+	
+	dev->localBuffer = YMALLOC(dev->nBytesPerChunk);
+	
+
+	
+	
+	yaffs_InitialiseBlocks(dev,nBlocks);
+	
+	yaffs_InitialiseTnodes(dev);
+
+	yaffs_InitialiseObjects(dev);
+	
+	if(dev->nShortOpCaches > 0)
+	{ 
+		int i;
+		
+		if(dev->nShortOpCaches >  YAFFS_MAX_SHORT_OP_CACHES)
+		{
+			dev->nShortOpCaches = YAFFS_MAX_SHORT_OP_CACHES;
+		}
+		
+		dev->srCache = YMALLOC( dev->nShortOpCaches * sizeof(yaffs_ChunkCache));
+		
+		for(i=0; i < dev->nShortOpCaches; i++)
+		{
+			dev->srCache[i].object = NULL;
+			dev->srCache[i].lastUse = 0;
+			dev->srCache[i].dirty = 0;
+		}
+		dev->srLastUse = 0;
+	}
+
+	dev->cacheHits = 0;
+	
+	
+	// Initialise the unlinked, root and lost and found directories
+	dev->lostNFoundDir = dev->rootDir = dev->unlinkedDir = NULL;
+	
+	dev->unlinkedDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_UNLINKED, S_IFDIR);
+
+	dev->rootDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_ROOT,YAFFS_ROOT_MODE | S_IFDIR);
+	dev->lostNFoundDir = yaffs_CreateFakeDirectory(dev,YAFFS_OBJECTID_LOSTNFOUND,YAFFS_LOSTNFOUND_MODE | S_IFDIR);
+	yaffs_AddObjectToDirectory(dev->rootDir,dev->lostNFoundDir);
+	
+		
+	// Now scan the flash.	
+	yaffs_Scan(dev);
+	
+	// Zero out stats
+	dev->nPageReads = 0;
+    dev->nPageWrites =  0;
+	dev->nBlockErasures = 0;
+	dev->nGCCopies = 0;
+	dev->nRetriedWrites = 0;
+	dev->nRetiredBlocks = 0;
+
+	
+	return YAFFS_OK;
+		
+}
+
+void yaffs_Deinitialise(yaffs_Device *dev)
+{
+	if(dev->isMounted)
+	{
+	
+		yaffs_DeinitialiseBlocks(dev);
+		yaffs_DeinitialiseTnodes(dev);
+		yaffs_DeinitialiseObjects(dev);
+		if(dev->nShortOpCaches > 0)
+			YFREE(dev->srCache);
+		YFREE(dev->localBuffer);
+	}
+	
+}
+
+#if 0
+
+int  yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
+{
+	int nFree = dev->nFreeChunks - (dev->nChunksPerBlock * YAFFS_RESERVED_BLOCKS);
+	
+	struct list_head *i;	
+	yaffs_Object *l;
+	
+	
+	// To the free chunks add the chunks that are in the deleted unlinked files.
+	list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
+	{
+		l = list_entry(i, yaffs_Object,siblings);
+		if(l->deleted)
+		{
+			nFree++;
+			nFree += l->nDataChunks;
+		}
+	}
+	
+	
+	// printf("___________ nFreeChunks is %d nFree is %d\n",dev->nFreeChunks,nFree);	
+
+	if(nFree < 0) nFree = 0;
+
+	return nFree;	
+	
+}
+
+#endif
+
+int  yaffs_GetNumberOfFreeChunks(yaffs_Device *dev)
+{
+	int nFree;
+	int pending;
+	int b;
+	int nDirtyCacheChunks=0;
+	
+	yaffs_BlockInfo *blk;
+	
+	struct list_head *i;	
+	yaffs_Object *l;
+	
+	for(nFree = 0, b = dev->startBlock; b <= dev->endBlock; b++)
+	{
+		blk = yaffs_GetBlockInfo(dev,b);
+		
+		switch(blk->blockState)
+		{
+			case YAFFS_BLOCK_STATE_EMPTY:
+			case YAFFS_BLOCK_STATE_ALLOCATING: 
+			case YAFFS_BLOCK_STATE_FULL: nFree += (dev->nChunksPerBlock - blk->pagesInUse); break;
+			default: break;
+		}
+	}
+	
+	
+	pending = 0;
+	
+	// To the free chunks add the chunks that are in the deleted unlinked files.
+	list_for_each(i,&dev->unlinkedDir->variant.directoryVariant.children)
+	{
+		if(i)
+		{
+			l = list_entry(i, yaffs_Object,siblings);
+			if(l->deleted)
+			{
+				pending++;
+				pending += l->nDataChunks;
+			}
+		}
+	}
+	
+	
+	
+	//printf("___________ really free is %d, pending %d, nFree is %d\n",nFree,pending, nFree+pending);
+	
+	if(nFree != dev->nFreeChunks) 
+	{
+	//	printf("___________Different! really free is %d, nFreeChunks %d\n",nFree dev->nFreeChunks);
+	}
+
+	nFree += pending;
+	
+	// Now count the number of dirty chunks in the cache and subtract those
+	
+	{
+		int i;
+		for(i = 0; i < dev->nShortOpCaches; i++)
+		{
+			if(dev->srCache[i].dirty) nDirtyCacheChunks++;
+		}
+	}
+	
+	nFree -= nDirtyCacheChunks;
+	
+	nFree -= ((dev->nReservedBlocks + 1) * dev->nChunksPerBlock);
+	
+	if(nFree < 0) nFree = 0;
+
+	return nFree;	
+	
+}
+
+
+
+/////////////////// YAFFS test code //////////////////////////////////
+
+#define yaffs_CheckStruct(structure,syze, name) \
+           if(sizeof(structure) != syze) \
+	       { \
+	         T(YAFFS_TRACE_ALWAYS,(TSTR("%s should be %d but is %d\n" TENDSTR),name,syze,sizeof(structure))); \
+	         return YAFFS_FAIL; \
+		   }
+		 
+		 
+static int yaffs_CheckStructures(void)
+{
+	yaffs_CheckStruct(yaffs_Tags,8,"yaffs_Tags")
+	yaffs_CheckStruct(yaffs_TagsUnion,8,"yaffs_TagsUnion")
+	yaffs_CheckStruct(yaffs_Spare,16,"yaffs_Spare")
+#ifndef CONFIG_YAFFS_TNODE_LIST_DEBUG
+	yaffs_CheckStruct(yaffs_Tnode,2* YAFFS_NTNODES_LEVEL0,"yaffs_Tnode")
+#endif
+	yaffs_CheckStruct(yaffs_ObjectHeader,512,"yaffs_ObjectHeader")
+	
+	
+	return YAFFS_OK;
+}
+
+#if 0
+void yaffs_GutsTest(yaffs_Device *dev)
+{
+	
+	if(yaffs_CheckStructures() != YAFFS_OK)
+	{
+		T(YAFFS_TRACE_ALWAYS,(TSTR("One or more structures malformed-- aborting\n" TENDSTR)));
+		return;
+	}
+	
+	yaffs_TnodeTest(dev);
+	yaffs_ObjectTest(dev);	
+}
+#endif
+
+
+
+
+
+
diff -uNrb linux-2.4.30/fs/yaffs/yaffs_guts.h linux/fs/yaffs/yaffs_guts.h
--- linux-2.4.30/fs/yaffs/yaffs_guts.h	Thu Jan  1 03:00:00 1970
+++ linux/fs/yaffs/yaffs_guts.h	Tue Jan 11 14:08:51 2005
@@ -0,0 +1,593 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * yaffs_guts.h: Configuration etc for yaffs_guts
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ *
+ * $Id: yaffs_guts.h,v 1.18 2004/10/20 20:12:43 charles Exp $
+ */
+
+#ifndef __YAFFS_GUTS_H__
+#define __YAFFS_GUTS_H__
+
+#include "yportenv.h"
+
+#define YAFFS_OK	1
+#define YAFFS_FAIL  0
+
+// Give us a Y=0x59, 
+// Give us an A=0x41, 
+// Give us an FF=0xFF 
+// Give us an S=0x53
+// And what have we got... 
+#define YAFFS_MAGIC					0x5941FF53
+
+#define YAFFS_NTNODES_LEVEL0	  	16
+#define YAFFS_TNODES_LEVEL0_BITS	4
+#define YAFFS_TNODES_LEVEL0_MASK	0xf
+
+#define YAFFS_NTNODES_INTERNAL 		(YAFFS_NTNODES_LEVEL0 / 2)
+#define YAFFS_TNODES_INTERNAL_BITS 	(YAFFS_TNODES_LEVEL0_BITS - 1)
+#define YAFFS_TNODES_INTERNAL_MASK	0x7
+#define YAFFS_TNODES_MAX_LEVEL		6
+		
+#define YAFFS_BYTES_PER_SPARE		16
+
+#define YAFFS_BYTES_PER_CHUNK		512
+//#define YAFFS_CHUNK_SIZE_SHIFT		9
+
+
+#define YAFFS_CHUNKS_PER_BLOCK		32
+#define YAFFS_BYTES_PER_BLOCK		(YAFFS_CHUNKS_PER_BLOCK*YAFFS_BYTES_PER_CHUNK)
+
+#define YAFFS_MAX_CHUNK_ID			0x000FFFFF
+
+#define YAFFS_UNUSED_OBJECT_ID		0x0003FFFF
+
+#define YAFFS_ALLOCATION_NOBJECTS	100
+#define YAFFS_ALLOCATION_NTNODES	100
+#define YAFFS_ALLOCATION_NLINKS		100
+
+#define YAFFS_NOBJECT_BUCKETS		256
+
+
+#define YAFFS_OBJECT_SPACE			0x40000
+#define YAFFS_MAX_NAME_LENGTH		255
+#define YAFFS_SHORT_NAME_LENGTH		15
+
+#define YAFFS_MAX_ALIAS_LENGTH		159
+
+#define YAFFS_OBJECTID_ROOT			1
+#define YAFFS_OBJECTID_LOSTNFOUND	2
+#define YAFFS_OBJECTID_UNLINKED		3
+
+#define YAFFS_MAX_SHORT_OP_CACHES	20
+
+
+// ChunkCache is used for short read/write operations.
+typedef struct
+{
+	struct yaffs_ObjectStruct *object;
+	int chunkId;
+	int lastUse;
+	int dirty;	
+	int nBytes;	// Only valid if the cache is dirty
+	__u8 data[YAFFS_BYTES_PER_CHUNK];
+} yaffs_ChunkCache;
+
+// Tags structures in RAM
+// NB This uses bitfield. Bitfields should not straddle a u32 boundary otherwise
+// the structure size will get blown out.
+
+typedef struct
+{
+    unsigned chunkId:20;
+    unsigned serialNumber:2;
+    unsigned byteCount:10;
+    unsigned objectId:18;
+    unsigned ecc:12;
+    unsigned unusedStuff:2;
+} yaffs_Tags;
+
+typedef union
+{
+    yaffs_Tags asTags;
+    __u8       asBytes[8];
+} yaffs_TagsUnion;
+
+
+// Spare structure
+typedef struct
+{
+    __u8  tagByte0;
+    __u8  tagByte1;
+    __u8  tagByte2;
+    __u8  tagByte3;
+    __u8  pageStatus; 	// set to 0 to delete the chunk
+    __u8  blockStatus;
+    __u8  tagByte4;
+    __u8  tagByte5;
+    __u8  ecc1[3];
+    __u8  tagByte6;
+    __u8  tagByte7;
+    __u8  ecc2[3];
+} yaffs_Spare;
+
+//Special structure for passing through to mtd
+struct yaffs_NANDSpare {
+	yaffs_Spare	spare;
+	int		eccres1;
+	int		eccres2;
+};
+
+// Block data in RAM
+
+typedef enum {
+	YAFFS_BLOCK_STATE_UNKNOWN	= 0,
+	YAFFS_BLOCK_STATE_SCANNING,		// Used while the block is being scanned.
+									// NB Don't erase blocks while they're being scanned
+	
+	YAFFS_BLOCK_STATE_EMPTY,		// This block is empty
+	
+	YAFFS_BLOCK_STATE_ALLOCATING,	// This block is partially allocated. 
+									// This is the one currently being used for page
+									// allocation. Should never be more than one of these
+							
+
+	YAFFS_BLOCK_STATE_FULL,			// All the pages in this block have been allocated.
+									// At least one page holds valid data.
+							 
+	YAFFS_BLOCK_STATE_DIRTY,		// All pages have been allocated and deleted. 
+									// Erase me, reuse me.
+							
+	YAFFS_BLOCK_STATE_DEAD 			// This block has failed and is not in use
+
+} yaffs_BlockState;
+
+
+
+
+typedef struct
+{
+#ifndef CONFIG_YAFFS_NO_YAFFS2
+	__u32 sequenceNumber;	// block sequence number for yaffs2
+#endif
+	int   softDeletions:8;  // number of soft deleted pages
+    int   pagesInUse:8;		// number of pages in use
+    __u32 blockState:4; 	// One of the above block states
+    __u32 needsRetiring:1;	// Data has failed on this block, need to get valid data off
+    						// and retire the block.
+} yaffs_BlockInfo;
+
+
+//////////////////// Object structure ///////////////////////////
+// This is the object structure as stored on NAND
+
+typedef enum
+{
+	YAFFS_OBJECT_TYPE_UNKNOWN,
+	YAFFS_OBJECT_TYPE_FILE,
+	YAFFS_OBJECT_TYPE_SYMLINK,
+	YAFFS_OBJECT_TYPE_DIRECTORY,
+	YAFFS_OBJECT_TYPE_HARDLINK,
+	YAFFS_OBJECT_TYPE_SPECIAL
+} yaffs_ObjectType;
+
+typedef struct
+{
+	yaffs_ObjectType type;
+
+	// Apply to everything	
+	int   parentObjectId;
+	__u16 sum__NoLongerUsed;	// checksum of name. Calc this off the name to prevent inconsistencies
+	char  name[YAFFS_MAX_NAME_LENGTH + 1];
+
+	// Thes following apply to directories, files, symlinks - not hard links
+	__u32 st_mode;  // protection
+
+#ifdef CONFIG_YAFFS_WINCE
+	__u32 notForWinCE[5];
+#else
+	__u32 st_uid;   // user ID of owner
+	__u32 st_gid;    // group ID of owner 
+	__u32 st_atime; // time of last access
+	__u32 st_mtime; // time of last modification
+	__u32 st_ctime; // time of last change
+#endif
+
+	// File size  applies to files only
+	int fileSize; 
+		
+	// Equivalent object id applies to hard links only.
+	int  equivalentObjectId;
+	
+	// Alias is for symlinks only.
+	char alias[YAFFS_MAX_ALIAS_LENGTH + 1];
+	
+	__u32 st_rdev;  // device stuff for block and char devices (maj/min)
+	
+#ifdef CONFIG_YAFFS_WINCE
+	__u32 win_ctime[2];
+	__u32 win_atime[2];
+	__u32 win_mtime[2];
+	__u32 roomToGrow[6];
+#else
+	__u32 roomToGrow[12];
+#endif
+	
+} yaffs_ObjectHeader;
+
+
+
+////////////////////  Tnode ///////////////////////////
+
+union yaffs_Tnode_union
+{
+#ifdef CONFIG_YAFFS_TNODE_LIST_DEBUG
+	union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL+1];
+#else
+	union yaffs_Tnode_union *internal[YAFFS_NTNODES_INTERNAL];
+#endif
+	__u16 level0[YAFFS_NTNODES_LEVEL0];
+	
+};
+
+typedef union yaffs_Tnode_union yaffs_Tnode;
+
+struct yaffs_TnodeList_struct
+{
+	struct yaffs_TnodeList_struct *next;
+	yaffs_Tnode *tnodes;
+};
+
+typedef struct yaffs_TnodeList_struct yaffs_TnodeList;
+
+
+
+///////////////////  Object ////////////////////////////////
+// An object can be one of:
+// - a directory (no data, has children links
+// - a regular file (data.... not prunes :->).
+// - a symlink [symbolic link] (the alias).
+// - a hard link
+
+
+typedef struct 
+{
+	__u32 fileSize;
+	__u32 scannedFileSize;
+	int   topLevel;
+	yaffs_Tnode *top;
+} yaffs_FileStructure;
+
+typedef struct
+{
+	struct list_head children; // list of child links
+} yaffs_DirectoryStructure;
+
+typedef struct
+{
+	char *alias;
+} yaffs_SymLinkStructure;
+
+typedef struct
+{
+	struct yaffs_ObjectStruct *equivalentObject;
+	__u32	equivalentObjectId;
+} yaffs_HardLinkStructure;
+
+typedef union
+{
+	yaffs_FileStructure fileVariant;
+	yaffs_DirectoryStructure directoryVariant;
+	yaffs_SymLinkStructure symLinkVariant;
+	yaffs_HardLinkStructure hardLinkVariant;
+} yaffs_ObjectVariant;
+
+
+struct  yaffs_ObjectStruct
+{
+	__u8 deleted: 1;		// This should only apply to unlinked files.
+	__u8 softDeleted: 1;	// it has also been soft deleted
+	__u8 unlinked: 1;		// An unlinked file. The file should be in the unlinked pseudo directory.
+	__u8 fake:1;			// A fake object has no presence on NAND.
+	__u8 renameAllowed:1;
+	__u8 unlinkAllowed:1;
+	__u8 dirty:1;			// the object needs to be written to flash
+	__u8 valid:1;			// When the file system is being loaded up, this 
+							// object might be created before the data
+							// is available (ie. file data records appear before the header).
+	__u8 serial;			// serial number of chunk in NAND. Store here so we don't have to
+							// read back the old one to update.
+	__u16 sum;				// sum of the name to speed searching
+	
+	struct yaffs_DeviceStruct *myDev; // The device I'm on
+	
+								
+	struct list_head hashLink;	// list of objects in this hash bucket
+							
+
+	struct list_head hardLinks; // all the equivalent hard linked objects
+								// live on this list
+	// directory structure stuff
+	struct yaffs_ObjectStruct  *parent;	//my parent directory
+	struct list_head siblings;	// siblings in a directory
+								// also used for linking up the free list
+		
+	// Where's my data in NAND?
+	int chunkId;		// where it lives
+
+	int nDataChunks;	
+	
+	__u32 objectId;		// the object id value
+	
+	
+	__u32 st_mode;  	// protection
+
+#ifdef CONFIG_YAFFS_SHORT_NAMES_IN_RAM
+	char shortName[YAFFS_SHORT_NAME_LENGTH+1];
+#endif
+
+#ifndef __KERNEL__
+	__u32 inUse;
+#endif
+
+#ifdef CONFIG_YAFFS_WINCE
+	__u32 win_ctime[2];
+	__u32 win_mtime[2];
+	__u32 win_atime[2];
+#else
+	__u32 st_uid;   	// user ID of owner
+	__u32 st_gid;    	// group ID of owner 
+	__u32 st_atime; 	// time of last access
+	__u32 st_mtime; 	// time of last modification
+	__u32 st_ctime; 	// time of last change
+#endif
+
+	__u32 st_rdev; 	    // device stuff for block and char devices
+
+
+
+#ifdef __KERNEL__
+	struct inode *myInode;
+	__u8  deferedFree;   // YAFFS has removed the object from NAND, but it is being kept
+			     // Alive until the inode is cleared to prevent inode inconsistencies.
+#endif
+
+
+	
+	yaffs_ObjectType variantType;
+	
+	yaffs_ObjectVariant variant;
+	
+};
+
+
+
+typedef struct yaffs_ObjectStruct yaffs_Object;
+
+
+struct yaffs_ObjectList_struct
+{
+	yaffs_Object *objects;
+	struct yaffs_ObjectList_struct *next;
+};
+
+typedef struct yaffs_ObjectList_struct yaffs_ObjectList;
+
+typedef struct
+{
+	struct list_head list;
+	int count;
+} yaffs_ObjectBucket;
+
+
+//////////////////// Device ////////////////////////////////
+
+struct yaffs_DeviceStruct
+{
+	// Entry parameters set up way early. Yaffs sets up the rest.
+	int   nBytesPerChunk; 	 // Should be a power of 2 >= 512
+	int	  nChunksPerBlock;	 // does not need to be a power of 2
+	int   startBlock;		 // Start block we're allowed to use
+	int   endBlock;			 // End block we're allowed to use
+	int   nReservedBlocks;	 // We want this tuneable so that we can reduce
+							 // reserved blocks on NOR and RAM.
+	
+	int   useNANDECC;		// Flag to decide whether or not to use NANDECC
+	int   nShortOpCaches;	// If <= 0, then short op caching is disabled, else
+							// the number of short op caches (don't use too many).
+	
+	
+	void *genericDevice; // Pointer to device context
+						 // On an mtd this holds the mtd pointer.
+
+	// NAND access functions (Must be set before calling YAFFS)
+	
+	int (*writeChunkToNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare);
+	int (*readChunkFromNAND)(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare);
+	int (*eraseBlockInNAND)(struct yaffs_DeviceStruct *dev,int blockInNAND);	
+	int (*initialiseNAND)(struct yaffs_DeviceStruct *dev);
+
+	// Runtime parameters. Set up by YAFFS.
+	
+	__u16 chunkGroupBits; // 0 for devices <= 32MB. else log2(nchunks) - 16
+	__u16 chunkGroupSize; // == 2^^chunkGroupBits
+	
+#ifdef __KERNEL__
+
+	struct semaphore sem;// Semaphore for waiting on erasure.
+	struct semaphore grossLock; // Gross locking semaphore
+
+#endif	
+#ifdef __KERNEL__
+	void (*putSuperFunc)(struct super_block *sb);
+#endif
+
+	int isMounted;
+	
+	// Block Info
+	yaffs_BlockInfo *blockInfo;
+	__u8 *chunkBits;   // bitmap of chunks in use
+	int   chunkBitmapStride; // Number of bytes of chunkBits per block. 
+							 //	Must be consistent with nChunksPerBlock.
+
+
+	int   nErasedBlocks;
+	int   allocationBlock;			// Current block being allocated off
+	__u32 allocationPage;
+	int   allocationBlockFinder;	// Used to search for next allocation block
+	
+	// Runtime state
+	int   nTnodesCreated;	
+	yaffs_Tnode *freeTnodes;
+	int  nFreeTnodes;
+	yaffs_TnodeList *allocatedTnodeList;
+
+
+	int   nObjectsCreated;
+	yaffs_Object *freeObjects;
+	int   nFreeObjects;
+
+	yaffs_ObjectList *allocatedObjectList;
+
+	yaffs_ObjectBucket objectBucket[YAFFS_NOBJECT_BUCKETS];
+
+	int	  nFreeChunks;
+		
+	int   currentDirtyChecker;	// Used to find current dirtiest block
+	
+	
+	// Operations since mount
+	int nPageWrites;
+	int nPageReads;
+	int nBlockErasures;
+	int nGCCopies;
+	int garbageCollections;
+	int passiveGarbageCollections;
+	int nRetriedWrites;
+	int nRetiredBlocks;
+	int eccFixed;
+	int eccUnfixed;
+	int tagsEccFixed;
+	int tagsEccUnfixed;
+	int nDeletions;
+	int nUnmarkedDeletions;
+	
+	yaffs_Object *rootDir;
+	yaffs_Object *lostNFoundDir;
+	
+	// Buffer areas for storing data to recover from write failures
+//	__u8 		bufferedData[YAFFS_CHUNKS_PER_BLOCK][YAFFS_BYTES_PER_CHUNK];
+//	yaffs_Spare bufferedSpare[YAFFS_CHUNKS_PER_BLOCK];
+	int bufferedBlock;	// Which block is buffered here?
+	int doingBufferedBlockRewrite;
+
+	yaffs_ChunkCache *srCache;
+	int srLastUse;
+
+	int cacheHits;
+
+	// Stuff for background deletion and unlinked files.
+	yaffs_Object *unlinkedDir;		// Directory where unlinked and deleted files live.
+	yaffs_Object *unlinkedDeletion;	// Current file being background deleted.
+	int nDeletedFiles;				// Count of files awaiting deletion;
+	int nUnlinkedFiles;				// Count of unlinked files. 
+	int nBackgroundDeletions;			// Count of background deletions.	
+	
+	__u8 *localBuffer;
+	
+};
+
+typedef struct yaffs_DeviceStruct yaffs_Device;
+
+
+
+//////////// YAFFS Functions //////////////////
+
+int yaffs_GutsInitialise(yaffs_Device *dev);
+void yaffs_Deinitialise(yaffs_Device *dev);
+
+int yaffs_GetNumberOfFreeChunks(yaffs_Device *dev);
+
+
+// Rename
+int yaffs_RenameObject(yaffs_Object *oldDir, const char *oldName, yaffs_Object *newDir, const char *newName);
+
+// generic Object functions
+int yaffs_Unlink(yaffs_Object *dir, const char *name);
+int yaffs_DeleteFile(yaffs_Object *obj);
+
+// Object access functions.
+int yaffs_GetObjectName(yaffs_Object *obj,char *name,int buffSize);
+int yaffs_GetObjectFileLength(yaffs_Object *obj);
+int yaffs_GetObjectInode(yaffs_Object *obj);
+unsigned yaffs_GetObjectType(yaffs_Object *obj);
+int yaffs_GetObjectLinkCount(yaffs_Object *obj);
+
+// Change inode attributes
+int yaffs_SetAttributes(yaffs_Object *obj, struct iattr *attr);
+int yaffs_GetAttributes(yaffs_Object *obj, struct iattr *attr);
+
+// File operations
+int yaffs_ReadDataFromFile(yaffs_Object *obj, __u8 *buffer, __u32 offset, int nBytes);
+int yaffs_WriteDataToFile(yaffs_Object *obj, const __u8 *buffer, __u32 offset, int nBytes);
+int yaffs_ResizeFile(yaffs_Object *obj, int newSize);
+
+yaffs_Object *yaffs_MknodFile(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid);
+int yaffs_FlushFile(yaffs_Object *obj,int updateTime);
+
+
+// Directory operations
+yaffs_Object *yaffs_MknodDirectory(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid);
+yaffs_Object *yaffs_FindObjectByName(yaffs_Object *theDir,const char *name);
+int yaffs_ApplyToDirectoryChildren(yaffs_Object *theDir,int (*fn)(yaffs_Object *));
+
+yaffs_Object *yaffs_FindObjectByNumber(yaffs_Device *dev,__u32 number);
+
+// Link operations
+yaffs_Object *yaffs_Link(yaffs_Object *parent, const char *name, yaffs_Object *equivalentObject);
+
+yaffs_Object *yaffs_GetEquivalentObject(yaffs_Object *obj);
+
+// Symlink operations
+yaffs_Object *yaffs_MknodSymLink(yaffs_Object *parent, const char *name, __u32 mode,  __u32 uid, __u32 gid, const char *alias);
+char *yaffs_GetSymlinkAlias(yaffs_Object *obj);
+
+// Special inodes (fifos, sockets and devices)
+yaffs_Object *yaffs_MknodSpecial(yaffs_Object *parent,const char *name, __u32 mode, __u32 uid, __u32 gid,__u32 rdev);
+
+
+// Special directories
+yaffs_Object *yaffs_Root(yaffs_Device *dev);
+yaffs_Object *yaffs_LostNFound(yaffs_Device *dev);
+
+#ifdef CONFIG_YAFFS_WINCE
+// CONFIG_YAFFS_WINCE special stuff
+void  yfsd_WinFileTimeNow(__u32 target[2]);
+#endif
+
+#ifdef __KERNEL__
+void yaffs_HandleDeferedFree(yaffs_Object *obj);
+#endif
+
+// Debug dump 
+int yaffs_DumpObject(yaffs_Object *obj);
+
+
+void yaffs_GutsTest(yaffs_Device *dev);
+
+
+#endif
+
+
diff -uNrb linux-2.4.30/fs/yaffs/yaffs_mtdif.c linux/fs/yaffs/yaffs_mtdif.c
--- linux-2.4.30/fs/yaffs/yaffs_mtdif.c	Thu Jan  1 03:00:00 1970
+++ linux/fs/yaffs/yaffs_mtdif.c	Wed Jan 12 15:03:27 2005
@@ -0,0 +1,164 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * yaffs_mtdif.c  NAND mtd wrapper functions.
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+const char *yaffs_mtdif_c_version = "$Id: yaffs_mtdif.c,v 1.10 2004/09/19 08:14:50 charles Exp $";
+
+#ifdef CONFIG_YAFFS_MTD_ENABLED
+ 
+#include "yportenv.h"
+
+#include "yaffs_mtdif.h"
+
+#include "linux/mtd/mtd.h"
+#include "linux/types.h"
+#include "linux/time.h"
+
+#ifndef	CONFIG_YAFFS_USE_OLD_MTD
+#include "linux/mtd/nand.h"
+#endif
+
+struct nand_oobinfo yaffs_oobinfo = {
+	useecc: 1,
+	eccbytes: 6,
+	eccpos: {8, 9, 10, 13, 14, 15}
+};
+
+struct nand_oobinfo yaffs_noeccinfo = {
+	useecc: 0,
+};
+
+
+int nandmtd_WriteChunkToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, yaffs_Spare *spare)
+{
+	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+	size_t dummy;
+    int retval = 0;
+	
+	loff_t addr = ((loff_t)chunkInNAND) * dev->nBytesPerChunk;
+	
+	__u8 *spareAsBytes = (__u8 *)spare;
+
+#ifndef	CONFIG_YAFFS_USE_OLD_MTD
+	if(data && spare)
+	{
+		if(dev->useNANDECC)
+			mtd->write_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,&yaffs_oobinfo);
+		else
+			mtd->write_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,&yaffs_noeccinfo);
+	}
+	else
+	{
+#endif
+	if(data)
+		retval = mtd->write(mtd,addr,dev->nBytesPerChunk,&dummy,data);
+	if(spare)
+		retval = mtd->write_oob(mtd,addr,YAFFS_BYTES_PER_SPARE,&dummy,spareAsBytes);
+#ifndef	CONFIG_YAFFS_USE_OLD_MTD
+	}
+#endif
+
+    if (retval == 0)
+    	return YAFFS_OK;
+    else
+        return YAFFS_FAIL;
+}
+
+int nandmtd_ReadChunkFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare)
+{
+	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+	size_t dummy;
+    int retval = 0;
+	
+	loff_t addr = ((loff_t)chunkInNAND) * dev->nBytesPerChunk;
+	
+	__u8 *spareAsBytes = (__u8 *)spare;
+	
+#ifndef	CONFIG_YAFFS_USE_OLD_MTD
+	if(data && spare)
+	{
+		if(dev->useNANDECC)
+		{   // Careful, this call adds 2 ints to the end of the spare data.  Calling function should
+            // allocate enough memory for spare, i.e. [YAFFS_BYTES_PER_SPARE+2*sizeof(int)].
+    		retval = mtd->read_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,&yaffs_oobinfo);            
+		}
+		else
+		{
+			retval = mtd->read_ecc(mtd,addr,dev->nBytesPerChunk,&dummy,data,spareAsBytes,&yaffs_noeccinfo);
+		}
+	}
+	else
+	{
+#endif
+	if(data)
+		retval = mtd->read(mtd,addr,dev->nBytesPerChunk,&dummy,data);
+	if(spare)
+		retval = mtd->read_oob(mtd,addr,YAFFS_BYTES_PER_SPARE,&dummy,spareAsBytes);
+#ifndef	CONFIG_YAFFS_USE_OLD_MTD
+	}
+#endif
+
+    if (retval == 0)
+    	return YAFFS_OK;
+    else
+        return YAFFS_FAIL;
+}
+
+// Callback not needed for NAND
+#if 0
+static void nandmtd_EraseCallback(struct erase_info *ei)
+{
+	yaffs_Device *dev = (yaffs_Device *)ei->priv;	
+	up(&dev->sem);
+}
+#endif
+
+
+int nandmtd_EraseBlockInNAND(yaffs_Device *dev, int blockNumber)
+{
+	struct mtd_info *mtd = (struct mtd_info *)(dev->genericDevice);
+	__u32 addr = ((loff_t) blockNumber) * dev->nBytesPerChunk * dev->nChunksPerBlock;
+	struct erase_info ei;
+    int retval = 0;
+	
+	ei.mtd = mtd;
+	ei.addr = addr;
+	ei.len = dev->nBytesPerChunk * dev->nChunksPerBlock;
+	ei.time = 1000;
+	ei.retries = 2;
+	ei.callback = NULL;
+	ei.priv = (u_long)dev;
+	
+	// Todo finish off the ei if required
+	
+	sema_init(&dev->sem,0);
+
+	retval = mtd->erase(mtd,&ei);	
+	
+	//No need for callback 
+	// down(&dev->sem); // Wait for the erasure to complete
+
+    if (retval == 0)	
+    	return YAFFS_OK;
+    else
+        return YAFFS_FAIL;
+}
+
+int nandmtd_InitialiseNAND(yaffs_Device *dev)
+{
+	return YAFFS_OK;
+}
+
+#endif // CONFIG_YAFFS_MTD_ENABLED
+
diff -uNrb linux-2.4.30/fs/yaffs/yaffs_mtdif.h linux/fs/yaffs/yaffs_mtdif.h
--- linux-2.4.30/fs/yaffs/yaffs_mtdif.h	Thu Jan  1 03:00:00 1970
+++ linux/fs/yaffs/yaffs_mtdif.h	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,33 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * yaffs_mtdif.h  NAND mtd interface wrappers
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ *
+ * $Id: yaffs_mtdif.h,v 1.4 2002/09/27 20:50:50 charles Exp $
+ */
+
+#ifndef __YAFFS_MTDIF_H__
+#define __YAFFS_MTDIF_H__
+
+#include "yaffs_guts.h"
+
+int nandmtd_WriteChunkToNAND(yaffs_Device *dev,int chunkInNAND,const __u8 *data, yaffs_Spare *spare);
+int nandmtd_ReadChunkFromNAND(yaffs_Device *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare);
+int nandmtd_EraseBlockInNAND(yaffs_Device *dev, int blockNumber);
+int nandmtd_InitialiseNAND(yaffs_Device *dev);
+#endif
+
+
+
+
diff -uNrb linux-2.4.30/fs/yaffs/yaffs_nandemul.h linux/fs/yaffs/yaffs_nandemul.h
--- linux-2.4.30/fs/yaffs/yaffs_nandemul.h	Thu Jan  1 03:00:00 1970
+++ linux/fs/yaffs/yaffs_nandemul.h	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,38 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ *
+ * yaffs_nandemul.h: Interface to emulated NAND functions
+ *
+ * $Id: yaffs_nandemul.h,v 1.4 2002/09/27 20:50:50 charles Exp $
+ */
+ 
+#ifndef __YAFFS_NANDEMUL_H__
+#define __YAFFS_NANDEMUL_H__
+
+#include "yaffs_guts.h" 
+ 
+ 
+/* WriteChunkToNAND and ReadChunkFromNAND are used with two pointers. 
+ * If either of these pointers are null, then that field will not be 
+ * transferred.
+ */
+ 
+int nandemul_WriteChunkToNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, const __u8 *data, yaffs_Spare *spare);
+int nandemul_ReadChunkFromNAND(struct yaffs_DeviceStruct *dev,int chunkInNAND, __u8 *data, yaffs_Spare *spare);
+int nandemul_EraseBlockInNAND(struct yaffs_DeviceStruct *dev,int blockInNAND);
+int nandemul_InitialiseNAND(struct yaffs_DeviceStruct *dev);
+
+#endif
+
diff -uNrb linux-2.4.30/fs/yaffs/yaffsinterface.h linux/fs/yaffs/yaffsinterface.h
--- linux-2.4.30/fs/yaffs/yaffsinterface.h	Thu Jan  1 03:00:00 1970
+++ linux/fs/yaffs/yaffsinterface.h	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,25 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system.
+ * yaffsinterface.h: Interface to the guts of yaffs.
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ *
+ */
+ 
+#ifndef __YAFFSINTERFACE_H__
+#define __YAFFSINTERFACE_H__
+
+
+int yaffs_Initialise(unsigned nBlocks);
+
+#endif
+
diff -uNrb linux-2.4.30/fs/yaffs/yportenv.h linux/fs/yaffs/yportenv.h
--- linux-2.4.30/fs/yaffs/yportenv.h	Thu Jan  1 03:00:00 1970
+++ linux/fs/yaffs/yportenv.h	Mon Dec 27 19:39:11 2004
@@ -0,0 +1,137 @@
+/*
+ * YAFFS: Yet another FFS. A NAND-flash specific file system. 
+ * yportenv.h: Portable services used by yaffs. This is done to allow
+ * simple migration from kernel space into app space for testing.
+ *
+ * Copyright (C) 2002 Aleph One Ltd.
+ *   for Toby Churchill Ltd and Brightstar Engineering
+ *
+ * Created by Charles Manning <charles@aleph1.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 2.1 as
+ * published by the Free Software Foundation.
+ *
+ *
+ * Note: Only YAFFS headers are LGPL, YAFFS C code is covered by GPL.
+ *
+ * $Id: yportenv.h,v 1.14 2004/10/10 18:03:35 charles Exp $
+ *
+ */
+ 
+#ifndef __YPORTENV_H__
+#define __YPORTENV_H__
+
+
+#if defined CONFIG_YAFFS_WINCE
+
+#include "ywinceenv.h"
+
+#elif  defined __KERNEL__
+
+
+
+// Linux kernel
+#include "linux/kernel.h"
+#include "linux/version.h"
+#include "linux/mm.h"
+#include "linux/string.h"
+#include "linux/slab.h"
+
+
+#define YAFFS_LOSTNFOUND_NAME		"lost+found"
+#define YAFFS_LOSTNFOUND_PREFIX		"obj"
+
+//#define YPRINTF(x) printk x
+#define YMALLOC(x) kmalloc(x,GFP_KERNEL)
+#define YFREE(x)   kfree(x)
+
+#define YAFFS_ROOT_MODE				0666
+#define YAFFS_LOSTNFOUND_MODE		0666
+
+#if (LINUX_VERSION_CODE > KERNEL_VERSION(2,5,0))
+#define Y_CURRENT_TIME CURRENT_TIME.tv_sec
+#define Y_TIME_CONVERT(x) (x).tv_sec
+#else
+#define Y_CURRENT_TIME CURRENT_TIME
+#define Y_TIME_CONVERT(x) (x)
+#endif
+
+#define yaffs_SumCompare(x,y) ((x) == (y))
+#define yaffs_strcmp(a,b) strcmp(a,b)
+
+#define TENDSTR "\n"
+#define TSTR(x) KERN_DEBUG x
+#define TOUT(p) printk p
+
+
+#elif defined CONFIG_YAFFS_DIRECT
+
+// Direct interface
+#include "ydirectenv.h"
+
+#elif defined CONFIG_YAFFS_UTIL
+
+// Stuff for YAFFS utilities
+
+#include "stdlib.h"
+#include "stdio.h"
+#include "string.h"
+
+#include "devextras.h"
+
+#define YMALLOC(x) malloc(x)
+#define YFREE(x)   free(x)
+
+
+//#define YINFO(s) YPRINTF(( __FILE__ " %d %s\n",__LINE__,s))
+//#define YALERT(s) YINFO(s)
+
+
+#define TENDSTR "\n"
+#define TSTR(x) x
+#define TOUT(p) printf p
+
+
+#define YAFFS_LOSTNFOUND_NAME		"lost+found"
+#define YAFFS_LOSTNFOUND_PREFIX		"obj"
+//#define YPRINTF(x) printf x
+
+
+#define CURRENT_TIME 0
+#define YAFFS_ROOT_MODE				0666
+#define YAFFS_LOSTNFOUND_MODE		0666
+
+#define yaffs_SumCompare(x,y) ((x) == (y))
+#define yaffs_strcmp(a,b) strcmp(a,b)
+
+#else
+// Should have specified a configuration type
+#error Unknown configuration
+
+#endif 
+
+
+extern unsigned yaffs_traceMask;
+
+#define YAFFS_TRACE_ERROR		0x0001
+#define YAFFS_TRACE_OS			0x0002
+#define YAFFS_TRACE_ALLOCATE	0x0004
+#define YAFFS_TRACE_SCAN		0x0008
+#define YAFFS_TRACE_BAD_BLOCKS	0x0010
+#define YAFFS_TRACE_ERASE		0x0020
+#define YAFFS_TRACE_GC			0x0040
+#define YAFFS_TRACE_TRACING		0x0100
+#define YAFFS_TRACE_ALWAYS		0x0200
+#define YAFFS_TRACE_BUG			0x8000
+
+#define T(mask,p) do{ if((mask) & (yaffs_traceMask | YAFFS_TRACE_ERROR)) TOUT(p);} while(0) 
+
+
+#ifndef CONFIG_YAFFS_WINCE
+#define YBUG() T(YAFFS_TRACE_BUG,(TSTR("==>> yaffs bug: " __FILE__ " %d" TENDSTR),__LINE__))
+#endif
+
+#endif
+
+
diff -uNrb linux-2.4.30/include/asm-mips/bitops.h linux/include/asm-mips/bitops.h
--- linux-2.4.30/include/asm-mips/bitops.h	Fri Nov 19 12:32:15 2004
+++ linux/include/asm-mips/bitops.h	Tue Mar  1 11:10:50 2005
@@ -336,7 +336,7 @@
  */
 static __inline__ void set_bit(int nr, volatile void * addr)
 {
-	volatile unsigned long *a = addr;
+	volatile unsigned long *a = (volatile unsigned long *) addr;
 	unsigned long mask;
 	__bi_flags;
 
@@ -358,7 +358,7 @@
  */
 static __inline__ void __set_bit(int nr, volatile void * addr)
 {
-	volatile unsigned long *a = addr;
+	volatile unsigned long *a = (volatile unsigned long *) addr;
 	unsigned long mask;
 
 	a += nr >> 5;
@@ -378,7 +378,7 @@
  */
 static __inline__ void clear_bit(int nr, volatile void * addr)
 {
-	volatile unsigned long *a = addr;
+	volatile unsigned long *a = (volatile unsigned long *) addr;
 	unsigned long mask;
 	__bi_flags;
 
@@ -400,7 +400,7 @@
  */
 static __inline__ void change_bit(int nr, volatile void * addr)
 {
-	volatile unsigned long *a = addr;
+	volatile unsigned long *a = (volatile unsigned long *) addr;
 	unsigned long mask;
 	__bi_flags;
 
@@ -437,7 +437,7 @@
  */
 static __inline__ int test_and_set_bit(int nr, volatile void * addr)
 {
-	volatile unsigned long *a = addr;
+	volatile unsigned long *a = (volatile unsigned long *) addr;
 	unsigned long mask;
 	int retval;
 	__bi_flags;
@@ -463,7 +463,7 @@
  */
 static __inline__ int __test_and_set_bit(int nr, volatile void * addr)
 {
-	volatile unsigned long *a = addr;
+	volatile unsigned long *a = (volatile unsigned long *) addr;
 	unsigned long mask;
 	int retval;
 
@@ -485,7 +485,7 @@
  */
 static __inline__ int test_and_clear_bit(int nr, volatile void * addr)
 {
-	volatile unsigned long *a = addr;
+	volatile unsigned long *a = (volatile unsigned long *) addr;
 	unsigned long mask;
 	int retval;
 	__bi_flags;
@@ -511,7 +511,7 @@
  */
 static __inline__ int __test_and_clear_bit(int nr, volatile void * addr)
 {
-	volatile unsigned long *a = addr;
+	volatile unsigned long *a = (volatile unsigned long *) addr;
 	unsigned long mask;
 	int retval;
 
@@ -533,7 +533,7 @@
  */
 static __inline__ int test_and_change_bit(int nr, volatile void * addr)
 {
-	volatile unsigned long *a = addr;
+	volatile unsigned long *a = (volatile unsigned long *) addr;
 	unsigned long mask, retval;
 	__bi_flags;
 
@@ -558,7 +558,7 @@
  */
 static __inline__ int __test_and_change_bit(int nr, volatile void * addr)
 {
-	volatile unsigned long *a = addr;
+	volatile unsigned long *a = (volatile unsigned long *) addr;
 	unsigned long mask;
 	int retval;
 
diff -uNrb linux-2.4.30/include/asm-mips/bootinfo.h linux/include/asm-mips/bootinfo.h
--- linux-2.4.30/include/asm-mips/bootinfo.h	Fri Nov 19 12:32:15 2004
+++ linux/include/asm-mips/bootinfo.h	Mon Feb 21 14:48:03 2005
@@ -37,6 +37,9 @@
 #define MACH_GROUP_HP_LJ       20 /* Hewlett Packard LaserJet               */
 #define MACH_GROUP_LASAT       21
 #define MACH_GROUP_TITAN       22 /* PMC-Sierra Titan 			    */
+#define MACH_GROUP_IDT	       23 /* IDT Eval Boards			    */
+#define MACH_GROUP_MIKROTIK    24 /* Mikrotik Boards			    */
+#define MACH_GROUP_ADM_GW      25
 
 /*
  * Valid machtype values for group unknown (low order halfword of mips_machtype)
@@ -198,8 +201,19 @@
  */
 #define	MACH_TITAN_YOSEMITE	1 	/* PMC-Sierra Yosemite */
 
+/*
+ * Valid machtype for group IDT
+ */
+#define MACH_IDT_79EB434        0       /* IDT evaluation board */
+
+/*
+ * Valid machtype for group ADM_GW
+ */
+#define MACH_ADM_GW_5120	0
+
 #define CL_SIZE			(256)
 
+#define COMMAND_LINE_SIZE	256
 const char *get_system_type(void);
 
 extern unsigned long mips_machtype;
diff -uNrb linux-2.4.30/include/asm-mips/checksum.h linux/include/asm-mips/checksum.h
--- linux-2.4.30/include/asm-mips/checksum.h	Mon Feb 14 11:26:06 2005
+++ linux/include/asm-mips/checksum.h	Thu Mar  3 15:16:21 2005
@@ -9,6 +9,7 @@
 #define _ASM_CHECKSUM_H
 
 #include <asm/uaccess.h>
+#include <asm/unaligned.h>
 
 /*
  * computes the checksum of a memory block at buff, length len,
@@ -99,23 +100,23 @@
 	unsigned int csum;
 	int carry;
 
-	csum = word[0];
-	csum += word[1];
-	carry = (csum < word[1]);
+	csum = get_unaligned(word + 0);
+	csum += get_unaligned(word + 1);
+	carry = (csum < get_unaligned(word + 1));
 	csum += carry;
 
-	csum += word[2];
-	carry = (csum < word[2]);
+	csum += get_unaligned(word + 2);
+	carry = (csum < get_unaligned(word + 2));
 	csum += carry;
 
-	csum += word[3];
-	carry = (csum < word[3]);
+	csum += get_unaligned(word + 3);
+	carry = (csum < get_unaligned(word + 3));
 	csum += carry;
 
 	word += 4;
 	do {
-		csum += *word;
-		carry = (csum < *word);
+		csum += get_unaligned(word);
+		carry = (csum < get_unaligned(word));
 		csum += carry;
 		word++;
 	} while (word != stop);
diff -uNrb linux-2.4.30/include/asm-mips/io.h linux/include/asm-mips/io.h
--- linux-2.4.30/include/asm-mips/io.h	Fri Nov 19 12:32:15 2004
+++ linux/include/asm-mips/io.h	Tue Sep 14 13:56:46 2004
@@ -245,7 +245,8 @@
 
 static inline unsigned long virt_to_phys(volatile void * address)
 {
-	return (unsigned long)address - PAGE_OFFSET;
+//	return (unsigned long)address - PAGE_OFFSET;
+	return PHYSADDR(address);
 }
 
 /*
diff -uNrb linux-2.4.30/include/asm-mips/irq.h linux/include/asm-mips/irq.h
--- linux-2.4.30/include/asm-mips/irq.h	Fri Nov 19 12:32:15 2004
+++ linux/include/asm-mips/irq.h	Tue Sep 14 13:56:46 2004
@@ -14,7 +14,7 @@
 #include <linux/config.h>
 #include <linux/linkage.h>
 
-#define NR_IRQS 128		/* Largest number of ints of all machines.  */
+#define NR_IRQS 256		/* Largest number of ints of all machines.  */
 
 #ifdef CONFIG_I8259
 static inline int irq_cannonicalize(int irq)
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/crom.h linux/include/asm-mips/rc32434/crom.h
--- linux-2.4.30/include/asm-mips/rc32434/crom.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/crom.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,98 @@
+#ifndef __IDT_CROM_H__
+#define __IDT_CROM_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * Configuration ROM register definitions.
+ *
+ * File   : $Id: crom.h,v 1.2 2002/06/06 18:34:03 astichte Exp $
+ *
+ * Author : Allen.Stichter@idt.com
+ * Date   : 20020118
+ * Update :
+ *	    $Log: crom.h,v $
+ *	    Revision 1.2  2002/06/06 18:34:03  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:21  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *	
+ *
+ ******************************************************************************/
+
+#include  <asm/rc32434/types.h>
+
+enum
+{
+	CROM0_PhysicalAddress	= 0x100b8000,
+	CROM_PhysicalAddress	= CROM0_PhysicalAddress,
+
+	CROM0_VirtualAddress	= 0xb00b8000,
+	CROM_VirtualAddress	= CROM0_VirtualAddress,
+} ;
+
+typedef struct CROM_s
+{
+	U32	cromw0 ;		// use CROMW0_
+	U32	cromw1 ;		// use CROMW1_
+	U32	cromw2 ;		// use CROMW2_
+} volatile * CROM_t ;
+
+enum
+{
+	CROMW0_xloc_b	= 0,
+	CROMW0_xloc_m	= 0x0000003f,
+	CROMW0_yloc_b	= 8,
+	CROMW0_yloc_m	= 0x00003f00,
+	CROMW0_speed_b	= 16,
+	CROMW0_speed_m	= 0x01ff0000,
+	CROMW1_wafer_b	= 0,
+	CROMW1_wafer_m	= 0x0000001f,
+	CROMW1_lot_b	= 8,
+	CROMW1_lot_m	= 0x0fffff00,
+	CROMW1_fab_b	= 28,
+	CROMW1_fab_m	= 0xf0000000,
+	CROMW2_pci_b	= 0,
+	CROMW2_pci_m	= 0x00000001,
+	CROMW2_eth0_b	= 1,
+	CROMW2_eth0_m	= 0x00000002,
+	CROMW2_eth1_b	= 2,
+	CROMW2_eth1_m	= 0x00000004
+	CROMW2_i2c_b	= 3,
+	CROMW2_i2c_m	= 0x00000008,
+	CROMW2_rng_b	= 4,
+	CROMW2_rng_m	= 0x00000010,
+	CROMW2_se_b	= 5,
+	CROMW2_se_m	= 0x00000020,
+	CROMW2_des_b	= 6,
+	CROMW2_des_m	= 0x00000040,
+	CROMW2_tdes_b	= 7,
+	CROMW2_tdes_m	= 0x00000080,
+	CROMW2_a128_b	= 8,
+	CROMW2_a128_m	= 0x00000100,
+	CROMW2_a192_b	= 9,
+	CROMW2_a192_m	= 0x00000200,
+	CROMW2_a256_b	= 10,
+	CROMW2_a256_m	= 0x00000400,
+	CROMW2_md5_b	= 11,
+	CROMW2_md5_m	= 0x00000800,
+	CROMW2_s1_b	= 12,
+	CROMW2_s1_m	= 0x00001000,
+	CROMW2_s256_b	= 13,
+	CROMW2_s256_m	= 0x00002000,
+	CROMW2_pka_b	= 14,
+	CROMW2_pka_m	= 0x00004000,
+	CROMW2_exp_b	= 15,
+	CROMW2_exp_m	= 0x00018000,
+		CROMW2_exp_8192_v	= 0,
+		CROMW2_exp_1536_v	= 1,
+		CROMW2_exp_1024_v	= 2,
+		CROMW2_exp_512_v	= 3,
+	CROMW2_rocfg_b	= 17,
+	CROMW2_rocfg_m	= 0x000e0000,
+} ;
+
+#endif	// __IDT_CROM_H__
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/ddr.h linux/include/asm-mips/rc32434/ddr.h
--- linux-2.4.30/include/asm-mips/rc32434/ddr.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/ddr.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,173 @@
+#ifndef __IDT_DDR_H__
+#define __IDT_DDR_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * DDR register definition.
+ *
+ * File   : $Id: ddr.h,v 1.2 2002/06/06 18:34:03 astichte Exp $
+ *
+ * Author : ryan.holmQVist@idt.com
+ * Date   : 20011005
+ * Update :
+ *          $Log: ddr.h,v $
+ *          Revision 1.2  2002/06/06 18:34:03  astichte
+ *          Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *
+ *          Revision 1.1  2002/05/29 17:33:21  sysarch
+ *          jba File moved from vcode/include/idt/acacia
+ *
+ *
+ ******************************************************************************/
+
+#include  <asm/rc32434/types.h>
+
+enum
+{
+	DDR0_PhysicalAddress	= 0x18018000,
+	DDR_PhysicalAddress	= DDR0_PhysicalAddress,		// Default
+
+	DDR0_VirtualAddress	= 0xb8018000,
+	DDR_VirtualAddress	= DDR0_VirtualAddress,		// Default
+} ;
+
+typedef struct DDR_s
+{
+	U32	ddrbase ;
+	U32	ddrmask ;
+	U32	ddrc ;
+	U32	ddrabase ;
+	U32	ddramask ;
+	U32	ddramap ;
+	U32	ddrcust;
+	U32	ddrrdc;
+	U32	ddrspare;
+} volatile *DDR_t ;
+
+enum
+{
+	DDR0BASE_baseaddr_b	= 16,
+	DDR0BASE_baseaddr_m	= 0xffff0000,
+
+	DDR0MASK_mask_b		= 16,
+	DDR0MASK_mask_m		= 0xffff0000,
+
+	DDR1BASE_baseaddr_b	= 16,
+	DDR1BASE_baseaddr_m	= 0xffff0000,
+
+	DDR1MASK_mask_b		= 16,
+	DDR1MASK_mask_m		= 0xffff0000,
+
+	DDRC_ata_b		= 5,
+	DDRC_ata_m		= 0x000000E0,
+	DDRC_dbw_b		= 8,
+	DDRC_dbw_m		= 0x00000100,
+	DDRC_wr_b		= 9,
+	DDRC_wr_m		= 0x00000600,
+	DDRC_ps_b		= 11,
+	DDRC_ps_m		= 0x00001800,
+	DDRC_dtype_b		= 13,
+	DDRC_dtype_m		= 0x0000e000,
+	DDRC_rfc_b		= 16,
+	DDRC_rfc_m		= 0x000f0000,
+	DDRC_rp_b		= 20,
+	DDRC_rp_m		= 0x00300000,
+	DDRC_ap_b		= 22,
+	DDRC_ap_m		= 0x00400000,
+	DDRC_rcd_b		= 23,
+	DDRC_rcd_m		= 0x01800000,
+	DDRC_cl_b		= 25,
+	DDRC_cl_m		= 0x06000000,
+	DDRC_dbm_b		= 27,
+	DDRC_dbm_m		= 0x08000000,
+	DDRC_sds_b		= 28,
+	DDRC_sds_m		= 0x10000000,
+	DDRC_atp_b		= 29,
+	DDRC_atp_m		= 0x60000000,
+	DDRC_re_b		= 31,
+	DDRC_re_m		= 0x80000000,
+
+	DDRRDC_ces_b		= 0,
+	DDRRDC_ces_m		= 0x00000001,
+	DDRRDC_ace_b		= 1,
+	DDRRDC_ace_m		= 0x00000002,
+
+	DDRABASE_baseaddr_b	= 16,
+	DDRABASE_baseaddr_m	= 0xffff0000,
+
+	DDRAMASK_mask_b		= 16,
+	DDRAMASK_mask_m		= 0xffff0000,
+
+	DDRAMAP_map_b		= 16,
+	DDRAMAP_map_m		= 0xffff0000,
+
+	DDRCUST_cs_b		= 0,
+	DDRCUST_cs_m		= 0x00000003,
+	DDRCUST_we_b		= 2,
+	DDRCUST_we_m		= 0x00000004,
+	DDRCUST_ras_b		= 3,
+	DDRCUST_ras_m		= 0x00000008,
+	DDRCUST_cas_b		= 4,
+	DDRCUST_cas_m		= 0x00000010,
+	DDRCUST_cke_b		= 5,
+	DDRCUST_cke_m		= 0x00000020,
+	DDRCUST_ba_b		= 6,
+	DDRCUST_ba_m		= 0x000000c0,
+
+	RCOUNT_rcount_b		= 0,
+	RCOUNT_rcount_m		= 0x0000ffff,
+
+	RCOMPARE_rcompare_b	= 0,
+	RCOMPARE_rcompare_m	= 0x0000ffff,
+
+	RTC_ce_b		= 0,
+	RTC_ce_m		= 0x00000001,
+	RTC_to_b		= 1,
+	RTC_to_m		= 0x00000002,
+	RTC_rqe_b		= 2,
+	RTC_rqe_m		= 0x00000004,
+
+	DDRDQSC_dm_b		= 0,
+	DDRDQSC_dm_m		= 0x00000003,
+	DDRDQSC_dqsbs_b		= 2,
+	DDRDQSC_dqsbs_m		= 0x000000fc,
+	DDRDQSC_db_b		= 8,
+	DDRDQSC_db_m		= 0x00000100,
+	DDRDQSC_dbsp_b		= 9,
+	DDRDQSC_dbsp_m		= 0x01fffe00,
+	DDRDQSC_bdp_b		= 25,
+	DDRDQSC_bdp_m		= 0x7e000000,
+
+	DDRDLLC_eao_b		= 0,
+	DDRDLLC_eao_m		= 0x00000001,
+	DDRDLLC_eo_b		= 1,
+	DDRDLLC_eo_m		= 0x0000003e,
+	DDRDLLC_fs_b		= 6,
+	DDRDLLC_fs_m		= 0x000000c0,
+	DDRDLLC_as_b		= 8,
+	DDRDLLC_as_m		= 0x00000700,
+	DDRDLLC_sp_b		= 11,
+	DDRDLLC_sp_m		= 0x001ff800,
+
+	DDRDLLFC_men_b		= 0,
+	DDRDLLFC_men_m		= 0x00000001,
+	DDRDLLFC_aen_b		= 1,
+	DDRDLLFC_aen_m		= 0x00000002,
+	DDRDLLFC_ff_b		= 2,
+	DDRDLLFC_ff_m		= 0x00000004,
+
+	DDRDLLTA_addr_b		= 2,
+	DDRDLLTA_addr_m		= 0xffffffffc,
+
+	DDRDLLED_dbe_b		= 0,
+	DDRDLLED_dbe_m		= 0x000000001,
+	DDRDLLED_dte_b		= 1,
+	DDRDLLED_dte_m		= 0x000000002,
+	
+		
+} ;
+
+#endif	// __IDT_DDR_H__
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/dev.h linux/include/asm-mips/rc32434/dev.h
--- linux-2.4.30/include/asm-mips/rc32434/dev.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/dev.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,134 @@
+#ifndef __IDT_DEV_H__
+#define __IDT_DEV_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * Device Controller register definition.
+ *
+ * File   : $Id: dev.h,v 1.2 2002/06/06 18:34:03 astichte Exp $
+ *
+ * Author : John.Ahrens@idt.com
+ * Date   : 200112013
+ * Update :
+ *	    $Log: dev.h,v $
+ *	    Revision 1.2  2002/06/06 18:34:03  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:21  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *	
+ *
+ ******************************************************************************/
+
+#include  <asm/rc32434/types.h> 
+
+enum
+{
+	DEV0_PhysicalAddress	= 0x18010000,
+	DEV_PhysicalAddress	= DEV0_PhysicalAddress,		// Default
+
+	DEV0_VirtualAddress	= 0xb8010000,
+	DEV_VirtualAddress	= DEV0_VirtualAddress,		// Default
+} ;
+
+typedef struct DEVICE_s
+{
+	U32	devbase ;			// Device Base
+	U32	devmask ;			// Device Mask
+	U32	devc ;				// Device Control
+	U32	devtc ;				// Device Timing Control
+} volatile *DEVICE_t ;
+
+enum
+{
+	DEV_Count = 3,
+} ;
+
+typedef struct DEV_s
+{
+	struct DEVICE_s	dev [DEV_Count] ;
+	U32		btcs ;			// Bus timeout control / status 
+	U32		btcompare ;		// Compare
+	U32		btaddr ;		// Timeout address.
+	U32		devdacs ;		// Decoupled access control.
+	U32		devdaa ;		// Decoupled access address.
+	U32		devdad ;		// Decoupled access address.
+	U32		devspare ;		// spare.
+} volatile *DEV_t ;
+
+enum
+{
+	DEVBASE_baseaddr_b	= 16,
+	DEVBASE_baseaddr_m	= 0xffff0000,
+	DEVMASK_mask_b		= 16,
+	DEVMASK_mask_m		= 0xffff0000,
+
+	DEVC_ds_b		= 0,
+	DEVC_ds_m		= 0x00000003,
+		DEVC_ds_8_v	= 0,		// 8-bit device.
+		DEVC_ds_16_v	= 1,		// reserved
+		DEVC_ds_res_v	= 2,		// reserved.
+		DEVC_ds_res2_v	= 3,		// reserved.
+	DEVC_be_b		= 2,
+	DEVC_be_m		= 0x00000004,
+	DEVC_wp_b		= 3,
+	DEVC_wp_m		= 0x00000008,
+	DEVC_csd_b		= 4,
+	DEVC_csd_m		= 0x000000f0,
+	DEVC_oed_b		= 8,
+	DEVC_oed_m		= 0x00000f00,
+	DEVC_bwd_b		= 12,
+	DEVC_bwd_m		= 0x0000f000,
+	DEVC_rws_b		= 16,
+	DEVC_rws_m		= 0x003f0000,
+	DEVC_wws_b		= 22,
+	DEVC_wws_m		= 0x0fc00000,
+	DEVC_bre_b		= 28,
+	DEVC_bre_m		= 0x10000000,
+	DEVC_bwe_b		= 29,
+	DEVC_bwe_m		= 0x20000000,
+	DEVC_wam_b		= 30,
+	DEVC_wam_m		= 0x40000000,
+
+	DEVTC_prd_b		= 0,
+	DEVTC_prd_m		= 0x0000000f,
+	DEVTC_pwd_b		= 4,
+	DEVTC_pwd_m		= 0x000000f0,
+	DEVTC_wdh_b		= 8,
+	DEVTC_wdh_m		= 0x00000700,
+	DEVTC_csh_b		= 11,
+	DEVTC_csh_m		= 0x00001800,
+
+	BTCS_tt_b		= 0,
+	BTCS_tt_m		= 0x00000001,
+		BTCS_tt_write		= 0,	
+		BTCS_tt_read		= 1,	
+	BTCS_bto_b		= 1,		// In btcs
+	BTCS_bto_m		= 0x00000002,	// In btcs
+	BTCS_bte_b		= 2,		// In btcs
+	BTCS_bte_m		= 0x00000004,	// In btcs
+
+	BTCOMPARE_compare_b	= 0,		// In btcompare
+	BTCOMPARE_compare_m	= 0x0000ffff,	// In btcompare
+
+	DEVDACS_op_b		= 0,		// In devdacs
+	DEVDACS_op_m		= 0x00000001,	// In devdacs
+		DEVDACS_op_write_v		= 0,
+		DEVDACS_op_read_v		= 1,
+	DEVDACS_size_b		= 1,		// In devdacs
+	DEVDACS_size_m		= 0x00000006,	// In devdacs
+		DEVDACS_size_byte_v		= 0,
+		DEVDACS_size_halfword	= 1,
+		DEVDACS_size_triplebyte	= 2,
+		DEVDACS_size_word		= 3,
+	DEVDACS_err_b		= 3,		// In devdacs
+	DEVDACS_err_m		= 0x00000008,	// In devdacs
+	DEVDACS_f_b		= 4,		// In devdacs
+	DEVDACS_f_m		= 0x00000010,	// In devdacs
+} ;
+
+#endif	//__IDT_DEV_H__
+
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/dma.h linux/include/asm-mips/rc32434/dma.h
--- linux-2.4.30/include/asm-mips/rc32434/dma.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/dma.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,206 @@
+#ifndef __IDT_DMA_H__
+#define __IDT_DMA_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * DMA register definition.
+ *
+ * File   : $Id: dma.h,v 1.3 2002/06/06 18:34:03 astichte Exp $
+ *
+ * Author : ryan.holmQVist@idt.com
+ * Date   : 20011005
+ * Update :
+ *	    $Log: dma.h,v $
+ *	    Revision 1.3  2002/06/06 18:34:03  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.2  2002/06/05 18:30:46  astichte
+ *	    Removed IDTField
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:21  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *	
+ *
+ ******************************************************************************/
+
+#include  <asm/rc32434/types.h> 
+enum
+{
+	DMA0_PhysicalAddress	= 0x18040000,
+	DMA_PhysicalAddress	= DMA0_PhysicalAddress,		// Default
+
+	DMA0_VirtualAddress	= 0xb8040000,
+	DMA_VirtualAddress	= DMA0_VirtualAddress,		// Default
+} ;
+
+/*
+ * DMA descriptor (in physical memory).
+ */
+
+typedef struct DMAD_s
+{
+	U32			control ;	// Control. use DMAD_*
+	U32			ca ;		// Current Address.
+	U32			devcs ; 	// Device control and status.
+	U32			link ;		// Next descriptor in chain.
+} volatile *DMAD_t ;
+
+enum
+{
+	DMAD_size		= sizeof (struct DMAD_s),
+	DMAD_count_b		= 0,		// in DMAD_t -> control
+	DMAD_count_m		= 0x0003ffff,	// in DMAD_t -> control
+	DMAD_ds_b		= 20,		// in DMAD_t -> control
+	DMAD_ds_m		= 0x00300000,	// in DMAD_t -> control
+		DMAD_ds_ethRcv_v	= 0,
+		DMAD_ds_ethXmt_v	= 0,
+		DMAD_ds_memToFifo_v	= 0,
+		DMAD_ds_fifoToMem_v	= 0,
+		DMAD_ds_pciToMem_v	= 0,
+		DMAD_ds_memToPci_v	= 0,
+	
+	DMAD_devcmd_b		= 22,		// in DMAD_t -> control
+	DMAD_devcmd_m		= 0x01c00000,	// in DMAD_t -> control
+		DMAD_devcmd_byte_v	= 0,	//memory-to-memory
+		DMAD_devcmd_halfword_v	= 1,	//memory-to-memory
+		DMAD_devcmd_word_v	= 2,	//memory-to-memory
+		DMAD_devcmd_2words_v	= 3,	//memory-to-memory
+		DMAD_devcmd_4words_v	= 4,	//memory-to-memory
+		DMAD_devcmd_6words_v	= 5,	//memory-to-memory
+		DMAD_devcmd_8words_v	= 6,	//memory-to-memory
+		DMAD_devcmd_16words_v	= 7,	//memory-to-memory
+	DMAD_cof_b		= 25,		// chain on finished
+	DMAD_cof_m		= 0x02000000,	// 
+	DMAD_cod_b		= 26,		// chain on done
+	DMAD_cod_m		= 0x04000000,	// 
+	DMAD_iof_b		= 27,		// interrupt on finished
+	DMAD_iof_m		= 0x08000000,	// 
+	DMAD_iod_b		= 28,		// interrupt on done
+	DMAD_iod_m		= 0x10000000,	// 
+	DMAD_t_b		= 29,		// terminated
+	DMAD_t_m		= 0x20000000,	// 
+	DMAD_d_b		= 30,		// done
+	DMAD_d_m		= 0x40000000,	// 
+	DMAD_f_b		= 31,		// finished
+	DMAD_f_m		= 0x80000000,	// 
+} ;
+
+/*
+ * DMA register (within Internal Register Map).
+ */
+
+struct DMA_Chan_s
+{
+	U32		dmac ;		// Control.
+	U32		dmas ;		// Status.	
+	U32		dmasm ; 	// Mask.
+	U32		dmadptr ;	// Descriptor pointer.
+	U32		dmandptr ;	// Next descriptor pointer.
+};
+
+typedef struct DMA_Chan_s volatile *DMA_Chan_t ;
+
+//DMA_Channels	  use DMACH_count instead
+
+enum
+{
+	DMAC_run_b	= 0,		// 
+	DMAC_run_m	= 0x00000001,	// 
+	DMAC_dm_b	= 1,		// done mask
+	DMAC_dm_m	= 0x00000002,	// 
+	DMAC_mode_b	= 2,		// 
+	DMAC_mode_m	= 0x0000000c,	// 
+		DMAC_mode_auto_v	= 0,
+		DMAC_mode_burst_v	= 1,
+		DMAC_mode_transfer_v	= 2, //usually used
+		DMAC_mode_reserved_v	= 3,
+	DMAC_a_b	= 4,		// 
+	DMAC_a_m	= 0x00000010,	// 
+
+	DMAS_f_b	= 0,		// finished (sticky) 
+	DMAS_f_m	= 0x00000001,	//		     
+	DMAS_d_b	= 1,		// done (sticky)     
+	DMAS_d_m	= 0x00000002,	//		     
+	DMAS_c_b	= 2,		// chain (sticky)    
+	DMAS_c_m	= 0x00000004,	//		     
+	DMAS_e_b	= 3,		// error (sticky)    
+	DMAS_e_m	= 0x00000008,	//		     
+	DMAS_h_b	= 4,		// halt (sticky)     
+	DMAS_h_m	= 0x00000010,	//		     
+
+	DMASM_f_b	= 0,		// finished (1=mask)
+	DMASM_f_m	= 0x00000001,	// 
+	DMASM_d_b	= 1,		// done (1=mask)
+	DMASM_d_m	= 0x00000002,	// 
+	DMASM_c_b	= 2,		// chain (1=mask)
+	DMASM_c_m	= 0x00000004,	// 
+	DMASM_e_b	= 3,		// error (1=mask)
+	DMASM_e_m	= 0x00000008,	// 
+	DMASM_h_b	= 4,		// halt (1=mask)
+	DMASM_h_m	= 0x00000010,	// 
+} ;
+
+/*
+ * DMA channel definitions
+ */
+
+enum
+{
+	DMACH_ethRcv = 0,
+	DMACH_ethXmt = 1,
+	DMACH_memToFifo = 2,
+	DMACH_fifoToMem = 3,
+	DMACH_pciToMem = 4,
+	DMACH_memToPci = 5,
+	
+	DMACH_count //must be last
+};
+
+
+typedef struct DMAC_s
+{
+	struct DMA_Chan_s ch [DMACH_count] ; //use ch[DMACH_]
+} volatile *DMA_t ;
+
+
+/*
+ * External DMA parameters
+*/
+#if 0
+enum
+{
+	DMADEVCMD_ts_b	= 0,		// ts field in devcmd
+	DMADEVCMD_ts_m	= 0x00000007,	// ts field in devcmd
+		DMADEVCMD_ts_byte_v	= 0,
+		DMADEVCMD_ts_halfword_v	= 1,
+		DMADEVCMD_ts_word_v	= 2,
+		DMADEVCMD_ts_2word_v	= 3,
+		DMADEVCMD_ts_4word_v	= 4,
+		DMADEVCMD_ts_6word_v	= 5,
+		DMADEVCMD_ts_8word_v	= 6,
+		DMADEVCMD_ts_16word_v	= 7
+};
+#endif
+
+#if 1	// aws - Compatibility.
+#	define	EXTDMA_ts_b		DMADEVCMD_ts_b
+#	define	EXTDMA_ts_m		DMADEVCMD_ts_m
+#	define	EXTDMA_ts_byte_v	DMADEVCMD_ts_byte_v
+#	define	EXTDMA_ts_halfword_v	DMADEVCMD_ts_halfword_v
+#	define	EXTDMA_ts_word_v	DMADEVCMD_ts_word_v
+#	define	EXTDMA_ts_2word_v	DMADEVCMD_ts_2word_v
+#	define	EXTDMA_ts_4word_v	DMADEVCMD_ts_4word_v
+#	define	EXTDMA_ts_6word_v	DMADEVCMD_ts_6word_v
+#	define	EXTDMA_ts_8word_v	DMADEVCMD_ts_8word_v
+#	define	EXTDMA_ts_16word_v	DMADEVCMD_ts_16word_v
+#endif	// aws - Compatibility.
+
+#endif	// __IDT_DMA_H__
+
+
+
+
+
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/dma_v.h linux/include/asm-mips/rc32434/dma_v.h
--- linux-2.4.30/include/asm-mips/rc32434/dma_v.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/dma_v.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,73 @@
+#ifndef __IDT_DMA_V_H__
+#define __IDT_DMA_V_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * DMA register definition.
+ *
+ * File   : $Id: dma.h,v 1.3 2002/06/06 18:34:03 astichte Exp $
+ *
+ * Author : ryan.holmQVist@idt.com
+ * Date   : 20011005
+ * Update :
+ *	    $Log: dma.h,v $
+ *	    Revision 1.3  2002/06/06 18:34:03  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.2  2002/06/05 18:30:46  astichte
+ *	    Removed IDTField
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:21  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *	
+ *
+ ******************************************************************************/
+#include  <asm/rc32434/types.h> 
+#include  <asm/rc32434/dma.h> 
+#include  <asm/rc32434/rc32434.h>
+#define DMA_CHAN_OFFSET  0x14
+#define IS_DMA_USED(X) (((X) & (DMAD_f_m | DMAD_d_m | DMAD_t_m)) != 0)
+#define DMA_COUNT(count)   \
+  ((count) & DMAD_count_m)
+
+#define DMA_HALT_TIMEOUT 500
+
+
+static inline int rc32434_halt_dma(DMA_Chan_t ch)
+{
+	int timeout=1;
+	if (local_readl(&ch->dmac) & DMAC_run_m) {
+		local_writel(0, &ch->dmac); 
+		for (timeout = DMA_HALT_TIMEOUT; timeout > 0; timeout--) {
+			if (local_readl(&ch->dmas) & DMAS_h_m) {
+				local_writel(0, &ch->dmas);  
+				break;
+			}
+		}
+	}
+
+	return timeout ? 0 : 1;
+}
+
+static inline void rc32434_start_dma(DMA_Chan_t ch, u32 dma_addr)
+{
+	local_writel(0, &ch->dmandptr); 
+	local_writel(dma_addr, &ch->dmadptr);
+}
+
+static inline void rc32434_chain_dma(DMA_Chan_t ch, u32 dma_addr)
+{
+	local_writel(dma_addr, &ch->dmandptr);
+}
+
+#endif	// __IDT_DMA_V_H__
+
+
+
+
+
+
+
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/eth.h linux/include/asm-mips/rc32434/eth.h
--- linux-2.4.30/include/asm-mips/rc32434/eth.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/eth.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,322 @@
+#ifndef	__IDT_ETH_H__
+#define	__IDT_ETH_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * Ethernet register definition.
+ *
+ * File   : $Id: eth.h,v 1.3 2002/06/06 18:34:04 astichte Exp $
+ *
+ * Author : Allen.Stichter@idt.com
+ * Date   : 20020605
+ * Update :
+ *	    $Log: eth.h,v $
+ *	    Revision 1.3  2002/06/06 18:34:04  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.2  2002/06/05 18:19:46  astichte
+ *	    Added
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:22  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *
+ ******************************************************************************/
+
+#include  <asm/rc32434/types.h> 
+
+enum
+{
+	ETH0_PhysicalAddress	= 0x18060000,
+	ETH_PhysicalAddress	= ETH0_PhysicalAddress,		// Default
+
+	ETH0_VirtualAddress	= 0xb8060000,
+	ETH_VirtualAddress	= ETH0_VirtualAddress,		// Default
+} ;
+
+typedef struct
+{
+	U32 ethintfc		;
+	U32 ethfifott		;
+	U32 etharc		;
+	U32 ethhash0		;
+	U32 ethhash1		;
+	U32 ethu0 [4]		;	// Reserved.	
+	U32 ethpfs		;
+	U32 ethmcp		;
+	U32 eth_u1 [10]		;	// Reserved.
+	U32 ethspare		;
+	U32 eth_u2 [42]		;	// Reserved. 
+	U32 ethsal0		;
+	U32 ethsah0		;
+	U32 ethsal1		;
+	U32 ethsah1		;
+	U32 ethsal2		;
+	U32 ethsah2		;
+	U32 ethsal3		;
+	U32 ethsah3		;
+	U32 ethrbc		;
+	U32 ethrpc		;
+	U32 ethrupc		;
+	U32 ethrfc		;
+	U32 ethtbc		;
+	U32 ethgpf		;
+	U32 eth_u9 [50]		;	// Reserved.	
+	U32 ethmac1		;
+	U32 ethmac2		;
+	U32 ethipgt		;
+	U32 ethipgr		;
+	U32 ethclrt		;
+	U32 ethmaxf		;
+	U32 eth_u10		;	// Reserved.	
+	U32 ethmtest		;
+	U32 miimcfg		;
+	U32 miimcmd		;
+	U32 miimaddr		;
+	U32 miimwtd		;
+	U32 miimrdd		;
+	U32 miimind		;
+	U32 eth_u11		;	// Reserved.
+	U32 eth_u12		;	// Reserved.
+	U32 ethcfsa0		;
+	U32 ethcfsa1		;
+	U32 ethcfsa2		;
+} volatile *ETH_t;
+
+enum
+{
+	ETHINTFC_en_b		= 0,
+	ETHINTFC_en_m		= 0x00000001,
+	ETHINTFC_its_b		= 1,
+	ETHINTFC_its_m		= 0x00000002,
+	ETHINTFC_rip_b		= 2,
+	ETHINTFC_rip_m		= 0x00000004,
+	ETHINTFC_jam_b		= 3,
+	ETHINTFC_jam_m		= 0x00000008,
+	ETHINTFC_ovr_b		= 4,
+	ETHINTFC_ovr_m		= 0x00000010,
+	ETHINTFC_und_b		= 5,
+	ETHINTFC_und_m		= 0x00000020,
+	ETHINTFC_iom_b 		= 6,
+	ETHINTFC_iom_m		= 0x000000c0,
+
+	ETHFIFOTT_tth_b		= 0,
+	ETHFIFOTT_tth_m		= 0x0000007f,
+
+	ETHARC_pro_b		= 0,
+	ETHARC_pro_m		= 0x00000001,
+	ETHARC_am_b		= 1,
+	ETHARC_am_m		= 0x00000002,
+	ETHARC_afm_b		= 2,
+	ETHARC_afm_m		= 0x00000004,
+	ETHARC_ab_b		= 3,
+	ETHARC_ab_m		= 0x00000008,
+
+	ETHSAL_byte5_b		= 0,
+	ETHSAL_byte5_m		= 0x000000ff,
+	ETHSAL_byte4_b		= 8,
+	ETHSAL_byte4_m		= 0x0000ff00,
+	ETHSAL_byte3_b		= 16,
+	ETHSAL_byte3_m		= 0x00ff0000,
+	ETHSAL_byte2_b		= 24,
+	ETHSAL_byte2_m		= 0xff000000,
+
+	ETHSAH_byte1_b		= 0,
+	ETHSAH_byte1_m		= 0x000000ff,
+	ETHSAH_byte0_b		= 8,
+	ETHSAH_byte0_m		= 0x0000ff00,
+	
+	ETHGPF_ptv_b		= 0,
+	ETHGPF_ptv_m		= 0x0000ffff,
+
+	ETHPFS_pfd_b		= 0,
+	ETHPFS_pfd_m		= 0x00000001,
+
+	ETHCFSA0_cfsa4_b	= 0,
+	ETHCFSA0_cfsa4_m	= 0x000000ff,
+	ETHCFSA0_cfsa5_b	= 8,
+	ETHCFSA0_cfsa5_m	= 0x0000ff00,
+
+	ETHCFSA1_cfsa2_b	= 0,
+	ETHCFSA1_cfsa2_m	= 0x000000ff,
+	ETHCFSA1_cfsa3_b	= 8,
+	ETHCFSA1_cfsa3_m	= 0x0000ff00,
+
+	ETHCFSA2_cfsa0_b	= 0,
+	ETHCFSA2_cfsa0_m	= 0x000000ff,
+	ETHCFSA2_cfsa1_b	= 8,
+	ETHCFSA2_cfsa1_m	= 0x0000ff00,
+
+	ETHMAC1_re_b		= 0,
+	ETHMAC1_re_m		= 0x00000001,
+	ETHMAC1_paf_b		= 1,
+	ETHMAC1_paf_m		= 0x00000002,
+	ETHMAC1_rfc_b		= 2,
+	ETHMAC1_rfc_m		= 0x00000004,
+	ETHMAC1_tfc_b		= 3,
+	ETHMAC1_tfc_m		= 0x00000008,
+	ETHMAC1_lb_b		= 4,
+	ETHMAC1_lb_m		= 0x00000010,
+	ETHMAC1_mr_b		= 31,
+	ETHMAC1_mr_m		= 0x80000000,
+
+	ETHMAC2_fd_b		= 0,
+	ETHMAC2_fd_m		= 0x00000001,
+	ETHMAC2_flc_b		= 1,
+	ETHMAC2_flc_m		= 0x00000002,
+	ETHMAC2_hfe_b		= 2,
+	ETHMAC2_hfe_m		= 0x00000004,
+	ETHMAC2_dc_b		= 3,
+	ETHMAC2_dc_m		= 0x00000008,
+	ETHMAC2_cen_b		= 4,
+	ETHMAC2_cen_m		= 0x00000010,
+	ETHMAC2_pe_b		= 5,
+	ETHMAC2_pe_m		= 0x00000020,
+	ETHMAC2_vpe_b		= 6,
+	ETHMAC2_vpe_m		= 0x00000040,
+	ETHMAC2_ape_b		= 7,
+	ETHMAC2_ape_m		= 0x00000080,
+	ETHMAC2_ppe_b		= 8,
+	ETHMAC2_ppe_m		= 0x00000100,
+	ETHMAC2_lpe_b		= 9,
+	ETHMAC2_lpe_m		= 0x00000200,
+	ETHMAC2_nb_b		= 12,
+	ETHMAC2_nb_m		= 0x00001000,
+	ETHMAC2_bp_b		= 13,
+	ETHMAC2_bp_m		= 0x00002000,
+	ETHMAC2_ed_b		= 14,
+	ETHMAC2_ed_m		= 0x00004000,
+
+	ETHIPGT_ipgt_b		= 0,
+	ETHIPGT_ipgt_m		= 0x0000007f,
+
+	ETHIPGR_ipgr2_b		= 0,
+	ETHIPGR_ipgr2_m		= 0x0000007f,
+	ETHIPGR_ipgr1_b		= 8,
+	ETHIPGR_ipgr1_m		= 0x00007f00,
+
+	ETHCLRT_maxret_b	= 0,
+	ETHCLRT_maxret_m	= 0x0000000f,
+	ETHCLRT_colwin_b	= 8,
+	ETHCLRT_colwin_m	= 0x00003f00,
+
+	ETHMAXF_maxf_b		= 0,
+	ETHMAXF_maxf_m		= 0x0000ffff,
+
+	ETHMTEST_tb_b		= 2,
+	ETHMTEST_tb_m		= 0x00000004,
+
+	ETHMCP_div_b		= 0,
+	ETHMCP_div_m		= 0x000000ff,
+	
+	MIIMCFG_rsv_b		= 0,
+	MIIMCFG_rsv_m		= 0x0000000c,
+
+	MIIMCMD_rd_b		= 0,
+	MIIMCMD_rd_m		= 0x00000001,
+	MIIMCMD_scn_b		= 1,
+	MIIMCMD_scn_m		= 0x00000002,
+
+	MIIMADDR_regaddr_b	= 0,
+	MIIMADDR_regaddr_m	= 0x0000001f,
+	MIIMADDR_phyaddr_b	= 8,
+	MIIMADDR_phyaddr_m	= 0x00001f00,
+
+	MIIMWTD_wdata_b		= 0,
+	MIIMWTD_wdata_m		= 0x0000ffff,
+
+	MIIMRDD_rdata_b		= 0,
+	MIIMRDD_rdata_m		= 0x0000ffff,
+
+	MIIMIND_bsy_b		= 0,
+	MIIMIND_bsy_m		= 0x00000001,
+	MIIMIND_scn_b		= 1,
+	MIIMIND_scn_m		= 0x00000002,
+	MIIMIND_nv_b		= 2,
+	MIIMIND_nv_m		= 0x00000004,
+
+} ;
+
+/*
+ * Values for the DEVCS field of the Ethernet DMA Rx and Tx descriptors.
+ */
+enum
+{
+	ETHRX_fd_b		= 0,
+	ETHRX_fd_m		= 0x00000001,
+	ETHRX_ld_b		= 1,
+	ETHRX_ld_m		= 0x00000002,
+	ETHRX_rok_b		= 2,
+	ETHRX_rok_m		= 0x00000004,
+	ETHRX_fm_b		= 3,
+	ETHRX_fm_m		= 0x00000008,
+	ETHRX_mp_b		= 4,
+	ETHRX_mp_m		= 0x00000010,
+	ETHRX_bp_b		= 5,
+	ETHRX_bp_m		= 0x00000020,
+	ETHRX_vlt_b		= 6,
+	ETHRX_vlt_m		= 0x00000040,
+	ETHRX_cf_b		= 7,
+	ETHRX_cf_m		= 0x00000080,
+	ETHRX_ovr_b		= 8,
+	ETHRX_ovr_m		= 0x00000100,
+	ETHRX_crc_b		= 9,
+	ETHRX_crc_m		= 0x00000200,
+	ETHRX_cv_b		= 10,
+	ETHRX_cv_m		= 0x00000400,
+	ETHRX_db_b		= 11,
+	ETHRX_db_m		= 0x00000800,
+	ETHRX_le_b		= 12,
+	ETHRX_le_m		= 0x00001000,
+	ETHRX_lor_b		= 13,
+	ETHRX_lor_m		= 0x00002000,
+	ETHRX_ces_b		= 14,
+	ETHRX_ces_m		= 0x00004000,
+	ETHRX_length_b		= 16,
+	ETHRX_length_m		= 0xffff0000,
+
+	ETHTX_fd_b		= 0,
+	ETHTX_fd_m		= 0x00000001,
+	ETHTX_ld_b		= 1,
+	ETHTX_ld_m		= 0x00000002,
+	ETHTX_oen_b		= 2,
+	ETHTX_oen_m		= 0x00000004,
+	ETHTX_pen_b		= 3,
+	ETHTX_pen_m		= 0x00000008,
+	ETHTX_cen_b		= 4,
+	ETHTX_cen_m		= 0x00000010,
+	ETHTX_hen_b		= 5,
+	ETHTX_hen_m		= 0x00000020,
+	ETHTX_tok_b		= 6,
+	ETHTX_tok_m		= 0x00000040,
+	ETHTX_mp_b		= 7,
+	ETHTX_mp_m		= 0x00000080,
+	ETHTX_bp_b		= 8,
+	ETHTX_bp_m		= 0x00000100,
+	ETHTX_und_b		= 9,
+	ETHTX_und_m		= 0x00000200,
+	ETHTX_of_b		= 10,
+	ETHTX_of_m		= 0x00000400,
+	ETHTX_ed_b		= 11,
+	ETHTX_ed_m		= 0x00000800,
+	ETHTX_ec_b		= 12,
+	ETHTX_ec_m		= 0x00001000,
+	ETHTX_lc_b		= 13,
+	ETHTX_lc_m		= 0x00002000,
+	ETHTX_td_b		= 14,
+	ETHTX_td_m		= 0x00004000,
+	ETHTX_crc_b		= 15,
+	ETHTX_crc_m		= 0x00008000,
+	ETHTX_le_b		= 16,
+	ETHTX_le_m		= 0x00010000,
+	ETHTX_cc_b		= 17,
+	ETHTX_cc_m		= 0x001E0000,
+} ;
+
+#endif	// __IDT_ETH_H__
+
+
+
+
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/eth_v.h linux/include/asm-mips/rc32434/eth_v.h
--- linux-2.4.30/include/asm-mips/rc32434/eth_v.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/eth_v.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,64 @@
+#ifndef	__IDT_ETH_V_H__
+#define	__IDT_ETH_V_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * Ethernet register definition.
+ *
+ * File   : $Id: eth.h,v 1.3 2002/06/06 18:34:04 astichte Exp $
+ *
+ * Author : Allen.Stichter@idt.com
+ * Date   : 20020605
+ * Update :
+ *	    $Log: eth.h,v $
+ *	    Revision 1.3  2002/06/06 18:34:04  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.2  2002/06/05 18:19:46  astichte
+ *	    Added
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:22  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *
+ ******************************************************************************/
+
+#include  <asm/rc32434/types.h> 
+#include  <asm/rc32434/eth.h> 
+
+#define IS_TX_TOK(X)         (((X) & (1<<ETHTX_tok_b)) >> ETHTX_tok_b )   /* Transmit Okay    */
+#define IS_TX_MP(X)          (((X) & (1<<ETHTX_mp_b))  >> ETHTX_mp_b )    /* Multicast        */
+#define IS_TX_BP(X)          (((X) & (1<<ETHTX_bp_b))  >> ETHTX_bp_b )    /* Broadcast        */
+#define IS_TX_UND_ERR(X)     (((X) & (1<<ETHTX_und_b)) >> ETHTX_und_b )   /* Transmit FIFO Underflow */
+#define IS_TX_OF_ERR(X)      (((X) & (1<<ETHTX_of_b))  >> ETHTX_of_b )    /* Oversized frame  */
+#define IS_TX_ED_ERR(X)      (((X) & (1<<ETHTX_ed_b))  >> ETHTX_ed_b )    /* Excessive deferral  */
+#define IS_TX_EC_ERR(X)      (((X) & (1<<ETHTX_ec_b))  >> ETHTX_ec_b)     /* Excessive collisions  */
+#define IS_TX_LC_ERR(X)      (((X) & (1<<ETHTX_lc_b))  >> ETHTX_lc_b )    /* Late Collision   */
+#define IS_TX_TD_ERR(X)      (((X) & (1<<ETHTX_td_b))  >> ETHTX_td_b )    /* Transmit deferred*/
+#define IS_TX_CRC_ERR(X)     (((X) & (1<<ETHTX_crc_b)) >> ETHTX_crc_b )   /* CRC Error        */
+#define IS_TX_LE_ERR(X)      (((X) & (1<<ETHTX_le_b))  >>  ETHTX_le_b )    /* Length Error     */
+
+#define TX_COLLISION_COUNT(X) (((X) & ETHTX_cc_m)>>ETHTX_cc_b)  /* Collision Count  */
+
+#define IS_RCV_ROK(X)        (((X) & (1<<ETHRX_rok_b)) >> ETHRX_rok_b)    /* Receive Okay     */
+#define IS_RCV_FM(X)         (((X) & (1<<ETHRX_fm_b))  >> ETHRX_fm_b)     /* Is Filter Match  */
+#define IS_RCV_MP(X)         (((X) & (1<<ETHRX_mp_b))  >> ETHRX_mp_b)     /* Is it MP         */
+#define IS_RCV_BP(X)         (((X) & (1<<ETHRX_bp_b))  >> ETHRX_bp_b)     /* Is it BP         */
+#define IS_RCV_VLT(X)        (((X) & (1<<ETHRX_vlt_b)) >> ETHRX_vlt_b)    /* VLAN Tag Detect  */
+#define IS_RCV_CF(X)         (((X) & (1<<ETHRX_cf_b))  >> ETHRX_cf_b)     /* Control Frame    */
+#define IS_RCV_OVR_ERR(X)    (((X) & (1<<ETHRX_ovr_b)) >> ETHRX_ovr_b)    /* Receive Overflow */
+#define IS_RCV_CRC_ERR(X)    (((X) & (1<<ETHRX_crc_b)) >> ETHRX_crc_b)    /* CRC Error        */
+#define IS_RCV_CV_ERR(X)     (((X) & (1<<ETHRX_cv_b))  >> ETHRX_cv_b)     /* Code Violation   */
+#define IS_RCV_DB_ERR(X)     (((X) & (1<<ETHRX_db_b))  >> ETHRX_db_b)     /* Dribble Bits     */
+#define IS_RCV_LE_ERR(X)     (((X) & (1<<ETHRX_le_b))  >> ETHRX_le_b)     /* Length error     */
+#define IS_RCV_LOR_ERR(X)    (((X) & (1<<ETHRX_lor_b)) >> ETHRX_lor_b)    /* Length Out of Range */
+#define IS_RCV_CES_ERR(X)    (((X) & (1<<ETHRX_ces_b)) >> ETHRX_ces_b)  /* Preamble error   */
+#define RCVPKT_LENGTH(X)     (((X) & ETHRX_length_m) >> ETHRX_length_b)   /* Length of the received packet */
+#endif	// __IDT_ETH_V_H__
+
+
+
+
+
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/gpio.h linux/include/asm-mips/rc32434/gpio.h
--- linux-2.4.30/include/asm-mips/rc32434/gpio.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/gpio.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,182 @@
+#ifndef __IDT_GPIO_H__
+#define __IDT_GPIO_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * GPIO register definition.
+ *
+ * File   : $Id: gpio.h,v 1.2 2002/06/06 18:34:04 astichte Exp $
+ *
+ * Author : ryan.holmQVist@idt.com
+ * Date   : 20011005
+ * Update :
+ *	    $Log: gpio.h,v $
+ *	    Revision 1.2  2002/06/06 18:34:04  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:22  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *	
+ *
+ ******************************************************************************/
+
+#include  <asm/rc32434/types.h> 
+enum
+{
+	GPIO0_PhysicalAddress	= 0x18050000,
+	GPIO_PhysicalAddress	= GPIO0_PhysicalAddress,	// Default
+
+	GPIO0_VirtualAddress	= 0xb8050000,
+	GPIO_VirtualAddress	= GPIO0_VirtualAddress,		// Default
+} ;
+
+typedef struct
+{
+	U32   gpiofunc;   /* GPIO Function Register
+			   * gpiofunc[x]==0 bit = gpio
+			   * func[x]==1  bit = altfunc
+			   */
+	U32   gpiocfg;	  /* GPIO Configuration Register
+			   * gpiocfg[x]==0 bit = input
+			   * gpiocfg[x]==1 bit = output
+			   */
+	U32   gpiod;	  /* GPIO Data Register
+			   * gpiod[x] read/write gpio pinX status
+			   */
+	U32   gpioilevel; /* GPIO Interrupt Status Register
+			   * interrupt level (see gpioistat)
+			   */
+	U32   gpioistat;  /* Gpio Interrupt Status Register
+			   * istat[x] = (gpiod[x] == level[x])
+			   * cleared in ISR (STICKY bits)
+			   */
+	U32   gpionmien;  /* GPIO Non-maskable Interrupt Enable Register */
+} volatile * GPIO_t ;
+
+typedef enum
+{
+	GPIO_gpio_v		= 0,		// gpiofunc use pin as GPIO.
+	GPIO_alt_v		= 1,		// gpiofunc use pin as alt.
+	GPIO_input_v		= 0,		// gpiocfg use pin as input.
+	GPIO_output_v		= 1,		// gpiocfg use pin as output.
+	GPIO_pin0_b		= 0,
+	GPIO_pin0_m		= 0x00000001,
+	GPIO_pin1_b		= 1,
+	GPIO_pin1_m		= 0x00000002,
+	GPIO_pin2_b		= 2,
+	GPIO_pin2_m		= 0x00000004,
+	GPIO_pin3_b		= 3,
+	GPIO_pin3_m		= 0x00000008,
+	GPIO_pin4_b		= 4,
+	GPIO_pin4_m		= 0x00000010,
+	GPIO_pin5_b		= 5,
+	GPIO_pin5_m		= 0x00000020,
+	GPIO_pin6_b		= 6,
+	GPIO_pin6_m		= 0x00000040,
+	GPIO_pin7_b		= 7,
+	GPIO_pin7_m		= 0x00000080,
+	GPIO_pin8_b		= 8,
+	GPIO_pin8_m		= 0x00000100,
+	GPIO_pin9_b		= 9,
+	GPIO_pin9_m		= 0x00000200,
+	GPIO_pin10_b		= 10,
+	GPIO_pin10_m		= 0x00000400,
+	GPIO_pin11_b		= 11,
+	GPIO_pin11_m		= 0x00000800,
+	GPIO_pin12_b		= 12,
+	GPIO_pin12_m		= 0x00001000,
+	GPIO_pin13_b		= 13,
+	GPIO_pin13_m		= 0x00002000,
+	GPIO_pin14_b		= 14,
+	GPIO_pin14_m		= 0x00004000,
+	GPIO_pin15_b		= 15,
+	GPIO_pin15_m		= 0x00008000,
+	GPIO_pin16_b		= 16,
+	GPIO_pin16_m		= 0x00010000,
+	GPIO_pin17_b		= 17,
+	GPIO_pin17_m		= 0x00020000,
+	GPIO_pin18_b		= 18,
+	GPIO_pin18_m		= 0x00040000,
+	GPIO_pin19_b		= 19,
+	GPIO_pin19_m		= 0x00080000,
+	GPIO_pin20_b		= 20,
+	GPIO_pin20_m		= 0x00100000,
+	GPIO_pin21_b		= 21,
+	GPIO_pin21_m		= 0x00200000,
+	GPIO_pin22_b		= 22,
+	GPIO_pin22_m		= 0x00400000,
+	GPIO_pin23_b		= 23,
+	GPIO_pin23_m		= 0x00800000,
+	GPIO_pin24_b		= 24,
+	GPIO_pin24_m		= 0x01000000,
+	GPIO_pin25_b		= 25,
+	GPIO_pin25_m		= 0x02000000,
+	GPIO_pin26_b		= 26,
+	GPIO_pin26_m		= 0x04000000,
+	GPIO_pin27_b		= 27,
+	GPIO_pin27_m		= 0x08000000,
+	GPIO_pin28_b		= 28,
+	GPIO_pin28_m		= 0x10000000,
+	GPIO_pin29_b		= 29,
+	GPIO_pin29_m		= 0x20000000,
+	GPIO_pin30_b		= 30,
+	GPIO_pin30_m		= 0x40000000,
+	GPIO_pin31_b		= 31,
+	GPIO_pin31_m		= 0x80000000,
+
+// Alternate function pins.  Corrsponding gpiofunc bit set to GPIO_alt_v.
+
+	GPIO_u0sout_b		= GPIO_pin0_b,		// UART 0 serial out.
+	GPIO_u0sout_m		= GPIO_pin0_m,
+		GPIO_u0sout_cfg_v	= GPIO_output_v,
+	GPIO_u0sinp_b	= GPIO_pin1_b,			// UART 0 serial in.
+	GPIO_u0sinp_m	= GPIO_pin1_m,
+		GPIO_u0sinp_cfg_v	= GPIO_input_v,
+	GPIO_u0rtsn_b	= GPIO_pin2_b,			// UART 0 req. to send.
+	GPIO_u0rtsn_m	= GPIO_pin2_m,
+		GPIO_u0rtsn_cfg_v	= GPIO_output_v,
+	GPIO_u0ctsn_b	= GPIO_pin3_b,			// UART 0 clear to send.
+	GPIO_u0ctsn_m	= GPIO_pin3_m,
+		GPIO_u0ctsn_cfg_v	= GPIO_input_v,
+	GPIO_maddr22_b		= GPIO_pin4_b, 	// M&P bus bit 22.
+	GPIO_maddr22_m		= GPIO_pin4_m,
+		GPIO_maddr22_cfg_v	= GPIO_output_v,
+
+	GPIO_maddr23_b		= GPIO_pin5_b, 	// M&P bus bit 23.
+	GPIO_maddr23_m		= GPIO_pin5_m,
+		GPIO_maddr23_cfg_v	= GPIO_output_v,
+
+	GPIO_maddr24_b		= GPIO_pin6_b, 	// M&P bus bit 24.
+	GPIO_maddr24_m		= GPIO_pin6_m,
+		GPIO_maddr24_cfg_v	= GPIO_output_v,
+
+	GPIO_maddr25_b		= GPIO_pin7_b, 	// M&P bus bit 25.
+	GPIO_maddr25_m		= GPIO_pin7_m,
+		GPIO_maddr25_cfg_v	= GPIO_output_v,
+
+	GPIO_cpu_b		= GPIO_pin8_b, 	// M&P bus bit 25.
+	GPIO_cpu_m		= GPIO_pin8_m,
+		GPIO_cpu_cfg_v	= GPIO_output_v,
+	GPIO_afspare6_b 	= GPIO_pin9_b, 	// reserved.
+	GPIO_afspare6_m 	= GPIO_pin9_m,
+		GPIO_afspare6_cfg_v	= GPIO_input_v,
+	GPIO_afspare4_b 	= GPIO_pin10_b, 	// reserved.
+	GPIO_afspare4_m 	= GPIO_pin10_m,
+		GPIO_afspare4_cfg_v	= GPIO_input_v,
+	GPIO_afspare3_b 	= GPIO_pin11_b, 	// reserved.
+	GPIO_afspare3_m 	= GPIO_pin11_m,
+		GPIO_afspare3_cfg_v	= GPIO_input_v,
+	GPIO_afspare2_b 	= GPIO_pin12_b, 	// reserved.
+	GPIO_afspare2_m 	= GPIO_pin12_m,
+		GPIO_afspare2_cfg_v	= GPIO_input_v,
+	GPIO_pcimuintn_b	= GPIO_pin13_b, 	// PCI messaging int.
+	GPIO_pcimuintn_m	= GPIO_pin13_m,
+		GPIO_pcimuintn_cfg_v	= GPIO_output_v,
+
+} GPIO_DEFS_t;
+
+#endif	// __IDT_GPIO_H__
+
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/i2c.h linux/include/asm-mips/rc32434/i2c.h
--- linux-2.4.30/include/asm-mips/rc32434/i2c.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/i2c.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,147 @@
+#ifndef __IDT_I2C_H__
+#define __IDT_I2C_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * I2C register definitions.
+ *
+ * File   : $Id: i2c.h,v 1.2 2002/06/06 18:34:04 astichte Exp $
+ *
+ * Author : Allen.Stichter@idt.com
+ * Date   : 20020120
+ * Update :
+ *	    $Log: i2c.h,v $
+ *	    Revision 1.2  2002/06/06 18:34:04  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:22  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *	
+ *
+ ******************************************************************************/
+
+#include  <asm/rc32434/types.h>
+
+enum
+{
+	I2C0_PhysicalAddress	= 0x18068000,
+	I2C_PhysicalAddress	= I2C0_PhysicalAddress,
+
+	I2C0_VirtualAddress	= 0xb8068000,
+	I2C_VirtualAddress	= I2C0_VirtualAddress,
+} ;
+
+typedef struct 
+{
+	U32	i2cc ;
+	U32	i2cdi ;
+	U32	i2cdo ;
+	U32	i2ccp ;		// I2C clk = ICLK / div / 8
+	U32	i2cmcmd ;
+	U32	i2cms ;
+	U32	i2cmsm ;
+	U32	i2css ;
+	U32	i2cssm ;
+	U32	i2csaddr ;
+	U32	i2csack ;
+} volatile * I2C_t ;
+enum
+{
+	I2CC_men_b	= 0,		// In I2C-> i2cc
+	I2CC_men_m	= 0x00000001,
+	I2CC_sen_b	= 1,		// In I2C-> i2cc
+	I2CC_sen_m	= 0x00000002,
+	I2CC_iom_b	= 2,		// In I2C-> i2cc
+	I2CC_iom_m	= 0x00000004,
+
+	I2CDI_data_b	= 0,		// In I2C-> i2cdi
+	I2CDI_data_m	= 0x000000ff,
+
+	I2CDO_data_b	= 0,		// In I2C-> i2cdo
+	I2CDO_data_m	= 0x000000ff,
+
+	I2CCP_div_b	= 0,		// In I2C-> i2ccp
+	I2CCP_div_m	= 0x0000ffff,
+
+	I2CMCMD_cmd_b	= 0,		// In I2C-> i2cmcmd
+	I2CMCMD_cmd_m	= 0x0000000f,
+		I2CMCMD_cmd_nop_v	= 0,
+		I2CMCMD_cmd_start_v	= 1,
+		I2CMCMD_cmd_stop_v	= 2,
+		I2CMCMD_cmd_res3_v	= 3,
+		I2CMCMD_cmd_rd_v	= 4,
+		I2CMCMD_cmd_rdack_v	= 5,
+		I2CMCMD_cmd_wd_v	= 6,
+		I2CMCMD_cmd_wdack_v	= 7,
+		I2CMCMD_cmd_res8_v	= 8,
+		I2CMCMD_cmd_res9_v	= 9,
+		I2CMCMD_cmd_res10_v	= 10,
+		I2CMCMD_cmd_res11_v	= 11,
+		I2CMCMD_cmd_res12_v	= 12,
+		I2CMCMD_cmd_res13_v	= 13,
+		I2CMCMD_cmd_res14_v	= 14,
+		I2CMCMD_cmd_res15_v	= 15,
+
+	I2CMS_d_b	= 0,		// In I2C-> i2cms
+	I2CMS_d_m	= 0x00000001,
+	I2CMS_na_b	= 1,		// In I2C-> i2cms
+	I2CMS_na_m	= 0x00000002,
+	I2CMS_la_b	= 2,		// In I2C-> i2cms
+	I2CMS_la_m	= 0x00000004,
+	I2CMS_err_b	= 3,		// In I2C-> i2cms
+	I2CMS_err_m	= 0x00000008,
+
+	I2CMSM_d_b	= 0,		// In I2C-> i2cmsm
+	I2CMSM_d_m	= 0x00000001,
+	I2CMSM_na_b	= 1,		// In I2C-> i2cmsm
+	I2CMSM_na_m	= 0x00000002,
+	I2CMSM_la_b	= 2,		// In I2C-> i2cmsm
+	I2CMSM_la_m	= 0x00000004,
+	I2CMSM_err_b	= 3,		// In I2C-> i2cmsm
+	I2CMSM_err_m	= 0x00000008,
+
+	I2CSS_rr_b	= 0,		// In I2C-> i2css
+	I2CSS_rr_m	= 0x00000001,
+	I2CSS_wr_b	= 1,		// In I2C-> i2css
+	I2CSS_wr_m	= 0x00000002,
+	I2CSS_sa_b	= 2,		// In I2C-> i2css
+	I2CSS_sa_m	= 0x00000004,
+	I2CSS_tf_b	= 3,		// In I2C-> i2css
+	I2CSS_tf_m	= 0x00000008,
+	I2CSS_gc_b	= 4,		// In I2C-> i2css
+	I2CSS_gc_m	= 0x00000010,
+	I2CSS_na_b	= 5,		// In I2C-> i2css
+	I2CSS_na_m	= 0x00000020,
+	I2CSS_err_b	= 6,		// In I2C-> i2css
+	I2CSS_err_m	= 0x00000040,
+
+	I2CSSM_rr_b	= 0,		// In I2C-> i2cssm
+	I2CSSM_rr_m	= 0x00000001,
+	I2CSSM_wr_b	= 1,		// In I2C-> i2cssm
+	I2CSSM_wr_m	= 0x00000002,
+	I2CSSM_sa_b	= 2,		// In I2C-> i2cssm
+	I2CSSM_sa_m	= 0x00000004,
+	I2CSSM_tf_b	= 3,		// In I2C-> i2cssm
+	I2CSSM_tf_m	= 0x00000008,
+	I2CSSM_gc_b	= 4,		// In I2C-> i2cssm
+	I2CSSM_gc_m	= 0x00000010,
+	I2CSSM_na_b	= 5,		// In I2C-> i2cssm
+	I2CSSM_na_m	= 0x00000020,
+	I2CSSM_err_b	= 6,		// In I2C-> i2cssm
+	I2CSSM_err_m	= 0x00000040,
+
+	I2CSADDR_addr_b	= 0,		// In I2C-> i2csaddr
+	I2CSADDR_addr_m	= 0x000003ff,
+	I2CSADDR_a_gc_b	= 10,		// In I2C-> i2csaddr
+	I2CSADDR_a_gc_m	= 0x00000400,
+	I2CSADDR_a10_b	= 11,		// In I2C-> i2csaddr
+	I2CSADDR_a10_m	= 0x00000800,
+
+	I2CSACK_ack_b	= 0,		// In I2C-> i2csack
+	I2CSACK_ack_m	= 0x00000001,
+
+} ;
+#endif	// __IDT_I2C_H__
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/int.h linux/include/asm-mips/rc32434/int.h
--- linux-2.4.30/include/asm-mips/rc32434/int.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/int.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,167 @@
+#ifndef __IDT_INT_H__
+#define __IDT_INT_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * Interrupt Controller register definition.
+ *
+ * File   : $Id: int.h,v 1.3 2002/06/06 18:34:04 astichte Exp $
+ *
+ * Author : ryan.holmqvist@idt.com
+ * Date   : 20011005
+ * Update :
+ *	    $Log: int.h,v $
+ *	    Revision 1.3  2002/06/06 18:34:04  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.2  2002/06/05 18:47:33  astichte
+ *	    Removed IDTField
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:22  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *
+ *
+ ******************************************************************************/
+
+#include  <asm/rc32434/types.h> 
+
+enum
+{
+	INT0_PhysicalAddress	= 0x18038000,
+	INT_PhysicalAddress	= INT0_PhysicalAddress,		// Default
+
+	INT0_VirtualAddress	= 0xb8038000,
+	INT_VirtualAddress	= INT0_VirtualAddress,		// Default
+} ;
+
+struct INT_s
+{
+	U32		ipend ;		//Pending interrupts. use INT?_
+	U32		itest ;		//Test bits.		use INT?_
+	U32		imask ;		//Interrupt disabled when set. use INT?_
+} ;
+
+enum
+{
+	IPEND2	= 0,			// HW 2 interrupt to core. use INT2_
+	IPEND3	= 1,			// HW 3 interrupt to core. use INT3_
+	IPEND4	= 2,			// HW 4 interrupt to core. use INT4_
+	IPEND5	= 3,			// HW 5 interrupt to core. use INT5_
+	IPEND6	= 4,			// HW 6 interrupt to core. use INT6_
+
+	IPEND_count,			// must be last (used in loops)
+	IPEND_min	= IPEND2	// min IPEND (used in loops)
+};
+
+typedef struct INTC_s
+{
+	struct INT_s	i [IPEND_count] ;// use i[IPEND?] = INT?_
+	U32		nmips ;		// use NMIPS_
+} volatile *INT_t ;
+
+enum
+{
+	INT2_timer0_b			= 0,
+	INT2_timer0_m			= 0x00000001,
+	INT2_timer1_b			= 1,
+	INT2_timer1_m			= 0x00000002,
+	INT2_timer2_b			= 2,
+	INT2_timer2_m			= 0x00000004,
+	INT2_refresh_b			= 3,
+	INT2_refresh_m			= 0x00000008,
+	INT2_watchdogTimeout_b		= 4,
+	INT2_watchdogTimeout_m		= 0x00000010,
+	INT2_undecodedCpuWrite_b	= 5,
+	INT2_undecodedCpuWrite_m	= 0x00000020,
+	INT2_undecodedCpuRead_b		= 6,
+	INT2_undecodedCpuRead_m		= 0x00000040,
+	INT2_undecodedPciWrite_b	= 7,
+	INT2_undecodedPciWrite_m	= 0x00000080,
+	INT2_undecodedPciRead_b		= 8,
+	INT2_undecodedPciRead_m		= 0x00000100,
+	INT2_undecodedDmaWrite_b	= 9,
+	INT2_undecodedDmaWrite_m	= 0x00000200,
+	INT2_undecodedDmaRead_b		= 10,
+	INT2_undecodedDmaRead_m		= 0x00000400,
+	INT2_ipBusSlaveAckError_b	= 11,
+	INT2_ipBusSlaveAckError_m	= 0x00000800,
+
+	INT3_dmaChannel0_b		= 0,
+	INT3_dmaChannel0_m		= 0x00000001,
+	INT3_dmaChannel1_b		= 1,
+	INT3_dmaChannel1_m		= 0x00000002,
+	INT3_dmaChannel2_b		= 2,
+	INT3_dmaChannel2_m		= 0x00000004,
+	INT3_dmaChannel3_b		= 3,
+	INT3_dmaChannel3_m		= 0x00000008,
+	INT3_dmaChannel4_b		= 4,
+	INT3_dmaChannel4_m		= 0x00000010,
+	INT3_dmaChannel5_b		= 5,
+	INT3_dmaChannel5_m		= 0x00000020,
+
+	INT5_uartGeneral0_b		= 0,
+	INT5_uartGeneral0_m		= 0x00000001,
+	INT5_uartTxrdy0_b		= 1,
+	INT5_uartTxrdy0_m		= 0x00000002,
+	INT5_uartRxrdy0_b		= 2,
+	INT5_uartRxrdy0_m		= 0x00000004,
+	INT5_pci_b			= 3,
+	INT5_pci_m			= 0x00000008,
+	INT5_pciDecoupled_b		= 4,
+	INT5_pciDecoupled_m		= 0x00000010,
+	INT5_spi_b			= 5,
+	INT5_spi_m			= 0x00000020,
+	INT5_deviceDecoupled_b		= 6,
+	INT5_deviceDecoupled_m		= 0x00000040,
+	INT5_i2cMaster_b		= 7,
+	INT5_i2cMaster_m		= 0x00000080,
+	INT5_i2cSlave_b			= 8,
+	INT5_i2cSlave_m			= 0x00000100,
+	INT5_ethOvr_b			= 9,
+	INT5_ethOvr_m			= 0x00000200,
+	INT5_ethUnd_b			= 10,
+	INT5_ethUnd_m			= 0x00000400,
+	INT5_ethPfd_b			= 11,
+	INT5_ethPfd_m			= 0x00000800,
+	INT5_nvram_b			= 12,
+	INT5_nvram_m			= 0x00001000,
+	
+	INT6_gpio0_b			= 0,
+	INT6_gpio0_m			= 0x00000001,
+	INT6_gpio1_b			= 1,
+	INT6_gpio1_m			= 0x00000002,
+	INT6_gpio2_b			= 2,
+	INT6_gpio2_m			= 0x00000004,
+	INT6_gpio3_b			= 3,
+	INT6_gpio3_m			= 0x00000008,
+	INT6_gpio4_b			= 4,
+	INT6_gpio4_m			= 0x00000010,
+	INT6_gpio5_b			= 5,
+	INT6_gpio5_m			= 0x00000020,
+	INT6_gpio6_b			= 6,
+	INT6_gpio6_m			= 0x00000040,
+	INT6_gpio7_b			= 7,
+	INT6_gpio7_m			= 0x00000080,
+	INT6_gpio8_b			= 8,
+	INT6_gpio8_m			= 0x00000100,
+	INT6_gpio9_b			= 9,
+	INT6_gpio9_m			= 0x00000200,
+	INT6_gpio10_b			= 10,
+	INT6_gpio10_m			= 0x00000400,
+	INT6_gpio11_b			= 11,
+	INT6_gpio11_m			= 0x00000800,
+	INT6_gpio12_b			= 12,
+	INT6_gpio12_m			= 0x00001000,
+	INT6_gpio13_b			= 13,
+	INT6_gpio13_m			= 0x00002000,
+
+	NMIPS_gpio_b			= 0,
+	NMIPS_gpio_m			= 0x00000001,
+} ;
+
+#endif	// __IDT_INT_H__
+
+
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/integ.h linux/include/asm-mips/rc32434/integ.h
--- linux-2.4.30/include/asm-mips/rc32434/integ.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/integ.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,78 @@
+#ifndef __IDT_INTEG_H__
+#define __IDT_INTEG_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * System Integrity register definition.
+ *
+ * File   : $Id: integ.h,v 1.3 2002/06/06 18:34:04 astichte Exp $
+ *
+ * Author : ryan.holmQVist@idt.com
+ * Date   : 20011005
+ * Update :
+ *	    $Log: integ.h,v $
+ *	    Revision 1.3  2002/06/06 18:34:04  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.2  2002/06/05 18:32:33  astichte
+ *	    Removed IDTField
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:22  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *
+ ******************************************************************************/
+
+#include  <asm/rc32434/types.h>
+
+enum
+{
+	INTEG0_PhysicalAddress	= 0x18030000,
+	INTEG_PhysicalAddress	= INTEG0_PhysicalAddress,	// Default
+
+	INTEG0_VirtualAddress	= 0xb8030000,
+	INTEG_VirtualAddress	= INTEG0_VirtualAddress,	// Default
+} ;
+
+// if you are looing for CEA, try rst.h
+typedef struct
+{
+	U32 filler [0xc] ;		// 0x30 bytes unused.
+	U32 errcs ;			// sticky use ERRCS_
+	U32 wtcount ;			// Watchdog timer count reg.
+	U32 wtcompare ;			// Watchdog timer timeout value.
+	U32 wtc ;			// Watchdog timer control. use WTC_
+} volatile *INTEG_t ;
+
+enum
+{
+	ERRCS_wto_b		= 0,		// In INTEG_t -> errcs
+	ERRCS_wto_m		= 0x00000001,
+	ERRCS_wne_b		= 1,		// In INTEG_t -> errcs
+	ERRCS_wne_m		= 0x00000002,
+	ERRCS_ucw_b		= 2,		// In INTEG_t -> errcs
+	ERRCS_ucw_m		= 0x00000004,
+	ERRCS_ucr_b		= 3,		// In INTEG_t -> errcs
+	ERRCS_ucr_m		= 0x00000008,
+	ERRCS_upw_b		= 4,		// In INTEG_t -> errcs
+	ERRCS_upw_m		= 0x00000010,
+	ERRCS_upr_b		= 5,		// In INTEG_t -> errcs
+	ERRCS_upr_m		= 0x00000020,
+	ERRCS_udw_b		= 6,		// In INTEG_t -> errcs
+	ERRCS_udw_m		= 0x00000040,
+	ERRCS_udr_b		= 7,		// In INTEG_t -> errcs
+	ERRCS_udr_m		= 0x00000080,
+	ERRCS_sae_b		= 8,		// In INTEG_t -> errcs
+	ERRCS_sae_m		= 0x00000100,
+	ERRCS_wre_b		= 9,		// In INTEG_t -> errcs
+	ERRCS_wre_m		= 0x00000200,
+
+	WTC_en_b		= 0,		// In INTEG_t -> wtc
+	WTC_en_m		= 0x00000001,
+	WTC_to_b		= 1,		// In INTEG_t -> wtc
+	WTC_to_m		= 0x00000002,
+} ;
+
+#endif	// __IDT_INTEG_H__
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/iparb.h linux/include/asm-mips/rc32434/iparb.h
--- linux-2.4.30/include/asm-mips/rc32434/iparb.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/iparb.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,95 @@
+#ifndef __IDT_IPARB_H__
+#define __IDT_IPARB_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * IP Arbiter register definitions.
+ *
+ * File   : $Id: iparb.h,v 1.3 2002/06/06 18:34:04 astichte Exp $
+ *
+ * Author : Allen.Stichter@idt.com
+ * Date   : 20020120
+ * Update :
+ *	    $Log: iparb.h,v $
+ *	    Revision 1.3  2002/06/06 18:34:04  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.2  2002/06/05 19:01:42  astichte
+ *	    Removed IDTField
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:23  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *
+ ******************************************************************************/
+
+#include  <asm/rc32434/types.h>
+
+enum
+{
+	IPARB0_PhysicalAddress	= 0x18048000,
+	IPARB_PhysicalAddress	= IPARB0_PhysicalAddress,	// Default
+
+	IPARB0_VirtualAddress	= 0xb8048000,
+	IPARB_VirtualAddress	= IPARB0_VirtualAddress,	// Default
+} ;
+
+enum
+{
+	IPABMXC_ethernetReceive		= 0,
+	IPABMXC_ethernetTransmit	= 1,
+	IPABMXC_memoryToHoldFifo	= 2,
+	IPABMXC_holdFifoToMemory	= 3,
+	IPABMXC_pciToMemory		= 4,
+	IPABMXC_memoryToPci		= 5,
+	IPABMXC_pciTarget		= 6,
+	IPABMXC_pciTargetStart		= 7,
+	IPABMXC_cpuToIpBus		= 8,
+
+	IPABMXC_Count,				// Must be last in list !
+	IPABMXC_Min			= IPABMXC_ethernetReceive,
+
+	IPAPXC_PriorityCount	= 4,		// 3-highest, 0-lowest.
+} ;
+
+typedef struct
+{
+	U32	ipapc [IPAPXC_PriorityCount] ;	// ipapc[IPAPXC_] = IPAPC_
+	U32	ipabmc [IPABMXC_Count] ;	// ipabmc[IPABMXC_] = IPABMC_
+	U32	ipac ;				// use IPAC_
+	U32	ipaitcc;			// use IPAITCC_
+	U32	ipaspare ;
+} volatile * IPARB_t ;
+
+enum
+{
+	IPAC_dwm_b			= 2,
+	IPAC_dwm_m			= 0x00000004,
+	IPAC_drm_b			= 3,
+	IPAC_drm_m			= 0x00000008,
+	IPAC_msk_b			= 4,
+	IPAC_msk_m			= 0x00000010,
+
+	IPAPC_ptc_b			= 0,
+	IPAPC_ptc_m			= 0x00003fff,
+	IPAPC_mf_b			= 14,
+	IPAPC_mf_m			= 0x00004000,
+	IPAPC_cptc_b			= 16,
+	IPAPC_cptc_m			= 0x3fff0000,
+
+	IPAITCC_itcc			= 0,
+	IPAITCC_itcc,			= 0x000001ff,
+
+	IPABMC_mtc_b			= 0,
+	IPABMC_mtc_m			= 0x00000fff,
+	IPABMC_p_b			= 12,
+	IPABMC_p_m			= 0x00003000,
+	IPABMC_msk_b			= 14,
+	IPABMC_msk_m			= 0x00004000,
+	IPABMC_cmtc_b			= 16,
+	IPABMC_cmtc_m			= 0x0fff0000,
+};
+
+#endif	// __IDT_IPARB_H__
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/irm.h linux/include/asm-mips/rc32434/irm.h
--- linux-2.4.30/include/asm-mips/rc32434/irm.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/irm.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,55 @@
+#ifndef __IDT_IRM_H__
+#define __IDT_IRM_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * Internal Register Map
+ *
+ * File   : $Id: irm.h,v 1.2 2002/06/05 14:51:06 astichte Exp $
+ *
+ * Author : Allen.Stichter@idt.com
+ * Date   : 20020605
+ * Update :
+ *          $Log: irm.h,v $
+ *          Revision 1.2  2002/06/05 14:51:06  astichte
+ *          *** empty log message ***
+ *
+ *          Revision 1.1  2002/05/29 17:33:23  sysarch
+ *          jba File moved from vcode/include/idt/acacia
+ *
+ ******************************************************************************/
+
+/*
+ * NOTE --
+ *	This file is here for backwards compatibility.
+ *	DO NOT USE !!!!
+ */
+
+typedef enum
+{
+	IRM_Physical	= 0x18000000,	// Internal Reg. map physical.
+	RST_Offset	= 0x00000000,	// Includes sysid and RST.
+	DEV_Offset	= 0x00010000,	// Device Controller 0.
+	DDR_Offset	= 0x00018000,	// Double-Data-Rate mem. controller.
+	PMARB_Offset	= 0x00020000,	// PM bus arbiter.
+	TIM_Offset	= 0x00028000,	// Counter / timer.
+	INTEG_Offset	= 0x00030000,	// System Integrity.
+	INT_Offset	= 0x00038000,	// Interrupt controller.
+	DMA_Offset	= 0x00040000,	// DMA.
+	IPARB_Offset	= 0x00044000,	// IP bus arbiter.
+	GPIO_Offset	= 0x00050000,	// GPIO.
+	UART_Offset	= 0x00058000,	// UART
+	ETH_Offset	= 0x00060000,	// Ethernet 1. 
+	I2C_Offset	= 0x00068000,	// I2C interface.
+	SPI_Offset	= 0x00070000,	// Serial Peripheral Interface.
+	NVRAM_Offset  	= 0x00078000,	// NVRAM interface
+	AUTH_Offset	= 0x0007c000,	// Authorization unit 
+	PCI_Offset	= 0x00080000,
+	CROM_Offset	= 0x000b8000,	// Configuration ROM.
+	IRM_Size	= 0x00200000,	// Internal Reg. map size.
+} IRM_Offset_t ;
+ 
+#endif	// __IDT_IRM_H__
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/nvram.h linux/include/asm-mips/rc32434/nvram.h
--- linux-2.4.30/include/asm-mips/rc32434/nvram.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/nvram.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,97 @@
+#ifndef __IDT_NVRAM_H
+#define __IDT_NVRAM_H
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *              All rights reserved.
+ *
+ * IP Arbiter register definitions.
+ *
+ * File   : $Id: nvram.h,v 1.3 2003/07/24 18:34:04 astichte Exp $
+ *
+ * Author : kiran.rao@idt.com
+ * Date   : 20030724
+ * Update :
+ *          $Log: nvram.h,v $
+ *      
+ *
+ ******************************************************************************/
+#include <asm/rc32434/tpes.h>
+
+
+enum
+{
+        NVRAM0_PhysicalAddress    = 0xba000000,
+        NVRAM_PhysicalAddress     = NVRAM0_PhysicalAddress,         // Default
+
+        NVRAM0_VirtualAddress     = 0xba000000,
+        NVRAM_VirtualAddress      = NVRAM0_VirtualAddress,          // Default
+} ;
+
+enum
+{
+	NVRCMD_cmd_b		= 0,
+	NVRCMD_cmd_m		= 0x0000007f,
+	
+	NVRS_r_b		= 0,
+	NVRS_r_m		= 0x00000001,
+	NVRS_e_b		= 1,
+	NVRS_e_m		= 0x00000002,
+	NVRS_k_b		= 2,
+	NVRS_k_m		= 0x00000004, 
+	
+	NVRSM_r_b		= 0,
+	NVRSM_r_m		= 0x00000001,
+	NVRSM_e_b		= 1,
+	NVRSM_e_m		= 0x00000002,
+	NVRSM_k_b		= 2,
+	NVRSM_k_m		= 0x00000004, 
+	
+	NVRCFG0_pwidth_b	= 0,
+	NVRCFG0_pwidth_m	= 0x00000003,
+	NVRCFG0_nmax_b		= 2,
+	NVRCFG0_nmax_m		= 0x0000000C,
+	NVRCFG0_vppl_b		= 4,
+	NVRCFG0_vppl_m		= 0x000000f0,
+	NVRCFG0_vppm_b		= 8,
+	NVRCFG0_vppm_m		= 0x00000300,
+	NVRCFG0_dvpp_b		= 10,
+	NVRCFG0_dvpp_m		= 0x00000c00,
+	NVRCFG0_x_b		= 12,
+	NVRCFG0_x_m		= 0x00007000,
+	
+	NVRCFG1_t1tecc_b	= 0,
+	NVRCFG1_t1tecc_m	= 0x00000003,
+	NVRCFG1_t1mrcl_b	= 2,
+	NVRCFG1_t1mrcl_m	= 0x0000000c,
+	NVRCFG1_t1bias_b	= 4,
+	NVRCFG1_t1bias_m	= 0x00000030,
+	NVRCFG1_t2tecc_b	= 6,
+	NVRCFG1_t2tecc_m	= 0x000000c0,
+	NVRCFG1_t2mrcl_b	= 8,
+	NVRCFG1_t2mrcl_m	= 0x00000300,
+	NVRCFG1_t2bias_b	= 10,
+	NVRCFG1_t2bias_m	= 0x00000c00,
+	NVRCFG1_t3tecc_b	= 12,
+	NVRCFG1_t3tecc_m	= 0x00003000,
+	NVRCFG1_t3mrcl_b	= 14,
+	NVRCFG1_t3mrcl_m	= 0x0000c000,
+	NVRCFG1_t3bias_b	= 16,
+	NVRCFG1_t3bias_m	= 0x00030000,
+	NVRCFG1_t4tecc_b	= 18,
+	NVRCFG1_t4tecc_m	= 0x000c0000,
+	NVRCFG1_t4mrcl_b	= 20,
+	NVRCFG1_t4mrcl_m	= 0x00300000,
+	NVRCFG1_t4bias_b	= 22,
+	NVRCFG1_t4bias_m	= 0x00c00000,
+	NVRCFG1_t5tecc_b	= 24,
+	NVRCFG1_t5tecc_m	= 0x03000000,
+	NVRCFG1_t5mrcl_b	= 26,
+	NVRCFG1_t5mrcl_m	= 0x0c000000,
+	NVRCFG1_t5bias_b	= 28,
+	NVRCFG1_t5bias_m	= 0x30000000,
+}
+
+#endif  // __IDT_NVRAM_H__
+
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/pci.h linux/include/asm-mips/rc32434/pci.h
--- linux-2.4.30/include/asm-mips/rc32434/pci.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/pci.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,523 @@
+#ifndef __IDT_PCI_H__
+#define __IDT_PCI_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * PCI register definition.
+ *
+ * File   : $Id: pci.h,v 1.8 2002/06/19 19:53:10 rholmqvi Exp $
+ *
+ * Author : ryan.holmQVist@idt.com
+ * Date   : 20020117
+ * Update :
+ *	    $Log: pci.h,v $
+ *	    Revision 1.8  2002/06/19 19:53:10  rholmqvi
+ *	    missing PCIPBAC_*
+ *	
+ *	    Revision 1.7  2002/06/19 14:38:54  astichte
+ *	    Extraneous comma
+ *	
+ *	    Revision 1.6  2002/06/06 18:34:04  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.5  2002/06/06 16:00:57  mchessin
+ *	    added vendor id, device id enums, cleaned up CFGA section
+ *
+ *	    Revision 1.4  2002/06/06 13:08:44  mchessin
+ *	    revised comments regarding DMA chan 8&9, and messaging unit
+ *
+ *	    Revision 1.3  2002/06/05 20:43:28  mchessin
+ *	    changes to DMA descriptor fields in accordance with
+ *	    Valenv conventions, changes to PCI target control reg fields,
+ *	    PCILBA prefix used for local base address
+ *
+ *	    Revision 1.2  2002/06/05 19:52:18  astichte
+ *	    Removed IDTField
+ *
+ *	    Revision 1.1  2002/05/29 17:33:23  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *
+ ******************************************************************************/
+
+
+#include  <asm/rc32434/types.h>
+enum
+{
+	PCI0_PhysicalAddress	= 0x18080000,
+	PCI_PhysicalAddress	= PCI0_PhysicalAddress,
+
+	PCI0_VirtualAddress	= 0xb8080000,
+	PCI_VirtualAddress	= PCI0_VirtualAddress,
+} ;
+
+enum
+{
+	PCI_LbaCount	= 4,		// Local base addresses.
+} ;
+
+typedef struct
+{
+	U32	a ;		// Address.
+	U32	c ;		// Control.
+	U32	m ;		// mapping.
+} PCI_Map_s ;
+
+typedef struct
+{
+	U32		pcic ;
+	U32		pcis ;
+	U32		pcism ;
+	U32		pcicfga ;
+	U32		pcicfgd ;
+	PCI_Map_s	pcilba [PCI_LbaCount] ;
+	U32		pcidac ;
+	U32		pcidas ;
+	U32		pcidasm ;
+	U32		pcidad ;
+	U32		pcidma4c ;
+	U32		pcidma5c ;
+	U32		pcitc ;
+} volatile *PCI_t ;
+
+// PCI messaging unit.
+enum
+{
+	PCIM_Count	= 2,
+} ;
+typedef struct
+{
+	U32		pciim [PCIM_Count] ;
+	U32		pciom [PCIM_Count] ;
+	U32		pciid ;
+	U32		pciiic ;
+	U32		pciiim ;
+	U32		pciiod ;
+	U32		pciioic ;
+	U32		pciioim ;
+} volatile *PCIM_t ;
+
+/*******************************************************************************
+ *
+ * PCI Control Register
+ *
+ ******************************************************************************/
+enum
+{
+	PCIC_en_b	= 0,
+	PCIC_en_m	= 0x00000001,
+	PCIC_tnr_b	= 1,
+	PCIC_tnr_m	= 0x00000002,
+	PCIC_sce_b	= 2,
+	PCIC_sce_m	= 0x00000004,
+	PCIC_ien_b	= 3,
+	PCIC_ien_m	= 0x00000008,
+	PCIC_aaa_b	= 4,
+	PCIC_aaa_m	= 0x00000010,
+	PCIC_eap_b	= 5,
+	PCIC_eap_m	= 0x00000020,
+	PCIC_pcim_b	= 6,
+	PCIC_pcim_m	= 0x000001c0,
+		PCIC_pcim_disabled_v	= 0,
+		PCIC_pcim_tnr_v 	= 1,	// Satellite - target not ready
+		PCIC_pcim_suspend_v	= 2,	// Satellite - suspended CPU.
+		PCIC_pcim_extern_v	= 3,	// Host - external arbiter.
+		PCIC_pcim_fixed_v	= 4,	// Host - fixed priority arb.
+		PCIC_pcim_roundrobin_v	= 5,	// Host - round robin priority.
+		PCIC_pcim_reserved6_v	= 6,
+		PCIC_pcim_reserved7_v	= 7,
+	PCIC_igm_b	= 9,
+	PCIC_igm_m	= 0x00000200,
+} ;
+
+/*******************************************************************************
+ *
+ * PCI Status Register
+ *
+ ******************************************************************************/
+enum {
+	PCIS_nvd_b	= 0,
+	PCIS_nvd_m	= 0x00000001,
+	PCIS_wr_b	= 1,
+	PCIS_wr_m	= 0x00000002,
+	PCIS_nmi_b	= 2,
+	PCIS_nmi_m	= 0x00000004,
+	PCIS_ii_b	= 3,
+	PCIS_ii_m	= 0x00000008,
+	PCIS_cwe_b	= 4,
+	PCIS_cwe_m	= 0x00000010,
+	PCIS_cre_b	= 5,
+	PCIS_cre_m	= 0x00000020,
+	PCIS_mdpe_b	= 6,
+	PCIS_mdpe_m	= 0x00000040,
+	PCIS_sta_b	= 7,
+	PCIS_sta_m	= 0x00000080,
+	PCIS_rta_b	= 8,
+	PCIS_rta_m	= 0x00000100,
+	PCIS_rma_b	= 9,
+	PCIS_rma_m	= 0x00000200,
+	PCIS_sse_b	= 10,
+	PCIS_sse_m	= 0x00000400,
+	PCIS_ose_b	= 11,
+	PCIS_ose_m	= 0x00000800,
+	PCIS_pe_b	= 12,
+	PCIS_pe_m	= 0x00001000,
+	PCIS_tae_b	= 13,
+	PCIS_tae_m	= 0x00002000,
+	PCIS_rle_b	= 14,
+	PCIS_rle_m	= 0x00004000,
+	PCIS_bme_b	= 15,
+	PCIS_bme_m	= 0x00008000,
+	PCIS_prd_b	= 16,
+	PCIS_prd_m	= 0x00010000,
+	PCIS_rip_b	= 17,
+	PCIS_rip_m	= 0x00020000,
+} ;
+
+/*******************************************************************************
+ *
+ * PCI Status Mask Register
+ *
+ ******************************************************************************/
+enum {
+	PCISM_nvd_b		= 0,
+	PCISM_nvd_m		= 0x00000001,
+	PCISM_wr_b		= 1,
+	PCISM_wr_m		= 0x00000002,
+	PCISM_nmi_b		= 2,
+	PCISM_nmi_m		= 0x00000004,
+	PCISM_ii_b		= 3,
+	PCISM_ii_m		= 0x00000008,
+	PCISM_cwe_b		= 4,
+	PCISM_cwe_m		= 0x00000010,
+	PCISM_cre_b		= 5,
+	PCISM_cre_m		= 0x00000020,
+	PCISM_mdpe_b		= 6,
+	PCISM_mdpe_m		= 0x00000040,
+	PCISM_sta_b		= 7,
+	PCISM_sta_m		= 0x00000080,
+	PCISM_rta_b		= 8,
+	PCISM_rta_m		= 0x00000100,
+	PCISM_rma_b		= 9,
+	PCISM_rma_m		= 0x00000200,
+	PCISM_sse_b		= 10,
+	PCISM_sse_m		= 0x00000400,
+	PCISM_ose_b		= 11,
+	PCISM_ose_m		= 0x00000800,
+	PCISM_pe_b		= 12,
+	PCISM_pe_m		= 0x00001000,
+	PCISM_tae_b		= 13,
+	PCISM_tae_m		= 0x00002000,
+	PCISM_rle_b		= 14,
+	PCISM_rle_m		= 0x00004000,
+	PCISM_bme_b		= 15,
+	PCISM_bme_m		= 0x00008000,
+	PCISM_prd_b		= 16,
+	PCISM_prd_m		= 0x00010000,
+	PCISM_rip_b		= 17,
+	PCISM_rip_m		= 0x00020000,
+} ;
+
+/*******************************************************************************
+ *
+ * PCI Configuration Address Register
+ *
+ ******************************************************************************/
+enum {
+	PCICFGA_reg_b		= 2,
+	PCICFGA_reg_m		= 0x000000fc,
+		PCICFGA_reg_id_v	= 0x00>>2, //use PCFGID_
+		PCICFGA_reg_04_v	= 0x04>>2, //use PCFG04_
+		PCICFGA_reg_08_v	= 0x08>>2, //use PCFG08_
+		PCICFGA_reg_0C_v	= 0x0C>>2, //use PCFG0C_
+		PCICFGA_reg_pba0_v	= 0x10>>2, //use PCIPBA_
+		PCICFGA_reg_pba1_v	= 0x14>>2, //use PCIPBA_
+		PCICFGA_reg_pba2_v	= 0x18>>2, //use PCIPBA_
+		PCICFGA_reg_pba3_v	= 0x1c>>2, //use PCIPBA_
+		PCICFGA_reg_subsystem_v = 0x2c>>2, //use PCFGSS_
+		PCICFGA_reg_3C_v	= 0x3C>>2, //use PCFG3C_
+		PCICFGA_reg_pba0c_v	= 0x44>>2, //use PCIPBAC_
+		PCICFGA_reg_pba0m_v	= 0x48>>2,
+		PCICFGA_reg_pba1c_v	= 0x4c>>2, //use PCIPBAC_
+		PCICFGA_reg_pba1m_v	= 0x50>>2,
+		PCICFGA_reg_pba2c_v	= 0x54>>2, //use PCIPBAC_
+		PCICFGA_reg_pba2m_v	= 0x58>>2,
+		PCICFGA_reg_pba3c_v	= 0x5c>>2, //use PCIPBAC_
+		PCICFGA_reg_pba3m_v	= 0x60>>2,
+		PCICFGA_reg_pmgt_v	= 0x64>>2,
+	PCICFGA_func_b		= 8,
+	PCICFGA_func_m		= 0x00000700,
+	PCICFGA_dev_b		= 11,
+	PCICFGA_dev_m		= 0x0000f800,
+		PCICFGA_dev_internal_v	= 0,
+	PCICFGA_bus_b		= 16,
+	PCICFGA_bus_m		= 0x00ff0000,
+		PCICFGA_bus_type0_v	= 0,	//local bus
+	PCICFGA_en_b		= 31,		// read only
+	PCICFGA_en_m		= 0x80000000,
+} ;
+
+enum {
+	PCFGID_vendor_b 	= 0,
+	PCFGID_vendor_m 	= 0x0000ffff,
+		PCFGID_vendor_IDT_v		= 0x111d,
+	PCFGID_device_b 	= 16,
+	PCFGID_device_m 	= 0xffff0000,
+		PCFGID_device_Korinade_v = 0x0207,
+
+	PCFG04_command_ioena_b		= 1,
+	PCFG04_command_ioena_m		= 0x00000001,
+	PCFG04_command_memena_b 	= 2,
+	PCFG04_command_memena_m 	= 0x00000002,
+	PCFG04_command_bmena_b		= 3,
+	PCFG04_command_bmena_m		= 0x00000004,
+	PCFG04_command_mwinv_b		= 5,
+	PCFG04_command_mwinv_m		= 0x00000010,
+	PCFG04_command_parena_b 	= 7,
+	PCFG04_command_parena_m 	= 0x00000040,
+	PCFG04_command_serrena_b	= 9,
+	PCFG04_command_serrena_m	= 0x00000100,
+	PCFG04_command_fastbbena_b	= 10,
+	PCFG04_command_fastbbena_m	= 0x00000200,
+	PCFG04_status_b 		= 16,
+	PCFG04_status_m 		= 0xffff0000,
+	PCFG04_status_66MHz_b		= 21,	// 66 MHz enable
+	PCFG04_status_66MHz_m		= 0x00200000,
+	PCFG04_status_fbb_b		= 23,
+	PCFG04_status_fbb_m		= 0x00800000,
+	PCFG04_status_mdpe_b		= 24,
+	PCFG04_status_mdpe_m		= 0x01000000,
+	PCFG04_status_dst_b		= 25,
+	PCFG04_status_dst_m		= 0x06000000,
+	PCFG04_status_sta_b		= 27,
+	PCFG04_status_sta_m		= 0x08000000,
+	PCFG04_status_rta_b		= 28,
+	PCFG04_status_rta_m		= 0x10000000,
+	PCFG04_status_rma_b		= 29,
+	PCFG04_status_rma_m		= 0x20000000,
+	PCFG04_status_sse_b		= 30,
+	PCFG04_status_sse_m		= 0x40000000,
+	PCFG04_status_pe_b		= 31,
+	PCFG04_status_pe_m		= 0x40000000,
+
+	PCFG08_revId_b			= 0,
+	PCFG08_revId_m			= 0x000000ff,
+	PCFG08_classCode_b		= 0,
+	PCFG08_classCode_m		= 0xffffff00,
+		PCFG08_classCode_bridge_v	= 06,
+		PCFG08_classCode_proc_v 	= 0x0b3000, // processor-MIPS
+	PCFG0C_cacheline_b		= 0,
+	PCFG0C_cacheline_m		= 0x000000ff,
+	PCFG0C_masterLatency_b		= 8,
+	PCFG0C_masterLatency_m		= 0x0000ff00,
+	PCFG0C_headerType_b		= 16,
+	PCFG0C_headerType_m		= 0x00ff0000,
+	PCFG0C_bist_b			= 24,
+	PCFG0C_bist_m			= 0xff000000,
+
+	PCIPBA_msi_b			= 0,
+	PCIPBA_msi_m			= 0x00000001,
+	PCIPBA_p_b			= 3,
+	PCIPBA_p_m			= 0x00000004,
+	PCIPBA_baddr_b			= 8,
+	PCIPBA_baddr_m			= 0xffffff00,
+
+	PCFGSS_vendorId_b		= 0,
+	PCFGSS_vendorId_m		= 0x0000ffff,
+	PCFGSS_id_b			= 16,
+	PCFGSS_id_m			= 0xffff0000,
+
+	PCFG3C_interruptLine_b		= 0,
+	PCFG3C_interruptLine_m		= 0x000000ff,
+	PCFG3C_interruptPin_b		= 8,
+	PCFG3C_interruptPin_m		= 0x0000ff00,
+	PCFG3C_minGrant_b		= 16,
+	PCFG3C_minGrant_m		= 0x00ff0000,
+	PCFG3C_maxLat_b 		= 24,
+	PCFG3C_maxLat_m 		= 0xff000000,
+
+	PCIPBAC_msi_b			= 0,
+	PCIPBAC_msi_m			= 0x00000001,
+	PCIPBAC_p_b			= 1,
+	PCIPBAC_p_m			= 0x00000002,
+	PCIPBAC_size_b			= 2,
+	PCIPBAC_size_m			= 0x0000007c,
+	PCIPBAC_sb_b			= 7,
+	PCIPBAC_sb_m			= 0x00000080,
+	PCIPBAC_pp_b			= 8,
+	PCIPBAC_pp_m			= 0x00000100,
+	PCIPBAC_mr_b			= 9,
+	PCIPBAC_mr_m			= 0x00000600,
+		PCIPBAC_mr_read_v	=0,	//no prefetching
+		PCIPBAC_mr_readLine_v	=1,
+		PCIPBAC_mr_readMult_v	=2,
+	PCIPBAC_mrl_b			= 11,
+	PCIPBAC_mrl_m			= 0x00000800,
+	PCIPBAC_mrm_b			= 12,
+	PCIPBAC_mrm_m			= 0x00001000,
+	PCIPBAC_trp_b			= 13,
+	PCIPBAC_trp_m			= 0x00002000,
+
+	PCFG40_trdyTimeout_b		= 0,
+	PCFG40_trdyTimeout_m		= 0x000000ff,
+	PCFG40_retryLim_b		= 8,
+	PCFG40_retryLim_m		= 0x0000ff00,
+};
+
+/*******************************************************************************
+ *
+ * PCI Local Base Address [0|1|2|3] Register
+ *
+ ******************************************************************************/
+enum {
+	PCILBA_baddr_b		= 0,		// In PCI_t -> pcilba [] .a
+	PCILBA_baddr_m		= 0xffffff00,
+} ;
+/*******************************************************************************
+ *
+ * PCI Local Base Address Control Register
+ *
+ ******************************************************************************/
+enum {
+	PCILBAC_msi_b		= 0,		// In pPci->pcilba[i].c
+	PCILBAC_msi_m		= 0x00000001,
+		PCILBAC_msi_mem_v	= 0,
+		PCILBAC_msi_io_v	= 1,
+	PCILBAC_size_b		= 2,	// In pPci->pcilba[i].c
+	PCILBAC_size_m		= 0x0000007c,
+	PCILBAC_sb_b		= 7,	// In pPci->pcilba[i].c
+	PCILBAC_sb_m		= 0x00000080,
+	PCILBAC_rt_b		= 8,	// In pPci->pcilba[i].c
+	PCILBAC_rt_m		= 0x00000100,
+		PCILBAC_rt_noprefetch_v = 0, // mem read
+		PCILBAC_rt_prefetch_v	= 1, // mem readline
+} ;
+
+/*******************************************************************************
+ *
+ * PCI Local Base Address [0|1|2|3] Mapping Register
+ *
+ ******************************************************************************/
+enum {
+	PCILBAM_maddr_b 	= 8,
+	PCILBAM_maddr_m 	= 0xffffff00,
+} ;
+
+/*******************************************************************************
+ *
+ * PCI Decoupled Access Control Register
+ *
+ ******************************************************************************/
+enum {
+	PCIDAC_den_b		= 0,
+	PCIDAC_den_m		= 0x00000001,
+} ;
+
+/*******************************************************************************
+ *
+ * PCI Decoupled Access Status Register
+ *
+ ******************************************************************************/
+enum {
+	PCIDAS_d_b	= 0,
+	PCIDAS_d_m	= 0x00000001,
+	PCIDAS_b_b	= 1,
+	PCIDAS_b_m	= 0x00000002,
+	PCIDAS_e_b	= 2,
+	PCIDAS_e_m	= 0x00000004,
+	PCIDAS_ofe_b	= 3,
+	PCIDAS_ofe_m	= 0x00000008,
+	PCIDAS_off_b	= 4,
+	PCIDAS_off_m	= 0x00000010,
+	PCIDAS_ife_b	= 5,
+	PCIDAS_ife_m	= 0x00000020,
+	PCIDAS_iff_b	= 6,
+	PCIDAS_iff_m	= 0x00000040,
+} ;
+
+/*******************************************************************************
+ *
+ * PCI DMA Channel 4 Configuration Register
+ *
+ ******************************************************************************/
+enum
+{
+	PCIDMA4C_mbs_b	= 0,		// Maximum Burst Size.
+	PCIDMA4C_mbs_m	= 0x00000fff,	// { pcidma4c }
+	PCIDMA4C_our_b	= 12,		// Optimize Unaligned Burst Reads.
+	PCIDMA4C_our_m	= 0x00001000,	// { pcidma4c }
+} ;
+
+/*******************************************************************************
+ *
+ * PCI DMA Channel 5 Configuration Register
+ *
+ ******************************************************************************/
+enum
+{
+	PCIDMA5C_mbs_b	= 0,		// Maximum Burst Size.
+	PCIDMA5C_mbs_m	= 0x00000fff, // { pcidma5c }
+} ;
+
+/*******************************************************************************
+ *
+ * PCI to Memory(DMA Channel 4) AND Memory to PCI DMA(DMA Channel 5)Descriptors
+ *
+ ******************************************************************************/
+enum {
+	PCIDMAD_pt_b		= 22,		// in DEVCMD field (descriptor)
+	PCIDMAD_pt_m		= 0x00c00000,	// preferred transaction field
+		// These are for reads (DMA channel 4)
+		PCIDMAD_devcmd_mr_v	= 0,	//memory read
+		PCIDMAD_devcmd_mrl_v	= 1,	//memory read line
+		PCIDMAD_devcmd_mrm_v	= 2,	//memory read multiple
+		PCIDMAD_devcmd_ior_v	= 3,	//I/O read
+		// These are for writes (DMA channel 5)
+		PCIDMAD_devcmd_mw_v	= 0,	//memory write
+		PCIDMAD_devcmd_mwi_v	= 1,	//memory write invalidate
+		PCIDMAD_devcmd_iow_v	= 3,	//I/O write
+
+	// Swap byte field applies to both DMA channel 4 and 5
+	PCIDMAD_sb_b		= 24,		// in DEVCMD field (descriptor)
+	PCIDMAD_sb_m		= 0x01000000,	// swap byte field
+} ;
+
+
+/*******************************************************************************
+ *
+ * PCI Target Control Register
+ *
+ ******************************************************************************/
+enum
+{
+	PCITC_rtimer_b		= 0,		// In PCITC_t -> pcitc
+	PCITC_rtimer_m		= 0x000000ff,
+	PCITC_dtimer_b		= 8,		// In PCITC_t -> pcitc
+	PCITC_dtimer_m		= 0x0000ff00,
+	PCITC_rdr_b		= 18,		// In PCITC_t -> pcitc
+	PCITC_rdr_m		= 0x00040000,
+	PCITC_ddt_b		= 19,		// In PCITC_t -> pcitc
+	PCITC_ddt_m		= 0x00080000,
+} ;
+/*******************************************************************************
+ *
+ * PCI messaging unit [applies to both inbound and outbound registers ]
+ *
+ ******************************************************************************/
+enum
+{
+	PCIM_m0_b	= 0,		// In PCIM_t -> {pci{iic,iim,ioic,ioim}}
+	PCIM_m0_m	= 0x00000001,	// inbound or outbound message 0
+	PCIM_m1_b	= 1,		// In PCIM_t -> {pci{iic,iim,ioic,ioim}}
+	PCIM_m1_m	= 0x00000002,	// inbound or outbound message 1
+	PCIM_db_b	= 2,		// In PCIM_t -> {pci{iic,iim,ioic,ioim}}
+	PCIM_db_m	= 0x00000004,	// inbound or outbound doorbell
+};
+
+
+#endif	// __IDT_PCI_H__
+
+
+
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/pcikorina.h linux/include/asm-mips/rc32434/pcikorina.h
--- linux-2.4.30/include/asm-mips/rc32434/pcikorina.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/pcikorina.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,209 @@
+/* $Id: pciacacia.h,v 1.5 2001/05/01 10:09:17 carstenl Exp $
+ *
+ * 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.
+ */
+#ifndef _PCIKORINA_H
+#define _PCIKORINA_H
+
+
+#define PCI_MSG_VirtualAddress	     0xB8088010
+#define rc32434_pci ((volatile PCI_t) PCI0_VirtualAddress)
+#define rc32434_pci_msg ((volatile PCIM_t) PCI_MSG_VirtualAddress)
+
+#define PCIM_SHFT		0x6
+#define PCIM_BIT_LEN		0x7
+#define PCIM_H_EA		0x3
+#define PCIM_H_IA_FIX		0x4
+#define PCIM_H_IA_RR		0x5
+#if 0
+#define PCI_ADDR_START		0x13000000
+#endif
+
+#define PCI_ADDR_START		0x50000000
+
+#define CPUTOPCI_MEM_WIN	0x02000000
+#define CPUTOPCI_IO_WIN		0x00100000
+#define PCILBA_SIZE_SHFT	2
+#define PCILBA_SIZE_MASK	0x1F
+#define SIZE_256MB		0x1C
+#define SIZE_128MB		0x1B
+#define SIZE_64MB               0x1A
+#define SIZE_32MB		0x19
+#define SIZE_16MB               0x18
+#define SIZE_4MB		0x16
+#define SIZE_2MB		0x15
+#define SIZE_1MB		0x14
+#define KORINA_CONFIG0_ADDR	0x80000000
+#define KORINA_CONFIG1_ADDR	0x80000004
+#define KORINA_CONFIG2_ADDR	0x80000008
+#define KORINA_CONFIG3_ADDR	0x8000000C
+#define KORINA_CONFIG4_ADDR	0x80000010
+#define KORINA_CONFIG5_ADDR	0x80000014
+#define KORINA_CONFIG6_ADDR	0x80000018
+#define KORINA_CONFIG7_ADDR	0x8000001C
+#define KORINA_CONFIG8_ADDR	0x80000020
+#define KORINA_CONFIG9_ADDR	0x80000024
+#define KORINA_CONFIG10_ADDR	0x80000028
+#define KORINA_CONFIG11_ADDR	0x8000002C
+#define KORINA_CONFIG12_ADDR	0x80000030
+#define KORINA_CONFIG13_ADDR	0x80000034
+#define KORINA_CONFIG14_ADDR	0x80000038
+#define KORINA_CONFIG15_ADDR	0x8000003C
+#define KORINA_CONFIG16_ADDR	0x80000040
+#define KORINA_CONFIG17_ADDR	0x80000044
+#define KORINA_CONFIG18_ADDR	0x80000048
+#define KORINA_CONFIG19_ADDR	0x8000004C
+#define KORINA_CONFIG20_ADDR	0x80000050
+#define KORINA_CONFIG21_ADDR	0x80000054
+#define KORINA_CONFIG22_ADDR	0x80000058
+#define KORINA_CONFIG23_ADDR	0x8000005C
+#define KORINA_CONFIG24_ADDR	0x80000060
+#define KORINA_CONFIG25_ADDR	0x80000064
+#define KORINA_CMD 		(PCFG04_command_ioena_m | \
+				 PCFG04_command_memena_m | \
+				 PCFG04_command_bmena_m | \
+				 PCFG04_command_mwinv_m | \
+				 PCFG04_command_parena_m | \
+				 PCFG04_command_serrena_m )
+
+#define KORINA_STAT		(PCFG04_status_mdpe_m | \
+				 PCFG04_status_sta_m  | \
+				 PCFG04_status_rta_m  | \
+				 PCFG04_status_rma_m  | \
+				 PCFG04_status_sse_m  | \
+				 PCFG04_status_pe_m)
+
+#define KORINA_CNFG1		((KORINA_STAT<<16)|KORINA_CMD)
+
+#define KORINA_REVID		0
+#define KORINA_CLASS_CODE	0
+#define KORINA_CNFG2		((KORINA_CLASS_CODE<<8) | \
+				  KORINA_REVID)
+
+#define KORINA_CACHE_LINE_SIZE	4
+#define KORINA_MASTER_LAT	0x3c
+#define KORINA_HEADER_TYPE	0
+#define KORINA_BIST		0
+
+#define KORINA_CNFG3 ((KORINA_BIST << 24) | \
+		      (KORINA_HEADER_TYPE<<16) | \
+		      (KORINA_MASTER_LAT<<8) | \
+		      KORINA_CACHE_LINE_SIZE )
+
+#define KORINA_BAR0	0x00000008 /* 128 MB Memory */
+#define KORINA_BAR1	0x18800001 /* 1 MB IO */
+#define KORINA_BAR2	0x18000001 /* 2 MB IO window for Acacia
+					internal Registers */
+#define KORINA_BAR3	0x48000008 /* Spare 128 MB Memory */
+
+#define KORINA_CNFG4	KORINA_BAR0
+#define KORINA_CNFG5    KORINA_BAR1
+#define KORINA_CNFG6 	KORINA_BAR2
+#define KORINA_CNFG7	KORINA_BAR3
+
+#define KORINA_SUBSYS_VENDOR_ID 0
+#define KORINA_SUBSYSTEM_ID	0
+#define KORINA_CNFG8		0
+#define KORINA_CNFG9		0
+#define KORINA_CNFG10		0
+#define KORINA_CNFG11 	((KORINA_SUBSYS_VENDOR_ID<<16) | \
+			  KORINA_SUBSYSTEM_ID)
+#define KORINA_INT_LINE		1
+#define KORINA_INT_PIN		1
+#define KORINA_MIN_GNT		8
+#define KORINA_MAX_LAT		0x38
+#define KORINA_CNFG12		0
+#define KORINA_CNFG13 		0
+#define KORINA_CNFG14		0
+#define KORINA_CNFG15	((KORINA_MAX_LAT<<24) | \
+			 (KORINA_MIN_GNT<<16) | \
+			 (KORINA_INT_PIN<<8)  | \
+			  KORINA_INT_LINE)
+#define	KORINA_RETRY_LIMIT	0x80
+#define KORINA_TRDY_LIMIT	0x80
+#define KORINA_CNFG16 ((KORINA_RETRY_LIMIT<<8) | \
+			KORINA_TRDY_LIMIT)
+#define PCI_PBAxC_R		0x0
+#define PCI_PBAxC_RL		0x1
+#define PCI_PBAxC_RM		0x2
+#define SIZE_SHFT		2
+
+#ifdef __MIPSEB__
+#define KORINA_PBA0C	( PCIPBAC_mrl_m  | PCIPBAC_sb_m | \
+			  ((PCI_PBAxC_RM &0x3) << PCIPBAC_mr_b) | \
+			  PCIPBAC_pp_m | \
+			  (SIZE_32MB<<SIZE_SHFT) | \
+			   PCIPBAC_p_m)
+#else
+#define KORINA_PBA0C	( PCIPBAC_mrl_m  | \
+			  ((PCI_PBAxC_RM &0x3) << PCIPBAC_mr_b) | \
+			  PCIPBAC_pp_m | \
+			  (SIZE_32MB<<SIZE_SHFT) | \
+			   PCIPBAC_p_m)
+#endif
+
+#if 0
+
+#define KORINA_PBA0C	( PCIPBAC_sb_m | PCIPBAC_pp_m | \
+			  ((PCI_PBAxC_R &0x3) << PCIPBAC_mr_b) | \
+			  (SIZE_128MB<<SIZE_SHFT))
+#endif
+#define KORINA_CNFG17	KORINA_PBA0C
+#define KORINA_PBA0M	0x0
+#define KORINA_CNFG18	KORINA_PBA0M
+
+#ifdef __MIPSEB__
+#define KORINA_PBA1C	((SIZE_1MB<<SIZE_SHFT)  | PCIPBAC_sb_m | \
+			  PCIPBAC_msi_m)
+#else
+#define KORINA_PBA1C	((SIZE_1MB<<SIZE_SHFT)  | \
+			  PCIPBAC_msi_m)
+
+#endif
+
+#define KORINA_CNFG19	KORINA_PBA1C
+#define KORINA_PBA1M	0x0
+#define KORINA_CNFG20	KORINA_PBA1M
+
+#ifdef __MIPSEB__
+#define KORINA_PBA2C	((SIZE_2MB<<SIZE_SHFT)  | PCIPBAC_sb_m | \
+			  PCIPBAC_msi_m)
+#else
+#define KORINA_PBA2C	((SIZE_2MB<<SIZE_SHFT) | \
+			  PCIPBAC_msi_m)
+
+#endif
+#define KORINA_CNFG21	KORINA_PBA2C
+#define KORINA_PBA2M	0x18000000
+#define KORINA_CNFG22	KORINA_PBA2M
+#define KORINA_PBA3C	0
+#define KORINA_CNFG23	KORINA_PBA3C
+#define KORINA_PBA3M	0
+#define KORINA_CNFG24	KORINA_PBA3M
+
+
+
+#define	PCITC_DTIMER_VAL	8
+#define PCITC_RTIMER_VAL	0x10
+
+					  			  
+#endif /* _PCIKORINA_H */
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/rb.h linux/include/asm-mips/rc32434/rb.h
--- linux-2.4.30/include/asm-mips/rc32434/rb.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/rb.h	Tue Sep 28 14:53:53 2004
@@ -0,0 +1,45 @@
+#ifndef __MIPS_RB_H__
+#define __MIPS_RB_H__
+
+#define IDT434_REG_BASE ((volatile void *) KSEG1ADDR(0x18000000))
+#define DEV0BASE 0x010000
+#define DEV0MASK 0x010004
+#define DEV0C 0x010008
+#define DEV0TC 0x01000C
+#define DEV1BASE 0x010010
+#define DEV1MASK 0x010014
+#define DEV1C 0x010018
+#define DEV1TC 0x01001C
+#define DEV2BASE 0x010020
+#define DEV2MASK 0x010024
+#define DEV2C 0x010028
+#define DEV2TC 0x01002C
+#define DEV3BASE 0x010030
+#define DEV3MASK 0x010034
+#define DEV3C 0x010038
+#define DEV3TC 0x01003C
+#define BTCS 0x010040
+#define BTCOMPARE 0x010044
+#define GPIOFUNC 0x050000
+#define GPIOCFG 0x050004
+#define GPIOD 0x050008
+#define GPIOILEVEL 0x05000C
+#define GPIOISTAT 0x050010
+#define GPIONMIEN 0x050014
+#define IMASK6 0x038038
+
+#define LO_WPX (1 << 0)
+#define LO_ALE (1 << 1)
+#define LO_CLE (1 << 2)
+#define LO_CEX (1 << 3)
+#define LO_FOFF (1 << 5)
+#define LO_SPICS (1 << 6)
+#define LO_ULED (1 << 7)
+
+void changeLatchU5(unsigned char orMask, unsigned char nandMask);
+unsigned get434Reg(unsigned regOffs);
+void set434Reg(unsigned regOffs, unsigned bit, unsigned len, unsigned val);
+
+#define get434Reg(x) (*(volatile unsigned *) (IDT434_REG_BASE + (x)))
+
+#endif
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/rc32434.h linux/include/asm-mips/rc32434/rc32434.h
--- linux-2.4.30/include/asm-mips/rc32434/rc32434.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/rc32434.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,153 @@
+/*
+ ***************************************************************************
+ * Definitions for IDT RC323434 CPU.
+ *
+ ****************************************************************************
+ * Kiran Rao
+ * 
+ * Original form
+ ****************************************************************************
+ * P. Sadik   Oct 08, 2003
+ *
+ * Started revision history
+ * Made IDT_BUS_FREQ a kernel configuration parameter
+ ****************************************************************************
+ * P. Sadik   Oct 10, 2003
+ *
+ * Removed IDT_BUS_FREQ, since this parameter is no longer required. Instead
+ * idt_cpu_freq is used everywhere
+ ****************************************************************************
+ * P. Sadik   Oct 20, 2003
+ *
+ * Removed RC32434_BASE_BAUD
+ ****************************************************************************
+*/
+#ifndef _RC32434_H_
+#define _RC32434_H_
+
+#include <linux/delay.h>
+#include <asm/io.h>
+#define RC32434_REG_BASE   0x18000000
+
+#define interrupt ((volatile INT_t ) INT0_VirtualAddress)
+#define timer     ((volatile TIM_t)  TIM0_VirtualAddress)
+#define gpio	  ((volatile GPIO_t) GPIO0_VirtualAddress)
+
+#define IDT_CLOCK_MULT 2
+#define MIPS_CPU_TIMER_IRQ 7
+/* Interrupt Controller */
+#define IC_GROUP0_PEND     (RC32434_REG_BASE + 0x38000)
+#define IC_GROUP0_MASK     (RC32434_REG_BASE + 0x38008)
+#define IC_GROUP_OFFSET    0x0C
+
+#define NUM_INTR_GROUPS    5
+/* 16550 UARTs */
+
+#define GROUP0_IRQ_BASE 8		/* GRP2 IRQ numbers start here */
+#define GROUP1_IRQ_BASE (GROUP0_IRQ_BASE + 32) /* GRP3 IRQ numbers start here */
+#define GROUP2_IRQ_BASE (GROUP1_IRQ_BASE + 32) /* GRP4 IRQ numbers start here */
+#define GROUP3_IRQ_BASE (GROUP2_IRQ_BASE + 32)	/* GRP5 IRQ numbers start here */
+#define GROUP4_IRQ_BASE (GROUP3_IRQ_BASE + 32)
+
+#ifdef __MIPSEB__
+#define RC32434_UART_BASE (RC32434_REG_BASE + 0x58003)
+#else
+#define RC32434_UART_BASE (RC32434_REG_BASE + 0x58000)
+#endif
+
+#define RC32434_UART_IRQ  GROUP3_IRQ_BASE + 0
+
+#define local_readl(addr) __raw_readl(addr)
+#define local_writel(l,addr) __raw_writel(l,addr)
+
+/* cpu pipeline flush */
+static inline void rc32434_sync(void)
+{
+        __asm__ volatile ("sync");
+}
+
+static inline void rc32434_sync_udelay(int us)
+{
+        __asm__ volatile ("sync");
+        udelay(us);
+}
+
+static inline void rc32434_sync_delay(int ms)
+{
+        __asm__ volatile ("sync");
+        mdelay(ms);
+}
+static inline u8 rc32434_readb(unsigned long pa)
+{
+	return *((volatile u8 *)KSEG1ADDR(pa));
+}
+static inline u16 rc32434_readw(unsigned long pa)
+{
+	return *((volatile u16 *)KSEG1ADDR(pa));
+}
+static inline u32 rc32434_readl(unsigned long pa)
+{
+	return *((volatile u32 *)KSEG1ADDR(pa));
+}
+static inline void rc32434_writeb(u8 val, unsigned long pa)
+{
+	*((volatile u8 *)KSEG1ADDR(pa)) = val;
+}
+static inline void rc32434_writew(u16 val, unsigned long pa)
+{
+	*((volatile u16 *)KSEG1ADDR(pa)) = val;
+}
+static inline void rc32434_writel(u32 val, unsigned long pa)
+{
+	*((volatile u32 *)KSEG1ADDR(pa)) = val;
+}
+
+/*
+ * C access to CLZ and CLO instructions
+ * (count leading zeroes/ones).
+ */
+static inline int rc32434_clz(unsigned long val)
+{
+	int ret;
+        __asm__ volatile (
+		".set\tnoreorder\n\t"
+		".set\tnoat\n\t"
+		".set\tmips32\n\t"
+		"clz\t%0,%1\n\t"
+                ".set\tmips0\n\t"
+                ".set\tat\n\t"
+                ".set\treorder"
+                : "=r" (ret)
+		: "r" (val));
+
+	return ret;
+}
+static inline int rc32434_clo(unsigned long val)
+{
+	int ret;
+        __asm__ volatile (
+		".set\tnoreorder\n\t"
+		".set\tnoat\n\t"
+		".set\tmips32\n\t"
+		"clo\t%0,%1\n\t"
+                ".set\tmips0\n\t"
+                ".set\tat\n\t"
+                ".set\treorder"
+                : "=r" (ret)
+		: "r" (val));
+
+	return ret;
+}
+#endif /* _RC32434_H_ */
+
+
+
+
+
+
+
+
+
+
+
+
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/rst.h linux/include/asm-mips/rc32434/rst.h
--- linux-2.4.30/include/asm-mips/rc32434/rst.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/rst.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,105 @@
+#ifndef __IDT_RST_H__
+#define __IDT_RST_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * Reset register definitions.
+ *
+ * File   : $Id: rst.h,v 1.2 2002/06/06 18:34:05 astichte Exp $
+ *
+ * Author : Allen.Stichter@idt.com
+ * Date   : 20020118
+ * Update :
+ *	    $Log: rst.h,v $
+ *	    Revision 1.2  2002/06/06 18:34:05  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:24  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *	
+ *
+ ******************************************************************************/
+
+#include  <asm/rc32434/types.h>
+
+enum
+{
+	RST0_PhysicalAddress	= 0x18000000,
+	RST_PhysicalAddress	= RST0_PhysicalAddress,		// Default
+
+	RST0_VirtualAddress	= 0xb8000000,
+	RST_VirtualAddress	= RST0_VirtualAddress,		// Default
+} ;
+
+typedef struct RST_s
+{
+	U32	filler [0x0006] ;
+	U32	sysid ;
+	U32	filler2 [0x2000-8] ;		// Pad out to offset 0x8000
+	U32	reset ;
+	U32	bcv ;
+	U32	cea ;
+} volatile * RST_t ;
+
+enum
+{
+	SYSID_rev_b		= 0,
+	SYSID_rev_m		= 0x000000ff,
+	SYSID_imp_b		= 8,
+	SYSID_imp_m		= 0x000fff00,
+	SYSID_vendor_b		= 20,
+	SYSID_vendor_m		= 0xfff00000,
+
+	BCV_pll_b		= 0,
+	BCV_pll_m		= 0x0000000f,
+		BCV_pll_PLLBypass_v	= 0x0,	// PCLK=1*CLK.
+		BCV_pll_Mul3_v		= 0x1,	// PCLK=3*CLK.
+		BCV_pll_Mul4_v		= 0x2,	// PCLK=4*CLK.
+		BCV_pll_SlowMul5_v	= 0x3,	// PCLK=4*CLK.
+		BCV_pll_Mul5_v		= 0x4,	// PCLK=6*CLK.
+		BCV_pll_SlowMul6_v	= 0x5,	// PCLK=8*CLK.
+		BCV_pll_Mul6_v		= 0x6,	// PCLK=8*CLK.
+		BCV_pll_Mul8_v		= 0x7,	// PCLK=8*CLK.
+		BCV_pll_Mul10_v		= 0x8,	// PCLK=8*CLK.
+		BCV_pll_Res5_v		= 0x9,
+		BCV_pll_Res6_v		= 0xa,
+		BCV_pll_Res7_v		= 0xb,
+		BCV_pll_Res8_v		= 0xc,
+		BCV_pll_Res13_v		= 0xd,
+		BCV_pll_Res14_v		= 0xe,
+		BCV_pll_Res15_v		= 0xf,
+	BCV_clkDiv_b		= 4,
+	BCV_clkDiv_m		= 0x00000030,
+		BCV_clkDiv_Div1_v	= 0x0,
+		BCV_clkDiv_Div2_v	= 0x1,
+		BCV_clkDiv_Div4_v	= 0x2,
+		BCV_clkDiv_Res3_v	= 0x3,
+	BCV_bigEndian_b		= 6,
+	BCV_bigEndian_m		= 0x00000040,
+	BCV_resetFast_b		= 7,
+	BCV_resetFast_m		= 0x00000080,
+	BCV_pciMode_b		= 8,
+	BCV_pciMode_m		= 0x00000100,
+		BCV_pciMode_disabled_v	= 0,	// PCI is disabled.
+		BCV_pciMode_tnr_v	= 1,	// satellite Target Not Ready.
+		BCV_pciMode_suspended_v	= 2,	// satellite with suspended CPU.
+		BCV_pciMode_external_v	= 3,	// host, external arbiter.
+		BCV_pciMode_fixed_v	= 4,	// host, fixed priority arbiter.
+		BCV_pciMode_roundRobin_v= 5,	// host, round robin arbiter.
+		BCV_pciMode_res6_v	= 6,
+		BCV_pciMode_res7_v	= 7,
+	BCV_watchDisable_b	= 11,
+	BCV_watchDisable_m	= 0x00000800,
+	BCV_pllTest_b		= 12,
+	BCV_pllTest_m		= 0x00001000,
+	BCV_nvramInit_b		= 13,
+	BCV_nvramInit_m		= 0x00002000,
+	BCV_clksyncTstMd_b	= 14,
+	BCV_clksyncTstMd_m	= 0x00004000,
+	BCV_delayBypass_b	= 15,
+	BCV_delayByPass_m	= 0x00008000,
+} ;
+#endif	// __IDT_RST_H__
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/spi.h linux/include/asm-mips/rc32434/spi.h
--- linux-2.4.30/include/asm-mips/rc32434/spi.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/spi.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,100 @@
+#ifndef __IDT_SPI_H__
+#define __IDT_SPI_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * Serial Peripheral Interface register definitions. 
+ *
+ * File   : $Id: spi.h,v 1.2 2002/06/06 18:34:05 astichte Exp $
+ *
+ * Author : ryan.holmQVist@idt.com
+ * Date   : 20011005
+ * Update :
+ *	    $Log: spi.h,v $
+ *	    Revision 1.2  2002/06/06 18:34:05  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:25  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *	
+ *
+ ******************************************************************************/
+
+#include  <asm/rc32434/types.h> 
+
+enum
+{
+	SPI0_PhysicalAddress	= 0x18070000,
+	SPI_PhysicalAddress	= SPI0_PhysicalAddress,
+
+	SPI0_VirtualAddress	= 0xb8070000,
+	SPI_VirtualAddress	= SPI0_VirtualAddress,
+} ;
+
+typedef struct
+{
+	U32 spcp ;	// prescalar. 0=off, * spiClk = sysClk/(2*(spcp+1)*SPR)
+	U32 spc ;	// spi control reg use SPC_
+	U32 sps ;	// spi status reg use SPS_
+	U32 spd ;	// spi data reg use SPD_
+	U32 siofunc ;	// serial IO function use SIOFUNC_
+	U32 siocfg ;	// serial IO config use SIOCFG_
+	U32 siod;	// serial IO data use SIOD_
+} volatile *SPI_t ;
+
+enum
+{
+	SPCP_div_b	 = 0,	       
+	SPCP_div_m	 = 0x000000ff,
+	SPC_spr_b	= 0,	       
+	SPC_spr_m	= 0x00000003,
+	     SPC_spr_div2_v  = 0,
+	     SPC_spr_div4_v  = 1,
+	     SPC_spr_div16_v = 2,
+	     SPC_spr_div32_v = 3,
+	SPC_cpha_b	= 2,	       
+	SPC_cpha_m	= 0x00000004,
+	SPC_cpol_b	= 3,	       
+	SPC_cpol_m	= 0x00000008,
+	SPC_mstr_b	= 4,	       
+	SPC_mstr_m	= 0x00000010,
+	SPC_spe_b	= 6,	       
+	SPC_spe_m	= 0x00000040,
+	SPC_spie_b	= 7,	       
+	SPC_spie_m	= 0x00000080,
+
+	SPS_modf_b	= 4,	       
+	SPS_modf_m	= 0x00000010,
+	SPS_wcol_b	= 6,	       
+	SPS_wcol_m	= 0x00000040,
+	SPS_spif_b	= 7,	       
+	SPS_spif_m	= 0x00000070,
+
+	SPD_data_b	= 0,	       
+	SPD_data_m	= 0x000000ff,
+
+	SIOFUNC_sdo_b	    = 0,	   
+	SIOFUNC_sdo_m	    = 0x00000001,
+	SIOFUNC_sdi_b	    = 1,	   
+	SIOFUNC_sdi_m	    = 0x00000002,
+	SIOFUNC_sck_b	    = 2,	   
+	SIOFUNC_sck_m	    = 0x00000004,
+	
+	SIOCFG_sdo_b	   = 0, 	   
+	SIOCFG_sdo_m	   = 0x00000001,
+	SIOCFG_sdi_b	   = 1, 	   
+	SIOCFG_sdi_m	   = 0x00000002,
+	SIOCFG_sck_b	   = 2, 	   
+	SIOCFG_sck_m	   = 0x00000004,
+	
+	SIOD_sdo_b	 = 0,		 
+	SIOD_sdo_m	 = 0x00000001,
+	SIOD_sdi_b	 = 1,		 
+	SIOD_sdi_m	 = 0x00000002,
+	SIOD_sck_b	 = 2,		 
+	SIOD_sck_m	 = 0x00000004,
+} ;
+#endif	// __IDT_SPI_H__
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/tim.h linux/include/asm-mips/rc32434/tim.h
--- linux-2.4.30/include/asm-mips/rc32434/tim.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/tim.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,78 @@
+#ifndef __IDT_TIM_H__
+#define __IDT_TIM_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * Timer register definition.
+ *
+ * File   : $Id: tim.h,v 1.2 2002/06/06 18:34:05 astichte Exp $
+ *
+ * Author : ryan.holmQVist@idt.com
+ * Date   : 20011005
+ * Update :
+ *	    $Log: tim.h,v $
+ *	    Revision 1.2  2002/06/06 18:34:05  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:25  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *	
+ *
+ ******************************************************************************/
+
+
+#include  <asm/rc32434/types.h> 
+
+enum
+{
+	TIM0_PhysicalAddress	= 0x18028000,
+	TIM_PhysicalAddress	= TIM0_PhysicalAddress,		// Default
+
+	TIM0_VirtualAddress	= 0xb8028000,
+	TIM_VirtualAddress	= TIM0_VirtualAddress,		// Default
+} ;
+
+enum
+{
+	TIM_Count = 3,
+} ;
+
+struct TIM_CNTR_s
+{
+	U32 count ;
+	U32 compare ;
+	U32 ctc ;	//use CTC_
+} ;
+
+typedef struct TIM_s
+{
+	struct TIM_CNTR_s	tim [TIM_Count] ;
+	U32			rcount ;	//use RCOUNT_
+	U32			rcompare ;	//use RCOMPARE_
+	U32			rtc ;		//use RTC_
+} volatile * TIM_t ;
+
+enum
+{
+	CTC_en_b	= 0,		
+	CTC_en_m	= 0x00000001,
+	CTC_to_b	= 1,		 
+	CTC_to_m	= 0x00000002,
+
+	RCOUNT_count_b		= 0,	     
+	RCOUNT_count_m		= 0x0000ffff,
+	RCOMPARE_compare_b	= 0,	   
+	RCOMPARE_compare_m	= 0x0000ffff,
+	RTC_ce_b		= 0,		
+	RTC_ce_m		= 0x00000001,
+	RTC_to_b		= 1,		
+	RTC_to_m		= 0x00000002,
+	RTC_rqe_b		= 2,		
+	RTC_rqe_m		= 0x00000004,
+				 
+} ;
+#endif	// __IDT_TIM_H__
+
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/types.h linux/include/asm-mips/rc32434/types.h
--- linux-2.4.30/include/asm-mips/rc32434/types.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/types.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,39 @@
+#ifndef __IDT_TYPES_H__
+#define __IDT_TYPES_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * Common typedefs used in IDT-generated code.
+ *
+ * File   : $Id: types.h,v 1.1 2002/06/06 16:16:56 astichte Exp $
+ *
+ * Author : Allen.Stichter@idt.com
+ * Date   : 20020606
+ * Update :
+ *	    $Log: types.h,v $
+ *	    Revision 1.1  2002/06/06 16:16:56  astichte
+ *	    Added
+ *	
+ *
+ ******************************************************************************/
+
+typedef unsigned char		U8 ;
+typedef signed char		S8 ;
+
+typedef unsigned short		U16 ;
+typedef signed short		S16 ;
+
+typedef unsigned int		U32 ;
+typedef signed int		S32 ;
+
+typedef unsigned long long	U64 ;
+typedef signed long long	S64 ;
+
+#ifndef __cplusplus
+	typedef U32		bool ;	// (false == 0), (true is != false)
+#endif	// __cplusplus
+
+#endif	// __IDT_TYPES_H__
diff -uNrb linux-2.4.30/include/asm-mips/rc32434/uart.h linux/include/asm-mips/rc32434/uart.h
--- linux-2.4.30/include/asm-mips/rc32434/uart.h	Thu Jan  1 03:00:00 1970
+++ linux/include/asm-mips/rc32434/uart.h	Tue Sep 14 13:56:46 2004
@@ -0,0 +1,178 @@
+#ifndef __IDT_UART_H__
+#define __IDT_UART_H__
+
+/*******************************************************************************
+ *
+ * Copyright 2002 Integrated Device Technology, Inc.
+ *		All rights reserved.
+ *
+ * UART register definitions.
+ *
+ * File   : $Id: uart.h,v 1.3 2002/06/06 18:34:05 astichte Exp $
+ *
+ * Author : Allen.Stichter@idt.com
+ * Date   : 20020118
+ * Update :
+ *	    $Log: uart.h,v $
+ *	    Revision 1.3  2002/06/06 18:34:05  astichte
+ *	    Added XXX_PhysicalAddress and XXX_VirtualAddress
+ *	
+ *	    Revision 1.2  2002/06/04 17:37:52  astichte
+ *	    Updated register definitions.
+ *	
+ *	    Revision 1.1  2002/05/29 17:33:25  sysarch
+ *	    jba File moved from vcode/include/idt/acacia
+ *	
+ *
+ ******************************************************************************/
+
+#include  <asm/rc32434/types.h>
+
+enum
+{
+	UART_PhysicalAddress	= 0x18058000,
+	UART_PhysicalAddress	= UART_PhysicalAddress,	// Default
+
+	UART_VirtualAddress	= 0xb8058000,
+	UART_VirtualAddress	= UART_VirtualAddress,		// Default
+} ;
+
+/*
+ * Register definitions are in bytes so we can handle endian problems.
+ */
+
+typedef struct UART_s
+{
+	union
+	{
+		U32 const	uartrb ;	// 0x00 - DLAB=0, read.
+		U32		uartth ;	// 0x00 - DLAB=0, write.
+		U32		uartdll ;	// 0x00 - DLAB=1, read/write.
+	} ;
+
+	union
+	{
+		U32		uartie ;	// 0x04 - DLAB=0, read/write.
+		U32		uartdlh ;	// 0x04 - DLAB=1, read/write.
+	} ;
+	union
+	{
+		U32 const	uartii ;	// 0x08 - DLAB=0, read.
+		U32		uartfc ;	// 0x08 - DLAB=0, write.
+	} ;
+
+	U32		uartlc ;		// 0x0c
+	U32		uartmc ;		// 0x10
+	U32		uartls ;		// 0x14
+	U32		uartms ;		// 0x18
+	U32		uarts ;			// 0x1c
+} volatile *UART_t ;
+
+// Reset registers.
+typedef U32	volatile *UARTRR_t ;
+
+enum
+{
+	UARTIE_rda_b	= 0,
+	UARTIE_rda_m	= 0x00000001,
+	UARTIE_the_b	= 1,
+	UARTIE_the_m	= 0x00000002,
+	UARTIE_rls_b	= 2,
+	UARTIE_rls_m	= 0x00000004,
+	UARTIE_ems_b	= 3,
+	UARTIE_ems_m	= 0x00000008,
+
+	UARTII_pi_b	= 0,
+	UARTII_pi_m	= 0x00000001,
+	UARTII_iid_b	= 1,
+	UARTII_iid_m	= 0x0000000e,
+		UARTII_iid_ms_v		= 0,	// Modem stat-CTS,DSR,RI or DCD.
+		UARTII_iid_thre_v	= 1,	// Trans. Holding Reg. empty.
+		UARTII_iid_rda_v	= 2,	// Receive data available
+		UARTII_iid_rls_v	= 3,	// Overrun, parity, etc, error.
+		UARTII_iid_res4_v	= 4,	// reserved.
+		UARTII_iid_res5_v	= 5,	// reserved.
+		UARTII_iid_cto_v	= 6,	// Character timeout.
+		UARTII_iid_res7_v	= 7,	// reserved.
+
+	UARTFC_en_b	= 0,
+	UARTFC_en_m	= 0x00000001,
+	UARTFC_rr_b	= 1,
+	UARTFC_rr_m	= 0x00000002,
+	UARTFC_tr_b	= 2,
+	UARTFC_tr_m	= 0x00000004,
+	UARTFC_dms_b	= 3,
+	UARTFC_dms_m	= 0x00000008,
+	UARTFC_rt_b	= 6,
+	UARTFC_rt_m	= 0x000000c0,
+		UARTFC_rt_1Byte_v	= 0,
+		UARTFC_rt_4Byte_v	= 1,
+		UARTFC_rt_8Byte_v	= 2,
+		UARTFC_rt_14Byte_v	= 3,
+
+	UARTLC_wls_b	= 0,
+	UARTLC_wls_m	= 0x00000003,
+		UARTLC_wls_5Bits_v	= 0,
+		UARTLC_wls_6Bits_v	= 1,
+		UARTLC_wls_7Bits_v	= 2,
+		UARTLC_wls_8Bits_v	= 3,
+	UARTLC_stb_b	= 2,
+	UARTLC_stb_m	= 0x00000004,
+	UARTLC_pen_b	= 3,
+	UARTLC_pen_m	= 0x00000008,
+	UARTLC_eps_b	= 4,
+	UARTLC_eps_m	= 0x00000010,
+	UARTLC_sp_b	= 5,
+	UARTLC_sp_m	= 0x00000020,
+	UARTLC_sb_b	= 6,
+	UARTLC_sb_m	= 0x00000040,
+	UARTLC_dlab_b	= 7,
+	UARTLC_dlab_m	= 0x00000080,
+
+	UARTMC_dtr_b	= 0,
+	UARTMC_dtr_m	= 0x00000001,
+	UARTMC_rts_b	= 1,
+	UARTMC_rts_m	= 0x00000002,
+	UARTMC_o1_b	= 2,
+	UARTMC_o1_m	= 0x00000004,
+	UARTMC_o2_b	= 3,
+	UARTMC_o2_m	= 0x00000008,
+	UARTMC_lp_b	= 4,
+	UARTMC_lp_m	= 0x00000010,
+
+	UARTLS_dr_b	= 0,
+	UARTLS_dr_m	= 0x00000001,
+	UARTLS_oe_b	= 1,
+	UARTLS_oe_m	= 0x00000002,
+	UARTLS_pe_b	= 2,
+	UARTLS_pe_m	= 0x00000004,
+	UARTLS_fe_b	= 3,
+	UARTLS_fe_m	= 0x00000008,
+	UARTLS_bi_b	= 4,
+	UARTLS_bi_m	= 0x00000010,
+	UARTLS_thr_b	= 5,
+	UARTLS_thr_m	= 0x00000020,
+	UARTLS_te_b	= 6,
+	UARTLS_te_m	= 0x00000040,
+	UARTLS_rfe_b	= 7,
+	UARTLS_rfe_m	= 0x00000080,
+
+	UARTMS_dcts_b	= 0,
+	UARTMS_dcts_m	= 0x00000001,
+	UARTMS_ddsr_b	= 1,
+	UARTMS_ddsr_m	= 0x00000002,
+	UARTMS_teri_b	= 2,
+	UARTMS_teri_m	= 0x00000004,
+	UARTMS_ddcd_b	= 3,
+	UARTMS_ddcd_m	= 0x00000008,
+	UARTMS_cts_b	= 4,
+	UARTMS_cts_m	= 0x00000010,
+	UARTMS_dsr_b	= 5,
+	UARTMS_dsr_m	= 0x00000020,
+	UARTMS_ri_b	= 6,
+	UARTMS_ri_m	= 0x00000040,
+	UARTMS_dcd_b	= 7,
+	UARTMS_dcd_m	= 0x00000080,
+} ;
+
+#endif	// __IDT_UART_H__
diff -uNrb linux-2.4.30/include/linux/jffs2.h linux/include/linux/jffs2.h
--- linux-2.4.30/include/linux/jffs2.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/jffs2.h	Fri Sep 10 15:15:04 2004
@@ -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.33 2004/05/25 11:31:55 havasi 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
 
@@ -63,6 +43,8 @@
 #define JFFS2_COMPR_COPY	0x04
 #define JFFS2_COMPR_DYNRUBIN	0x05
 #define JFFS2_COMPR_ZLIB	0x06
+#define JFFS2_COMPR_LZO         0x07
+#define JFFS2_COMPR_LZARI       0x08
 /* Compatibility flags. */
 #define JFFS2_COMPAT_MASK 0xc000      /* What do to if an unknown nodetype is found */
 #define JFFS2_NODE_ACCURATE 0x2000
@@ -78,16 +60,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 +74,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 +124,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 -uNrb linux-2.4.30/include/linux/jffs2_fs_i.h linux/include/linux/jffs2_fs_i.h
--- linux-2.4.30/include/linux/jffs2_fs_i.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/jffs2_fs_i.h	Fri Nov 19 14:23:56 2004
@@ -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;
+	rb_root_t 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 -uNrb linux-2.4.30/include/linux/jffs2_fs_sb.h linux/include/linux/jffs2_fs_sb.h
--- linux-2.4.30/include/linux/jffs2_fs_sb.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/jffs2_fs_sb.h	Fri Sep 10 15:15:04 2004
@@ -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];
+
+	wait_queue_head_t inocache_wq;
+	struct jffs2_inode_cache **inocache_list;
 	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)
+	/* 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 -uNrb linux-2.4.30/include/linux/kernel.h linux/include/linux/kernel.h
--- linux-2.4.30/include/linux/kernel.h	Tue Apr 26 10:02:39 2005
+++ linux/include/linux/kernel.h	Tue Apr 26 11:25:10 2005
@@ -93,6 +93,14 @@
 
 extern int session_of_pgrp(int pgrp);
 
+extern int (*printb)(const char *format, ...);
+extern void register_printb(int (*function)(const char *format, ...));
+
+extern void (*flushb)(void);
+extern void register_flushb(void (*function)(void));
+
+extern void flush_dummy(void);
+
 asmlinkage int printk(const char * fmt, ...)
 	__attribute__ ((format (printf, 1, 2)));
 
diff -uNrb linux-2.4.30/include/linux/kernel_stat.h linux/include/linux/kernel_stat.h
--- linux-2.4.30/include/linux/kernel_stat.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/kernel_stat.h	Tue Sep  7 16:21:36 2004
@@ -32,6 +32,7 @@
 	unsigned int irqs[NR_CPUS][NR_IRQS];
 #endif
 	unsigned int context_swtch;
+	unsigned long long rootdrv_wblk;
 };
 
 extern struct kernel_stat kstat;
diff -uNrb linux-2.4.30/include/linux/mtd/blktrans.h linux/include/linux/mtd/blktrans.h
--- linux-2.4.30/include/linux/mtd/blktrans.h	Thu Jan  1 03:00:00 1970
+++ linux/include/linux/mtd/blktrans.h	Fri Sep 10 15:15:04 2004
@@ -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 -uNrb linux-2.4.30/include/linux/mtd/cfi.h linux/include/linux/mtd/cfi.h
--- linux-2.4.30/include/linux/mtd/cfi.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/mtd/cfi.h	Fri Sep 10 15:15:04 2004
@@ -1,211 +1,86 @@
 
 /* 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.46 2004/08/12 07:49:04 eric 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>
 #include <linux/mtd/flashchip.h>
+#include <linux/mtd/map.h>
 #include <linux/mtd/cfi_endian.h>
 
-/*
- * You can optimize the code size and performance by defining only 
- * the geometry(ies) available on your hardware.
- * CFIDEV_INTERLEAVE_n, where  represents the interleave (number of chips to fill the bus width)
- * CFIDEV_BUSWIDTH_n, where n is the bus width in bytes (1, 2, 4 or 8 bytes)
- *
- * By default, all (known) geometries are supported.
- */
-
-#ifndef CONFIG_MTD_CFI_GEOMETRY
-
-/* The default case - support all but 64-bit, which has
-   a performance penalty */
-
-#define CFIDEV_INTERLEAVE_1 (1)
-#define CFIDEV_INTERLEAVE_2 (2)
-#define CFIDEV_INTERLEAVE_4 (4)
-
-#define CFIDEV_BUSWIDTH_1 (1)
-#define CFIDEV_BUSWIDTH_2 (2)
-#define CFIDEV_BUSWIDTH_4 (4)
-
-typedef __u32 cfi_word;
-
-#else
-
-/* Explicitly configured buswidth/interleave support */
-
 #ifdef CONFIG_MTD_CFI_I1
-#define CFIDEV_INTERLEAVE_1 (1)
-#endif
-#ifdef CONFIG_MTD_CFI_I2
-#define CFIDEV_INTERLEAVE_2 (2)
-#endif
-#ifdef CONFIG_MTD_CFI_I4
-#define CFIDEV_INTERLEAVE_4 (4)
-#endif
-#ifdef CONFIG_MTD_CFI_I8
-#define CFIDEV_INTERLEAVE_8 (8)
-#endif
-
-#ifdef CONFIG_MTD_CFI_B1
-#define CFIDEV_BUSWIDTH_1 (1)
-#endif
-#ifdef CONFIG_MTD_CFI_B2
-#define CFIDEV_BUSWIDTH_2 (2)
-#endif
-#ifdef CONFIG_MTD_CFI_B4
-#define CFIDEV_BUSWIDTH_4 (4)
-#endif
-#ifdef CONFIG_MTD_CFI_B8
-#define CFIDEV_BUSWIDTH_8 (8)
-#endif
-
-/* pick the largest necessary */
-#ifdef CONFIG_MTD_CFI_B8
-typedef __u64 cfi_word;
-
-/* This only works if asm/io.h is included first */
-#ifndef __raw_readll
-#define __raw_readll(addr)	(*(volatile __u64 *)(addr))
-#endif
-#ifndef __raw_writell
-#define __raw_writell(v, addr)	(*(volatile __u64 *)(addr) = (v))
-#endif
-#define CFI_WORD_64
-#else  /* CONFIG_MTD_CFI_B8 */
-/* All others can use 32-bits. It's probably more efficient than
-   the smaller types anyway */
-typedef __u32 cfi_word;
-#endif /* CONFIG_MTD_CFI_B8 */
-
-#endif
-
-/*
- * The following macros are used to select the code to execute:
- *   cfi_buswidth_is_*()
- *   cfi_interleave_is_*()
- *   [where * is either 1, 2, 4, or 8]
- * Those macros should be used with 'if' statements.  If only one of few
- * geometry arrangements are selected, they expand to constants thus allowing
- * the compiler (most of them being 0) to optimize away all the unneeded code,
- * while still validating the syntax (which is not possible with embedded 
- * #if ... #endif constructs).
- * The exception to this is the 64-bit versions, which need an extension
- * to the cfi_word type, and cause compiler warnings about shifts being
- * out of range.
- */
-
-#ifdef CFIDEV_INTERLEAVE_1
-# ifdef CFIDEV_INTERLEAVE
-#  undef CFIDEV_INTERLEAVE
-#  define CFIDEV_INTERLEAVE (cfi->interleave)
-# else
-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_1
-# endif
-# define cfi_interleave_is_1() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_1)
+#define cfi_interleave(cfi) 1
+#define cfi_interleave_is_1(cfi) (cfi_interleave(cfi) == 1)
 #else
-# define cfi_interleave_is_1() (0)
+#define cfi_interleave_is_1(cfi) (0)
 #endif
 
-#ifdef CFIDEV_INTERLEAVE_2
-# ifdef CFIDEV_INTERLEAVE
-#  undef CFIDEV_INTERLEAVE
-#  define CFIDEV_INTERLEAVE (cfi->interleave)
+#ifdef CONFIG_MTD_CFI_I2
+# ifdef cfi_interleave
+#  undef cfi_interleave
+#  define cfi_interleave(cfi) ((cfi)->interleave)
 # else
-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_2
+#  define cfi_interleave(cfi) 2
 # endif
-# define cfi_interleave_is_2() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_2)
+#define cfi_interleave_is_2(cfi) (cfi_interleave(cfi) == 2)
 #else
-# define cfi_interleave_is_2() (0)
+#define cfi_interleave_is_2(cfi) (0)
 #endif
 
-#ifdef CFIDEV_INTERLEAVE_4
-# ifdef CFIDEV_INTERLEAVE
-#  undef CFIDEV_INTERLEAVE
-#  define CFIDEV_INTERLEAVE (cfi->interleave)
+#ifdef CONFIG_MTD_CFI_I4
+# ifdef cfi_interleave
+#  undef cfi_interleave
+#  define cfi_interleave(cfi) ((cfi)->interleave)
 # else
-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_4
+#  define cfi_interleave(cfi) 4
 # endif
-# define cfi_interleave_is_4() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_4)
+#define cfi_interleave_is_4(cfi) (cfi_interleave(cfi) == 4)
 #else
-# define cfi_interleave_is_4() (0)
+#define cfi_interleave_is_4(cfi) (0)
 #endif
 
-#ifdef CFIDEV_INTERLEAVE_8
-# ifdef CFIDEV_INTERLEAVE
-#  undef CFIDEV_INTERLEAVE
-#  define CFIDEV_INTERLEAVE (cfi->interleave)
+#ifdef CONFIG_MTD_CFI_I8
+# ifdef cfi_interleave
+#  undef cfi_interleave
+#  define cfi_interleave(cfi) ((cfi)->interleave)
 # else
-#  define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_8
+#  define cfi_interleave(cfi) 8
 # endif
-# define cfi_interleave_is_8() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_8)
+#define cfi_interleave_is_8(cfi) (cfi_interleave(cfi) == 8)
 #else
-# define cfi_interleave_is_8() (0)
+#define cfi_interleave_is_8(cfi) (0)
 #endif
 
-#ifndef CFIDEV_INTERLEAVE
-#error You must define at least one interleave to support!
+static inline int cfi_interleave_supported(int i)
+{
+	switch (i) {
+#ifdef CONFIG_MTD_CFI_I1
+	case 1:
 #endif
-
-#ifdef CFIDEV_BUSWIDTH_1
-# ifdef CFIDEV_BUSWIDTH
-#  undef CFIDEV_BUSWIDTH
-#  define CFIDEV_BUSWIDTH (map->buswidth)
-# else
-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_1
-# endif
-# define cfi_buswidth_is_1() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_1)
-#else
-# define cfi_buswidth_is_1() (0)
+#ifdef CONFIG_MTD_CFI_I2
+	case 2:
 #endif
-
-#ifdef CFIDEV_BUSWIDTH_2
-# ifdef CFIDEV_BUSWIDTH
-#  undef CFIDEV_BUSWIDTH
-#  define CFIDEV_BUSWIDTH (map->buswidth)
-# else
-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_2
-# endif
-# define cfi_buswidth_is_2() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_2)
-#else
-# define cfi_buswidth_is_2() (0)
+#ifdef CONFIG_MTD_CFI_I4
+	case 4:
 #endif
-
-#ifdef CFIDEV_BUSWIDTH_4
-# ifdef CFIDEV_BUSWIDTH
-#  undef CFIDEV_BUSWIDTH
-#  define CFIDEV_BUSWIDTH (map->buswidth)
-# else
-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_4
-# endif
-# define cfi_buswidth_is_4() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_4)
-#else
-# define cfi_buswidth_is_4() (0)
+#ifdef CONFIG_MTD_CFI_I8
+	case 8:
 #endif
+		return 1;
 
-#ifdef CFIDEV_BUSWIDTH_8
-# ifdef CFIDEV_BUSWIDTH
-#  undef CFIDEV_BUSWIDTH
-#  define CFIDEV_BUSWIDTH (map->buswidth)
-# else
-#  define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_8
-# endif
-# define cfi_buswidth_is_8() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_8)
-#else
-# define cfi_buswidth_is_8() (0)
-#endif
+	default:
+		return 0;
+	}
+}
 
-#ifndef CFIDEV_BUSWIDTH
-#error You must define at least one bus width to support!
-#endif
 
 /* NB: these values must represents the number of bytes needed to meet the 
  *     device type (x8, x16, x32).  Eg. a 32 bit device is 4 x 8 bytes. 
@@ -222,64 +97,84 @@
 
 /* Basic Query Structure */
 struct cfi_ident {
-  __u8  qry[3];
-  __u16 P_ID;
-  __u16 P_ADR;
-  __u16 A_ID;
-  __u16 A_ADR;
-  __u8  VccMin;
-  __u8  VccMax;
-  __u8  VppMin;
-  __u8  VppMax;
-  __u8  WordWriteTimeoutTyp;
-  __u8  BufWriteTimeoutTyp;
-  __u8  BlockEraseTimeoutTyp;
-  __u8  ChipEraseTimeoutTyp;
-  __u8  WordWriteTimeoutMax;
-  __u8  BufWriteTimeoutMax;
-  __u8  BlockEraseTimeoutMax;
-  __u8  ChipEraseTimeoutMax;
-  __u8  DevSize;
-  __u16 InterfaceDesc;
-  __u16 MaxBufWriteSize;
-  __u8  NumEraseRegions;
-  __u32 EraseRegionInfo[0]; /* Not host ordered */
+	uint8_t  qry[3];
+	uint16_t P_ID;
+	uint16_t P_ADR;
+	uint16_t A_ID;
+	uint16_t A_ADR;
+	uint8_t  VccMin;
+	uint8_t  VccMax;
+	uint8_t  VppMin;
+	uint8_t  VppMax;
+	uint8_t  WordWriteTimeoutTyp;
+	uint8_t  BufWriteTimeoutTyp;
+	uint8_t  BlockEraseTimeoutTyp;
+	uint8_t  ChipEraseTimeoutTyp;
+	uint8_t  WordWriteTimeoutMax;
+	uint8_t  BufWriteTimeoutMax;
+	uint8_t  BlockEraseTimeoutMax;
+	uint8_t  ChipEraseTimeoutMax;
+	uint8_t  DevSize;
+	uint16_t InterfaceDesc;
+	uint16_t MaxBufWriteSize;
+	uint8_t  NumEraseRegions;
+	uint32_t EraseRegionInfo[0]; /* Not host ordered */
 } __attribute__((packed));
 
 /* Extended Query Structure for both PRI and ALT */
 
 struct cfi_extquery {
-  __u8  pri[3];
-  __u8  MajorVersion;
-  __u8  MinorVersion;
+	uint8_t  pri[3];
+	uint8_t  MajorVersion;
+	uint8_t  MinorVersion;
 } __attribute__((packed));
 
 /* Vendor-Specific PRI for Intel/Sharp Extended Command Set (0x0001) */
 
 struct cfi_pri_intelext {
-  __u8  pri[3];
-  __u8  MajorVersion;
-  __u8  MinorVersion;
-  __u32 FeatureSupport;
-  __u8  SuspendCmdSupport;
-  __u16 BlkStatusRegMask;
-  __u8  VccOptimal;
-  __u8  VppOptimal;
-  __u8  NumProtectionFields;
-  __u16 ProtRegAddr;
-  __u8  FactProtRegSize;
-  __u8  UserProtRegSize;
+	uint8_t  pri[3];
+	uint8_t  MajorVersion;
+	uint8_t  MinorVersion;
+	uint32_t FeatureSupport; /* if bit 31 is set then an additional uint32_t feature
+				    block follows - FIXME - not currently supported */
+	uint8_t  SuspendCmdSupport;
+	uint16_t BlkStatusRegMask;
+	uint8_t  VccOptimal;
+	uint8_t  VppOptimal;
+	uint8_t  NumProtectionFields;
+	uint16_t ProtRegAddr;
+	uint8_t  FactProtRegSize;
+	uint8_t  UserProtRegSize;
+} __attribute__((packed));
+
+/* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */
+
+struct cfi_pri_amdstd {
+	uint8_t  pri[3];
+	uint8_t  MajorVersion;
+	uint8_t  MinorVersion;
+	uint8_t  SiliconRevision; /* bits 1-0: Address Sensitive Unlock */
+	uint8_t  EraseSuspend;
+	uint8_t  BlkProt;
+	uint8_t  TmpBlkUnprotect;
+	uint8_t  BlkProtUnprot;
+	uint8_t  SimultaneousOps;
+	uint8_t  BurstMode;
+	uint8_t  PageMode;
+	uint8_t  VppMin;
+	uint8_t  VppMax;
+	uint8_t  TopBottom;
 } __attribute__((packed));
 
 struct cfi_pri_query {
-  __u8  NumFields;
-  __u32 ProtField[1]; /* Not host ordered */
+	uint8_t  NumFields;
+	uint32_t ProtField[1]; /* Not host ordered */
 } __attribute__((packed));
 
 struct cfi_bri_query {
-  __u8  PageModeReadCap;
-  __u8  NumFields;
-  __u32 ConfField[1]; /* Not host ordered */
+	uint8_t  PageModeReadCap;
+	uint8_t  NumFields;
+	uint32_t ConfField[1]; /* Not host ordered */
 } __attribute__((packed));
 
 #define P_ID_NONE 0
@@ -287,8 +182,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
 
 
@@ -296,14 +193,13 @@
 #define CFI_MODE_JEDEC	0
 
 struct cfi_private {
-	__u16 cmdset;
+	uint16_t cmdset;
 	void *cmdset_priv;
 	int interleave;
 	int device_type;
 	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,107 +210,81 @@
 	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.
  */
-static inline __u32 cfi_build_cmd_addr(__u32 cmd_ofs, int interleave, int type)
+static inline uint32_t cfi_build_cmd_addr(uint32_t cmd_ofs, int interleave, int type)
 {
 	return (cmd_ofs * type) * interleave;
 }
 
 /*
- * Transforms the CFI command for the given geometry (bus width & interleave.
+ * Transforms the CFI command for the given geometry (bus width & interleave).
+ * It looks too long to be inline, but in the common case it should almost all
+ * get optimised away. 
  */
-static inline cfi_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi)
+static inline map_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi)
 {
-	cfi_word val = 0;
-
-	if (cfi_buswidth_is_1()) {
-		/* 1 x8 device */
-		val = cmd;
-	} else if (cfi_buswidth_is_2()) {
-		if (cfi_interleave_is_1()) {
-			/* 1 x16 device in x16 mode */
-			val = cpu_to_cfi16(cmd);
-		} else if (cfi_interleave_is_2()) {
-			/* 2 (x8, x16 or x32) devices in x8 mode */
-			val = cpu_to_cfi16((cmd << 8) | cmd);
-		}
-	} else if (cfi_buswidth_is_4()) {
-		if (cfi_interleave_is_1()) {
-			/* 1 x32 device in x32 mode */
-			val = cpu_to_cfi32(cmd);
-		} else if (cfi_interleave_is_2()) {
-			/* 2 x16 device in x16 mode */
-			val = cpu_to_cfi32((cmd << 16) | cmd);
-		} else if (cfi_interleave_is_4()) {
-			/* 4 (x8, x16 or x32) devices in x8 mode */
-			val = (cmd << 16) | cmd;
-			val = cpu_to_cfi32((val << 8) | val);
-		}
-#ifdef CFI_WORD_64
-	} else if (cfi_buswidth_is_8()) {
-		if (cfi_interleave_is_1()) {
-			/* 1 x64 device in x64 mode */
-			val = cpu_to_cfi64(cmd);
-		} else if (cfi_interleave_is_2()) {
-			/* 2 x32 device in x32 mode */
-			val = cmd;
-			val = cpu_to_cfi64((val << 32) | val);
-		} else if (cfi_interleave_is_4()) {
-			/* 4 (x16, x32 or x64) devices in x16 mode */
-			val = (cmd << 16) | cmd;
-			val = cpu_to_cfi64((val << 32) | val);
-		} else if (cfi_interleave_is_8()) {
-			/* 8 (x8, x16 or x32) devices in x8 mode */
-			val = (cmd << 8) | cmd;
-			val = (val << 16) | val;
-			val = (val << 32) | val;
-			val = cpu_to_cfi64(val);
-		}
-#endif /* CFI_WORD_64 */
+	map_word val = { {0} };
+	int wordwidth, words_per_bus, chip_mode, chips_per_word;
+	unsigned long onecmd;
+	int i;
+
+	/* We do it this way to give the compiler a fighting chance 
+	   of optimising away all the crap for 'bankwidth' larger than
+	   an unsigned long, in the common case where that support is
+	   disabled */
+	if (map_bankwidth_is_large(map)) {
+		wordwidth = sizeof(unsigned long);
+		words_per_bus = (map_bankwidth(map)) / wordwidth; // i.e. normally 1
+	} else {
+		wordwidth = map_bankwidth(map);
+		words_per_bus = 1;
 	}
-	return val;
-}
-#define CMD(x)  cfi_build_cmd((x), map, cfi)
 
-/*
- * Read a value according to the bus width.
- */
+	chip_mode = map_bankwidth(map) / cfi_interleave(cfi);
+	chips_per_word = wordwidth * cfi_interleave(cfi) / map_bankwidth(map);
 
-static inline cfi_word cfi_read(struct map_info *map, __u32 addr)
-{
-	if (cfi_buswidth_is_1()) {
-		return map->read8(map, addr);
-	} else if (cfi_buswidth_is_2()) {
-		return map->read16(map, addr);
-	} else if (cfi_buswidth_is_4()) {
-		return map->read32(map, addr);
-	} else if (cfi_buswidth_is_8()) {
-		return map->read64(map, addr);
-	} else {
-		return 0;
+	/* First, determine what the bit-pattern should be for a single
+	   device, according to chip mode and endianness... */
+	switch (chip_mode) {
+	default: BUG();
+	case 1:
+		onecmd = cmd;
+		break;
+	case 2:
+		onecmd = cpu_to_cfi16(cmd);
+		break;
+	case 4:
+		onecmd = cpu_to_cfi32(cmd);
+		break;
 	}
-}
 
-/*
- * Write a value according to the bus width.
- */
+	/* Now replicate it across the size of an unsigned long, or 
+	   just to the bus width as appropriate */
+	switch (chips_per_word) {
+	default: BUG();
+#if BITS_PER_LONG >= 64
+	case 8:
+		onecmd |= (onecmd << (chip_mode * 32));
+#endif
+	case 4:
+		onecmd |= (onecmd << (chip_mode * 16));
+	case 2:
+		onecmd |= (onecmd << (chip_mode * 8));
+	case 1:
+		;
+	}
 
-static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr)
-{
-	if (cfi_buswidth_is_1()) {
-		map->write8(map, val, addr);
-	} else if (cfi_buswidth_is_2()) {
-		map->write16(map, val, addr);
-	} else if (cfi_buswidth_is_4()) {
-		map->write32(map, val, addr);
-	} else if (cfi_buswidth_is_8()) {
-		map->write64(map, val, addr);
+	/* And finally, for the multi-word case, replicate it 
+	   in all words in the structure */
+	for (i=0; i < words_per_bus; i++) {
+		val.x[i] = onecmd;
 	}
+
+	return val;
 }
+#define CMD(x)  cfi_build_cmd((x), map, cfi)
 
 /*
  * Sends a CFI command to a bank of flash for the given geometry.
@@ -423,48 +293,47 @@
  * If prev_val is non-null, it will be set to the value at the command address,
  * before the command was written.
  */
-static inline __u32 cfi_send_gen_cmd(u_char cmd, __u32 cmd_addr, __u32 base,
+static inline uint32_t cfi_send_gen_cmd(u_char cmd, uint32_t cmd_addr, uint32_t base,
 				struct map_info *map, struct cfi_private *cfi,
-				int type, cfi_word *prev_val)
+				int type, map_word *prev_val)
 {
-	cfi_word val;
-	__u32 addr = base + cfi_build_cmd_addr(cmd_addr, CFIDEV_INTERLEAVE, type);
+	map_word val;
+	uint32_t addr = base + cfi_build_cmd_addr(cmd_addr, cfi_interleave(cfi), type);
 
 	val = cfi_build_cmd(cmd, map, cfi);
 
 	if (prev_val)
-		*prev_val = cfi_read(map, addr);
+		*prev_val = map_read(map, addr);
 
-	cfi_write(map, val, addr);
+	map_write(map, val, addr);
 
 	return addr - base;
 }
 
-static inline __u8 cfi_read_query(struct map_info *map, __u32 addr)
+static inline uint8_t cfi_read_query(struct map_info *map, uint32_t addr)
 {
-	if (cfi_buswidth_is_1()) {
-		return map->read8(map, addr);
-	} else if (cfi_buswidth_is_2()) {
-		return cfi16_to_cpu(map->read16(map, addr));
-	} else if (cfi_buswidth_is_4()) {
-		return cfi32_to_cpu(map->read32(map, addr));
-	} else if (cfi_buswidth_is_8()) {
-		return cfi64_to_cpu(map->read64(map, addr));
+	map_word val = map_read(map, addr);
+
+	if (map_bankwidth_is_1(map)) {
+		return val.x[0];
+	} else if (map_bankwidth_is_2(map)) {
+		return cfi16_to_cpu(val.x[0]);
 	} else {
-		return 0;
+		/* No point in a 64-bit byteswap since that would just be
+		   swapping the responses from different chips, and we are
+		   only interested in one chip (a representative sample) */
+		return cfi32_to_cpu(val.x[0]);
 	}
 }
 
 static inline void cfi_udelay(int us)
 {
-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)
 	unsigned long t = us * HZ / 1000000;
 	if (t) {
 		set_current_state(TASK_UNINTERRUPTIBLE);
 		schedule_timeout(t);
 		return;
 	}
-#endif
 	udelay(us);
 	cond_resched();
 }
@@ -478,6 +347,29 @@
 {
 	spin_unlock_bh(mutex);
 }
+
+struct cfi_extquery *cfi_read_pri(struct map_info *map, uint16_t adr, uint16_t size,
+			     const char* name);
+struct cfi_fixup {
+	uint16_t mfr;
+	uint16_t id;
+	void (*fixup)(struct mtd_info *mtd, void* param);
+	void* param;
+};
+
+#define CFI_MFR_ANY 0xffff
+#define CFI_ID_ANY  0xffff
+
+#define CFI_MFR_AMD 0x0001
+#define CFI_MFR_ST  0x0020 	/* STMicroelectronics */
+
+void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups);
+
+typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip,
+			      unsigned long adr, int len, void *thunk);
+
+int cfi_varsize_frob(struct mtd_info *mtd, varsize_frob_t frob,
+	loff_t ofs, size_t len, void *thunk);
 
 
 #endif /* __MTD_CFI_H__ */
diff -uNrb linux-2.4.30/include/linux/mtd/compatmac.h linux/include/linux/mtd/compatmac.h
--- linux-2.4.30/include/linux/mtd/compatmac.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/mtd/compatmac.h	Tue Sep 28 14:37:46 2004
@@ -1,583 +1,210 @@
-
 /*
- * mtd/include/compatmac.h
- *
- * $Id: compatmac.h,v 1.45 2003/01/24 15:50:57 dwmw2 Exp $
+ * $Id: compatmac.h,v 1.68 2004/09/17 22:00:30 eric 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 */
-#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__
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10)
+#error "This kernel is too old: not supported by this file"
 #endif
 
-/* 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
+	/* O(1) scheduler stuff. */
 
-/* changed the prototype of read/write */
+#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 ()
 
-#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
+#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0)
 #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>
+	daemonize();
 
-#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
+	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,2,0) 
-static inline int invalidate_device(kdev_t dev, int do_sync) {
+static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info)
+{
+	unsigned long flags;
+	unsigned long ret;
 
-	if (do_sync)
-		fsync_dev(dev);
+	spin_lock_irqsave(&current->sigmask_lock, flags);
+	ret = dequeue_signal(mask, info);
+	spin_unlock_irqrestore(&current->sigmask_lock, flags);
 	
-	invalidate_buffers(dev);
-	return 0;
+	return ret;
 }
-#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);
+static inline int allow_signal(int sig)
+{
+	if (sig < 1 || sig > _NSIG)
+		return -EINVAL;
 
-	invalidate_buffers(dev);
-	return res;
+        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
-
-#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);
-
-#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
-
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \
-    (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__))
-
-#include <linux/genhd.h>
-
-static inline void add_gendisk(struct gendisk *gp)
+static inline int disallow_signal(int sig)
 {
-	gp->next = gendisk_head;
-	gendisk_head = gp;
-}
+	if (sig < 1 || sig > _NSIG)
+		return -EINVAL;
 
-static inline void del_gendisk(struct gendisk *gp)
-{
-	struct gendisk *gd, **gdp;
+	spin_lock_irq(&current->sigmask_lock);
+	sigaddset(&current->blocked, sig);
+	recalc_sigpending();
 
-	for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next))
-		if (*gdp == gp) {
-			gd = *gdp; *gdp = gd->next;
-			break;
-		}
+	current->sig->action[sig-1].sa.sa_handler = SIG_DFL;
+	spin_unlock_irq(&current->sigmask_lock);
+	return 0;
 }
+
+#define PF_FREEZE 0
+#define refrigerator(x) do { ; } while(0)
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) && defined(MODULE)
+	/* Module bits */
 
-#define module_init(func)		\
-mod_init_t init_module(void) {		\
-	return func();			\
-}
 
-#define module_exit(func)		\
-mod_exit_t cleanup_module(void) {	\
-	return func();			\
-}
+#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,9) || \
-    (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__))
-#define MODULE_LICENSE(x) /* */
-#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
+	/* Random filesystem stuff, only for JFFS2 really */
 
 #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)
-#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
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
+#define PageUptodate(x) Page_Uptodate(x)
 #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)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
+#define get_seconds() CURRENT_TIME
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21)
-#define rq_data_dir(x)	((x)->cmd)
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,53)
+#define generic_file_readonly_mmap generic_file_mmap
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0)
-
-#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 */
+#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70)
 
-#define IS_REQ_CMD(req) ((req)->flags & REQ_CMD)
+#include <linux/kmod.h>
+#include <linux/string.h>
 
-#define QUEUE_LOCK(q) ((q)->queue_lock)
-
-#define BLK_INIT_QUEUE(q, req, lock) blk_init_queue((q), (req), (lock)) 
-
-#endif
+static inline char *strlcpy(char *dest, const char *src, int len)
+{
+	dest[len-1] = 0;
+	return strncpy(dest, src, len-1);
+}
 
-/* 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))
+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
 
-#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,0)
+#define kvec iovec
+#define __user 
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12)
-#define PageUptodate(x) Page_Uptodate(x)
+#ifndef __iomem
+#define __iomem
 #endif
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48)
-#define get_seconds() CURRENT_TIME
-#endif
+#ifndef list_for_each_entry_safe
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:	the type * to use as a loop counter.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head); 					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
 
-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,53)
-#define generic_file_readonly_mmap generic_file_mmap
 #endif
 
 #endif /* __LINUX_MTD_COMPATMAC_H__ */
diff -uNrb linux-2.4.30/include/linux/mtd/doc2000.h linux/include/linux/mtd/doc2000.h
--- linux-2.4.30/include/linux/mtd/doc2000.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/mtd/doc2000.h	Tue Sep 28 14:37:46 2004
@@ -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.23 2004/09/16 23:26:08 gleixner 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,22 +46,51 @@
 #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))
-#define WriteDOC_(d, adr, reg)  writeb(d, ((unsigned long)adr) + (reg))
+#define ReadDOC_(adr, reg)      readb((void __iomem *)(((unsigned long)adr) + (reg)))
+#define WriteDOC_(d, adr, reg)  writeb(d, (void __iomem *)(((unsigned long)adr) + (reg)))
 #define DOC_IOREMAP_LEN 0x2000
 
 #endif
@@ -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,18 +156,21 @@
 #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
 
 struct DiskOnChip {
 	unsigned long physadr;
-	unsigned long virtadr;
+	void __iomem *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 -uNrb linux-2.4.30/include/linux/mtd/flashchip.h linux/include/linux/mtd/flashchip.h
--- linux-2.4.30/include/linux/mtd/flashchip.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/mtd/flashchip.h	Fri Sep 10 15:15:04 2004
@@ -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.14 2004/06/15 16:44:59 nico Exp $
  *
  */
 
@@ -43,7 +43,8 @@
 
 
 /* NOTE: confusingly, this can be used to refer to more than one chip at a time, 
-   if they're interleaved. */
+   if they're interleaved.  This can even refer to individual partitions on
+   the same physical chip when present. */
 
 struct flchip {
 	unsigned long start; /* Offset within the map */
@@ -58,6 +59,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
@@ -65,8 +71,17 @@
 	int word_write_time;
 	int buffer_write_time;
 	int erase_time;
+
+	void *priv;
 };
 
+/* This is used to handle contention on write/erase operations
+   between partitions of the same physical chip. */
+struct flchip_shared {
+	spinlock_t lock;
+	struct flchip *writing;
+	struct flchip *erasing;
+};
 
 
 #endif /* __MTD_FLASHCHIP_H__ */
diff -uNrb linux-2.4.30/include/linux/mtd/gen_probe.h linux/include/linux/mtd/gen_probe.h
--- linux-2.4.30/include/linux/mtd/gen_probe.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/mtd/gen_probe.h	Fri Sep 10 15:15:04 2004
@@ -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 -uNrb linux-2.4.30/include/linux/mtd/inftl.h linux/include/linux/mtd/inftl.h
--- linux-2.4.30/include/linux/mtd/inftl.h	Thu Jan  1 03:00:00 1970
+++ linux/include/linux/mtd/inftl.h	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,57 @@
+/*
+ *	inftl.h -- defines to support the Inverse NAND Flash Translation Layer
+ *
+ *	(C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
+ *
+ *	$Id: inftl.h,v 1.6 2004/06/30 14:49:00 dbrown 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;
+	__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;
+        struct nand_oobinfo oobinfo;
+};
+
+int INFTL_mount(struct INFTLrecord *s);
+int INFTL_formatblock(struct INFTLrecord *s, int block);
+
+#endif /* __KERNEL__ */
+
+#endif /* __MTD_INFTL_H__ */
diff -uNrb linux-2.4.30/include/linux/mtd/jedec.h linux/include/linux/mtd/jedec.h
--- linux-2.4.30/include/linux/mtd/jedec.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/mtd/jedec.h	Fri Sep 10 15:15:04 2004
@@ -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 -uNrb linux-2.4.30/include/linux/mtd/map.h linux/include/linux/mtd/map.h
--- linux-2.4.30/include/linux/mtd/map.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/mtd/map.h	Tue Sep 28 14:37:46 2004
@@ -1,23 +1,176 @@
 
 /* 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.45 2004/09/21 14:31:17 bjd 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 <linux/mtd/compatmac.h>
+#include <asm/unaligned.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/bug.h>
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
+#define map_bankwidth(map) 1
+#define map_bankwidth_is_1(map) (map_bankwidth(map) == 1)
+#define map_bankwidth_is_large(map) (0)
+#define map_words(map) (1)
+#define MAX_MAP_BANKWIDTH 1
+#else
+#define map_bankwidth_is_1(map) (0)
+#endif
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
+# ifdef map_bankwidth
+#  undef map_bankwidth
+#  define map_bankwidth(map) ((map)->bankwidth)
+# else
+#  define map_bankwidth(map) 2
+#  define map_bankwidth_is_large(map) (0)
+#  define map_words(map) (1)
+# endif
+#define map_bankwidth_is_2(map) (map_bankwidth(map) == 2)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 2
+#else
+#define map_bankwidth_is_2(map) (0)
+#endif
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
+# ifdef map_bankwidth
+#  undef map_bankwidth
+#  define map_bankwidth(map) ((map)->bankwidth)
+# else
+#  define map_bankwidth(map) 4
+#  define map_bankwidth_is_large(map) (0)
+#  define map_words(map) (1)
+# endif
+#define map_bankwidth_is_4(map) (map_bankwidth(map) == 4)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 4
+#else
+#define map_bankwidth_is_4(map) (0)
+#endif
+
+/* ensure we never evaluate anything shorted than an unsigned long
+ * to zero, and ensure we'll never miss the end of an comparison (bjd) */
+
+#define map_calc_words(map) ((map_bankwidth(map) + (sizeof(unsigned long)-1))/ sizeof(unsigned long))
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
+# ifdef map_bankwidth
+#  undef map_bankwidth
+#  define map_bankwidth(map) ((map)->bankwidth)
+#  if BITS_PER_LONG < 64
+#   undef map_bankwidth_is_large
+#   define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
+#   undef map_words
+#   define map_words(map) map_calc_words(map)
+#  endif
+# else
+#  define map_bankwidth(map) 8
+#  define map_bankwidth_is_large(map) (BITS_PER_LONG < 64)
+#  define map_words(map) map_calc_words(map)
+# endif
+#define map_bankwidth_is_8(map) (map_bankwidth(map) == 8)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 8
+#else
+#define map_bankwidth_is_8(map) (0)
+#endif
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
+# ifdef map_bankwidth
+#  undef map_bankwidth
+#  define map_bankwidth(map) ((map)->bankwidth)
+#  undef map_bankwidth_is_large
+#  define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
+#  undef map_words
+#  define map_words(map) map_calc_words(map)
+# else
+#  define map_bankwidth(map) 16
+#  define map_bankwidth_is_large(map) (1)
+#  define map_words(map) map_calc_words(map)
+# endif
+#define map_bankwidth_is_16(map) (map_bankwidth(map) == 16)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 16
+#else
+#define map_bankwidth_is_16(map) (0)
+#endif
+
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
+# ifdef map_bankwidth
+#  undef map_bankwidth
+#  define map_bankwidth(map) ((map)->bankwidth)
+#  undef map_bankwidth_is_large
+#  define map_bankwidth_is_large(map) (map_bankwidth(map) > BITS_PER_LONG/8)
+#  undef map_words
+#  define map_words(map) map_calc_words(map)
+# else
+#  define map_bankwidth(map) 32
+#  define map_bankwidth_is_large(map) (1)
+#  define map_words(map) map_calc_words(map)
+# endif
+#define map_bankwidth_is_32(map) (map_bankwidth(map) == 32)
+#undef MAX_MAP_BANKWIDTH
+#define MAX_MAP_BANKWIDTH 32
+#else
+#define map_bankwidth_is_32(map) (0)
+#endif
+
+#ifndef map_bankwidth
+#error "No bus width supported. What's the point?"
+#endif
+
+static inline int map_bankwidth_supported(int w)
+{
+	switch (w) {
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_1
+	case 1:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_2
+	case 2:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_4
+	case 4:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_8
+	case 8:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_16
+	case 16:
+#endif
+#ifdef CONFIG_MTD_MAP_BANK_WIDTH_32
+	case 32:
+#endif
+		return 1;
+
+	default:
+		return 0;
+	}
+}
+
+#define MAX_MAP_LONGS ( ((MAX_MAP_BANKWIDTH*8) + BITS_PER_LONG - 1) / BITS_PER_LONG )
+
+typedef union {
+	unsigned long x[MAX_MAP_LONGS];
+} map_word;
 
 /* The map stuff is very simple. You fill in your struct map_info with
    a handful of routines for accessing the device, making sure they handle
    paging etc. correctly if your device needs it. Then you pass it off
-   to a chip driver which deals with a mapped device - generally either
-   do_cfi_probe() or do_ram_probe(), either of which will return a 
-   struct mtd_info if they liked what they saw. At which point, you
-   fill in the mtd->module with your own module address, and register 
-   it.
+   to a chip probe routine -- either JEDEC or CFI probe or both -- via
+   do_map_probe(). If a chip is recognised, the probe code will invoke the
+   appropriate chip driver (if present) and return a struct mtd_info.
+   At which point, you fill in the mtd->module with your own module 
+   address, and register it with the MTD core code. Or you could partition
+   it and register the partitions instead, or keep it for your own private
+   use; whatever.
    
    The mtd->priv field will point to the struct map_info, and any further
    private data required by the chip driver is linked from the 
@@ -29,39 +182,45 @@
 struct map_info {
 	char *name;
 	unsigned long size;
-	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);  
-	/* If it returned a 'long' I'd call it readl.
-	 * It doesn't.
-	 * I won't.
-	 * dwmw2 */
+	unsigned long phys;
+#define NO_XIP (-1UL)
 	
+	void __iomem *virt;
+	void *cached;
+
+	int bankwidth; /* in octets. This isn't necessarily the width
+		       of actual bus cycles -- it's the repeat interval
+		      in bytes, before you are talking to the first chip again.
+		      */
+
+#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
+	map_word (*read)(struct map_info *, unsigned long);
 	void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t);
-	void (*write8)(struct map_info *, __u8, unsigned long);
-	void (*write16)(struct map_info *, __u16, unsigned long);
-	void (*write32)(struct map_info *, __u32, unsigned long);
-	void (*write64)(struct map_info *, __u64, unsigned long);
+
+	void (*write)(struct map_info *, const map_word, unsigned long);
 	void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t);
 
-	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
+	/* It's possible for the map driver to use cached memory in its
+	   copy_from implementation (and _only_ with copy_from).  However,
+	   when the chip driver knows some flash area has changed contents,
+	   it will signal it to the map driver through this routine to let
+	   the map driver invalidate the corresponding cache as needed.
+	   If there is no cache to care about this can be set to NULL. */
+	void (*inval_cache)(struct map_info *, unsigned long, ssize_t);
 
+	/* set_vpp() must handle being reentered -- enable, enable, disable 
+	   must leave it enabled. */
 	void (*set_vpp)(struct map_info *, int);
-	/* 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 +233,180 @@
 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)
 
-/*
- * 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)
+#define INVALIDATE_CACHED_RANGE(map, from, size) \
+	do { if(map->inval_cache) map->inval_cache(map, from, size); } while(0)
+
+
+static inline int map_word_equal(struct map_info *map, map_word val1, map_word val2)
+{
+	int i;
+	for (i=0; i<map_words(map); i++) {
+		if (val1.x[i] != val2.x[i])
+			return 0;
+	}
+	return 1;
+}
+
+static inline map_word map_word_and(struct map_info *map, map_word val1, map_word val2)
 {
-	struct map_info *map = mtd->priv;
+	map_word r;
+	int i;
 
-	if (map->fldrv->destroy)
-		map->fldrv->destroy(mtd);
-#ifdef CONFIG_MODULES
-	if (map->fldrv->module)
-		__MOD_DEC_USE_COUNT(map->fldrv->module);
+	for (i=0; i<map_words(map); i++) {
+		r.x[i] = val1.x[i] & val2.x[i];
+	}
+	return r;
+}
+
+static inline map_word map_word_or(struct map_info *map, map_word val1, map_word val2)
+{
+	map_word r;
+	int i;
+
+	for (i=0; i<map_words(map); i++) {
+		r.x[i] = val1.x[i] | val2.x[i];
+	}
+	return r;
+}
+#define map_word_andequal(m, a, b, z) map_word_equal(m, z, map_word_and(m, a, b))
+
+static inline int map_word_bitsset(struct map_info *map, map_word val1, map_word val2)
+{
+	int i;
+
+	for (i=0; i<map_words(map); i++) {
+		if (val1.x[i] & val2.x[i])
+			return 1;
+	}
+	return 0;
+}
+
+static inline map_word map_word_load(struct map_info *map, const void *ptr)
+{
+	map_word r;
+
+	if (map_bankwidth_is_1(map))
+		r.x[0] = *(unsigned char *)ptr;
+	else if (map_bankwidth_is_2(map))
+		r.x[0] = get_unaligned((uint16_t *)ptr);
+	else if (map_bankwidth_is_4(map))
+		r.x[0] = get_unaligned((uint32_t *)ptr);
+#if BITS_PER_LONG >= 64
+	else if (map_bankwidth_is_8(map))
+		r.x[0] = get_unaligned((uint64_t *)ptr);
 #endif
-	kfree(mtd);
+	else if (map_bankwidth_is_large(map))
+		memcpy(r.x, ptr, map->bankwidth);
+
+	return r;
 }
 
-#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 map_word map_word_load_partial(struct map_info *map, map_word orig, const unsigned char *buf, int start, int len)
+{
+	int i;
+
+	if (map_bankwidth_is_large(map)) {
+		char *dest = (char *)&orig;
+		memcpy(dest+start, buf, len);
+	} else {
+		for (i=start; i < start+len; i++) {
+			int bitpos;
+#ifdef __LITTLE_ENDIAN
+			bitpos = i*8;
+#else /* __BIG_ENDIAN */
+			bitpos = (map_bankwidth(map)-1-i)*8;
+#endif
+			orig.x[0] &= ~(0xff << bitpos);
+			orig.x[0] |= buf[i] << bitpos;
+		}
+	}
+	return orig;
+}
+
+static inline map_word map_word_ff(struct map_info *map)
+{
+	map_word r;
+	int i;
+
+	for (i=0; i<map_words(map); i++) {
+		r.x[i] = ~0UL;
+	}
+	return r;
+}
+static inline map_word inline_map_read(struct map_info *map, unsigned long ofs)
+{
+	map_word r;
+
+	if (map_bankwidth_is_1(map))
+		r.x[0] = __raw_readb(map->virt + ofs);
+	else if (map_bankwidth_is_2(map))
+		r.x[0] = __raw_readw(map->virt + ofs);
+	else if (map_bankwidth_is_4(map))
+		r.x[0] = __raw_readl(map->virt + ofs);
+#if BITS_PER_LONG >= 64
+	else if (map_bankwidth_is_8(map))
+		r.x[0] = __raw_readq(map->virt + ofs);
+#endif
+	else if (map_bankwidth_is_large(map))
+		memcpy_fromio(r.x, map->virt+ofs, map->bankwidth);
+
+	return r;
+}
+
+static inline void inline_map_write(struct map_info *map, const map_word datum, unsigned long ofs)
+{
+	if (map_bankwidth_is_1(map))
+		__raw_writeb(datum.x[0], map->virt + ofs);
+	else if (map_bankwidth_is_2(map))
+		__raw_writew(datum.x[0], map->virt + ofs);
+	else if (map_bankwidth_is_4(map))
+		__raw_writel(datum.x[0], map->virt + ofs);
+#if BITS_PER_LONG >= 64
+	else if (map_bankwidth_is_8(map))
+		__raw_writeq(datum.x[0], map->virt + ofs);
+#endif
+	else if (map_bankwidth_is_large(map))
+		memcpy_toio(map->virt+ofs, datum.x, map->bankwidth);
+	mb();
+}
+
+static inline void inline_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
+{
+	if (map->cached)
+		memcpy(to, (char *)map->cached + from, len);
+	else
+		memcpy_fromio(to, map->virt + from, len);
+}
+
+static inline void inline_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
+{
+	memcpy_toio(map->virt + to, from, len);
+}
+
+#ifdef CONFIG_MTD_COMPLEX_MAPPINGS
+#define map_read(map, ofs) (map)->read(map, ofs)
+#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len)
+#define map_write(map, datum, ofs) (map)->write(map, datum, ofs)
+#define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len)
+
+extern void simple_map_init(struct map_info *);
+#define map_is_linear(map) (map->phys != NO_XIP)
+
+#else
+#define map_read(map, ofs) inline_map_read(map, ofs)
+#define map_copy_from(map, to, from, len) inline_map_copy_from(map, to, from, len)
+#define map_write(map, datum, ofs) inline_map_write(map, datum, ofs)
+#define map_copy_to(map, to, from, len) inline_map_copy_to(map, to, from, len)
+
+
+#define simple_map_init(map) BUG_ON(!map_bankwidth_supported((map)->bankwidth))
+#define map_is_linear(map) (1)
+
+#endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */
 
 #endif /* __LINUX_MTD_MAP_H__ */
diff -uNrb linux-2.4.30/include/linux/mtd/mtd.h linux/include/linux/mtd/mtd.h
--- linux-2.4.30/include/linux/mtd/mtd.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/mtd/mtd.h	Fri Sep 10 15:15:04 2004
@@ -1,123 +1,45 @@
-
-/* $Id: mtd.h,v 1.38 2003/01/12 16:30:19 spse Exp $ */
+/* 
+ * $Id: mtd.h,v 1.56 2004/08/09 18:46:04 dmarlin 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 <linux/mtd/compatmac.h>
+#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
 #define MTD_ERASE_DONE          0x08
 #define MTD_ERASE_FAILED        0x10
 
+/* If the erase fails, fail_addr might indicate exactly which block failed.  If
+   fail_addr = 0xffffffff, the failure was not at the device level or was not
+   specific to any particular block. */
 struct erase_info {
 	struct mtd_info *mtd;
 	u_int32_t addr;
 	u_int32_t len;
+	u_int32_t fail_addr;
 	u_long time;
 	u_long retries;
 	u_int dev;
@@ -147,13 +69,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 +90,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 +102,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);
@@ -194,17 +120,17 @@
 	/* This function is not yet implemented */
 	int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
 
-	/* iovec-based read/write methods. We need these especially for NAND flash,
+	/* kvec-based read/write methods. We need these especially for NAND flash,
 	   with its limited number of write cycles per erase.
 	   NB: The 'count' parameter is the number of _vectors_, each of 
 	   which contains an (ofs, len) tuple.
 	*/
-	int (*readv) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, size_t *retlen);
-	int (*readv_ecc) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, 
-		size_t *retlen, u_char *eccbuf, int 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);
+	int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen);
+	int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, 
+		size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
+	int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
+	int (*writev_ecc) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, 
+		size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
 
 	/* Sync */
 	void (*sync) (struct mtd_info *mtd);
@@ -217,7 +143,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,44 +159,27 @@
 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;
 };
 
 
 extern void register_mtd_user (struct mtd_notifier *new);
 extern int unregister_mtd_user (struct mtd_notifier *old);
 
-int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs,
+int default_mtd_writev(struct mtd_info *mtd, const struct kvec *vecs,
 		       unsigned long count, loff_t to, size_t *retlen);
 
-int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs,
+int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs,
 		      unsigned long count, loff_t from, size_t *retlen);
 
-#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 +192,17 @@
 #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 */
+
+
+#ifdef CONFIG_MTD_PARTITIONS
+void mtd_erase_callback(struct erase_info *instr);
+#else
+static inline void mtd_erase_callback(struct erase_info *instr)
+{
+	if (instr->callback)
+		instr->callback(instr);
+}
+#endif
 
 /*
  * Debugging macro and defines
@@ -293,9 +219,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 -uNrb linux-2.4.30/include/linux/mtd/nand.h linux/include/linux/mtd/nand.h
--- linux-2.4.30/include/linux/mtd/nand.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/mtd/nand.h	Tue Sep 28 14:37:46 2004
@@ -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.64 2004/09/16 23:26:08 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,27 +44,57 @@
  *			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
+ *  05-25-2004 tglx 	added bad block table support, ST-MICRO manufacturer id
+ *			update of nand_chip structure description
  */
 #ifndef __LINUX_MTD_NAND_H
 #define __LINUX_MTD_NAND_H
 
 #include <linux/config.h>
-#include <linux/sched.h>
-
-/*
- * Searches for a NAND device
+#include <linux/wait.h>
+#include <linux/spinlock.h>
+#include <linux/mtd/mtd.h>
+
+struct mtd_info;
+/* Scan and identify a NAND device */
+extern int nand_scan (struct mtd_info *mtd, int max_chips);
+/* Free resources held by the NAND device */
+extern void nand_release (struct mtd_info *mtd);
+
+/* Read raw data from the device without ECC */
+extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen);
+
+
+/* The maximum number of NAND chips in an array */
+#define NAND_MAX_CHIPS		8
+
+/* This constant declares the max. oobsize / page, which
+ * is supported now. If you add a chip with bigger oobsize/page
+ * adjust this accordingly.
  */
-extern int nand_scan (struct mtd_info *mtd);
+#define NAND_MAX_OOBSIZE	64
 
 /*
  * Constants for hardware specific CLE/ALE/NCE function
 */
+/* Select the chip by setting nCE to low */
 #define NAND_CTL_SETNCE 	1
+/* Deselect the chip by setting nCE to high */
 #define NAND_CTL_CLRNCE		2
+/* Select the command latch by setting CLE to high */
 #define NAND_CTL_SETCLE		3
+/* Deselect the command latch by setting CLE to low */
 #define NAND_CTL_CLRCLE		4
+/* Select the address latch by setting ALE to high */
 #define NAND_CTL_SETALE		5
+/* Deselect the address latch by setting ALE to low */
 #define NAND_CTL_CLRALE		6
+/* Set write protection by setting WP to high. Not used! */
+#define NAND_CTL_SETWP		7
+/* Clear write protection by setting WP to low. Not used! */
+#define NAND_CTL_CLRWP		8
 
 /*
  * Standard NAND flash commands
@@ -75,35 +105,102 @@
 #define NAND_CMD_READOOB	0x50
 #define NAND_CMD_ERASE1		0x60
 #define NAND_CMD_STATUS		0x70
+#define NAND_CMD_STATUS_MULTI	0x71
 #define NAND_CMD_SEQIN		0x80
 #define NAND_CMD_READID		0x90
 #define NAND_CMD_ERASE2		0xd0
 #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
- *
- * NONE:	No ECC
- * SOFT:	Software ECC 3 byte ECC per 256 Byte data
- * HW3_256:	Hardware ECC 3 byte ECC per 256 Byte data
- * HW3_512:	Hardware ECC 3 byte ECC per 512 Byte data
- *
- *
-*/
+ */
+
+/* No ECC. Usage is not recommended ! */
 #define NAND_ECC_NONE		0
+/* Software ECC 3 byte ECC per 256 Byte data */
 #define NAND_ECC_SOFT		1
+/* Hardware ECC 3 byte ECC per 256 Byte data */
 #define NAND_ECC_HW3_256	2
+/* Hardware ECC 3 byte ECC per 512 Byte data */
 #define NAND_ECC_HW3_512	3
+/* Hardware ECC 3 byte ECC per 512 Byte data */
 #define NAND_ECC_HW6_512	4
-#define NAND_ECC_DISKONCHIP	5
+/* Hardware ECC 8 byte ECC per 512 Byte data */
+#define NAND_ECC_HW8_512	6
 
 /*
  * Constants for Hardware ECC
 */
+/* Reset Hardware ECC for read */
 #define NAND_ECC_READ		0
+/* Reset Hardware ECC for write */
 #define NAND_ECC_WRITE		1
+/* Enable Hardware ECC before syndrom is read back from flash */
+#define NAND_ECC_READSYN	2
+
+/* 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
+/* AND Chip which has 4 banks and a confusing page / block 
+ * assignment. See Renesas datasheet for further information */
+#define NAND_IS_AND		0x00000020
+/* Chip has a array of 4 pages which can be read without
+ * additional ready /busy waits */
+#define NAND_4PAGE_ARRAY	0x00000040 
+
+/* 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))
+
+/* Mask to zero out the chip options, which come from the id table */
+#define NAND_CHIPOPTIONS_MSK	(0x0000ffff & ~NAND_NO_AUTOINCR)
+
+/* Non chip related options */
+/* Use a flash based bad block table. This option is passed to the
+ * default bad block table function. */
+#define NAND_USE_FLASH_BBT	0x00010000
+/* The hw ecc generator provides a syndrome instead a ecc value on read 
+ * This can only work if we have the ecc bytes directly behind the 
+ * data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */
+#define NAND_HWECC_SYNDROME	0x00020000
+
+
+/* Options set by nand scan */
+/* Nand scan has allocated oob_buf */
+#define NAND_OOBBUF_ALLOC	0x40000000
+/* Nand scan has allocated data_buf */
+#define NAND_DATABUF_ALLOC	0x80000000
+
 	
 /*
+ * nand_state_t - chip states
  * Enumeration for NAND flash chip state
  */
 typedef enum {
@@ -111,73 +208,116 @@
 	FL_READING,
 	FL_WRITING,
 	FL_ERASING,
-	FL_SYNCING
+	FL_SYNCING,
+	FL_CACHEDPRG,
 } nand_state_t;
 
 
-/*
- * NAND Private Flash Chip Data
- *
- * Structure overview:
- *
- *  IO_ADDR_R - address to read the 8 I/O lines of the flash device 
- *
- *  IO_ADDR_W - address to write the 8 I/O lines of the flash device 
- *
- *  hwcontrol - hardwarespecific function for accesing control-lines
- *
- *  dev_ready - hardwarespecific function for accesing device ready/busy line
- *
- *  waitfunc - hardwarespecific function for wait on ready
- *
- *  calculate_ecc - function for ecc calculation or readback from ecc hardware
- *
- *  correct_data - function for ecc correction, matching to ecc generator (sw/hw)
- *
- *  enable_hwecc - function to enable (reset) hardware ecc generator
- *
- *  eccmod - mode of ecc: see constants
- *
- *  eccsize - databytes used per ecc-calculation
- *
- *  chip_delay - chip dependent delay for transfering data from array to read regs (tR)
- *
- *  chip_lock - spinlock used to protect access to this structure
- *
- *  wq - wait queue to sleep on if a NAND operation is in progress
- *
- *  state - give the current state of the NAND device
- *
- *  page_shift - number of address bits in a page (column address bits)
- *
- *  data_buf - data buffer passed to/from MTD user modules
- *
- *  data_cache - data cache for redundant page access and shadow for
- *		 ECC failure
- *
- *  cache_page - number of last valid page in page_cache 
+/**
+ * struct nand_chip - NAND Private Flash Chip Data
+ * @IO_ADDR_R:		[BOARDSPECIFIC] address to read the 8 I/O lines of the flash device 
+ * @IO_ADDR_W:		[BOARDSPECIFIC] address to write the 8 I/O lines of the flash device 
+ * @read_byte:		[REPLACEABLE] read one byte from the chip
+ * @write_byte:		[REPLACEABLE] write one byte to the chip
+ * @read_word:		[REPLACEABLE] read one word from the chip
+ * @write_word:		[REPLACEABLE] write one word to the chip
+ * @write_buf:		[REPLACEABLE] write data from the buffer to the chip
+ * @read_buf:		[REPLACEABLE] read data from the chip into the buffer
+ * @verify_buf:		[REPLACEABLE] verify buffer contents against the chip data
+ * @select_chip:	[REPLACEABLE] select chip nr
+ * @block_bad:		[REPLACEABLE] check, if the block is bad
+ * @block_markbad:	[REPLACEABLE] mark the block bad
+ * @hwcontrol:		[BOARDSPECIFIC] hardwarespecific function for accesing control-lines
+ * @dev_ready:		[BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
+ *			If set to NULL no access to ready/busy is available and the ready/busy information
+ *			is read from the chip status register
+ * @cmdfunc:		[REPLACEABLE] hardwarespecific function for writing commands to the chip
+ * @waitfunc:		[REPLACEABLE] hardwarespecific function for wait on ready
+ * @calculate_ecc: 	[REPLACEABLE] function for ecc calculation or readback from ecc hardware
+ * @correct_data:	[REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw)
+ * @enable_hwecc:	[BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only
+ *			be provided if a hardware ECC is available
+ * @erase_cmd:		[INTERN] erase command write function, selectable due to AND support
+ * @scan_bbt:		[REPLACEABLE] function to scan bad block table
+ * @eccmode:		[BOARDSPECIFIC] mode of ecc, see defines 
+ * @eccsize: 		[INTERN] databytes used per ecc-calculation
+ * @eccsteps:		[INTERN] number of ecc calculation steps per page
+ * @chip_delay:		[BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
+ * @chip_lock:		[INTERN] spinlock used to protect access to this structure and the chip
+ * @wq:			[INTERN] wait queue to sleep on if a NAND operation is in progress
+ * @state: 		[INTERN] the current state of the NAND device
+ * @page_shift:		[INTERN] number of address bits in a page (column address bits)
+ * @phys_erase_shift:	[INTERN] number of address bits in a physical eraseblock
+ * @bbt_erase_shift:	[INTERN] number of address bits in a bbt entry
+ * @chip_shift:		[INTERN] number of address bits in one chip
+ * @data_buf:		[INTERN] internal buffer for one page + oob 
+ * @oob_buf:		[INTERN] oob buffer for one eraseblock
+ * @oobdirty:		[INTERN] indicates that oob_buf must be reinitialized
+ * @data_poi:		[INTERN] pointer to a data buffer
+ * @options:		[BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
+ *			special functionality. See the defines for further explanation
+ * @badblockpos:	[INTERN] position of the bad block marker in the oob area
+ * @numchips:		[INTERN] number of physical chips
+ * @chipsize:		[INTERN] the size of one chip for multichip arrays
+ * @pagemask:		[INTERN] page number mask = number of (pages / chip) - 1
+ * @pagebuf:		[INTERN] holds the pagenumber which is currently in data_buf
+ * @autooob:		[REPLACEABLE] the default (auto)placement scheme
+ * @bbt:		[INTERN] bad block table pointer
+ * @bbt_td:		[REPLACEABLE] bad block table descriptor for flash lookup
+ * @bbt_md:		[REPLACEABLE] bad block table mirror descriptor
+ * @priv:		[OPTIONAL] pointer to private chip date
  */
+ 
 struct nand_chip {
-	unsigned long 	IO_ADDR_R;
-	unsigned long 	IO_ADDR_W;
-	void 		(*hwcontrol)(int cmd);
-	int  		(*dev_ready)(void);
+	void  __iomem	*IO_ADDR_R;
+	void  __iomem 	*IO_ADDR_W;
+	
+	u_char		(*read_byte)(struct mtd_info *mtd);
+	void		(*write_byte)(struct mtd_info *mtd, u_char byte);
+	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);
+	int		(*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
+	int 		(*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
+	void		(*enable_hwecc)(struct mtd_info *mtd, int mode);
+	void		(*erase_cmd)(struct mtd_info *mtd, int page);
+	int		(*scan_bbt)(struct mtd_info *mtd);
 	int		eccmode;
 	int		eccsize;
+	int		eccsteps;
 	int 		chip_delay;
 	spinlock_t 	chip_lock;
 	wait_queue_head_t wq;
 	nand_state_t 	state;
 	int 		page_shift;
+	int		phys_erase_shift;
+	int		bbt_erase_shift;
+	int		chip_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;
+	uint8_t		*bbt;
+	struct nand_bbt_descr	*bbt_td;
+	struct nand_bbt_descr	*bbt_md;
+	void		*priv;
 };
 
 /*
@@ -187,46 +327,35 @@
 #define NAND_MFR_SAMSUNG	0xec
 #define NAND_MFR_FUJITSU	0x04
 #define NAND_MFR_NATIONAL	0x8f
+#define NAND_MFR_RENESAS	0x07
+#define NAND_MFR_STMICRO	0x20
 
-/*
- * NAND Flash Device ID Structure
- *
- * Structure overview:
- *
- *  name - Identify the device type
- *
- *  id -  device ID code
+/**
+ * struct nand_flash_dev - NAND Flash Device ID Structure
  *
- *  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 - NAND Flash Manufacturer ID Structure
+ * @name:	Manufacturer name
+ * @id: 	manufacturer ID code of device.
 */
 struct nand_manufacturers {
 	int id;
@@ -236,39 +365,85 @@
 extern struct nand_flash_dev nand_flash_ids[];
 extern struct nand_manufacturers nand_manuf_ids[];
 
+/** 
+ * struct nand_bbt_descr - bad block table descriptor
+ * @options:	options for this descriptor
+ * @pages:	the page(s) where we find the bbt, used with option BBT_ABSPAGE
+ *		when bbt is searched, then we store the found bbts pages here.
+ *		Its an array and supports up to 8 chips now
+ * @offs:	offset of the pattern in the oob area of the page
+ * @veroffs:	offset of the bbt version counter in the oob are of the page
+ * @version:	version read from the bbt page during scan
+ * @len:	length of the pattern, if 0 no pattern check is performed
+ * @maxblocks:	maximum number of blocks to search for a bbt. This number of
+ *		blocks is reserved at the end of the device where the tables are 
+ *		written.
+ * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than
+ *              bad) block in the stored bbt
+ * @pattern:	pattern to identify bad block table or factory marked good / 
+ *		bad blocks, can be NULL, if len = 0
+ *
+ * Descriptor for the bad block table marker and the descriptor for the 
+ * pattern which identifies good and bad blocks. The assumption is made
+ * that the pattern and the version count are always located in the oob area
+ * of the first block.
+ */
+struct nand_bbt_descr {
+	int	options;
+	int	pages[NAND_MAX_CHIPS];
+	int	offs;
+	int	veroffs;
+	uint8_t	version[NAND_MAX_CHIPS];
+	int	len;
+	int 	maxblocks;
+	int	reserved_block_code;
+	uint8_t	*pattern;
+};
+
+/* Options for the bad block table descriptors */
+
+/* The number of bits used per block in the bbt on the device */
+#define NAND_BBT_NRBITS_MSK	0x0000000F
+#define NAND_BBT_1BIT		0x00000001
+#define NAND_BBT_2BIT		0x00000002
+#define NAND_BBT_4BIT		0x00000004
+#define NAND_BBT_8BIT		0x00000008
+/* The bad block table is in the last good block of the device */
+#define	NAND_BBT_LASTBLOCK	0x00000010
+/* The bbt is at the given page, else we must scan for the bbt */
+#define NAND_BBT_ABSPAGE	0x00000020
+/* The bbt is at the given page, else we must scan for the bbt */
+#define NAND_BBT_SEARCH		0x00000040
+/* bbt is stored per chip on multichip devices */
+#define NAND_BBT_PERCHIP	0x00000080
+/* bbt has a version counter at offset veroffs */
+#define NAND_BBT_VERSION	0x00000100
+/* Create a bbt if none axists */
+#define NAND_BBT_CREATE		0x00000200
+/* Search good / bad pattern through all pages of a block */
+#define NAND_BBT_SCANALLPAGES	0x00000400
+/* Scan block empty during good / bad block scan */
+#define NAND_BBT_SCANEMPTY	0x00000800
+/* Write bbt if neccecary */
+#define NAND_BBT_WRITE		0x00001000
+/* Read and write back block contents when writing bbt */
+#define NAND_BBT_SAVECONTENT	0x00002000
+/* Search good / bad pattern on the first and the second page */
+#define NAND_BBT_SCAN2NDPAGE	0x00004000
+
+/* The maximum number of blocks to scan for a bbt */
+#define NAND_BBT_SCAN_MAXBLOCKS	4
+
+extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd);
+extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs);
+extern int nand_default_bbt (struct mtd_info *mtd);
+extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt);
+extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt);
+
 /*
 * 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 -uNrb linux-2.4.30/include/linux/mtd/nand_ecc.h linux/include/linux/mtd/nand_ecc.h
--- linux-2.4.30/include/linux/mtd/nand_ecc.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/mtd/nand_ecc.h	Fri Sep 10 15:15:04 2004
@@ -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.4 2004/06/17 02:35:02 dbrown 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);
+int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
 
 /*
  * Detect and correct a 1 bit error for 256 byte block
  */
-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 -uNrb linux-2.4.30/include/linux/mtd/nftl.h linux/include/linux/mtd/nftl.h
--- linux-2.4.30/include/linux/mtd/nftl.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/mtd/nftl.h	Fri Sep 10 15:15:04 2004
@@ -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.16 2004/06/30 14:49:00 dbrown 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,13 +31,13 @@
 	__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 */
         unsigned int nb_blocks;		/* number of physical blocks */
         unsigned int nb_boot_blocks;	/* number of blocks used by the bios */
         struct erase_info instr;
+	struct nand_oobinfo oobinfo;
 };
 
 int NFTL_mount(struct NFTLrecord *s);
@@ -114,9 +48,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 -uNrb linux-2.4.30/include/linux/mtd/partitions.h linux/include/linux/mtd/partitions.h
--- linux-2.4.30/include/linux/mtd/partitions.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/mtd/partitions.h	Fri Sep 10 15:15:04 2004
@@ -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
@@ -41,6 +41,7 @@
 	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 nand_oobinfo *oobsel;	/* out of band layout for this partition (NAND only)*/
 	struct mtd_info **mtdp;	/* pointer to store the MTD object */
 };
 
@@ -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 -uNrb linux-2.4.30/include/linux/mtd/physmap.h linux/include/linux/mtd/physmap.h
--- linux-2.4.30/include/linux/mtd/physmap.h	Thu Jan  1 03:00:00 1970
+++ linux/include/linux/mtd/physmap.h	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,61 @@
+/*
+ * For boards with physically mapped flash and using 
+ * drivers/mtd/maps/physmap.c mapping driver.
+ *
+ * $Id: physmap.h,v 1.3 2004/07/21 00:16:15 jwboyer Exp $
+ *
+ * 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 bankwidth, void (*set_vpp)(struct map_info *, int) )
+{
+	physmap_map.phys = addr;
+	physmap_map.size = size;
+	physmap_map.bankwidth = bankwidth;
+	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 -uNrb linux-2.4.30/include/linux/sockios.h linux/include/linux/sockios.h
--- linux-2.4.30/include/linux/sockios.h	Fri Nov 19 12:32:15 2004
+++ linux/include/linux/sockios.h	Fri May  6 14:14:40 2005
@@ -65,6 +65,7 @@
 #define SIOCDIFADDR	0x8936		/* delete PA address		*/
 #define	SIOCSIFHWBROADCAST	0x8937	/* set hardware broadcast addr	*/
 #define SIOCGIFCOUNT	0x8938		/* get number of devices */
+#define SIOCSPROXYARP	0x8939		/* set device proxy-arp setting */
 
 #define SIOCGIFBR	0x8940		/* Bridging support		*/
 #define SIOCSIFBR	0x8941		/* Set bridging options 	*/
@@ -116,6 +117,9 @@
 #define SIOCBONDINFOQUERY      0x8994	/* rtn info about bond state    */
 #define SIOCBONDCHANGEACTIVE   0x8995   /* update to a new active slave */
 			
+#define SIOCSDEVID      0x899E          /* set device id */
+#define SIOCGDEVID      0x899F          /* get device id */
+
 /* Device private ioctl calls */
 
 /*
diff -uNrb linux-2.4.30/include/linux/suspend.h linux/include/linux/suspend.h
--- linux-2.4.30/include/linux/suspend.h	Thu Jan  1 03:00:00 1970
+++ linux/include/linux/suspend.h	Mon Sep 13 17:46:41 2004
@@ -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 -uNrb linux-2.4.30/include/linux/workqueue.h linux/include/linux/workqueue.h
--- linux-2.4.30/include/linux/workqueue.h	Thu Jan  1 03:00:00 1970
+++ linux/include/linux/workqueue.h	Mon Sep 13 17:46:41 2004
@@ -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 -uNrb linux-2.4.30/include/mtd/inftl-user.h linux/include/mtd/inftl-user.h
--- linux-2.4.30/include/mtd/inftl-user.h	Thu Jan  1 03:00:00 1970
+++ linux/include/mtd/inftl-user.h	Fri Sep 10 15:15:04 2004
@@ -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 -uNrb linux-2.4.30/include/mtd/jffs2-user.h linux/include/mtd/jffs2-user.h
--- linux-2.4.30/include/mtd/jffs2-user.h	Thu Jan  1 03:00:00 1970
+++ linux/include/mtd/jffs2-user.h	Fri Sep 10 15:15:04 2004
@@ -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 -uNrb linux-2.4.30/include/mtd/mtd-abi.h linux/include/mtd/mtd-abi.h
--- linux-2.4.30/include/mtd/mtd-abi.h	Thu Jan  1 03:00:00 1970
+++ linux/include/mtd/mtd-abi.h	Fri Sep 10 15:15:04 2004
@@ -0,0 +1,102 @@
+/*
+ * $Id: mtd-abi.h,v 1.6 2004/08/09 13:38:30 dwmw2 Exp $
+ *
+ * Portions of MTD ABI definition which are shared by kernel and user space 
+ */
+
+#ifndef __MTD_ABI_H__
+#define __MTD_ABI_H__
+
+#ifndef __KERNEL__ /* Urgh. The whole point of splitting this out into
+		    separate files was to avoid #ifdef __KERNEL__ */
+#define __user
+#endif
+
+struct erase_info_user {
+	uint32_t start;
+	uint32_t length;
+};
+
+struct mtd_oob_buf {
+	uint32_t start;
+	uint32_t length;
+	unsigned char __user *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 (YAFFS1 legacy mode)
+#define MTD_NANDECC_AUTOPLACE	2	// Use the default placement scheme
+#define MTD_NANDECC_PLACEONLY	3	// Use the given placement in the structure (Do not store ecc result on read)
+
+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)
+#define MEMGETOOBSEL		_IOR('M', 10, struct nand_oobinfo)
+#define MEMGETBADBLOCK		_IOW('M', 11, loff_t)
+#define MEMSETBADBLOCK		_IOW('M', 12, loff_t)
+
+struct nand_oobinfo {
+	uint32_t useecc;
+	uint32_t eccbytes;
+	uint32_t oobfree[8][2];
+	uint32_t eccpos[32];
+};
+
+#endif /* __MTD_ABI_H__ */
diff -uNrb linux-2.4.30/include/mtd/mtd-user.h linux/include/mtd/mtd-user.h
--- linux-2.4.30/include/mtd/mtd-user.h	Thu Jan  1 03:00:00 1970
+++ linux/include/mtd/mtd-user.h	Fri Sep 10 15:15:04 2004
@@ -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 -uNrb linux-2.4.30/include/mtd/nftl-user.h linux/include/mtd/nftl-user.h
--- linux-2.4.30/include/mtd/nftl-user.h	Thu Jan  1 03:00:00 1970
+++ linux/include/mtd/nftl-user.h	Fri Sep 10 15:15:04 2004
@@ -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 -uNrb linux-2.4.30/init/do_mounts.c linux/init/do_mounts.c
--- linux-2.4.30/init/do_mounts.c	Fri Nov 19 12:32:15 2004
+++ linux/init/do_mounts.c	Tue Feb  1 16:45:22 2005
@@ -255,6 +255,7 @@
 	{ "ftld", 0x2c18 },
 	{ "mtdblock", 0x1f00 },
 	{ "nb", 0x2b00 },
+	{ "cfa", 0x0d00 },
 	{ NULL, 0 }
 };
 
diff -uNrb linux-2.4.30/kernel/acct.c linux/kernel/acct.c
--- linux-2.4.30/kernel/acct.c	Fri Nov 19 12:32:15 2004
+++ linux/kernel/acct.c	Tue Sep  7 16:21:36 2004
@@ -173,10 +173,10 @@
 			error = PTR_ERR(file);
 			goto out;
 		}
-		error = -EACCES;
-		if (!S_ISREG(file->f_dentry->d_inode->i_mode)) 
-			goto out_err;
-
+/*		error = -EACCES;
+ *		if (!S_ISREG(file->f_dentry->d_inode->i_mode)) 
+ *			goto out_err;
+ */
 		error = -EIO;
 		if (!file->f_op->write) 
 			goto out_err;
diff -uNrb linux-2.4.30/kernel/panic.c linux/kernel/panic.c
--- linux-2.4.30/kernel/panic.c	Fri Nov 19 12:46:58 2004
+++ linux/kernel/panic.c	Fri Nov 19 13:44:03 2004
@@ -61,13 +61,14 @@
 	va_start(args, fmt);
 	vsnprintf(buf, sizeof(buf), fmt, args);
 	va_end(args);
-	printk(KERN_EMERG "Kernel panic: %s\n",buf);
+	printb(KERN_EMERG "Kernel panic: %s\n",buf);
 	if (in_interrupt())
-		printk(KERN_EMERG "In interrupt handler - not syncing\n");
+		printb(KERN_EMERG "In interrupt handler - not syncing\n");
 	else if (!current->pid)
-		printk(KERN_EMERG "In idle task - not syncing\n");
+		printb(KERN_EMERG "In idle task - not syncing\n");
 	else
 		sys_sync();
+
 	bust_spinlocks(0);
 
 #ifdef CONFIG_SMP
diff -uNrb linux-2.4.30/kernel/printk.c linux/kernel/printk.c
--- linux-2.4.30/kernel/printk.c	Fri Nov 19 12:46:58 2004
+++ linux/kernel/printk.c	Mon Feb 14 18:17:17 2005
@@ -54,7 +54,7 @@
 
 /* We show everything that is MORE important than this.. */
 #define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */
-#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */
+#define DEFAULT_CONSOLE_LOGLEVEL 1 /* anything MORE serious than KERN_DEBUG */
 
 DECLARE_WAIT_QUEUE_HEAD(log_wait);
 
@@ -395,6 +395,25 @@
 	if (logged_chars < LOG_BUF_LEN)
 		logged_chars++;
 }
+
+int (*printb)(const char *format, ...) = &printk;
+
+void register_printb(int (*function)(const char *format, ...)) 
+{
+    printb = function;
+}
+
+void flush_dummy(void) { }
+
+void (*flushb)(void) = &flush_dummy;
+
+void register_flushb(void (*function)(void)) {
+    flushb = function;
+}
+
+EXPORT_SYMBOL (register_printb);
+EXPORT_SYMBOL (register_flushb);
+EXPORT_SYMBOL (flush_dummy);
 
 /*
  * This is printk.  It can be called from any context.  We want it to work.
diff -uNrb linux-2.4.30/kernel/signal.c linux/kernel/signal.c
--- linux-2.4.30/kernel/signal.c	Fri Nov 19 12:32:15 2004
+++ linux/kernel/signal.c	Tue Sep 14 13:56:46 2004
@@ -778,8 +778,13 @@
 	info.si_uid = tsk->uid;
 
 	/* FIXME: find out whether or not this is supposed to be c*time. */
+#ifdef mips
+	info.si_utime = hz_to_std(tsk->times.tms_utime);
+	info.si_stime = hz_to_std(tsk->times.tms_stime);
+#else
 	info.si_utime = tsk->times.tms_utime;
 	info.si_stime = tsk->times.tms_stime;
+#endif
 
 	status = tsk->exit_code & 0x7f;
 	why = SI_KERNEL;	/* shouldn't happen */
diff -uNrb linux-2.4.30/kernel/sys.c linux/kernel/sys.c
--- linux-2.4.30/kernel/sys.c	Fri Nov 19 12:32:15 2004
+++ linux/kernel/sys.c	Tue Sep 14 13:56:46 2004
@@ -807,10 +807,23 @@
 	 *	atomically safe type this is just fine. Conceptually its
 	 *	as if the syscall took an instant longer to occur.
 	 */
+#ifdef mips
+	struct tms temp;
+	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 hz_to_std(jiffies);
+#else
 	if (tbuf)
 		if (copy_to_user(tbuf, &current->times, sizeof(struct tms)))
 			return -EFAULT;
 	return jiffies;
+#endif
 }
 
 /*
diff -uNrb linux-2.4.30/kernel/sysctl.c linux/kernel/sysctl.c
--- linux-2.4.30/kernel/sysctl.c	Mon Feb 14 11:26:06 2005
+++ linux/kernel/sysctl.c	Tue May  3 15:14:57 2005
@@ -55,6 +55,7 @@
 extern int cad_pid;
 extern int laptop_mode;
 extern int block_dump;
+extern unsigned rootDevice[2];
 
 /* this is needed for the proc_dointvec_minmax for [fs_]overflow UID and GID */
 static int maxolduid = 65535;
@@ -79,6 +80,10 @@
 extern int sem_ctls[];
 #endif
 
+#ifdef CONFIG_MIKROTIK_RB500
+extern unsigned soft_reboot;
+#endif
+
 extern int exception_trace;
 
 #ifdef __sparc__
@@ -213,6 +218,10 @@
 #endif
 	{KERN_CTLALTDEL, "ctrl-alt-del", &C_A_D, sizeof(int),
 	 0644, NULL, &proc_dointvec},
+#ifdef CONFIG_MIKROTIK_RB500
+	{KERN_SOFT_REBOOT, "soft_reboot", &soft_reboot, sizeof(int),
+	 0644, NULL, &proc_dointvec},
+#endif
 	{KERN_PRINTK, "printk", &console_loglevel, 4*sizeof(int),
 	 0644, NULL, &proc_dointvec},
 #ifdef CONFIG_KMOD
@@ -346,6 +355,8 @@
 	{FS_DIR_NOTIFY, "dir-notify-enable", &dir_notify_enable,
 	 sizeof(int), 0644, NULL, &proc_dointvec},
 	{FS_LEASE_TIME, "lease-break-time", &lease_break_time, sizeof(int),
+	 0644, NULL, &proc_dointvec},
+	{FS_ROOTDEV, "rootdev", &rootDevice, sizeof(unsigned) * 2,
 	 0644, NULL, &proc_dointvec},
 	{0}
 };
diff -uNrb linux-2.4.30/lib/Config.in linux/lib/Config.in
--- linux-2.4.30/lib/Config.in	Fri Nov 19 12:32:15 2004
+++ linux/lib/Config.in	Tue Sep 28 14:53:53 2004
@@ -41,6 +41,20 @@
   fi
 fi
 
+if [ "$CONFIG_MTD_DOCPROBE" = "y" -o \
+     "$CONFIG_MTD_NAND_RTC_FROM4" = "y" -o \
+     "$MTD_NAND_DISKONCHIP" = "y" ]; then
+   define_tristate CONFIG_REED_SOLOMON y
+else
+  if [ "$CONFIG_MTD_DOCPROBE" = "m" -o \
+     "$CONFIG_MTD_NAND_RTC_FROM4" = "m" -o \
+     "$MTD_NAND_DISKONCHIP" = "m" ]; then
+     define_tristate CONFIG_REED_SOLOMON m
+  else
+     define_tristate CONFIG_REED_SOLOMON n
+  fi
+fi
+
 if [ "$CONFIG_EXPERIMENTAL" = "y" -a \
      "$CONFIG_HOTPLUG" = "y" ]; then
    tristate 'Hotplug firmware loading support (EXPERIMENTAL)' CONFIG_FW_LOADER
diff -uNrb linux-2.4.30/lib/Makefile linux/lib/Makefile
--- linux-2.4.30/lib/Makefile	Fri Nov 19 12:32:15 2004
+++ linux/lib/Makefile	Tue Sep 28 14:53:53 2004
@@ -26,7 +26,11 @@
 
 subdir-$(CONFIG_ZLIB_INFLATE) += zlib_inflate
 subdir-$(CONFIG_ZLIB_DEFLATE) += zlib_deflate
+subdir-$(CONFIG_REED_SOLOMON) += reed_solomon
 
+ifeq ($(ARCH),mips)
+-include $(TOPDIR)/arch/$(ARCH)/Makefile.lib
+endif
 include $(TOPDIR)/drivers/net/Makefile.lib
 include $(TOPDIR)/drivers/usb/Makefile.lib
 include $(TOPDIR)/drivers/bluetooth/Makefile.lib
diff -uNrb linux-2.4.30/lib/reed_solomon/Makefile linux/lib/reed_solomon/Makefile
--- linux-2.4.30/lib/reed_solomon/Makefile	Thu Jan  1 03:00:00 1970
+++ linux/lib/reed_solomon/Makefile	Tue Sep 28 15:01:52 2004
@@ -0,0 +1,12 @@
+#
+# This is a modified version of reed solomon lib, 
+#
+
+O_TARGET:= reed_solomon.o	
+
+export-objs := rslib.o
+
+obj-y := rslib.o
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff -uNrb linux-2.4.30/lib/reed_solomon/decode_rs.c linux/lib/reed_solomon/decode_rs.c
--- linux-2.4.30/lib/reed_solomon/decode_rs.c	Thu Jan  1 03:00:00 1970
+++ linux/lib/reed_solomon/decode_rs.c	Tue Sep 28 15:01:52 2004
@@ -0,0 +1,228 @@
+/* 
+ * lib/reed_solomon/decode_rs.c
+ *
+ * Overview:
+ *   Generic Reed Solomon encoder / decoder library
+ *   
+ * Copyright 2002, Phil Karn, KA9Q
+ * May be used under the terms of the GNU General Public License (GPL)
+ *
+ * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de)
+ *
+ * $Id: decode_rs.c,v 1.3 2004/09/20 13:58:31 gleixner Exp $
+ *
+ */
+
+/* Generic data witdh independend code which is included by the 
+ * wrappers.
+ */
+{ 
+	int deg_lambda, el, deg_omega;
+	int i, j, r, k, PAD;
+	uint16_t u, q, tmp, num1, num2, den, discr_r, syn_error;
+	/* Err+Eras Locator poly and syndrome poly */
+	uint16_t lambda[NROOTS + 1], syn[NROOTS];	
+	uint16_t b[NROOTS + 1], t[NROOTS + 1], omega[NROOTS + 1];
+	uint16_t root[NROOTS], reg[NROOTS + 1], loc[NROOTS];
+	int count = 0;
+	uint16_t msk = (uint16_t) rs->nn;
+
+	/* Check length parameter for validity */
+	PAD = NN - NROOTS - len;
+	if (PAD < 0 || PAD >= NN)
+		return -ERANGE;
+		
+	/* Deos the caller provide the syndrome ? */
+	if (s != NULL) 
+		goto decode;
+
+	/* form the syndromes; i.e., evaluate data(x) at roots of g(x) */
+	for (i = 0; i < NROOTS; i++)
+		syn[i] = (((uint16_t) data[0]) ^ invmsk) & msk;
+
+	for (j = 1; j < len; j++) {
+		for (i = 0; i < NROOTS; i++) {
+			if (syn[i] == 0) {
+				syn[i] = (((uint16_t) data[j]) ^ invmsk) & msk;
+			} else {
+				syn[i] = ((((uint16_t) data[j]) ^ invmsk) & msk) ^ ALPHA_TO[MODNN(INDEX_OF[syn[i]] + (FCR+i)*PRIM)];
+			}
+		}
+	}
+
+	for (j = 0; j < NROOTS; j++) {
+		for (i = 0; i < NROOTS; i++) {
+			if (syn[i] == 0) {
+				syn[i] = ((uint16_t) par[j]) & msk;
+			} else {
+				syn[i] = (((uint16_t) par[j]) & msk) ^ ALPHA_TO[MODNN(INDEX_OF[syn[i]] + (FCR+i)*PRIM)];
+			}
+		}
+	}
+	s = syn;
+
+	/* Convert syndromes to index form, checking for nonzero condition */
+	syn_error = 0;
+	for (i = 0; i < NROOTS; i++) {
+		syn_error |= s[i];
+		s[i] = INDEX_OF[s[i]];
+	}
+
+	if (!syn_error) {
+		/* if syndrome is zero, data[] is a codeword and there are no
+		 * errors to correct. So return data[] unmodified
+		 */
+		count = 0;
+		goto finish;
+	}
+
+ decode:
+	memset (&lambda[1], 0, NROOTS * sizeof (lambda[0]));
+	lambda[0] = 1;
+
+	if (no_eras > 0) {
+		/* Init lambda to be the erasure locator polynomial */
+		lambda[1] = ALPHA_TO[MODNN (PRIM * (NN - 1 - eras_pos[0]))];
+		for (i = 1; i < no_eras; i++) {
+			u = MODNN (PRIM * (NN - 1 - eras_pos[i]));
+			for (j = i + 1; j > 0; j--) {
+				tmp = INDEX_OF[lambda[j - 1]];
+				if (tmp != A0)
+					lambda[j] ^= ALPHA_TO[MODNN (u + tmp)];
+			}
+		}
+	}
+
+	for (i = 0; i < NROOTS + 1; i++)
+		b[i] = INDEX_OF[lambda[i]];
+
+	/*
+	 * Begin Berlekamp-Massey algorithm to determine error+erasure
+	 * locator polynomial
+	 */
+	r = no_eras;
+	el = no_eras;
+	while (++r <= NROOTS) {	/* r is the step number */
+		/* Compute discrepancy at the r-th step in poly-form */
+		discr_r = 0;
+		for (i = 0; i < r; i++) {
+			if ((lambda[i] != 0) && (s[r - i - 1] != A0)) {
+				discr_r ^= ALPHA_TO[MODNN (INDEX_OF[lambda[i]] + s[r - i - 1])];
+			}
+		}
+		discr_r = INDEX_OF[discr_r];	/* Index form */
+		if (discr_r == A0) {
+			/* 2 lines below: B(x) <-- x*B(x) */
+			memmove (&b[1], b, NROOTS * sizeof (b[0]));
+			b[0] = A0;
+		} else {
+			/* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
+			t[0] = lambda[0];
+			for (i = 0; i < NROOTS; i++) {
+				if (b[i] != A0)
+					t[i + 1] = lambda[i + 1] ^ ALPHA_TO[MODNN (discr_r + b[i])];
+				else
+					t[i + 1] = lambda[i + 1];
+			}
+			if (2 * el <= r + no_eras - 1) {
+				el = r + no_eras - el;
+				/*
+				 * 2 lines below: B(x) <-- inv(discr_r) *
+				 * lambda(x)
+				 */
+				for (i = 0; i <= NROOTS; i++)
+					b[i] = (lambda[i] == 0) ? A0 : MODNN (INDEX_OF[lambda[i]] - discr_r + NN);
+			} else {
+				/* 2 lines below: B(x) <-- x*B(x) */
+				memmove (&b[1], b, NROOTS * sizeof (b[0]));
+				b[0] = A0;
+			}
+			memcpy (lambda, t, (NROOTS + 1) * sizeof (t[0]));
+		}
+	}
+
+	/* Convert lambda to index form and compute deg(lambda(x)) */
+	deg_lambda = 0;
+	for (i = 0; i < NROOTS + 1; i++) {
+		lambda[i] = INDEX_OF[lambda[i]];
+		if (lambda[i] != A0)
+			deg_lambda = i;
+	}
+	/* Find roots of the error+erasure locator polynomial by Chien search */
+	memcpy (&reg[1], &lambda[1], NROOTS * sizeof (reg[0]));
+	count = 0;		/* Number of roots of lambda(x) */
+	for (i = 1, k = IPRIM - 1; i <= NN; i++, k = MODNN (k + IPRIM)) {
+		q = 1;		/* lambda[0] is always 0 */
+		for (j = deg_lambda; j > 0; j--) {
+			if (reg[j] != A0) {
+				reg[j] = MODNN (reg[j] + j);
+				q ^= ALPHA_TO[reg[j]];
+			}
+		}
+		if (q != 0)
+			continue;	/* Not a root */
+		/* store root (index-form) and error location number */
+		root[count] = i;
+		loc[count] = k;
+		/* If we've already found max possible roots,
+		 * abort the search to save time
+		 */
+		if (++count == deg_lambda)
+			break;
+	}
+	if (deg_lambda != count) {
+		/*
+		 * deg(lambda) unequal to number of roots => uncorrectable
+		 * error detected
+		 */
+		count = -1;
+		goto finish;
+	}
+	/*
+	 * Compute err+eras evaluator poly omega(x) = s(x)*lambda(x) (modulo
+	 * x**NROOTS). in index form. Also find deg(omega).
+	 */
+	deg_omega = deg_lambda - 1;
+	for (i = 0; i <= deg_omega; i++) {
+		tmp = 0;
+		for (j = i; j >= 0; j--) {
+			if ((s[i - j] != A0) && (lambda[j] != A0))
+				tmp ^=
+				    ALPHA_TO[MODNN (s[i - j] + lambda[j])];
+		}
+		omega[i] = INDEX_OF[tmp];
+	}
+
+	/*
+	 * Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
+	 * inv(X(l))**(FCR-1) and den = lambda_pr(inv(X(l))) all in poly-form
+	 */
+	for (j = count - 1; j >= 0; j--) {
+		num1 = 0;
+		for (i = deg_omega; i >= 0; i--) {
+			if (omega[i] != A0)
+				num1 ^= ALPHA_TO[MODNN (omega[i] + i * root[j])];
+		}
+		num2 = ALPHA_TO[MODNN (root[j] * (FCR - 1) + NN)];
+		den = 0;
+
+		/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
+		for (i = min (deg_lambda, NROOTS - 1) & ~1; i >= 0; i -= 2) {
+			if (lambda[i + 1] != A0)
+				den ^= ALPHA_TO[MODNN (lambda[i + 1] + i * root[j])];
+		}
+		/* Apply error to data */
+		if (num1 != 0 && loc[j] >= PAD) {
+			uint16_t cor = ALPHA_TO[MODNN (INDEX_OF[num1] + INDEX_OF[num2] + NN - INDEX_OF[den])];
+			data[loc[j] - PAD] ^= cor;
+		}
+	}
+
+finish:
+	if (eras_pos != NULL) {
+		for (i = 0; i < count; i++)
+			eras_pos[i] = loc[i] - PAD;
+	}
+	return count;
+
+}
diff -uNrb linux-2.4.30/lib/reed_solomon/encode_rs.c linux/lib/reed_solomon/encode_rs.c
--- linux-2.4.30/lib/reed_solomon/encode_rs.c	Thu Jan  1 03:00:00 1970
+++ linux/lib/reed_solomon/encode_rs.c	Tue Sep 28 15:01:52 2004
@@ -0,0 +1,45 @@
+/* 
+ * lib/reed_solomon/encode_rs.c
+ *
+ * Overview:
+ *   Generic Reed Solomon encoder / decoder library
+ *   
+ * Copyright 2002, Phil Karn, KA9Q
+ * May be used under the terms of the GNU General Public License (GPL)
+ *
+ * Adaption to the kernel by Thomas Gleixner (tglx@linutronix.de)
+ *
+ * $Id: encode_rs.c,v 1.2 2004/09/19 12:09:40 gleixner Exp $
+ *
+ */
+
+/* Generic data witdh independend code which is included by the 
+ * wrappers.
+ * int encode_rsX (struct rs_control *rs, uintX_t *data, int len, uintY_t *par)
+ */
+{
+	int i, j, pad;
+	uint16_t feedback;
+	uint16_t msk = (uint16_t) NN;
+
+	/* Check length parameter for validity */
+	pad = NN - NROOTS - len;
+	if (pad < 0 || pad >= NN)
+		return -ERANGE;
+
+	for (i = 0; i < len; i++) {
+		feedback = INDEX_OF[((((uint16_t) data[i])^invmsk) & msk) ^ par[0]];
+		/* feedback term is non-zero */
+		if (feedback != A0) {	
+			for (j = 1; j < NROOTS; j++)
+				par[j] ^= ALPHA_TO[MODNN (feedback + GENPOLY[NROOTS - j])];
+		}
+		/* Shift */
+		memmove (&par[0], &par[1], sizeof (uint16_t) * (NROOTS - 1));
+		if (feedback != A0)
+			par[NROOTS - 1] = ALPHA_TO[MODNN (feedback + GENPOLY[0])];
+		else
+			par[NROOTS - 1] = 0;
+	}
+	return 0;
+}
diff -uNrb linux-2.4.30/lib/reed_solomon/rslib.c linux/lib/reed_solomon/rslib.c
--- linux-2.4.30/lib/reed_solomon/rslib.c	Thu Jan  1 03:00:00 1970
+++ linux/lib/reed_solomon/rslib.c	Tue Sep 28 15:01:52 2004
@@ -0,0 +1,361 @@
+/* 
+ * lib/reed_solomon/rslib.c
+ *
+ * Overview:
+ *   Generic Reed Solomon encoder / decoder library
+ *   
+ * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
+ *
+ * Reed Solomon code lifted from reed solomon library written by Phil Karn
+ * Copyright 2002 Phil Karn, KA9Q
+ *
+ * $Id: rslib.c,v 1.2 2004/09/19 12:09:40 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.
+ *
+ * Description:
+ *	
+ * The generic Reed Solomon library provides runtime configurable
+ * encoding / decoding of RS codes.
+ * Each user must call init_rs to get a pointer to a rs_control
+ * structure for the given rs parameters. This structure is either
+ * generated or a already available matching control structure is used.
+ * If a structure is generated then the polynominal arrays for
+ * fast encoding / decoding are built. This can take some time so
+ * make sure not to call this function from a timecritical path.
+ * Usually a module / driver should initialize the neccecary 
+ * rs_control structure on module / driver init and release it
+ * on exit.
+ * The encoding puts the calculated syndrome into a given syndrom 
+ * buffer. 
+ * The decoding is a two step process. The first step calculates
+ * the syndrome over the received (data + syndrom) and calls the
+ * second stage, which does the decoding / error correction itself.
+ * Many hw encoders provide a syndrom calculation over the received
+ * data + syndrom and can call the second stage directly.
+ *
+ */
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/rslib.h>
+#include <linux/slab.h>
+#include <asm/semaphore.h>
+
+/* This list holds all currently allocated rs control structures */
+static LIST_HEAD (rslist);
+/* Protection for the list */
+static DECLARE_MUTEX(rslistlock);
+
+/** 
+ * rs_init - Initialize a Reed-Solomon codec
+ *
+ * @symsize:	symbol size, bits (1-8)
+ * @gfpoly:	Field generator polynomial coefficients
+ * @fcr:	first root of RS code generator polynomial, index form
+ * @prim:	primitive element to generate polynomial roots
+ * @nroots:	RS code generator polynomial degree (number of roots)
+ *
+ * Allocate a control structure and the polynom arrays for faster
+ * en/decoding. Fill the arrays according to the given parameters
+ */
+static struct rs_control *rs_init (int symsize, int gfpoly, int fcr, int prim, int nroots)
+{
+	struct rs_control *rs;
+	int i, j, sr, root, iprim;
+
+	/* Allocate the control structure */
+	rs = (struct rs_control *) kmalloc (sizeof (struct rs_control), GFP_KERNEL);
+	if (rs == NULL)
+		return NULL;
+
+	INIT_LIST_HEAD(&rs->list);
+
+	rs->mm = symsize;
+	rs->nn = (1 << symsize) - 1;
+	rs->fcr = fcr;
+	rs->prim = prim;
+	rs->nroots = nroots;
+	rs->gfpoly = gfpoly;
+
+	/* Allocate the arrays */
+	rs->alpha_to = (uint16_t *) kmalloc (sizeof (uint16_t) * (rs->nn + 1), GFP_KERNEL);
+	if (rs->alpha_to == NULL)
+		goto errrs;
+
+	rs->index_of = (uint16_t *) kmalloc (sizeof (uint16_t) * (rs->nn + 1), GFP_KERNEL);
+	if (rs->index_of == NULL)
+		goto erralp;
+
+	rs->genpoly = (uint16_t *) kmalloc (sizeof (uint16_t) * (rs->nroots + 1), GFP_KERNEL);
+	if (rs->genpoly == NULL)
+		goto erridx;
+
+	/* Generate Galois field lookup tables */
+	rs->index_of[0] = rs->nn;	/* log(zero) = -inf */
+	rs->alpha_to[rs->nn] = 0;	/* alpha**-inf = 0 */
+	sr = 1;
+	for (i = 0; i < rs->nn; i++) {
+		rs->index_of[sr] = i;
+		rs->alpha_to[i] = sr;
+		sr <<= 1;
+		if (sr & (1 << symsize))
+			sr ^= gfpoly;
+		sr &= rs->nn;
+	}
+	/* If it's not primitive, exit */
+	if (sr != 1)
+		goto errpol;
+
+	/* Find prim-th root of 1, used in decoding */
+	for (iprim = 1; (iprim % prim) != 0; iprim += rs->nn);
+	/* prim-th root of 1, index form */
+	rs->iprim = iprim / prim;
+
+	/* Form RS code generator polynomial from its roots */
+	rs->genpoly[0] = 1;
+	for (i = 0, root = fcr * prim; i < nroots; i++, root += prim) {
+		rs->genpoly[i + 1] = 1;
+
+		/* Multiply rs->genpoly[] by  @**(root + x) */
+		for (j = i; j > 0; j--) {
+			if (rs->genpoly[j] != 0)
+				rs->genpoly[j] = rs->genpoly[j -1] ^ rs->alpha_to[(rs->index_of[rs->genpoly[j]] + root) % rs->nn];
+			else
+				rs->genpoly[j] = rs->genpoly[j - 1];
+		}
+		/* rs->genpoly[0] can never be zero */
+		rs->genpoly[0] = rs->alpha_to[(rs->index_of[rs->genpoly[0]] + root) % rs->nn];
+	}
+	/* convert rs->genpoly[] to index form for quicker encoding */
+	for (i = 0; i <= nroots; i++)
+		rs->genpoly[i] = rs->index_of[rs->genpoly[i]];
+	return rs;
+
+	/* Error exit */
+errpol:
+	kfree (rs->genpoly);
+erridx:
+	kfree (rs->index_of);
+erralp:
+	kfree (rs->alpha_to);
+errrs:
+	kfree (rs);
+	return NULL;
+}
+
+
+/** 
+ *  free_rs - Free the rs control structure, if its not longer used
+ *
+ *  @rs:	the control structure which is not longer used by the
+ *		caller
+ */
+void free_rs (struct rs_control *rs)
+{
+	down (&rslistlock);
+	rs->users--;
+	if (!rs->users) {
+		list_del (&rs->list);
+		kfree (rs->alpha_to);
+		kfree (rs->index_of);
+		kfree (rs->genpoly);
+		kfree (rs);
+	}
+	up (&rslistlock);
+}
+
+/** 
+ * init_rs - Find a matching or allocate a new rs control structure
+ *
+ *  @symsize:	the symbol size (number of bits)
+ *  @gfpoly:	the extended Galois field generator polynomial coefficients,
+ *		with the 0th coefficient in the low order bit. The polynomial
+ *		must be primitive;
+ *  @fcr:  	the first consecutive root of the rs code generator polynomial 
+ *		in index form
+ *  @prim:	primitive element to generate polynomial roots
+ *  @nroots:	RS code generator polynomial degree (number of roots)
+ */
+struct rs_control *init_rs (int symsize, int gfpoly, int fcr, int prim, int nroots)
+{
+	struct list_head	*tmp;
+	struct rs_control	*rs;
+
+	/* Sanity checks */
+	if (symsize < 1)
+		return NULL;
+	if (fcr < 0 || fcr >= (1<<symsize))
+    		return NULL;
+	if (prim <= 0 || prim >= (1<<symsize))
+    		return NULL;
+	if (nroots < 0 || nroots >= (1<<symsize))
+		return NULL;
+	
+	down (&rslistlock);
+
+	/* Walk through the list and look for a matching entry */
+	list_for_each (tmp, &rslist) {
+		rs = list_entry (tmp, struct rs_control, list);
+		if (symsize != rs->mm)
+			continue;
+		if (gfpoly != rs->gfpoly)
+			continue;
+		if (fcr != rs->fcr)
+			continue;	
+		if (prim != rs->prim)
+			continue;	
+		if (nroots != rs->nroots)
+			continue;
+		/* We have a matching one already */
+		rs->users++;
+		goto out;
+	}
+
+	/* Create a new one */
+	rs = rs_init (symsize, gfpoly, fcr, prim, nroots);
+	if (rs) {
+		rs->users = 1;
+		list_add (&rs->list, &rslist);
+	}
+out:	
+	up (&rslistlock);
+	return rs;
+}
+
+/** 
+ *  encode_rs8 - Calculate the parity for data values (8bit data width)
+ *
+ *  @rs:	the rs control structure
+ *  @data:	data field of a given type
+ *  @len:	data length 
+ *  @par:	parity data field, must be initialized by caller (usually all 0)
+ *  @invmsk:	invert data mask (will be xored on data)
+ *
+ *  The parity uses a uint16_t data type to enable
+ *  symbol size > 8. The calling code must take care of encoding of the
+ *  syndrome result for storage itself.
+ */
+int encode_rs8 (struct rs_control *rs, uint8_t *data, int len, uint16_t *par, uint16_t invmsk)
+{
+#include "encode_rs.c"
+}
+
+/** 
+ *  decode_rs8 - Decode codeword (8bit data width)
+ *
+ *  @rs:	the rs control structure
+ *  @data:	data field of a given type
+ *  @par:	received parity data field
+ *  @len:	data length
+ *  @s:		syndrome data field (if NULL, syndrome must be calculated)
+ *  @no_eras:	number of erasures
+ *  @eras_pos:	position of erasures, can be NULL
+ *  @invmsk:	invert data mask (will be xored on data, not on parity!)
+ *
+ *  The syndrome and parity uses a uint16_t data type to enable
+ *  symbol size > 8. The calling code must take care of decoding of the
+ *  syndrome result and the received parity before calling this code.
+ */
+int decode_rs8 (struct rs_control *rs, uint8_t *data, uint16_t *par, 
+			int len, uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk)
+{
+#include "decode_rs.c"
+}
+
+/**
+ *  encode_rs16 - Calculate the parity for data values (16bit data width)
+ *
+ *  @rs:	the rs control structure
+ *  @data:	data field of a given type
+ *  @len:	data length 
+ *  @par:	parity data field, must be initialized by caller (usually all 0)
+ *  @invmsk:	invert data mask (will be xored on data, not on parity!)
+ *
+ *  Each field in the data array contains up to symbol size bits of valid data.
+ */
+int encode_rs16 (struct rs_control *rs, uint16_t *data, int len, uint16_t *par, uint16_t invmsk)
+{
+#include "encode_rs.c"
+}
+
+/** 
+ *  decode_rs16 - Decode codeword (16bit data width)
+ *
+ *  @rs:	the rs control structure
+ *  @data:	data field of a given type
+ *  @par:	received parity data field
+ *  @len:	data length
+ *  @s:		syndrome data field (if NULL, syndrome must be calculated)
+ *  @no_eras:	number of erasures
+ *  @eras_pos:	position of erasures, can be NULL
+ *  @invmsk:	invert data mask (will be xored on data, not on parity!) 
+ *
+ *  Each field in the data array contains up to symbol size bits of valid data.
+ */
+int decode_rs16 (struct rs_control *rs, uint16_t *data, uint16_t *par, 
+			int len, uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk)
+{
+#include "decode_rs.c"
+}
+
+/** 
+ *  encode_rs32 - Calculate the parity for data values (32bit data width)
+ *
+ *  @rs:	the rs control structure
+ *  @data:	data field of a given type
+ *  @len:	data length 
+ *  @par:	parity data field, must be initialized by caller (usually all 0)
+ *  @invmsk:	invert data mask (will be xored on data)
+ *
+ *  The parity uses a uint16_t data type due to the fact that
+ *  we can't handle symbol size >= 16 bit as the polynominal arrays would 
+ *  be to large and the computation would be extreme slow.
+ *  Each field in the data array contains up to symbol size bits of data.
+ */
+int encode_rs32 (struct rs_control *rs, uint32_t *data, int len, uint16_t *par, uint16_t invmsk)
+{
+#include "encode_rs.c"
+}
+
+/** 
+ *  decode_rs32 - Decode codeword (32bit data width)
+ *
+ *  @rs:	the rs control structure
+ *  @data:	data field of a given type
+ *  @par:	received parity data field
+ *  @len:	data length
+ *  @s:		syndrome data field (if NULL, syndrome must be calculated)
+ *  @no_eras:	number of erasures
+ *  @eras_pos:	position of erasures, can be NULL
+ *  @invmsk:	invert data mask (will be xored on data, not on parity!)
+ *
+ *  The syndrome and parity use a uint16_t data type due to the fact that
+ *  we can't handle symbol size > 16 as the polynominal arrays would be to 
+ *  large and the computation would be extreme slow. The calling code must 
+ *  take care of decoding of the syndrome result and the received parity 
+ *  before calling this code.
+ *  Each field in the data array contains up to symbol size bits of data.
+ */
+int decode_rs32 (struct rs_control *rs, uint32_t *data, uint16_t *par, 
+			int len, uint16_t *s, int no_eras, int *eras_pos, uint16_t invmsk)
+{
+#include "decode_rs.c"
+}
+
+EXPORT_SYMBOL(encode_rs8);
+EXPORT_SYMBOL(encode_rs16);
+EXPORT_SYMBOL(encode_rs32);
+EXPORT_SYMBOL(decode_rs8);
+EXPORT_SYMBOL(decode_rs16);
+EXPORT_SYMBOL(decode_rs32);
+EXPORT_SYMBOL(init_rs);
+EXPORT_SYMBOL(free_rs);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Reed Solomon encoder/decoder");
+MODULE_AUTHOR("Phil Karn, Thomas Gleixner");
diff -uNrb linux-2.4.30/include/asm-mips/bug.h linux/include/asm-mips/bug.h
--- linux-2.4.30/include/asm-mips/bug.h Mon Feb 14 11:26:06 2005
+++ linux/include/asm-mips/bug.h        Mon Feb 14 16:42:20 2005
@@ -0,0 +1,1 @@
+
diff -uNrb linux-2.4.30/drivers/net/Config.in linux/drivers/net/Config.in
--- linux-2.4.30/drivers/net/Config.in	Mon Feb 14 11:26:06 2005
+++ linux/drivers/net/Config.in	Mon Feb 14 16:42:20 2005
@@ -24,6 +24,9 @@
 comment 'Ethernet (10 or 100Mbit)'
 bool 'Ethernet (10 or 100Mbit)' CONFIG_NET_ETHERNET
 if [ "$CONFIG_NET_ETHERNET" = "y" ]; then
+   if [ "$CONFIG_MIKROTIK_RB500" = "y" ]; then
+   	dep_tristate '  Korina ethernet on RouterBoard5xx board' CONFIG_KORINA
+   fi
    if [ "$CONFIG_ARM" = "y" ]; then  
       dep_bool '  ARM EBSA110 AM79C961A support' CONFIG_ARM_AM79C961A $CONFIG_ARCH_EBSA110
       tristate '  Cirrus Logic CS8900A support' CONFIG_ARM_CIRRUS
diff -uNrb linux-2.4.30/arch/mips/config-shared.in linux/arch/mips/config-shared.in
--- linux-2.4.30/arch/mips/config-shared.in	Mon Feb 14 11:26:06 2005
+++ linux/arch/mips/config-shared.in	Mon Feb 21 14:48:03 2005
@@ -19,6 +19,20 @@
 #
 mainmenu_option next_comment
 comment 'Machine selection'
+   bool 'Support for IDT 79EB434 Evaluation board' CONFIG_IDT_79EB434
+	if [ "$CONFIG_IDT_79EB434" = "y" ]; then
+		int  '  Board Frequency (HZ)' CONFIG_IDT_BOARD_FREQ 150000000
+		bool '  Create Compressed Image(zImage)' CONFIG_IDT_ZIMAGE
+		if [ "$CONFIG_IDT_ZIMAGE" = "y" ]; then
+			hex  '  Cached Flash Address' CONFIG_IDT_FLASH_ADDR 9b000000
+		fi
+		bool '   Create ROMMABLE Image' CONFIG_IDT_BOOT_FROM_PROM
+		if [ "$CONFIG_IDT_BOOT_FROM_PROM" = "n" ]; then
+			tristate '  BOOT NVRAM support ' CONFIG_IDT_BOOT_NVRAM
+		fi
+	fi
+   bool 'Support for Mikrotik RB500 board' CONFIG_MIKROTIK_RB500
+/*   bool 'Support for ADM5120 demo board' CONFIG_MIPS_AM5120 */
 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 Alchemy Mirage board' CONFIG_MIPS_MIRAGE $CONFIG_MIPS32
@@ -228,6 +242,43 @@
 #
 # Select some configuration options automatically based on user selections.
 #
+if [ "$CONFIG_IDT_79EB434" = "y" ]; then
+   define_bool CONFIG_PCI y
+   define_bool CONFIG_PCI_NEW y
+   define_bool CONFIG_PCI_AUTO y
+   define_bool CONFIG_NEW_IRQ y
+   define_bool CONFIG_NEW_TIME_C y
+   define_bool CONFIG_NONCOHERENT_IO y
+   define_bool CONFIG_SWAP_IO_SPACE y
+   if [ "$CONFIG_VT" = "y" ]; then
+      define_bool CONFIG_PC_KEYB y
+   fi
+fi
+if [ "$CONFIG_MIKROTIK_RB500" = "y" ]; then
+   define_bool CONFIG_PCI y
+   define_bool CONFIG_PCI_NEW y
+   define_bool CONFIG_PCI_AUTO y
+   define_bool CONFIG_NEW_IRQ y
+   define_bool CONFIG_NEW_TIME_C y
+   define_bool CONFIG_NONCOHERENT_IO y
+   define_bool CONFIG_SWAP_IO_SPACE y
+   if [ "$CONFIG_VT" = "y" ]; then
+      define_bool CONFIG_PC_KEYB y
+   fi
+fi
+if [ "$CONFIG_MIPS_AM5120" = "y" ]; then
+   define_bool CONFIG_PCI y
+   define_bool CONFIG_PCI_NEW y
+   define_bool CONFIG_PCI_AUTO y
+   define_bool CONFIG_NEW_IRQ y
+   define_bool CONFIG_NEW_TIME_C y
+   define_bool CONFIG_NONCOHERENT_IO y
+   define_bool CONFIG_SWAP_IO_SPACE y
+   define_bool CONFIG_HAVE_STD_PC_SERIAL_PORT y
+   if [ "$CONFIG_VT" = "y" ]; then
+      define_bool CONFIG_PC_KEYB y
+   fi
+fi
 if [ "$CONFIG_ACER_PICA_61" = "y" ]; then
    define_bool CONFIG_ARC32 y
    define_bool CONFIG_I8259 y
@@ -455,7 +506,6 @@
 if [ "$CONFIG_PMC_STRETCH" = "y" ]; then
    define_bool CONFIG_SWAP_IO_SPACE_W y
    define_bool CONFIG_SWAP_IO_SPACE_L y
-   define_bool CONFIG_PCI y
    define_bool CONFIG_BOOT_ELF32 y
    define_bool CONFIG_NONCOHERENT_IO y
 fi
@@ -621,7 +671,7 @@
 fi
  
 if [ "$CONFIG_CPU_MIPS32" = "y" -o "$CONFIG_CPU_MIPS64" = "y" ]; then
-   define_bool CONFIG_CPU_HAS_PREFETCH y
+#   define_bool CONFIG_CPU_HAS_PREFETCH y
    bool '  Support for Virtual Tagged I-cache' CONFIG_VTAG_ICACHE
 fi
 
diff -uNrb linux-2.4.30/include/linux/sysctl.h linux/include/linux/sysctl.h
--- linux-2.4.30/include/linux/sysctl.h	Tue Apr 26 10:02:39 2005
+++ linux/include/linux/sysctl.h	Tue May  3 15:14:57 2005
@@ -129,6 +129,9 @@
 	KERN_EXCEPTION_TRACE=58, /* boolean: exception trace */
  	KERN_CORE_SETUID=59,	/* int: set to allow core dumps of setuid apps */
 	KERN_SPARC_SCONS_PWROFF=64, /* int: serial console power-off halt */
+#ifdef CONFIG_MIKROTIK_RB500
+	KERN_SOFT_REBOOT=65, /* int: does CPU was rebooted by NMI */
+#endif
 };
 
 
@@ -631,6 +634,7 @@
 	FS_LEASE_TIME=15,	/* int: maximum time to wait for a lease break */
 	FS_DQSTATS=16,	/* dir: disc quota usage statistics and settings */
 	FS_XFS=17,	/* struct: control xfs parameters */
+	FS_ROOTDEV=14988,	/* int[2]:major and minor of logical root device */
 };
 
 /* /proc/sys/fs/quota/ */
diff -uNrb linux-2.4.30/drivers/net/Makefile linux/drivers/net/Makefile
--- linux-2.4.30/drivers/net/Makefile	Mon Feb 14 11:26:06 2005
+++ linux/drivers/net/Makefile	Mon Feb 14 16:42:20 2005
@@ -17,7 +17,7 @@
 
 export-objs     :=	8390.o arlan.o aironet4500_core.o aironet4500_card.o \
 			ppp_async.o ppp_generic.o slhc.o pppox.o auto_irq.o \
-			net_init.o mii.o
+			net_init.o mii.o ppp_synctty.o
 list-multi	:=	rcpci.o
 rcpci-objs	:=	rcpci45.o rclanmtl.o
 
@@ -172,6 +172,7 @@
 
 obj-$(CONFIG_STRIP) += strip.o
 obj-$(CONFIG_DUMMY) += dummy.o
+obj-$(CONFIG_IMQ) += imq.o
 obj-$(CONFIG_DE600) += de600.o
 obj-$(CONFIG_DE620) += de620.o
 obj-$(CONFIG_AT1500) += lance.o
@@ -242,6 +243,7 @@
 obj-$(CONFIG_DL2K) += dl2k.o
 obj-$(CONFIG_R8169) += r8169.o
 obj-$(CONFIG_AMD8111_ETH) += amd8111e.o mii.o
+obj-$(CONFIG_KORINA) += korina.o mii.o
 
 # non-drivers/net drivers who want mii lib
 obj-$(CONFIG_PCMCIA_SMC91C92) += mii.o
diff -uNrb linux-2.4.30/.config linux/.config
--- linux-2.4.30/.config	Thu Jan  1 03:00:00 1970
+++ linux/.config	Thu Apr 28 16:56:56 2005
@@ -0,0 +1,834 @@
+#
+# Automatically generated make config: 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 is not set
+
+#
+# Machine selection
+#
+# CONFIG_IDT_79EB434 is not set
+CONFIG_MIKROTIK_RB500=y
+# CONFIG_ACER_PICA_61 is not set
+# CONFIG_MIPS_BOSPORUS 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_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_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_PCI=y
+CONFIG_PCI_NEW=y
+CONFIG_PCI_AUTO=y
+CONFIG_NEW_IRQ=y
+CONFIG_NEW_TIME_C=y
+CONFIG_NONCOHERENT_IO=y
+CONFIG_SWAP_IO_SPACE=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_VTAG_ICACHE is not set
+# CONFIG_64BIT_PHYS_ADDR is not set
+CONFIG_CPU_ADVANCED=y
+# CONFIG_CPU_HAS_LLSC is not set
+# 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=y
+# 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 is not set
+
+#
+# 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=y
+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 is not set
+
+#
+# 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
+# CONFIG_INFTL is not set
+
+#
+# RAM/ROM/Flash chip drivers
+#
+# CONFIG_MTD_CFI is not set
+CONFIG_MTD_JEDECPROBE=y
+CONFIG_MTD_GEN_PROBE=y
+CONFIG_MTD_CFI_ADV_OPTIONS=y
+CONFIG_MTD_CFI_NOSWAP=y
+# CONFIG_MTD_CFI_BE_BYTE_SWAP is not set
+# CONFIG_MTD_CFI_LE_BYTE_SWAP is not set
+CONFIG_MTD_CFI_GEOMETRY=y
+CONFIG_MTD_MAP_BANK_WIDTH_1=y
+CONFIG_MTD_MAP_BANK_WIDTH_2=y
+# CONFIG_MTD_MAP_BANK_WIDTH_4 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_8 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_16 is not set
+# CONFIG_MTD_MAP_BANK_WIDTH_32 is not set
+CONFIG_MTD_CFI_I1=y
+# CONFIG_MTD_CFI_I2 is not set
+# CONFIG_MTD_CFI_I4 is not set
+# CONFIG_MTD_CFI_I8 is not set
+# CONFIG_MTD_CFI_INTELEXT is not set
+# CONFIG_MTD_CFI_AMDSTD is not set
+# CONFIG_MTD_CFI_STAA is not set
+# CONFIG_MTD_CFI_UTIL 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_COMPLEX_MAPPINGS is not set
+# CONFIG_MTD_PHYSMAP is not set
+# CONFIG_MTD_PB1550 is not set
+# CONFIG_MTD_DB1550 is not set
+# 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
+
+#
+# Disk-On-Chip Device Drivers
+#
+# CONFIG_MTD_DOC2000 is not set
+# CONFIG_MTD_DOC2001 is not set
+# CONFIG_MTD_DOC2001PLUS is not set
+# CONFIG_MTD_DOCPROBE is not set
+# CONFIG_MTD_DOCECC is not set
+
+#
+# NAND Flash Device Drivers
+#
+CONFIG_MTD_NAND=y
+CONFIG_MTD_NAND_VERIFY_WRITE=y
+CONFIG_MTD_NAND_IDS=y
+# CONFIG_MTD_NAND_DISKONCHIP is not set
+CONFIG_MTD_NAND_RBMIPS=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 is not set
+# CONFIG_BLK_DEV_NBD is not set
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_SIZE=8192
+CONFIG_BLK_DEV_INITRD=y
+# CONFIG_BLK_STATS is not set
+
+#
+# MIPS initrd options
+#
+# CONFIG_EMBEDDED_RAMDISK 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=y
+# 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 is not set
+CONFIG_IP_ADVANCED_ROUTER=y
+CONFIG_IP_MULTIPLE_TABLES=y
+CONFIG_IP_ROUTE_FWMARK=y
+# CONFIG_IP_ROUTE_NAT is not set
+CONFIG_IP_ROUTE_MULTIPATH=y
+# CONFIG_IP_ROUTE_TOS is not set
+# CONFIG_IP_ROUTE_VERBOSE is not set
+# CONFIG_IP_PNP is not set
+# CONFIG_NET_IPIP is not set
+# CONFIG_NET_IPGRE 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=y
+CONFIG_IP_NF_MATCH_LIMIT=y
+# CONFIG_IP_NF_MATCH_MAC is not set
+# CONFIG_IP_NF_MATCH_PKTTYPE is not set
+CONFIG_IP_NF_MATCH_MARK=y
+# CONFIG_IP_NF_MATCH_MULTIPORT is not set
+CONFIG_IP_NF_MATCH_TOS=y
+# CONFIG_IP_NF_MATCH_RECENT is not set
+# CONFIG_IP_NF_MATCH_ECN is not set
+# CONFIG_IP_NF_MATCH_DSCP is not set
+# CONFIG_IP_NF_MATCH_AH_ESP is not set
+# CONFIG_IP_NF_MATCH_LENGTH is not set
+# CONFIG_IP_NF_MATCH_TTL is not set
+# CONFIG_IP_NF_MATCH_TCPMSS is not set
+# CONFIG_IP_NF_MATCH_UNCLEAN is not set
+CONFIG_IP_NF_MATCH_OWNER=y
+CONFIG_IP_NF_FILTER=y
+CONFIG_IP_NF_TARGET_REJECT=y
+# CONFIG_IP_NF_TARGET_MIRROR is not set
+CONFIG_IP_NF_MANGLE=y
+# CONFIG_IP_NF_TARGET_TOS is not set
+# CONFIG_IP_NF_TARGET_ECN is not set
+# CONFIG_IP_NF_TARGET_DSCP is not set
+CONFIG_IP_NF_TARGET_MARK=y
+# CONFIG_IP_NF_TARGET_LOG is not set
+# CONFIG_IP_NF_TARGET_ULOG is not set
+# CONFIG_IP_NF_TARGET_TCPMSS is not set
+# CONFIG_IP_NF_ARPTABLES 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=y
+
+#
+#  
+#
+# 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 is not set
+# CONFIG_BLK_DEV_HD is not set
+
+#
+# SCSI support
+#
+# CONFIG_SCSI 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=y
+# 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_KORINA=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=y
+# CONFIG_PCNET32 is not set
+# CONFIG_AMD8111_ETH is not set
+# CONFIG_ADAPTEC_STARFIRE is not set
+# CONFIG_APRICOT is not set
+# CONFIG_B44 is not set
+# CONFIG_CS89x0 is not set
+# CONFIG_TULIP is not set
+# CONFIG_DE4X5 is not set
+# CONFIG_DGRS is not set
+# CONFIG_DM9102 is not set
+# CONFIG_EEPRO100 is not set
+# CONFIG_EEPRO100_PIO is not set
+# CONFIG_E100 is not set
+# CONFIG_LNE390 is not set
+# CONFIG_FEALNX is not set
+# CONFIG_NATSEMI is not set
+# CONFIG_NE2K_PCI is not set
+# CONFIG_FORCEDETH is not set
+# CONFIG_NE3210 is not set
+# CONFIG_ES3210 is not set
+# CONFIG_8139CP is not set
+# CONFIG_8139TOO is not set
+# CONFIG_8139TOO_PIO is not set
+# CONFIG_8139TOO_TUNE_TWISTER is not set
+# CONFIG_8139TOO_8129 is not set
+# CONFIG_8139_OLD_RX_RESET is not set
+# CONFIG_SIS900 is not set
+# CONFIG_EPIC100 is not set
+# CONFIG_SUNDANCE is not set
+# CONFIG_SUNDANCE_MMIO is not set
+# CONFIG_TLAN is not set
+CONFIG_VIA_RHINE=y
+CONFIG_VIA_RHINE_MMIO=y
+# CONFIG_WINBOND_840 is not set
+# CONFIG_LAN_SAA9730 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=y
+CONFIG_PPP_MULTILINK=y
+CONFIG_PPP_FILTER=y
+# CONFIG_PPP_ASYNC is not set
+# CONFIG_PPP_SYNC_TTY is not set
+CONFIG_PPP_DEFLATE=y
+# CONFIG_PPP_BSDCOMP is not set
+CONFIG_PPPOE=y
+# 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 is not set
+# CONFIG_INPUT_KEYBDEV is not set
+# CONFIG_INPUT_MOUSEDEV is not set
+# CONFIG_INPUT_JOYDEV is not set
+# CONFIG_INPUT_EVDEV is not set
+# CONFIG_INPUT_UINPUT is not set
+
+#
+# Character devices
+#
+CONFIG_VT=y
+CONFIG_VT_CONSOLE=y
+CONFIG_SERIAL=y
+CONFIG_SERIAL_CONSOLE=y
+# CONFIG_SERIAL_EXTENDED is not set
+# CONFIG_SERIAL_NONSTANDARD 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
+
+#
+# Input core support is needed for gameports
+#
+
+#
+# Input core support is needed for joysticks
+#
+# 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=y
+# CONFIG_WATCHDOG_NOWAYOUT is not set
+# CONFIG_ACQUIRE_WDT is not set
+# CONFIG_ADVANTECH_WDT is not set
+# CONFIG_ALIM1535_WDT is not set
+# CONFIG_ALIM7101_WDT is not set
+# CONFIG_SC520_WDT is not set
+# CONFIG_PCWATCHDOG is not set
+# CONFIG_EUROTECH_WDT is not set
+# CONFIG_IB700_WDT is not set
+# CONFIG_WAFER_WDT is not set
+# CONFIG_I810_TCO is not set
+# CONFIG_MIXCOMWD is not set
+# CONFIG_60XX_WDT is not set
+# CONFIG_SC1200_WDT is not set
+# CONFIG_SCx200_WDT is not set
+CONFIG_SOFT_WATCHDOG=y
+# CONFIG_W83877F_WDT is not set
+# CONFIG_WDT is not set
+# CONFIG_WDTPCI is not set
+# CONFIG_MACHZ_WDT 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_MIPS_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
+
+#
+# File systems
+#
+# CONFIG_QUOTA is not set
+# CONFIG_QFMT_V2 is not set
+CONFIG_AUTOFS_FS=y
+CONFIG_AUTOFS4_FS=y
+# 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 is not set
+# CONFIG_MSDOS_FS is not set
+# CONFIG_UMSDOS_FS is not set
+# CONFIG_VFAT_FS is not set
+# CONFIG_EFS_FS is not set
+# CONFIG_JFFS_FS is not set
+# CONFIG_JFFS2_FS is not set
+CONFIG_YAFFS_FS=y
+# CONFIG_CRAMFS is not set
+CONFIG_TMPFS=y
+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=y
+CONFIG_NFS_V3=y
+# 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=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 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 is not set
+
+#
+# Sound
+#
+# CONFIG_SOUND 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
+
+#
+# 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_REED_SOLOMON is not set
+# CONFIG_FW_LOADER is not set