add initial support for the crisarchitecture used on foxboards to openwrt
[openwrt/openwrt.git] / target / linux / etrax-2.6 / patches / cris / 002-arch-cris.patch
diff --git a/target/linux/etrax-2.6/patches/cris/002-arch-cris.patch b/target/linux/etrax-2.6/patches/cris/002-arch-cris.patch
new file mode 100644 (file)
index 0000000..ee35210
--- /dev/null
@@ -0,0 +1,24914 @@
+diff -urN linux-2.6.19.2.old/arch/cris/Kconfig linux-2.6.19.2.dev/arch/cris/Kconfig
+--- linux-2.6.19.2.old/arch/cris/Kconfig       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/Kconfig       2007-01-17 18:17:18.000000000 +0100
+@@ -16,6 +16,10 @@
+ config RWSEM_XCHGADD_ALGORITHM
+       bool
++config GENERIC_IOMAP
++       bool
++       default y
++
+ config GENERIC_FIND_NEXT_BIT
+       bool
+       default y
+@@ -42,6 +46,29 @@
+ source "fs/Kconfig.binfmt"
++config GENERIC_HARDIRQS
++      bool
++      default y
++
++config SMP
++       bool "SMP"
++       help
++         SMP support. Always Say N.
++
++config NR_CPUS
++       int
++       depends on SMP
++       default 2
++
++config SCHED_MC
++      bool "Multi-core scheduler support"
++      depends on SMP
++      default y
++      help
++        Multi-core scheduler support improves the CPU scheduler's decision
++        making when dealing with multi-core CPU chips at a cost of slightly
++        increased overhead in some places. If unsure say N here.
++
+ config ETRAX_CMDLINE
+       string "Kernel command line"
+       default "root=/dev/mtdblock3"
+@@ -70,17 +97,11 @@
+        timers).
+        This is needed if CONFIG_ETRAX_SERIAL_FAST_TIMER is enabled.
+-config PREEMPT
+-      bool "Preemptible Kernel"
+-      help
+-        This option reduces the latency of the kernel when reacting to
+-        real-time or interactive events by allowing a low priority process to
+-        be preempted even if it is in kernel mode executing a system call.
+-        This allows applications to run more reliably even when the system is
+-        under load.
++config OOM_REBOOT
++       bool "Enable reboot at out of memory"
+-        Say Y here if you are building a kernel for a desktop, embedded
+-        or real-time system.  Say N if you are unsure.
++source "kernel/Kconfig.preempt"
++source "kernel/Kconfig.sched"
+ source mm/Kconfig
+@@ -107,6 +128,16 @@
+       help
+         Support the xsim ETRAX Simulator.
++config ETRAXFS
++      bool "ETRAX-FS-V32"
++      help
++        Support CRIS V32.
++
++config ETRAXFS_SIM
++      bool "ETRAX-FS-V32-Simulator"
++      help
++        Support CRIS V32 VCS simualtor.
++
+ endchoice
+ config ETRAX_ARCH_V10
+@@ -114,6 +145,11 @@
+        default y if ETRAX100LX || ETRAX100LX_V2
+        default n if !(ETRAX100LX || ETRAX100LX_V2)
++config ETRAX_ARCH_V32
++       bool
++       default y if ETRAXFS || ETRAXFS_SIM
++       default n if !(ETRAXFS || ETRAXFS_SIM) 
++
+ config ETRAX_DRAM_SIZE
+       int "DRAM size (dec, in MB)"
+       default "8"
+@@ -121,12 +157,23 @@
+         Size of DRAM (decimal in MB) typically 2, 8 or 16.
+ config ETRAX_FLASH_BUSWIDTH
+-      int "Buswidth of flash in bytes"
++      int "Buswidth of NOR flash in bytes"
+       default "2"
+       help
+-        Width in bytes of the Flash bus (1, 2 or 4). Is usually 2.
++        Width in bytes of the NOR Flash bus (1, 2 or 4). Is usually 2.
+-source arch/cris/arch-v10/Kconfig
++config ETRAX_NANDFLASH_BUSWIDTH
++      int "Buswidth of NAND flash in bytes"
++      default "1"
++      help
++        Width in bytes of the NAND flash (1 or 2).
++
++config ETRAX_FLASH1_SIZE
++       int "FLASH1 size (dec, in MB. 0 = Unknown)"
++       default "0"
++
++# arch/cris/arch is a symlink to correct arch (arch-v10 or arch-v32)
++source arch/cris/arch/Kconfig
+ endmenu
+@@ -134,7 +181,8 @@
+ # bring in ETRAX built-in drivers
+ menu "Drivers for built-in interfaces"
+-source arch/cris/arch-v10/drivers/Kconfig
++# arch/cris/arch is a symlink to correct arch (arch-v10 or arch-v32)
++source arch/cris/arch/drivers/Kconfig
+ endmenu
+@@ -181,6 +229,10 @@
+ source "sound/Kconfig"
++source "drivers/pcmcia/Kconfig"
++
++source "drivers/pci/Kconfig"
++
+ source "drivers/usb/Kconfig"
+ source "arch/cris/Kconfig.debug"
+diff -urN linux-2.6.19.2.old/arch/cris/Kconfig.debug linux-2.6.19.2.dev/arch/cris/Kconfig.debug
+--- linux-2.6.19.2.old/arch/cris/Kconfig.debug 2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/Kconfig.debug 2005-10-31 09:48:02.000000000 +0100
+@@ -1,14 +1,15 @@
+ menu "Kernel hacking"
++source "lib/Kconfig.debug"
++
+ #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
++
+ config PROFILING
+       bool "Kernel profiling support"
+ config SYSTEM_PROFILER
+       bool "System profiling support"
+-source "lib/Kconfig.debug"
+-
+ config ETRAX_KGDB
+       bool "Use kernel GDB debugger"
+       depends on DEBUG_KERNEL
+diff -urN linux-2.6.19.2.old/arch/cris/Makefile linux-2.6.19.2.dev/arch/cris/Makefile
+--- linux-2.6.19.2.old/arch/cris/Makefile      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/Makefile      2006-11-29 17:05:40.000000000 +0100
+@@ -1,4 +1,4 @@
+-# $Id: Makefile,v 1.28 2005/03/17 10:44:37 larsv Exp $
++# $Id: Makefile,v 1.41 2006/11/29 16:05:40 ricardw Exp $
+ # cris/Makefile
+ #
+ # This file is included by the global makefile so that you can add your own
+@@ -10,14 +10,11 @@
+ # License.  See the file "COPYING" in the main directory of this archive
+ # for more details.
+-# A bug in ld prevents us from having a (constant-value) symbol in a
+-# "ORIGIN =" or "LENGTH =" expression.
+-
+ arch-y := v10
+ arch-$(CONFIG_ETRAX_ARCH_V10) := v10
+ arch-$(CONFIG_ETRAX_ARCH_V32) := v32
+-# No config avaiable for make clean etc
++# No config available for make clean etc
+ ifneq ($(arch-y),)
+ SARCH := arch-$(arch-y)
+ else
+@@ -52,79 +49,58 @@
+ # cris object files path
+ OBJ_ARCH              = $(objtree)/arch/$(ARCH)
+-target_boot_arch_dir  = $(OBJ_ARCH)/$(SARCH)/boot
+-target_boot_dir       = $(OBJ_ARCH)/boot
+-src_boot_dir          = $(SRC_ARCH)/boot
+-target_compressed_dir = $(OBJ_ARCH)/boot/compressed
+-src_compressed_dir    = $(SRC_ARCH)/boot/compressed
+-target_rescue_dir     = $(OBJ_ARCH)/boot/rescue
+-src_rescue_dir        = $(SRC_ARCH)/boot/rescue
+-
+-export target_boot_arch_dir target_boot_dir src_boot_dir target_compressed_dir src_compressed_dir target_rescue_dir src_rescue_dir
+-
+-vmlinux.bin: vmlinux
+-      $(OBJCOPY) $(OBJCOPYFLAGS) vmlinux vmlinux.bin
+-
+-timage: vmlinux.bin
+-      cat vmlinux.bin cramfs.img >timage
+-
+-simimage: timage
+-      cp vmlinux.bin simvmlinux.bin
+-
+-# the following will remake timage without compiling the kernel
+-# it does of course require that all object files exist...
+-
+-cramfs:
+-## cramfs      - Creates a cramfs image
+-      mkcramfs -b 8192 -m romfs_meta.txt root cramfs.img
+-      cat vmlinux.bin cramfs.img >timage
+-
+-clinux: vmlinux.bin decompress.bin rescue.bin
+-
+-decompress.bin: $(target_boot_dir)
+-      @$(MAKE) -f $(src_compressed_dir)/Makefile $(target_compressed_dir)/decompress.bin
+-
+-$(target_rescue_dir)/rescue.bin: $(target_boot_dir)
+-      @$(MAKE) -f $(src_rescue_dir)/Makefile $(target_rescue_dir)/rescue.bin
++boot := arch/$(ARCH)/boot
++MACHINE := arch/$(ARCH)/$(SARCH)
+-zImage: $(target_boot_dir) vmlinux.bin $(target_rescue_dir)/rescue.bin
+-## zImage     - Compressed kernel (gzip)
+-      @$(MAKE) -f $(src_boot_dir)/Makefile zImage
++all: zImage
+-$(target_boot_dir): $(target_boot_arch_dir)
+-      ln -sfn $< $@
+-
+-$(target_boot_arch_dir):
+-      mkdir -p $@
+-
+-compressed: zImage
+-
+-archmrproper:
+-archclean:
+-      @if [ -d arch/$(ARCH)/boot ]; then \
+-              $(MAKE) $(clean)=arch/$(ARCH)/boot ; \
+-      fi
+-      rm -f timage vmlinux.bin decompress.bin rescue.bin cramfs.img
+-      rm -rf $(LD_SCRIPT).tmp
++zImage Image: vmlinux 
++      $(Q)$(MAKE) $(build)=$(boot) MACHINE=$(MACHINE) $(boot)/$@
+ archprepare: $(SRC_ARCH)/.links $(srctree)/include/asm-$(ARCH)/.arch
+ # Create some links to make all tools happy
+ $(SRC_ARCH)/.links:
+       @rm -rf $(SRC_ARCH)/drivers
+-      @ln -sfn $(SRC_ARCH)/$(SARCH)/drivers $(SRC_ARCH)/drivers
++      @ln -sfn $(SARCH)/drivers $(SRC_ARCH)/drivers
+       @rm -rf $(SRC_ARCH)/boot
+-      @ln -sfn $(SRC_ARCH)/$(SARCH)/boot $(SRC_ARCH)/boot
++      @ln -sfn $(SARCH)/boot $(SRC_ARCH)/boot
+       @rm -rf $(SRC_ARCH)/lib
+-      @ln -sfn $(SRC_ARCH)/$(SARCH)/lib $(SRC_ARCH)/lib
+-      @ln -sfn $(SRC_ARCH)/$(SARCH) $(SRC_ARCH)/arch
+-      @ln -sfn $(SRC_ARCH)/$(SARCH)/vmlinux.lds.S $(SRC_ARCH)/kernel/vmlinux.lds.S
+-      @ln -sfn $(SRC_ARCH)/$(SARCH)/kernel/asm-offsets.c $(SRC_ARCH)/kernel/asm-offsets.c
++      @ln -sfn $(SARCH)/lib $(SRC_ARCH)/lib
++      @rm -rf $(SRC_ARCH)/arch
++      @ln -sfn $(SARCH) $(SRC_ARCH)/arch
++      @rm -rf $(SRC_ARCH)/kernel/vmlinux.lds.S
++      @ln -sfn ../$(SARCH)/vmlinux.lds.S $(SRC_ARCH)/kernel/vmlinux.lds.S
++      @rm -rf $(SRC_ARCH)/kernel/asm-offsets.c
++      @ln -sfn ../$(SARCH)/kernel/asm-offsets.c $(SRC_ARCH)/kernel/asm-offsets.c
+       @touch $@
+ # Create link to sub arch includes
+ $(srctree)/include/asm-$(ARCH)/.arch: $(wildcard include/config/arch/*.h)
+-      @echo '  Making $(srctree)/include/asm-$(ARCH)/arch -> $(srctree)/include/asm-$(ARCH)/$(SARCH) symlink'
++      @echo '  SYMLINK include/asm-$(ARCH)/arch -> include/asm-$(ARCH)/$(SARCH)'
+       @rm -f include/asm-$(ARCH)/arch
+-      @ln -sf $(srctree)/include/asm-$(ARCH)/$(SARCH) $(srctree)/include/asm-$(ARCH)/arch
++      @ln -sf $(SARCH) $(srctree)/include/asm-$(ARCH)/arch
+       @touch $@
++
++archclean:
++      $(Q)if [ -e arch/$(ARCH)/boot ]; then \
++              $(MAKE) $(clean)=arch/$(ARCH)/boot; \
++      fi
++
++CLEAN_FILES += \
++      $(MACHINE)/boot/zImage \
++      $(SRC_ARCH)/.links \
++      $(srctree)/include/asm-$(ARCH)/.arch
++
++MRPROPER_FILES += \
++      $(SRC_ARCH)/drivers \
++      $(SRC_ARCH)/boot \
++      $(SRC_ARCH)/lib \
++      $(SRC_ARCH)/arch \
++      $(SRC_ARCH)/kernel/vmlinux.lds.S \
++      $(SRC_ARCH)/kernel/asm-offsets.c
++
++define archhelp
++  echo  '* zImage        - Compressed kernel image (arch/$(ARCH)/boot/zImage)'
++  echo  '* Image         - Uncompressed kernel image (arch/$(ARCH)/boot/Image)'
++endef
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/README.mm linux-2.6.19.2.dev/arch/cris/arch-v10/README.mm
+--- linux-2.6.19.2.old/arch/cris/arch-v10/README.mm    2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/README.mm    2005-08-23 11:44:32.000000000 +0200
+@@ -3,6 +3,9 @@
+ HISTORY:
+ $Log: README.mm,v $
++Revision 1.2  2005/08/23 09:44:32  starvik
++extern inline -> static inline. Patch provided by Adrian Bunk <bunk@stusta.de>
++
+ Revision 1.1  2001/12/17 13:59:27  bjornw
+ Initial revision
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/boot/Makefile linux-2.6.19.2.dev/arch/cris/arch-v10/boot/Makefile
+--- linux-2.6.19.2.old/arch/cris/arch-v10/boot/Makefile        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/boot/Makefile        2006-11-29 17:05:40.000000000 +0100
+@@ -1,13 +1,21 @@
+ #
+-# arch/cris/boot/Makefile
++# arch/cris/arch-v10/boot/Makefile
+ #
+-target = $(target_boot_dir)
+-src    = $(src_boot_dir)
+-zImage: compressed/vmlinuz
++OBJCOPY = objcopy-cris
++OBJCOPYFLAGS = -O binary --remove-section=.bss
+-compressed/vmlinuz:
+-      @$(MAKE) -f $(src)/compressed/Makefile $(target_compressed_dir)/vmlinuz
++subdir- := compressed rescue
++targets := Image
+-clean:
+-      @$(MAKE) -f $(src)/compressed/Makefile clean
++$(obj)/Image: vmlinux FORCE
++      $(call if_changed,objcopy)
++      @echo '  Kernel: $@ is ready'
++
++$(obj)/compressed/vmlinux: $(obj)/Image FORCE
++      $(Q)$(MAKE) $(build)=$(obj)/compressed $@
++      $(Q)$(MAKE) $(build)=$(obj)/rescue $(obj)/rescue/rescue.bin
++
++$(obj)/zImage:  $(obj)/compressed/vmlinux
++      @cp $< $@
++      @echo '  Kernel: $@ is ready'
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/boot/compressed/Makefile linux-2.6.19.2.dev/arch/cris/arch-v10/boot/compressed/Makefile
+--- linux-2.6.19.2.old/arch/cris/arch-v10/boot/compressed/Makefile     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/boot/compressed/Makefile     2006-10-11 17:47:04.000000000 +0200
+@@ -1,45 +1,34 @@
+ #
+-# create a compressed vmlinuz image from the binary vmlinux.bin file
++# arch/cris/arch-v10/boot/compressed/Makefile
+ #
+-target = $(target_compressed_dir)
+-src    = $(src_compressed_dir)
+ CC = gcc-cris -melf $(LINUXINCLUDE)
+ CFLAGS = -O2
+ LD = ld-cris
++LDFLAGS = -T $(obj)/decompress.ld
++OBJECTS = $(obj)/head.o $(obj)/misc.o
+ OBJCOPY = objcopy-cris
+ OBJCOPYFLAGS = -O binary --remove-section=.bss
+-OBJECTS = $(target)/head.o $(target)/misc.o
+-# files to compress
+-SYSTEM = $(objtree)/vmlinux.bin
++quiet_cmd_image = BUILD   $@
++cmd_image = cat $(obj)/decompress.bin $(obj)/piggy.gz > $@
+-all: $(target_compressed_dir)/vmlinuz
++targets := vmlinux piggy.gz decompress.o decompress.bin
+-$(target)/decompress.bin: $(OBJECTS)
+-      $(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS)
+-      $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin
++$(obj)/decompress.o: $(OBJECTS) FORCE
++      $(call if_changed,ld)
+-# Create vmlinuz image in top-level build directory
+-$(target_compressed_dir)/vmlinuz: $(target) piggy.img $(target)/decompress.bin
+-      @echo "  COMPR   vmlinux.bin --> vmlinuz"
+-      @cat $(target)/decompress.bin piggy.img > $(target_compressed_dir)/vmlinuz
+-      @rm -f piggy.img
++$(obj)/decompress.bin: $(obj)/decompress.o FORCE
++      $(call if_changed,objcopy)
+-$(target)/head.o: $(src)/head.S
+-      $(CC) -D__ASSEMBLY__ -traditional -c $< -o $@
++$(obj)/head.o: $(obj)/head.S .config
++      @$(CC) -D__ASSEMBLY__ -traditional -c $< -o $@
+-$(target)/misc.o: $(src)/misc.c
+-      $(CC) -D__KERNEL__ -c $< -o $@
++$(obj)/misc.o: $(obj)/misc.c .config
++      @$(CC) -D__KERNEL__ -c $< -o $@
+-# gzip the kernel image
+-
+-piggy.img: $(SYSTEM)
+-      @cat $(SYSTEM) | gzip -f -9 > piggy.img
+-
+-$(target):
+-      mkdir -p $(target)
+-
+-clean:
+-      rm -f piggy.img $(objtree)/vmlinuz
++$(obj)/vmlinux: $(obj)/piggy.gz $(obj)/decompress.bin FORCE
++      $(call if_changed,image)
++$(obj)/piggy.gz: $(obj)/../Image FORCE
++      $(call if_changed,gzip)
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/boot/compressed/misc.c linux-2.6.19.2.dev/arch/cris/arch-v10/boot/compressed/misc.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/boot/compressed/misc.c       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/boot/compressed/misc.c       2006-10-13 14:43:10.000000000 +0200
+@@ -1,7 +1,7 @@
+ /*
+  * misc.c
+  *
+- * $Id: misc.c,v 1.6 2003/10/27 08:04:31 starvik Exp $
++ * $Id: misc.c,v 1.7 2006/10/13 12:43:10 starvik Exp $
+  * 
+  * This is a collection of several routines from gzip-1.0.3 
+  * adapted for Linux.
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/Makefile linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/Makefile
+--- linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/Makefile 2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/Makefile 2006-11-30 11:42:39.000000000 +0100
+@@ -1,56 +1,38 @@
+ #
+-# Makefile for rescue code
++# Makefile for rescue (bootstrap) code
+ #
+-target = $(target_rescue_dir)
+-src    = $(src_rescue_dir)
+ CC = gcc-cris -mlinux $(LINUXINCLUDE)
+ CFLAGS = -O2
+-LD = gcc-cris -mlinux -nostdlib
++AFLAGS = -traditional
++LD = gcc-cris -mlinux -nostdlib 
++LDFLAGS = -T $(obj)/rescue.ld
+ OBJCOPY = objcopy-cris
+ OBJCOPYFLAGS = -O binary --remove-section=.bss
++obj-y = head.o
++OBJECT = $(obj)/$(obj-y)
+-all: $(target)/rescue.bin $(target)/testrescue.bin $(target)/kimagerescue.bin
++targets := rescue.o rescue.bin
+-$(target)/rescue.bin: $(target) $(target)/head.o
+-      $(LD) -T $(src)/rescue.ld -o $(target)/rescue.o $(target)/head.o
+-      $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/rescue.o $(target)/rescue.bin
+-# Place a copy in top-level build directory
+-      cp -p $(target)/rescue.bin $(objtree)
++$(obj)/rescue.o: $(OBJECT) FORCE
++      $(call if_changed,ld)
+-$(target)/testrescue.bin: $(target) $(target)/testrescue.o
+-      $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/testrescue.o tr.bin
++$(obj)/rescue.bin: $(obj)/rescue.o FORCE
++      $(call if_changed,objcopy)
++      cp -p $(obj)/rescue.bin $(objtree)
++
++$(obj)/testrescue.bin: $(obj)/testrescue.o
++      $(OBJCOPY) $(OBJCOPYFLAGS) $(obj)/testrescue.o tr.bin
+ # Pad it to 784 bytes
+       dd if=/dev/zero of=tmp2423 bs=1 count=784
+       cat tr.bin tmp2423 >testrescue_tmp.bin
+-      dd if=testrescue_tmp.bin of=$(target)/testrescue.bin bs=1 count=784
++      dd if=testrescue_tmp.bin of=$(obj)/testrescue.bin bs=1 count=784
+       rm tr.bin tmp2423 testrescue_tmp.bin
+-$(target)/kimagerescue.bin: $(target) $(target)/kimagerescue.o
+-      $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/kimagerescue.o ktr.bin
++$(obj)/kimagerescue.bin: $(obj)/kimagerescue.o
++      $(OBJCOPY) $(OBJCOPYFLAGS) $(obj)/kimagerescue.o ktr.bin
+ # Pad it to 784 bytes, that's what the rescue loader expects
+       dd if=/dev/zero of=tmp2423 bs=1 count=784
+       cat ktr.bin tmp2423 >kimagerescue_tmp.bin
+-      dd if=kimagerescue_tmp.bin of=$(target)/kimagerescue.bin bs=1 count=784
++      dd if=kimagerescue_tmp.bin of=$(obj)/kimagerescue.bin bs=1 count=784
+       rm ktr.bin tmp2423 kimagerescue_tmp.bin
+-
+-$(target):
+-      mkdir -p $(target)
+-
+-$(target)/head.o: $(src)/head.S
+-      $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
+-
+-$(target)/testrescue.o: $(src)/testrescue.S
+-      $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
+-
+-$(target)/kimagerescue.o: $(src)/kimagerescue.S
+-      $(CC) -D__ASSEMBLY__ -traditional -c $< -o $*.o
+-
+-clean:
+-      rm -f $(target)/*.o $(target)/*.bin
+-
+-fastdep:
+-
+-modules:
+-
+-modules-install:
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/head.S linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/head.S
+--- linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/head.S   2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/head.S   2006-10-13 14:43:10.000000000 +0200
+@@ -1,4 +1,4 @@
+-/* $Id: head.S,v 1.7 2005/03/07 12:11:06 starvik Exp $
++/* $Id: head.S,v 1.9 2006/10/13 12:43:10 starvik Exp $
+  * 
+  * Rescue code, made to reside at the beginning of the
+  * flash-memory. when it starts, it checks a partition
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/kimagerescue.S linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/kimagerescue.S
+--- linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/kimagerescue.S   2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/kimagerescue.S   2006-11-29 17:05:41.000000000 +0100
+@@ -1,4 +1,4 @@
+-/* $Id: kimagerescue.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $
++/* $Id: kimagerescue.S,v 1.3 2006/11/29 16:05:41 ricardw Exp $
+  * 
+  * Rescue code to be prepended on a kimage and copied to the
+  * rescue serial port.
+@@ -7,7 +7,7 @@
+  */
+ #define ASSEMBLER_MACROS_ONLY
+-#include <asm/sv_addr_ag.h>
++#include <asm/arch/sv_addr_ag.h>
+ #define CODE_START 0x40004000
+ #define CODE_LENGTH 784
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/testrescue.S linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/testrescue.S
+--- linux-2.6.19.2.old/arch/cris/arch-v10/boot/rescue/testrescue.S     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/boot/rescue/testrescue.S     2006-11-29 17:05:41.000000000 +0100
+@@ -1,4 +1,4 @@
+-/* $Id: testrescue.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $
++/* $Id: testrescue.S,v 1.2 2006/11/29 16:05:41 ricardw Exp $
+  *
+  * Simple testcode to download by the rescue block.
+  * Just lits some LEDs to show it was downloaded correctly.
+@@ -7,7 +7,7 @@
+  */
+ #define ASSEMBLER_MACROS_ONLY
+-#include <asm/sv_addr_ag.h>
++#include <asm/arch/sv_addr_ag.h>
+       .text
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/defconfig linux-2.6.19.2.dev/arch/cris/arch-v10/defconfig
+--- linux-2.6.19.2.old/arch/cris/arch-v10/defconfig    2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/defconfig    2006-01-03 15:48:23.000000000 +0100
+@@ -106,7 +106,6 @@
+ CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C=y
+ # CONFIG_ETRAX_I2C_EEPROM is not set
+ CONFIG_ETRAX_GPIO=y
+-CONFIG_ETRAX_PA_BUTTON_BITMASK=02
+ CONFIG_ETRAX_PA_CHANGEABLE_DIR=00
+ CONFIG_ETRAX_PA_CHANGEABLE_BITS=FF
+ CONFIG_ETRAX_PB_CHANGEABLE_DIR=00
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/Kconfig linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/Kconfig
+--- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/Kconfig      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/Kconfig      2007-01-09 10:29:18.000000000 +0100
+@@ -6,6 +6,12 @@
+         This option enables the ETRAX 100LX built-in 10/100Mbit Ethernet
+         controller.
++config ETRAX_NO_PHY
++       bool "PHY not present"
++       depends on ETRAX_ETHERNET
++       help
++         Enable if PHY is not present.
++
+ choice
+       prompt "Network LED behavior"
+       depends on ETRAX_ETHERNET
+@@ -90,7 +96,7 @@
+ config ETRAX_SERIAL_PORT0_DMA6_OUT
+       bool "DMA 6"
+-
++      depends on !ETRAX_DEBUG_PORT0
+ endchoice
+ choice
+@@ -103,7 +109,7 @@
+ config ETRAX_SERIAL_PORT0_DMA7_IN
+       bool "DMA 7"
+-
++      depends on !ETRAX_DEBUG_PORT0
+ endchoice
+ choice
+@@ -204,7 +210,7 @@
+ config ETRAX_SERIAL_PORT1_DMA8_OUT
+       bool "DMA 8"
+-
++      depends on !ETRAX_DEBUG_PORT1
+ endchoice
+ choice
+@@ -217,7 +223,7 @@
+ config ETRAX_SERIAL_PORT1_DMA9_IN
+       bool "DMA 9"
+-
++      depends on !ETRAX_DEBUG_PORT1
+ endchoice
+ choice
+@@ -321,7 +327,7 @@
+ config ETRAX_SERIAL_PORT2_DMA2_OUT
+       bool "DMA 2"
+-
++      depends on !ETRAX_DEBUG_PORT2
+ endchoice
+ choice
+@@ -334,7 +340,7 @@
+ config ETRAX_SERIAL_PORT2_DMA3_IN
+       bool "DMA 3"
+-
++      depends on !ETRAX_DEBUG_PORT2
+ endchoice
+ choice
+@@ -435,7 +441,7 @@
+ config ETRAX_SERIAL_PORT3_DMA4_OUT
+       bool "DMA 4"
+-
++      depends on !ETRAX_DEBUG_PORT3
+ endchoice
+ choice
+@@ -448,7 +454,7 @@
+ config ETRAX_SERIAL_PORT3_DMA5_IN
+       bool "DMA 5"
+-
++      depends on !ETRAX_DEBUG_PORT3
+ endchoice
+ choice
+@@ -514,8 +520,7 @@
+       bool "RS-485 support"
+       depends on ETRAX_SERIAL
+       help
+-        Enables support for RS-485 serial communication.  For a primer on
+-        RS-485, see <http://www.hw.cz/english/docs/rs485/rs485.html>.
++        Enables support for RS-485 serial communication.
+ config ETRAX_RS485_ON_PA
+       bool "RS-485 mode on PA"
+@@ -541,6 +546,27 @@
+         loopback.  Not all products are able to do this in software only.
+         Axis 2400/2401 must disable receiver.
++config ETRAX_SYNCHRONOUS_SERIAL
++      bool "Synchronous serial port driver"
++      help
++          Select this to enable the synchronous serial port driver.
++
++config ETRAX_SYNCHRONOUS_SERIAL_PORT0
++       bool "Synchronous serial port 0 enabled (sser1)"
++       depends on ETRAX_SYNCHRONOUS_SERIAL
++
++config ETRAX_SYNCHRONOUS_SERIAL0_DMA
++       bool "Use DMA for synchronous serial port 0"
++       depends on ETRAX_SYNCHRONOUS_SERIAL_PORT0
++
++config ETRAX_SYNCHRONOUS_SERIAL_PORT1
++       bool "Synchronous serial port 1 enabled (sser3)"
++       depends on ETRAX_SYNCHRONOUS_SERIAL
++
++config ETRAX_SYNCHRONOUS_SERIAL1_DMA
++       bool "Use DMA for synchronous serial port 1"
++       depends on ETRAX_SYNCHRONOUS_SERIAL_PORT1
++
+ config ETRAX_IDE
+       bool "ATA/IDE support"
+       select IDE
+@@ -604,8 +630,7 @@
+       select MTD
+       select MTD_CFI
+       select MTD_CFI_AMDSTD
+-      select MTD_OBSOLETE_CHIPS
+-      select MTD_AMDSTD
++      select MTD_JEDECPROBE
+       select MTD_CHAR
+       select MTD_BLOCK
+       select MTD_PARTITIONS
+@@ -615,6 +640,15 @@
+         This option enables MTD mapping of flash devices.  Needed to use
+         flash memories.  If unsure, say Y.
++config ETRAX_AXISFLASHMAP_MTD0WHOLE
++      bool "MTD0 is whole boot flash device"
++      depends on ETRAX_AXISFLASHMAP
++      default N
++      help
++        When this option is not set, mtd0 refers to the first partition
++        on the boot flash device. When set, mtd0 refers to the whole
++        device, with mtd1 referring to the first partition etc.
++
+ config ETRAX_PTABLE_SECTOR
+       int "Byte-offset of partition table sector"
+       depends on ETRAX_AXISFLASHMAP
+@@ -715,19 +749,6 @@
+         Remember that you need to setup the port directions appropriately in
+         the General configuration.
+-config ETRAX_PA_BUTTON_BITMASK
+-      hex "PA-buttons bitmask"
+-      depends on ETRAX_GPIO
+-      default "02"
+-      help
+-        This is a bitmask with information about what bits on PA that
+-        are used for buttons.
+-        Most products has a so called TEST button on PA1, if that's true
+-        use 02 here.
+-        Use 00 if there are no buttons on PA.
+-        If the bitmask is <> 00 a button driver will be included in the gpio
+-        driver. ETRAX general I/O support must be enabled.
+-
+ config ETRAX_PA_CHANGEABLE_DIR
+       hex "PA user changeable dir mask"
+       depends on ETRAX_GPIO
+@@ -768,6 +789,40 @@
+         Bit set = changeable.
+         You probably want 00 here.
++config ETRAX_DEF_R_PORT_G_DIR
++        bool "Port G Output"
++      help
++        CONFIG_ETRAX_DEF_R_PORT_G_DIR:
++        Set the direction of specified pins to output.
++
++config ETRAX_DEF_R_PORT_G0_DIR_OUT
++        bool "G0"
++        depends on ETRAX_DEF_R_PORT_G_DIR
++      help
++        CONFIG_ETRAX_DEF_R_PORT_G0_DIR_OUT:
++        Set G0 to output.
++
++config ETRAX_DEF_R_PORT_G8_15_DIR_OUT
++        bool "G8-G15"
++        depends on ETRAX_DEF_R_PORT_G_DIR
++      help
++        CONFIG_ETRAX_DEF_R_PORT_G8_15_DIR_OUT:
++        Set G8-G15 to output.
++
++config ETRAX_DEF_R_PORT_G16_23_DIR_OUT
++        bool "G16-G23"
++        depends on ETRAX_DEF_R_PORT_G_DIR
++      help
++        CONFIG_ETRAX_DEF_R_PORT_G16_23_DIR_OUT:
++        Set G16-G23 to output.
++
++config ETRAX_DEF_R_PORT_G24_DIR_OUT
++        bool "G24"
++        depends on ETRAX_DEF_R_PORT_G_DIR
++      help
++        CONFIG_ETRAX_DEF_R_PORT_G24_DIR_OUT:
++        Set G24 to output.
++
+ config ETRAX_RTC
+       bool "Real Time Clock support"
+       depends on ETRAX_ARCH_V10
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/Makefile linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/Makefile
+--- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/Makefile     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/Makefile     2005-12-12 10:05:46.000000000 +0100
+@@ -8,5 +8,5 @@
+ obj-$(CONFIG_ETRAX_GPIO)              += gpio.o
+ obj-$(CONFIG_ETRAX_DS1302)              += ds1302.o
+ obj-$(CONFIG_ETRAX_PCF8563)           += pcf8563.o
+-
++obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL)  += sync_serial.o
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/axisflashmap.c linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/axisflashmap.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/axisflashmap.c       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/axisflashmap.c       2006-11-22 13:26:55.000000000 +0100
+@@ -11,6 +11,26 @@
+  * partition split defined below.
+  *
+  * $Log: axisflashmap.c,v $
++ * Revision 1.17  2006/11/22 12:26:55  ricardw
++ * Added CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE option which when enabled puts mtd0
++ * as whole device, with first partition at mtd1, etc.
++ *
++ * Revision 1.16  2006/10/30 15:17:57  pkj
++ * Avoid a compiler warning.
++ *
++ * Revision 1.15  2006/10/13 12:43:10  starvik
++ * Merge of 2.6.18
++ *
++ * Revision 1.14  2006/08/30 13:20:00  karljope
++ * Do not use deprecated amd_flash to probe flash memory.
++ * Probe for flash chip with CFI first and if no chip was found try jedec_probe.
++ *
++ * Revision 1.13  2006/01/04 06:09:45  starvik
++ * Merge of Linux 2.6.15
++ *
++ * Revision 1.12  2005/06/21 09:13:06  starvik
++ * Change const char* to const char[] to save space (from domen@coderock.org).
++ *
+  * Revision 1.11  2004/11/15 10:27:14  starvik
+  * Corrected typo (Thanks to Milton Miller <miltonm@bga.com>).
+  *
+@@ -300,6 +320,15 @@
+       },
+ };
++#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE
++/* Main flash device */
++static struct mtd_partition main_partition = {
++      .name = "main",
++      .size = 0,
++      .offset = 0
++};
++#endif
++
+ /*
+  * Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash
+  * chips in that order (because the amd_flash-driver is faster).
+@@ -312,12 +341,12 @@
+                "%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n",
+              map_cs->name, map_cs->size, map_cs->map_priv_1);
+-#ifdef CONFIG_MTD_AMDSTD
+-      mtd_cs = do_map_probe("amd_flash", map_cs);
+-#endif
+ #ifdef CONFIG_MTD_CFI
++      mtd_cs = do_map_probe("cfi_probe", map_cs);
++#endif
++#ifdef CONFIG_MTD_JEDECPROBE
+       if (!mtd_cs) {
+-              mtd_cs = do_map_probe("cfi_probe", map_cs);
++              mtd_cs = do_map_probe("jedec_probe", map_cs);
+       }
+ #endif
+@@ -396,7 +425,7 @@
+       struct partitiontable_head *ptable_head = NULL;
+       struct partitiontable_entry *ptable;
+       int use_default_ptable = 1; /* Until proven otherwise. */
+-      const char *pmsg = "  /dev/flash%d at 0x%08x, size 0x%08x\n";
++      const char pmsg[] = "  /dev/flash%d at 0x%08x, size 0x%08x\n";
+       if (!(mymtd = flash_probe())) {
+               /* There's no reason to use this module if no flash chip can
+@@ -491,6 +520,16 @@
+               pidx++;
+       }
++#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE
++      if (mymtd) {
++              main_partition.size = mymtd->size;
++                err = add_mtd_partitions(mymtd, &main_partition, 1);
++              if (err)
++                      panic("axisflashmap: Could not initialize "
++                            "partition for whole main mtd device!\n");
++      }
++#endif
++
+         if (mymtd) {
+               if (use_default_ptable) {
+                       printk(KERN_INFO " Using default partition table.\n");
+@@ -524,7 +563,7 @@
+               }
+               printk(KERN_INFO " Adding RAM partition for romfs image:\n");
+-              printk(pmsg, pidx, romfs_start, romfs_length);
++              printk(pmsg, pidx, (unsigned)romfs_start, (unsigned)romfs_length);
+               err = mtdram_init_device(mtd_ram, (void*)romfs_start, 
+                                        romfs_length, "romfs");
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/ds1302.c linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/ds1302.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/ds1302.c     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/ds1302.c     2006-10-27 16:31:23.000000000 +0200
+@@ -6,136 +6,9 @@
+ *!
+ *! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init
+ *!
+-*! $Log: ds1302.c,v $
+-*! Revision 1.18  2005/01/24 09:11:26  mikaelam
+-*! Minor changes to get DS1302 RTC chip driver to work
+-*!
+-*! Revision 1.17  2005/01/05 06:11:22  starvik
+-*! No need to do local_irq_disable after local_irq_save.
+-*!
+-*! Revision 1.16  2004/12/13 12:21:52  starvik
+-*! Added I/O and DMA allocators from Linux 2.4
+-*!
+-*! Revision 1.14  2004/08/24 06:48:43  starvik
+-*! Whitespace cleanup
+-*!
+-*! Revision 1.13  2004/05/28 09:26:59  starvik
+-*! Modified I2C initialization to work in 2.6.
+-*!
+-*! Revision 1.12  2004/05/14 07:58:03  starvik
+-*! Merge of changes from 2.4
+-*!
+-*! Revision 1.10  2004/02/04 09:25:12  starvik
+-*! Merge of Linux 2.6.2
+-*!
+-*! Revision 1.9  2003/07/04 08:27:37  starvik
+-*! Merge of Linux 2.5.74
+-*!
+-*! Revision 1.8  2003/04/09 05:20:47  starvik
+-*! Merge of Linux 2.5.67
+-*!
+-*! Revision 1.6  2003/01/09 14:42:51  starvik
+-*! Merge of Linux 2.5.55
+-*!
+-*! Revision 1.4  2002/12/11 13:13:57  starvik
+-*! Added arch/ to v10 specific includes
+-*! Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer)
+-*!
+-*! Revision 1.3  2002/11/20 11:56:10  starvik
+-*! Merge of Linux 2.5.48
+-*!
+-*! Revision 1.2  2002/11/18 13:16:06  starvik
+-*! Linux 2.5 port of latest 2.4 drivers
+-*!
+-*! Revision 1.15  2002/10/11 16:14:33  johana
+-*! Added CONFIG_ETRAX_DS1302_TRICKLE_CHARGE and initial setting of the
+-*! trcklecharge register.
+-*!
+-*! Revision 1.14  2002/10/10 12:15:38  magnusmn
+-*! Added support for having the RST signal on bit g0
+-*!
+-*! Revision 1.13  2002/05/29 15:16:08  johana
+-*! Removed unused variables.
+-*!
+-*! Revision 1.12  2002/04/10 15:35:25  johana
+-*! Moved probe function closer to init function and marked it __init.
+-*!
+-*! Revision 1.11  2001/06/14 12:35:52  jonashg
+-*! The ATA hack is back. It is unfortunately the only way to set g27 to output.
+-*!
+-*! Revision 1.9  2001/06/14 10:00:14  jonashg
+-*! No need for tempudelay to be inline anymore (had to adjust the usec to
+-*! loops conversion because of this to make it slow enough to be a udelay).
+-*!
+-*! Revision 1.8  2001/06/14 08:06:32  jonashg
+-*! Made tempudelay delay usecs (well, just a tad more).
+-*!
+-*! Revision 1.7  2001/06/13 14:18:11  jonashg
+-*! Only allow processes with SYS_TIME capability to set time and charge.
+-*!
+-*! Revision 1.6  2001/06/12 15:22:07  jonashg
+-*! * Made init function __init.
+-*! * Parameter to out_byte() is unsigned char.
+-*! * The magic number 42 has got a name.
+-*! * Removed comment about /proc (nothing is exported there).
+-*!
+-*! Revision 1.5  2001/06/12 14:35:13  jonashg
+-*! Gave the module a name and added it to printk's.
+-*!
+-*! Revision 1.4  2001/05/31 14:53:40  jonashg
+-*! Made tempudelay() inline so that the watchdog doesn't reset (see
+-*! function comment).
+-*!
+-*! Revision 1.3  2001/03/26 16:03:06  bjornw
+-*! Needs linux/config.h
+-*!
+-*! Revision 1.2  2001/03/20 19:42:00  bjornw
+-*! Use the ETRAX prefix on the DS1302 options
+-*!
+-*! Revision 1.1  2001/03/20 09:13:50  magnusmn
+-*! Linux 2.4 port
+-*!
+-*! Revision 1.10  2000/07/05 15:38:23  bjornw
+-*! Dont update kernel time when a RTC_SET_TIME is done
+-*!
+-*! Revision 1.9  2000/03/02 15:42:59  macce
+-*! * Hack to make RTC work on all 2100/2400
+-*!
+-*! Revision 1.8  2000/02/23 16:59:18  torbjore
+-*! added setup of R_GEN_CONFIG when RTC is connected to the generic port.
+-*!
+-*! Revision 1.7  2000/01/17 15:51:43  johana
+-*! Added RTC_SET_CHARGE ioctl to enable trickle charger.
+-*!
+-*! Revision 1.6  1999/10/27 13:19:47  bjornw
+-*! Added update_xtime_from_cmos which reads back the updated RTC into the kernel.
+-*! /dev/rtc calls it now.
+-*!
+-*! Revision 1.5  1999/10/27 12:39:37  bjornw
+-*! Disabled superuser check. Anyone can now set the time.
+-*!
+-*! Revision 1.4  1999/09/02 13:27:46  pkj
+-*! Added shadow for R_PORT_PB_CONFIG.
+-*! Renamed port_g_shadow to port_g_data_shadow.
+-*!
+-*! Revision 1.3  1999/09/02 08:28:06  pkj
+-*! Made it possible to select either port PB or the generic port for the RST
+-*! signal line to the DS1302 RTC.
+-*! Also make sure the RST bit is configured as output on Port PB (if used).
+-*!
+-*! Revision 1.2  1999/09/01 14:47:20  bjornw
+-*! Added support for /dev/rtc operations with ioctl RD_TIME and SET_TIME to read
+-*! and set the date. Register as major 121.
+-*!
+-*! Revision 1.1  1999/09/01 09:45:29  bjornw
+-*! Implemented a DS1302 RTC driver.
+-*!
+-*!
+ *! ---------------------------------------------------------------------------
+ *!
+-*! (C) Copyright 1999, 2000, 2001, 2002, 2003, 2004  Axis Communications AB, LUND, SWEDEN
+-*!
+-*! $Id: ds1302.c,v 1.18 2005/01/24 09:11:26 mikaelam Exp $
++*! (C) Copyright 1999-2006 Axis Communications AB, LUND, SWEDEN
+ *!
+ *!***************************************************************************/
+@@ -305,14 +178,7 @@
+ void
+ ds1302_writereg(int reg, unsigned char val) 
+ {
+-#ifndef CONFIG_ETRAX_RTC_READONLY
+       int do_writereg = 1;
+-#else
+-      int do_writereg = 0;
+-
+-      if (reg == RTC_TRICKLECHARGER)
+-              do_writereg = 1;
+-#endif
+       if (do_writereg) {
+               ds1302_wenable();
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/eeprom.c linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/eeprom.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/eeprom.c     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/eeprom.c     2006-10-13 14:43:10.000000000 +0200
+@@ -20,6 +20,9 @@
+ *!                                  in the spin-lock.
+ *!
+ *!  $Log: eeprom.c,v $
++*!  Revision 1.13  2006/10/13 12:43:10  starvik
++*!  Merge of 2.6.18
++*!
+ *!  Revision 1.12  2005/06/19 17:06:46  starvik
+ *!  Merge of Linux 2.6.12.
+ *!
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/gpio.c linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/gpio.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/gpio.c       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/gpio.c       2007-02-05 12:54:34.000000000 +0100
+@@ -1,4 +1,4 @@
+-/* $Id: gpio.c,v 1.17 2005/06/19 17:06:46 starvik Exp $
++/* $Id: gpio.c,v 1.28 2007/02/05 11:54:34 pkj Exp $
+  *
+  * Etrax general port I/O device
+  *
+@@ -9,6 +9,40 @@
+  *             Johan Adolfsson  (read/set directions, write, port G)
+  *
+  * $Log: gpio.c,v $
++ * Revision 1.28  2007/02/05 11:54:34  pkj
++ * Merge of Linux 2.6.19
++ *
++ * Revision 1.27  2006/12/12 11:08:30  edgar
++ * In etrax_gpio_wake_up_check(), make flags unsigned long.
++ *
++ * Revision 1.26  2006/11/02 10:54:29  pkj
++ * Restored unique device id for request_irq() which was lost in the
++ * merge of 2.6.18.
++ *
++ * Revision 1.25  2006/10/13 12:43:10  starvik
++ * Merge of 2.6.18
++ *
++ * Revision 1.24  2006/07/13 07:42:20  starvik
++ * Set unique device id in request_irq
++ *
++ * Revision 1.23  2006/06/21 09:38:46  starvik
++ * Use correct spinlock macros
++ *
++ * Revision 1.22  2005/08/29 07:32:16  starvik
++ * Merge of 2.6.13
++ *
++ * Revision 1.21  2005/08/16 17:10:54  edgar
++ * dont leave locked spinlocks when returning.
++ *
++ * Revision 1.20  2005/08/15 13:10:47  orjanf
++ * Don't link struct into alarmlist until fully initialized.
++ *
++ * Revision 1.19  2005/07/13 11:43:11  karljope
++ * Corrected typo
++ *
++ * Revision 1.18  2005/06/21 12:26:53  starvik
++ * Improved alarm list locking.
++ *
+  * Revision 1.17  2005/06/19 17:06:46  starvik
+  * Merge of Linux 2.6.12.
+  *
+@@ -277,7 +311,7 @@
+       unsigned int mask = 0;
+       struct gpio_private *priv = (struct gpio_private *)file->private_data;
+       unsigned long data;
+-      spin_lock(&gpio_lock);
++      spin_lock_irq(&gpio_lock);
+       poll_wait(file, &priv->alarm_wq, wait);
+       if (priv->minor == GPIO_MINOR_A) {
+               unsigned long flags;
+@@ -297,15 +331,17 @@
+               data = *R_PORT_PB_DATA;
+       else if (priv->minor == GPIO_MINOR_G)
+               data = *R_PORT_G_DATA;
+-      else
++      else {
++              spin_unlock_irq(&gpio_lock);
+               return 0;
++      }
+       
+       if ((data & priv->highalarm) ||
+           (~data & priv->lowalarm)) {
+               mask = POLLIN|POLLRDNORM;
+       }
+-      spin_unlock(&gpio_lock);
++      spin_unlock_irq(&gpio_lock);
+       
+       DP(printk("gpio_poll ready: mask 0x%08X\n", mask));
+@@ -314,10 +350,12 @@
+ int etrax_gpio_wake_up_check(void)
+ {
+-      struct gpio_private *priv = alarmlist;
++      struct gpio_private *priv;
+       unsigned long data = 0;
+         int ret = 0;
+-      spin_lock(&gpio_lock);
++      unsigned long flags;
++      spin_lock_irqsave(&gpio_lock, flags);
++      priv = alarmlist;
+       while (priv) {
+               if (USE_PORTS(priv)) {
+                       data = *priv->port;
+@@ -332,12 +370,12 @@
+               }
+               priv = priv->next;
+       }
+-      spin_unlock(&gpio_lock);
++      spin_unlock_irqrestore(&gpio_lock, flags);
+         return ret;
+ }
+ static irqreturn_t
+-gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++gpio_poll_timer_interrupt(int irq, void *dev_id)
+ {
+       if (gpio_some_alarms) {
+               etrax_gpio_wake_up_check();
+@@ -347,7 +385,7 @@
+ }
+ static irqreturn_t
+-gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++gpio_pa_interrupt(int irq, void *dev_id)
+ {
+       unsigned long tmp;
+       spin_lock(&gpio_lock);
+@@ -376,9 +414,6 @@
+       struct gpio_private *priv = (struct gpio_private *)file->private_data;
+       unsigned char data, clk_mask, data_mask, write_msb;
+       unsigned long flags;
+-
+-      spin_lock(&gpio_lock);
+-
+       ssize_t retval = count;
+       if (priv->minor !=GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) {
+               return -EFAULT;
+@@ -394,6 +429,7 @@
+       if (clk_mask == 0 || data_mask == 0) {
+               return -EPERM;
+       }
++      spin_lock_irq(&gpio_lock);
+       write_msb = priv->write_msb;
+       D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb));
+       while (count--) {
+@@ -425,7 +461,7 @@
+                       }
+               }
+       }
+-      spin_unlock(&gpio_lock);
++      spin_unlock_irq(&gpio_lock);
+       return retval;
+ }
+@@ -445,13 +481,12 @@
+       if (!priv)
+               return -ENOMEM;
++      memset(priv, 0, sizeof(*priv));
+       priv->minor = p;
+-      /* initialize the io/alarm struct and link it into our alarmlist */
++      /* initialize the io/alarm struct */
+-      priv->next = alarmlist;
+-      alarmlist = priv;
+       if (USE_PORTS(priv)) { /* A and B */
+               priv->port = ports[p];
+               priv->shadow = shads[p];
+@@ -476,6 +511,12 @@
+       filp->private_data = (void *)priv;
++      /* link it into our alarmlist */
++      spin_lock_irq(&gpio_lock);
++      priv->next = alarmlist;
++      alarmlist = priv;
++      spin_unlock_irq(&gpio_lock);
++
+       return 0;
+ }
+@@ -485,10 +526,10 @@
+       struct gpio_private *p;
+       struct gpio_private *todel;
+-      spin_lock(&gpio_lock);
++      spin_lock_irq(&gpio_lock);
+-        p = alarmlist;
+-        todel = (struct gpio_private *)filp->private_data;
++      p = alarmlist;
++      todel = (struct gpio_private *)filp->private_data;
+       /* unlink from alarmlist and free the private structure */
+@@ -506,12 +547,13 @@
+       while (p) {
+               if (p->highalarm | p->lowalarm) {
+                       gpio_some_alarms = 1;
++                      spin_unlock_irq(&gpio_lock);    
+                       return 0;
+               }
+               p = p->next;
+       }
+       gpio_some_alarms = 0;
+-      spin_unlock(&gpio_lock);
++      spin_unlock_irq(&gpio_lock);    
+       return 0;
+ }
+@@ -691,6 +733,8 @@
+                       /* Must update gpio_some_alarms */
+                       struct gpio_private *p = alarmlist;
+                       int some_alarms;
++                      spin_lock_irq(&gpio_lock);
++                      p = alarmlist;
+                       some_alarms = 0;
+                       while (p) {
+                               if (p->highalarm | p->lowalarm) {
+@@ -700,6 +744,7 @@
+                               p = p->next;
+                       }
+                       gpio_some_alarms = some_alarms;
++                      spin_unlock_irq(&gpio_lock);
+               }
+               break;
+       case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
+@@ -937,11 +982,11 @@
+        * in some tests.
+        */  
+       if (request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt,
+-                      IRQF_SHARED | IRQF_DISABLED,"gpio poll", NULL)) {
++                      IRQF_SHARED | IRQF_DISABLED,"gpio poll", gpio_name)) {
+               printk(KERN_CRIT "err: timer0 irq for gpio\n");
+       }
+       if (request_irq(PA_IRQ_NBR, gpio_pa_interrupt,
+-                      IRQF_SHARED | IRQF_DISABLED,"gpio PA", NULL)) {
++                      IRQF_SHARED | IRQF_DISABLED,"gpio PA", gpio_name)) {
+               printk(KERN_CRIT "err: PA irq for gpio\n");
+       }
+       
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/i2c.c linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/i2c.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/i2c.c        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/i2c.c        2006-10-13 14:43:10.000000000 +0200
+@@ -11,7 +11,25 @@
+ *! Jan 14 2000  Johan Adolfsson    Fixed PB shadow register stuff - 
+ *!                                 don't use PB_I2C if DS1302 uses same bits,
+ *!                                 use PB.
++*! June 23 2003 Pieter Grimmerink  Added 'i2c_sendnack'. i2c_readreg now
++*!                                 generates nack on last received byte, 
++*!                                 instead of ack.
++*!                                 i2c_getack changed data level while clock
++*!                                 was high, causing DS75 to see  a stop condition
++*!
+ *! $Log: i2c.c,v $
++*! Revision 1.17  2006/10/13 12:43:10  starvik
++*! Merge of 2.6.18
++*!
++*! Revision 1.16  2005/09/29 13:33:35  bjarne
++*! If "first" should have any purpos it should probably change value....
++*!
++*! Revision 1.15  2005/08/29 07:32:16  starvik
++*! Merge of 2.6.13
++*!
++*! Revision 1.14  2005/06/30 18:07:31  starvik
++*! Added the sendnack patch from 2.4.
++*!
+ *! Revision 1.13  2005/03/07 13:13:07  starvik
+ *! Added spinlocks to protect states etc
+ *!
+@@ -84,7 +102,7 @@
+ *! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN
+ *!
+ *!***************************************************************************/
+-/* $Id: i2c.c,v 1.13 2005/03/07 13:13:07 starvik Exp $ */
++/* $Id: i2c.c,v 1.17 2006/10/13 12:43:10 starvik Exp $ */
+ /****************** INCLUDE FILES SECTION ***********************************/
+@@ -480,7 +498,7 @@
+       i2c_delay(CLOCK_HIGH_TIME);
+       i2c_clk(I2C_CLOCK_LOW);
+       i2c_delay(CLOCK_LOW_TIME);
+-
++      
+       i2c_dir_in();
+ }
+@@ -622,7 +640,7 @@
+                * last received byte needs to be nacked
+                * instead of acked
+                */
+-              i2c_sendack();
++              i2c_sendnack();
+               /*
+                * end sequence
+                */
+@@ -708,6 +726,7 @@
+       if (!first) {
+               return res;
+       }
++      first = 0;
+       /* Setup and enable the Port B I2C interface */
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/pcf8563.c linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/pcf8563.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/pcf8563.c    2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/pcf8563.c    2006-10-27 17:22:12.000000000 +0200
+@@ -8,14 +8,13 @@
+  * low detector are also provided. All address and data are transferred
+  * serially via two-line bidirectional I2C-bus. Maximum bus speed is
+  * 400 kbits/s. The built-in word address register is incremented
+- * automatically after each written or read bute.
++ * automatically after each written or read byte.
+  *
+- * Copyright (c) 2002, Axis Communications AB
++ * Copyright (c) 2002-2006, Axis Communications AB
+  * All rights reserved.
+  *
+  * Author: Tobias Anderberg <tobiasa@axis.com>.
+  *
+- * $Id: pcf8563.c,v 1.11 2005/03/07 13:13:07 starvik Exp $
+  */
+ #include <linux/module.h>
+@@ -27,93 +26,105 @@
+ #include <linux/ioctl.h>
+ #include <linux/delay.h>
+ #include <linux/bcd.h>
+-#include <linux/capability.h>
+ #include <asm/uaccess.h>
+ #include <asm/system.h>
+ #include <asm/io.h>
+-#include <asm/arch/svinto.h>
+ #include <asm/rtc.h>
++
+ #include "i2c.h"
+-#define PCF8563_MAJOR 121             /* Local major number. */
+-#define DEVICE_NAME "rtc"             /* Name which is registered in /proc/devices. */
+-#define PCF8563_NAME "PCF8563"
+-#define DRIVER_VERSION "$Revision: 1.11 $"
+-
+-/* I2C bus slave registers. */
+-#define RTC_I2C_READ          0xa3
+-#define RTC_I2C_WRITE         0xa2
++#define PCF8563_MAJOR 121     /* Local major number. */
++#define DEVICE_NAME   "rtc"   /* Name which is registered in /proc/devices. */
++#define PCF8563_NAME  "PCF8563"
++#define DRIVER_VERSION        "$Revision: 1.18 $"
+ /* Two simple wrapper macros, saves a few keystrokes. */
+ #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x)
+ #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y)
+ static DEFINE_SPINLOCK(rtc_lock); /* Protect state etc */
+-      
++
+ static const unsigned char days_in_month[] =
+       { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
++/* Cache VL bit value read at driver init since writing the RTC_SECOND 
++ * register clears the VL status.
++ */
++static int voltage_low = 0;
++
+ static struct file_operations pcf8563_fops = {
+-      .owner = THIS_MODULE,
+-      .ioctl = pcf8563_ioctl,
++      owner: THIS_MODULE,
++      ioctl: pcf8563_ioctl,
+ };
+ unsigned char
+-pcf8563_readreg(int reg) 
++pcf8563_readreg(int reg)
+ {
+-      unsigned char res = i2c_readreg(RTC_I2C_READ, reg);
++      unsigned char res = rtc_read(reg);
+-      /* The PCF8563 does not return 0 for unimplemented bits */
+-      switch(reg)
+-      {
++      /* The PCF8563 does not return 0 for unimplemented bits. */
++      switch (reg) {
+               case RTC_SECONDS:
+               case RTC_MINUTES:
+-                   res &= 0x7f;
+-                   break;
++                      res &= 0x7F;
++                      break;
+               case RTC_HOURS:
+               case RTC_DAY_OF_MONTH:
+-                   res &= 0x3f;
+-                   break;
++                      res &= 0x3F;
++                      break;
++              case RTC_WEEKDAY:
++                      res &= 0x07;
++                      break;
+               case RTC_MONTH:
+-                   res = (res & 0x1f) - 1;  /* PCF8563 returns month in range 1-12 */
+-                   break;
++                      res &= 0x1F;
++                      break;
++              case RTC_CONTROL1:
++                      res &= 0xA8;
++                      break;
++              case RTC_CONTROL2:
++                      res &= 0x1F;
++                      break;
++              case RTC_CLOCKOUT_FREQ:
++              case RTC_TIMER_CONTROL:
++                      res &= 0x83;
++                      break;
+       }
+       return res;
+ }
+ void
+-pcf8563_writereg(int reg, unsigned char val) 
++pcf8563_writereg(int reg, unsigned char val)
+ {
+-#ifdef CONFIG_ETRAX_RTC_READONLY
+-      if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR))
+-              return;
+-#endif
+-
+       rtc_write(reg, val);
+ }
+ void
+ get_rtc_time(struct rtc_time *tm)
+ {
+-      tm->tm_sec = rtc_read(RTC_SECONDS);
+-      tm->tm_min = rtc_read(RTC_MINUTES);
++      tm->tm_sec  = rtc_read(RTC_SECONDS);
++      tm->tm_min  = rtc_read(RTC_MINUTES);
+       tm->tm_hour = rtc_read(RTC_HOURS);
+       tm->tm_mday = rtc_read(RTC_DAY_OF_MONTH);
+-      tm->tm_mon = rtc_read(RTC_MONTH);
++      tm->tm_wday = rtc_read(RTC_WEEKDAY);
++      tm->tm_mon  = rtc_read(RTC_MONTH);
+       tm->tm_year = rtc_read(RTC_YEAR);
+-      if (tm->tm_sec & 0x80)
+-              printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME);
++      if (tm->tm_sec & 0x80) {
++              printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
++                     "information is no longer guaranteed!\n", PCF8563_NAME);
++      }
+-      tm->tm_year = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0);
+-      tm->tm_sec &= 0x7f;
+-      tm->tm_min &= 0x7f;
+-      tm->tm_hour &= 0x3f;
+-      tm->tm_mday &= 0x3f;
+-      tm->tm_mon &= 0x1f;
++      tm->tm_year  = BCD_TO_BIN(tm->tm_year) +
++                     ((tm->tm_mon & 0x80) ? 100 : 0);
++      tm->tm_sec  &= 0x7F;
++      tm->tm_min  &= 0x7F;
++      tm->tm_hour &= 0x3F;
++      tm->tm_mday &= 0x3F;
++      tm->tm_wday &= 0x07; /* Not coded in BCD. */
++      tm->tm_mon  &= 0x1F;
+       BCD_TO_BIN(tm->tm_sec);
+       BCD_TO_BIN(tm->tm_min);
+@@ -126,17 +137,25 @@
+ int __init
+ pcf8563_init(void)
+ {
+-      int ret;
++      static int res = 0;
++      static int first = 1;
++
++      if (!first) {
++              return res;
++      }
++      first = 0;
+-      if ((ret = i2c_init())) {
+-              printk(KERN_CRIT "pcf8563_init: failed to init i2c\n");
+-              return ret;
++      /* Initiate the i2c protocol. */
++      res = i2c_init();
++      if (res < 0) {
++              printk(KERN_CRIT "pcf8563_init: Failed to init i2c.\n");
++              return res;
+       }
+       /*
+        * First of all we need to reset the chip. This is done by
+-       * clearing control1, control2 and clk freq, clear the 
+-       * Voltage Low bit, and resetting all alarms.
++       * clearing control1, control2 and clk freq and resetting
++       * all alarms.
+        */
+       if (rtc_write(RTC_CONTROL1, 0x00) < 0)
+               goto err;
+@@ -147,41 +166,44 @@
+       if (rtc_write(RTC_CLOCKOUT_FREQ, 0x00) < 0)
+               goto err;
+-      /* Clear the VL bit in the seconds register. */
+-      ret = rtc_read(RTC_SECONDS);
+-      
+-      if (rtc_write(RTC_SECONDS, (ret & 0x7f)) < 0)
++      if (rtc_write(RTC_TIMER_CONTROL, 0x03) < 0)
+               goto err;
+-              
++
+       /* Reset the alarms. */
+-      if (rtc_write(RTC_MINUTE_ALARM, 0x00) < 0)
++      if (rtc_write(RTC_MINUTE_ALARM, 0x80) < 0)
+               goto err;
+-      
+-      if (rtc_write(RTC_HOUR_ALARM, 0x00) < 0)
++
++      if (rtc_write(RTC_HOUR_ALARM, 0x80) < 0)
+               goto err;
+-      
+-      if (rtc_write(RTC_DAY_ALARM, 0x00) < 0)
++
++      if (rtc_write(RTC_DAY_ALARM, 0x80) < 0)
+               goto err;
+-      
+-      if (rtc_write(RTC_WEEKDAY_ALARM, 0x00) < 0)
++
++      if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0)
+               goto err;
+-        
+-      /* Check for low voltage, and warn about it.. */
+-      if (rtc_read(RTC_SECONDS) & 0x80)
+-              printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME);
+-      
+-      return 0;
++
++      /* Check for low voltage, and warn about it. */
++      if (rtc_read(RTC_SECONDS) & 0x80) {
++              voltage_low = 1;
++              printk(KERN_WARNING "%s: RTC Voltage Low - reliable "
++                     "date/time information is no longer guaranteed!\n",
++                     PCF8563_NAME);
++      }
++
++      return res;
+ err:
+       printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME);
+-      return -1;
++      res = -1;
++      return res;
+ }
+ void __exit
+ pcf8563_exit(void)
+ {
+       if (unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME) < 0) {
+-              printk(KERN_INFO "%s: Unable to unregister device.\n", PCF8563_NAME);
++              printk(KERN_INFO "%s: Unable to unregister device.\n",
++                     PCF8563_NAME);
+       }
+ }
+@@ -190,7 +212,8 @@
+  * POSIX says so!
+  */
+ int
+-pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
++pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
++              unsigned long arg)
+ {
+       /* Some sanity checks. */
+       if (_IOC_TYPE(cmd) != RTC_MAGIC)
+@@ -201,123 +224,151 @@
+       switch (cmd) {
+               case RTC_RD_TIME:
+-                      {
+-                              struct rtc_time tm;
+-
+-                              spin_lock(&rtc_lock);
+-                              get_rtc_time(&tm);
++              {
++                      struct rtc_time tm;
+-                              if (copy_to_user((struct rtc_time *) arg, &tm, sizeof(struct rtc_time))) {
+-                                      spin_unlock(&rtc_lock);
+-                                      return -EFAULT;
+-                              }
++                      spin_lock(&rtc_lock);
++                      memset(&tm, 0, sizeof tm);
++                      get_rtc_time(&tm);
++                      if (copy_to_user((struct rtc_time *) arg, &tm,
++                                       sizeof tm)) {
+                               spin_unlock(&rtc_lock);
+-                              return 0;
++                              return -EFAULT;
+                       }
+-                      break;
++
++                      spin_unlock(&rtc_lock);
++
++                      return 0;
++              }
+               case RTC_SET_TIME:
+-                      {
+-#ifdef CONFIG_ETRAX_RTC_READONLY
++              {
++                      int leap;
++                      int year;
++                      int century;
++                      struct rtc_time tm;
++
++                      memset(&tm, 0, sizeof tm);
++                      if (!capable(CAP_SYS_TIME))
+                               return -EPERM;
+-#else
+-                              int leap;
+-                              int century;
+-                              struct rtc_time tm;
+-
+-                              memset(&tm, 0, sizeof (struct rtc_time));
+-                              if (!capable(CAP_SYS_TIME))
+-                                      return -EPERM;
+-
+-                              if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof(struct rtc_time)))
+-                                      return -EFAULT;
+-
+-                              /* Convert from struct tm to struct rtc_time. */
+-                              tm.tm_year += 1900;
+-                              tm.tm_mon += 1;
+-                              
+-                              leap = ((tm.tm_mon == 2) && ((tm.tm_year % 4) == 0)) ? 1 : 0;
+-
+-                              /* Perform some sanity checks. */
+-                              if ((tm.tm_year < 1970) ||
+-                                  (tm.tm_mon > 12) ||
+-                                  (tm.tm_mday == 0) ||
+-                                  (tm.tm_mday > days_in_month[tm.tm_mon] + leap) ||
+-                                  (tm.tm_hour >= 24) ||
+-                                  (tm.tm_min >= 60) ||
+-                                  (tm.tm_sec >= 60))
+-                                      return -EINVAL;
+-
+-                              century = (tm.tm_year >= 2000) ? 0x80 : 0;
+-                              tm.tm_year = tm.tm_year % 100;
+-
+-                              BIN_TO_BCD(tm.tm_year);
+-                              BIN_TO_BCD(tm.tm_mday);
+-                              BIN_TO_BCD(tm.tm_hour);
+-                              BIN_TO_BCD(tm.tm_min);
+-                              BIN_TO_BCD(tm.tm_sec);
+-                              tm.tm_mon |= century;
+-
+-                              spin_lock(&rtc_lock);
+-                              
+-                              rtc_write(RTC_YEAR, tm.tm_year);
+-                              rtc_write(RTC_MONTH, tm.tm_mon);
+-                              rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday);
+-                              rtc_write(RTC_HOURS, tm.tm_hour);
+-                              rtc_write(RTC_MINUTES, tm.tm_min);
+-                              rtc_write(RTC_SECONDS, tm.tm_sec);
+-                              spin_unlock(&rtc_lock);
++                      if (copy_from_user(&tm, (struct rtc_time *) arg,
++                                         sizeof tm)) {
++                              return -EFAULT;
++                      }
+-                              return 0;
+-#endif /* !CONFIG_ETRAX_RTC_READONLY */
++                      /* Convert from struct tm to struct rtc_time. */
++                      tm.tm_year += 1900;
++                      tm.tm_mon += 1;
++
++                      /*
++                       * Check if tm.tm_year is a leap year. A year is a leap
++                       * year if it is divisible by 4 but not 100, except
++                       * that years divisible by 400 _are_ leap years.
++                       */
++                      year = tm.tm_year;
++                      leap = (tm.tm_mon == 2) && 
++                              ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
++
++                      /* Perform some sanity checks. */
++                      if ((tm.tm_year < 1970) ||
++                          (tm.tm_mon > 12) ||
++                          (tm.tm_mday == 0) ||
++                          (tm.tm_mday > days_in_month[tm.tm_mon] + leap) ||
++                          (tm.tm_wday >= 7) ||
++                          (tm.tm_hour >= 24) ||
++                          (tm.tm_min >= 60) ||
++                          (tm.tm_sec >= 60)) {
++                              return -EINVAL;
+                       }
+-              case RTC_VLOW_RD:
+-              {
+-                      int vl_bit = 0;
++                      century = (tm.tm_year >= 2000) ? 0x80 : 0;
++                      tm.tm_year = tm.tm_year % 100;
+-                      if (rtc_read(RTC_SECONDS) & 0x80) {
+-                              vl_bit = 1;
+-                              printk(KERN_WARNING "%s: RTC Voltage Low - reliable "
+-                                     "date/time information is no longer guaranteed!\n",
+-                                     PCF8563_NAME);
+-                      }
+-                      if (copy_to_user((int *) arg, &vl_bit, sizeof(int)))
+-                              return -EFAULT;
++                      BIN_TO_BCD(tm.tm_year);
++                      BIN_TO_BCD(tm.tm_mon);
++                      BIN_TO_BCD(tm.tm_mday);
++                      BIN_TO_BCD(tm.tm_hour);
++                      BIN_TO_BCD(tm.tm_min);
++                      BIN_TO_BCD(tm.tm_sec);
++                      tm.tm_mon |= century;
++
++                      spin_lock(&rtc_lock);
++
++                      rtc_write(RTC_YEAR, tm.tm_year);
++                      rtc_write(RTC_MONTH, tm.tm_mon);
++                      rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */
++                      rtc_write(RTC_DAY_OF_MONTH, tm.tm_mday);
++                      rtc_write(RTC_HOURS, tm.tm_hour);
++                      rtc_write(RTC_MINUTES, tm.tm_min);
++                      rtc_write(RTC_SECONDS, tm.tm_sec);
++
++                      spin_unlock(&rtc_lock);
+                       return 0;
+               }
++              case RTC_VLOW_RD:
++                      if (voltage_low) {
++                              printk(KERN_WARNING "%s: RTC Voltage Low - "
++                                     "reliable date/time information is no "
++                                     "longer guaranteed!\n", PCF8563_NAME);
++                      }
++
++                      if (copy_to_user((int *) arg, &voltage_low, sizeof(int))) {
++                              return -EFAULT;
++                      }
++                      
++                      return 0;
+               case RTC_VLOW_SET:
+               {
+-                      /* Clear the VL bit in the seconds register */
++                      /* Clear the VL bit in the seconds register in case 
++                       * the time has not been set already (which would
++                       * have cleared it). This does not really matter 
++                       * because of the cached voltage_low value but do it
++                       * anyway for consistency. */
++
+                       int ret = rtc_read(RTC_SECONDS);
+                       rtc_write(RTC_SECONDS, (ret & 0x7F));
++                      /* Clear the cached value. */
++                      voltage_low = 0;
++
+                       return 0;
+               }
+-
+               default:
+-                              return -ENOTTY;
++                      return -ENOTTY;
+       }
+       return 0;
+ }
+-static int __init
++static int __init 
+ pcf8563_register(void)
+ {
+-      pcf8563_init();
++      if (pcf8563_init() < 0) {
++              printk(KERN_INFO "%s: Unable to initialize Real-Time Clock "
++                     "Driver, %s\n", PCF8563_NAME, DRIVER_VERSION);
++              return -1;
++      }
++
+       if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) {
+-              printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n",
+-                     PCF8563_NAME, PCF8563_MAJOR);
++              printk(KERN_INFO "%s: Unable to get major numer %d for RTC "
++                     "device.\n", PCF8563_NAME, PCF8563_MAJOR);
+               return -1;
+       }
+-      printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION);
+-        return 0;
++      printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME,
++             DRIVER_VERSION);
++
++      /* Check for low voltage, and warn about it. */
++      if (voltage_low) {
++              printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
++                     "information is no longer guaranteed!\n", PCF8563_NAME);
++      }
++
++      return 0;
+ }
+ module_init(pcf8563_register);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/drivers/sync_serial.c linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/sync_serial.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/drivers/sync_serial.c        1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/drivers/sync_serial.c        2007-02-05 12:56:34.000000000 +0100
+@@ -0,0 +1,1329 @@
++/*  
++ * Simple synchronous serial port driver for ETRAX 100LX.
++ *
++ * Synchronous serial ports are used for continuous streamed data like audio.
++ * The default setting for this driver is compatible with the STA 013 MP3
++ * decoder. The driver can easily be tuned to fit other audio encoder/decoders
++ * and SPI
++ *
++ * Copyright (c) 2001-2006 Axis Communications AB
++ * 
++ * Author: Mikael Starvik, Johan Adolfsson
++ *
++ */
++#include <linux/module.h>
++#include <linux/kernel.h>
++#include <linux/types.h>
++#include <linux/errno.h>
++#include <linux/major.h>
++#include <linux/sched.h>
++#include <linux/slab.h>
++#include <linux/interrupt.h>
++#include <linux/poll.h>
++#include <linux/init.h>
++#include <linux/timer.h>
++#include <asm/irq.h>
++#include <asm/dma.h>
++#include <asm/io.h>
++#include <asm/arch/svinto.h>
++#include <asm/uaccess.h>
++#include <asm/system.h>
++#include <asm/sync_serial.h>
++#include <asm/arch/io_interface_mux.h>
++
++/* The receiver is a bit tricky beacuse of the continuous stream of data.*/
++/*                                                                       */
++/* Three DMA descriptors are linked together. Each DMA descriptor is     */
++/* responsible for port->bufchunk of a common buffer.                    */
++/*                                                                       */
++/* +---------------------------------------------+                       */
++/* |   +----------+   +----------+   +----------+ |                      */
++/* +-> | Descr[0] |-->| Descr[1] |-->| Descr[2] |-+                      */
++/*     +----------+   +----------+   +----------+                        */
++/*         |            |              |                                 */
++/*         v            v              v                                 */
++/*   +-------------------------------------+                             */
++/*   |        BUFFER                       |                             */
++/*   +-------------------------------------+                             */
++/*      |<- data_avail ->|                                               */
++/*    readp          writep                                              */
++/*                                                                       */
++/* If the application keeps up the pace readp will be right after writep.*/
++/* If the application can't keep the pace we have to throw away data.    */ 
++/* The idea is that readp should be ready with the data pointed out by         */
++/* Descr[i] when the DMA has filled in Descr[i+1].                       */
++/* Otherwise we will discard                                           */
++/* the rest of the data pointed out by Descr1 and set readp to the start */
++/* of Descr2                                                             */
++
++#define SYNC_SERIAL_MAJOR 125
++
++/* IN_BUFFER_SIZE should be a multiple of 6 to make sure that 24 bit */
++/* words can be handled */
++#define IN_BUFFER_SIZE 12288
++#define IN_DESCR_SIZE 256
++#define NUM_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE)
++#define OUT_BUFFER_SIZE 4096
++
++#define DEFAULT_FRAME_RATE 0
++#define DEFAULT_WORD_RATE 7
++
++/* NOTE: Enabling some debug will likely cause overrun or underrun,
++ * especially if manual mode is use.
++ */
++#define DEBUG(x)
++#define DEBUGREAD(x)
++#define DEBUGWRITE(x)
++#define DEBUGPOLL(x)
++#define DEBUGRXINT(x)
++#define DEBUGTXINT(x)
++
++/* Define some macros to access ETRAX 100 registers */
++#define SETF(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \
++                                        IO_FIELD_(reg##_, field##_, val)
++#define SETS(var, reg, field, val) var = (var & ~IO_MASK_(reg##_, field##_)) | \
++                                        IO_STATE_(reg##_, field##_, _##val)
++
++typedef struct sync_port
++{
++      /* Etrax registers and bits*/
++      const volatile unsigned * const status;
++      volatile unsigned * const ctrl_data;
++      volatile unsigned * const output_dma_first;
++      volatile unsigned char * const output_dma_cmd;
++      volatile unsigned char * const output_dma_clr_irq;
++      volatile unsigned * const input_dma_first;
++      volatile unsigned char * const input_dma_cmd;
++      volatile unsigned * const input_dma_descr;
++      /* 8*4 */       
++      volatile unsigned char * const input_dma_clr_irq;
++      volatile unsigned * const data_out;
++      const volatile unsigned * const data_in;
++      char data_avail_bit; /* In R_IRQ_MASK1_RD/SET/CLR */
++      char transmitter_ready_bit; /* In R_IRQ_MASK1_RD/SET/CLR */
++      char input_dma_descr_bit; /* In R_IRQ_MASK2_RD */
++
++      char output_dma_bit; /* In R_IRQ_MASK2_RD */
++      /* End of fields initialised in array */
++      char started; /* 1 if port has been started */
++      char port_nbr; /* Port 0 or 1 */
++      char busy; /* 1 if port is busy */
++
++      char enabled;  /* 1 if port is enabled */
++      char use_dma;  /* 1 if port uses dma */
++      char tr_running;
++
++      char init_irqs;
++      
++      unsigned int ctrl_data_shadow; /* Register shadow */
++      volatile unsigned int out_count; /* Remaining bytes for current transfer */
++      unsigned char* outp; /* Current position in out_buffer */
++      /* 16*4 */
++      volatile unsigned char* volatile readp;  /* Next byte to be read by application */
++      volatile unsigned char* volatile writep; /* Next byte to be written by etrax */
++      unsigned int in_buffer_size;
++      unsigned int inbufchunk;
++      struct etrax_dma_descr out_descr __attribute__ ((aligned(32)));
++      struct etrax_dma_descr in_descr[NUM_IN_DESCR] __attribute__ ((aligned(32)));
++      unsigned char out_buffer[OUT_BUFFER_SIZE] __attribute__ ((aligned(32)));
++      unsigned char in_buffer[IN_BUFFER_SIZE]__attribute__ ((aligned(32)));
++      unsigned char flip[IN_BUFFER_SIZE] __attribute__ ((aligned(32)));
++      struct etrax_dma_descr* next_rx_desc;
++      struct etrax_dma_descr* prev_rx_desc;
++      int full;
++
++      wait_queue_head_t out_wait_q;
++      wait_queue_head_t in_wait_q;    
++} sync_port;
++
++
++static int etrax_sync_serial_init(void);
++static void initialize_port(int portnbr);
++static inline int sync_data_avail(struct sync_port *port);
++
++static int sync_serial_open(struct inode *, struct file*);
++static int sync_serial_release(struct inode*, struct file*);
++static unsigned int sync_serial_poll(struct file *filp, poll_table *wait);
++
++static int sync_serial_ioctl(struct inode*, struct file*,
++                           unsigned int cmd, unsigned long arg);
++static ssize_t sync_serial_write(struct file * file, const char * buf, 
++                               size_t count, loff_t *ppos);
++static ssize_t sync_serial_read(struct file *file, char *buf, 
++                              size_t count, loff_t *ppos);
++
++#if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \
++     defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)) || \
++    (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) && \
++     defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA))
++#define SYNC_SER_DMA
++#endif
++
++static void send_word(sync_port* port);
++static void start_dma(struct sync_port *port, const char* data, int count);
++static void start_dma_in(sync_port* port);
++#ifdef SYNC_SER_DMA
++static irqreturn_t tr_interrupt(int irq, void *dev_id);
++static irqreturn_t rx_interrupt(int irq, void *dev_id);
++#endif
++#if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \
++     !defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)) || \
++    (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1) && \
++     !defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA))
++#define SYNC_SER_MANUAL
++#endif
++#ifdef SYNC_SER_MANUAL
++static irqreturn_t manual_interrupt(int irq, void *dev_id);
++#endif
++
++/* The ports */
++static struct sync_port ports[]=
++{
++      {
++              .status                = R_SYNC_SERIAL1_STATUS,
++              .ctrl_data             = R_SYNC_SERIAL1_CTRL,  
++              .output_dma_first      = R_DMA_CH8_FIRST,
++              .output_dma_cmd        = R_DMA_CH8_CMD,
++              .output_dma_clr_irq    = R_DMA_CH8_CLR_INTR,     
++              .input_dma_first       = R_DMA_CH9_FIRST,
++              .input_dma_cmd         = R_DMA_CH9_CMD,
++              .input_dma_descr       = R_DMA_CH9_DESCR,
++              .input_dma_clr_irq     = R_DMA_CH9_CLR_INTR,
++              .data_out              = R_SYNC_SERIAL1_TR_DATA,
++              .data_in               = R_SYNC_SERIAL1_REC_DATA,
++              .data_avail_bit        = IO_BITNR(R_IRQ_MASK1_RD, ser1_data),
++              .transmitter_ready_bit = IO_BITNR(R_IRQ_MASK1_RD, ser1_ready),
++              .input_dma_descr_bit   = IO_BITNR(R_IRQ_MASK2_RD, dma9_descr),
++              .output_dma_bit        = IO_BITNR(R_IRQ_MASK2_RD, dma8_eop),
++              .init_irqs             = 1,
++#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)
++                .use_dma               = 1,
++#else
++                .use_dma               = 0,
++#endif
++      },
++      {
++              .status                = R_SYNC_SERIAL3_STATUS,
++              .ctrl_data             = R_SYNC_SERIAL3_CTRL,
++              .output_dma_first      = R_DMA_CH4_FIRST,
++              .output_dma_cmd        = R_DMA_CH4_CMD,
++              .output_dma_clr_irq    = R_DMA_CH4_CLR_INTR,
++              .input_dma_first       = R_DMA_CH5_FIRST,
++              .input_dma_cmd         = R_DMA_CH5_CMD,
++              .input_dma_descr       = R_DMA_CH5_DESCR,               
++              .input_dma_clr_irq     = R_DMA_CH5_CLR_INTR,
++              .data_out              = R_SYNC_SERIAL3_TR_DATA,
++              .data_in               = R_SYNC_SERIAL3_REC_DATA,
++              .data_avail_bit        = IO_BITNR(R_IRQ_MASK1_RD, ser3_data),
++              .transmitter_ready_bit = IO_BITNR(R_IRQ_MASK1_RD, ser3_ready),
++              .input_dma_descr_bit   = IO_BITNR(R_IRQ_MASK2_RD, dma5_descr),
++              .output_dma_bit        = IO_BITNR(R_IRQ_MASK2_RD, dma4_eop),
++              .init_irqs             = 1,
++#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA)
++                .use_dma               = 1,
++#else
++                .use_dma               = 0,
++#endif
++      }
++};
++
++/* Register shadows */
++static unsigned sync_serial_prescale_shadow = 0;
++
++#define NUMBER_OF_PORTS (sizeof(ports)/sizeof(sync_port))
++
++static struct file_operations sync_serial_fops = {
++      .owner   = THIS_MODULE,
++      .write   = sync_serial_write,
++      .read    = sync_serial_read,
++      .poll    = sync_serial_poll,
++      .ioctl   = sync_serial_ioctl,
++      .open    = sync_serial_open,
++      .release = sync_serial_release
++};
++
++static int __init etrax_sync_serial_init(void)
++{
++      ports[0].enabled = 0;
++      ports[1].enabled = 0;
++
++#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0)
++      if (cris_request_io_interface(if_sync_serial_1, "sync_ser1")) {
++              printk(KERN_CRIT "ETRAX100LX sync_serial: Could not allocate IO group for port %d\n", 0);
++              return -EBUSY;
++      }
++#endif
++#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1)
++      if (cris_request_io_interface(if_sync_serial_3, "sync_ser3")) {
++#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0)
++              cris_free_io_interface(if_sync_serial_1);
++#endif
++              printk(KERN_CRIT "ETRAX100LX sync_serial: Could not allocate IO group for port %d\n", 1);
++              return -EBUSY;
++      }
++#endif
++      
++      if (register_chrdev(SYNC_SERIAL_MAJOR,"sync serial", &sync_serial_fops) <0 ) 
++      {
++#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1)
++              cris_free_io_interface(if_sync_serial_3);
++#endif
++#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0)
++              cris_free_io_interface(if_sync_serial_1);
++#endif
++              printk("unable to get major for synchronous serial port\n");
++              return -EBUSY;
++      }
++
++      /* Deselect synchronous serial ports while configuring. */
++      SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, async);
++      SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, async);
++      *R_GEN_CONFIG_II = gen_config_ii_shadow;
++
++      /* Initialize Ports */
++#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0)
++      ports[0].enabled = 1;
++      SETS(port_pb_i2c_shadow, R_PORT_PB_I2C, syncser1, ss1extra);
++      SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync);
++#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL0_DMA)
++      ports[0].use_dma = 1;
++#else
++      ports[0].use_dma = 0;   
++#endif
++      initialize_port(0);
++#endif
++
++#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1)
++      ports[1].enabled = 1;
++      SETS(port_pb_i2c_shadow, R_PORT_PB_I2C, syncser3, ss3extra);
++      SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, sync);
++#if defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL1_DMA)
++      ports[1].use_dma = 1;
++#else
++      ports[1].use_dma = 0;
++#endif
++      initialize_port(1);
++#endif
++
++      *R_PORT_PB_I2C = port_pb_i2c_shadow; /* Use PB4/PB7 */
++
++      /* Set up timing */
++      *R_SYNC_SERIAL_PRESCALE = sync_serial_prescale_shadow = (
++        IO_STATE(R_SYNC_SERIAL_PRESCALE, clk_sel_u1, codec) | 
++        IO_STATE(R_SYNC_SERIAL_PRESCALE, word_stb_sel_u1, external) | 
++        IO_STATE(R_SYNC_SERIAL_PRESCALE, clk_sel_u3, codec) | 
++        IO_STATE(R_SYNC_SERIAL_PRESCALE, word_stb_sel_u3, external) | 
++        IO_STATE(R_SYNC_SERIAL_PRESCALE, prescaler, div4) | 
++        IO_FIELD(R_SYNC_SERIAL_PRESCALE, frame_rate, DEFAULT_FRAME_RATE) | 
++        IO_FIELD(R_SYNC_SERIAL_PRESCALE, word_rate, DEFAULT_WORD_RATE) | 
++        IO_STATE(R_SYNC_SERIAL_PRESCALE, warp_mode, normal));
++
++      /* Select synchronous ports */
++      *R_GEN_CONFIG_II = gen_config_ii_shadow;
++
++      printk("ETRAX 100LX synchronous serial port driver\n");
++      return 0;
++}
++
++static void __init initialize_port(int portnbr)
++{
++      struct sync_port* port = &ports[portnbr];
++
++      DEBUG(printk("Init sync serial port %d\n", portnbr));
++
++      port->started = 0;    
++      port->port_nbr = portnbr;       
++      port->busy = 0;
++      port->tr_running = 0;
++
++      port->out_count = 0;
++      port->outp = port->out_buffer;
++      
++      port->readp = port->flip;
++      port->writep = port->flip;
++      port->in_buffer_size = IN_BUFFER_SIZE;
++      port->inbufchunk = IN_DESCR_SIZE;
++      port->next_rx_desc = &port->in_descr[0];
++      port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR-1];
++      port->prev_rx_desc->ctrl = d_eol;
++
++      init_waitqueue_head(&port->out_wait_q);
++      init_waitqueue_head(&port->in_wait_q);
++      
++      port->ctrl_data_shadow =
++        IO_STATE(R_SYNC_SERIAL1_CTRL, tr_baud, c115k2Hz)   | 
++        IO_STATE(R_SYNC_SERIAL1_CTRL, mode, master_output) | 
++        IO_STATE(R_SYNC_SERIAL1_CTRL, error, ignore)       |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, rec_enable, disable) |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, f_synctype, normal)  |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, f_syncsize, word)    |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, f_sync, on)          |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, clk_mode, normal)    |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, clk_halt, stopped)   |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, bitorder, msb)       |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, tr_enable, disable)  |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit)  |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, buf_empty, lmt_8)    |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, buf_full, lmt_8)     |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, flow_ctrl, enabled)  |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, clk_polarity, neg)   |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, frame_polarity, normal)|
++        IO_STATE(R_SYNC_SERIAL1_CTRL, status_polarity, inverted)|
++        IO_STATE(R_SYNC_SERIAL1_CTRL, clk_driver, normal)   |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, frame_driver, normal) |
++        IO_STATE(R_SYNC_SERIAL1_CTRL, status_driver, normal)|
++        IO_STATE(R_SYNC_SERIAL1_CTRL, def_out0, high);
++  
++      if (port->use_dma)
++              port->ctrl_data_shadow |= IO_STATE(R_SYNC_SERIAL1_CTRL, dma_enable, on);
++      else
++              port->ctrl_data_shadow |= IO_STATE(R_SYNC_SERIAL1_CTRL, dma_enable, off);
++  
++      *port->ctrl_data = port->ctrl_data_shadow;
++}
++
++static inline int sync_data_avail(struct sync_port *port)
++{
++      int avail;
++      unsigned char *start;
++      unsigned char *end;
++      
++      start = (unsigned char*)port->readp; /* cast away volatile */
++      end = (unsigned char*)port->writep;  /* cast away volatile */
++      /* 0123456789  0123456789
++       *  -----      -    -----
++       *  ^rp  ^wp    ^wp ^rp
++       */
++              
++      if (end >= start)
++              avail = end - start;
++      else 
++              avail = port->in_buffer_size - (start - end);
++      return avail;
++}
++
++static inline int sync_data_avail_to_end(struct sync_port *port)
++{
++      int avail;
++      unsigned char *start;
++      unsigned char *end;
++      
++      start = (unsigned char*)port->readp; /* cast away volatile */
++      end = (unsigned char*)port->writep;  /* cast away volatile */
++      /* 0123456789  0123456789
++       *  -----           -----
++       *  ^rp  ^wp    ^wp ^rp
++       */
++              
++      if (end >= start)
++              avail = end - start;
++      else 
++              avail = port->flip + port->in_buffer_size - start;
++      return avail;
++}
++
++
++static int sync_serial_open(struct inode *inode, struct file *file)
++{
++      int dev = MINOR(inode->i_rdev);
++      sync_port* port;
++      int mode;
++      
++      DEBUG(printk("Open sync serial port %d\n", dev)); 
++  
++      if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
++      {
++              DEBUG(printk("Invalid minor %d\n", dev));
++              return -ENODEV;
++      }
++      port = &ports[dev];
++      /* Allow open this device twice (assuming one reader and one writer) */
++      if (port->busy == 2) 
++      {
++              DEBUG(printk("Device is busy.. \n"));
++              return -EBUSY;
++      }
++      if (port->init_irqs) {
++              if (port->use_dma) {
++                      if (port == &ports[0]){
++#ifdef SYNC_SER_DMA
++                              if(request_irq(24,
++                                             tr_interrupt,
++                                             0,
++                                             "synchronous serial 1 dma tr",
++                                             &ports[0])) {
++                                      printk(KERN_CRIT "Can't allocate sync serial port 1 IRQ");
++                                      return -EBUSY;
++                              } else if(request_irq(25,
++                                                    rx_interrupt,
++                                                    0,
++                                                    "synchronous serial 1 dma rx",
++                                                    &ports[0])) {
++                                      free_irq(24, &port[0]);
++                                      printk(KERN_CRIT "Can't allocate sync serial port 1 IRQ");
++                                      return -EBUSY;
++                              } else if (cris_request_dma(8,
++                                                          "synchronous serial 1 dma tr",
++                                                          DMA_VERBOSE_ON_ERROR,
++                                                          dma_ser1)) {
++                                      free_irq(24, &port[0]);
++                                      free_irq(25, &port[0]);
++                                      printk(KERN_CRIT "Can't allocate sync serial port 1 TX DMA channel");
++                                      return -EBUSY;
++                              } else if (cris_request_dma(9,
++                                                          "synchronous serial 1 dma rec",
++                                                          DMA_VERBOSE_ON_ERROR,
++                                                          dma_ser1)) {
++                                      cris_free_dma(8, NULL);
++                                      free_irq(24, &port[0]);
++                                      free_irq(25, &port[0]);
++                                      printk(KERN_CRIT "Can't allocate sync serial port 1 RX DMA channel");
++                                      return -EBUSY;
++                              }
++#endif
++                              RESET_DMA(8); WAIT_DMA(8);
++                              RESET_DMA(9); WAIT_DMA(9);
++                              *R_DMA_CH8_CLR_INTR = IO_STATE(R_DMA_CH8_CLR_INTR, clr_eop, do) |
++                                      IO_STATE(R_DMA_CH8_CLR_INTR, clr_descr, do); 
++                              *R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do) |
++                                      IO_STATE(R_DMA_CH9_CLR_INTR, clr_descr, do); 
++                              *R_IRQ_MASK2_SET =
++                                      IO_STATE(R_IRQ_MASK2_SET, dma8_eop, set) |
++                                      IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set);
++                      }
++                      else if (port == &ports[1]){
++#ifdef SYNC_SER_DMA
++                              if (request_irq(20,
++                                              tr_interrupt,
++                                              0,
++                                              "synchronous serial 3 dma tr",
++                                              &ports[1])) {
++                                      printk(KERN_CRIT "Can't allocate sync serial port 3 IRQ");
++                                      return -EBUSY;
++                              } else if (request_irq(21,
++                                                     rx_interrupt,
++                                                     0,
++                                                     "synchronous serial 3 dma rx",
++                                                     &ports[1])) {
++                                      free_irq(20, &ports[1]);
++                                      printk(KERN_CRIT "Can't allocate sync serial port 3 IRQ");
++                                      return -EBUSY;
++                              } else if (cris_request_dma(4,
++                                                   "synchronous serial 3 dma tr",
++                                                   DMA_VERBOSE_ON_ERROR,
++                                                   dma_ser3)) {
++                                      free_irq(21, &ports[1]);
++                                      free_irq(20, &ports[1]);
++                                      printk(KERN_CRIT "Can't allocate sync serial port 3 TX DMA channel");
++                                      return -EBUSY;
++                              } else if (cris_request_dma(5,
++                                                          "synchronous serial 3 dma rec",
++                                                          DMA_VERBOSE_ON_ERROR,
++                                                          dma_ser3)) {
++                                      cris_free_dma(4, NULL);
++                                      free_irq(21, &ports[1]);
++                                      free_irq(20, &ports[1]);
++                                      printk(KERN_CRIT "Can't allocate sync serial port 3 RX DMA channel");
++                                      return -EBUSY;
++                              }
++#endif
++                              RESET_DMA(4); WAIT_DMA(4);
++                              RESET_DMA(5); WAIT_DMA(5);
++                              *R_DMA_CH4_CLR_INTR = IO_STATE(R_DMA_CH4_CLR_INTR, clr_eop, do) |
++                                      IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, do); 
++                              *R_DMA_CH5_CLR_INTR = IO_STATE(R_DMA_CH5_CLR_INTR, clr_eop, do) |
++                                      IO_STATE(R_DMA_CH5_CLR_INTR, clr_descr, do); 
++                              *R_IRQ_MASK2_SET =
++                                      IO_STATE(R_IRQ_MASK2_SET, dma4_eop, set) |
++                                      IO_STATE(R_IRQ_MASK2_SET, dma5_descr, set);
++                      }
++                      start_dma_in(port);
++                      port->init_irqs = 0;
++              } else { /* !port->use_dma */
++#ifdef SYNC_SER_MANUAL
++                      if (port == &ports[0]) {
++                              if (request_irq(8,
++                                              manual_interrupt,
++                                              IRQF_SHARED | IRQF_DISABLED,
++                                              "synchronous serial manual irq",
++                                              &ports[0])) {
++                                      printk("Can't allocate sync serial manual irq");
++                                      return -EBUSY;
++                              }
++                      } else if (port == &ports[1]) {
++                              if (request_irq(8,
++                                              manual_interrupt,
++                                              IRQF_SHARED | IRQF_DISABLED,
++                                              "synchronous serial manual irq",
++                                              &ports[1])) {
++                                      printk(KERN_CRIT "Can't allocate sync serial manual irq");
++                                      return -EBUSY;
++                              }
++                      }
++                      port->init_irqs = 0;
++#else
++                      panic("sync_serial: Manual mode not supported.\n");
++#endif /* SYNC_SER_MANUAL */
++              }
++      } /* port->init_irqs */
++
++      port->busy++;
++      /* Start port if we use it as input */
++      mode = IO_EXTRACT(R_SYNC_SERIAL1_CTRL, mode, port->ctrl_data_shadow);
++      if (mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, master_input) ||
++          mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, slave_input) ||
++          mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, master_bidir) ||
++          mode == IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, mode, slave_bidir)) {
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, running);
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, enable);
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, enable);
++              port->started = 1;
++              *port->ctrl_data = port->ctrl_data_shadow;
++              if (!port->use_dma)
++                      *R_IRQ_MASK1_SET = 1 << port->data_avail_bit;
++              DEBUG(printk("sser%d rec started\n", dev)); 
++      }
++      return 0;
++}
++
++static int sync_serial_release(struct inode *inode, struct file *file)
++{
++      int dev = MINOR(inode->i_rdev);
++      sync_port* port;
++
++      if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
++      {
++              DEBUG(printk("Invalid minor %d\n", dev));
++              return -ENODEV;
++      }
++      port = &ports[dev];
++      if (port->busy)
++              port->busy--;
++      if (!port->busy) 
++              *R_IRQ_MASK1_CLR = ((1 << port->data_avail_bit) |
++                                  (1 << port->transmitter_ready_bit));
++
++      return 0;
++}
++
++
++
++static unsigned int sync_serial_poll(struct file *file, poll_table *wait)
++{
++      int dev = MINOR(file->f_dentry->d_inode->i_rdev);
++      unsigned int mask = 0;
++      sync_port* port;
++      DEBUGPOLL( static unsigned int prev_mask = 0; );
++      
++      port = &ports[dev];
++      poll_wait(file, &port->out_wait_q, wait);
++      poll_wait(file, &port->in_wait_q, wait);
++      /* Some room to write */
++      if (port->out_count < OUT_BUFFER_SIZE)
++              mask |=  POLLOUT | POLLWRNORM;
++      /* At least an inbufchunk of data */
++      if (sync_data_avail(port) >= port->inbufchunk)
++              mask |= POLLIN | POLLRDNORM;
++      
++      DEBUGPOLL(if (mask != prev_mask)
++            printk("sync_serial_poll: mask 0x%08X %s %s\n", mask,
++                   mask&POLLOUT?"POLLOUT":"", mask&POLLIN?"POLLIN":"");
++            prev_mask = mask;
++            );
++      return mask;
++}
++
++static int sync_serial_ioctl(struct inode *inode, struct file *file,
++                unsigned int cmd, unsigned long arg)
++{
++      int return_val = 0;
++      unsigned long flags;
++      
++      int dev = MINOR(file->f_dentry->d_inode->i_rdev);
++      sync_port* port;
++        
++      if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
++      {
++              DEBUG(printk("Invalid minor %d\n", dev));
++              return -1;
++      }
++        port = &ports[dev];
++
++      local_irq_save(flags);
++      /* Disable port while changing config */
++      if (dev)
++      {
++              if (port->use_dma) {
++                      RESET_DMA(4); WAIT_DMA(4);
++                        port->tr_running = 0;
++                        port->out_count = 0;
++                        port->outp = port->out_buffer;
++                      *R_DMA_CH4_CLR_INTR = IO_STATE(R_DMA_CH4_CLR_INTR, clr_eop, do) |
++                              IO_STATE(R_DMA_CH4_CLR_INTR, clr_descr, do);
++              }
++              SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, async);
++      }
++      else
++      {
++              if (port->use_dma) {            
++                      RESET_DMA(8); WAIT_DMA(8);
++                        port->tr_running = 0;
++                        port->out_count = 0;
++                        port->outp = port->out_buffer;
++                      *R_DMA_CH8_CLR_INTR = IO_STATE(R_DMA_CH8_CLR_INTR, clr_eop, do) |
++                              IO_STATE(R_DMA_CH8_CLR_INTR, clr_descr, do);
++              }
++              SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, async);
++      }
++      *R_GEN_CONFIG_II = gen_config_ii_shadow;
++      local_irq_restore(flags);
++
++      switch(cmd)
++      {
++      case SSP_SPEED:
++              if (GET_SPEED(arg) == CODEC)
++              {
++                      if (dev)
++                              SETS(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, clk_sel_u3, codec);
++                      else
++                              SETS(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, clk_sel_u1, codec);
++                      
++                      SETF(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, prescaler, GET_FREQ(arg));
++                      SETF(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, frame_rate, GET_FRAME_RATE(arg));
++                      SETF(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, word_rate, GET_WORD_RATE(arg));
++              }
++              else
++              {
++                      SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_baud, GET_SPEED(arg));
++                      if (dev)
++                              SETS(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, clk_sel_u3, baudrate);
++                      else
++                              SETS(sync_serial_prescale_shadow, R_SYNC_SERIAL_PRESCALE, clk_sel_u1, baudrate);
++              }
++              break;
++      case SSP_MODE:
++              if (arg > 5)
++                      return -EINVAL;
++              if (arg == MASTER_OUTPUT || arg == SLAVE_OUTPUT)
++                      *R_IRQ_MASK1_CLR = 1 << port->data_avail_bit;
++              else if (!port->use_dma)
++                      *R_IRQ_MASK1_SET = 1 << port->data_avail_bit;
++              SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, mode, arg);
++              break;
++      case SSP_FRAME_SYNC:
++              if (arg & NORMAL_SYNC)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_synctype, normal);
++              else if (arg & EARLY_SYNC)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_synctype, early);
++              
++              if (arg & BIT_SYNC)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, bit);
++              else if (arg & WORD_SYNC)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, word);
++              else if (arg & EXTENDED_SYNC)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, extended);
++              
++              if (arg & SYNC_ON)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_sync, on);
++              else if (arg & SYNC_OFF)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_sync, off);
++              
++              if (arg & WORD_SIZE_8)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size8bit);
++              else if (arg & WORD_SIZE_12)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size12bit);
++              else if (arg & WORD_SIZE_16)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size16bit);
++              else if (arg & WORD_SIZE_24)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size24bit);
++              else if (arg & WORD_SIZE_32)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size32bit);
++              
++              if (arg & BIT_ORDER_MSB)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, bitorder, msb);
++              else if (arg & BIT_ORDER_LSB)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, bitorder, lsb);
++              
++              if (arg & FLOW_CONTROL_ENABLE)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, flow_ctrl, enabled);
++              else if (arg & FLOW_CONTROL_DISABLE)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, flow_ctrl, disabled);
++              
++              if (arg & CLOCK_NOT_GATED)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_mode, normal);
++              else if (arg & CLOCK_GATED)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_mode, gated);    
++              
++              break;
++      case SSP_IPOLARITY:
++              /* NOTE!! negedge is considered NORMAL */
++              if (arg & CLOCK_NORMAL)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_polarity, neg);
++              else if (arg & CLOCK_INVERT)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_polarity, pos);
++              
++              if (arg & FRAME_NORMAL)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_polarity, normal);
++              else if (arg & FRAME_INVERT)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_polarity, inverted);
++              
++              if (arg & STATUS_NORMAL)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, status_polarity, normal);
++              else if (arg & STATUS_INVERT)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, status_polarity, inverted);
++              break;
++      case SSP_OPOLARITY:
++              if (arg & CLOCK_NORMAL)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_driver, normal);
++              else if (arg & CLOCK_INVERT)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_driver, inverted);
++              
++              if (arg & FRAME_NORMAL)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_driver, normal);
++              else if (arg & FRAME_INVERT)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_driver, inverted);
++              
++              if (arg & STATUS_NORMAL)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, status_driver, normal);
++              else if (arg & STATUS_INVERT)
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, status_driver, inverted);
++              break;
++      case SSP_SPI:
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, flow_ctrl, disabled);
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, bitorder, msb);
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, wordsize, size8bit);
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_sync, on);
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_syncsize, word);
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, f_synctype, normal);
++              if (arg & SPI_SLAVE)
++              {
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_polarity, inverted);
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_polarity, neg);
++                      SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, mode, SLAVE_INPUT);
++              }
++              else
++              {
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, frame_driver, inverted);
++                      SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_driver, inverted);
++                      SETF(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, mode, MASTER_OUTPUT);
++              }
++              break;
++      case SSP_INBUFCHUNK:
++#if 0
++              if (arg > port->in_buffer_size/NUM_IN_DESCR)
++                      return -EINVAL;
++              port->inbufchunk = arg;
++              /* Make sure in_buffer_size is a multiple of inbufchunk */
++              port->in_buffer_size = (port->in_buffer_size/port->inbufchunk) * port->inbufchunk;
++              DEBUG(printk("inbufchunk %i in_buffer_size: %i\n", port->inbufchunk, port->in_buffer_size));
++              if (port->use_dma) {
++                      if (port->port_nbr == 0) {
++                              RESET_DMA(9);
++                              WAIT_DMA(9);
++                      } else {
++                              RESET_DMA(5);
++                              WAIT_DMA(5);
++                      }
++                      start_dma_in(port);
++              }
++#endif
++              break;
++      default:
++              return_val = -1;
++      }
++      /* Make sure we write the config without interruption */
++      local_irq_save(flags);
++      /* Set config and enable port */
++      *port->ctrl_data = port->ctrl_data_shadow;
++      nop(); nop(); nop(); nop();
++      *R_SYNC_SERIAL_PRESCALE = sync_serial_prescale_shadow;
++      nop(); nop(); nop(); nop();
++      if (dev)
++              SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode3, sync);
++      else
++              SETS(gen_config_ii_shadow, R_GEN_CONFIG_II, sermode1, sync);
++
++      *R_GEN_CONFIG_II = gen_config_ii_shadow;
++      /* Reset DMA. At readout from serial port the data could be shifted
++       * one byte if not resetting DMA.
++       */
++      if (port->use_dma) {
++              if (port->port_nbr == 0) {
++                      RESET_DMA(9);
++                      WAIT_DMA(9);
++              } else {
++                      RESET_DMA(5);
++                      WAIT_DMA(5);
++              }
++              start_dma_in(port);
++      }
++      local_irq_restore(flags);
++      return return_val;
++}
++
++
++static ssize_t sync_serial_write(struct file * file, const char * buf, 
++                                 size_t count, loff_t *ppos)
++{
++      int dev = MINOR(file->f_dentry->d_inode->i_rdev);
++      DECLARE_WAITQUEUE(wait, current);       
++      sync_port *port;
++      unsigned long flags;
++      unsigned long c, c1;
++      unsigned long free_outp;
++      unsigned long outp;
++      unsigned long out_buffer;
++
++      if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
++      {
++              DEBUG(printk("Invalid minor %d\n", dev));
++              return -ENODEV;
++      }
++      port = &ports[dev];
++
++      DEBUGWRITE(printk("W d%d c %lu (%d/%d)\n", port->port_nbr, count, port->out_count, OUT_BUFFER_SIZE));
++      /* Space to end of buffer */
++      /* 
++       * out_buffer <c1>012345<-   c    ->OUT_BUFFER_SIZE
++       *            outp^    +out_count
++                              ^free_outp
++       * out_buffer 45<-     c      ->0123OUT_BUFFER_SIZE
++       *             +out_count   outp^
++       *              free_outp
++       *
++       */
++
++      /* Read variables that may be updated by interrupts */
++      local_irq_save(flags);
++      count = count > OUT_BUFFER_SIZE - port->out_count ? OUT_BUFFER_SIZE  - port->out_count : count;
++      outp = (unsigned long)port->outp;
++      free_outp = outp + port->out_count;
++      local_irq_restore(flags);
++      out_buffer = (unsigned long)port->out_buffer;
++      
++      /* Find out where and how much to write */
++      if (free_outp >= out_buffer + OUT_BUFFER_SIZE)
++              free_outp -= OUT_BUFFER_SIZE;
++      if (free_outp >= outp)
++              c = out_buffer + OUT_BUFFER_SIZE - free_outp;
++      else
++              c = outp - free_outp;
++      if (c > count)
++              c = count;
++      
++//    DEBUGWRITE(printk("w op %08lX fop %08lX c %lu\n", outp, free_outp, c));
++      if (copy_from_user((void*)free_outp, buf, c))
++              return -EFAULT;
++
++      if (c != count) {
++              buf += c;
++              c1 = count - c;
++              DEBUGWRITE(printk("w2 fi %lu c %lu c1 %lu\n", free_outp-out_buffer, c, c1));
++              if (copy_from_user((void*)out_buffer, buf, c1))
++                      return -EFAULT;
++      }
++      local_irq_save(flags);
++      port->out_count += count;
++      local_irq_restore(flags);
++
++      /* Make sure transmitter/receiver is running */
++      if (!port->started)
++      {
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, running);
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, enable);
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, enable);
++              port->started = 1;
++      }
++
++      *port->ctrl_data = port->ctrl_data_shadow;
++
++      if (file->f_flags & O_NONBLOCK) {
++              local_irq_save(flags);
++              if (!port->tr_running) {
++                      if (!port->use_dma) {
++                              /* Start sender by writing data */
++                              send_word(port);
++                              /* and enable transmitter ready IRQ */
++                              *R_IRQ_MASK1_SET = 1 << port->transmitter_ready_bit;
++                      } else {
++                              start_dma(port, (unsigned char* volatile )port->outp, c);
++                      }
++              }
++              local_irq_restore(flags);
++              DEBUGWRITE(printk("w d%d c %lu NB\n",
++                                port->port_nbr, count));
++              return count;
++      }
++
++      /* Sleep until all sent */
++      
++      add_wait_queue(&port->out_wait_q, &wait);
++      set_current_state(TASK_INTERRUPTIBLE);
++      local_irq_save(flags);
++      if (!port->tr_running) {
++              if (!port->use_dma) {
++                      /* Start sender by writing data */
++                      send_word(port);
++                      /* and enable transmitter ready IRQ */
++                      *R_IRQ_MASK1_SET = 1 << port->transmitter_ready_bit;
++              } else {
++                      start_dma(port, port->outp, c);
++              }
++      }
++      local_irq_restore(flags);
++      schedule();
++      set_current_state(TASK_RUNNING);
++      remove_wait_queue(&port->out_wait_q, &wait);
++      if (signal_pending(current))
++      {
++              return -EINTR;
++      }
++      DEBUGWRITE(printk("w d%d c %lu\n", port->port_nbr, count));
++      return count;
++}
++
++static ssize_t sync_serial_read(struct file * file, char * buf, 
++                              size_t count, loff_t *ppos)
++{
++      int dev = MINOR(file->f_dentry->d_inode->i_rdev);
++      int avail;
++      sync_port *port;
++      unsigned char* start; 
++      unsigned char* end;
++      unsigned long flags;    
++
++      if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
++      {
++              DEBUG(printk("Invalid minor %d\n", dev));
++              return -ENODEV;
++      }
++      port = &ports[dev];
++
++      DEBUGREAD(printk("R%d c %d ri %lu wi %lu /%lu\n", dev, count, port->readp - port->flip, port->writep - port->flip, port->in_buffer_size));
++
++      if (!port->started)
++      {
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, clk_halt, running);
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, tr_enable, enable);
++              SETS(port->ctrl_data_shadow, R_SYNC_SERIAL1_CTRL, rec_enable, enable);
++              port->started = 1;
++      }
++      *port->ctrl_data = port->ctrl_data_shadow;
++
++      
++      /* Calculate number of available bytes */
++      /* Save pointers to avoid that they are modified by interrupt */
++      local_irq_save(flags);
++      start = (unsigned char*)port->readp; /* cast away volatile */
++      end = (unsigned char*)port->writep;  /* cast away volatile */
++      local_irq_restore(flags);
++      while ((start == end) && !port->full) /* No data */
++      {
++              if (file->f_flags & O_NONBLOCK)
++              {  
++                      return -EAGAIN;
++              }
++          
++              interruptible_sleep_on(&port->in_wait_q);
++              if (signal_pending(current))
++              {
++                      return -EINTR;
++              }
++              local_irq_save(flags);
++              start = (unsigned char*)port->readp; /* cast away volatile */
++              end = (unsigned char*)port->writep;  /* cast away volatile */
++              local_irq_restore(flags);
++      }
++
++      /* Lazy read, never return wrapped data. */
++      if (port->full)
++              avail = port->in_buffer_size;
++      else if (end > start)
++              avail = end - start;
++      else 
++              avail = port->flip + port->in_buffer_size - start;
++  
++      count = count > avail ? avail : count;
++      if (copy_to_user(buf, start, count))
++              return -EFAULT;
++      /* Disable interrupts while updating readp */
++      local_irq_save(flags);
++      port->readp += count;
++      if (port->readp >= port->flip + port->in_buffer_size) /* Wrap? */
++              port->readp = port->flip;
++      port->full = 0;
++      local_irq_restore(flags);
++      DEBUGREAD(printk("r %d\n", count));
++      return count;
++}
++
++static void send_word(sync_port* port)
++{
++      switch(IO_EXTRACT(R_SYNC_SERIAL1_CTRL, wordsize, port->ctrl_data_shadow))
++      {
++       case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit):
++               port->out_count--;
++               *port->data_out = *port->outp++;
++               if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
++                       port->outp = port->out_buffer;
++               break;
++      case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size12bit):
++      {
++              int data = (*port->outp++) << 8;
++              data |= *port->outp++;
++              port->out_count-=2;  
++              *port->data_out = data;
++              if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
++                      port->outp = port->out_buffer;
++      }
++      break;
++      case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size16bit):
++              port->out_count-=2;
++              *port->data_out = *(unsigned short *)port->outp;
++              port->outp+=2;
++              if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
++                      port->outp = port->out_buffer;
++              break;
++      case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size24bit):
++              port->out_count-=3;
++              *port->data_out = *(unsigned int *)port->outp;
++              port->outp+=3;
++              if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
++                      port->outp = port->out_buffer;
++              break;
++      case IO_STATE_VALUE(R_SYNC_SERIAL1_CTRL, wordsize, size32bit):
++              port->out_count-=4;
++              *port->data_out = *(unsigned int *)port->outp;
++              port->outp+=4;
++              if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
++                      port->outp = port->out_buffer;
++              break;
++      }
++}
++
++
++static void start_dma(struct sync_port* port, const char* data, int count)
++{
++      port->tr_running = 1;
++      port->out_descr.hw_len = 0;
++      port->out_descr.next = 0;
++      port->out_descr.ctrl = d_eol | d_eop; /* No d_wait to avoid glitches */
++      port->out_descr.sw_len = count;
++      port->out_descr.buf = virt_to_phys((char*)data);
++      port->out_descr.status = 0;
++
++      *port->output_dma_first = virt_to_phys(&port->out_descr);
++      *port->output_dma_cmd = IO_STATE(R_DMA_CH0_CMD, cmd, start);
++      DEBUGTXINT(printk("dma %08lX c %d\n", (unsigned long)data, count));
++}
++
++static void start_dma_in(sync_port* port)
++{
++      int i;
++      unsigned long buf;
++      port->writep = port->flip;
++      
++      if (port->writep > port->flip + port->in_buffer_size)
++      {
++              panic("Offset too large in sync serial driver\n");
++              return;
++      }
++      buf = virt_to_phys(port->in_buffer);
++      for (i = 0; i < NUM_IN_DESCR; i++) {
++              port->in_descr[i].sw_len = port->inbufchunk;
++              port->in_descr[i].ctrl = d_int;
++              port->in_descr[i].next = virt_to_phys(&port->in_descr[i+1]);
++              port->in_descr[i].buf = buf;
++              port->in_descr[i].hw_len = 0;
++              port->in_descr[i].status = 0;
++              port->in_descr[i].fifo_len = 0;
++              buf += port->inbufchunk;
++              prepare_rx_descriptor(&port->in_descr[i]);
++      }
++      /* Link the last descriptor to the first */
++      port->in_descr[i-1].next = virt_to_phys(&port->in_descr[0]);
++      port->in_descr[i-1].ctrl |= d_eol;
++      port->next_rx_desc = &port->in_descr[0];
++      port->prev_rx_desc = &port->in_descr[NUM_IN_DESCR - 1];
++      *port->input_dma_first = virt_to_phys(port->next_rx_desc);
++      *port->input_dma_cmd = IO_STATE(R_DMA_CH0_CMD, cmd, start);
++}
++
++#ifdef SYNC_SER_DMA
++static irqreturn_t tr_interrupt(int irq, void *dev_id)
++{
++      unsigned long ireg = *R_IRQ_MASK2_RD;
++      int i;
++      struct etrax_dma_descr *descr;
++      unsigned int sentl;
++      int handled = 0;
++
++      for (i = 0; i < NUMBER_OF_PORTS; i++) 
++      {
++              sync_port *port = &ports[i];
++              if (!port->enabled  || !port->use_dma )
++                      continue;
++
++              if (ireg & (1 << port->output_dma_bit)) /* IRQ active for the port? */
++              {
++                      handled = 1;
++
++                      /* Clear IRQ */
++                      *port->output_dma_clr_irq = 
++                        IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do) |
++                        IO_STATE(R_DMA_CH0_CLR_INTR, clr_descr, do);
++
++                      descr = &port->out_descr;
++                      if (!(descr->status & d_stop)) {
++                              sentl = descr->sw_len;
++                      } else 
++                              /* otherwise we find the amount of data sent here */
++                              sentl = descr->hw_len;
++                      port->out_count -= sentl;
++                      port->outp += sentl;
++                      if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
++                              port->outp = port->out_buffer;
++                      if (port->out_count)  {
++                              int c;
++                              c = port->out_buffer + OUT_BUFFER_SIZE - port->outp;
++                              if (c > port->out_count)
++                                      c = port->out_count;
++                              DEBUGTXINT(printk("tx_int DMAWRITE %i %i\n", sentl, c));
++                              start_dma(port, port->outp, c);
++                      } else  {
++                              DEBUGTXINT(printk("tx_int DMA stop %i\n", sentl));                              
++                              port->tr_running = 0;
++                      }
++                      wake_up_interruptible(&port->out_wait_q); /* wake up the waiting process */
++              } 
++      }
++      return IRQ_RETVAL(handled);
++} /* tr_interrupt */
++
++static irqreturn_t rx_interrupt(int irq, void *dev_id)
++{
++      unsigned long ireg = *R_IRQ_MASK2_RD;
++      int i;
++      int handled = 0;
++
++      for (i = 0; i < NUMBER_OF_PORTS; i++) 
++      {
++              sync_port *port = &ports[i];
++
++              if (!port->enabled || !port->use_dma )
++                      continue;
++
++              if (ireg & (1 << port->input_dma_descr_bit)) /* Descriptor interrupt */
++              {
++                      handled = 1;
++                      while (*port->input_dma_descr != virt_to_phys(port->next_rx_desc)) {
++                      
++                              if (port->writep + port->inbufchunk > port->flip + port->in_buffer_size) {
++                                      int first_size = port->flip + port->in_buffer_size - port->writep;
++                                      memcpy(port->writep, phys_to_virt(port->next_rx_desc->buf), first_size);
++                                      memcpy(port->flip, phys_to_virt(port->next_rx_desc->buf+first_size), port->inbufchunk - first_size);
++                                      port->writep = port->flip + port->inbufchunk - first_size;
++                              } else {
++                                      memcpy(port->writep, phys_to_virt(port->next_rx_desc->buf), port->inbufchunk);
++                                      port->writep += port->inbufchunk;
++                                      if (port->writep >= port->flip + port->in_buffer_size)
++                                              port->writep = port->flip;
++                              }
++                                if (port->writep == port->readp)
++                                {
++                                port->full = 1;
++                                }
++                                
++                              prepare_rx_descriptor(port->next_rx_desc);
++                              port->next_rx_desc->ctrl |= d_eol;
++                              port->prev_rx_desc->ctrl &= ~d_eol;
++                              port->prev_rx_desc = phys_to_virt((unsigned)port->next_rx_desc);
++                              port->next_rx_desc = phys_to_virt((unsigned)port->next_rx_desc->next);
++                              wake_up_interruptible(&port->in_wait_q); /* wake up the waiting process */
++                              *port->input_dma_cmd =  IO_STATE(R_DMA_CH1_CMD, cmd, restart);
++                              /* DMA has reached end of descriptor */
++                              *port->input_dma_clr_irq = 
++                                IO_STATE(R_DMA_CH0_CLR_INTR, clr_descr, do);
++                      }
++              }
++      }
++      
++      return IRQ_RETVAL(handled);
++} /* rx_interrupt */
++#endif /* SYNC_SER_DMA */
++
++#ifdef SYNC_SER_MANUAL
++static irqreturn_t manual_interrupt(int irq, void *dev_id)
++{
++      int i;
++      int handled = 0;
++
++      for (i = 0; i < NUMBER_OF_PORTS; i++)
++      {
++              sync_port* port = &ports[i];
++
++              if (!port->enabled || port->use_dma)
++              {
++                      continue;
++              }
++
++              if (*R_IRQ_MASK1_RD & (1 << port->data_avail_bit))      /* Data received? */
++              {
++                      handled = 1;
++                      /* Read data */
++                      switch(port->ctrl_data_shadow & IO_MASK(R_SYNC_SERIAL1_CTRL, wordsize))
++                      {
++                      case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size8bit):
++                              *port->writep++ = *(volatile char *)port->data_in;
++                              break;
++                      case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size12bit):
++                      {
++                              int data = *(unsigned short *)port->data_in;
++                              *port->writep = (data & 0x0ff0) >> 4;
++                              *(port->writep + 1) = data & 0x0f;
++                              port->writep+=2;
++                      }
++                      break;
++                      case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size16bit):
++                              *(unsigned short*)port->writep = *(volatile unsigned short *)port->data_in;
++                              port->writep+=2;
++                              break;
++                      case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size24bit):
++                              *(unsigned int*)port->writep = *port->data_in;
++                              port->writep+=3;
++                              break;
++                      case IO_STATE(R_SYNC_SERIAL1_CTRL, wordsize, size32bit):
++                              *(unsigned int*)port->writep = *port->data_in;
++                              port->writep+=4;
++                              break;
++                      }
++
++                      if (port->writep >= port->flip + port->in_buffer_size) /* Wrap? */
++                              port->writep = port->flip;
++                      if (port->writep == port->readp) {
++                              /* receive buffer overrun, discard oldest data
++                               */
++                              port->readp++;
++                              if (port->readp >= port->flip + port->in_buffer_size) /* Wrap? */
++                                      port->readp = port->flip;
++                      }
++                      if (sync_data_avail(port) >= port->inbufchunk)
++                              wake_up_interruptible(&port->in_wait_q); /* Wake up application */
++              }
++
++              if (*R_IRQ_MASK1_RD & (1 << port->transmitter_ready_bit)) /* Transmitter ready? */
++              {
++                      if (port->out_count > 0) /* More data to send */
++                              send_word(port);
++                      else /* transmission finished */
++                      {
++                              *R_IRQ_MASK1_CLR = 1 << port->transmitter_ready_bit; /* Turn off IRQ */
++                              wake_up_interruptible(&port->out_wait_q); /* Wake up application */
++                      }
++              }
++      }
++      return IRQ_RETVAL(handled);
++}
++#endif
++
++module_init(etrax_sync_serial_init);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/debugport.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/debugport.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/debugport.c   2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/debugport.c   2006-10-30 16:17:57.000000000 +0100
+@@ -12,6 +12,34 @@
+  *    init_etrax_debug()
+  *
+  * $Log: debugport.c,v $
++ * Revision 1.36  2006/10/30 15:17:57  pkj
++ * Avoid a compiler warning.
++ *
++ * Revision 1.35  2006/10/13 12:43:11  starvik
++ * Merge of 2.6.18
++ *
++ * Revision 1.34  2006/09/29 10:32:01  starvik
++ * Don't reference serial driver if not present
++ *
++ * Revision 1.33  2006/09/08 07:59:29  karljope
++ * Makes v10 boot again when watchdog is enabled
++ *
++ * Revision 1.32  2006/06/20 08:23:36  pkj
++ * Reverted incorrect merge.
++ *
++ * Revision 1.31  2006/05/17 12:22:10  edgar
++ * check port before disable ints
++ *
++ * Revision 1.30  2005/11/15 12:08:42  starvik
++ * Set index when no debug port is defined
++ *
++ * Revision 1.29  2005/08/29 07:32:17  starvik
++ * Merge of 2.6.13
++ *
++ * Revision 1.28  2005/07/02 12:29:35  starvik
++ * Use the generic oops_in_progress instead of the raw_printk hack.
++ * Moved some functions to achr-independent code.
++ *
+  * Revision 1.27  2005/06/10 10:34:14  starvik
+  * Real console support
+  *
+@@ -112,6 +140,8 @@
+ #include <asm/arch/svinto.h>
+ #include <asm/io.h>             /* Get SIMCOUT. */
++extern void reset_watchdog(void);
++
+ struct dbg_port
+ {
+   unsigned int index;
+@@ -188,7 +218,9 @@
+   }
+ };
++#ifdef CONFIG_ETRAX_SERIAL
+ extern struct tty_driver *serial_driver;
++#endif
+ struct dbg_port* port =
+ #if defined(CONFIG_ETRAX_DEBUG_PORT0)
+@@ -368,11 +400,12 @@
+ {
+       int i;
+       unsigned long flags;
+-      local_irq_save(flags);
+-
++      
+         if (!port)
+               return;
+-
++      
++      local_irq_save(flags);
++      
+       /* Send data */
+       for (i = 0; i < len; i++) {
+               /* LF -> CRLF */
+@@ -386,26 +419,16 @@
+                       ;
+               *port->write = buf[i];
+       }
+-      local_irq_restore(flags);
+-}
+-int raw_printk(const char *fmt, ...)
+-{
+-      static char buf[1024];
+-      int printed_len;
+-      static int first = 1;
+-      if (first) {
+-              /* Force reinitialization of the port to get manual mode. */
+-              port->started = 0;
+-              start_port(port);
+-              first = 0;
+-      }
+-      va_list args;
+-      va_start(args, fmt);
+-      printed_len = vsnprintf(buf, sizeof(buf), fmt, args);
+-      va_end(args);
+-      console_write_direct(NULL, buf, strlen(buf));
+-      return printed_len;
++      /*
++       * Feed the watchdog, otherwise it will reset the chip during boot. 
++       * The time to send an ordinary boot message line (10-90 chars) 
++       * varies between 1-8ms at 115200. What makes up for the additional 
++       * 90ms that allows the watchdog to bite?
++      */
++      reset_watchdog();
++
++      local_irq_restore(flags);
+ }
+ static void
+@@ -500,6 +523,7 @@
+       return 0;
+ }
++
+ /* This is a dummy serial device that throws away anything written to it.
+  * This is used when no debug output is wanted.
+  */
+@@ -555,7 +579,13 @@
+ {
+       if (port)
+               *index = port->index;
++      else
++              *index = 0;
++#ifdef CONFIG_ETRAX_SERIAL
+         return port ? serial_driver : &dummy_driver;
++#else
++      return &dummy_driver;
++#endif
+ }
+ static struct console sercons = {
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/entry.S linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/entry.S
+--- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/entry.S       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/entry.S       2007-01-09 10:36:17.000000000 +0100
+@@ -1,4 +1,4 @@
+-/* $Id: entry.S,v 1.28 2005/06/20 05:06:30 starvik Exp $
++/* $Id: entry.S,v 1.38 2007/01/09 09:36:17 starvik Exp $
+  *
+  *  linux/arch/cris/entry.S
+  *
+@@ -7,6 +7,41 @@
+  *  Authors:  Bjorn Wesen (bjornw@axis.com)
+  *
+  *  $Log: entry.S,v $
++ *  Revision 1.38  2007/01/09 09:36:17  starvik
++ *  Corrected kernel_execve
++ *
++ *  Revision 1.37  2007/01/09 09:29:18  starvik
++ *  Merge of Linux 2.6.19
++ *
++ *  Revision 1.36  2006/12/08 13:08:54  orjanf
++ *  Copied from Linux 2.4:
++ *  * Return from an pin-generated NMI the same way as for other interrupts.
++ *  * Reverse check order between external nmi and watchdog nmi to avoid false
++ *    watchdog oops in case of a glitch on the nmi pin.
++ *
++ *  Revision 1.35  2006/10/13 12:43:11  starvik
++ *  Merge of 2.6.18
++ *
++ *  Revision 1.34  2006/06/25 15:00:09  starvik
++ *  Merge of Linux 2.6.17
++ *
++ *  Revision 1.33  2006/05/19 12:23:09  orjanf
++ *  * Moved blocking of ethernet rx/tx irq from ethernet interrupt handler to
++ *    low-level asm interrupt handlers.  Fixed in the multiple interrupt handler
++ *    also.  Copied from Linux 2.4.
++ *
++ *  Revision 1.32  2006/03/23 14:53:57  starvik
++ *  Corrected signal handling.
++ *
++ *  Revision 1.31  2006/03/22 09:56:55  starvik
++ *  Merge of Linux 2.6.16
++ *
++ *  Revision 1.30  2005/10/31 08:48:03  starvik
++ *  Merge of Linux 2.6.14
++ *
++ *  Revision 1.29  2005/08/29 07:32:17  starvik
++ *  Merge of 2.6.13
++ *
+  *  Revision 1.28  2005/06/20 05:06:30  starvik
+  *  Remove unnecessary diff to kernel.org tree
+  *
+@@ -500,9 +535,8 @@
+       ;; deal with pending signals and notify-resume requests
+       move.d  $r9, $r10       ; do_notify_resume syscall/irq param
+-      moveq   0, $r11         ; oldset param - 0 in this case
+-      move.d  $sp, $r12       ; the regs param
+-      move.d  $r1, $r13       ; the thread_info_flags parameter
++      move.d  $sp, $r11       ; the regs param
++      move.d  $r1, $r12       ; the thread_info_flags parameter
+       jsr     do_notify_resume
+       
+       ba _Rexit
+@@ -653,7 +687,7 @@
+       ;; special handlers for breakpoint and NMI
+ hwbreakpoint:
+       push    $dccr
+-      di
++      di      
+       push    $r10
+       push    $r11
+       move.d  [hw_bp_trig_ptr],$r10
+@@ -678,13 +712,19 @@
+       push    $r10            ; push orig_r10
+       clear.d [$sp=$sp-4]     ; frametype == 0, normal frame
++      ;; If there is a glitch on the NMI pin shorter than ~100ns 
++      ;; (i.e. non-active by the time we get here) then the nmi_pin bit
++      ;; in R_IRQ_MASK0_RD will already be cleared.  The watchdog_nmi bit
++      ;; is cleared by us however (when feeding the watchdog), which is why
++      ;; we use that bit to determine what brought us here.
++      
+       move.d  [R_IRQ_MASK0_RD], $r1 ; External NMI or watchdog?
+-      and.d   0x80000000, $r1
+-      beq     wdog
++      and.d   (1<<30), $r1
++      bne     wdog
+       move.d  $sp, $r10
+       jsr     handle_nmi
+       setf m                  ; Enable NMI again
+-      retb                    ; Return from NMI
++      ba      _Rexit          ; Return the standard way
+       nop
+ wdog:
+ #if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
+@@ -774,23 +814,10 @@
+       movem   $r13, [$sp]
+       push    $r10            ; push orig_r10
+       clear.d [$sp=$sp-4]     ; frametype == 0, normal frame
+-      
+-      moveq   2, $r2          ; first bit we care about is the timer0 irq
+-      move.d  [R_VECT_MASK_RD], $r0; read the irq bits that triggered the multiple irq
+-      move.d  $r0, [R_VECT_MASK_CLR] ; Block all active IRQs
+-1:    
+-      btst    $r2, $r0        ; check for the irq given by bit r2
+-      bpl     2f
+-      move.d  $r2, $r10       ; First argument to do_IRQ
+-      move.d  $sp, $r11       ; second argument to do_IRQ
+-      jsr     do_IRQ
+-2:
+-      addq    1, $r2          ; next vector bit
+-      cmp.b   32, $r2
+-      bne     1b      ; process all irq's up to and including number 31
+-      moveq   0, $r9  ; make ret_from_intr realise we came from an ir
+-      
+-      move.d  $r0, [R_VECT_MASK_SET] ;  Unblock all the IRQs
++
++      move.d  $sp, $r10
++      jsr     do_multiple_IRQ
++
+       jump    ret_from_intr
+ do_sigtrap:
+@@ -836,6 +863,13 @@
+       pop     $r0                     ; Restore r0. 
+       ba      do_sigtrap              ; SIGTRAP the offending process. 
+       pop     $dccr                   ; Restore dccr in delay slot.
++
++      .global kernel_execve
++kernel_execve:
++      move.d __NR_execve, $r9
++      break 13
++      ret
++      nop
+       
+       .data
+@@ -1135,7 +1169,38 @@
+       .long sys_add_key
+       .long sys_request_key
+       .long sys_keyctl
+-
++      .long sys_ioprio_set
++      .long sys_ioprio_get            /* 290 */
++      .long sys_inotify_init
++      .long sys_inotify_add_watch
++      .long sys_inotify_rm_watch
++      .long sys_migrate_pages
++      .long sys_openat                /* 295 */
++      .long sys_mkdirat
++      .long sys_mknodat
++      .long sys_fchownat
++      .long sys_futimesat
++      .long sys_fstatat64             /* 300 */
++      .long sys_unlinkat
++      .long sys_renameat
++      .long sys_linkat
++      .long sys_symlinkat
++      .long sys_readlinkat            /* 305 */
++      .long sys_fchmodat
++      .long sys_faccessat
++      .long sys_pselect6
++      .long sys_ppoll
++      .long sys_unshare               /* 310 */
++      .long sys_set_robust_list
++      .long sys_get_robust_list
++      .long sys_splice
++      .long sys_sync_file_range
++      .long sys_tee                   /* 315 */
++      .long sys_vmsplice
++      .long sys_move_pages
++      .long sys_getcpu
++      .long sys_epoll_pwait
++              
+         /*
+          * NOTE!! This doesn't have to be exact - we just have
+          * to make sure we have _enough_ of the "sys_ni_syscall"
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/fasttimer.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/fasttimer.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/fasttimer.c   2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/fasttimer.c   2007-02-05 12:54:34.000000000 +0100
+@@ -1,97 +1,10 @@
+-/* $Id: fasttimer.c,v 1.9 2005/03/04 08:16:16 starvik Exp $
++/* 
+  * linux/arch/cris/kernel/fasttimer.c
+  *
+  * Fast timers for ETRAX100/ETRAX100LX
+  * This may be useful in other OS than Linux so use 2 space indentation...
+  *
+- * $Log: fasttimer.c,v $
+- * Revision 1.9  2005/03/04 08:16:16  starvik
+- * Merge of Linux 2.6.11.
+- *
+- * Revision 1.8  2005/01/05 06:09:29  starvik
+- * cli()/sti() will be obsolete in 2.6.11.
+- *
+- * Revision 1.7  2005/01/03 13:35:46  starvik
+- * Removed obsolete stuff.
+- * Mark fast timer IRQ as not shared.
+- *
+- * Revision 1.6  2004/05/14 10:18:39  starvik
+- * Export fast_timer_list
+- *
+- * Revision 1.5  2004/05/14 07:58:01  starvik
+- * Merge of changes from 2.4
+- *
+- * Revision 1.4  2003/07/04 08:27:41  starvik
+- * Merge of Linux 2.5.74
+- *
+- * Revision 1.3  2002/12/12 08:26:32  starvik
+- * Don't use C-comments inside CVS comments
+- *
+- * Revision 1.2  2002/12/11 15:42:02  starvik
+- * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/
+- *
+- * Revision 1.1  2002/11/18 07:58:06  starvik
+- * Fast timers (from Linux 2.4)
+- *
+- * Revision 1.5  2002/10/15 06:21:39  starvik
+- * Added call to init_waitqueue_head
+- *
+- * Revision 1.4  2002/05/28 17:47:59  johana
+- * Added del_fast_timer()
+- *
+- * Revision 1.3  2002/05/28 16:16:07  johana
+- * Handle empty fast_timer_list
+- *
+- * Revision 1.2  2002/05/27 15:38:42  johana
+- * Made it compile without warnings on Linux 2.4.
+- * (includes, wait_queue, PROC_FS and snprintf)
+- *
+- * Revision 1.1  2002/05/27 15:32:25  johana
+- * arch/etrax100/kernel/fasttimer.c v1.8 from the elinux tree.
+- *
+- * Revision 1.8  2001/11/27 13:50:40  pkj
+- * Disable interrupts while stopping the timer and while modifying the
+- * list of active timers in timer1_handler() as it may be interrupted
+- * by other interrupts (e.g., the serial interrupt) which may add fast
+- * timers.
+- *
+- * Revision 1.7  2001/11/22 11:50:32  pkj
+- * * Only store information about the last 16 timers.
+- * * proc_fasttimer_read() now uses an allocated buffer, since it
+- *   requires more space than just a page even for only writing the
+- *   last 16 timers. The buffer is only allocated on request, so
+- *   unless /proc/fasttimer is read, it is never allocated.
+- * * Renamed fast_timer_started to fast_timers_started to match
+- *   fast_timers_added and fast_timers_expired.
+- * * Some clean-up.
+- *
+- * Revision 1.6  2000/12/13 14:02:08  johana
+- * Removed volatile for fast_timer_list
+- *
+- * Revision 1.5  2000/12/13 13:55:35  johana
+- * Added DEBUG_LOG, added som cli() and cleanup
+- *
+- * Revision 1.4  2000/12/05 13:48:50  johana
+- * Added range check when writing proc file, modified timer int handling
+- *
+- * Revision 1.3  2000/11/23 10:10:20  johana
+- * More debug/logging possibilities.
+- * Moved GET_JIFFIES_USEC() to timex.h and time.c
+- *
+- * Revision 1.2  2000/11/01 13:41:04  johana
+- * Clean up and bugfixes.
+- * Created new do_gettimeofday_fast() that gets a timeval struct
+- * with time based on jiffies and *R_TIMER0_DATA, uses a table
+- * for fast conversion of timer value to microseconds.
+- * (Much faster the standard do_gettimeofday() and we don't really
+- * wan't to use the true time - we wan't the "uptime" so timers don't screw up
+- * when we change the time.
+- * TODO: Add efficient support for continuous timers as well.
+- *
+- * Revision 1.1  2000/10/26 15:49:16  johana
+- * Added fasttimer, highresolution timers.
+- *
+- * Copyright (C) 2000,2001 2002 Axis Communications AB, Lund, Sweden
++ * Copyright (C) 2000-2006 Axis Communications AB, Lund, Sweden
+  */
+ #include <linux/errno.h>
+@@ -136,13 +49,13 @@
+ #define __INLINE__ inline
+-static int fast_timer_running = 0;
+-static int fast_timers_added = 0;
+-static int fast_timers_started = 0;
+-static int fast_timers_expired = 0;
+-static int fast_timers_deleted = 0;
+-static int fast_timer_is_init = 0;
+-static int fast_timer_ints = 0;
++static unsigned int fast_timer_running = 0;
++static unsigned int fast_timers_added = 0;
++static unsigned int fast_timers_started = 0;
++static unsigned int fast_timers_expired = 0;
++static unsigned int fast_timers_deleted = 0;
++static unsigned int fast_timer_is_init = 0;
++static unsigned int fast_timer_ints = 0;
+ struct fast_timer *fast_timer_list = NULL;
+@@ -150,8 +63,8 @@
+ #define DEBUG_LOG_MAX 128
+ static const char * debug_log_string[DEBUG_LOG_MAX];
+ static unsigned long debug_log_value[DEBUG_LOG_MAX];
+-static int debug_log_cnt = 0;
+-static int debug_log_cnt_wrapped = 0;
++static unsigned int debug_log_cnt = 0;
++static unsigned int debug_log_cnt_wrapped = 0;
+ #define DEBUG_LOG(string, value) \
+ { \
+@@ -206,41 +119,25 @@
+ int timer_delay_settings[NUM_TIMER_STATS];
+ /* Not true gettimeofday, only checks the jiffies (uptime) + useconds */
+-void __INLINE__ do_gettimeofday_fast(struct timeval *tv)
++void __INLINE__ do_gettimeofday_fast(struct fasttime_t *tv)
+ {
+-  unsigned long sec = jiffies;
+-  unsigned long usec = GET_JIFFIES_USEC();
+-
+-  usec += (sec % HZ) * (1000000 / HZ);
+-  sec = sec / HZ;
+-
+-  if (usec > 1000000)
+-  {
+-    usec -= 1000000;
+-    sec++;
+-  }
+-  tv->tv_sec = sec;
+-  tv->tv_usec = usec;
++  tv->tv_jiff = jiffies;
++  tv->tv_usec = GET_JIFFIES_USEC();
+ }
+-int __INLINE__ timeval_cmp(struct timeval *t0, struct timeval *t1)
++int __INLINE__ timeval_cmp(struct fasttime_t *t0, struct fasttime_t *t1)
+ {
+-  if (t0->tv_sec < t1->tv_sec)
+-  {
++  /* Compare jiffies. Takes care of wrapping */
++  if (time_before(t0->tv_jiff, t1->tv_jiff))
+     return -1;
+-  }
+-  else if (t0->tv_sec > t1->tv_sec)
+-  {
++  else if (time_after(t0->tv_jiff, t1->tv_jiff))
+     return 1;
+-  }
++
++  /* Compare us */
+   if (t0->tv_usec < t1->tv_usec)
+-  {
+     return -1;
+-  }
+   else if (t0->tv_usec > t1->tv_usec)
+-  {
+     return 1;
+-  }
+   return 0;
+ }
+@@ -340,7 +237,7 @@
+         printk(KERN_WARNING
+                "timer name: %s data: 0x%08lX already in list!\n", name, data);
+         sanity_failed++;
+-        return;
++        goto done;
+       }
+       else
+       {
+@@ -356,11 +253,11 @@
+   t->name = name;
+   t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000;
+-  t->tv_expires.tv_sec  = t->tv_set.tv_sec  + delay_us / 1000000;
++  t->tv_expires.tv_jiff = t->tv_set.tv_jiff + delay_us / 1000000 / HZ;
+   if (t->tv_expires.tv_usec > 1000000)
+   {
+     t->tv_expires.tv_usec -= 1000000;
+-    t->tv_expires.tv_sec++;
++    t->tv_expires.tv_jiff += HZ;
+   }
+ #ifdef FAST_TIMER_LOG
+   timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t;
+@@ -401,6 +298,7 @@
+   D2(printk("start_one_shot_timer: %d us done\n", delay_us));
++done:
+   local_irq_restore(flags);
+ } /* start_one_shot_timer */
+@@ -444,11 +342,18 @@
+ /* Timer 1 interrupt handler */
+ static irqreturn_t
+-timer1_handler(int irq, void *dev_id, struct pt_regs *regs)
++timer1_handler(int irq, void *dev_id)
+ {
+   struct fast_timer *t;
+   unsigned long flags;
++  /* We keep interrupts disabled not only when we modify the 
++   * fast timer list, but any time we hold a reference to a
++   * timer in the list, since del_fast_timer may be called
++   * from (another) interrupt context.  Thus, the only time
++   * when interrupts are enabled is when calling the timer
++   * callback function.
++   */
+   local_irq_save(flags);
+   /* Clear timer1 irq */
+@@ -466,16 +371,16 @@
+   fast_timer_running = 0;
+   fast_timer_ints++;
+-  local_irq_restore(flags);
+-
+   t = fast_timer_list;
+   while (t)
+   {
+-    struct timeval tv;
++    struct fasttime_t tv;
++    fast_timer_function_type *f;
++    unsigned long d;
+     /* Has it really expired? */
+     do_gettimeofday_fast(&tv);
+-    D1(printk("t: %is %06ius\n", tv.tv_sec, tv.tv_usec));
++    D1(printk("t: %is %06ius\n", tv.tv_jiff, tv.tv_usec));
+     if (timeval_cmp(&t->tv_expires, &tv) <= 0)
+     {
+@@ -486,7 +391,6 @@
+       fast_timers_expired++;
+       /* Remove this timer before call, since it may reuse the timer */
+-      local_irq_save(flags);
+       if (t->prev)
+       {
+         t->prev->next = t->next;
+@@ -501,11 +405,21 @@
+       }
+       t->prev = NULL;
+       t->next = NULL;
+-      local_irq_restore(flags);
+-      if (t->function != NULL)
++      /* Save function callback data before enabling interrupts,
++       * since the timer may be removed and we don't know how it
++       * was allocated (e.g. ->function and ->data may become
++       * overwritten after deletion if the timer was stack-allocated).
++       */
++      f = t->function;
++      d = t->data;
++
++      if (f != NULL)
+       {
+-        t->function(t->data);
++        /* Run the callback function with interrupts enabled. */
++        local_irq_restore(flags);
++        f(d);
++        local_irq_save(flags);
+       }
+       else
+       {
+@@ -518,16 +432,19 @@
+       D1(printk(".\n"));
+     }
+-    local_irq_save(flags);
+     if ((t = fast_timer_list) != NULL)
+     {
+       /* Start next timer.. */
+-      long us;
+-      struct timeval tv;
++      long us = 0;
++      struct fasttime_t tv;
+       do_gettimeofday_fast(&tv);
+-      us = ((t->tv_expires.tv_sec - tv.tv_sec) * 1000000 +
+-            t->tv_expires.tv_usec - tv.tv_usec);
++
++      /* time_after_eq takes care of wrapping */
++      if (time_after_eq(t->tv_expires.tv_jiff, tv.tv_jiff))
++      us = ((t->tv_expires.tv_jiff - tv.tv_jiff) * 1000000 / HZ +
++            t->tv_expires.tv_usec - tv.tv_usec);
++
+       if (us > 0)
+       {
+         if (!fast_timer_running)
+@@ -537,7 +454,6 @@
+ #endif
+           start_timer1(us);
+         }
+-        local_irq_restore(flags);
+         break;
+       }
+       else
+@@ -548,9 +464,10 @@
+         D1(printk("e! %d\n", us));
+       }
+     }
+-    local_irq_restore(flags);
+   }
++  local_irq_restore(flags);
++
+   if (!t)
+   {
+     D1(printk("t1 stop!\n"));
+@@ -575,28 +492,17 @@
+ void schedule_usleep(unsigned long us)
+ {
+   struct fast_timer t;
+-#ifdef DECLARE_WAITQUEUE
+   wait_queue_head_t sleep_wait;
+   init_waitqueue_head(&sleep_wait);
+-  {
+-  DECLARE_WAITQUEUE(wait, current);
+-#else
+-  struct wait_queue *sleep_wait = NULL;
+-  struct wait_queue wait = { current, NULL };
+-#endif
+   D1(printk("schedule_usleep(%d)\n", us));
+-  add_wait_queue(&sleep_wait, &wait);
+-  set_current_state(TASK_INTERRUPTIBLE);
+   start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us,
+                        "usleep");
+-  schedule();
+-  set_current_state(TASK_RUNNING);
+-  remove_wait_queue(&sleep_wait, &wait);
++  /* Uninterruptible sleep on the fast timer. (The condition is somewhat
++     redundant since the timer is what wakes us up.) */
++  wait_event(sleep_wait, !fast_timer_pending(&t));
++
+   D1(printk("done schedule_usleep(%d)\n", us));
+-#ifdef DECLARE_WAITQUEUE
+-  }
+-#endif  
+ }
+ #ifdef CONFIG_PROC_FS
+@@ -616,7 +522,7 @@
+   unsigned long flags;
+   int i = 0;
+   int num_to_show;
+-  struct timeval tv;
++  struct fasttime_t tv;
+   struct fast_timer *t, *nextt;
+   static char *bigbuf = NULL;
+   static unsigned long used;
+@@ -624,7 +530,8 @@
+   if (!bigbuf && !(bigbuf = vmalloc(BIG_BUF_SIZE)))
+   {
+     used = 0;
+-    bigbuf[0] = '\0';
++    if (buf)
++          buf[0] = '\0';
+     return 0;
+   }
+@@ -646,7 +553,7 @@
+     used += sprintf(bigbuf + used, "Fast timer running:    %s\n",
+                     fast_timer_running ? "yes" : "no");
+     used += sprintf(bigbuf + used, "Current time:          %lu.%06lu\n",
+-                    (unsigned long)tv.tv_sec,
++                    (unsigned long)tv.tv_jiff,
+                     (unsigned long)tv.tv_usec);
+ #ifdef FAST_TIMER_SANITY_CHECKS
+     used += sprintf(bigbuf + used, "Sanity failed:         %i\n",
+@@ -696,9 +603,9 @@
+                       "d: %6li us data: 0x%08lX"
+                       "\n",
+                       t->name,
+-                      (unsigned long)t->tv_set.tv_sec,
++                      (unsigned long)t->tv_set.tv_jiff,
+                       (unsigned long)t->tv_set.tv_usec,
+-                      (unsigned long)t->tv_expires.tv_sec,
++                      (unsigned long)t->tv_expires.tv_jiff,
+                       (unsigned long)t->tv_expires.tv_usec,
+                       t->delay_us,
+                       t->data
+@@ -718,9 +625,9 @@
+                       "d: %6li us data: 0x%08lX"
+                       "\n",
+                       t->name,
+-                      (unsigned long)t->tv_set.tv_sec,
++                      (unsigned long)t->tv_set.tv_jiff,
+                       (unsigned long)t->tv_set.tv_usec,
+-                      (unsigned long)t->tv_expires.tv_sec,
++                      (unsigned long)t->tv_expires.tv_jiff,
+                       (unsigned long)t->tv_expires.tv_usec,
+                       t->delay_us,
+                       t->data
+@@ -738,9 +645,9 @@
+                       "d: %6li us data: 0x%08lX"
+                       "\n",
+                       t->name,
+-                      (unsigned long)t->tv_set.tv_sec,
++                      (unsigned long)t->tv_set.tv_jiff,
+                       (unsigned long)t->tv_set.tv_usec,
+-                      (unsigned long)t->tv_expires.tv_sec,
++                      (unsigned long)t->tv_expires.tv_jiff,
+                       (unsigned long)t->tv_expires.tv_usec,
+                       t->delay_us,
+                       t->data
+@@ -761,15 +668,15 @@
+ /*                      " func: 0x%08lX" */
+                       "\n",
+                       t->name,
+-                      (unsigned long)t->tv_set.tv_sec,
++                      (unsigned long)t->tv_set.tv_jiff,
+                       (unsigned long)t->tv_set.tv_usec,
+-                      (unsigned long)t->tv_expires.tv_sec,
++                      (unsigned long)t->tv_expires.tv_jiff,
+                       (unsigned long)t->tv_expires.tv_usec,
+                       t->delay_us,
+                       t->data
+ /*                      , t->function */
+                       );
+-      local_irq_disable();
++      local_irq_save(flags);
+       if (t->next != nextt)
+       {
+         printk(KERN_WARNING "timer removed!\n");
+@@ -798,7 +705,7 @@
+ static struct fast_timer tr[10];
+ static int exp_num[10];
+-static struct timeval tv_exp[100];
++static struct fasttime_t tv_exp[100];
+ static void test_timeout(unsigned long data)
+ {
+@@ -836,7 +743,7 @@
+   int prev_num;
+   int j;
+-  struct timeval tv, tv0, tv1, tv2;
++  struct fasttime_t tv, tv0, tv1, tv2;
+   printk("fast_timer_test() start\n");
+   do_gettimeofday_fast(&tv);
+@@ -849,7 +756,7 @@
+   {
+     do_gettimeofday_fast(&tv_exp[j]);
+   }
+-  printk("fast_timer_test() %is %06i\n", tv.tv_sec, tv.tv_usec);
++  printk("fast_timer_test() %is %06i\n", tv.tv_jiff, tv.tv_usec);
+   for (j = 0; j < 1000; j++)
+   {
+@@ -859,11 +766,11 @@
+   for (j = 0; j < 100; j++)
+   {
+     printk("%i.%i %i.%i %i.%i %i.%i %i.%i\n",
+-           tv_exp[j].tv_sec,tv_exp[j].tv_usec,
+-           tv_exp[j+1].tv_sec,tv_exp[j+1].tv_usec,
+-           tv_exp[j+2].tv_sec,tv_exp[j+2].tv_usec,
+-           tv_exp[j+3].tv_sec,tv_exp[j+3].tv_usec,
+-           tv_exp[j+4].tv_sec,tv_exp[j+4].tv_usec);
++           tv_exp[j].tv_jiff,tv_exp[j].tv_usec,
++           tv_exp[j+1].tv_jiff,tv_exp[j+1].tv_usec,
++           tv_exp[j+2].tv_jiff,tv_exp[j+2].tv_usec,
++           tv_exp[j+3].tv_jiff,tv_exp[j+3].tv_usec,
++           tv_exp[j+4].tv_jiff,tv_exp[j+4].tv_usec);
+     j += 4;
+   }
+   do_gettimeofday_fast(&tv0);
+@@ -895,9 +802,9 @@
+     }
+   }
+   do_gettimeofday_fast(&tv2);
+-  printk("Timers started    %is %06i\n", tv0.tv_sec, tv0.tv_usec);
+-  printk("Timers started at %is %06i\n", tv1.tv_sec, tv1.tv_usec);
+-  printk("Timers done       %is %06i\n", tv2.tv_sec, tv2.tv_usec);
++  printk("Timers started    %is %06i\n", tv0.tv_jiff, tv0.tv_usec);
++  printk("Timers started at %is %06i\n", tv1.tv_jiff, tv1.tv_usec);
++  printk("Timers done       %is %06i\n", tv2.tv_jiff, tv2.tv_usec);
+   DP(printk("buf0:\n");
+      printk(buf0);
+      printk("buf1:\n");
+@@ -919,9 +826,9 @@
+     printk("%-10s set: %6is %06ius exp: %6is %06ius "
+            "data: 0x%08X func: 0x%08X\n",
+            t->name,
+-           t->tv_set.tv_sec,
++           t->tv_set.tv_jiff,
+            t->tv_set.tv_usec,
+-           t->tv_expires.tv_sec,
++           t->tv_expires.tv_jiff,
+            t->tv_expires.tv_usec,
+            t->data,
+            t->function
+@@ -929,10 +836,10 @@
+     printk("           del: %6ius     did exp: %6is %06ius as #%i error: %6li\n",
+            t->delay_us,
+-           tv_exp[j].tv_sec,
++           tv_exp[j].tv_jiff,
+            tv_exp[j].tv_usec,
+            exp_num[j],
+-           (tv_exp[j].tv_sec - t->tv_expires.tv_sec)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec);
++           (tv_exp[j].tv_jiff - t->tv_expires.tv_jiff)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec);
+   }
+   proc_fasttimer_read(buf5, NULL, 0, 0, 0);
+   printk("buf5 after all done:\n");
+@@ -942,7 +849,7 @@
+ #endif
+-void fast_timer_init(void)
++int fast_timer_init(void)
+ {
+   /* For some reason, request_irq() hangs when called froom time_init() */
+   if (!fast_timer_is_init)
+@@ -975,4 +882,6 @@
+     fast_timer_test();
+ #endif
+   }
++  return 0;
+ }
++__initcall(fast_timer_init);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/head.S linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/head.S
+--- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/head.S        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/head.S        2006-10-20 09:33:26.000000000 +0200
+@@ -1,4 +1,4 @@
+-/* $Id: head.S,v 1.10 2005/06/20 05:12:54 starvik Exp $
++/* $Id: head.S,v 1.13 2006/10/20 07:33:26 kjelld Exp $
+  * 
+  * Head of the kernel - alter with care
+  *
+@@ -7,6 +7,19 @@
+  * Authors:   Bjorn Wesen (bjornw@axis.com)
+  * 
+  * $Log: head.S,v $
++ * Revision 1.13  2006/10/20 07:33:26  kjelld
++ * If serial port 2 is used, select it in R_GEN_CONFIG.
++ * If serial port 2 is used, setup the control registers for the port.
++ * This is done to avoid a puls on the TXD line during start up.
++ * This puls could disturbe some units (e.g. some Axis 214 with internal
++ * ver. 1.08).
++ *
++ * Revision 1.12  2006/10/13 12:43:11  starvik
++ * Merge of 2.6.18
++ *
++ * Revision 1.11  2005/08/29 07:32:17  starvik
++ * Merge of 2.6.13
++ *
+  * Revision 1.10  2005/06/20 05:12:54  starvik
+  * Remove unnecessary diff to kernel.org tree
+  *
+@@ -595,11 +608,17 @@
+       moveq   0,$r0
++      ;; Select or disable serial port 2 
++#ifdef CONFIG_ETRAX_SERIAL_PORT2
++      or.d      IO_STATE (R_GEN_CONFIG, ser2, select),$r0
++#else
++      or.d      IO_STATE (R_GEN_CONFIG, ser2, disable),$r0    
++#endif
++      
+       ;; Init interfaces (disable them).
+       or.d      IO_STATE (R_GEN_CONFIG, scsi0, disable) \
+               | IO_STATE (R_GEN_CONFIG, ata, disable) \
+               | IO_STATE (R_GEN_CONFIG, par0, disable) \
+-              | IO_STATE (R_GEN_CONFIG, ser2, disable) \
+               | IO_STATE (R_GEN_CONFIG, mio, disable) \
+               | IO_STATE (R_GEN_CONFIG, scsi1, disable) \
+               | IO_STATE (R_GEN_CONFIG, scsi0w, disable) \
+@@ -801,6 +820,41 @@
+               | IO_STATE (R_SERIAL1_TR_CTRL, tr_bitnr, tr_8bit),$r0
+       move.b  $r0,[R_SERIAL1_TR_CTRL]
++#ifdef CONFIG_ETRAX_SERIAL_PORT2      
++      ;; setup the serial port 2 at 115200 baud for debug purposes
++      
++      moveq     IO_STATE (R_SERIAL2_XOFF, tx_stop, enable)            \
++              | IO_STATE (R_SERIAL2_XOFF, auto_xoff, disable)         \
++              | IO_FIELD (R_SERIAL2_XOFF, xoff_char, 0),$r0
++      move.d  $r0,[R_SERIAL2_XOFF] 
++
++      ; 115.2kbaud for both transmit and receive
++      move.b    IO_STATE (R_SERIAL2_BAUD, tr_baud, c115k2Hz)          \
++              | IO_STATE (R_SERIAL2_BAUD, rec_baud, c115k2Hz),$r0
++      move.b  $r0,[R_SERIAL2_BAUD]
++
++      ; Set up and enable the serial2 receiver.
++      move.b    IO_STATE (R_SERIAL2_REC_CTRL, dma_err, stop)          \
++              | IO_STATE (R_SERIAL2_REC_CTRL, rec_enable, enable)     \
++              | IO_STATE (R_SERIAL2_REC_CTRL, rts_, active)           \
++              | IO_STATE (R_SERIAL2_REC_CTRL, sampling, middle)       \
++              | IO_STATE (R_SERIAL2_REC_CTRL, rec_stick_par, normal)  \
++              | IO_STATE (R_SERIAL2_REC_CTRL, rec_par, even)          \
++              | IO_STATE (R_SERIAL2_REC_CTRL, rec_par_en, disable)    \
++              | IO_STATE (R_SERIAL2_REC_CTRL, rec_bitnr, rec_8bit),$r0
++      move.b  $r0,[R_SERIAL2_REC_CTRL] 
++      
++      ; Set up and enable the serial2 transmitter.
++      move.b    IO_FIELD (R_SERIAL2_TR_CTRL, txd, 0)                  \
++              | IO_STATE (R_SERIAL2_TR_CTRL, tr_enable, enable)       \
++              | IO_STATE (R_SERIAL2_TR_CTRL, auto_cts, disabled)      \
++              | IO_STATE (R_SERIAL2_TR_CTRL, stop_bits, one_bit)      \
++              | IO_STATE (R_SERIAL2_TR_CTRL, tr_stick_par, normal)    \
++              | IO_STATE (R_SERIAL2_TR_CTRL, tr_par, even)            \
++              | IO_STATE (R_SERIAL2_TR_CTRL, tr_par_en, disable)      \
++              | IO_STATE (R_SERIAL2_TR_CTRL, tr_bitnr, tr_8bit),$r0
++      move.b  $r0,[R_SERIAL2_TR_CTRL]
++#endif
+       
+ #ifdef CONFIG_ETRAX_SERIAL_PORT3      
+       ;; setup the serial port 3 at 115200 baud for debug purposes
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/io_interface_mux.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/io_interface_mux.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/io_interface_mux.c    2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/io_interface_mux.c    2006-10-04 20:21:18.000000000 +0200
+@@ -1,10 +1,10 @@
+ /* IO interface mux allocator for ETRAX100LX.
+- * Copyright 2004, Axis Communications AB
+- * $Id: io_interface_mux.c,v 1.2 2004/12/21 12:08:38 starvik Exp $
++ * Copyright 2004-2006, Axis Communications AB
++ * $Id: io_interface_mux.c,v 1.4 2006/10/04 18:21:18 henriken Exp $
+  */
+-/* C.f. ETRAX100LX Designer's Reference 20.9 */
++/* C.f. ETRAX100LX Designer's Reference 19.9 */
+ #include <linux/kernel.h>
+ #include <linux/slab.h>
+@@ -35,7 +35,7 @@
+ struct watcher
+ {
+       void (*notify)(const unsigned int gpio_in_available,
+-                     const unsigned int gpio_out_available,
++                     const unsigned int gpio_out_available, 
+                      const unsigned char pa_available,
+                      const unsigned char pb_available);
+       struct watcher *next;
+@@ -45,17 +45,40 @@
+ struct if_group
+ {
+       enum io_if_group        group;
+-      unsigned char           used;
+-      enum cris_io_interface  owner;
++      // name         - the name of the group 'A' to 'F'
++      char                   *name;
++      // used         - a bit mask of all pins in the group in the order listed
++      //                in in the tables in 19.9.1 to 19.9.6.  Note that no
++      //                distinction is made between in, out and in/out pins.
++      unsigned int            used;
+ };
+ struct interface
+ {
+       enum cris_io_interface   ioif;
++      // name - the name of the interface
++      char                    *name;
++      // groups       - OR'ed together io_if_group flags describing what pin groups
++      //                the interface uses pins in.
+       unsigned char            groups;
++      // used         - set when the interface is allocated.
+       unsigned char            used;
+       char                    *owner;
++      // group_a through group_f      - bit masks describing what pins in the
++      //                                pin groups the interface uses.
++      unsigned int             group_a;
++      unsigned int             group_b;
++      unsigned int             group_c;
++      unsigned int             group_d;
++      unsigned int             group_e;
++      unsigned int             group_f;
++
++      // gpio_g_in, gpio_g_out, gpio_b - bit masks telling what pins in the
++      //                                 GPIO ports the interface uses.  This
++      //                                 could be reconstucted using the group_X
++      //                                 masks and a table of what pins the GPIO
++      //                                 ports use, but that would be messy.
+       unsigned int             gpio_g_in;
+       unsigned int             gpio_g_out;
+       unsigned char            gpio_b;
+@@ -64,26 +87,32 @@
+ static struct if_group if_groups[6] = {
+       {
+               .group = group_a,
++              .name = "A",
+               .used = 0,
+       },
+       {
+               .group = group_b,
++              .name = "B",
+               .used = 0,
+       },
+       {
+               .group = group_c,
++              .name = "C",
+               .used = 0,
+       },
+       {
+               .group = group_d,
++              .name = "D",
+               .used = 0,
+       },
+       {
+               .group = group_e,
++              .name = "E",
+               .used = 0,
+       },
+       {
+               .group = group_f,
++              .name = "F",
+               .used = 0,
+       }
+ };
+@@ -94,14 +123,32 @@
+       /* Begin Non-multiplexed interfaces */
+       {
+               .ioif = if_eth,
++              .name = "ethernet",
+               .groups = 0,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0,
++
+               .gpio_g_in = 0,
+               .gpio_g_out = 0,
+               .gpio_b = 0
+       },
+       {
+               .ioif = if_serial_0,
++              .name = "serial_0",
+               .groups = 0,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0,
++
+               .gpio_g_in = 0,
+               .gpio_g_out = 0,
+               .gpio_b = 0
+@@ -109,172 +156,385 @@
+       /* End Non-multiplexed interfaces */
+       {
+               .ioif = if_serial_1,
++              .name = "serial_1",
+               .groups = group_e,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0x0f,
++              .group_f = 0,
++
+               .gpio_g_in =  0x00000000,
+               .gpio_g_out = 0x00000000,
+               .gpio_b = 0x00
+       },
+       {
+               .ioif = if_serial_2,
++              .name = "serial_2",
+               .groups = group_b,
++
++              .group_a = 0,
++              .group_b = 0x0f,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0,
++              
+               .gpio_g_in =  0x000000c0,
+               .gpio_g_out = 0x000000c0,
+               .gpio_b = 0x00
+       },
+       {
+               .ioif = if_serial_3,
++              .name = "serial_3",
+               .groups = group_c,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0x0f,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0,
++
+               .gpio_g_in =  0xc0000000,
+               .gpio_g_out = 0xc0000000,
+               .gpio_b = 0x00
+       },
+       {
+               .ioif = if_sync_serial_1,
+-              .groups = group_e | group_f, /* if_sync_serial_1 and if_sync_serial_3
+-                                             can be used simultaneously */
++              .name = "sync_serial_1",
++              .groups = group_e | group_f,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0x0f,
++              .group_f = 0x10,
++
+               .gpio_g_in =  0x00000000,
+               .gpio_g_out = 0x00000000,
+               .gpio_b = 0x10
+       },
+       {
+               .ioif = if_sync_serial_3,
++              .name = "sync_serial_3",
+               .groups = group_c | group_f,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0x0f,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0x80,
++
+               .gpio_g_in =  0xc0000000,
+               .gpio_g_out = 0xc0000000,
+               .gpio_b = 0x80
+       },
+       {
+               .ioif = if_shared_ram,
++              .name = "shared_ram",
+               .groups = group_a,
++
++              .group_a = 0x7f8ff,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0,
++
+               .gpio_g_in =  0x0000ff3e,
+               .gpio_g_out = 0x0000ff38,
+               .gpio_b = 0x00
+       },
+       {
+               .ioif = if_shared_ram_w,
++              .name = "shared_ram_w",
+               .groups = group_a | group_d,
++
++              .group_a = 0x7f8ff,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0xff,
++              .group_e = 0,
++              .group_f = 0,
++              
+               .gpio_g_in =  0x00ffff3e,
+               .gpio_g_out = 0x00ffff38,
+               .gpio_b = 0x00
+       },
+       {
+               .ioif = if_par_0,
++              .name = "par_0",
+               .groups = group_a,
++
++              .group_a = 0x7fbff,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0,
++
+               .gpio_g_in =  0x0000ff3e,
+               .gpio_g_out = 0x0000ff3e,
+               .gpio_b = 0x00
+       },
+       {
+               .ioif = if_par_1,
++              .name = "par_1",
+               .groups = group_d,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0x7feff,
++              .group_e = 0,
++              .group_f = 0,
++
+               .gpio_g_in =  0x3eff0000,
+               .gpio_g_out = 0x3eff0000,
+               .gpio_b = 0x00
+       },
+       {
+               .ioif = if_par_w,
++              .name = "par_w",
+               .groups = group_a | group_d,
++
++              .group_a = 0x7fbff,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0xff,
++              .group_e = 0,
++              .group_f = 0,
++              
+               .gpio_g_in =  0x00ffff3e,
+               .gpio_g_out = 0x00ffff3e,
+               .gpio_b = 0x00
+       },
+       {
+               .ioif = if_scsi8_0,
+-              .groups = group_a | group_b | group_f, /* if_scsi8_0 and if_scsi8_1
+-                                                        can be used simultaneously */
++              .name = "scsi8_0",
++              .groups = group_a | group_b | group_f,
++
++              .group_a = 0x7ffff,
++              .group_b = 0x0f,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0x10,
++
+               .gpio_g_in =  0x0000ffff,
+               .gpio_g_out = 0x0000ffff,
+               .gpio_b = 0x10
+       },
+       {
+               .ioif = if_scsi8_1,
+-              .groups = group_c | group_d | group_f, /* if_scsi8_0 and if_scsi8_1
+-                                                        can be used simultaneously */
++              .name = "scsi8_1",
++              .groups = group_c | group_d | group_f, 
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0x0f,
++              .group_d = 0x7ffff,
++              .group_e = 0,
++              .group_f = 0x80,
++
+               .gpio_g_in =  0xffff0000,
+               .gpio_g_out = 0xffff0000,
+               .gpio_b = 0x80
+       },
+       {
+               .ioif = if_scsi_w,
++              .name = "scsi_w",
+               .groups = group_a | group_b | group_d | group_f,
++
++              .group_a = 0x7ffff,
++              .group_b = 0x0f,
++              .group_c = 0,
++              .group_d = 0x601ff,
++              .group_e = 0,
++              .group_f = 0x90,
++
+               .gpio_g_in =  0x01ffffff,
+               .gpio_g_out = 0x07ffffff,
+               .gpio_b = 0x80
+       },
+       {
+               .ioif = if_ata,
++              .name = "ata",
+               .groups = group_a | group_b | group_c | group_d,
++
++              .group_a = 0x7ffff,
++              .group_b = 0x0f,
++              .group_c = 0x0f,
++              .group_d = 0x7cfff,
++              .group_e = 0,
++              .group_f = 0,
++
+               .gpio_g_in =  0xf9ffffff,
+               .gpio_g_out = 0xffffffff,
+               .gpio_b = 0x80
+       },
+       {
+               .ioif = if_csp,
+-              .groups = group_f, /* if_csp and if_i2c can be used simultaneously */
++              .name = "csp",
++              .groups = group_f,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0xfc,
++
+               .gpio_g_in =  0x00000000,
+               .gpio_g_out = 0x00000000,
+               .gpio_b = 0xfc
+       },
+       {
+               .ioif = if_i2c,
+-              .groups = group_f, /* if_csp and if_i2c can be used simultaneously */
++              .name = "i2c",
++              .groups = group_f,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0x03,
++
+               .gpio_g_in =  0x00000000,
+               .gpio_g_out = 0x00000000,
+               .gpio_b = 0x03
+       },
+       {
+               .ioif = if_usb_1,
++              .name = "usb_1",
+               .groups = group_e | group_f,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0x0f,
++              .group_f = 0x2c,
++              
+               .gpio_g_in =  0x00000000,
+               .gpio_g_out = 0x00000000,
+               .gpio_b = 0x2c
+       },
+       {
+               .ioif = if_usb_2,
++              .name = "usb_2",
+               .groups = group_d,
+-              .gpio_g_in =  0x0e000000,
+-              .gpio_g_out = 0x3c000000,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0x33e00,
++              .group_f = 0,
++              
++              .gpio_g_in =  0x3e000000,
++              .gpio_g_out = 0x0c000000,
+               .gpio_b = 0x00
+       },
+       /* GPIO pins */
+       {
+               .ioif = if_gpio_grp_a,
++              .name = "gpio_a",
+               .groups = group_a,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0,
++              
+               .gpio_g_in =  0x0000ff3f,
+               .gpio_g_out = 0x0000ff3f,
+               .gpio_b = 0x00
+       },
+       {
+               .ioif = if_gpio_grp_b,
++              .name = "gpio_b",
+               .groups = group_b,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0,
++              
+               .gpio_g_in =  0x000000c0,
+               .gpio_g_out = 0x000000c0,
+               .gpio_b = 0x00
+       },
+       {
+               .ioif = if_gpio_grp_c,
++              .name = "gpio_c",
+               .groups = group_c,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0,
++              
+               .gpio_g_in =  0xc0000000,
+               .gpio_g_out = 0xc0000000,
+               .gpio_b = 0x00
+       },
+       {
+               .ioif = if_gpio_grp_d,
++              .name = "gpio_d",
+               .groups = group_d,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0,
++              
+               .gpio_g_in =  0x3fff0000,
+               .gpio_g_out = 0x3fff0000,
+               .gpio_b = 0x00
+       },
+       {
+               .ioif = if_gpio_grp_e,
++              .name = "gpio_e",
+               .groups = group_e,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0,
++              
+               .gpio_g_in =  0x00000000,
+               .gpio_g_out = 0x00000000,
+               .gpio_b = 0x00
+       },
+       {
+               .ioif = if_gpio_grp_f,
++              .name = "gpio_f",
+               .groups = group_f,
++
++              .group_a = 0,
++              .group_b = 0,
++              .group_c = 0,
++              .group_d = 0,
++              .group_e = 0,
++              .group_f = 0,
++              
+               .gpio_g_in =  0x00000000,
+               .gpio_g_out = 0x00000000,
+               .gpio_b = 0xff
+@@ -284,11 +544,13 @@
+ static struct watcher *watchers = NULL;
++// The pins that are free to use in the GPIO ports.
+ static unsigned int gpio_in_pins =  0xffffffff;
+ static unsigned int gpio_out_pins = 0xffffffff;
+ static unsigned char gpio_pb_pins = 0xff;
+ static unsigned char gpio_pa_pins = 0xff;
++// Identifiers for the owners of the GPIO pins.
+ static enum cris_io_interface gpio_pa_owners[8];
+ static enum cris_io_interface gpio_pb_owners[8];
+ static enum cris_io_interface gpio_pg_owners[32];
+@@ -318,7 +580,7 @@
+       struct watcher *w = watchers;
+       DBG(printk("io_interface_mux: notifying watchers\n"));
+-
++      
+       while (NULL != w) {
+               w->notify((const unsigned int)gpio_in_pins,
+                         (const unsigned int)gpio_out_pins,
+@@ -354,37 +616,48 @@
+       if (interfaces[ioif].used) {
+               local_irq_restore(flags);
+-              printk(KERN_CRIT "cris_io_interface: Cannot allocate interface for %s, in use by %s\n",
++              printk(KERN_CRIT "cris_io_interface: Cannot allocate interface %s for %s, in use by %s\n",
++                     interfaces[ioif].name,
+                      device_id,
+                      interfaces[ioif].owner);
+               return -EBUSY;
+       }
+-      /* Check that all required groups are free before allocating, */
++      /* Check that all required pins in the used groups are free
++       * before allocating. */
+       group_set = interfaces[ioif].groups;
+       while (NULL != (grp = get_group(group_set))) {
+-              if (grp->used) {
+-                      if (grp->group == group_f) {
+-                              if ((if_sync_serial_1 ==  ioif) ||
+-                                  (if_sync_serial_3 ==  ioif)) {
+-                                      if ((grp->owner != if_sync_serial_1) &&
+-                                          (grp->owner != if_sync_serial_3)) {
+-                                              local_irq_restore(flags);
+-                                              return -EBUSY;
+-                                      }
+-                              } else if ((if_scsi8_0 == ioif) ||
+-                                         (if_scsi8_1 == ioif)) {
+-                                      if ((grp->owner != if_scsi8_0) &&
+-                                          (grp->owner != if_scsi8_1)) {
+-                                              local_irq_restore(flags);
+-                                              return -EBUSY;
+-                                      }
+-                              }
+-                      } else {
+-                              local_irq_restore(flags);
+-                              return -EBUSY;
+-                      }
++              unsigned int if_group_use = 0;
++              
++              switch(grp->group) {
++              case group_a:
++                      if_group_use = interfaces[ioif].group_a;
++                      break;
++              case group_b:
++                      if_group_use = interfaces[ioif].group_b;
++                      break;
++              case group_c:
++                      if_group_use = interfaces[ioif].group_c;
++                      break;
++              case group_d:
++                      if_group_use = interfaces[ioif].group_d;
++                      break;
++              case group_e:
++                      if_group_use = interfaces[ioif].group_e;
++                      break;
++              case group_f:
++                      if_group_use = interfaces[ioif].group_f;
++                      break;
++              default:
++                      BUG_ON(1);
+               }
++
++              if(if_group_use & grp->used) {
++                      local_irq_restore(flags);
++                      printk(KERN_INFO "cris_request_io_interface: group %s needed by %s not available\n", grp->name, interfaces[ioif].name);
++                      return -EBUSY;
++              }
++
+               group_set = clear_group_from_set(group_set, grp);
+       }
+@@ -392,22 +665,16 @@
+       if (((interfaces[ioif].gpio_g_in & gpio_in_pins) != interfaces[ioif].gpio_g_in) ||
+           ((interfaces[ioif].gpio_g_out & gpio_out_pins) != interfaces[ioif].gpio_g_out) ||
+           ((interfaces[ioif].gpio_b & gpio_pb_pins) != interfaces[ioif].gpio_b)) {
+-              printk(KERN_CRIT "cris_request_io_interface: Could not get required pins for interface %u\n",
+-                     ioif);
++              local_irq_restore(flags);
++              printk(KERN_CRIT "cris_request_io_interface: Could not get required pins for interface %s\n",
++                     interfaces[ioif].name);
+               return -EBUSY;
+       }
+-      /* All needed I/O pins and pin groups are free, allocate. */
+-      group_set = interfaces[ioif].groups;
+-      while (NULL != (grp = get_group(group_set))) {
+-              grp->used = 1;
+-              grp->owner = ioif;
+-              group_set = clear_group_from_set(group_set, grp);
+-      }
+-
++      /* Check which registers need to be reconfigured. */
+       gens = genconfig_shadow;
+       gens_ii = gen_config_ii_shadow;
+-
++      
+       set_gen_config = 1;
+       switch (ioif)
+       {
+@@ -494,9 +761,43 @@
+               set_gen_config = 0;
+               break;
+       default:
+-              panic("cris_request_io_interface: Bad interface %u submitted for %s\n",
+-                    ioif,
+-                    device_id);
++              local_irq_restore(flags);
++              printk(KERN_INFO "cris_request_io_interface: Bad interface %u submitted for %s\n",
++                     ioif,
++                     device_id);
++              return -EBUSY;
++      }
++
++      /* All needed I/O pins and pin groups are free, allocate. */
++      group_set = interfaces[ioif].groups;
++      while (NULL != (grp = get_group(group_set))) {
++              unsigned int if_group_use = 0;
++
++              switch(grp->group) {
++              case group_a:
++                      if_group_use = interfaces[ioif].group_a;
++                      break;
++              case group_b:
++                      if_group_use = interfaces[ioif].group_b;
++                      break;
++              case group_c:
++                      if_group_use = interfaces[ioif].group_c;
++                      break;
++              case group_d:
++                      if_group_use = interfaces[ioif].group_d;
++                      break;
++              case group_e:
++                      if_group_use = interfaces[ioif].group_e;
++                      break;
++              case group_f:
++                      if_group_use = interfaces[ioif].group_f;
++                      break;
++              default:
++                      BUG_ON(1);
++              }
++              grp->used |= if_group_use;
++
++              group_set = clear_group_from_set(group_set, grp);
+       }
+       interfaces[ioif].used = 1;
+@@ -528,7 +829,7 @@
+       DBG(printk("GPIO pins: available after: g_in=0x%08x g_out=0x%08x pb=0x%02x\n",
+                  gpio_in_pins, gpio_out_pins, gpio_pb_pins));
+-
++      
+       local_irq_restore(flags);
+       notify_watchers();
+@@ -559,43 +860,36 @@
+       }
+       group_set = interfaces[ioif].groups;
+       while (NULL != (grp = get_group(group_set))) {
+-              if (grp->group == group_f) {
+-                      switch (ioif)
+-                      {
+-                      case if_sync_serial_1:
+-                              if ((grp->owner == if_sync_serial_1) &&
+-                                  interfaces[if_sync_serial_3].used) {
+-                                      grp->owner = if_sync_serial_3;
+-                              } else
+-                                      grp->used = 0;
+-                              break;
+-                      case if_sync_serial_3:
+-                              if ((grp->owner == if_sync_serial_3) &&
+-                                  interfaces[if_sync_serial_1].used) {
+-                                      grp->owner = if_sync_serial_1;
+-                              } else
+-                                      grp->used = 0;
+-                              break;
+-                      case if_scsi8_0:
+-                              if ((grp->owner == if_scsi8_0) &&
+-                                  interfaces[if_scsi8_1].used) {
+-                                      grp->owner = if_scsi8_1;
+-                              } else
+-                                      grp->used = 0;
+-                              break;
+-                      case if_scsi8_1:
+-                              if ((grp->owner == if_scsi8_1) &&
+-                                  interfaces[if_scsi8_0].used) {
+-                                      grp->owner = if_scsi8_0;
+-                              } else
+-                                      grp->used = 0;
+-                              break;
+-                      default:
+-                              grp->used = 0;
+-                      }
+-              } else {
+-                      grp->used = 0;
++              unsigned int if_group_use = 0;
++
++              switch(grp->group) {
++              case group_a:
++                      if_group_use = interfaces[ioif].group_a;
++                      break;
++              case group_b:
++                      if_group_use = interfaces[ioif].group_b;
++                      break;
++              case group_c:
++                      if_group_use = interfaces[ioif].group_c;
++                      break;
++              case group_d:
++                      if_group_use = interfaces[ioif].group_d;
++                      break;
++              case group_e:
++                      if_group_use = interfaces[ioif].group_e;
++                      break;
++              case group_f:
++                      if_group_use = interfaces[ioif].group_f;
++                      break;
++              default:
++                      BUG_ON(1);
+               }
++
++              if ((grp->used & if_group_use) != if_group_use) {
++                      BUG_ON(1);
++              }
++              grp->used = grp->used & ~if_group_use;
++
+               group_set = clear_group_from_set(group_set, grp);
+       }
+       interfaces[ioif].used = 0;
+@@ -784,7 +1078,7 @@
+       for (i = start_bit; i <= stop_bit; i++) {
+               owners[i] = if_unclaimed;
+-      }
++      }       
+       local_irq_restore(flags);
+       notify_watchers();
+@@ -821,7 +1115,7 @@
+ }
+ void cris_io_interface_delete_watcher(void (*notify)(const unsigned int gpio_in_available,
+-                                                   const unsigned int gpio_out_available,
++                                                   const unsigned int gpio_out_available, 
+                                                      const unsigned char pa_available,
+                                                    const unsigned char pb_available))
+ {
+@@ -870,7 +1164,7 @@
+ module_init(cris_io_interface_init);
+-
++                                     
+ EXPORT_SYMBOL(cris_request_io_interface);
+ EXPORT_SYMBOL(cris_free_io_interface);
+ EXPORT_SYMBOL(cris_io_interface_allocate_pins);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/irq.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/irq.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/irq.c 2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/irq.c 2006-10-30 16:17:03.000000000 +0100
+@@ -1,4 +1,4 @@
+-/* $Id: irq.c,v 1.4 2005/01/04 12:22:28 starvik Exp $
++/* $Id: irq.c,v 1.9 2006/10/30 15:17:03 pkj Exp $
+  *
+  *    linux/arch/cris/kernel/irq.c
+  *
+@@ -12,7 +12,9 @@
+  */
+ #include <asm/irq.h>
++#include <asm/current.h>
+ #include <linux/irq.h>
++#include <linux/interrupt.h>
+ #include <linux/kernel.h>
+ #include <linux/init.h>
+@@ -75,8 +77,8 @@
+ BUILD_IRQ(13, 0x2000)
+ void mmu_bus_fault(void);      /* IRQ 14 is the bus fault interrupt */
+ void multiple_interrupt(void); /* IRQ 15 is the multiple IRQ interrupt */
+-BUILD_IRQ(16, 0x10000)
+-BUILD_IRQ(17, 0x20000)
++BUILD_IRQ(16, 0x10000 | 0x20000)  /* ethernet tx interrupt needs to block rx */
++BUILD_IRQ(17, 0x20000 | 0x10000)  /* ...and vice versa */
+ BUILD_IRQ(18, 0x40000)
+ BUILD_IRQ(19, 0x80000)
+ BUILD_IRQ(20, 0x100000)
+@@ -147,6 +149,55 @@
+ void do_sigtrap(void); /* from entry.S */
+ void gdb_handle_breakpoint(void); /* from entry.S */
++extern void do_IRQ(int irq, struct pt_regs * regs);
++
++/* Handle multiple IRQs */
++void do_multiple_IRQ(struct pt_regs* regs)
++{
++      int bit;
++      unsigned masked;
++      unsigned mask;
++      unsigned ethmask = 0;
++      
++      /* Get interrupts to mask and handle */
++      mask = masked = *R_VECT_MASK_RD;
++      
++      /* Never mask timer IRQ */
++      mask &= ~(IO_MASK(R_VECT_MASK_RD, timer0));
++
++      /* 
++       * If either ethernet interrupt (rx or tx) is active then block 
++       * the other one too. Unblock afterwards also.
++       */
++      if (mask & 
++          (IO_STATE(R_VECT_MASK_RD, dma0, active) |
++           IO_STATE(R_VECT_MASK_RD, dma1, active))) {
++              ethmask = (IO_MASK(R_VECT_MASK_RD, dma0) |
++                         IO_MASK(R_VECT_MASK_RD, dma1));
++      }
++
++      /* Block them */
++      *R_VECT_MASK_CLR = (mask | ethmask);
++
++      /* An extra irq_enter here to prevent softIRQs to run after
++       * each do_IRQ. This will decrease the interrupt latency. 
++       */
++      irq_enter();
++
++      /* Handle all IRQs */
++      for (bit = 2; bit < 32; bit++) {
++              if (masked & (1 << bit)) {
++                      do_IRQ(bit, regs);
++              }
++      }
++
++      /* This irq_exit() will trigger the soft IRQs. */
++      irq_exit();
++
++      /* Unblock the IRQs again */
++      *R_VECT_MASK_SET = (masked | ethmask);
++}
++
+ /* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and
+    setting the irq vector table.
+ */
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/kgdb.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/kgdb.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/kgdb.c        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/kgdb.c        2006-03-22 10:56:55.000000000 +0100
+@@ -18,6 +18,10 @@
+ *! Jul 21 1999  Bjorn Wesen     eLinux port
+ *!
+ *! $Log: kgdb.c,v $
++*! Revision 1.7  2006/03/22 09:56:55  starvik
++*! Merge of Linux 2.6.16
++*!
++*!
+ *! Revision 1.6  2005/01/14 10:12:17  starvik
+ *! KGDB on separate port.
+ *! Console fixes from 2.4.
+@@ -75,7 +79,7 @@
+ *!
+ *!---------------------------------------------------------------------------
+ *!
+-*! $Id: kgdb.c,v 1.6 2005/01/14 10:12:17 starvik Exp $
++*! $Id: kgdb.c,v 1.7 2006/03/22 09:56:55 starvik Exp $
+ *!
+ *! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN
+ *!
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/process.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/process.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/process.c     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/process.c     2006-10-13 14:43:11.000000000 +0200
+@@ -1,4 +1,4 @@
+-/* $Id: process.c,v 1.12 2004/12/27 11:18:32 starvik Exp $
++/* $Id: process.c,v 1.14 2006/10/13 12:43:11 starvik Exp $
+  * 
+  *  linux/arch/cris/kernel/process.c
+  *
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/ptrace.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/ptrace.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/ptrace.c      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/ptrace.c      2006-10-30 16:17:57.000000000 +0100
+@@ -66,6 +66,7 @@
+ ptrace_disable(struct task_struct *child)
+ {
+        /* Todo - pending singlesteps? */
++       clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+ }
+ /* 
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/setup.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/setup.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/setup.c       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/setup.c       2006-10-13 14:43:11.000000000 +0200
+@@ -1,4 +1,4 @@
+-/*
++/*  
+  *
+  *  linux/arch/cris/arch-v10/kernel/setup.c
+  *
+@@ -13,6 +13,7 @@
+ #include <linux/seq_file.h>
+ #include <linux/proc_fs.h>
+ #include <linux/delay.h>
++#include <linux/param.h>
+ #ifdef CONFIG_PROC_FS
+ #define HAS_FPU               0x0001
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/signal.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/signal.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/signal.c      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/signal.c      2006-03-22 10:56:55.000000000 +0100
+@@ -41,7 +41,7 @@
+  */
+ #define RESTART_CRIS_SYS(regs) regs->r10 = regs->orig_r10; regs->irp -= 2;
+-int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs);
++void do_signal(int canrestart, struct pt_regs *regs);
+ /*
+  * Atomically swap in the new signal mask, and wait for a signal.  Define 
+@@ -52,68 +52,16 @@
+ sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof, 
+                long srp, struct pt_regs *regs)
+ {
+-      sigset_t saveset;
+-
+       mask &= _BLOCKABLE;
+       spin_lock_irq(&current->sighand->siglock);
+-      saveset = current->blocked;
+-      siginitset(&current->blocked, mask);
+-      recalc_sigpending();
+-      spin_unlock_irq(&current->sighand->siglock);
+-
+-      regs->r10 = -EINTR;
+-      while (1) {
+-              current->state = TASK_INTERRUPTIBLE;
+-              schedule();
+-              if (do_signal(0, &saveset, regs))
+-                      /* We will get here twice: once to call the signal
+-                         handler, then again to return from the
+-                         sigsuspend system call.  When calling the
+-                         signal handler, R10 holds the signal number as
+-                         set through do_signal.  The sigsuspend call
+-                         will return with the restored value set above;
+-                         always -EINTR.  */
+-                      return regs->r10;
+-      }
+-}
+-
+-/* Define dummy arguments to be able to reach the regs argument.  (Note that
+- * this arrangement relies on size_t occupying one register.)
+- */
+-int
+-sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13, 
+-                  long mof, long srp, struct pt_regs *regs)
+-{
+-      sigset_t saveset, newset;
+-
+-      /* XXX: Don't preclude handling different sized sigset_t's.  */
+-      if (sigsetsize != sizeof(sigset_t))
+-              return -EINVAL;
+-
+-      if (copy_from_user(&newset, unewset, sizeof(newset)))
+-              return -EFAULT;
+-      sigdelsetmask(&newset, ~_BLOCKABLE);
+-
+-      spin_lock_irq(&current->sighand->siglock);
+-      saveset = current->blocked;
+-      current->blocked = newset;
++      current->saved_sigmask = current->blocked;
++        siginitset(&current->blocked, mask);
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+-
+-      regs->r10 = -EINTR;
+-      while (1) {
+-              current->state = TASK_INTERRUPTIBLE;
+-              schedule();
+-              if (do_signal(0, &saveset, regs))
+-                      /* We will get here twice: once to call the signal
+-                         handler, then again to return from the
+-                         sigsuspend system call.  When calling the
+-                         signal handler, R10 holds the signal number as
+-                         set through do_signal.  The sigsuspend call
+-                         will return with the restored value set above;
+-                         always -EINTR.  */
+-                      return regs->r10;
+-      }
++      current->state = TASK_INTERRUPTIBLE;
++      schedule();
++      set_thread_flag(TIF_RESTORE_SIGMASK);
++      return -ERESTARTNOHAND;
+ }
+ int 
+@@ -353,8 +301,8 @@
+  * user-mode trampoline.
+  */
+-static void setup_frame(int sig, struct k_sigaction *ka,
+-                      sigset_t *set, struct pt_regs * regs)
++static int setup_frame(int sig, struct k_sigaction *ka,
++                     sigset_t *set, struct pt_regs * regs)
+ {
+       struct sigframe __user *frame;
+       unsigned long return_ip;
+@@ -402,14 +350,15 @@
+       wrusp((unsigned long)frame);
+-      return;
++      return 0;
+ give_sigsegv:
+       force_sigsegv(sig, current);
++      return -EFAULT;
+ }
+-static void setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+-                         sigset_t *set, struct pt_regs * regs)
++static int setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
++                        sigset_t *set, struct pt_regs * regs)
+ {
+       struct rt_sigframe __user *frame;
+       unsigned long return_ip;
+@@ -466,21 +415,24 @@
+       wrusp((unsigned long)frame);
+-      return;
++      return 0;
+ give_sigsegv:
+       force_sigsegv(sig, current);
++      return -EFAULT;
+ }
+ /*
+  * OK, we're invoking a handler
+  */   
+-static inline void
++static inline int
+ handle_signal(int canrestart, unsigned long sig,
+             siginfo_t *info, struct k_sigaction *ka,
+               sigset_t *oldset, struct pt_regs * regs)
+ {
++      int ret;
++
+       /* Are we from a system call? */
+       if (canrestart) {
+               /* If so, check system call restarting.. */
+@@ -510,19 +462,20 @@
+       /* Set up the stack frame */
+       if (ka->sa.sa_flags & SA_SIGINFO)
+-              setup_rt_frame(sig, ka, info, oldset, regs);
++              ret = setup_rt_frame(sig, ka, info, oldset, regs);
+       else
+-              setup_frame(sig, ka, oldset, regs);
++              ret = setup_frame(sig, ka, oldset, regs);
+-      if (ka->sa.sa_flags & SA_ONESHOT)
+-              ka->sa.sa_handler = SIG_DFL;
++      if (ret == 0) {
++              spin_lock_irq(&current->sighand->siglock);
++              sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
++              if (!(ka->sa.sa_flags & SA_NODEFER))
++                    sigaddset(&current->blocked,sig);
++               recalc_sigpending();
++               spin_unlock_irq(&current->sighand->siglock);
++      }
+-      spin_lock_irq(&current->sighand->siglock);
+-      sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+-      if (!(ka->sa.sa_flags & SA_NODEFER))
+-              sigaddset(&current->blocked,sig);
+-      recalc_sigpending();
+-      spin_unlock_irq(&current->sighand->siglock);
++      return ret;
+ }
+ /*
+@@ -537,12 +490,13 @@
+  * mode below.
+  */
+-int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs)
++void do_signal(int canrestart, struct pt_regs *regs)
+ {
+       siginfo_t info;
+       int signr;
+         struct k_sigaction ka;
+-
++        sigset_t *oldset;
++        
+       /*
+        * We want the common case to go fast, which
+        * is why we may in certain cases get here from
+@@ -550,16 +504,26 @@
+        * if so.
+        */
+       if (!user_mode(regs))
+-              return 1;
++              return;
+-      if (!oldset)
++        if (test_thread_flag(TIF_RESTORE_SIGMASK))
++              oldset = &current->saved_sigmask;
++      else
+               oldset = &current->blocked;
+       signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+       if (signr > 0) {
+               /* Whee!  Actually deliver the signal.  */
+-              handle_signal(canrestart, signr, &info, &ka, oldset, regs);
+-              return 1;
++              if (handle_signal(canrestart, signr, &info, &ka, oldset, regs)) {
++                      /* a signal was successfully delivered; the saved
++                       * sigmask will have been stored in the signal frame,
++                       * and will be restored by sigreturn, so we can simply
++                       * clear the TIF_RESTORE_SIGMASK flag */
++                      if (test_thread_flag(TIF_RESTORE_SIGMASK))
++                              clear_thread_flag(TIF_RESTORE_SIGMASK);
++              }
++
++              return;
+       }
+       /* Did we come from a system call? */
+@@ -575,5 +539,11 @@
+                       regs->irp -= 2;
+               }
+       }
+-      return 0;
++
++      /* if there's no signal to deliver, we just put the saved sigmask
++       * back */
++      if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
++              clear_thread_flag(TIF_RESTORE_SIGMASK);
++              sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
++      }
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/time.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/time.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/time.c        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/time.c        2007-01-09 10:29:18.000000000 +0100
+@@ -1,4 +1,4 @@
+-/* $Id: time.c,v 1.5 2004/09/29 06:12:46 starvik Exp $
++/* $Id: time.c,v 1.10 2007/01/09 09:29:18 starvik Exp $
+  *
+  *  linux/arch/cris/arch-v10/kernel/time.c
+  *
+@@ -20,6 +20,7 @@
+ #include <asm/io.h>
+ #include <asm/delay.h>
+ #include <asm/rtc.h>
++#include <asm/irq_regs.h>
+ /* define this if you need to use print_timestamp */
+ /* it will make jiffies at 96 hz instead of 100 hz though */
+@@ -202,8 +203,9 @@
+ extern void cris_do_profile(struct pt_regs *regs);
+ static inline irqreturn_t
+-timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++timer_interrupt(int irq, void *dev_id)
+ {
++      struct pt_regs* regs = get_irq_regs();
+       /* acknowledge the timer irq */
+ #ifdef USE_CASCADE_TIMERS
+@@ -222,9 +224,11 @@
+ #endif
+       /* reset watchdog otherwise it resets us! */
+-
+       reset_watchdog();
+       
++        /* Update statistics. */
++      update_process_times(user_mode(regs));
++
+       /* call the real timer interrupt handler */
+       do_timer(1);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/kernel/traps.c linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/traps.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/kernel/traps.c       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/kernel/traps.c       2006-12-11 14:04:24.000000000 +0100
+@@ -1,13 +1,10 @@
+-/* $Id: traps.c,v 1.4 2005/04/24 18:47:55 starvik Exp $
++/*
++ * Helper functions for trap handlers
+  *
+- *  linux/arch/cris/arch-v10/traps.c
++ * Copyright (C) 2000-2006, Axis Communications AB.
+  *
+- *  Heler functions for trap handlers
+- * 
+- *  Copyright (C) 2000-2002 Axis Communications AB
+- *
+- *  Authors:   Bjorn Wesen
+- *           Hans-Peter Nilsson
++ * Authors:   Bjorn Wesen
++ *            Hans-Peter Nilsson
+  *
+  */
+@@ -15,124 +12,118 @@
+ #include <asm/uaccess.h>
+ #include <asm/arch/sv_addr_ag.h>
+-extern int raw_printk(const char *fmt, ...);
+-
+-void 
+-show_registers(struct pt_regs * regs)
++void
++show_registers(struct pt_regs *regs)
+ {
+-      /* We either use rdusp() - the USP register, which might not
+-         correspond to the current process for all cases we're called,
+-         or we use the current->thread.usp, which is not up to date for
+-         the current process.  Experience shows we want the USP
+-         register.  */
++      /*
++       * It's possible to use either the USP register or current->thread.usp.
++       * USP might not correspond to the current process for all cases this
++       * function is called, and current->thread.usp isn't up to date for the
++       * current process. Experience shows that using USP is the way to go.
++       */
+       unsigned long usp = rdusp();
+-      raw_printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n",
+-             regs->irp, regs->srp, regs->dccr, usp, regs->mof );
+-      raw_printk(" r0: %08lx  r1: %08lx   r2: %08lx  r3: %08lx\n",
++      printk("IRP: %08lx SRP: %08lx DCCR: %08lx USP: %08lx MOF: %08lx\n",
++             regs->irp, regs->srp, regs->dccr, usp, regs->mof);
++
++      printk(" r0: %08lx  r1: %08lx   r2: %08lx  r3: %08lx\n",
+              regs->r0, regs->r1, regs->r2, regs->r3);
+-      raw_printk(" r4: %08lx  r5: %08lx   r6: %08lx  r7: %08lx\n",
++
++      printk(" r4: %08lx  r5: %08lx   r6: %08lx  r7: %08lx\n",
+              regs->r4, regs->r5, regs->r6, regs->r7);
+-      raw_printk(" r8: %08lx  r9: %08lx  r10: %08lx r11: %08lx\n",
++
++      printk(" r8: %08lx  r9: %08lx  r10: %08lx r11: %08lx\n",
+              regs->r8, regs->r9, regs->r10, regs->r11);
+-      raw_printk("r12: %08lx r13: %08lx oR10: %08lx  sp: %08lx\n",
+-             regs->r12, regs->r13, regs->orig_r10, regs);
+-      raw_printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE);
+-      raw_printk("Process %s (pid: %d, stackpage=%08lx)\n",
++
++      printk("r12: %08lx r13: %08lx oR10: %08lx  sp: %08lx\n",
++             regs->r12, regs->r13, regs->orig_r10, (long unsigned)regs);
++
++      printk("R_MMU_CAUSE: %08lx\n", (unsigned long)*R_MMU_CAUSE);
++
++      printk("Process %s (pid: %d, stackpage=%08lx)\n",
+              current->comm, current->pid, (unsigned long)current);
+       /*
+-         * When in-kernel, we also print out the stack and code at the
+-         * time of the fault..
+-         */
+-        if (! user_mode(regs)) {
+-              int i;
++       * When in-kernel, we also print out the stack and code at the
++       * time of the fault..
++       */
++      if (!user_mode(regs)) {
++              int i;
+-                show_stack(NULL, (unsigned long*)usp);
++              show_stack(NULL, (unsigned long *)usp);
+-              /* Dump kernel stack if the previous dump wasn't one.  */
++              /*
++               * If the previous stack-dump wasn't a kernel one, dump the
++               * kernel stack now.
++               */
+               if (usp != 0)
+-                      show_stack (NULL, NULL);
++                      show_stack(NULL, NULL);
+-                raw_printk("\nCode: ");
+-                if(regs->irp < PAGE_OFFSET)
+-                        goto bad;
+-
+-              /* Often enough the value at regs->irp does not point to
+-                 the interesting instruction, which is most often the
+-                 _previous_ instruction.  So we dump at an offset large
+-                 enough that instruction decoding should be in sync at
+-                 the interesting point, but small enough to fit on a row
+-                 (sort of).  We point out the regs->irp location in a
+-                 ksymoops-friendly way by wrapping the byte for that
+-                 address in parentheses.  */
+-                for(i = -12; i < 12; i++)
+-                {
+-                        unsigned char c;
+-                        if(__get_user(c, &((unsigned char*)regs->irp)[i])) {
+-bad:
+-                                raw_printk(" Bad IP value.");
+-                                break;
+-                        }
++              printk("\nCode: ");
++
++              if (regs->irp < PAGE_OFFSET)
++                      goto bad_value;
++
++              /*
++               * Quite often the value at regs->irp doesn't point to the
++               * interesting instruction, which often is the previous
++               * instruction. So dump at an offset large enough that the
++               * instruction decoding should be in sync at the interesting
++               * point, but small enough to fit on a row. The regs->irp
++               * location is pointed out in a ksymoops-friendly way by
++               * wrapping the byte for that address in parenthesises.
++               */
++              for (i = -12; i < 12; i++) {
++                      unsigned char c;
++
++                      if (__get_user(c, &((unsigned char *)regs->irp)[i])) {
++bad_value:
++                              printk(" Bad IP value.");
++                              break;
++                      }
+                       if (i == 0)
+-                        raw_printk("(%02x) ", c);
++                              printk("(%02x) ", c);
+                       else
+-                        raw_printk("%02x ", c);
+-                }
+-              raw_printk("\n");
+-        }
++                              printk("%02x ", c);
++              }
++              printk("\n");
++      }
+ }
+-/* Called from entry.S when the watchdog has bitten
+- * We print out something resembling an oops dump, and if
+- * we have the nice doggy development flag set, we halt here
+- * instead of rebooting.
+- */
+-
+-extern void reset_watchdog(void);
+-extern void stop_watchdog(void);
+-
+-
+ void
+-watchdog_bite_hook(struct pt_regs *regs)
++arch_enable_nmi(void)
+ {
+-#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+-      local_irq_disable();
+-      stop_watchdog();
+-      show_registers(regs);
+-      while(1) /* nothing */;
+-#else
+-      show_registers(regs);
+-#endif        
++      asm volatile ("setf m");
+ }
+-/* This is normally the 'Oops' routine */
+-void 
+-die_if_kernel(const char * str, struct pt_regs * regs, long err)
++extern void (*nmi_handler)(struct pt_regs*);
++void handle_nmi(struct pt_regs* regs)
+ {
+-      if(user_mode(regs))
+-              return;
+-
+-#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+-      /* This printout might take too long and trigger the 
+-       * watchdog normally. If we're in the nice doggy
+-       * development mode, stop the watchdog during printout.
+-       */
+-      stop_watchdog();
+-#endif
+-
+-      raw_printk("%s: %04lx\n", str, err & 0xffff);
+-
+-      show_registers(regs);
++  if (nmi_handler)
++    nmi_handler(regs);
+-#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+-      reset_watchdog();
+-#endif
+-      do_exit(SIGSEGV);
++  /* Wait until nmi is no longer active. (We enable NMI immediately after
++     returning from this function, and we don't want it happening while
++     exiting from the NMI interrupt handler.) */
++  while(*R_IRQ_MASK0_RD & IO_STATE(R_IRQ_MASK0_RD, nmi_pin, active)); 
+ }
+-void arch_enable_nmi(void)
++#ifdef CONFIG_DEBUG_BUGVERBOSE
++void
++handle_BUG(struct pt_regs *regs)
+ {
+-  asm volatile("setf m");
++      struct bug_frame f;
++      unsigned char c;
++      unsigned long irp = regs->irp;
++
++      if (__copy_from_user(&f, (const void __user *)(irp - 8), sizeof f))
++              return;
++      if (f.prefix != BUG_PREFIX || f.magic != BUG_MAGIC)
++              return;
++      if (__get_user(c, f.filename))
++              f.filename = "<bad filename>";
++
++      printk("kernel BUG at %s:%d!\n", f.filename, f.line);
+ }
++#endif
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/lib/checksum.S linux-2.6.19.2.dev/arch/cris/arch-v10/lib/checksum.S
+--- linux-2.6.19.2.old/arch/cris/arch-v10/lib/checksum.S       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/lib/checksum.S       2005-08-16 12:38:52.000000000 +0200
+@@ -1,4 +1,4 @@
+-/* $Id: checksum.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $
++/* $Id: checksum.S,v 1.2 2005/08/16 10:38:52 edgar Exp $
+  * A fast checksum routine using movem
+  * Copyright (c) 1998-2001 Axis Communications AB
+  *
+@@ -61,8 +61,6 @@
+       
+       ax
+       addq    0,$r12
+-      ax                      ; do it again, since we might have generated a carry
+-      addq    0,$r12
+       subq    10*4,$r11
+       bge     _mloop
+@@ -88,10 +86,6 @@
+       lsrq    16,$r13         ; r13 = checksum >> 16
+       and.d   $r9,$r12                ; checksum = checksum & 0xffff
+       add.d   $r13,$r12               ; checksum += r13
+-      move.d  $r12,$r13               ; do the same again, maybe we got a carry last add
+-      lsrq    16,$r13
+-      and.d   $r9,$r12
+-      add.d   $r13,$r12
+ _no_fold:
+       cmpq    2,$r11
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/lib/checksumcopy.S linux-2.6.19.2.dev/arch/cris/arch-v10/lib/checksumcopy.S
+--- linux-2.6.19.2.old/arch/cris/arch-v10/lib/checksumcopy.S   2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/lib/checksumcopy.S   2005-08-16 12:38:52.000000000 +0200
+@@ -1,4 +1,4 @@
+-/* $Id: checksumcopy.S,v 1.1 2001/12/17 13:59:27 bjornw Exp $
++/* $Id: checksumcopy.S,v 1.2 2005/08/16 10:38:52 edgar Exp $
+  * A fast checksum+copy routine using movem
+  * Copyright (c) 1998, 2001 Axis Communications AB
+  *
+@@ -67,8 +67,6 @@
+       
+       ax
+       addq    0,$r13
+-      ax                      ; do it again, since we might have generated a carry
+-      addq    0,$r13
+       subq    10*4,$r12
+       bge     _mloop
+@@ -91,10 +89,6 @@
+       lsrq    16,$r9          ; r0 = checksum >> 16
+       and.d   0xffff,$r13     ; checksum = checksum & 0xffff
+       add.d   $r9,$r13        ; checksum += r0
+-      move.d  $r13,$r9        ; do the same again, maybe we got a carry last add
+-      lsrq    16,$r9
+-      and.d   0xffff,$r13
+-      add.d   $r9,$r13
+       
+ _no_fold:
+       cmpq    2,$r12
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/lib/dram_init.S linux-2.6.19.2.dev/arch/cris/arch-v10/lib/dram_init.S
+--- linux-2.6.19.2.old/arch/cris/arch-v10/lib/dram_init.S      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/lib/dram_init.S      2006-10-13 14:43:11.000000000 +0200
+@@ -1,4 +1,4 @@
+-/* $Id: dram_init.S,v 1.4 2003/09/22 09:21:59 starvik Exp $
++/* $Id: dram_init.S,v 1.5 2006/10/13 12:43:11 starvik Exp $
+  * 
+  * DRAM/SDRAM initialization - alter with care
+  * This file is intended to be included from other assembler files
+@@ -11,6 +11,9 @@
+  * Authors:  Mikael Starvik (starvik@axis.com)        
+  * 
+  * $Log: dram_init.S,v $
++ * Revision 1.5  2006/10/13 12:43:11  starvik
++ * Merge of 2.6.18
++ *
+  * Revision 1.4  2003/09/22 09:21:59  starvik
+  * Decompresser is linked to 0x407xxxxx and sdram commands are at 0x000xxxxx
+  * so we need to mask off 12 bits.
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/mm/fault.c linux-2.6.19.2.dev/arch/cris/arch-v10/mm/fault.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/mm/fault.c   2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/mm/fault.c   2006-12-11 12:32:10.000000000 +0100
+@@ -73,7 +73,7 @@
+       /* leave it to the MM system fault handler */
+       if (miss)
+               do_page_fault(address, regs, 0, writeac);
+-        else
++        else  
+               do_page_fault(address, regs, 1, we);
+         /* Reload TLB with new entry to avoid an extra miss exception.
+@@ -84,12 +84,13 @@
+       local_irq_disable();
+       pmd = (pmd_t *)(pgd + pgd_index(address));
+       if (pmd_none(*pmd))
+-              return;
++              goto exit;
+       pte = *pte_offset_kernel(pmd, address);
+       if (!pte_present(pte))
+-              return;
++              goto exit;
+       *R_TLB_SELECT = select;
+       *R_TLB_HI = cause;
+       *R_TLB_LO = pte_val(pte);
++exit:
+       local_irq_restore(flags);
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v10/mm/tlb.c linux-2.6.19.2.dev/arch/cris/arch-v10/mm/tlb.c
+--- linux-2.6.19.2.old/arch/cris/arch-v10/mm/tlb.c     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v10/mm/tlb.c     2006-08-07 12:08:35.000000000 +0200
+@@ -179,23 +179,26 @@
+ switch_mm(struct mm_struct *prev, struct mm_struct *next,
+         struct task_struct *tsk)
+ {
+-      /* make sure we have a context */
++      if (prev != next) {
++              /* make sure we have a context */
++              get_mmu_context(next);
++
++              /* remember the pgd for the fault handlers
++               * this is similar to the pgd register in some other CPU's.
++               * we need our own copy of it because current and active_mm
++               * might be invalid at points where we still need to derefer
++               * the pgd.
++               */
+-      get_mmu_context(next);
++              per_cpu(current_pgd, smp_processor_id()) = next->pgd;
+-      /* remember the pgd for the fault handlers
+-       * this is similar to the pgd register in some other CPU's.
+-       * we need our own copy of it because current and active_mm
+-       * might be invalid at points where we still need to derefer
+-       * the pgd.
+-       */
+-
+-      per_cpu(current_pgd, smp_processor_id()) = next->pgd;
+-
+-      /* switch context in the MMU */
++              /* switch context in the MMU */
+       
+-      D(printk("switching mmu_context to %d (%p)\n", next->context, next));
++              D(printk("switching mmu_context to %d (%p)\n",
++                       next->context, next));
+-      *R_MMU_CONTEXT = IO_FIELD(R_MMU_CONTEXT, page_id, next->context.page_id);
++              *R_MMU_CONTEXT = IO_FIELD(R_MMU_CONTEXT,
++                                        page_id, next->context.page_id);
++      }
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/Kconfig linux-2.6.19.2.dev/arch/cris/arch-v32/Kconfig
+--- linux-2.6.19.2.old/arch/cris/arch-v32/Kconfig      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/Kconfig      2007-01-09 10:29:18.000000000 +0100
+@@ -1,23 +1,66 @@
++source drivers/cpufreq/Kconfig
++
+ config ETRAX_DRAM_VIRTUAL_BASE
+       hex
+       depends on ETRAX_ARCH_V32
+       default "c0000000"
+-config ETRAX_LED1G
+-      string "First green LED bit"
++choice
++      prompt "Nbr of Ethernet LED groups"
+       depends on ETRAX_ARCH_V32
++      default ETRAX_NBR_LED_GRP_ONE
++      help
++        Select how many Ethernet LED groups that can be used. Usually one per Ethernet
++        interface is a good choice.
++
++config        ETRAX_NBR_LED_GRP_ZERO
++      bool "Use zero LED groups"
++      help
++        Select this if you do not want any Ethernet LEDs.
++
++config        ETRAX_NBR_LED_GRP_ONE
++      bool "Use one LED group"
++      help
++        Select this if you want one Ethernet LED group. This LED group can be used for
++        one or more Ethernet interfaces. However, it is recomended that each Ethernet 
++        interface use a dedicated LED group.
++
++config        ETRAX_NBR_LED_GRP_TWO
++      bool "Use two LED groups"
++      help
++        Select this if you want two Ethernet LED groups. This is the best choice if you
++        have more than one Ethernet interface and would like to have separate LEDs for
++        the interfaces.
++
++endchoice
++
++config ETRAX_LED_G_NET0
++      string "Ethernet LED group 0 green LED bit"
++      depends on ETRAX_ARCH_V32 && (ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO)
+       default "PA3"
+       help
+-        Bit to use for the first green LED (network LED).
+-        Most Axis products use bit A3 here.
++        Bit to use for the green LED in Ethernet LED group 0.
+-config ETRAX_LED1R
+-      string "First red LED bit"
+-      depends on ETRAX_ARCH_V32
++config ETRAX_LED_R_NET0
++      string "Ethernet LED group 0 red LED bit"
++      depends on ETRAX_ARCH_V32 && (ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO)
+       default "PA4"
+       help
+-        Bit to use for the first red LED (network LED).
+-        Most Axis products use bit A4 here.
++        Bit to use for the red LED in Ethernet LED group 0.
++
++config ETRAX_LED_G_NET1
++      string "Ethernet group 1 green LED bit"
++      depends on ETRAX_ARCH_V32 && ETRAX_NBR_LED_GRP_TWO
++      default ""
++      help
++        Bit to use for the green LED in Ethernet LED group 1.
++
++config ETRAX_LED_R_NET1
++      string "Ethernet group 1 red LED bit"
++      depends on ETRAX_ARCH_V32 && ETRAX_NBR_LED_GRP_TWO
++      default ""
++      help
++        Bit to use for the red LED in Ethernet LED group 1.
+ config ETRAX_LED2G
+       string "Second green LED bit"
+@@ -294,3 +337,22 @@
+       help
+         Configures the initial data for the general port E bits.  Most
+         products should use 00000 here.
++
++config ETRAX_DEF_GIO_PV_OE
++      hex "GIO_PV_OE"
++      depends on ETRAX_VIRTUAL_GPIO
++      default "0000"
++      help
++        Configures the direction of virtual general port V bits. 1 is out,
++        0 is in. This is often totally different depending on the product
++        used. These bits are used for all kinds of stuff. If you don't know
++        what to use, it is always safe to put all as inputs, although
++        floating inputs isn't good.
++
++config ETRAX_DEF_GIO_PV_OUT
++      hex "GIO_PV_OUT"
++      depends on ETRAX_VIRTUAL_GPIO
++      default "0000"
++      help
++        Configures the initial data for the virtual general port V bits.
++        Most products should use 0000 here.
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/Makefile linux-2.6.19.2.dev/arch/cris/arch-v32/boot/Makefile
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/Makefile        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/Makefile        2006-11-29 17:05:41.000000000 +0100
+@@ -1,14 +1,21 @@
+ #
+ # arch/cris/arch-v32/boot/Makefile
+ #
+-target = $(target_boot_dir)
+-src    = $(src_boot_dir)
+-zImage: compressed/vmlinuz
++OBJCOPY = objcopy-cris
++OBJCOPYFLAGS = -O binary -R .note -R .comment
+-compressed/vmlinuz: $(objtree)/vmlinux
+-      @$(MAKE) -f $(src)/compressed/Makefile $(objtree)/vmlinuz
++subdir- := compressed rescue
++targets := Image
+-clean:
+-      rm -f zImage tools/build compressed/vmlinux.out
+-      @$(MAKE) -f $(src)/compressed/Makefile clean
++$(obj)/Image: vmlinux FORCE
++      $(call if_changed,objcopy)
++      @echo '  Kernel: $@ is ready'
++
++$(obj)/compressed/vmlinux: $(obj)/Image FORCE
++      $(Q)$(MAKE) $(build)=$(obj)/compressed $@
++      $(Q)$(MAKE) $(build)=$(obj)/rescue $(obj)/rescue/rescue.bin
++
++$(obj)/zImage:  $(obj)/compressed/vmlinux
++      @cp $< $@
++      @echo '  Kernel: $@ is ready'
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/Makefile linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/Makefile
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/Makefile     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/Makefile     2006-11-29 17:05:42.000000000 +0100
+@@ -1,41 +1,29 @@
+ #
+-# lx25/arch/cris/arch-v32/boot/compressed/Makefile
++# arch/cris/arch-v32/boot/compressed/Makefile
+ #
+-# create a compressed vmlinux image from the original vmlinux files and romfs
+-#
+-
+-target = $(target_compressed_dir)
+-src    = $(src_compressed_dir)
+ CC = gcc-cris -mlinux -march=v32 -I $(TOPDIR)/include
+ CFLAGS = -O2
+ LD = gcc-cris -mlinux -march=v32 -nostdlib
++LDFLAGS = -T $(obj)/decompress.ld
++obj-y = head.o misc.o
++OBJECTS = $(obj)/head.o $(obj)/misc.o
+ OBJCOPY = objcopy-cris
+ OBJCOPYFLAGS = -O binary --remove-section=.bss
+-OBJECTS = $(target)/head.o $(target)/misc.o
+-
+-# files to compress
+-SYSTEM = $(objtree)/vmlinux.bin
+-
+-all: vmlinuz
+-
+-$(target)/decompress.bin: $(OBJECTS)
+-      $(LD) -T $(src)/decompress.ld -o $(target)/decompress.o $(OBJECTS)
+-      $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/decompress.o $(target)/decompress.bin
+-$(objtree)/vmlinuz: $(target) piggy.img $(target)/decompress.bin
+-      cat $(target)/decompress.bin piggy.img > $(objtree)/vmlinuz
+-      rm -f piggy.img
+-      cp $(objtree)/vmlinuz $(src)
++quiet_cmd_image = BUILD   $@
++cmd_image = cat $(obj)/decompress.bin $(obj)/piggy.gz > $@
+-$(target)/head.o: $(src)/head.S
+-      $(CC) -D__ASSEMBLY__ -c $< -o $@
++targets := vmlinux piggy.gz decompress.o decompress.bin
+-# gzip the kernel image
++$(obj)/decompress.o: $(OBJECTS) FORCE
++      $(call if_changed,ld)
+-piggy.img: $(SYSTEM)
+-      cat $(SYSTEM) | gzip -f -9 > piggy.img
++$(obj)/decompress.bin: $(obj)/decompress.o FORCE
++      $(call if_changed,objcopy)
+-clean:
+-      rm -f piggy.img $(objtree)/vmlinuz vmlinuz.o decompress.o decompress.bin $(OBJECTS)
++$(obj)/vmlinux: $(obj)/piggy.gz $(obj)/decompress.bin FORCE
++      $(call if_changed,image)
++$(obj)/piggy.gz: $(obj)/../Image FORCE
++      $(call if_changed,gzip)
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/README linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/README
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/README       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/README       2003-08-21 11:37:03.000000000 +0200
+@@ -5,14 +5,14 @@
+ This can be slightly confusing because it's a process with many steps.
+ The kernel object built by the arch/etrax100/Makefile, vmlinux, is split
+-by that makefile into text and data binary files, vmlinux.text and
++by that makefile into text and data binary files, vmlinux.text and 
+ vmlinux.data.
+ Those files together with a ROM filesystem can be catted together and
+ burned into a flash or executed directly at the DRAM origin.
+ They can also be catted together and compressed with gzip, which is what
+-happens in this makefile. Together they make up piggy.img.
++happens in this makefile. Together they make up piggy.img. 
+ The decompressor is built into the file decompress.o. It is turned into
+ the binary file decompress.bin, which is catted together with piggy.img
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/decompress.ld linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/decompress.ld
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/decompress.ld        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/decompress.ld        2003-08-21 11:57:56.000000000 +0200
+@@ -1,7 +1,7 @@
+ /*#OUTPUT_FORMAT(elf32-us-cris) */
+ OUTPUT_ARCH (crisv32)
+-MEMORY
++MEMORY 
+       {
+       dram : ORIGIN = 0x40700000,
+              LENGTH = 0x00100000
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/head.S linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/head.S
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/head.S       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/head.S       2007-01-09 10:29:20.000000000 +0100
+@@ -2,19 +2,19 @@
+  *  Code that sets up the DRAM registers, calls the
+  *  decompressor to unpack the piggybacked kernel, and jumps.
+  *
+- *  Copyright (C) 1999 - 2003, Axis Communications AB
++ *  Copyright (C) 1999 - 2006, Axis Communications AB
+  */
+ #define ASSEMBLER_MACROS_ONLY
+ #include <asm/arch/hwregs/asm/reg_map_asm.h>
+ #include <asm/arch/hwregs/asm/gio_defs_asm.h>
+ #include <asm/arch/hwregs/asm/config_defs_asm.h>
+-
++      
+ #define RAM_INIT_MAGIC 0x56902387
+ #define COMMAND_LINE_MAGIC 0x87109563
+       ;; Exported symbols
+-
++      
+       .globl  input_data
+       .text
+@@ -29,53 +29,16 @@
+              REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0
+       move.d $r0, [$r1]
+-      ;; If booting from NAND flash we first have to copy some
+-      ;; data from NAND flash to internal RAM to get the code
+-      ;; that initializes the SDRAM. Lets copy 20 KB. This
+-      ;; code executes at 0x38010000 if booting from NAND and
+-      ;; we are guaranted that at least 0x200 bytes are good so
+-      ;; lets start from there. The first 8192 bytes in the nand
+-      ;; flash is spliced with zeroes and is thus 16384 bytes.
+-      move.d 0x38010200, $r10
+-      move.d 0x14200, $r11    ; Start offset in NAND flash 0x10200 + 16384
+-      move.d 0x5000, $r12     ; Length of copy
+-
+-      ;; Before this code the tools add a partitiontable so the PC
+-      ;; has an offset from the linked address.
+-offset1:
+-      lapcq  ., $r13          ; get PC
+-      add.d   first_copy_complete-offset1, $r13
+-
+-#include "../../lib/nand_init.S"
+-
+-first_copy_complete:
+-      ;; Initialze the DRAM registers.
++      ;; Initialize the DRAM registers.
+       cmp.d   RAM_INIT_MAGIC, $r8     ; Already initialized?
+       beq     dram_init_finished
+       nop
+ #include "../../lib/dram_init.S"
+-
++      
+ dram_init_finished:
+-      lapcq  ., $r13          ; get PC
+-      add.d   second_copy_complete-dram_init_finished, $r13
+-
+-      move.d REG_ADDR(config, regi_config, r_bootsel), $r0
+-      move.d [$r0], $r0
+-      and.d  REG_MASK(config, r_bootsel, boot_mode), $r0
+-      cmp.d  REG_STATE(config, r_bootsel, boot_mode, nand), $r0
+-      bne second_copy_complete ; No NAND boot
+-      nop
+-
+-      ;; Copy 2MB from NAND flash to SDRAM (at 2-4MB into the SDRAM)
+-      move.d 0x40204000, $r10
+-      move.d 0x8000, $r11
+-      move.d 0x200000, $r12
+-      ba copy_nand_to_ram
+-      nop
+-second_copy_complete:
+-
+-      ;; Initiate the PA port.
++      
++      ;; Initiate the GIO ports.
+       move.d  CONFIG_ETRAX_DEF_GIO_PA_OUT, $r0
+       move.d  REG_ADDR(gio, regi_gio, rw_pa_dout), $r1
+       move.d  $r0, [$r1]
+@@ -84,57 +47,74 @@
+       move.d  REG_ADDR(gio, regi_gio, rw_pa_oe), $r1
+       move.d  $r0, [$r1]
++      move.d  CONFIG_ETRAX_DEF_GIO_PB_OUT, $r0
++      move.d  REG_ADDR(gio, regi_gio, rw_pb_dout), $r1
++      move.d  $r0, [$r1]
++
++      move.d  CONFIG_ETRAX_DEF_GIO_PB_OE, $r0
++      move.d  REG_ADDR(gio, regi_gio, rw_pb_oe), $r1
++      move.d  $r0, [$r1]
++
++      move.d  CONFIG_ETRAX_DEF_GIO_PC_OUT, $r0
++      move.d  REG_ADDR(gio, regi_gio, rw_pc_dout), $r1
++      move.d  $r0, [$r1]
++
++      move.d  CONFIG_ETRAX_DEF_GIO_PC_OE, $r0
++      move.d  REG_ADDR(gio, regi_gio, rw_pc_oe), $r1
++      move.d  $r0, [$r1]
++
++      move.d  CONFIG_ETRAX_DEF_GIO_PD_OUT, $r0
++      move.d  REG_ADDR(gio, regi_gio, rw_pd_dout), $r1
++      move.d  $r0, [$r1]
++
++      move.d  CONFIG_ETRAX_DEF_GIO_PD_OE, $r0
++      move.d  REG_ADDR(gio, regi_gio, rw_pd_oe), $r1
++      move.d  $r0, [$r1]
++
++      move.d  CONFIG_ETRAX_DEF_GIO_PE_OUT, $r0
++      move.d  REG_ADDR(gio, regi_gio, rw_pe_dout), $r1
++      move.d  $r0, [$r1]
++
++      move.d  CONFIG_ETRAX_DEF_GIO_PE_OE, $r0
++      move.d  REG_ADDR(gio, regi_gio, rw_pe_oe), $r1
++      move.d  $r0, [$r1]
++
+       ;; Setup the stack to a suitably high address.
+-      ;; We assume 8 MB is the minimum DRAM and put
++      ;; We assume 8 MB is the minimum DRAM and put 
+       ;; the SP at the top for now.
+       move.d  0x40800000, $sp
+-      ;; Figure out where the compressed piggyback image is
+-      ;; in the flash (since we wont try to copy it to DRAM
+-      ;; before unpacking). It is at _edata, but in flash.
++      ;; Figure out where the compressed piggyback image is.
++      ;; It is either in [NOR] flash (we don't want to copy it 
++      ;; to DRAM before unpacking), or copied to DRAM
++      ;; by the [NAND] flash boot loader.
++      ;; The piggyback image is at _edata, but relative to where the
++      ;; image is actually located in memory, not where it is linked 
++      ;; (the decompressor is linked at 0x40700000+ and runs there).
+       ;; Use (_edata - herami) as offset to the current PC.
+-      move.d REG_ADDR(config, regi_config, r_bootsel), $r0
+-      move.d [$r0], $r0
+-      and.d  REG_MASK(config, r_bootsel, boot_mode), $r0
+-      cmp.d  REG_STATE(config, r_bootsel, boot_mode, nand), $r0
+-      beq hereami2
+-      nop
+-hereami:
++hereami:      
+       lapcq   ., $r5          ; get PC
+       and.d   0x7fffffff, $r5 ; strip any non-cache bit
+-      move.d  $r5, $r0        ; save for later - flash address of 'herami'
++      move.d  $r5, $r0        ; source address of 'herami'
+       add.d   _edata, $r5
+       sub.d   hereami, $r5    ; r5 = flash address of '_edata'
+       move.d  hereami, $r1    ; destination
+-      ba 2f
+-      nop
+-hereami2:
+-      lapcq   ., $r5          ; get PC
+-      and.d   0x00ffffff, $r5 ; strip any non-cache bit
+-      move.d  $r5, $r6
+-      or.d    0x40200000, $r6
+-      move.d  $r6, $r0        ; save for later - flash address of 'herami'
+-      add.d   _edata, $r5
+-      sub.d   hereami2, $r5   ; r5 = flash address of '_edata'
+-      add.d   0x40200000, $r5
+-      move.d  hereami2, $r1   ; destination
+-2:
+-      ;; Copy text+data to DRAM
++      ;; Copy text+data to DRAM
++      
+       move.d  _edata, $r2     ; end destination
+-1:    move.w  [$r0+], $r3
+-      move.w  $r3, [$r1+]
+-      cmp.d   $r2, $r1
++1:    move.w  [$r0+], $r3     ; from herami+ source
++      move.w  $r3, [$r1+]     ; to hereami+ destination (linked address)
++      cmp.d   $r2, $r1        ; finish when destination == _edata
+       bcs     1b
+       nop
+-
+-      move.d  input_data, $r0 ; for the decompressor
++      move.d  input_data, $r0 ; for the decompressor  
+       move.d  $r5, [$r0]      ; for the decompressor
+       ;; Clear the decompressors BSS (between _edata and _end)
+-
++      
+       moveq   0, $r0
+       move.d  _edata, $r1
+       move.d  _end, $r2
+@@ -144,40 +124,47 @@
+       nop
+       ;;  Save command line magic and address.
+-      move.d  _cmd_line_magic, $r12
+-      move.d  $r10, [$r12]
+-      move.d  _cmd_line_addr, $r12
+-      move.d  $r11, [$r12]
+-
++      move.d  _cmd_line_magic, $r0
++      move.d  $r10, [$r0]
++      move.d  _cmd_line_addr, $r0
++      move.d  $r11, [$r0]
++
++      ;;  Save boot source indicator
++      move.d  _boot_source, $r0
++      move.d  $r12, [$r0]
++      
+       ;; Do the decompression and save compressed size in _inptr
+       jsr     decompress_kernel
+       nop
++      ;; Restore boot source indicator
++      move.d  _boot_source, $r12
++      move.d  [$r12], $r12
++
+       ;; Restore command line magic and address.
+       move.d  _cmd_line_magic, $r10
+       move.d  [$r10], $r10
+       move.d  _cmd_line_addr, $r11
+       move.d  [$r11], $r11
+-
++      
+       ;; Put start address of root partition in r9 so the kernel can use it
+       ;; when mounting from flash
+       move.d  input_data, $r0
+       move.d  [$r0], $r9              ; flash address of compressed kernel
+       move.d  inptr, $r0
+       add.d   [$r0], $r9              ; size of compressed kernel
+-      cmp.d   0x40200000, $r9
+-      blo     enter_kernel
+-      nop
+-      sub.d   0x40200000, $r9
+-      add.d   0x4000, $r9
+-
+-enter_kernel:
++      cmp.d   0x40000000, $r9         ; image in DRAM ?
++      blo     enter_kernel            ; no, must be [NOR] flash, jump
++      nop                             ; delay slot
++      and.d   0x001fffff, $r9         ; assume compressed kernel was < 2M
++              
++enter_kernel:         
+       ;; Enter the decompressed kernel
+       move.d  RAM_INIT_MAGIC, $r8     ; Tell kernel that DRAM is initialized
+       jump    0x40004000      ; kernel is linked to this address
+       nop
+-
++      
+       .data
+ input_data:
+@@ -185,8 +172,8 @@
+ _cmd_line_magic:
+       .dword 0
+ _cmd_line_addr:
++      .dword 0        
++_boot_source:
+       .dword 0
+-is_nand_boot:
+-      .dword  0
+-
++      
+ #include "../../lib/hw_settings.S"
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/misc.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/misc.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/compressed/misc.c       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/compressed/misc.c       2006-11-03 11:35:51.000000000 +0100
+@@ -1,15 +1,15 @@
+ /*
+  * misc.c
+  *
+- * $Id: misc.c,v 1.8 2005/04/24 18:34:29 starvik Exp $
+- *
+- * This is a collection of several routines from gzip-1.0.3
++ * $Id: misc.c,v 1.12 2006/11/03 10:35:51 pkj Exp $
++ * 
++ * 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
+  * adoptation for Linux/CRIS Axis Communications AB, 1999
+- *
++ * 
+  */
+ /* where the piggybacked kernel image expects itself to live.
+@@ -20,11 +20,11 @@
+ #define KERNEL_LOAD_ADR 0x40004000
+-
+ #include <linux/types.h>
+ #include <asm/arch/hwregs/reg_rdwr.h>
+ #include <asm/arch/hwregs/reg_map.h>
+ #include <asm/arch/hwregs/ser_defs.h>
++#include <asm/arch/hwregs/pinmux_defs.h>
+ /*
+  * gzip declarations
+@@ -66,8 +66,8 @@
+ #define ENCRYPTED    0x20 /* bit 5 set: file is encrypted */
+ #define RESERVED     0xC0 /* bit 6,7:   reserved */
+-#define get_byte() inbuf[inptr++]
+-
++#define get_byte() inbuf[inptr++]     
++      
+ /* Diagnostic functions */
+ #ifdef DEBUG
+ #  define Assert(cond,msg) {if(!(cond)) error(msg);}
+@@ -96,20 +96,20 @@
+ static long bytes_out = 0;
+ static uch *output_data;
+ static unsigned long output_ptr = 0;
+-
++ 
+ 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 void puts(const char *);
+ /* the "heap" is put directly after the BSS ends, at end */
+-
++  
+ extern int _end;
+ static long free_mem_ptr = (long)&_end;
+-
++ 
+ #include "../../../../../lib/inflate.c"
+ static void *malloc(int size)
+@@ -152,7 +152,7 @@
+               rs = REG_RD(ser, regi_ser, rs_stat_din);
+       }
+       while (!rs.tr_rdy);/* Wait for tranceiver. */
+-
++      
+       REG_WR(ser, regi_ser, rw_dout, dout);
+ }
+@@ -209,9 +209,9 @@
+     ulg c = crc;         /* temporary variable */
+     unsigned n;
+     uch *in, *out, ch;
+-
++    
+     in = window;
+-    out = &output_data[output_ptr];
++    out = &output_data[output_ptr]; 
+     for (n = 0; n < outcnt; n++) {
+           ch = *out++ = *in++;
+           c = crc_32_tab[((int)c ^ ch) & 0xff] ^ (c >> 8);
+@@ -225,9 +225,9 @@
+ static void
+ error(char *x)
+ {
+-      puts("\n\n");
++      puts("\r\n\n");
+       puts(x);
+-      puts("\n\n -- System halted\n");
++      puts("\r\n\n -- System halted\n");
+       while(1);       /* Halt */
+ }
+@@ -246,13 +246,13 @@
+       reg_ser_rw_rec_ctrl rec_ctrl;
+       reg_ser_rw_tr_baud_div tr_baud;
+       reg_ser_rw_rec_baud_div rec_baud;
+-
++      
+       /* Turn off XOFF. */
+       xoff = REG_RD(ser, regi_ser, rw_xoff);
+-
++      
+       xoff.chr = 0;
+       xoff.automatic = regk_ser_no;
+-
++      
+       REG_WR(ser, regi_ser, rw_xoff, xoff);
+       /* Set baudrate and stopbits. */
+@@ -260,19 +260,21 @@
+       rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
+       tr_baud = REG_RD(ser, regi_ser, rw_tr_baud_div);
+       rec_baud = REG_RD(ser, regi_ser, rw_rec_baud_div);
+-
++      
+       tr_ctrl.stop_bits = 1;  /* 2 stop bits. */
+-
+-      /*
+-       * The baudrate setup is a bit fishy, but in the end the tranceiver is
+-       * set to 4800 and the receiver to 115200. The magic value is
+-       * 29.493 MHz.
++      tr_ctrl.en = 1; /* enable transmitter */
++      rec_ctrl.en = 1; /* enabler receiver */
++      
++      /* 
++       * The baudrate setup used to be a bit fishy, but now transmitter and
++       * receiver are both set to the intended baud rate, 115200.
++       * The magic value is 29.493 MHz.
+        */
+       tr_ctrl.base_freq = regk_ser_f29_493;
+       rec_ctrl.base_freq = regk_ser_f29_493;
+-      tr_baud.div = (29493000 / 8) / 4800;
++      tr_baud.div = (29493000 / 8) / 115200;
+       rec_baud.div = (29493000 / 8) / 115200;
+-
++      
+       REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
+       REG_WR(ser, regi_ser, rw_tr_baud_div, tr_baud);
+       REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
+@@ -283,22 +285,28 @@
+ decompress_kernel()
+ {
+       char revision;
+-
++      reg_pinmux_rw_hwprot hwprot;
++      
+       /* input_data is set in head.S */
+       inbuf = input_data;
+-
++      
++      hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
+ #ifdef CONFIG_ETRAX_DEBUG_PORT0
+       serial_setup(regi_ser0);
+ #endif
+ #ifdef CONFIG_ETRAX_DEBUG_PORT1
++      hwprot.ser1 = regk_pinmux_yes;
+       serial_setup(regi_ser1);
+ #endif
+ #ifdef CONFIG_ETRAX_DEBUG_PORT2
++      hwprot.ser2 = regk_pinmux_yes;
+       serial_setup(regi_ser2);
+ #endif
+ #ifdef CONFIG_ETRAX_DEBUG_PORT3
++      hwprot.ser3 = regk_pinmux_yes;
+       serial_setup(regi_ser3);
+ #endif
++      REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot);
+       setup_normal_output_buffer();
+@@ -307,11 +315,11 @@
+       __asm__ volatile ("move $vr,%0" : "=rm" (revision));
+       if (revision < 32)
+       {
+-              puts("You need an ETRAX FS to run Linux 2.6/crisv32.\n");
++              puts("You need an ETRAX FS to run Linux 2.6/crisv32.\r\n");
+               while(1);
+       }
+-      puts("Uncompressing Linux...\n");
++      puts("Uncompressing Linux...\r\n");
+       gunzip();
+-      puts("Done. Now booting the kernel.\n");
++      puts("Done. Now booting the kernel.\r\n");
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/Makefile linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/Makefile
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/Makefile 2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/Makefile 2007-01-17 14:24:50.000000000 +0100
+@@ -1,36 +1,29 @@
+ #
+-# Makefile for rescue code
++# Makefile for rescue (bootstrap) code
+ #
+-target = $(target_rescue_dir)
+-src    = $(src_rescue_dir)
+ CC = gcc-cris -mlinux -march=v32 $(LINUXINCLUDE)
+ CFLAGS = -O2
+-LD = gcc-cris -mlinux -march=v32 -nostdlib
++LD = gcc-cris -mlinux -march=v32 -nostdlib 
++LDFLAGS = -T $(obj)/rescue.ld
++LDPOSTFLAGS = -lgcc
+ OBJCOPY = objcopy-cris
+ OBJCOPYFLAGS = -O binary --remove-section=.bss
+-
+-all: $(target)/rescue.bin
+-
+-rescue: rescue.bin
+-      # do nothing
+-
+-$(target)/rescue.bin: $(target) $(target)/head.o
+-      $(LD) -T $(src)/rescue.ld -o $(target)/rescue.o $(target)/head.o
+-      $(OBJCOPY) $(OBJCOPYFLAGS) $(target)/rescue.o $(target)/rescue.bin
+-      cp -p $(target)/rescue.bin $(objtree)
+-
+-$(target):
+-      mkdir -p $(target)
+-
+-$(target)/head.o: $(src)/head.S
+-      $(CC) -D__ASSEMBLY__ -c $< -o $*.o
+-
+-clean:
+-      rm -f $(target)/*.o $(target)/*.bin
+-
+-fastdep:
+-
+-modules:
+-
+-modules-install:
++obj-y = head.o bootload.o crisv32_nand.o nand_base.o nand_ids.o nand_ecc.o \
++        lib.o
++OBJECTS = $(obj)/head.o $(obj)/bootload.o \
++        $(obj)/crisv32_nand.o $(obj)/nand_base.o \
++          $(obj)/nand_ids.o $(obj)/nand_ecc.o \
++        $(obj)/lib.o $(obj)/../../lib/lib.a
++
++targets := rescue.o rescue.bin
++
++quiet_cmd_ldlibgcc = LD      $@
++cmd_ldlibgcc = $(LD) $(LDFLAGS) $(filter-out FORCE,$^) $(LDPOSTFLAGS) -o $@
++
++$(obj)/rescue.o: $(OBJECTS) FORCE
++      $(call if_changed,ldlibgcc)
++
++$(obj)/rescue.bin: $(obj)/rescue.o FORCE
++      $(call if_changed,objcopy)
++      cp -p $(obj)/rescue.bin $(objtree)
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/bootload.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/bootload.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/bootload.c       1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/bootload.c       2006-11-28 11:05:39.000000000 +0100
+@@ -0,0 +1,277 @@
++/*
++ * bootload.c
++ * Simple boot loader for NAND chips on Etrax FS
++ *
++ * $Id: bootload.c,v 1.8 2006/11/28 10:05:39 ricardw Exp $
++ * 
++ */
++
++#include <linux/types.h>
++#include <linux/delay.h>
++
++#include "mtd.h"
++#include "nand.h"
++
++#include <asm/arch/hwregs/reg_rdwr.h>
++#include <asm/arch/hwregs/reg_map.h>
++#include <asm/arch/hwregs/pinmux_defs.h>
++
++#include "lib.h"
++
++#define BOOT_ADDR  (CONFIG_ETRAX_PTABLE_SECTOR + 0x40200000)
++
++/* bits for nand_rw() `cmd'; or together as needed */
++
++#define NANDRW_READ           0x01
++#define NANDRW_WRITE          0x00
++#define NANDRW_JFFS2          0x02
++#define NANDRW_JFFS2_SKIP     0x04
++
++#define ROUND_DOWN(value, boundary)      ((value) & (~((boundary)-1)))
++
++/* set $r8 to RAM_INIT_MAGIC, $r12 to NAND_BOOT_MAGIC then jump */
++#define BOOT(addr)  __asm__ volatile (" \
++      move.d 0x56902387, $r8\n\
++      move.d 0x9A9DB001, $r12\n\
++      jump %0\n\
++      nop\n\
++      " : : "r" (addr))
++
++#define D(x) 
++
++extern struct mtd_info *crisv32_nand_flash_probe(void);
++
++extern int _end, _bss, _edata;
++
++/* 
++ * NAND read/write from U-Boot 1.4.4
++ * Modified for newer mtd and use of mtd interface instead of nand directly.
++ *
++ * cmd: 0: NANDRW_WRITE                       write, fail on bad block
++ *    1: NANDRW_READ                  read, fail on bad block
++ *    2: NANDRW_WRITE | NANDRW_JFFS2  write, skip bad blocks
++ *    3: NANDRW_READ | NANDRW_JFFS2   read, data all 0xff for bad blocks
++ *      7: NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP read, skip bad blocks
++ */
++static int
++nand_rw (struct mtd_info* mtd, int cmd,
++       size_t start, size_t len,
++       size_t *retlen, u_char * buf)
++{
++      int ret = 0, n, total = 0;
++
++      /* eblk (once set) is the start of the erase block containing the
++       * data being processed.
++       */
++      size_t eblk = ~0;       /* force mismatch on first pass */
++      size_t erasesize = mtd->erasesize;
++
++      while (len) {
++              if ((start & (-erasesize)) != eblk) {
++                      /* have crossed into new erase block, deal with
++                       * it if it is marked bad.
++                       */
++                      eblk = start & (-erasesize); /* start of block */
++                      D(
++                        puts("New block ");
++                        putx(eblk);
++                        putnl();
++                       )
++                      if (mtd->block_isbad(mtd, eblk)) {
++                              if (cmd == (NANDRW_READ | NANDRW_JFFS2)) {
++                                      while (len > 0 &&
++                                             start - eblk < erasesize) {
++                                              *(buf++) = 0xff;
++                                              ++start;
++                                              ++total;
++                                              --len;
++                                      }
++                                      continue;
++                              } else if (cmd == (NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP)) {
++                                      start += erasesize;
++                                      continue;
++                              } else if (cmd == (NANDRW_WRITE | NANDRW_JFFS2)) {
++                                      /* skip bad block */
++                                      start += erasesize;
++                                      continue;
++                              } else {
++                                      ret = 1;
++                                      break;
++                              }
++                      }
++              }
++              /* The ECC will not be calculated correctly if
++                 less than 512 is written or read */
++              /* Is request at least 512 bytes AND it starts on a proper boundry */
++              if((start != ROUND_DOWN(start, 0x200)) || (len < 0x200))
++                      puts("Warning block writes should be at least 512 bytes and start on a 512 byte boundry\r\n");
++
++#if 0
++              buf = (void *) 0x38008000; /* to fixed address, for testing */
++#endif
++
++              if (cmd & NANDRW_READ) {
++                      ret = mtd->read_ecc(mtd, start, 
++                                          min(len, eblk + erasesize - start),
++                                          (size_t *)&n, (u_char*)buf, 
++                                          NULL, NULL);
++              } else {
++                      ret = mtd->write_ecc(mtd, start,
++                                           min(len, eblk + erasesize - start),
++                                           (size_t *)&n, (u_char*)buf, 
++                                           NULL, NULL);
++              }
++
++              if (ret) {
++                      break;
++              }
++
++              start  += n;
++              buf   += n;
++              total += n;
++              len   -= n;
++      }
++      if (retlen)
++              *retlen = total;
++
++      return ret;
++}
++
++
++void
++bootload()
++{
++      char revision;
++      struct mtd_info *mtd;
++
++      serial_init();
++
++      __asm__ volatile ("move $vr,%0" : "=rm" (revision));
++      if (revision < 32)
++      {
++              puts("You need an ETRAX FS to run Linux 2.6/crisv32.\r\n");
++              while(1);
++      }
++
++      puts("\r\n\nETRAX FS NAND boot loader\r\n");
++      puts("=========================\r\n");
++      puts("Rev 1, " __DATE__ " " __TIME__ "\r\n");
++
++      puts("CPU revision: ");
++      putx(revision);
++      putnl();
++
++      puts("Bootloader main at ") ;
++      putx((int) bootload);
++      putnl();
++
++      puts("Data end: ");
++      putx((long) &_edata);
++      putnl();
++
++      puts("Bss: ");
++      putx((long) &_bss);
++      putnl();
++
++      puts("Heap: ");
++      putx((long) &_end);
++      putnl();
++
++#if 0 /* loop calibration */
++      volatile int i;
++      puts ("10000 loops...");
++      for (i = 0; i < 10000; i++)
++              udelay(1000);
++      puts("done\r\n");
++#endif
++
++      puts("Identifying nand chip...\r\n");
++      mtd = crisv32_nand_flash_probe();
++      puts("Done.\r\n");
++      
++      if (mtd) {
++              puts("Chip identified. ");
++#if 0 /* print chip parameters */
++              if (mtd->name)
++                      puts(mtd->name);
++
++              puts("\r\ntype: ");
++              putx(mtd->type);
++              puts("\r\nflags: ");
++              putx(mtd->flags);
++              puts("\r\nsize: ");
++              putx(mtd->size);
++              puts("\r\nerasesize: ");
++              putx(mtd->erasesize);
++              puts("\r\noobblock: ");
++              putx(mtd->oobblock);
++              puts("\r\noobsize: ");
++              putx(mtd->oobsize);
++              puts("\r\necctype: ");
++              putx(mtd->ecctype);
++              puts("\r\neccsize: ");
++              putx(mtd->eccsize);
++#endif
++              putnl();
++
++              puts("Bad blocks:\r\n");
++
++              int i;
++              for (i = 0; i < mtd->size; i += mtd->erasesize) {
++                      if (mtd->block_isbad(mtd, i)) {
++                              putx(i);
++                              putnl();
++                      }
++              }
++
++#if 0 /* print oob parameters */
++              puts("Oob info:\r\n");
++              puts("useecc: ");
++              putx(mtd->oobinfo.useecc);
++              puts("\r\neccbytes: ");
++              putx(mtd->oobinfo.eccbytes);
++              puts("\r\neccpos: ");
++              for (i = 0; i < mtd->oobinfo.eccbytes; i++) {
++                      putx(mtd->oobinfo.eccpos[i]);
++                      putc(' ');
++              }
++              putnl();
++#endif
++
++              puts("Bootload in progress...");
++              int res, copied;
++              res = nand_rw(mtd, 
++                            NANDRW_READ | NANDRW_JFFS2 | NANDRW_JFFS2_SKIP, 
++                            CONFIG_ETRAX_PTABLE_SECTOR, 
++                            0x200000, /* 2 megs */
++                            &copied, 
++                            (void *) BOOT_ADDR);
++
++              puts("complete, status ");
++              putx(res);
++              puts(", loaded ");
++              putx(copied);
++              puts(" bytes\r\n");
++#if 1
++              puts("Data in DRAM:\r\n");
++              putx(* (int *) (BOOT_ADDR + 0));
++              putc(' ');
++              putx(* (int *) (BOOT_ADDR + 4));
++              putc(' ');
++              putx(* (int *) (BOOT_ADDR + 8));
++              putnl();
++#endif
++
++              if (res) 
++                      error("Corrupt data in NAND flash.");
++              else
++              {
++                      puts("Booting...\r\n");
++                      BOOT(BOOT_ADDR);
++              }
++      } else
++              error("No NAND flash chip found to boot from.");
++
++      while (1)
++              ; /* hang around until hell freezes over */
++}
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/crisv32_nand.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/crisv32_nand.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/crisv32_nand.c   1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/crisv32_nand.c   2006-11-21 15:40:02.000000000 +0100
+@@ -0,0 +1,157 @@
++/*
++ * Taken from arch/cris/arch-v32/drivers/nandflash.c
++ * and modified to use for boot loader.
++ *
++ *  Copyright (c) 2004
++ *
++ *  Derived from drivers/mtd/nand/spia.c
++ *      Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
++ * 
++ * $Id: crisv32_nand.c,v 1.2 2006/11/21 14:40:02 ricardw 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 "mtd.h"
++#include "nand.h"
++
++#if 0
++#include <linux/mtd/partitions.h>
++#endif
++
++#if 0
++#include <asm/arch/memmap.h>
++#endif
++
++#include <asm/arch/hwregs/reg_map.h>
++#include <asm/arch/hwregs/reg_rdwr.h>
++#include <asm/arch/hwregs/gio_defs.h>
++#include <asm/arch/hwregs/bif_core_defs.h>
++
++#include "lib.h"
++
++/* Hardware */
++
++#define CE_BIT 4
++#define CLE_BIT 5
++#define ALE_BIT 6
++#define BY_BIT 7
++
++#define NAND_RD_ADDR 0x90000000 /* read address */
++#define NAND_WR_ADDR 0x94000000 /* write address */
++
++static struct mtd_info *crisv32_mtd = NULL;
++
++/* 
++ *    hardware specific access to control-lines
++ */
++static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd)
++{
++      unsigned long flags;
++      reg_gio_rw_pa_dout dout = REG_RD(gio, regi_gio, rw_pa_dout);
++
++      switch(cmd){
++              case NAND_CTL_SETCLE: 
++                   dout.data |= (1<<CLE_BIT);
++                   break;
++              case NAND_CTL_CLRCLE: 
++                   dout.data &= ~(1<<CLE_BIT);
++                   break;
++              case NAND_CTL_SETALE:
++                   dout.data |= (1<<ALE_BIT);
++                   break;
++              case NAND_CTL_CLRALE: 
++                   dout.data &= ~(1<<ALE_BIT);
++                   break;
++              case NAND_CTL_SETNCE:
++                   dout.data &= ~(1<<CE_BIT);
++                   break;
++              case NAND_CTL_CLRNCE:
++                   dout.data |= (1<<CE_BIT);
++                   break;
++      }
++      REG_WR(gio, regi_gio, rw_pa_dout, dout);
++#if 0 
++      /* read from gpio reg to flush pipeline.
++       * doesn't seem to be necessary.
++       */
++      (void) REG_RD(gio, regi_gio, rw_pa_dout); /* gpio sync */
++#endif
++}
++
++/*
++ *    read device ready pin
++ */
++int crisv32_device_ready(struct mtd_info *mtd)
++{
++      reg_gio_r_pa_din din = REG_RD(gio, regi_gio, r_pa_din);
++      return ((din.data & (1 << BY_BIT)) >> BY_BIT);
++}
++
++/*
++ * Main initialization routine
++ */
++struct mtd_info* crisv32_nand_flash_probe (void)
++{
++      reg_bif_core_rw_grp3_cfg bif_cfg = REG_RD(bif_core, regi_bif_core, rw_grp3_cfg);
++      reg_gio_rw_pa_oe pa_oe = REG_RD(gio, regi_gio, rw_pa_oe);
++      struct nand_chip *this;
++      int err = 0;
++
++      /* Allocate memory for MTD device structure and private data */
++      crisv32_mtd = malloc (sizeof(struct mtd_info) + sizeof (struct nand_chip));
++      if (!crisv32_mtd) {
++              puts ("Unable to allocate CRISv32 NAND MTD device structure.\r\n");
++              err = -ENOMEM;
++              return NULL;
++      }
++
++      /* Get pointer to private data */
++      this = (struct nand_chip *) (&crisv32_mtd[1]);
++
++      pa_oe.oe |= 1 << CE_BIT;
++      pa_oe.oe |= 1 << ALE_BIT;
++      pa_oe.oe |= 1 << CLE_BIT;
++      pa_oe.oe &= ~ (1 << BY_BIT);
++      REG_WR(gio, regi_gio, rw_pa_oe, pa_oe);
++
++      bif_cfg.gated_csp0 = regk_bif_core_rd;
++      bif_cfg.gated_csp1 = regk_bif_core_wr;
++      REG_WR(bif_core, regi_bif_core, rw_grp3_cfg, bif_cfg);
++
++      /* Initialize structures */
++      memset((char *) crisv32_mtd, 0, sizeof(struct mtd_info));
++      memset((char *) this, 0, sizeof(struct nand_chip));
++
++      /* Link the private data with the MTD structure */
++      crisv32_mtd->priv = this;
++
++      /* Set address of NAND IO lines */
++      this->IO_ADDR_R = (void *) NAND_RD_ADDR;
++      this->IO_ADDR_W = (void *) NAND_WR_ADDR;
++      this->hwcontrol = crisv32_hwcontrol;
++      this->dev_ready = crisv32_device_ready;
++      /* 20 us command delay time */
++      this->chip_delay = 20;          
++      this->eccmode = NAND_ECC_SOFT;
++
++#if 0 /* don't use BBT in boot loader */
++      /* Enable the following for a flash based bad block table */
++      this->options = NAND_USE_FLASH_BBT;
++#endif
++      /* don't scan for BBT */
++      this->options = NAND_SKIP_BBTSCAN;
++
++      /* Scan to find existance of the device */
++      if (nand_scan (crisv32_mtd, 1)) {
++              err = -ENXIO;
++              puts ("nand_scan failed\r\n");
++              free (crisv32_mtd);
++              return NULL;
++      }
++      
++      return crisv32_mtd;
++}
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/head.S linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/head.S
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/head.S   2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/head.S   2007-01-31 16:52:19.000000000 +0100
+@@ -1,14 +1,85 @@
+-/* $Id: head.S,v 1.4 2004/11/01 16:10:28 starvik Exp $
+- *
+- * This used to be the rescue code but now that is handled by the
+- * RedBoot based RFL instead. Nothing to see here, move along.
++/* $Id: head.S,v 1.16 2007/01/31 15:52:19 pkj Exp $
++ * 
++ * Simple boot loader which can also handle NAND chips.
+  */
+-#include <asm/arch/hwregs/reg_map_asm.h>
+-#include <asm/arch/hwregs/config_defs_asm.h>
++#include <asm/arch/hwregs/asm/reg_map_asm.h>
++#include <asm/arch/hwregs/asm/config_defs_asm.h>
++
++#define RAM_INIT_MAGIC 0x56902387
++#define NAND_BOOT_MAGIC 0x9A9DB001
++
++;; Debugging. Normally all these are set to 0.
++#define LEDS (0)
++#define LEDTEST (0)
++#define FLASH_LEDS_INSTEAD_OF_BOOT (0)
++#define SERIAL_DUMP (0)
++#define SERIAL_PORT (0)
++#define SERIAL_RECEIVE (0)
++
++#if LEDS
++#include <asm/arch/hwregs/asm/gio_defs_asm.h>
++#include <asm/arch/hwregs/asm/pinmux_defs_asm.h>
++#include <asm/arch/hwregs/asm/bif_core_defs_asm.h>
++#endif
++
++#if SERIAL_DUMP
++#include <asm/arch/hwregs/asm/ser_defs_asm.h>
++#include <asm/arch/hwregs/asm/pinmux_defs_asm.h>
++#endif
++
++
++#if (SERIAL_PORT == 0)
++#define regi_serial regi_ser0
++#endif
++#if (SERIAL_PORT == 1)
++#define regi_serial regi_ser1
++#endif
++
++;; Macros
++
++#if LEDS
++.macro        SAY x
++      orq 31, $r9
++      and.d ~(\x), $r9
++      move.d $r9, [$r8]
++.endm
++#else
++.macro        SAY x
++      ;; nothing
++.endm
++#endif
++
++#if SERIAL_DUMP
++.macro        DISPLAY x
++      move.d \x, $r6  ; save value
++      moveq 28, $r5 ; counter / shift amount
++8:
++      move.d $r6, $r3 ; fetch value
++      bsr nybbleout
++      lsr.d $r5, $r3 ; shift nybble we want (delay slot)
++      subq 4, $r5  ; count down bits
++      bpl 8b ; loop
++      nop
++      bsr serout
++      moveq 13, $r3 ; delay slot
++      bsr serout
++      moveq 10, $r3 ; delay slot
++.endm
++#else
++.macro        DISPLAY x
++      ;; nothing
++.endm
++#endif
++
++
++;; Code
+       .text
++start:
++      ;; TODO: Add code for Ronny's search-for-good-block boot rom algorithm
++      
+       ;; Start clocks for used blocks.
+       move.d REG_ADDR(config, regi_config, rw_clk_ctrl), $r1
+       move.d [$r1], $r0
+@@ -17,22 +88,258 @@
+              REG_STATE(config, rw_clk_ctrl, fix_io, yes), $r0
+       move.d $r0, [$r1]
+-      ;; Copy 68KB NAND flash to Internal RAM (if NAND boot)
+-      move.d 0x38004000, $r10
+-      move.d 0x8000, $r11
+-      move.d 0x11000, $r12
+-      move.d copy_complete, $r13
+-      and.d  0x000fffff, $r13
+-      or.d   0x38000000, $r13
++
++#if LEDS
++      ;; set up for led control on PB 0..4
++      move.d REG_ADDR(gio, regi_gio, rw_pb_dout), $r8 ; output reg
++      move.d [$r8], $r9 ; shadow
++
++      ;; set up pinmux
++      move.d REG_ADDR(pinmux, regi_pinmux, rw_pb_gio), $r2
++      move.d [$r2], $r3
++        orq 31, $r3
++      move.d $r3, [$r2]
++
++      ;; set up GPIO
++      ;; set to outputs
++      move.d REG_ADDR(gio, regi_gio, rw_pb_oe), $r2
++      move.d [$r2], $r3
++      orq 31, $r3
++      move.d $r3, [$r2]
++
++
++#if LEDTEST
++              ;; led test
++
++      moveq 0, $r1 ; led 5-bit binary counter
++1:
++      or.d 31, $r9
++      xor $r1, $r9
++      move.d $r9, [$r8]
++      addq 1, $r1
++
++      move.d 100000000, $r2 ; delay loop (100e6 => 1s @200Mc)
++2:
++      bne 2b
++      subq 1, $r2
++
++      ba 1b ; loop
++      nop
++#endif
++
++#endif
++
++
++check_nand_boot:
++      ;; Check boot source by checking highest nybble of PC:
++      ;; If we're running at address 0x0XXXXXXX, we're in flash/eprom/sram
++      ;; If we're running at address 0x38000000, we're in internal RAM,
++      ;; so we're most likely coming from NAND.
++      ;; If we're running at address 0x40000000, we're in SDRAM,
++      ;; so we've most likely been started by some sort of bootstrapper
++      ;; e.g. fsboot, which in turn implies NAND, else we would be booting
++      ;; normally at 0x0XXXXXXX
++
++      SAY 1
++
++here:
++      lapcq   ., $r0          ; get PC
++      sub.d   (here-start), $r0 ; offset from here
++      beq     normal_boot     ; running at address 0 - normal boot
++      move.d  $r0, $r13       ; save offset for later (unused delay slot)
++      lsrq    28, $r0         ; get highest nybble
++
++      SAY 2
++
++      ;; Prepare to copy 128KB of the NAND flash to internal RAM
++      move.d  0x38000200, $r10 ; dest in internal RAM
++      move.d  0x1fe00, $r12   ; #bytes
++      cmpq    4, $r0          ; running in RAM => started by fsboot
++      bhs     copy_from_ram
++      movu.w 0x0200, $r11     ; source in flash (byte address) (DELAY SLOT)
+ #include "../../lib/nand_init.S"
+-      ;; No NAND found
++#if LEDS
++      ;; must set up registers again (clobbered by nand_init)
++      move.d REG_ADDR(gio, regi_gio, rw_pb_dout), $r8 ; output reg
++      move.d [$r8], $r9 ; shadow
++#endif
++
++      SAY 3
++
++      ba      copy_complete
++      nop
++
++        ; Since the code above has to reside within the first 256 bytes of 
++        ; NAND flash, verify that the code so far hasn't gone past this
++      ; limit. If you're considering removing this, you haven't 
++      ; properly understood the problem; see nand_init.S for details.
++      .org PAGE_SIZE_ADDRESSES        ; from nand_init.S
++      .org PAGE_SIZE_BYTES            ; from nand_init.S
++
++normal_boot:
++      SAY 4
++
++      ;; Normal NOR boot
+       move.d  CONFIG_ETRAX_PTABLE_SECTOR, $r10
+-      jump    $r10 ; Jump to decompresser
++      jump    $r10                    ; Jump to decompresser 
++      nop
++                              
++copy_from_ram:
++      add.d   $r13, $r11              ; mem offs + src offs -> src addr
++1:
++      move.d  [$r11+], $r0            ; read source
++      subq    4, $r12                 ; 4 bytes at a time
++      bne     1b                      ; loop until done
++      move.d  $r0, [$r10+]            ; write dest (DELAY SLOT)
++      jump    copy_complete           ; jump to internal RAM
++      nop                             ; delay slot
++
++copy_complete:        
++      SAY 5
++
++#if FLASH_LEDS_INSTEAD_OF_BOOT
++
++flash:
++
++              ;; led test
++
++      moveq 0, $r1 ; led binary counter
++1:
++      or.d 31, $r9
++      xor $r1, $r9
++      move.d $r9, [$r8]
++      addq 1, $r1
++
++      move.d 100000000, $r2 ; delay loop (100e6 => 1s)
++2:
++      bne 2b
++      subq 1, $r2
++
++      ba 1b ; loop forever
+       nop
++#endif
+-copy_complete:
+-      move.d  0x38000000 + CONFIG_ETRAX_PTABLE_SECTOR, $r10
+-      jump    $r10 ; Jump to decompresser
++
++#if SERIAL_DUMP
++      ;; dump memory to serial port
++
++#if (SERIAL_PORT == 1)
++      ;; set up serial-1 pins
++      move.d REG_ADDR(pinmux, regi_pinmux, rw_hwprot), $r1
++      move.d [$r1], $r0
++      or.d REG_STATE(pinmux, rw_hwprot, ser1, yes), $r0
++      move.d $r0, [$r1]
++#endif
++
++      ;; rw_xoff: chr and automatic = 0
++      move.d REG_ADDR(ser, regi_serial, rw_xoff), $r1
++      move.d [$r1], $r0
++      and.d ~(REG_MASK(ser, rw_xoff, automatic) | REG_MASK(ser, rw_xoff, chr)), $r0
++      move.d $r0, [$r1]
++
++      ;; tr control
++      move.d REG_ADDR(ser, regi_serial, rw_tr_ctrl), $r1
++      move.d [$r1], $r0
++      and.d ~(REG_MASK(ser, rw_tr_ctrl, base_freq) | REG_MASK(ser, rw_tr_ctrl, stop_bits)), $r0
++      or.d REG_STATE(ser, rw_tr_ctrl, stop_bits, bits2) | REG_STATE(ser, rw_tr_ctrl, base_freq, f29_493) | REG_STATE(ser, rw_tr_ctrl, en, yes), $r0
++      move.d $r0, [$r1]
++
++      ;; tr baud
++      move.d REG_ADDR(ser, regi_serial, rw_tr_baud_div), $r1
++      move.d [$r1], $r0
++      move.w (29493000 / 8) / 115200, $r0
++      move.d $r0, [$r1]
++
++#if SERIAL_RECEIVE
++      ;; rec control
++      move.d REG_ADDR(ser, regi_serial, rw_rec_ctrl), $r1
++      move.d [$r1], $r0
++      and.d ~(REG_MASK(ser, rw_rec_ctrl, base_freq)), $r0
++      or.d REG_STATE(ser, rw_rec_ctrl, base_freq, f29_493) | REG_STATE(ser, rw_tr_ctrl, en, yes), $r0
++      move.d $r0, [$r1]
++
++      ;; rec baud
++      move.d REG_ADDR(ser, regi_serial, rw_rec_baud_div), $r1
++      move.d [$r1], $r0
++      move.w (29493000 / 8) / 115200, $r0
++      move.d $r0, [$r1]
++#endif
++
++      ;; dump memory
++
++      move.d 0x38000000, $r5 ; pointer
++
++      bsr serout
++      moveq 13, $r3
++      bsr serout
++      moveq 10, $r3
++      bsr serout
++      moveq 10, $r3
++
++1:
++      movu.b [$r5+], $r3 ; get value
++      move.d $r3, $r6 ; save
++
++      bsr nybbleout
++      lsrq 4, $r3 ; high nybble (delay slot)
++
++      move.d $r6, $r3 ; restore
++      bsr nybbleout
++      andq 15, $r3 ; delay slot
++
++      movu.b 32, $r3
++      bsr serout
+       nop
++
++      move.d $r5, $r3
++      andq 15, $r3
++      bne 1b
++      nop
++
++      bsr serout
++      moveq 13, $r3 ; delay slot
++      bsr serout 
++      moveq 10, $r3 ; delay slot
++
++      ba 1b ; loop forever
++      nop
++
++nybbleout:
++      cmpq 10, $r3
++      blo 1f
++      addq 48, $r3 ; delay slot
++      addq 7, $r3
++1:
++serout:
++      move.d REG_ADDR(ser, regi_serial, rs_stat_din), $r1
++      move.d REG_ADDR(ser, regi_serial, rw_dout), $r2
++2:
++      move.d [$r1], $r4
++      btstq REG_BIT(ser, rs_stat_din, tr_rdy), $r4
++      bpl 2b
++      nop
++      ret
++      move.d $r3, [$r2] ; delay slot
++
++#endif
++
++;; Init DRAM
++
++#include "../../lib/dram_init.S"
++      move.d  RAM_INIT_MAGIC, $r8     ; tell kernel boot loader dram init'd
++      move.d  NAND_BOOT_MAGIC, $r12   ; booted from NAND flash
++
++;; TODO: Clear .bss (not needed yet because size of .bss is zero)
++;; TODO: Change .ld script so that BSS is in DRAM?
++
++;; Ok, time to do something. Continue with boot loader in C.
++;; Must set up minimal C environment first though.
++
++      move.d  0x38020000, $sp ; stack pointer at top of internal RAM
++
++      move.d  bootload, $r10
++      jump    $r10                    ; Jump to boot loader
++      nop
++
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/lib.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/lib.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/lib.c    1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/lib.c    2006-11-03 11:35:52.000000000 +0100
+@@ -0,0 +1,243 @@
++/*
++ * lib.c
++ * Small practical functions for boot loader
++ * malloc/free
++ * memcpy/memset
++ * writeb/writew/readb/readw
++ * putc/puts/putnybble/putx
++ * error/error2/BUG
++ * serial_init
++ *
++ * $Id: lib.c,v 1.4 2006/11/03 10:35:52 pkj Exp $
++ * 
++ */
++
++#include <linux/types.h>
++#include <asm/arch/hwregs/reg_rdwr.h>
++#include <asm/arch/hwregs/reg_map.h>
++#include <asm/arch/hwregs/ser_defs.h>
++#include <asm/arch/hwregs/pinmux_defs.h>
++
++#include "lib.h"
++
++/* the "heap" is put directly after BSS ends, at _end */
++  
++extern int _end;
++static long free_mem_ptr = (long)&_end;
++ 
++void *malloc(int size)
++{
++      void *p;
++
++      if (size <0) error("Malloc error");
++
++      free_mem_ptr = (free_mem_ptr + 3) & ~3; /* Align */
++
++      p = (void *)free_mem_ptr;
++      free_mem_ptr += size;
++
++      return p;
++}
++
++void free(void *where)
++{     /* Don't care */
++}
++
++/* I/O */
++
++unsigned char readb(const volatile void *addr)
++{
++      return *(volatile unsigned char *) addr;
++}
++
++unsigned short readw(const volatile void *addr)
++{
++      return *(volatile unsigned short *) addr;
++}
++
++void writeb(unsigned char b, volatile void *addr)
++{
++      *(volatile unsigned char *) addr = b;
++}
++
++void writew(unsigned short b, volatile void *addr)
++{
++      *(volatile unsigned short *) addr = b;
++}
++
++/* info and error messages to serial console */
++
++#ifndef CONFIG_ETRAX_DEBUG_PORT_NULL
++static inline void
++serout(const char c, reg_scope_instances regi_ser)
++{
++      reg_ser_rs_stat_din rs;
++      reg_ser_rw_dout dout = {.data = c};
++
++      do {
++              rs = REG_RD(ser, regi_ser, rs_stat_din);
++      }
++      while (!rs.tr_rdy);/* Wait for tranceiver. */
++      
++      REG_WR(ser, regi_ser, rw_dout, dout);
++}
++
++
++void
++putc(const char c)
++{
++#ifdef CONFIG_ETRAX_DEBUG_PORT0
++      serout(c, regi_ser0);
++#endif
++#ifdef CONFIG_ETRAX_DEBUG_PORT1
++      serout(c, regi_ser1);
++#endif
++#ifdef CONFIG_ETRAX_DEBUG_PORT2
++      serout(c, regi_ser2);
++#endif
++#ifdef CONFIG_ETRAX_DEBUG_PORT3
++      serout(c, regi_ser3);
++#endif
++}
++
++
++void
++puts(const char *s)
++{
++      while (*s)
++              putc(*s++);
++}
++
++void
++putnybble(int n)
++{
++      putc("0123456789abcdef"[n & 15]);
++}
++
++void
++putx(int x)
++{
++      int i;
++
++      puts("0x");
++      for (i = 7; i >= 0; i--)
++              putnybble(x >> 4*i);
++}
++
++void
++putnl()
++{
++      puts("\r\n");
++}
++
++#endif /* CONFIG_ETRAX_DEBUG_PORT_NULL */
++
++void*
++memset(void* s, int c, size_t n)
++{
++      int i;
++      char *ss = (char*)s;
++
++      for (i=0;i<n;i++) ss[i] = c;
++}
++
++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];
++}
++
++void
++error(const char *x)
++{
++      puts("\r\n\n");
++      puts(x);
++      puts("\r\n\n -- System halted\n");
++
++      while(1);       /* Halt */
++}
++
++void
++error2(const char *x, int y, const char *z)
++{
++      puts("\r\n\n");
++      puts(x);
++      putc(':');
++      putx(y);
++      putc(':');
++      puts(z);
++      puts("\r\n\n -- System halted\n");
++
++      while(1);       /* Halt */
++}
++
++static inline void
++serial_setup(reg_scope_instances regi_ser)
++{
++      reg_ser_rw_xoff xoff;
++      reg_ser_rw_tr_ctrl tr_ctrl;
++      reg_ser_rw_rec_ctrl rec_ctrl;
++      reg_ser_rw_tr_baud_div tr_baud;
++      reg_ser_rw_rec_baud_div rec_baud;
++      
++      /* Turn off XOFF. */
++      xoff = REG_RD(ser, regi_ser, rw_xoff);
++      
++      xoff.chr = 0;
++      xoff.automatic = regk_ser_no;
++      
++      REG_WR(ser, regi_ser, rw_xoff, xoff);
++
++      /* Set baudrate and stopbits. */
++      tr_ctrl = REG_RD(ser, regi_ser, rw_tr_ctrl);
++      rec_ctrl = REG_RD(ser, regi_ser, rw_rec_ctrl);
++      tr_baud = REG_RD(ser, regi_ser, rw_tr_baud_div);
++      rec_baud = REG_RD(ser, regi_ser, rw_rec_baud_div);
++      
++      tr_ctrl.stop_bits = 1;  /* 2 stop bits. */
++      tr_ctrl.en = 1; /* enable transmitter */
++      rec_ctrl.en = 1; /* enabler receiver */
++      
++      /* 
++       * The baudrate setup used to be a bit fishy, but now transmitter and
++       * receiver are both set to the intended baud rate, 115200.
++       * The magic value is 29.493 MHz.
++       */
++      tr_ctrl.base_freq = regk_ser_f29_493;
++      rec_ctrl.base_freq = regk_ser_f29_493;
++      tr_baud.div = (29493000 / 8) / 115200;
++      rec_baud.div = (29493000 / 8) / 115200;
++      
++      REG_WR(ser, regi_ser, rw_tr_ctrl, tr_ctrl);
++      REG_WR(ser, regi_ser, rw_tr_baud_div, tr_baud);
++      REG_WR(ser, regi_ser, rw_rec_ctrl, rec_ctrl);
++      REG_WR(ser, regi_ser, rw_rec_baud_div, rec_baud);
++}
++
++void
++serial_init()
++{
++      reg_pinmux_rw_hwprot hwprot;
++      
++      hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
++#ifdef CONFIG_ETRAX_DEBUG_PORT0
++      serial_setup(regi_ser0);
++#endif
++#ifdef CONFIG_ETRAX_DEBUG_PORT1
++      hwprot.ser1 = regk_pinmux_yes;
++      serial_setup(regi_ser1);
++#endif
++#ifdef CONFIG_ETRAX_DEBUG_PORT2
++      hwprot.ser2 = regk_pinmux_yes;
++      serial_setup(regi_ser2);
++#endif
++#ifdef CONFIG_ETRAX_DEBUG_PORT3
++      hwprot.ser3 = regk_pinmux_yes;
++      serial_setup(regi_ser3);
++#endif
++      REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot);
++}
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/lib.h linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/lib.h
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/lib.h    1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/lib.h    2006-11-03 11:35:52.000000000 +0100
+@@ -0,0 +1,56 @@
++/*
++ * lib.c
++ * Small practical functions for boot loader
++ * malloc/free
++ * memset/memcpy
++ * putc/puts/putnybble/putd
++ * writeb/writew/readb/readw
++ * error/error2/BUG
++ * serial_init
++ *
++ * $Id: lib.h,v 1.3 2006/11/03 10:35:52 pkj Exp $
++ * 
++ */
++
++#ifndef _LIB_H
++#define _LIB_H
++
++#include <linux/types.h>
++
++/* nice stuff we need without having any library around */
++
++void* memset(void* s, int c, size_t n);
++void* memcpy(void* __dest, __const void* __src,
++           size_t __n);
++
++#define memzero(s, n)     memset ((s), 0, (n))
++
++#undef BUG
++#define BUG() error2("BUG in " __FILE__, __LINE__, __FUNCTION__)
++
++void *malloc(int size);
++void free(void *where);
++void error(const char *m);
++void error2(const char *m, int l, const char *f);
++void serial_init(void);
++
++#ifndef CONFIG_ETRAX_DEBUG_PORT_NULL 
++void putc(const char);
++void puts(const char *);
++void putnybble(int n);
++void putx(int x);
++void putnl();
++#else
++#define putc(ch) 
++#define puts(str)
++#define putnybble(nyb)
++#define putx(x)
++#define putnl()
++#endif /* CONFIG_ETRAX_DEBUG_PORT_NULL */
++
++unsigned char readb(const volatile void *addr);
++unsigned short readw(const volatile void *addr);
++void writeb(unsigned char b, volatile void *addr);
++void writew(unsigned short b, volatile void *addr);
++
++#endif /* _LIB_H */
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/mtd-abi.h linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/mtd-abi.h
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/mtd-abi.h        1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/mtd-abi.h        2006-09-06 11:21:07.000000000 +0200
+@@ -0,0 +1,121 @@
++/*
++ * $Id: mtd-abi.h,v 1.1 2006/09/06 09:21:07 ricardw 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_DATAFLASH         6
++#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
++#define MTD_NO_VIRTBLOCKS     256     // Virtual blocks not allowed
++#define MTD_PROGRAM_REGIONS   512     // Configurable Programming Regions
++
++// Some common devices / combinations of capabilities
++#define MTD_CAP_ROM           0
++#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)
++#define MTD_NANDECC_AUTOPL_USR        4       // Use the given autoplacement scheme rather than using the default
++
++/* OTP mode selection */
++#define MTD_OTP_OFF           0
++#define MTD_OTP_FACTORY               1
++#define MTD_OTP_USER          2
++
++struct mtd_info_user {
++      uint8_t type;
++      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;
++};
++
++struct otp_info {
++      uint32_t start;
++      uint32_t length;
++      uint32_t locked;
++};
++
++#define MEMGETINFO              _IOR('M', 1, struct mtd_info_user)
++#define MEMERASE                _IOW('M', 2, struct erase_info_user)
++#define MEMWRITEOOB             _IOWR('M', 3, struct mtd_oob_buf)
++#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)
++#define OTPSELECT             _IOR('M', 13, int)
++#define OTPGETREGIONCOUNT     _IOW('M', 14, int)
++#define OTPGETREGIONINFO      _IOW('M', 15, struct otp_info)
++#define OTPLOCK               _IOR('M', 16, struct otp_info)
++
++struct nand_oobinfo {
++      uint32_t useecc;
++      uint32_t eccbytes;
++      uint32_t oobfree[8][2];
++      uint32_t eccpos[32];
++};
++
++#endif /* __MTD_ABI_H__ */
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/mtd.h linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/mtd.h
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/mtd.h    1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/mtd.h    2006-12-14 07:59:24.000000000 +0100
+@@ -0,0 +1,270 @@
++/*
++ * $Id: mtd.h,v 1.5 2006/12/14 06:59:24 ricardw Exp $
++ *
++ * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
++ *
++ * Released under GPL
++ */
++
++#ifndef __MTD_MTD_H__
++#define __MTD_MTD_H__
++
++#ifndef __KERNEL__
++#error This is a kernel header. Perhaps include mtd-user.h instead?
++#endif
++
++/* only include absolutely necessary linux headers which will not change
++ * significantly 
++ */
++#include <linux/types.h>
++#include <linux/errno.h>
++
++#if 0
++#include <linux/module.h>
++#include <linux/uio.h>
++#include <linux/notifier.h>
++
++#include <linux/mtd/compatmac.h>
++#include <mtd/mtd-abi.h>
++#endif
++
++#include "mtd-abi.h"
++
++/* local config, we don't want linux/config.h here */
++/* any undef/define pairs here avoid warnings due to linux autoconf includes */
++#undef CONFIG_MTD_PARTITIONS
++#undef CONFIG_MTD_DEBUG
++#undef CONFIG_MTD_NAND_VERIFY_WRITE
++#define CONFIG_MTD_NAND_VERIFY_WRITE
++
++/* our MTD config */
++
++#define NAND_BBT_SUPPORT  0
++#define NAND_WRITE_SUPPORT 0
++#define NAND_ERASE_SUPPORT 0
++#define NAND_MULTICHIP_SUPPORT 0
++#define NAND_HWECC_SUPPORT 0
++#define NAND_KVEC_SUPPORT 0
++
++
++#define MTD_CHAR_MAJOR 90
++#define MTD_BLOCK_MAJOR 31
++#define MAX_MTD_DEVICES 16
++
++#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;
++      u_int cell;
++      void (*callback) (struct erase_info *self);
++      u_long priv;
++      u_char state;
++      struct erase_info *next;
++};
++
++struct mtd_erase_region_info {
++      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 of erasesize in this region */
++};
++
++struct mtd_info {
++      u_char type;
++      u_int32_t flags;
++      u_int32_t size;  // Total size of the MTD
++
++      /* "Major" erase size for the device. Naïve users may take this
++       * to be the only erase size available, or may use the more detailed
++       * information below if they desire
++       */
++      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;
++
++      /*
++       * Reuse some of the above unused fields in the case of NOR flash
++       * with configurable programming regions to avoid modifying the
++       * user visible structure layout/size.  Only valid when the
++       * MTD_PROGRAM_REGIONS flag is set.
++       * (Maybe we should have an union for those?)
++       */
++#define MTD_PROGREGION_SIZE(mtd)  (mtd)->oobblock
++#define MTD_PROGREGION_CTRLMODE_VALID(mtd)  (mtd)->oobsize
++#define MTD_PROGREGION_CTRLMODE_INVALID(mtd)  (mtd)->ecctype
++
++      // Kernel-only stuff starts here.
++      char *name;
++      int index;
++
++      // oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO)
++      struct nand_oobinfo oobinfo;
++      u_int32_t oobavail;  // Number of bytes in OOB area available for fs
++
++      /* Data for variable erase regions. If numeraseregions is zero,
++       * it means that the whole device has erasesize as given above.
++       */
++      int numeraseregions;
++      struct mtd_erase_region_info *eraseregions;
++
++      /* This really shouldn't be here. It can go away in 2.5 */
++      u_int32_t bank_size;
++
++      int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
++
++      /* This stuff for eXecute-In-Place */
++      int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf);
++
++      /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
++      void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len);
++
++
++      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, 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);
++
++      /*
++       * Methods to access the protection register area, present in some
++       * flash devices. The user data is one time programmable but the
++       * factory data is read only.
++       */
++      int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
++      int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
++      int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
++      int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
++      int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
++      int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
++
++#if NAND_KVEC_SUPPORT
++      /* 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 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);
++#endif
++
++      /* Sync */
++      void (*sync) (struct mtd_info *mtd);
++
++      /* Chip-supported device locking */
++      int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
++      int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
++
++      /* Power Management functions */
++      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);
++
++#if 0 /* don't know what this is for */
++      struct notifier_block reboot_notifier;  /* default mode before reboot */
++#endif
++
++      void *priv;
++
++      struct module *owner;
++      int usecount;
++};
++
++#if 0 /* don't need these */
++      /* Kernel-side ioctl definitions */
++
++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 void put_mtd_device(struct mtd_info *mtd);
++
++
++struct mtd_notifier {
++      void (*add)(struct mtd_info *mtd);
++      void (*remove)(struct mtd_info *mtd);
++      struct list_head list;
++};
++
++
++extern void register_mtd_user (struct mtd_notifier *new);
++extern int unregister_mtd_user (struct mtd_notifier *old);
++#endif
++
++#if NAND_KVEC_SUPPORT
++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 kvec *vecs,
++                    unsigned long count, loff_t from, size_t *retlen);
++#endif
++
++#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)
++#define MTD_READ(mtd, args...) (*(mtd->read))(mtd, args)
++#define MTD_WRITE(mtd, args...) (*(mtd->write))(mtd, args)
++#define MTD_READV(mtd, args...) (*(mtd->readv))(mtd, args)
++#define MTD_WRITEV(mtd, args...) (*(mtd->writev))(mtd, args)
++#define MTD_READECC(mtd, args...) (*(mtd->read_ecc))(mtd, args)
++#define MTD_WRITEECC(mtd, args...) (*(mtd->write_ecc))(mtd, args)
++#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)
++
++
++#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
++ */
++#define MTD_DEBUG_LEVEL0      (0)     /* Quiet   */
++#define MTD_DEBUG_LEVEL1      (1)     /* Audible */
++#define MTD_DEBUG_LEVEL2      (2)     /* Loud    */
++#define MTD_DEBUG_LEVEL3      (3)     /* Noisy   */
++
++#ifdef CONFIG_MTD_DEBUG
++#define DEBUG(n, args...)                             \
++      do {                                            \
++              if (n <= CONFIG_MTD_DEBUG_VERBOSE)      \
++                      printk(KERN_INFO args);         \
++      } while(0)
++#else /* CONFIG_MTD_DEBUG */
++#define DEBUG(n, args...) do { } while(0)
++
++#endif /* CONFIG_MTD_DEBUG */
++
++#endif /* __MTD_MTD_H__ */
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand.h linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand.h
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand.h   1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand.h   2006-11-03 11:35:52.000000000 +0100
+@@ -0,0 +1,521 @@
++/*
++ *  linux/include/linux/mtd/nand.h
++ *
++ *  Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com>
++ *                     Steven J. Hill <sjhill@realitydiluted.com>
++ *                   Thomas Gleixner <tglx@linutronix.de>
++ *
++ * $Id: nand.h,v 1.4 2006/11/03 10:35:52 pkj 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.
++ *
++ *  Info:
++ *   Contains standard defines and IDs for NAND flash devices
++ *
++ *  Changelog:
++ *   01-31-2000 DMW     Created
++ *   09-18-2000 SJH     Moved structure out of the Disk-On-Chip drivers
++ *                    so it can be used by other NAND flash device
++ *                    drivers. I also changed the copyright since none
++ *                    of the original contents of this file are specific
++ *                    to DoC devices. David can whack me with a baseball
++ *                    bat later if I did something naughty.
++ *   10-11-2000 SJH     Added private NAND flash structure for driver
++ *   10-24-2000 SJH     Added prototype for 'nand_scan' function
++ *   10-29-2001 TG    changed nand_chip structure to support
++ *                    hardwarespecific function for accessing control lines
++ *   02-21-2002 TG    added support for different read/write adress and
++ *                    ready/busy line access function
++ *   02-26-2002 TG    added chip_delay to nand_chip structure to optimize
++ *                    command delay times for different chips
++ *   04-28-2002 TG    OOB config defines moved from nand.c to avoid duplicate
++ *                    defines in jffs2/wbuf.c
++ *   08-07-2002 TG    forced bad block location to byte 5 of OOB, even if
++ *                    CONFIG_MTD_NAND_ECC_JFFS2 is not set
++ *   08-10-2002 TG    extensions to nand_chip structure to support HW-ECC
++ *
++ *   08-29-2002 tglx  nand_chip structure: data_poi for selecting
++ *                    internal / fs-driver buffer
++ *                    support for 6byte/512byte hardware ECC
++ *                    read_ecc, write_ecc extended for different oob-layout
++ *                    oob layout selections: NAND_NONE_OOB, NAND_JFFS2_OOB,
++ *                    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
++ *  01-17-2005 dmarlin        added extended commands for AG-AND device and added option
++ *                    for BBT_AUTO_REFRESH.
++ *  01-20-2005 dmarlin        added optional pointer to hardware specific callback for
++ *                    extra error status checks.
++ */
++#ifndef __LINUX_MTD_NAND_H
++#define __LINUX_MTD_NAND_H
++
++#if 0 /* avoid these as much as possible */
++#include <linux/wait.h>
++#include <linux/spinlock.h>
++#include <linux/mtd/mtd.h>
++#endif
++
++#include "mtd.h" /* local */
++
++#include <linux/kernel.h> /* we do need this though ... */
++
++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.
++ */
++#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
++ */
++#define NAND_CMD_READ0                0
++#define NAND_CMD_READ1                1
++#define NAND_CMD_PAGEPROG     0x10
++#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
++
++/* Extended commands for AG-AND device */
++/*
++ * Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but
++ *       there is no way to distinguish that from NAND_CMD_READ0
++ *       until the remaining sequence of commands has been completed
++ *       so add a high order bit and mask it off in the command.
++ */
++#define NAND_CMD_DEPLETE1     0x100
++#define NAND_CMD_DEPLETE2     0x38
++#define NAND_CMD_STATUS_MULTI 0x71
++#define NAND_CMD_STATUS_ERROR 0x72
++/* multi-bank error status (banks 0-3) */
++#define NAND_CMD_STATUS_ERROR0        0x73
++#define NAND_CMD_STATUS_ERROR1        0x74
++#define NAND_CMD_STATUS_ERROR2        0x75
++#define NAND_CMD_STATUS_ERROR3        0x76
++#define NAND_CMD_STATUS_RESET 0x7f
++#define NAND_CMD_STATUS_CLEAR 0xff
++
++/* Status bits */
++#define NAND_STATUS_FAIL      0x01
++#define NAND_STATUS_FAIL_N1   0x02
++#define NAND_STATUS_TRUE_READY        0x20
++#define NAND_STATUS_READY     0x40
++#define NAND_STATUS_WP                0x80
++
++/*
++ * Constants for ECC_MODES
++ */
++
++/* 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
++/* Hardware ECC 8 byte ECC per 512 Byte data */
++#define NAND_ECC_HW8_512      6
++/* Hardware ECC 12 byte ECC per 2048 Byte data */
++#define NAND_ECC_HW12_2048    7
++
++/*
++ * Constants for Hardware ECC
++ */
++/* Reset Hardware ECC for read */
++#define NAND_ECC_READ         0
++/* Reset Hardware ECC for write */
++#define NAND_ECC_WRITE                1
++/* Enable Hardware ECC before syndrom is read back from flash */
++#define NAND_ECC_READSYN      2
++
++/* Bit mask for flags passed to do_nand_read_ecc */
++#define NAND_GET_DEVICE               0x80
++
++
++/* Option constants for bizarre disfunctionality and real
++*  features
++*/
++/* 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
++/* Chip requires that BBT is periodically rewritten to prevent
++ * bits from adjacent blocks from 'leaking' in altering data.
++ * This happens with the Renesas AG-AND chips, possibly others.  */
++#define BBT_AUTO_REFRESH      0x00000080
++
++/* Options valid for Samsung large page devices */
++#define NAND_SAMSUNG_LP_OPTIONS \
++      (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
++/* This option skips the bbt scan during initialization. */
++#define NAND_SKIP_BBTSCAN     0x00040000
++
++/* Options set by nand scan */
++/* Nand scan has allocated oob_buf */
++#define NAND_OOBBUF_ALLOC     0x40000000
++/* Nand scan has allocated data_buf */
++#define NAND_DATABUF_ALLOC    0x80000000
++
++
++/*
++ * nand_state_t - chip states
++ * Enumeration for NAND flash chip state
++ */
++typedef enum {
++      FL_READY,
++      FL_READING,
++      FL_WRITING,
++      FL_ERASING,
++      FL_SYNCING,
++      FL_CACHEDPRG,
++      FL_PM_SUSPENDED,
++} nand_state_t;
++
++/* Keep gcc happy */
++struct nand_chip;
++
++/**
++ * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices
++ * @lock:               protection lock
++ * @active:           the mtd device which holds the controller currently
++ * @wq:                       wait queue to sleep on if a NAND operation is in progress
++ *                      used instead of the per chip wait queue when a hw controller is available
++ */
++#if NAND_HWECC_SUPPORT
++struct nand_hw_control {
++      spinlock_t       lock;
++      struct nand_chip *active;
++      wait_queue_head_t wq;
++};
++#endif
++
++/**
++ * struct nand_chip - NAND Private Flash Chip Data
++ * @IO_ADDR_R:                [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device
++ * @IO_ADDR_W:                [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device
++ * @read_byte:                [REPLACEABLE] read one byte from the chip
++ * @write_byte:               [REPLACEABLE] write one byte to the chip
++ * @read_word:                [REPLACEABLE] read one word from the chip
++ * @write_word:               [REPLACEABLE] write one word to the chip
++ * @write_buf:                [REPLACEABLE] write data from the buffer to the chip
++ * @read_buf:         [REPLACEABLE] read data from the chip into the buffer
++ * @verify_buf:               [REPLACEABLE] verify buffer contents against the chip data
++ * @select_chip:      [REPLACEABLE] select chip nr
++ * @block_bad:                [REPLACEABLE] check, if the block is bad
++ * @block_markbad:    [REPLACEABLE] mark the block bad
++ * @hwcontrol:                [BOARDSPECIFIC] hardwarespecific function for accesing control-lines
++ * @dev_ready:                [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
++ *                    If set to NULL no access to ready/busy is available and the ready/busy information
++ *                    is read from the chip status register
++ * @cmdfunc:          [REPLACEABLE] hardwarespecific function for writing commands to the chip
++ * @waitfunc:         [REPLACEABLE] hardwarespecific function for wait on ready
++ * @calculate_ecc:    [REPLACEABLE] function for ecc calculation or readback from ecc hardware
++ * @correct_data:     [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw)
++ * @enable_hwecc:     [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only
++ *                    be provided if a hardware ECC is available
++ * @erase_cmd:                [INTERN] erase command write function, selectable due to AND support
++ * @scan_bbt:         [REPLACEABLE] function to scan bad block table
++ * @eccmode:          [BOARDSPECIFIC] mode of ecc, see defines
++ * @eccsize:          [INTERN] databytes used per ecc-calculation
++ * @eccbytes:                 [INTERN] number of ecc bytes per ecc-calculation step
++ * @eccsteps:         [INTERN] number of ecc calculation steps per page
++ * @chip_delay:               [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
++ * @chip_lock:                [INTERN] spinlock used to protect access to this structure and the chip
++ * @wq:                       [INTERN] wait queue to sleep on if a NAND operation is in progress
++ * @state:            [INTERN] the current state of the NAND device
++ * @page_shift:               [INTERN] number of address bits in a page (column address bits)
++ * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
++ * @bbt_erase_shift:  [INTERN] number of address bits in a bbt entry
++ * @chip_shift:               [INTERN] number of address bits in one chip
++ * @data_buf:         [INTERN] internal buffer for one page + oob
++ * @oob_buf:          [INTERN] oob buffer for one eraseblock
++ * @oobdirty:         [INTERN] indicates that oob_buf must be reinitialized
++ * @data_poi:         [INTERN] pointer to a data buffer
++ * @options:          [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
++ *                    special functionality. See the defines for further explanation
++ * @badblockpos:      [INTERN] position of the bad block marker in the oob area
++ * @numchips:         [INTERN] number of physical chips
++ * @chipsize:         [INTERN] the size of one chip for multichip arrays
++ * @pagemask:         [INTERN] page number mask = number of (pages / chip) - 1
++ * @pagebuf:          [INTERN] holds the pagenumber which is currently in data_buf
++ * @autooob:          [REPLACEABLE] the default (auto)placement scheme
++ * @bbt:              [INTERN] bad block table pointer
++ * @bbt_td:           [REPLACEABLE] bad block table descriptor for flash lookup
++ * @bbt_md:           [REPLACEABLE] bad block table mirror descriptor
++ * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan
++ * @controller:               [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices
++ * @priv:             [OPTIONAL] pointer to private chip date
++ * @errstat:          [OPTIONAL] hardware specific function to perform additional error status checks
++ *                    (determine if errors are correctable)
++ */
++
++struct nand_chip {
++      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);
++      int             (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
++      int             (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
++      void            (*enable_hwecc)(struct mtd_info *mtd, int mode);
++      void            (*erase_cmd)(struct mtd_info *mtd, int page);
++      int             (*scan_bbt)(struct mtd_info *mtd);
++      int             eccmode;
++      int             eccsize;
++      int             eccbytes;
++      int             eccsteps;
++      int             chip_delay;
++#if 0 /* no spinlocks or wait queues in boot loader */
++      spinlock_t      chip_lock;
++      wait_queue_head_t wq;
++#endif
++      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;
++      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;
++      struct nand_bbt_descr   *badblock_pattern;
++      struct nand_hw_control  *controller;
++      void            *priv;
++      int             (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
++};
++
++/*
++ * NAND Flash Manufacturer ID Codes
++ */
++#define NAND_MFR_TOSHIBA      0x98
++#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
++#define NAND_MFR_HYNIX          0xad
++
++/**
++ * struct nand_flash_dev - NAND Flash Device ID Structure
++ *
++ * @name:     Identify the device type
++ * @id:       device ID code
++ * @pagesize:         Pagesize in bytes. Either 256 or 512 or 0
++ *            If the pagesize is 0, then the real pagesize
++ *            and the eraseize are determined from the
++ *            extended id bytes in the chip
++ * @erasesize:        Size of an erase block in the flash device.
++ * @chipsize:         Total chipsize in Mega Bytes
++ * @options:  Bitfield to store chip relevant options
++ */
++struct nand_flash_dev {
++      char *name;
++      int id;
++      unsigned long pagesize;
++      unsigned long chipsize;
++      unsigned long erasesize;
++      unsigned long options;
++};
++
++/**
++ * struct nand_manufacturers - NAND Flash Manufacturer ID Structure
++ * @name:     Manufacturer name
++ * @id:       manufacturer ID code of device.
++*/
++struct nand_manufacturers {
++      int id;
++      char * name;
++};
++
++extern struct nand_flash_dev nand_flash_ids[];
++extern struct nand_manufacturers nand_manuf_ids[];
++
++/**
++ * struct nand_bbt_descr - bad block table descriptor
++ * @options:  options for this descriptor
++ * @pages:    the page(s) where we find the bbt, used with option BBT_ABSPAGE
++ *            when bbt is searched, then we store the found bbts pages here.
++ *            Its an array and supports up to 8 chips now
++ * @offs:     offset of the pattern in the oob area of the page
++ * @veroffs:  offset of the bbt version counter in the oob are of the page
++ * @version:  version read from the bbt page during scan
++ * @len:      length of the pattern, if 0 no pattern check is performed
++ * @maxblocks:        maximum number of blocks to search for a bbt. This number of
++ *            blocks is reserved at the end of the device where the tables are
++ *            written.
++ * @reserved_block_code: if non-0, this pattern denotes a reserved (rather than
++ *              bad) block in the stored bbt
++ * @pattern:  pattern to identify bad block table or factory marked good /
++ *            bad blocks, can be NULL, if len = 0
++ *
++ * Descriptor for the bad block table marker and the descriptor for the
++ * pattern which identifies good and bad blocks. The assumption is made
++ * that the pattern and the version count are always located in the oob area
++ * of the first block.
++ */
++struct nand_bbt_descr {
++      int     options;
++      int     pages[NAND_MAX_CHIPS];
++      int     offs;
++      int     veroffs;
++      uint8_t version[NAND_MAX_CHIPS];
++      int     len;
++      int     maxblocks;
++      int     reserved_block_code;
++      uint8_t *pattern;
++};
++
++/* Options for the bad block table descriptors */
++
++/* The number of bits used per block in the bbt on the device */
++#define NAND_BBT_NRBITS_MSK   0x0000000F
++#define NAND_BBT_1BIT         0x00000001
++#define NAND_BBT_2BIT         0x00000002
++#define NAND_BBT_4BIT         0x00000004
++#define NAND_BBT_8BIT         0x00000008
++/* The bad block table is in the last good block of the device */
++#define       NAND_BBT_LASTBLOCK      0x00000010
++/* The bbt is at the given page, else we must scan for the bbt */
++#define NAND_BBT_ABSPAGE      0x00000020
++/* The bbt is at the given page, else we must scan for the bbt */
++#define NAND_BBT_SEARCH               0x00000040
++/* bbt is stored per chip on multichip devices */
++#define NAND_BBT_PERCHIP      0x00000080
++/* bbt has a version counter at offset veroffs */
++#define NAND_BBT_VERSION      0x00000100
++/* Create a bbt if none axists */
++#define NAND_BBT_CREATE               0x00000200
++/* Search good / bad pattern through all pages of a block */
++#define NAND_BBT_SCANALLPAGES 0x00000400
++/* Scan block empty during good / bad block scan */
++#define NAND_BBT_SCANEMPTY    0x00000800
++/* Write bbt if neccecary */
++#define NAND_BBT_WRITE                0x00001000
++/* Read and write back block contents when writing bbt */
++#define NAND_BBT_SAVECONTENT  0x00002000
++/* Search good / bad pattern on the first and the second page */
++#define NAND_BBT_SCAN2NDPAGE  0x00004000
++
++/* The maximum number of blocks to scan for a bbt */
++#define NAND_BBT_SCAN_MAXBLOCKS       4
++
++extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd);
++extern int nand_update_bbt (struct mtd_info *mtd, loff_t offs);
++extern int nand_default_bbt (struct mtd_info *mtd);
++extern int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt);
++extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt);
++extern int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
++                             size_t * retlen, u_char * buf, u_char * oob_buf,
++                             struct nand_oobinfo *oobsel, int flags);
++
++/*
++* Constants for oob configuration
++*/
++#define NAND_SMALL_BADBLOCK_POS               5
++#define NAND_LARGE_BADBLOCK_POS               0
++
++#endif /* __LINUX_MTD_NAND_H */
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_base.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_base.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_base.c      1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_base.c      2006-11-10 09:55:58.000000000 +0100
+@@ -0,0 +1,2910 @@
++/*
++ * Snitched from  drivers/mtd/nand_base.c
++ * Modified to run outside Linux.
++ * #if 0'd to remove non-essential functionality
++ *
++ *  Overview:
++ *   This is the generic MTD driver for NAND flash devices. It should be
++ *   capable of working with almost all NAND chips currently available.
++ *   Basic support for AG-AND chips is provided.
++ *
++ *    Additional technical information is available on
++ *    http://www.linux-mtd.infradead.org/tech/nand.html
++ *
++ *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
++ *              2002 Thomas Gleixner (tglx@linutronix.de)
++ *
++ *  02-08-2004  tglx: support for strange chips, which cannot auto increment
++ *            pages on read / read_oob
++ *
++ *  03-17-2004  tglx: Check ready before auto increment check. Simon Bayes
++ *            pointed this out, as he marked an auto increment capable chip
++ *            as NOAUTOINCR in the board driver.
++ *            Make reads over block boundaries work too
++ *
++ *  04-14-2004        tglx: first working version for 2k page size chips
++ *
++ *  05-19-2004  tglx: Basic support for Renesas AG-AND chips
++ *
++ *  09-24-2004  tglx: add support for hardware controllers (e.g. ECC) shared
++ *            among multiple independend devices. Suggestions and initial patch
++ *            from Ben Dooks <ben-mtd@fluff.org>
++ *
++ *  12-05-2004        dmarlin: add workaround for Renesas AG-AND chips "disturb" issue.
++ *            Basically, any block not rewritten may lose data when surrounding blocks
++ *            are rewritten many times.  JFFS2 ensures this doesn't happen for blocks
++ *            it uses, but the Bad Block Table(s) may not be rewritten.  To ensure they
++ *            do not lose data, force them to be rewritten when some of the surrounding
++ *            blocks are erased.  Rather than tracking a specific nearby block (which
++ *            could itself go bad), use a page address 'mask' to select several blocks
++ *            in the same area, and rewrite the BBT when any of them are erased.
++ *
++ *  01-03-2005        dmarlin: added support for the device recovery command sequence for Renesas
++ *            AG-AND chips.  If there was a sudden loss of power during an erase operation,
++ *            a "device recovery" operation must be performed when power is restored
++ *            to ensure correct operation.
++ *
++ *  01-20-2005        dmarlin: added support for optional hardware specific callback routine to
++ *            perform extra error status checks on erase and write failures.  This required
++ *            adding a wrapper function for nand_read_ecc.
++ *
++ * 08-20-2005 vwool: suspend/resume added
++ *
++ * Credits:
++ *    David Woodhouse for adding multichip support
++ *
++ *    Aleph One Ltd. and Toby Churchill Ltd. for supporting the
++ *    rework for 2K page size chips
++ *
++ * TODO:
++ *    Enable cached programming for 2k page size chips
++ *    Check, if mtd->ecctype should be set to MTD_ECC_HW
++ *    if we have HW ecc support.
++ *    The AG-AND chips have nice features for speed improvement,
++ *    which are not supported yet. Read / program 4 pages in one go.
++ *
++ * $Id: nand_base.c,v 1.11 2006/11/10 08:55:58 ricardw 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/types.h>
++#include <linux/delay.h>
++#include <linux/errno.h>
++#if 0
++#include <linux/sched.h>
++#include <linux/slab.h>
++#endif
++
++#include "mtd.h"
++#include "nand.h"
++#include "nand_ecc.h"
++
++#if 0
++#include <linux/mtd/compatmac.h>
++#include <linux/interrupt.h>
++#include <linux/bitops.h>
++#include <asm/io.h>
++#endif
++
++#ifdef CONFIG_MTD_PARTITIONS
++#include <linux/mtd/partitions.h>
++
++#endif
++
++#include "lib.h"
++
++#define GPIO_SYNC 0
++
++#undef DEBUG /* from mtd.h */
++#define DEBUG(n, args...) do { } while(0)
++
++#define D(x) 
++
++/* 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 */
++#if NAND_WRITE_SUPPORT
++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,
++};
++#endif
++
++/*
++ * 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);
++#if NAND_KVEC_SUPPORT
++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);
++#endif
++static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
++static void nand_sync (struct mtd_info *mtd);
++
++/* Some internal functions */
++static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,
++              struct nand_oobinfo *oobsel, int mode);
++#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
++static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages,
++      u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode);
++#else
++#define nand_verify_pages(...) (0)
++#endif
++
++static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state);
++
++/**
++ * nand_release_device - [GENERIC] release chip
++ * @mtd:      MTD device structure
++ *
++ * Deselect, release chip lock and wake up anyone waiting on the device
++ */
++static void nand_release_device (struct mtd_info *mtd)
++{
++      struct nand_chip *this = mtd->priv;
++
++      /* De-select the NAND device */
++      this->select_chip(mtd, -1);
++#if 0
++
++      if (this->controller) {
++              /* Release the controller and the chip */
++              spin_lock(&this->controller->lock);
++              this->controller->active = NULL;
++              this->state = FL_READY;
++              wake_up(&this->controller->wq);
++              spin_unlock(&this->controller->lock);
++      } else {
++              /* Release the chip */
++              spin_lock(&this->chip_lock);
++              this->state = FL_READY;
++              wake_up(&this->wq);
++              spin_unlock(&this->chip_lock);
++      }
++#endif
++}
++
++/**
++ * 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);
++
++#if GPIO_SYNC
++      /* Bus sync: Read from address we just wrote.
++       * This generates no signal to the NAND flash, since only chip 
++       * select lines are pulled out to the chip, and read is not 
++       * gated with chip select for the write  area.
++       * Turns out this is (probably) the wrong way to do it; the
++       * right way is to set up suitable wait states in the kernel
++       * config (CONFIG_ETRAX_MEM_GRP3_CONFIG).
++       */
++      (void) readb(this->IO_ADDR_W); /* that's right, IO_ADDR_W */
++#endif
++}
++
++/**
++ * nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip
++ * @mtd:      MTD device structure
++ *
++ * Default read function for 16bit buswith with
++ * endianess conversion
++ */
++static u_char nand_read_byte16(struct mtd_info *mtd)
++{
++      struct nand_chip *this = mtd->priv;
++      return (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
++}
++
++/**
++ * nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip
++ * @mtd:      MTD device structure
++ * @byte:     pointer to data byte to write
++ *
++ * Default write function for 16bit buswith with
++ * endianess conversion
++ */
++static void nand_write_byte16(struct mtd_info *mtd, u_char byte)
++{
++      struct nand_chip *this = mtd->priv;
++      writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
++}
++
++/**
++ * nand_read_word - [DEFAULT] read one word from the chip
++ * @mtd:      MTD device structure
++ *
++ * Default read function for 16bit buswith without
++ * endianess conversion
++ */
++static u16 nand_read_word(struct mtd_info *mtd)
++{
++      struct nand_chip *this = mtd->priv;
++      return readw(this->IO_ADDR_R);
++}
++
++/**
++ * nand_write_word - [DEFAULT] write one word to the chip
++ * @mtd:      MTD device structure
++ * @word:     data word to write
++ *
++ * Default write function for 16bit buswith without
++ * endianess conversion
++ */
++static void nand_write_word(struct mtd_info *mtd, u16 word)
++{
++      struct nand_chip *this = mtd->priv;
++      writew(word, this->IO_ADDR_W);
++}
++
++/**
++ * nand_select_chip - [DEFAULT] control CE line
++ * @mtd:      MTD device structure
++ * @chip:     chipnumber to select, -1 for deselect
++ *
++ * Default select function for 1 chip devices.
++ */
++static void nand_select_chip(struct mtd_info *mtd, int chip)
++{
++      struct nand_chip *this = mtd->priv;
++      switch(chip) {
++      case -1:
++              this->hwcontrol(mtd, NAND_CTL_CLRNCE);
++              break;
++      case 0:
++              this->hwcontrol(mtd, NAND_CTL_SETNCE);
++              break;
++
++      default:
++              BUG();
++      }
++}
++
++/**
++ * nand_write_buf - [DEFAULT] write buffer to chip
++ * @mtd:      MTD device structure
++ * @buf:      data buffer
++ * @len:      number of bytes to write
++ *
++ * Default write function for 8bit buswith
++ */
++static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++      int i;
++      struct nand_chip *this = mtd->priv;
++
++      for (i=0; i<len; i++)
++              writeb(buf[i], this->IO_ADDR_W);
++}
++
++/**
++ * nand_read_buf - [DEFAULT] read chip data into buffer
++ * @mtd:      MTD device structure
++ * @buf:      buffer to store date
++ * @len:      number of bytes to read
++ *
++ * Default read function for 8bit buswith
++ */
++static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
++{
++      int i;
++      struct nand_chip *this = mtd->priv;
++
++      for (i=0; i<len; i++)
++              buf[i] = readb(this->IO_ADDR_R);
++}
++
++/**
++ * nand_verify_buf - [DEFAULT] Verify chip data against buffer
++ * @mtd:      MTD device structure
++ * @buf:      buffer containing the data to compare
++ * @len:      number of bytes to compare
++ *
++ * Default verify function for 8bit buswith
++ */
++static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
++{
++      int i;
++      struct nand_chip *this = mtd->priv;
++
++      for (i=0; i<len; i++)
++              if (buf[i] != readb(this->IO_ADDR_R))
++                      return -EFAULT;
++
++      return 0;
++}
++
++/**
++ * nand_write_buf16 - [DEFAULT] write buffer to chip
++ * @mtd:      MTD device structure
++ * @buf:      data buffer
++ * @len:      number of bytes to write
++ *
++ * Default write function for 16bit buswith
++ */
++static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
++{
++      int i;
++      struct nand_chip *this = mtd->priv;
++      u16 *p = (u16 *) buf;
++      len >>= 1;
++
++      for (i=0; i<len; i++)
++              writew(p[i], this->IO_ADDR_W);
++
++}
++
++/**
++ * nand_read_buf16 - [DEFAULT] read chip data into buffer
++ * @mtd:      MTD device structure
++ * @buf:      buffer to store date
++ * @len:      number of bytes to read
++ *
++ * Default read function for 16bit buswith
++ */
++static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
++{
++      int i;
++      struct nand_chip *this = mtd->priv;
++      u16 *p = (u16 *) buf;
++      len >>= 1;
++
++      for (i=0; i<len; i++)
++              p[i] = readw(this->IO_ADDR_R);
++}
++
++/**
++ * nand_verify_buf16 - [DEFAULT] Verify chip data against buffer
++ * @mtd:      MTD device structure
++ * @buf:      buffer containing the data to compare
++ * @len:      number of bytes to compare
++ *
++ * Default verify function for 16bit buswith
++ */
++static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
++{
++      int i;
++      struct nand_chip *this = mtd->priv;
++      u16 *p = (u16 *) buf;
++      len >>= 1;
++
++      for (i=0; i<len; i++)
++              if (p[i] != readw(this->IO_ADDR_R))
++                      return -EFAULT;
++
++      return 0;
++}
++
++/**
++ * nand_block_bad - [DEFAULT] Read bad block marker from the chip
++ * @mtd:      MTD device structure
++ * @ofs:      offset from device start
++ * @getchip:  0, if the chip is already selected
++ *
++ * Check, if the block is bad.
++ */
++static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
++{
++      int page, chipnr, res = 0;
++      struct nand_chip *this = mtd->priv;
++      u16 bad;
++
++      if (getchip) {
++              page = (int)(ofs >> this->page_shift);
++              chipnr = (int)(ofs >> this->chip_shift);
++
++              /* Grab the lock and see if the device is available */
++              nand_get_device (this, mtd, FL_READING);
++
++              /* Select the NAND device */
++              this->select_chip(mtd, chipnr);
++      } else
++              page = (int) ofs;
++
++      if (this->options & NAND_BUSWIDTH_16) {
++              this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask);
++              bad = cpu_to_le16(this->read_word(mtd));
++              if (this->badblockpos & 0x1)
++                      bad >>= 8;
++              if ((bad & 0xFF) != 0xff)
++                      res = 1;
++      } else {
++              this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask);
++              if (this->read_byte(mtd) != 0xff)
++                      res = 1;
++      }
++
++      if (getchip) {
++              /* Deselect and wake up anyone waiting on the device */
++              nand_release_device(mtd);
++      }
++
++      return res;
++}
++
++/**
++ * nand_default_block_markbad - [DEFAULT] mark a block bad
++ * @mtd:      MTD device structure
++ * @ofs:      offset from device start
++ *
++ * This is the default implementation, which can be overridden by
++ * a hardware specific driver.
++*/
++#if NAND_WRITE_SUPPORT
++static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
++{
++      struct nand_chip *this = mtd->priv;
++      u_char buf[2] = {0, 0};
++      size_t  retlen;
++      int block;
++
++      /* Get block number */
++      block = ((int) ofs) >> this->bbt_erase_shift;
++      if (this->bbt)
++              this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
++
++#if NAND_BBT_SUPPORT
++      /* Do we have a flash based bad block table ? */
++      if (this->options & NAND_USE_FLASH_BBT)
++              return nand_update_bbt (mtd, ofs);
++#endif
++
++      /* 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);
++}
++#endif
++
++/**
++ * 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
++ */
++#if NAND_WRITE_SUPPORT
++static int nand_check_wp (struct mtd_info *mtd)
++{
++      struct nand_chip *this = mtd->priv;
++      /* Check the WP bit */
++      this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
++      return (this->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1;
++}
++#endif
++
++/**
++ * 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);
++
++#if NAND_BBT_SUPPORT
++      /* Return info from the table */
++      return nand_isbad_bbt (mtd, ofs, allowbbt);
++#endif
++      BUG(); /* should not happen */
++}
++
++/*
++ * Wait for the ready pin, after a command
++ * The timeout is catched later.
++ */
++static void nand_wait_ready(struct mtd_info *mtd)
++{
++      struct nand_chip *this = mtd->priv;
++#if 0
++      unsigned long   timeo = jiffies + 2;
++#endif
++
++      /* wait until command is processed or timeout occures */
++      do {
++              if (this->dev_ready(mtd))
++                      return;
++#if 0
++              touch_softlockup_watchdog();
++      } while (time_before(jiffies, timeo));
++#endif
++      } while (1);
++}
++
++/**
++ * nand_command - [DEFAULT] Send command to NAND device
++ * @mtd:      MTD device structure
++ * @command:  the command to be sent
++ * @column:   the column address for this command, -1 if none
++ * @page_addr:        the page address for this command, -1 if none
++ *
++ * Send command to NAND device. This function is used for small page
++ * devices (256/512 Bytes per page)
++ */
++static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
++{
++      register struct nand_chip *this = mtd->priv;
++
++      /* Begin command latch cycle */
++      this->hwcontrol(mtd, NAND_CTL_SETCLE);
++      /*
++       * Write out the command to the device.
++       */
++      if (command == NAND_CMD_SEQIN) {
++              int readcmd;
++
++              if (column >= mtd->oobblock) {
++                      /* OOB area */
++                      column -= mtd->oobblock;
++                      readcmd = NAND_CMD_READOOB;
++              } else if (column < 256) {
++                      /* First 256 bytes --> READ0 */
++                      readcmd = NAND_CMD_READ0;
++              } else {
++                      column -= 256;
++                      readcmd = NAND_CMD_READ1;
++              }
++              this->write_byte(mtd, readcmd);
++      }
++      this->write_byte(mtd, command);
++
++      /* Set ALE and clear CLE to start address cycle */
++      this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++
++      if (column != -1 || page_addr != -1) {
++              this->hwcontrol(mtd, NAND_CTL_SETALE);
++
++              /* Serially input address */
++              if (column != -1) {
++                      /* Adjust columns for 16 bit buswidth */
++                      if (this->options & NAND_BUSWIDTH_16)
++                              column >>= 1;
++                      this->write_byte(mtd, column);
++              }
++              if (page_addr != -1) {
++                      this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
++                      this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
++                      /* One more address cycle for devices > 32MiB */
++                      if (this->chipsize > (32 << 20))
++                              this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
++              }
++              /* Latch in address */
++              this->hwcontrol(mtd, NAND_CTL_CLRALE);
++      }
++
++      /*
++       * program and erase have their own busy handlers
++       * status and sequential in needs no delay
++      */
++      switch (command) {
++
++      case NAND_CMD_PAGEPROG:
++      case NAND_CMD_ERASE1:
++      case NAND_CMD_ERASE2:
++      case NAND_CMD_SEQIN:
++      case NAND_CMD_STATUS:
++              return;
++
++      case NAND_CMD_RESET:
++              if (this->dev_ready)
++                      break;
++              udelay(this->chip_delay);
++              this->hwcontrol(mtd, NAND_CTL_SETCLE);
++              this->write_byte(mtd, NAND_CMD_STATUS);
++              this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++              while ( !(this->read_byte(mtd) & NAND_STATUS_READY));
++              return;
++
++      /* This applies to read commands */
++      default:
++              /*
++               * If we don't have access to the busy pin, we apply the given
++               * command delay
++              */
++              if (!this->dev_ready) {
++                      udelay (this->chip_delay);
++                      return;
++              }
++      }
++      /* Apply this short delay always to ensure that we do wait tWB in
++       * any case on any machine. */
++      ndelay (100);
++
++      nand_wait_ready(mtd);
++}
++
++/**
++ * nand_command_lp - [DEFAULT] Send command to NAND large page device
++ * @mtd:      MTD device structure
++ * @command:  the command to be sent
++ * @column:   the column address for this command, -1 if none
++ * @page_addr:        the page address for this command, -1 if none
++ *
++ * Send command to NAND device. This is the version for the new large page devices
++ * We dont have the seperate regions as we have in the small page devices.
++ * We must emulate NAND_CMD_READOOB to keep the code compatible.
++ *
++ */
++static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr)
++{
++      register struct nand_chip *this = mtd->priv;
++
++      /* Emulate NAND_CMD_READOOB */
++      if (command == NAND_CMD_READOOB) {
++              column += mtd->oobblock;
++              command = NAND_CMD_READ0;
++      }
++
++
++      /* Begin command latch cycle */
++      this->hwcontrol(mtd, NAND_CTL_SETCLE);
++      /* Write out the command to the device. */
++      this->write_byte(mtd, (command & 0xff));
++      /* End command latch cycle */
++      this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++
++      if (column != -1 || page_addr != -1) {
++              this->hwcontrol(mtd, NAND_CTL_SETALE);
++
++              /* Serially input address */
++              if (column != -1) {
++                      /* Adjust columns for 16 bit buswidth */
++                      if (this->options & NAND_BUSWIDTH_16)
++                              column >>= 1;
++                      this->write_byte(mtd, column & 0xff);
++                      this->write_byte(mtd, column >> 8);
++              }
++              if (page_addr != -1) {
++                      this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
++                      this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
++                      /* One more address cycle for devices > 128MiB */
++                      if (this->chipsize > (128 << 20))
++                              this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff));
++              }
++              /* Latch in address */
++              this->hwcontrol(mtd, NAND_CTL_CLRALE);
++      }
++
++      /*
++       * program and erase have their own busy handlers
++       * status, sequential in, and deplete1 need no delay
++       */
++      switch (command) {
++
++      case NAND_CMD_CACHEDPROG:
++      case NAND_CMD_PAGEPROG:
++      case NAND_CMD_ERASE1:
++      case NAND_CMD_ERASE2:
++      case NAND_CMD_SEQIN:
++      case NAND_CMD_STATUS:
++      case NAND_CMD_DEPLETE1:
++              return;
++
++      /*
++       * read error status commands require only a short delay
++       */
++      case NAND_CMD_STATUS_ERROR:
++      case NAND_CMD_STATUS_ERROR0:
++      case NAND_CMD_STATUS_ERROR1:
++      case NAND_CMD_STATUS_ERROR2:
++      case NAND_CMD_STATUS_ERROR3:
++              udelay(this->chip_delay);
++              return;
++
++      case NAND_CMD_RESET:
++              if (this->dev_ready)
++                      break;
++              udelay(this->chip_delay);
++              this->hwcontrol(mtd, NAND_CTL_SETCLE);
++              this->write_byte(mtd, NAND_CMD_STATUS);
++              this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++              while ( !(this->read_byte(mtd) & NAND_STATUS_READY));
++              return;
++
++      case NAND_CMD_READ0:
++              /* Begin command latch cycle */
++              this->hwcontrol(mtd, NAND_CTL_SETCLE);
++              /* Write out the start read command */
++              this->write_byte(mtd, NAND_CMD_READSTART);
++              /* End command latch cycle */
++              this->hwcontrol(mtd, NAND_CTL_CLRCLE);
++              /* Fall through into ready check */
++
++      /* This applies to read commands */
++      default:
++              /*
++               * If we don't have access to the busy pin, we apply the given
++               * command delay
++              */
++              if (!this->dev_ready) {
++                      udelay (this->chip_delay);
++                      return;
++              }
++      }
++
++      /* Apply this short delay always to ensure that we do wait tWB in
++       * any case on any machine. */
++      ndelay (100);
++
++      nand_wait_ready(mtd);
++}
++
++/**
++ * nand_get_device - [GENERIC] Get chip for selected access
++ * @this:     the nand chip descriptor
++ * @mtd:      MTD device structure
++ * @new_state:        the state which is requested
++ *
++ * Get the device and lock it for exclusive access
++ */
++static int nand_get_device (struct nand_chip *this, struct mtd_info *mtd, int new_state)
++{
++      this->state = new_state;
++      return 0;
++#if 0
++      struct nand_chip *active;
++      spinlock_t *lock;
++      wait_queue_head_t *wq;
++      DECLARE_WAITQUEUE (wait, current);
++
++      lock = (this->controller) ? &this->controller->lock : &this->chip_lock;
++      wq = (this->controller) ? &this->controller->wq : &this->wq;
++retry:
++      active = this;
++      spin_lock(lock);
++
++      /* Hardware controller shared among independend devices */
++      if (this->controller) {
++              if (this->controller->active)
++                      active = this->controller->active;
++              else
++                      this->controller->active = this;
++      }
++      if (active == this && this->state == FL_READY) {
++              this->state = new_state;
++              spin_unlock(lock);
++              return 0;
++      }
++      if (new_state == FL_PM_SUSPENDED) {
++              spin_unlock(lock);
++              return (this->state == FL_PM_SUSPENDED) ? 0 : -EAGAIN;
++      }
++      set_current_state(TASK_UNINTERRUPTIBLE);
++      add_wait_queue(wq, &wait);
++      spin_unlock(lock);
++      schedule();
++      remove_wait_queue(wq, &wait);
++      goto retry;
++#endif
++}
++
++/**
++ * 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)
++{
++
++#if 0
++      unsigned long   timeo = jiffies;
++#endif
++      int     status;
++
++#if 0
++      if (state == FL_ERASING)
++               timeo += (HZ * 400) / 1000;
++      else
++               timeo += (HZ * 20) / 1000;
++#endif
++
++      /* Apply this short delay always to ensure that we do wait tWB in
++       * any case on any machine. */
++      ndelay (100);
++
++      if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
++              this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1);
++      else
++              this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
++
++#if 0
++      while (time_before(jiffies, timeo)) {
++              /* Check, if we were interrupted */
++              if (this->state != state)
++                      return 0;
++#endif
++      while (1) { /* wait indefinitely */
++
++              if (this->dev_ready) {
++                      if (this->dev_ready(mtd))
++                              break;
++              } else {
++                      if (this->read_byte(mtd) & NAND_STATUS_READY)
++                              break;
++              }
++
++#if 0
++              cond_resched();
++#endif
++      }
++      status = (int) this->read_byte(mtd);
++      return status;
++}
++
++/**
++ * nand_write_page - [GENERIC] write one page
++ * @mtd:      MTD device structure
++ * @this:     NAND chip structure
++ * @page:     startpage inside the chip, must be called with (page & this->pagemask)
++ * @oob_buf:  out of band data buffer
++ * @oobsel:   out of band selecttion structre
++ * @cached:   1 = enable cached programming if supported by chip
++ *
++ * Nand_page_program function is used for write and writev !
++ * This function will always program a full page of data
++ * If you call it with a non page aligned buffer, you're lost :)
++ *
++ * Cached programming is not supported yet.
++ */
++#if NAND_WRITE_SUPPORT
++static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page,
++      u_char *oob_buf,  struct nand_oobinfo *oobsel, int cached)
++{
++      int     i, status;
++      u_char  ecc_code[32];
++      int     eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
++      int     *oob_config = oobsel->eccpos;
++      int     datidx = 0, eccidx = 0, eccsteps = this->eccsteps;
++      int     eccbytes = 0;
++
++      /* FIXME: Enable cached programming */
++      cached = 0;
++
++      /* Send command to begin auto page programming */
++      this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);
++
++      /* Write out complete page of data, take care of eccmode */
++      switch (eccmode) {
++      /* No ecc, write all */
++      case NAND_ECC_NONE:
++              puts ("Writing data without ECC to NAND-FLASH is not recommended\r\n");
++              this->write_buf(mtd, this->data_poi, mtd->oobblock);
++              break;
++
++      /* Software ecc 3/256, write all */
++      case NAND_ECC_SOFT:
++              for (; eccsteps; eccsteps--) {
++                      this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
++                      for (i = 0; i < 3; i++, eccidx++)
++                              oob_buf[oob_config[eccidx]] = ecc_code[i];
++                      datidx += this->eccsize;
++              }
++              this->write_buf(mtd, this->data_poi, mtd->oobblock);
++              break;
++      default:
++              eccbytes = this->eccbytes;
++              for (; eccsteps; eccsteps--) {
++                      /* enable hardware ecc logic for write */
++                      this->enable_hwecc(mtd, NAND_ECC_WRITE);
++                      this->write_buf(mtd, &this->data_poi[datidx], this->eccsize);
++                      this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
++                      for (i = 0; i < eccbytes; i++, eccidx++)
++                              oob_buf[oob_config[eccidx]] = ecc_code[i];
++                      /* If the hardware ecc provides syndromes then
++                       * the ecc code must be written immidiately after
++                       * the data bytes (words) */
++                      if (this->options & NAND_HWECC_SYNDROME)
++                              this->write_buf(mtd, ecc_code, eccbytes);
++                      datidx += this->eccsize;
++              }
++              break;
++      }
++
++      /* Write out OOB data */
++      if (this->options & NAND_HWECC_SYNDROME)
++              this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes);
++      else
++              this->write_buf(mtd, oob_buf, mtd->oobsize);
++
++      /* Send command to actually program the data */
++      this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1);
++
++      if (!cached) {
++              /* call wait ready function */
++              status = this->waitfunc (mtd, this, FL_WRITING);
++
++              /* See if operation failed and additional status checks are available */
++              if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
++                      status = this->errstat(mtd, this, FL_WRITING, status, page);
++              }
++
++              /* See if device thinks it succeeded */
++              if (status & NAND_STATUS_FAIL) {
++                      DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
++                      return -EIO;
++              }
++      } else {
++              /* FIXME: Implement cached programming ! */
++              /* wait until cache is ready*/
++              // status = this->waitfunc (mtd, this, FL_CACHEDRPG);
++      }
++      return 0;
++}
++#endif
++
++#if NAND_WRITE_SUPPORT
++#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
++/**
++ * nand_verify_pages - [GENERIC] verify the chip contents after a write
++ * @mtd:      MTD device structure
++ * @this:     NAND chip structure
++ * @page:     startpage inside the chip, must be called with (page & this->pagemask)
++ * @numpages: number of pages to verify
++ * @oob_buf:  out of band data buffer
++ * @oobsel:   out of band selecttion structre
++ * @chipnr:   number of the current chip
++ * @oobmode:  1 = full buffer verify, 0 = ecc only
++ *
++ * The NAND device assumes that it is always writing to a cleanly erased page.
++ * Hence, it performs its internal write verification only on bits that
++ * transitioned from 1 to 0. The device does NOT verify the whole page on a
++ * byte by byte basis. It is possible that the page was not completely erased
++ * or the page is becoming unusable due to wear. The read with ECC would catch
++ * the error later when the ECC page check fails, but we would rather catch
++ * it early in the page write stage. Better to write no data than invalid data.
++ */
++static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages,
++      u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode)
++{
++      int     i, j, datidx = 0, oobofs = 0, res = -EIO;
++      int     eccsteps = this->eccsteps;
++      int     hweccbytes;
++      u_char  oobdata[64];
++
++      hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0;
++
++      /* Send command to read back the first page */
++      this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
++
++      for(;;) {
++              for (j = 0; j < eccsteps; j++) {
++                      /* Loop through and verify the data */
++                      if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) {
++                              DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
++                              goto out;
++                      }
++                      datidx += mtd->eccsize;
++                      /* Have we a hw generator layout ? */
++                      if (!hweccbytes)
++                              continue;
++                      if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) {
++                              DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
++                              goto out;
++                      }
++                      oobofs += hweccbytes;
++              }
++
++              /* check, if we must compare all data or if we just have to
++               * compare the ecc bytes
++               */
++              if (oobmode) {
++                      if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) {
++                              DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
++                              goto out;
++                      }
++              } else {
++                      /* Read always, else autoincrement fails */
++                      this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps);
++
++                      if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) {
++                              int ecccnt = oobsel->eccbytes;
++
++                              for (i = 0; i < ecccnt; i++) {
++                                      int idx = oobsel->eccpos[i];
++                                      if (oobdata[idx] != oob_buf[oobofs + idx] ) {
++                                              DEBUG (MTD_DEBUG_LEVEL0,
++                                              "%s: Failed ECC write "
++                                              "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i);
++                                              goto out;
++                                      }
++                              }
++                      }
++              }
++              oobofs += mtd->oobsize - hweccbytes * eccsteps;
++              page++;
++              numpages--;
++
++              /* Apply delay or wait for ready/busy pin
++               * Do this before the AUTOINCR check, so no problems
++               * arise if a chip which does auto increment
++               * is marked as NOAUTOINCR by the board driver.
++               * Do this also before returning, so the chip is
++               * ready for the next command.
++              */
++              if (!this->dev_ready)
++                      udelay (this->chip_delay);
++              else
++                      nand_wait_ready(mtd);
++
++              /* All done, return happy */
++              if (!numpages)
++                      return 0;
++
++
++              /* Check, if the chip supports auto page increment */
++              if (!NAND_CANAUTOINCR(this))
++                      this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
++      }
++      /*
++       * Terminate the read command. We come here in case of an error
++       * So we must issue a reset command.
++       */
++out:
++      this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1);
++      return res;
++}
++#endif
++#endif
++
++/**
++ * nand_read - [MTD Interface] MTD compability function for nand_do_read_ecc
++ * @mtd:      MTD device structure
++ * @from:     offset to read from
++ * @len:      number of bytes to read
++ * @retlen:   pointer to variable to store the number of read bytes
++ * @buf:      the databuffer to put data
++ *
++ * This function simply calls nand_do_read_ecc with oob buffer and oobsel = NULL
++ * and flags = 0xff
++ */
++static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
++{
++      return nand_do_read_ecc (mtd, from, len, retlen, buf, NULL, &mtd->oobinfo, 0xff);
++}
++
++
++/**
++ * nand_read_ecc - [MTD Interface] MTD compability function for nand_do_read_ecc
++ * @mtd:      MTD device structure
++ * @from:     offset to read from
++ * @len:      number of bytes to read
++ * @retlen:   pointer to variable to store the number of read bytes
++ * @buf:      the databuffer to put data
++ * @oob_buf:  filesystem supplied oob data buffer
++ * @oobsel:   oob selection structure
++ *
++ * This function simply calls nand_do_read_ecc with flags = 0xff
++ */
++static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
++                        size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
++{
++      /* use userspace supplied oobinfo, if zero */
++      if (oobsel == NULL)
++              oobsel = &mtd->oobinfo;
++      return nand_do_read_ecc(mtd, from, len, retlen, buf, oob_buf, oobsel, 0xff);
++}
++
++
++/**
++ * nand_do_read_ecc - [MTD Interface] Read data with ECC
++ * @mtd:      MTD device structure
++ * @from:     offset to read from
++ * @len:      number of bytes to read
++ * @retlen:   pointer to variable to store the number of read bytes
++ * @buf:      the databuffer to put data
++ * @oob_buf:  filesystem supplied oob data buffer (can be NULL)
++ * @oobsel:   oob selection structure
++ * @flags:    flag to indicate if nand_get_device/nand_release_device should be preformed
++ *            and how many corrected error bits are acceptable:
++ *              bits 0..7 - number of tolerable errors
++ *              bit  8    - 0 == do not get/release chip, 1 == get/release chip
++ *
++ * NAND read with ECC
++ */
++int nand_do_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
++                           size_t * retlen, u_char * buf, u_char * oob_buf,
++                           struct nand_oobinfo *oobsel, int flags)
++{
++
++      int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
++      int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
++      struct nand_chip *this = mtd->priv;
++      u_char *data_poi, *oob_data = oob_buf;
++      u_char ecc_calc[32];
++      u_char ecc_code[32];
++        int eccmode, eccsteps;
++      int     *oob_config, datidx;
++      int     blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
++      int     eccbytes;
++      int     compareecc = 1;
++      int     oobreadlen;
++
++
++      DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
++      D(
++        puts ("nand_read_ecc: from = ");
++        putx (from);
++        puts (", len = ");
++        putx(len);
++        putnl();
++       )
++
++      /* 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");
++              D(puts("nand_read_ecc: Attempt read beyond end of device\r\n"));
++              *retlen = 0;
++              return -EINVAL;
++      }
++
++      /* Grab the lock and see if the device is available */
++      if (flags & NAND_GET_DEVICE)
++              nand_get_device (this, mtd, FL_READING);
++
++      /* Autoplace of oob data ? Use the default placement scheme */
++      if (oobsel->useecc == MTD_NANDECC_AUTOPLACE)
++              oobsel = this->autooob;
++
++      eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
++      oob_config = oobsel->eccpos;
++
++      /* Select the NAND device */
++      chipnr = (int)(from >> this->chip_shift);
++      this->select_chip(mtd, chipnr);
++
++      /* First we calculate the starting page */
++      realpage = (int) (from >> this->page_shift);
++      page = realpage & this->pagemask;
++
++      /* Get raw starting column */
++      col = from & (mtd->oobblock - 1);
++
++      end = mtd->oobblock;
++      ecc = this->eccsize;
++      eccbytes = this->eccbytes;
++
++      if ((eccmode == NAND_ECC_NONE) || (this->options & NAND_HWECC_SYNDROME))
++              compareecc = 0;
++
++      oobreadlen = mtd->oobsize;
++      if (this->options & NAND_HWECC_SYNDROME)
++              oobreadlen -= oobsel->eccbytes;
++
++      /* Loop until all data read */
++      while (read < len) {
++
++              int aligned = (!col && (len - read) >= end);
++              /*
++               * If the read is not page aligned, we have to read into data buffer
++               * due to ecc, else we read into return buffer direct
++               */
++              if (aligned)
++                      data_poi = &buf[read];
++              else
++                      data_poi = this->data_buf;
++
++              /* Check, if we have this page in the buffer
++               *
++               * FIXME: Make it work when we must provide oob data too,
++               * check the usage of data_buf oob field
++               */
++              if (realpage == this->pagebuf && !oob_buf) {
++                      /* aligned read ? */
++                      if (aligned)
++                              memcpy (data_poi, this->data_buf, end);
++                      goto readdata;
++              }
++
++              /* Check, if we must send the read command */
++              if (sndcmd) {
++                      this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
++                      sndcmd = 0;
++              }
++
++              /* get oob area, if we have no oob buffer from fs-driver */
++              if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE ||
++                      oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
++                      oob_data = &this->data_buf[end];
++
++              eccsteps = this->eccsteps;
++
++              switch (eccmode) {
++              case NAND_ECC_NONE: {   /* No ECC, Read in a page */
++#if 0
++                      static unsigned long lastwhinge = 0;
++                      if ((lastwhinge / HZ) != (jiffies / HZ)) {
++                              puts ("Reading data from NAND FLASH without ECC is not recommended\r\n");
++                              lastwhinge = jiffies;
++                      }
++#endif
++                      this->read_buf(mtd, data_poi, end);
++                      break;
++              }
++
++              case NAND_ECC_SOFT:     /* Software ECC 3/256: Read in a page + oob data */
++                      this->read_buf(mtd, data_poi, end);
++                      for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc)
++                              this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
++                      break;
++
++              default:
++#if NAND_HWECC_SUPPORT
++                      for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) {
++                              this->enable_hwecc(mtd, NAND_ECC_READ);
++                              this->read_buf(mtd, &data_poi[datidx], ecc);
++
++                              /* HW ecc with syndrome calculation must read the
++                               * syndrome from flash immidiately after the data */
++                              if (!compareecc) {
++                                      /* Some hw ecc generators need to know when the
++                                       * syndrome is read from flash */
++                                      this->enable_hwecc(mtd, NAND_ECC_READSYN);
++                                      this->read_buf(mtd, &oob_data[i], eccbytes);
++                                      /* We calc error correction directly, it checks the hw
++                                       * generator for an error, reads back the syndrome and
++                                       * does the error correction on the fly */
++                                      ecc_status = this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]);
++                                      if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
++                                              DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: "
++                                                      "Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
++                                              ecc_failed++;
++                                      }
++                              } else {
++                                      this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
++                              }
++                      }
++#endif
++                      break;
++              }
++
++              /* read oobdata */
++              this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen);
++
++              /* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */
++              if (!compareecc)
++                      goto readoob;
++
++              /* Pick the ECC bytes out of the oob data */
++              for (j = 0; j < oobsel->eccbytes; j++)
++                      ecc_code[j] = oob_data[oob_config[j]];
++
++              /* correct data, if neccecary */
++              for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) {
++                      ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);
++
++                      /* Get next chunk of ecc bytes */
++                      j += eccbytes;
++
++                      /* Check, if we have a fs supplied oob-buffer,
++                       * This is the legacy mode. Used by YAFFS1
++                       * Should go away some day
++                       */
++                      if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) {
++                              int *p = (int *)(&oob_data[mtd->oobsize]);
++                              p[i] = ecc_status;
++                      }
++
++                      if ((ecc_status == -1) || (ecc_status > (flags && 0xff))) {
++                              DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
++                              D(
++                                puts ("nand_read_ecc: " "Failed ECC read, page ");
++                                putx (page);
++                                putnl();
++                               )
++                              ecc_failed++;
++                      }
++              }
++
++      readoob:
++              /* check, if we have a fs supplied oob-buffer */
++              if (oob_buf) {
++                      /* without autoplace. Legacy mode used by YAFFS1 */
++                      switch(oobsel->useecc) {
++                      case MTD_NANDECC_AUTOPLACE:
++                      case MTD_NANDECC_AUTOPL_USR:
++                              /* Walk through the autoplace chunks */
++                              for (i = 0; oobsel->oobfree[i][1]; i++) {
++                                      int from = oobsel->oobfree[i][0];
++                                      int num = oobsel->oobfree[i][1];
++                                      memcpy(&oob_buf[oob], &oob_data[from], num);
++                                      oob += num;
++                              }
++                              break;
++                      case MTD_NANDECC_PLACE:
++                              /* YAFFS1 legacy mode */
++                              oob_data += this->eccsteps * sizeof (int);
++                      default:
++                              oob_data += mtd->oobsize;
++                      }
++              }
++      readdata:
++              /* Partial page read, transfer data into fs buffer */
++              if (!aligned) {
++                      for (j = col; j < end && read < len; j++)
++                              buf[read++] = data_poi[j];
++                      this->pagebuf = realpage;
++              } else
++                      read += mtd->oobblock;
++
++              /* Apply delay or wait for ready/busy pin
++               * Do this before the AUTOINCR check, so no problems
++               * arise if a chip which does auto increment
++               * is marked as NOAUTOINCR by the board driver.
++              */
++              if (!this->dev_ready)
++                      udelay (this->chip_delay);
++              else
++                      nand_wait_ready(mtd);
++
++              if (read == len)
++                      break;
++
++              /* For subsequent reads align to page boundary. */
++              col = 0;
++              /* Increment page address */
++              realpage++;
++
++              page = realpage & this->pagemask;
++              /* Check, if we cross a chip boundary */
++              if (!page) {
++                      chipnr++;
++                      this->select_chip(mtd, -1);
++                      this->select_chip(mtd, chipnr);
++              }
++              /* Check, if the chip supports auto page increment
++               * or if we have hit a block boundary.
++              */
++              if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
++                      sndcmd = 1;
++      }
++
++      /* Deselect and wake up anyone waiting on the device */
++      if (flags & NAND_GET_DEVICE)
++              nand_release_device(mtd);
++
++      /*
++       * Return success, if no ECC failures, else -EBADMSG
++       * fs driver will take care of that, because
++       * retlen == desired len and result == -EBADMSG
++       */
++      *retlen = read;
++      return ecc_failed ? -EBADMSG : 0;
++}
++
++/**
++ * nand_read_oob - [MTD Interface] NAND read out-of-band
++ * @mtd:      MTD device structure
++ * @from:     offset to read from
++ * @len:      number of bytes to read
++ * @retlen:   pointer to variable to store the number of read bytes
++ * @buf:      the databuffer to put data
++ *
++ * NAND read out-of-band data from the spare area
++ */
++static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
++{
++      int i, col, page, chipnr;
++      struct nand_chip *this = mtd->priv;
++      int     blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
++
++      DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
++
++      /* Shift to get page */
++      page = (int)(from >> this->page_shift);
++      chipnr = (int)(from >> this->chip_shift);
++
++      /* Mask to get column */
++      col = from & (mtd->oobsize - 1);
++
++      /* Initialize return length value */
++      *retlen = 0;
++
++      /* Do not allow reads past end of device */
++      if ((from + len) > mtd->size) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n");
++              *retlen = 0;
++              return -EINVAL;
++      }
++
++      /* Grab the lock and see if the device is available */
++      nand_get_device (this, mtd , FL_READING);
++
++      /* Select the NAND device */
++      this->select_chip(mtd, chipnr);
++
++      /* Send the read command */
++      this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask);
++      /*
++       * Read the data, if we read more than one page
++       * oob data, let the device transfer the data !
++       */
++      i = 0;
++      while (i < len) {
++              int thislen = mtd->oobsize - col;
++              thislen = min_t(int, thislen, len);
++              this->read_buf(mtd, &buf[i], thislen);
++              i += thislen;
++
++              /* Read more ? */
++              if (i < len) {
++                      page++;
++                      col = 0;
++
++                      /* Check, if we cross a chip boundary */
++                      if (!(page & this->pagemask)) {
++                              chipnr++;
++                              this->select_chip(mtd, -1);
++                              this->select_chip(mtd, chipnr);
++                      }
++
++                      /* Apply delay or wait for ready/busy pin
++                       * Do this before the AUTOINCR check, so no problems
++                       * arise if a chip which does auto increment
++                       * is marked as NOAUTOINCR by the board driver.
++                       */
++                      if (!this->dev_ready)
++                              udelay (this->chip_delay);
++                      else
++                              nand_wait_ready(mtd);
++
++                      /* Check, if the chip supports auto page increment
++                       * or if we have hit a block boundary.
++                      */
++                      if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) {
++                              /* For subsequent page reads set offset to 0 */
++                              this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask);
++                      }
++              }
++      }
++
++      /* Deselect and wake up anyone waiting on the device */
++      nand_release_device(mtd);
++
++      /* Return happy */
++      *retlen = len;
++      return 0;
++}
++
++/**
++ * nand_read_raw - [GENERIC] Read raw data including oob into buffer
++ * @mtd:      MTD device structure
++ * @buf:      temporary buffer
++ * @from:     offset to read from
++ * @len:      number of bytes to read
++ * @ooblen:   number of oob data bytes to read
++ *
++ * Read raw data including oob into buffer
++ */
++int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen)
++{
++      struct nand_chip *this = mtd->priv;
++      int page = (int) (from >> this->page_shift);
++      int chip = (int) (from >> this->chip_shift);
++      int sndcmd = 1;
++      int cnt = 0;
++      int pagesize = mtd->oobblock + mtd->oobsize;
++      int     blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
++
++      /* Do not allow reads past end of device */
++      if ((from + len) > mtd->size) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt read beyond end of device\n");
++              return -EINVAL;
++      }
++
++      /* Grab the lock and see if the device is available */
++      nand_get_device (this, mtd , FL_READING);
++
++      this->select_chip (mtd, chip);
++
++      /* Add requested oob length */
++      len += ooblen;
++
++      while (len) {
++              if (sndcmd)
++                      this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask);
++              sndcmd = 0;
++
++              this->read_buf (mtd, &buf[cnt], pagesize);
++
++              len -= pagesize;
++              cnt += pagesize;
++              page++;
++
++              if (!this->dev_ready)
++                      udelay (this->chip_delay);
++              else
++                      nand_wait_ready(mtd);
++
++              /* Check, if the chip supports auto page increment */
++              if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
++                      sndcmd = 1;
++      }
++
++      /* Deselect and wake up anyone waiting on the device */
++      nand_release_device(mtd);
++      return 0;
++}
++
++
++/**
++ * nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer
++ * @mtd:      MTD device structure
++ * @fsbuf:    buffer given by fs driver
++ * @oobsel:   out of band selection structre
++ * @autoplace:        1 = place given buffer into the oob bytes
++ * @numpages: number of pages to prepare
++ *
++ * Return:
++ * 1. Filesystem buffer available and autoplacement is off,
++ *    return filesystem buffer
++ * 2. No filesystem buffer or autoplace is off, return internal
++ *    buffer
++ * 3. Filesystem buffer is given and autoplace selected
++ *    put data from fs buffer into internal buffer and
++ *    retrun internal buffer
++ *
++ * Note: The internal buffer is filled with 0xff. This must
++ * be done only once, when no autoplacement happens
++ * Autoplacement sets the buffer dirty flag, which
++ * forces the 0xff fill before using the buffer again.
++ *
++*/
++#if NAND_WRITE_SUPPORT
++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;
++}
++#endif
++
++#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
++ *
++*/
++#if NAND_WRITE_SUPPORT
++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));
++}
++#endif
++
++/**
++ * 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
++ */
++#if NAND_WRITE_SUPPORT
++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)) {
++              puts ("nand_write_ecc: Attempt to write not page aligned data\r\n");
++              return -EINVAL;
++      }
++
++      /* Grab the lock and see if the device is available */
++      nand_get_device (this, mtd, FL_WRITING);
++
++      /* Calculate chipnr */
++      chipnr = (int)(to >> this->chip_shift);
++      /* Select the NAND device */
++      this->select_chip(mtd, chipnr);
++
++      /* Check, if it is write protected */
++      if (nand_check_wp(mtd))
++              goto out;
++
++      /* if oobsel is NULL, use chip defaults */
++      if (oobsel == NULL)
++              oobsel = &mtd->oobinfo;
++
++      /* Autoplace of oob data ? Use the default placement scheme */
++      if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
++              oobsel = this->autooob;
++              autoplace = 1;
++      }
++      if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
++              autoplace = 1;
++
++      /* Setup variables and oob buffer */
++      totalpages = len >> this->page_shift;
++      page = (int) (to >> this->page_shift);
++      /* Invalidate the page cache, if we write to the cached page */
++      if (page <= this->pagebuf && this->pagebuf < (page + totalpages))
++              this->pagebuf = -1;
++
++      /* Set it relative to chip */
++      page &= this->pagemask;
++      startpage = page;
++      /* Calc number of pages we can write in one go */
++      numpages = min (ppblock - (startpage  & (ppblock - 1)), totalpages);
++      oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages);
++      bufstart = (u_char *)buf;
++
++      /* Loop until all data is written */
++      while (written < len) {
++
++              this->data_poi = (u_char*) &buf[written];
++              /* Write one page. If this is the last page to write
++               * or the last page in this block, then use the
++               * real pageprogram command, else select cached programming
++               * if supported by the chip.
++               */
++              ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0));
++              if (ret) {
++                      DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret);
++                      goto out;
++              }
++              /* Next oob page */
++              oob += mtd->oobsize;
++              /* Update written bytes count */
++              written += mtd->oobblock;
++              if (written == len)
++                      goto cmp;
++
++              /* Increment page address */
++              page++;
++
++              /* Have we hit a block boundary ? Then we have to verify and
++               * if verify is ok, we have to setup the oob buffer for
++               * the next pages.
++              */
++              if (!(page & (ppblock - 1))){
++                      int ofs;
++                      this->data_poi = bufstart;
++                      ret = nand_verify_pages (mtd, this, startpage,
++                              page - startpage,
++                              oobbuf, oobsel, chipnr, (eccbuf != NULL));
++                      if (ret) {
++                              DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);
++                              goto out;
++                      }
++                      *retlen = written;
++
++                      ofs = autoplace ? mtd->oobavail : mtd->oobsize;
++                      if (eccbuf)
++                              eccbuf += (page - startpage) * ofs;
++                      totalpages -= page - startpage;
++                      numpages = min (totalpages, ppblock);
++                      page &= this->pagemask;
++                      startpage = page;
++                      oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel,
++                                      autoplace, numpages);
++                      oob = 0;
++                      /* Check, if we cross a chip boundary */
++                      if (!page) {
++                              chipnr++;
++                              this->select_chip(mtd, -1);
++                              this->select_chip(mtd, chipnr);
++                      }
++              }
++      }
++      /* Verify the remaining pages */
++cmp:
++      this->data_poi = bufstart;
++      ret = nand_verify_pages (mtd, this, startpage, totalpages,
++              oobbuf, oobsel, chipnr, (eccbuf != NULL));
++      if (!ret)
++              *retlen = written;
++      else
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);
++
++out:
++      /* Deselect and wake up anyone waiting on the device */
++      nand_release_device(mtd);
++
++      return ret;
++}
++#endif
++
++
++/**
++ * 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
++ */
++#if NAND_WRITE_SUPPORT
++static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
++{
++      int column, page, status, ret = -EIO, chipnr;
++      struct nand_chip *this = mtd->priv;
++
++      DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
++
++      /* Shift to get page */
++      page = (int) (to >> this->page_shift);
++      chipnr = (int) (to >> this->chip_shift);
++
++      /* Mask to get column */
++      column = to & (mtd->oobsize - 1);
++
++      /* Initialize return length value */
++      *retlen = 0;
++
++      /* Do not allow write past end of page */
++      if ((column + len) > mtd->oobsize) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n");
++              return -EINVAL;
++      }
++
++      /* Grab the lock and see if the device is available */
++      nand_get_device (this, mtd, FL_WRITING);
++
++      /* Select the NAND device */
++      this->select_chip(mtd, chipnr);
++
++      /* Reset the chip. Some chips (like the Toshiba TC5832DC found
++         in one of my DiskOnChip 2000 test units) will clear the whole
++         data page too if we don't do this. I have no clue why, but
++         I seem to have 'fixed' it in the doc2000 driver in
++         August 1999.  dwmw2. */
++      this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
++
++      /* Check, if it is write protected */
++      if (nand_check_wp(mtd))
++              goto out;
++
++      /* Invalidate the page cache, if we write to the cached page */
++      if (page == this->pagebuf)
++              this->pagebuf = -1;
++
++      if (NAND_MUST_PAD(this)) {
++              /* Write out desired data */
++              this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask);
++              /* prepad 0xff for partial programming */
++              this->write_buf(mtd, ffchars, column);
++              /* write data */
++              this->write_buf(mtd, buf, len);
++              /* postpad 0xff for partial programming */
++              this->write_buf(mtd, ffchars, mtd->oobsize - (len+column));
++      } else {
++              /* Write out desired data */
++              this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask);
++              /* write data */
++              this->write_buf(mtd, buf, len);
++      }
++      /* Send command to program the OOB data */
++      this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
++
++      status = this->waitfunc (mtd, this, FL_WRITING);
++
++      /* See if device thinks it succeeded */
++      if (status & NAND_STATUS_FAIL) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page);
++              ret = -EIO;
++              goto out;
++      }
++      /* Return happy */
++      *retlen = len;
++
++#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
++      /* Send command to read back the data */
++      this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask);
++
++      if (this->verify_buf(mtd, buf, len)) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page);
++              ret = -EIO;
++              goto out;
++      }
++#endif
++      ret = 0;
++out:
++      /* Deselect and wake up anyone waiting on the device */
++      nand_release_device(mtd);
++
++      return ret;
++}
++#endif
++
++
++/**
++ * 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
++ */
++#if NAND_KVEC_SUPPORT
++#if NAND_WRITE_SUPPORT
++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));
++}
++#endif
++#endif
++
++/**
++ * 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
++ */
++#if NAND_KVEC_SUPPORT
++#if NAND_WRITE_SUPPORT
++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)) {
++              puts ("nand_write_ecc: Attempt to write not page aligned data\r\n");
++              return -EINVAL;
++      }
++
++      /* Grab the lock and see if the device is available */
++      nand_get_device (this, mtd, FL_WRITING);
++
++      /* Get the current chip-nr */
++      chipnr = (int) (to >> this->chip_shift);
++      /* Select the NAND device */
++      this->select_chip(mtd, chipnr);
++
++      /* Check, if it is write protected */
++      if (nand_check_wp(mtd))
++              goto out;
++
++      /* if oobsel is NULL, use chip defaults */
++      if (oobsel == NULL)
++              oobsel = &mtd->oobinfo;
++
++      /* Autoplace of oob data ? Use the default placement scheme */
++      if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
++              oobsel = this->autooob;
++              autoplace = 1;
++      }
++      if (oobsel->useecc == MTD_NANDECC_AUTOPL_USR)
++              autoplace = 1;
++
++      /* Setup start page */
++      page = (int) (to >> this->page_shift);
++      /* Invalidate the page cache, if we write to the cached page */
++      if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift))
++              this->pagebuf = -1;
++
++      startpage = page & this->pagemask;
++
++      /* Loop until all kvec' data has been written */
++      len = 0;
++      while (count) {
++              /* If the given tuple is >= pagesize then
++               * write it out from the iov
++               */
++              if ((vecs->iov_len - len) >= mtd->oobblock) {
++                      /* Calc number of pages we can write
++                       * out of this iov in one go */
++                      numpages = (vecs->iov_len - len) >> this->page_shift;
++                      /* Do not cross block boundaries */
++                      numpages = min (ppblock - (startpage & (ppblock - 1)), numpages);
++                      oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
++                      bufstart = (u_char *)vecs->iov_base;
++                      bufstart += len;
++                      this->data_poi = bufstart;
++                      oob = 0;
++                      for (i = 1; i <= numpages; i++) {
++                              /* Write one page. If this is the last page to write
++                               * then use the real pageprogram command, else select
++                               * cached programming if supported by the chip.
++                               */
++                              ret = nand_write_page (mtd, this, page & this->pagemask,
++                                      &oobbuf[oob], oobsel, i != numpages);
++                              if (ret)
++                                      goto out;
++                              this->data_poi += mtd->oobblock;
++                              len += mtd->oobblock;
++                              oob += mtd->oobsize;
++                              page++;
++                      }
++                      /* Check, if we have to switch to the next tuple */
++                      if (len >= (int) vecs->iov_len) {
++                              vecs++;
++                              len = 0;
++                              count--;
++                      }
++              } else {
++                      /* We must use the internal buffer, read data out of each
++                       * tuple until we have a full page to write
++                       */
++                      int cnt = 0;
++                      while (cnt < mtd->oobblock) {
++                              if (vecs->iov_base != NULL && vecs->iov_len)
++                                      this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++];
++                              /* Check, if we have to switch to the next tuple */
++                              if (len >= (int) vecs->iov_len) {
++                                      vecs++;
++                                      len = 0;
++                                      count--;
++                              }
++                      }
++                      this->pagebuf = page;
++                      this->data_poi = this->data_buf;
++                      bufstart = this->data_poi;
++                      numpages = 1;
++                      oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
++                      ret = nand_write_page (mtd, this, page & this->pagemask,
++                              oobbuf, oobsel, 0);
++                      if (ret)
++                              goto out;
++                      page++;
++              }
++
++              this->data_poi = bufstart;
++              ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0);
++              if (ret)
++                      goto out;
++
++              written += mtd->oobblock * numpages;
++              /* All done ? */
++              if (!count)
++                      break;
++
++              startpage = page & this->pagemask;
++              /* Check, if we cross a chip boundary */
++              if (!startpage) {
++                      chipnr++;
++                      this->select_chip(mtd, -1);
++                      this->select_chip(mtd, chipnr);
++              }
++      }
++      ret = 0;
++out:
++      /* Deselect and wake up anyone waiting on the device */
++      nand_release_device(mtd);
++
++      *retlen = written;
++      return ret;
++}
++#endif
++#endif
++
++/**
++ * 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
++ */
++#if NAND_ERASE_SUPPORT
++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);
++}
++#endif
++
++/**
++ * 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
++ */
++#if NAND_ERASE_SUPPORT
++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);
++}
++#endif
++
++/**
++ * nand_erase - [MTD Interface] erase block(s)
++ * @mtd:      MTD device structure
++ * @instr:    erase instruction
++ *
++ * Erase one ore more blocks
++ */
++#if NAND_ERASE_SUPPORT
++static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
++{
++      return nand_erase_nand (mtd, instr, 0);
++}
++#endif
++
++#define BBT_PAGE_MASK 0xffffff3f
++/**
++ * nand_erase_intern - [NAND Interface] erase block(s)
++ * @mtd:      MTD device structure
++ * @instr:    erase instruction
++ * @allowbbt: allow erasing the bbt area
++ *
++ * Erase one ore more blocks
++ */
++#if NAND_ERASE_SUPPORT
++int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt)
++{
++      int page, len, status, pages_per_block, ret, chipnr;
++      struct nand_chip *this = mtd->priv;
++      int rewrite_bbt[NAND_MAX_CHIPS]={0};    /* flags to indicate the page, if bbt needs to be rewritten. */
++      unsigned int bbt_masked_page;           /* bbt mask to compare to page being erased. */
++                                              /* It is used to see if the current page is in the same */
++                                              /*   256 block group and the same bank as the bbt. */
++
++      DEBUG (MTD_DEBUG_LEVEL3,
++             "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
++
++      /* Start address must align on block boundary */
++      if (instr->addr & ((1 << this->phys_erase_shift) - 1)) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
++              return -EINVAL;
++      }
++
++      /* Length must align on block boundary */
++      if (instr->len & ((1 << this->phys_erase_shift) - 1)) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n");
++              return -EINVAL;
++      }
++
++      /* Do not allow erase past end of device */
++      if ((instr->len + instr->addr) > mtd->size) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n");
++              return -EINVAL;
++      }
++
++      instr->fail_addr = 0xffffffff;
++
++      /* Grab the lock and see if the device is available */
++      nand_get_device (this, mtd, FL_ERASING);
++
++      /* Shift to get first page */
++      page = (int) (instr->addr >> this->page_shift);
++      chipnr = (int) (instr->addr >> this->chip_shift);
++
++      /* Calculate pages in each block */
++      pages_per_block = 1 << (this->phys_erase_shift - this->page_shift);
++
++      /* Select the NAND device */
++      this->select_chip(mtd, chipnr);
++
++      /* Check the WP bit */
++      /* Check, if it is write protected */
++      if (nand_check_wp(mtd)) {
++              DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");
++              instr->state = MTD_ERASE_FAILED;
++              goto erase_exit;
++      }
++
++      /* if BBT requires refresh, set the BBT page mask to see if the BBT should be rewritten */
++      if (this->options & BBT_AUTO_REFRESH) {
++              bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
++      } else {
++              bbt_masked_page = 0xffffffff;   /* should not match anything */
++      }
++
++      /* Loop through the pages */
++      len = instr->len;
++
++      instr->state = MTD_ERASING;
++
++      while (len) {
++              /* Check if we have a bad block, we do not erase bad blocks ! */
++              if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) {
++                      puts ("nand_erase: attempt to erase a bad block at page ");
++                      putx (page);
++                      putnl ();
++                      instr->state = MTD_ERASE_FAILED;
++                      goto erase_exit;
++              }
++
++              /* Invalidate the page cache, if we erase the block which contains
++                 the current cached page */
++              if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block))
++                      this->pagebuf = -1;
++
++              this->erase_cmd (mtd, page & this->pagemask);
++
++              status = this->waitfunc (mtd, this, FL_ERASING);
++
++              /* See if operation failed and additional status checks are available */
++              if ((status & NAND_STATUS_FAIL) && (this->errstat)) {
++                      status = this->errstat(mtd, this, FL_ERASING, status, page);
++              }
++
++              /* See if block erase succeeded */
++              if (status & NAND_STATUS_FAIL) {
++                      DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
++                      instr->state = MTD_ERASE_FAILED;
++                      instr->fail_addr = (page << this->page_shift);
++                      goto erase_exit;
++              }
++
++              /* if BBT requires refresh, set the BBT rewrite flag to the page being erased */
++              if (this->options & BBT_AUTO_REFRESH) {
++                      if (((page & BBT_PAGE_MASK) == bbt_masked_page) &&
++                           (page != this->bbt_td->pages[chipnr])) {
++                              rewrite_bbt[chipnr] = (page << this->page_shift);
++                      }
++              }
++
++              /* Increment page address and decrement length */
++              len -= (1 << this->phys_erase_shift);
++              page += pages_per_block;
++
++              /* Check, if we cross a chip boundary */
++              if (len && !(page & this->pagemask)) {
++                      chipnr++;
++                      this->select_chip(mtd, -1);
++                      this->select_chip(mtd, chipnr);
++
++                      /* if BBT requires refresh and BBT-PERCHIP,
++                       *   set the BBT page mask to see if this BBT should be rewritten */
++                      if ((this->options & BBT_AUTO_REFRESH) && (this->bbt_td->options & NAND_BBT_PERCHIP)) {
++                              bbt_masked_page = this->bbt_td->pages[chipnr] & BBT_PAGE_MASK;
++                      }
++
++              }
++      }
++      instr->state = MTD_ERASE_DONE;
++
++erase_exit:
++
++      ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
++#if 0
++      /* Do call back function */
++      if (!ret)
++              mtd_erase_callback(instr);
++#endif
++
++      /* Deselect and wake up anyone waiting on the device */
++      nand_release_device(mtd);
++
++#if NAND_BBT_SUPPORT
++      /* if BBT requires refresh and erase was successful, rewrite any selected bad block tables */
++      if ((this->options & BBT_AUTO_REFRESH) && (!ret)) {
++              for (chipnr = 0; chipnr < this->numchips; chipnr++) {
++                      if (rewrite_bbt[chipnr]) {
++                              /* update the BBT for chip */
++                              DEBUG (MTD_DEBUG_LEVEL0, "nand_erase_nand: nand_update_bbt (%d:0x%0x 0x%0x)\n",
++                                      chipnr, rewrite_bbt[chipnr], this->bbt_td->pages[chipnr]);
++                              nand_update_bbt (mtd, rewrite_bbt[chipnr]);
++                      }
++              }
++      }
++#endif
++
++      /* Return more or less happy */
++      return ret;
++}
++#endif
++
++/**
++ * nand_sync - [MTD Interface] sync
++ * @mtd:      MTD device structure
++ *
++ * Sync is actually a wait for chip ready function
++ */
++#if 0 /* not needed */
++static void nand_sync (struct mtd_info *mtd)
++{
++      struct nand_chip *this = mtd->priv;
++
++      DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
++
++      /* Grab the lock and see if the device is available */
++      nand_get_device (this, mtd, FL_SYNCING);
++      /* Release it and go back */
++      nand_release_device (mtd);
++}
++#endif
++
++
++/**
++ * 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
++ */
++#if NAND_WRITE_SUPPORT
++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);
++}
++#endif
++
++/**
++ * nand_suspend - [MTD Interface] Suspend the NAND flash
++ * @mtd:      MTD device structure
++ */
++#if 0 /* don't need it */
++static int nand_suspend(struct mtd_info *mtd)
++{
++      struct nand_chip *this = mtd->priv;
++
++      return nand_get_device (this, mtd, FL_PM_SUSPENDED);
++}
++#endif
++
++/**
++ * nand_resume - [MTD Interface] Resume the NAND flash
++ * @mtd:      MTD device structure
++ */
++#if 0 /* don't need it */
++static void nand_resume(struct mtd_info *mtd)
++{
++      struct nand_chip *this = mtd->priv;
++
++      if (this->state == FL_PM_SUSPENDED)
++              nand_release_device(mtd);
++      else
++              puts("resume() called for the chip which is not in suspended state\n");
++
++}
++#endif
++
++
++/**
++ * nand_scan - [NAND Interface] Scan for the NAND device
++ * @mtd:      MTD device structure
++ * @maxchips: Number of chips to scan for
++ *
++ * This fills out all the not initialized function pointers
++ * with the defaults.
++ * The flash ID is read and the mtd/chip structures are
++ * filled with the appropriate values. Buffers are allocated if
++ * they are not provided by the board driver
++ *
++ */
++int nand_scan (struct mtd_info *mtd, int maxchips)
++{
++      int i, nand_maf_id, nand_dev_id, busw, maf_id;
++      struct nand_chip *this = mtd->priv;
++
++      /* Get buswidth to select the correct functions*/
++      busw = this->options & NAND_BUSWIDTH_16;
++
++      /* check for proper chip_delay setup, set 20us if not */
++      if (!this->chip_delay)
++              this->chip_delay = 20;
++
++      /* check, if a user supplied command function given */
++      if (this->cmdfunc == NULL)
++              this->cmdfunc = nand_command;
++
++      /* check, if a user supplied wait function given */
++      if (this->waitfunc == NULL)
++              this->waitfunc = nand_wait;
++
++      if (!this->select_chip)
++              this->select_chip = nand_select_chip;
++      if (!this->write_byte)
++              this->write_byte = busw ? nand_write_byte16 : nand_write_byte;
++      if (!this->read_byte)
++              this->read_byte = busw ? nand_read_byte16 : nand_read_byte;
++      if (!this->write_word)
++              this->write_word = nand_write_word;
++      if (!this->read_word)
++              this->read_word = nand_read_word;
++      if (!this->block_bad)
++              this->block_bad = nand_block_bad;
++#if NAND_WRITE_SUPPORT
++      if (!this->block_markbad)
++              this->block_markbad = nand_default_block_markbad;
++#endif
++      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 NAND_BBT_SUPPORT
++      if (!this->scan_bbt)
++              this->scan_bbt = nand_default_bbt;
++#endif
++
++      /* 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 & 0x01)) * (mtd->oobblock >> 9);
++                      extid >>= 2;
++                      /* Calc blocksize. Blocksize is multiples of 64KiB */
++                      mtd->erasesize = (64 * 1024)  << (extid & 0x03);
++                      extid >>= 2;
++                      /* Get buswidth information */
++                      busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
++
++              } else {
++                      /* Old devices have this data hardcoded in the
++                       * device id table */
++                      mtd->erasesize = nand_flash_ids[i].erasesize;
++                      mtd->oobblock = nand_flash_ids[i].pagesize;
++                      mtd->oobsize = mtd->oobblock / 32;
++                      busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;
++              }
++
++              /* Try to identify manufacturer */
++              for (maf_id = 0; nand_manuf_ids[maf_id].id != 0x0; maf_id++) {
++                      if (nand_manuf_ids[maf_id].id == nand_maf_id)
++                              break;
++              }
++
++              /* Check, if buswidth is correct. Hardware drivers should set
++               * this correct ! */
++              if (busw != (this->options & NAND_BUSWIDTH_16)) {
++#if 0
++                      puts ("Manufacturer ID: ");
++                      putx (nand_maf_id);
++                      puts (", Chip ID: ");
++                      putx (nand_dev_id);
++                      puts (" (");
++                      puts (nand_manuf_ids[maf_id].name);
++                      putc (' ');
++                      puts (mtd->name);
++                      puts (")\r\n");
++#endif
++                      puts ("Expected NAND bus width ");
++                      putx ((this->options & NAND_BUSWIDTH_16) ? 16 : 8);
++                      puts (",found ");
++                      putx (busw ? 16 : 8);
++                      putnl();
++                      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;
++
++#if NAND_ERASE_SUPPORT
++              /* 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;
++#endif
++
++              /* Do not replace user supplied command function ! */
++              if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
++                      this->cmdfunc = nand_command_lp;
++
++              puts ("Manufacturer ID / Chip ID: ");
++              putx (nand_maf_id << 8 | nand_dev_id);
++              puts (" (");
++              puts (nand_manuf_ids[maf_id].name);
++              putc (' ');
++              puts (nand_flash_ids[i].name);
++              puts (")\r\n");
++              break;
++      }
++
++      if (!nand_flash_ids[i].name) {
++              puts ("No NAND device found!!!\r\n");
++              this->select_chip(mtd, -1);
++              return 1;
++      }
++
++#if NAND_MULTICHIP_SUPPORT
++      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) {
++              putx (i);
++              puts (" NAND chips detected\r\n");
++      }
++#endif
++
++      /* Allocate buffers, if neccecary */
++      if (!this->oob_buf) {
++              size_t len;
++              len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);
++              this->oob_buf = malloc (len);
++              if (!this->oob_buf) {
++                      puts ("nand_scan(): Cannot allocate oob_buf\r\n");
++                      return -ENOMEM;
++              }
++              this->options |= NAND_OOBBUF_ALLOC;
++      }
++
++      if (!this->data_buf) {
++              size_t len;
++              len = mtd->oobblock + mtd->oobsize;
++              this->data_buf = malloc (len);
++              if (!this->data_buf) {
++                      if (this->options & NAND_OOBBUF_ALLOC)
++                              free (this->oob_buf);
++                      puts ("nand_scan(): Cannot allocate data_buf\r\n");
++                      return -ENOMEM;
++              }
++              this->options |= NAND_DATABUF_ALLOC;
++      }
++
++#if NAND_MULTICHIP_SUPP0RT
++      /* Store the number of chips and calc total size for mtd */
++      this->numchips = i;
++      mtd->size = i * this->chipsize;
++#else
++      /* Store the number of chips and calc total size for mtd */
++      this->numchips = 1;
++      mtd->size = this->chipsize;
++#endif
++
++      /* 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:
++                      puts ("No oob scheme defined for oobsize ");
++                      putx (mtd->oobsize);
++                      putnl ();
++                      BUG();
++              }
++      }
++
++      /* The number of bytes available for the filesystem to place fs dependend
++       * oob data */
++      mtd->oobavail = 0;
++      for (i = 0; this->autooob->oobfree[i][1]; i++)
++              mtd->oobavail += this->autooob->oobfree[i][1];
++
++      /*
++       * check ECC mode, default to software
++       * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
++       * fallback to software ECC
++      */
++      this->eccsize = 256;    /* set default eccsize */
++      this->eccbytes = 3;
++
++      switch (this->eccmode) {
++#if NAND_HWECC_SUPPORT
++      case NAND_ECC_HW12_2048:
++              if (mtd->oobblock < 2048) {
++                      puts ("2048 byte HW ECC not possible on ");
++                      putx (mtd->oobblock);
++                      puts (" byte page size, fallback to SW ECC\r\n");
++                      this->eccmode = NAND_ECC_SOFT;
++                      this->calculate_ecc = nand_calculate_ecc;
++                      this->correct_data = nand_correct_data;
++              } else
++                      this->eccsize = 2048;
++              break;
++
++      case NAND_ECC_HW3_512:
++      case NAND_ECC_HW6_512:
++      case NAND_ECC_HW8_512:
++              if (mtd->oobblock == 256) {
++                      puts ("512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC\r\n");
++                      this->eccmode = NAND_ECC_SOFT;
++                      this->calculate_ecc = nand_calculate_ecc;
++                      this->correct_data = nand_correct_data;
++              } else
++                      this->eccsize = 512; /* set eccsize to 512 */
++              break;
++
++      case NAND_ECC_HW3_256:
++              break;
++#endif
++
++      case NAND_ECC_NONE:
++              puts ("NAND_ECC_NONE selected by board driver. This is not recommended !!\r\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:
++              puts ("Invalid NAND_ECC_MODE ");
++              putx (this->eccmode);
++              putnl ();
++              BUG();
++      }
++
++      /* Check hardware ecc function availability and adjust number of ecc bytes per
++       * calculation step
++      */
++      switch (this->eccmode) {
++      case NAND_ECC_HW12_2048:
++              this->eccbytes += 4;
++      case NAND_ECC_HW8_512:
++              this->eccbytes += 2;
++      case NAND_ECC_HW6_512:
++              this->eccbytes += 3;
++      case NAND_ECC_HW3_512:
++      case NAND_ECC_HW3_256:
++              if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
++                      break;
++              puts ("No ECC functions supplied, Hardware ECC not possible\r\n");
++              BUG();
++      }
++
++      mtd->eccsize = this->eccsize;
++
++      /* Set the number of read / write steps for one page to ensure ECC generation */
++      switch (this->eccmode) {
++      case NAND_ECC_HW12_2048:
++              this->eccsteps = mtd->oobblock / 2048;
++              break;
++      case NAND_ECC_HW3_512:
++      case NAND_ECC_HW6_512:
++      case NAND_ECC_HW8_512:
++              this->eccsteps = mtd->oobblock / 512;
++              break;
++      case NAND_ECC_HW3_256:
++      case NAND_ECC_SOFT:
++              this->eccsteps = mtd->oobblock / 256;
++              break;
++
++      case NAND_ECC_NONE:
++              this->eccsteps = 1;
++              break;
++      }
++
++      /* Initialize state, waitqueue and spinlock */
++      this->state = FL_READY;
++#if 0
++      init_waitqueue_head (&this->wq);
++      spin_lock_init (&this->chip_lock);
++#endif
++
++      /* 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;
++#if NAND_ERASE_SUPPORT
++      mtd->erase = nand_erase;
++#endif
++#if 0 /* not needed */
++      mtd->point = NULL;
++      mtd->unpoint = NULL;
++#endif
++      mtd->read = nand_read;
++#if NAND_WRITE_SUPPORT
++      mtd->write = nand_write;
++#endif
++      mtd->read_ecc = nand_read_ecc;
++#if NAND_WRITE_SUPPORT
++      mtd->write_ecc = nand_write_ecc;
++#endif
++      mtd->read_oob = nand_read_oob;
++#if NAND_WRITE_SUPPORT
++      mtd->write_oob = nand_write_oob;
++#endif
++#if NAND_KVEC_SUPPORT
++      mtd->readv = NULL;
++#endif
++#if NAND_WRITE_SUPPORT && NAND_KVEC_SUPPORT
++      mtd->writev = nand_writev;
++      mtd->writev_ecc = nand_writev_ecc;
++#endif
++#if 0 /* not needed */
++      mtd->sync = nand_sync;
++      mtd->lock = NULL;
++      mtd->unlock = NULL;
++      mtd->suspend = nand_suspend;
++      mtd->resume = nand_resume;
++#endif
++      mtd->block_isbad = nand_block_isbad;
++#if NAND_WRITE_SUPPORT
++      mtd->block_markbad = nand_block_markbad;
++#endif
++
++      /* and make the autooob the default one */
++      memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
++
++#ifdef THIS_MODULE /* normally isn't for us */
++      mtd->owner = THIS_MODULE;
++#endif
++
++      /* Check, if we should skip the bad block table scan */
++      if (this->options & NAND_SKIP_BBTSCAN)
++              return 0;
++
++#if NAND_BBT_SUPPORT
++      /* Build bad block table */
++      return this->scan_bbt (mtd);
++#else
++      return -1;
++#endif
++}
++
++/**
++ * nand_release - [NAND Interface] Free resources held by the NAND device
++ * @mtd:      MTD device structure
++*/
++#if 0 /* don't need it */
++void nand_release (struct mtd_info *mtd)
++{
++      struct nand_chip *this = mtd->priv;
++
++#if 0
++#ifdef CONFIG_MTD_PARTITIONS
++      /* Deregister partitions */
++      del_mtd_partitions (mtd);
++#endif
++      /* Deregister the device */
++      del_mtd_device (mtd);
++#endif
++
++      /* Free bad block table memory */
++      free (this->bbt);
++      /* Buffer allocated by nand_scan ? */
++      if (this->options & NAND_OOBBUF_ALLOC)
++              free (this->oob_buf);
++      /* Buffer allocated by nand_scan ? */
++      if (this->options & NAND_DATABUF_ALLOC)
++              free (this->data_buf);
++}
++#endif
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_bbt.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_bbt.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_bbt.c       1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_bbt.c       2006-11-10 09:55:58.000000000 +0100
+@@ -0,0 +1,1151 @@
++/*
++ *  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.5 2006/11/10 08:55:58 ricardw 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
++ *
++ */
++
++#if 0
++#include <linux/slab.h>
++#endif
++
++#include <linux/types.h>
++#include "mtd.h"
++#include "nand.h"
++#include "nand_ecc.h"
++
++#if 0
++#include <linux/mtd/compatmac.h>
++#include <linux/bitops.h>
++#endif
++
++#include <linux/delay.h>
++
++#include "lib.h"
++
++
++/**
++ * check_pattern - [GENERIC] check if a pattern is in the buffer
++ * @buf:      the buffer to search
++ * @len:      the length of buffer to search
++ * @paglen:   the pagelength
++ * @td:               search pattern descriptor
++ *
++ * Check for a pattern at the given place. Used to search bad block
++ * tables and good / bad block identifiers.
++ * If the SCAN_EMPTY option is set then check, if all bytes except the
++ * pattern area contain 0xff
++ *
++*/
++static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
++{
++      int i, end = 0;
++      uint8_t *p = buf;
++
++      end = paglen + td->offs;
++      if (td->options & NAND_BBT_SCANEMPTY) {
++              for (i = 0; i < end; i++) {
++                      if (p[i] != 0xff)
++                              return -1;
++              }
++      }
++      p += end;
++
++      /* Compare the pattern */
++      for (i = 0; i < td->len; i++) {
++              if (p[i] != td->pattern[i])
++                      return -1;
++      }
++
++      if (td->options & NAND_BBT_SCANEMPTY) {
++              p += td->len;
++              end += td->len;
++              for (i = end; i < len; i++) {
++                      if (*p++ != 0xff)
++                              return -1;
++              }
++      }
++      return 0;
++}
++
++/**
++ * check_short_pattern - [GENERIC] check if a pattern is in the buffer
++ * @buf:      the buffer to search
++ * @td:               search pattern descriptor
++ *
++ * Check for a pattern at the given place. Used to search bad block
++ * tables and good / bad block identifiers. Same as check_pattern, but
++ * no optional empty check
++ *
++*/
++static int check_short_pattern (uint8_t *buf, struct nand_bbt_descr *td)
++{
++      int i;
++      uint8_t *p = buf;
++
++      /* Compare the pattern */
++      for (i = 0; i < td->len; i++) {
++              if (p[td->offs + i] != td->pattern[i])
++                      return -1;
++      }
++      return 0;
++}
++
++/**
++ * read_bbt - [GENERIC] Read the bad block table starting from page
++ * @mtd:      MTD device structure
++ * @buf:      temporary buffer
++ * @page:     the starting page
++ * @num:      the number of bbt descriptors to read
++ * @bits:     number of bits per block
++ * @offs:     offset in the memory table
++ * @reserved_block_code:      Pattern to identify reserved blocks
++ *
++ * Read the bad block table starting from page.
++ *
++ */
++static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
++      int bits, int offs, int reserved_block_code)
++{
++      int res, i, j, act = 0;
++      struct nand_chip *this = mtd->priv;
++      size_t retlen, len, totlen;
++      loff_t from;
++      uint8_t msk = (uint8_t) ((1 << bits) - 1);
++
++      totlen = (num * bits) >> 3;
++      from = ((loff_t)page) << this->page_shift;
++
++      while (totlen) {
++              len = min (totlen, (size_t) (1 << this->bbt_erase_shift));
++              res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob);
++              if (res < 0) {
++                      if (retlen != len) {
++                              puts ("nand_bbt: Error reading bad block table\n");
++                              return res;
++                      }
++                      puts ("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)) {
++                                      puts ("nand_read_bbt: Reserved block at");
++                                      putx (((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
++                                      putnl ();
++                                      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 */
++                              puts ("nand_read_bbt: Bad block at ");
++                              putx (((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
++                              putnl ();
++                              /* 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];
++              puts ("Bad block table at page ");
++              putx (td->pages[0]);
++              puts (", version ");
++              putx (td->version[0]);
++              putnl ();
++      }
++
++      /* 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];
++              puts ("Bad block table at page ");
++              putx (md->pages[0]);
++              puts (", version ");
++              putx (md->version[0]);
++              putnl ();
++      }
++
++      return 1;
++}
++
++/**
++ * create_bbt - [GENERIC] Create a bad block table by scanning the device
++ * @mtd:      MTD device structure
++ * @buf:      temporary buffer
++ * @bd:               descriptor for the good/bad block search pattern
++ * @chip:     create the table for a specific chip, -1 read all chips.
++ *            Applies only if NAND_BBT_PERCHIP option is set
++ *
++ * Create a bad block table by scanning the device
++ * for the given good/bad block identify pattern
++ */
++static int create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
++{
++      struct nand_chip *this = mtd->priv;
++      int i, j, numblocks, len, scanlen;
++      int startblock;
++      loff_t from;
++      size_t readlen, ooblen;
++
++      puts ("Scanning device for bad blocks\n");
++
++      if (bd->options & NAND_BBT_SCANALLPAGES)
++              len = 1 << (this->bbt_erase_shift - this->page_shift);
++      else {
++              if (bd->options & NAND_BBT_SCAN2NDPAGE)
++                      len = 2;
++              else
++                      len = 1;
++      }
++
++      if (!(bd->options & NAND_BBT_SCANEMPTY)) {
++              /* We need only read few bytes from the OOB area */
++              scanlen = ooblen = 0;
++              readlen = bd->len;
++      } else {
++              /* Full page content should be read */
++              scanlen = mtd->oobblock + mtd->oobsize;
++              readlen = len * mtd->oobblock;
++              ooblen = len * mtd->oobsize;
++      }
++
++      if (chip == -1) {
++              /* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it
++               * makes shifting and masking less painful */
++              numblocks = mtd->size >> (this->bbt_erase_shift - 1);
++              startblock = 0;
++              from = 0;
++      } else {
++              if (chip >= this->numchips) {
++                      puts ("create_bbt(): chipnr (");
++                      putx (chip + 1);
++                      puts (" > available chips ");
++                      putx (this->numchips);
++                      putnl ();
++                      return -EINVAL;
++              }
++              numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
++              startblock = chip * numblocks;
++              numblocks += startblock;
++              from = startblock << (this->bbt_erase_shift - 1);
++      }
++
++      for (i = startblock; i < numblocks;) {
++              int ret;
++
++              if (bd->options & NAND_BBT_SCANEMPTY)
++                      if ((ret = nand_read_raw (mtd, buf, from, readlen, ooblen)))
++                              return ret;
++
++              for (j = 0; j < len; j++) {
++                      if (!(bd->options & NAND_BBT_SCANEMPTY)) {
++                              size_t retlen;
++
++                              /* Read the full oob until read_oob is fixed to
++                               * handle single byte reads for 16 bit buswidth */
++                              ret = mtd->read_oob(mtd, from + j * mtd->oobblock,
++                                                      mtd->oobsize, &retlen, buf);
++                              if (ret)
++                                      return ret;
++
++                              if (check_short_pattern (buf, bd)) {
++                                      this->bbt[i >> 3] |= 0x03 << (i & 0x6);
++                                      puts ("Bad eraseblock ");
++                                      putx (i >> 1);
++                                      puts (" at ");
++                                      putx ((unsigned int) from);
++                                      putnl ();
++                                      break;
++                              }
++                      } else {
++                              if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
++                                      this->bbt[i >> 3] |= 0x03 << (i & 0x6);
++                                      puts ("Bad eraseblock ");
++                                      putx (i >> 1);
++                                      puts (" at ");
++                                      putx ((unsigned int) from);
++                                      putnl ();
++                                      break;
++                              }
++                      }
++              }
++              i += 2;
++              from += (1 << this->bbt_erase_shift);
++      }
++      return 0;
++}
++
++/**
++ * search_bbt - [GENERIC] scan the device for a specific bad block table
++ * @mtd:      MTD device structure
++ * @buf:      temporary buffer
++ * @td:               descriptor for the bad block table
++ *
++ * Read the bad block table by searching for a given ident pattern.
++ * Search is preformed either from the beginning up or from the end of
++ * the device downwards. The search starts always at the start of a
++ * block.
++ * If the option NAND_BBT_PERCHIP is given, each chip is searched
++ * for a bbt, which contains the bad block information of this chip.
++ * This is neccecary to provide support for certain DOC devices.
++ *
++ * The bbt ident pattern resides in the oob area of the first page
++ * in a block.
++ */
++static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
++{
++      struct nand_chip *this = mtd->priv;
++      int i, chips;
++      int bits, startblock, block, dir;
++      int scanlen = mtd->oobblock + mtd->oobsize;
++      int bbtblocks;
++
++      /* Search direction top -> down ? */
++      if (td->options & NAND_BBT_LASTBLOCK) {
++              startblock = (mtd->size >> this->bbt_erase_shift) -1;
++              dir = -1;
++      } else {
++              startblock = 0;
++              dir = 1;
++      }
++
++      /* Do we have a bbt per chip ? */
++      if (td->options & NAND_BBT_PERCHIP) {
++              chips = this->numchips;
++              bbtblocks = this->chipsize >> this->bbt_erase_shift;
++              startblock &= bbtblocks - 1;
++      } else {
++              chips = 1;
++              bbtblocks = mtd->size >> this->bbt_erase_shift;
++      }
++
++      /* Number of bits for each erase block in the bbt */
++      bits = td->options & NAND_BBT_NRBITS_MSK;
++
++      for (i = 0; i < chips; i++) {
++              /* Reset version information */
++              td->version[i] = 0;
++              td->pages[i] = -1;
++              /* Scan the maximum number of blocks */
++              for (block = 0; block < td->maxblocks; block++) {
++                      int actblock = startblock + dir * block;
++                      /* Read first page */
++                      nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize);
++                      if (!check_pattern(buf, scanlen, mtd->oobblock, td)) {
++                              td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift);
++                              if (td->options & NAND_BBT_VERSION) {
++                                      td->version[i] = buf[mtd->oobblock + td->veroffs];
++                              }
++                              break;
++                      }
++              }
++              startblock += this->chipsize >> this->bbt_erase_shift;
++      }
++      /* Check, if we found a bbt for each requested chip */
++      for (i = 0; i < chips; i++) {
++              if (td->pages[i] == -1) {
++                      puts ("Bad block table not found for chip ");
++                      putx (i);
++                      putnl ();
++              } else {
++                      puts ("Bad block table found at page ");
++                      putx (td->pages[i]);
++                      puts (" version ");
++                      putx (td->version[i]);
++                      putnl ();
++              }
++      }
++      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;
++              }
++              puts ("No space left to write bad block table\r\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) {
++                                      puts ("nand_bbt: Error reading block for writing the bad block table\r\n");
++                                      return res;
++                              }
++                              puts ("nand_bbt: ECC error while reading block for writing bad block table\r\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) {
++                      puts ("nand_bbt: Error during block erase: ");
++                      putx (res);
++                      putnl();
++                      return res;
++              }
++
++              res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
++              if (res < 0) {
++                      puts ("nand_bbt: Error while writing bad block table ");
++                      putx (res);
++                      putnl ();
++                      return res;
++              }
++              puts ("Bad block table written to ");
++              putx ((unsigned int) to);
++              puts (", version ");
++              putx (td->version[chip]);
++              putnl ();
++
++              /* Mark it as used */
++              td->pages[chip] = page;
++      }
++      return 0;
++}
++
++/**
++ * nand_memory_bbt - [GENERIC] create a memory based bad block table
++ * @mtd:      MTD device structure
++ * @bd:               descriptor for the good/bad block search pattern
++ *
++ * The function creates a memory based bbt by scanning the device
++ * for manufacturer / software marked good / bad blocks
++*/
++static inline int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
++{
++      struct nand_chip *this = mtd->priv;
++
++      bd->options &= ~NAND_BBT_SCANEMPTY;
++      return create_bbt (mtd, this->data_buf, bd, -1);
++}
++
++/**
++ * check_create - [GENERIC] create and write bbt(s) if neccecary
++ * @mtd:      MTD device structure
++ * @buf:      temporary buffer
++ * @bd:               descriptor for the good/bad block search pattern
++ *
++ * The function checks the results of the previous call to read_bbt
++ * and creates / updates the bbt(s) if neccecary
++ * Creation is neccecary if no bbt was found for the chip/device
++ * Update is neccecary if one of the tables is missing or the
++ * version nr. of one table is less than the other
++*/
++static int check_create (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
++{
++      int i, chips, writeops, chipsel, res;
++      struct nand_chip *this = mtd->priv;
++      struct nand_bbt_descr *td = this->bbt_td;
++      struct nand_bbt_descr *md = this->bbt_md;
++      struct nand_bbt_descr *rd, *rd2;
++
++      /* Do we have a bbt per chip ? */
++      if (td->options & NAND_BBT_PERCHIP)
++              chips = this->numchips;
++      else
++              chips = 1;
++
++      for (i = 0; i < chips; i++) {
++              writeops = 0;
++              rd = NULL;
++              rd2 = NULL;
++              /* Per chip or per device ? */
++              chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
++              /* Mirrored table avilable ? */
++              if (md) {
++                      if (td->pages[i] == -1 && md->pages[i] == -1) {
++                              writeops = 0x03;
++                              goto create;
++                      }
++
++                      if (td->pages[i] == -1) {
++                              rd = md;
++                              td->version[i] = md->version[i];
++                              writeops = 1;
++                              goto writecheck;
++                      }
++
++                      if (md->pages[i] == -1) {
++                              rd = td;
++                              md->version[i] = td->version[i];
++                              writeops = 2;
++                              goto writecheck;
++                      }
++
++                      if (td->version[i] == md->version[i]) {
++                              rd = td;
++                              if (!(td->options & NAND_BBT_VERSION))
++                                      rd2 = md;
++                              goto writecheck;
++                      }
++
++                      if (((int8_t) (td->version[i] - md->version[i])) > 0) {
++                              rd = td;
++                              md->version[i] = td->version[i];
++                              writeops = 2;
++                      } else {
++                              rd = md;
++                              td->version[i] = md->version[i];
++                              writeops = 1;
++                      }
++
++                      goto writecheck;
++
++              } else {
++                      if (td->pages[i] == -1) {
++                              writeops = 0x01;
++                              goto create;
++                      }
++                      rd = td;
++                      goto writecheck;
++              }
++create:
++              /* Create the bad block table by scanning the device ? */
++              if (!(td->options & NAND_BBT_CREATE))
++                      continue;
++
++              /* Create the table in memory by scanning the chip(s) */
++              create_bbt (mtd, buf, bd, chipsel);
++
++              td->version[i] = 1;
++              if (md)
++                      md->version[i] = 1;
++writecheck:
++              /* read back first ? */
++              if (rd)
++                      read_abs_bbt (mtd, buf, rd, chipsel);
++              /* If they weren't versioned, read both. */
++              if (rd2)
++                      read_abs_bbt (mtd, buf, rd2, chipsel);
++
++              /* Write the bad block table to the device ? */
++              if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
++                      res = write_bbt (mtd, buf, td, md, chipsel);
++                      if (res < 0)
++                              return res;
++              }
++
++              /* Write the mirror bad block table to the device ? */
++              if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
++                      res = write_bbt (mtd, buf, md, td, chipsel);
++                      if (res < 0)
++                              return res;
++              }
++      }
++      return 0;
++}
++
++/**
++ * mark_bbt_regions - [GENERIC] mark the bad block table regions
++ * @mtd:      MTD device structure
++ * @td:               bad block table descriptor
++ *
++ * The bad block table regions are marked as "bad" to prevent
++ * accidental erasures / writes. The regions are identified by
++ * the mark 0x02.
++*/
++static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
++{
++      struct nand_chip *this = mtd->priv;
++      int i, j, chips, block, nrblocks, update;
++      uint8_t oldval, newval;
++
++      /* Do we have a bbt per chip ? */
++      if (td->options & NAND_BBT_PERCHIP) {
++              chips = this->numchips;
++              nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
++      } else {
++              chips = 1;
++              nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
++      }
++
++      for (i = 0; i < chips; i++) {
++              if ((td->options & NAND_BBT_ABSPAGE) ||
++                  !(td->options & NAND_BBT_WRITE)) {
++                      if (td->pages[i] == -1) continue;
++                      block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
++                      block <<= 1;
++                      oldval = this->bbt[(block >> 3)];
++                      newval = oldval | (0x2 << (block & 0x06));
++                      this->bbt[(block >> 3)] = newval;
++                      if ((oldval != newval) && td->reserved_block_code)
++                              nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1));
++                      continue;
++              }
++              update = 0;
++              if (td->options & NAND_BBT_LASTBLOCK)
++                      block = ((i + 1) * nrblocks) - td->maxblocks;
++              else
++                      block = i * nrblocks;
++              block <<= 1;
++              for (j = 0; j < td->maxblocks; j++) {
++                      oldval = this->bbt[(block >> 3)];
++                      newval = oldval | (0x2 << (block & 0x06));
++                      this->bbt[(block >> 3)] = newval;
++                      if (oldval != newval) update = 1;
++                      block += 2;
++              }
++              /* If we want reserved blocks to be recorded to flash, and some
++                 new ones have been marked, then we need to update the stored
++                 bbts.  This should only happen once. */
++              if (update && td->reserved_block_code)
++                      nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1));
++      }
++}
++
++/**
++ * nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
++ * @mtd:      MTD device structure
++ * @bd:               descriptor for the good/bad block search pattern
++ *
++ * The function checks, if a bad block table(s) is/are already
++ * available. If not it scans the device for manufacturer
++ * marked good / bad blocks and writes the bad block table(s) to
++ * the selected place.
++ *
++ * The bad block table memory is allocated here. It must be freed
++ * by calling the nand_free_bbt function.
++ *
++*/
++int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
++{
++      struct nand_chip *this = mtd->priv;
++      int len, res = 0;
++      uint8_t *buf;
++      struct nand_bbt_descr *td = this->bbt_td;
++      struct nand_bbt_descr *md = this->bbt_md;
++
++      len = mtd->size >> (this->bbt_erase_shift + 2);
++      /* Allocate memory (2bit per block) */
++      this->bbt = malloc (len);
++      if (!this->bbt) {
++              puts ("nand_scan_bbt: Out of memory\r\n");
++              return -ENOMEM;
++      }
++      /* Clear the memory bad block table */
++      memset (this->bbt, 0x00, len);
++
++      /* If no primary table decriptor is given, scan the device
++       * to build a memory based bad block table
++       */
++      if (!td) {
++              if ((res = nand_memory_bbt(mtd, bd))) {
++                      puts ("nand_bbt: Can't scan flash and build the RAM-based BBT\r\n");
++                      free (this->bbt);
++                      this->bbt = NULL;
++              }
++              return res;
++      }
++
++      /* Allocate a temporary buffer for one eraseblock incl. oob */
++      len = (1 << this->bbt_erase_shift);
++      len += (len >> this->page_shift) * mtd->oobsize;
++      buf = malloc (len);
++      if (!buf) {
++              puts ("nand_bbt: Out of memory\r\n");
++              free (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);
++
++      free (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 = malloc (len);
++      if (!buf) {
++              puts ("nand_update_bbt: Out of memory\r\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:
++      free (buf);
++      return res;
++}
++
++/* Define some generic bad / good block scan pattern which are used
++ * while scanning a device for factory marked good / bad blocks. */
++static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
++
++static struct nand_bbt_descr smallpage_memorybased = {
++      .options = NAND_BBT_SCAN2NDPAGE,
++      .offs = 5,
++      .len = 1,
++      .pattern = scan_ff_pattern
++};
++
++static struct nand_bbt_descr largepage_memorybased = {
++      .options = 0,
++      .offs = 0,
++      .len = 2,
++      .pattern = scan_ff_pattern
++};
++
++static struct nand_bbt_descr smallpage_flashbased = {
++      .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
++      .offs = 5,
++      .len = 1,
++      .pattern = scan_ff_pattern
++};
++
++static struct nand_bbt_descr largepage_flashbased = {
++      .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
++      .offs = 0,
++      .len = 2,
++      .pattern = scan_ff_pattern
++};
++
++static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 };
++
++static struct nand_bbt_descr agand_flashbased = {
++      .options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
++      .offs = 0x20,
++      .len = 6,
++      .pattern = scan_agand_pattern
++};
++
++/* Generic flash bbt decriptors
++*/
++static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
++static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
++
++static struct nand_bbt_descr bbt_main_descr = {
++      .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
++              | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
++      .offs = 8,
++      .len = 4,
++      .veroffs = 12,
++      .maxblocks = 4,
++      .pattern = bbt_pattern
++};
++
++static struct nand_bbt_descr bbt_mirror_descr = {
++      .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
++              | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
++      .offs = 8,
++      .len = 4,
++      .veroffs = 12,
++      .maxblocks = 4,
++      .pattern = mirror_pattern
++};
++
++/**
++ * nand_default_bbt - [NAND Interface] Select a default bad block table for the device
++ * @mtd:      MTD device structure
++ *
++ * This function selects the default bad block table
++ * support for the device and calls the nand_scan_bbt function
++ *
++*/
++int nand_default_bbt (struct mtd_info *mtd)
++{
++      struct nand_chip *this = mtd->priv;
++
++      /* Default for AG-AND. We must use a flash based
++       * bad block table as the devices have factory marked
++       * _good_ blocks. Erasing those blocks leads to loss
++       * of the good / bad information, so we _must_ store
++       * this information in a good / bad table during
++       * startup
++      */
++      if (this->options & NAND_IS_AND) {
++              /* Use the default pattern descriptors */
++              if (!this->bbt_td) {
++                      this->bbt_td = &bbt_main_descr;
++                      this->bbt_md = &bbt_mirror_descr;
++              }
++              this->options |= NAND_USE_FLASH_BBT;
++              return nand_scan_bbt (mtd, &agand_flashbased);
++      }
++
++
++      /* Is a flash based bad block table requested ? */
++      if (this->options & NAND_USE_FLASH_BBT) {
++              /* Use the default pattern descriptors */
++              if (!this->bbt_td) {
++                      this->bbt_td = &bbt_main_descr;
++                      this->bbt_md = &bbt_mirror_descr;
++              }
++              if (!this->badblock_pattern) {
++                      this->badblock_pattern = (mtd->oobblock > 512) ?
++                              &largepage_flashbased : &smallpage_flashbased;
++              }
++      } else {
++              this->bbt_td = NULL;
++              this->bbt_md = NULL;
++              if (!this->badblock_pattern) {
++                      this->badblock_pattern = (mtd->oobblock > 512) ?
++                              &largepage_memorybased : &smallpage_memorybased;
++              }
++      }
++      return nand_scan_bbt (mtd, this->badblock_pattern);
++}
++
++/**
++ * nand_isbad_bbt - [NAND Interface] Check if a block is bad
++ * @mtd:      MTD device structure
++ * @offs:     offset in the device
++ * @allowbbt: allow access to bad block table region
++ *
++*/
++int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
++{
++      struct nand_chip *this = mtd->priv;
++      int block;
++      uint8_t res;
++
++      /* Get block number * 2 */
++      block = (int) (offs >> (this->bbt_erase_shift - 1));
++      res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
++
++      DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
++              (unsigned int)offs, block >> 1, res);
++
++      switch ((int)res) {
++      case 0x00:      return 0;
++      case 0x01:      return 1;
++      case 0x02:      return allowbbt ? 0 : 1;
++      }
++      return 1;
++}
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_ecc.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_ecc.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_ecc.c       1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_ecc.c       2006-08-04 16:38:14.000000000 +0200
+@@ -0,0 +1,246 @@
++/*
++ * This file contains an ECC algorithm from Toshiba that detects and
++ * corrects 1 bit errors in a 256 byte block of data.
++ * 
++ * Taken from drivers/mtd/nand/nand_ecc.c, modified for
++ * NAND flash boot on Etrax FS.
++ *
++ * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
++ *                         Toshiba America Electronics Components, Inc.
++ *
++ * $Id: nand_ecc.c,v 1.1 2006/08/04 14:38:14 ricardw Exp $
++ *
++ * This file is free software; you can redistribute it and/or modify it
++ * under the terms of the GNU General Public License as published by the
++ * Free Software Foundation; either version 2 or (at your option) any
++ * later version.
++ *
++ * This file is distributed in the hope that it will be useful, but WITHOUT
++ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
++ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
++ * for more details.
++ *
++ * You should have received a copy of the GNU General Public License along
++ * with this file; if not, write to the Free Software Foundation, Inc.,
++ * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
++ *
++ * As a special exception, if other files instantiate templates or use
++ * macros or inline functions from these files, or you compile these
++ * files and link them with other works to produce a work based on these
++ * files, these files do not by themselves cause the resulting work to be
++ * covered by the GNU General Public License. However the source code for
++ * these files must still be made available in accordance with section (3)
++ * of the GNU General Public License.
++ *
++ * This exception does not invalidate any other reasons why a work based on
++ * this file might be covered by the GNU General Public License.
++ */
++
++#include <linux/types.h>
++#if 0
++#include <linux/kernel.h>
++#include <linux/module.h>
++#endif
++#include "nand_ecc.h"
++
++/*
++ * Pre-calculated 256-way 1 byte column parity
++ */
++static const u_char nand_ecc_precalc_table[] = {
++      0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
++      0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
++      0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
++      0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
++      0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
++      0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
++      0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
++      0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
++      0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
++      0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
++      0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
++      0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
++      0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
++      0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
++      0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
++      0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
++};
++
++
++/**
++ * 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,
++      u_char *ecc_code)
++{
++      u_char a, b, i, tmp1, tmp2;
++
++      /* Initialize variables */
++      a = b = 0x80;
++      tmp1 = tmp2 = 0;
++
++      /* Calculate first ECC byte */
++      for (i = 0; i < 4; i++) {
++              if (reg3 & a)           /* LP15,13,11,9 --> ecc_code[0] */
++                      tmp1 |= b;
++              b >>= 1;
++              if (reg2 & a)           /* LP14,12,10,8 --> ecc_code[0] */
++                      tmp1 |= b;
++              b >>= 1;
++              a >>= 1;
++      }
++
++      /* Calculate second ECC byte */
++      b = 0x80;
++      for (i = 0; i < 4; i++) {
++              if (reg3 & a)           /* LP7,5,3,1 --> ecc_code[1] */
++                      tmp2 |= b;
++              b >>= 1;
++              if (reg2 & a)           /* LP6,4,2,0 --> ecc_code[1] */
++                      tmp2 |= b;
++              b >>= 1;
++              a >>= 1;
++      }
++
++      /* Store two of the ECC bytes */
++      ecc_code[0] = tmp1;
++      ecc_code[1] = tmp2;
++}
++
++/**
++ * 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
++ */
++int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
++{
++      u_char idx, reg1, reg2, reg3;
++      int j;
++
++      /* Initialize variables */
++      reg1 = reg2 = reg3 = 0;
++      ecc_code[0] = ecc_code[1] = ecc_code[2] = 0;
++
++      /* Build up column parity */
++      for(j = 0; j < 256; j++) {
++
++              /* Get CP0 - CP5 from table */
++              idx = nand_ecc_precalc_table[dat[j]];
++              reg1 ^= (idx & 0x3f);
++
++              /* All bit XOR = 1 ? */
++              if (idx & 0x40) {
++                      reg3 ^= (u_char) j;
++                      reg2 ^= ~((u_char) j);
++              }
++      }
++
++      /* Create non-inverted ECC code from line parity */
++      nand_trans_result(reg2, reg3, ecc_code);
++
++      /* Calculate final ECC code */
++      ecc_code[0] = ~ecc_code[0];
++      ecc_code[1] = ~ecc_code[1];
++      ecc_code[2] = ((~reg1) << 2) | 0x03;
++      return 0;
++}
++
++/**
++ * nand_correct_data - [NAND Interface] Detect and correct bit error(s)
++ * @mtd:      MTD block structure
++ * @dat:      raw data read from the chip
++ * @read_ecc: ECC from the chip
++ * @calc_ecc: the ECC calculated from raw data
++ *
++ * Detect and correct a 1 bit error for 256 byte block
++ */
++int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
++{
++      u_char a, b, c, d1, d2, d3, add, bit, i;
++
++      /* Do error detection */
++      d1 = calc_ecc[0] ^ read_ecc[0];
++      d2 = calc_ecc[1] ^ read_ecc[1];
++      d3 = calc_ecc[2] ^ read_ecc[2];
++
++      if ((d1 | d2 | d3) == 0) {
++              /* No errors */
++              return 0;
++      }
++      else {
++              a = (d1 ^ (d1 >> 1)) & 0x55;
++              b = (d2 ^ (d2 >> 1)) & 0x55;
++              c = (d3 ^ (d3 >> 1)) & 0x54;
++
++              /* Found and will correct single bit error in the data */
++              if ((a == 0x55) && (b == 0x55) && (c == 0x54)) {
++                      c = 0x80;
++                      add = 0;
++                      a = 0x80;
++                      for (i=0; i<4; i++) {
++                              if (d1 & c)
++                                      add |= a;
++                              c >>= 2;
++                              a >>= 1;
++                      }
++                      c = 0x80;
++                      for (i=0; i<4; i++) {
++                              if (d2 & c)
++                                      add |= a;
++                              c >>= 2;
++                              a >>= 1;
++                      }
++                      bit = 0;
++                      b = 0x04;
++                      c = 0x80;
++                      for (i=0; i<3; i++) {
++                              if (d3 & c)
++                                      bit |= b;
++                              c >>= 2;
++                              b >>= 1;
++                      }
++                      b = 0x01;
++                      a = dat[add];
++                      a ^= (b << bit);
++                      dat[add] = a;
++                      return 1;
++              }
++              else {
++                      i = 0;
++                      while (d1) {
++                              if (d1 & 0x01)
++                                      ++i;
++                              d1 >>= 1;
++                      }
++                      while (d2) {
++                              if (d2 & 0x01)
++                                      ++i;
++                              d2 >>= 1;
++                      }
++                      while (d3) {
++                              if (d3 & 0x01)
++                                      ++i;
++                              d3 >>= 1;
++                      }
++                      if (i == 1) {
++                              /* ECC Code Error Correction */
++                              read_ecc[0] = calc_ecc[0];
++                              read_ecc[1] = calc_ecc[1];
++                              read_ecc[2] = calc_ecc[2];
++                              return 2;
++                      }
++                      else {
++                              /* Uncorrectable Error */
++                              return -1;
++                      }
++              }
++      }
++
++      /* Should never happen */
++      return -1;
++}
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_ecc.h linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_ecc.h
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_ecc.h       1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_ecc.h       2006-08-04 16:38:14.000000000 +0200
+@@ -0,0 +1,30 @@
++/*
++ *  drivers/mtd/nand_ecc.h
++ *
++ *  Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
++ *
++ * $Id: nand_ecc.h,v 1.1 2006/08/04 14:38:14 ricardw 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.
++ *
++ * This file is the header for the ECC algorithm.
++ */
++
++#ifndef __MTD_NAND_ECC_H__
++#define __MTD_NAND_ECC_H__
++
++struct mtd_info;
++
++/*
++ * Calculate 3 byte ECC code for 256 byte block
++ */
++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(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
++
++#endif /* __MTD_NAND_ECC_H__ */
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_ids.c linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_ids.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/nand_ids.c       1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/nand_ids.c       2006-08-04 16:38:14.000000000 +0200
+@@ -0,0 +1,133 @@
++/*
++ *  drivers/mtd/nandids.c
++ *
++ *  Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
++ *
++ * $Id: nand_ids.c,v 1.1 2006/08/04 14:38:14 ricardw 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.
++ *
++ */
++#if 0
++#include <linux/module.h>
++#endif
++
++#include "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 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 1,8V 8-bit",      0x39, 512, 128, 0x4000, 0},
++      {"NAND 128MiB 3,3V 8-bit",      0x79, 512, 128, 0x4000, 0},
++      {"NAND 128MiB 1,8V 16-bit",     0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
++      {"NAND 128MiB 1,8V 16-bit",     0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16},
++      {"NAND 128MiB 3,3V 16-bit",     0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},
++      {"NAND 128MiB 3,3V 16-bit",     0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16},
++
++      {"NAND 256MiB 3,3V 8-bit",      0x71, 512, 256, 0x4000, 0},
++
++      /* These are the new chips with large page size. The pagesize
++      * and the erasesize is determined from the extended id bytes
++      */
++      /*512 Megabit */
++      {"NAND 64MiB 1,8V 8-bit",       0xA2, 0,  64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
++      {"NAND 64MiB 3,3V 8-bit",       0xF2, 0,  64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
++      {"NAND 64MiB 1,8V 16-bit",      0xB2, 0,  64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
++      {"NAND 64MiB 3,3V 16-bit",      0xC2, 0,  64, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
++
++      /* 1 Gigabit */
++      {"NAND 128MiB 1,8V 8-bit",      0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
++      {"NAND 128MiB 3,3V 8-bit",      0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
++      {"NAND 128MiB 1,8V 16-bit",     0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
++      {"NAND 128MiB 3,3V 16-bit",     0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
++
++      /* 2 Gigabit */
++      {"NAND 256MiB 1,8V 8-bit",      0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
++      {"NAND 256MiB 3,3V 8-bit",      0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
++      {"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 | BBT_AUTO_REFRESH},
++
++      {NULL,}
++};
++
++/*
++*     Manufacturer ID list
++*/
++struct nand_manufacturers nand_manuf_ids[] = {
++      {NAND_MFR_TOSHIBA, "Toshiba"},
++      {NAND_MFR_SAMSUNG, "Samsung"},
++      {NAND_MFR_FUJITSU, "Fujitsu"},
++      {NAND_MFR_NATIONAL, "National"},
++      {NAND_MFR_RENESAS, "Renesas"},
++      {NAND_MFR_STMICRO, "ST Micro"},
++        {NAND_MFR_HYNIX, "Hynix"},
++      {0x0, "Unknown"}
++};
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/rescue.ld linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/rescue.ld
+--- linux-2.6.19.2.old/arch/cris/arch-v32/boot/rescue/rescue.ld        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/boot/rescue/rescue.ld        2006-10-18 10:52:47.000000000 +0200
+@@ -1,20 +1,40 @@
+-MEMORY
++/*#OUTPUT_FORMAT(elf32-us-cris) */
++OUTPUT_ARCH (crisv32)
++
++MEMORY 
+       {
+-      flash : ORIGIN = 0x00000000,
+-              LENGTH = 0x00100000
++      bootblk : ORIGIN = 0x38000000,
++                LENGTH = 0x00004000
++      intmem  : ORIGIN = 0x38004000,
++                LENGTH = 0x00005000
+       }
+ SECTIONS
+ {
+       .text :
+       {
+-              stext = . ;
++              _stext = . ;
+               *(.text)
+-              etext = . ;
+-      } > flash
++              *(.rodata)
++              *(.rodata.*)
++              _etext = . ;
++      } > bootblk
+       .data :
+       {
+               *(.data)
+-              edata = . ;
+-      } > flash
++              _edata = . ;
++      } > bootblk
++      .bss :
++      {
++              _bss = . ;
++              *(.bss)
++              _end = ALIGN( 0x10 ) ;
++      } > intmem
++
++      /* Get rid of stuff from EXPORT_SYMBOL(foo). */
++      /DISCARD/ :
++      {
++              *(__ksymtab_strings)
++              *(__ksymtab)
++      }
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/Kconfig linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/Kconfig
+--- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/Kconfig      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/Kconfig      2007-01-29 16:14:16.000000000 +0100
+@@ -6,14 +6,6 @@
+         This option enables the ETRAX FS built-in 10/100Mbit Ethernet
+         controller.
+-config ETRAX_ETHERNET_HW_CSUM
+-      bool "Hardware accelerated ethernet checksum and scatter/gather"
+-      depends on ETRAX_ETHERNET
+-      depends on ETRAX_STREAMCOPROC
+-      default y
+-      help
+-        Hardware acceleration of checksumming and scatter/gather
+-
+ config ETRAX_ETHERNET_IFACE0
+       depends on ETRAX_ETHERNET
+       bool "Enable network interface 0"
+@@ -23,6 +15,52 @@
+       bool "Enable network interface 1 (uses DMA6 and DMA7)"
+ choice
++      prompt "Eth0 led group"
++      depends on ETRAX_ETHERNET_IFACE0
++      default ETRAX_ETH0_USE_LEDGRP0
++
++config ETRAX_ETH0_USE_LEDGRP0
++      bool "Use LED grp 0"
++      depends on ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO
++      help
++        Use LED grp 0 for eth0
++
++config ETRAX_ETH0_USE_LEDGRP1
++      bool "Use LED grp 1"
++      depends on ETRAX_NBR_LED_GRP_TWO
++      help
++        Use LED grp 1 for eth0
++
++config ETRAX_ETH0_USE_LEDGRPNULL
++      bool "Use no LEDs for eth0"
++      help
++        Use no LEDs for eth0
++endchoice
++
++choice
++      prompt "Eth1 led group"
++      depends on ETRAX_ETHERNET_IFACE1
++      default ETRAX_ETH1_USE_LEDGRP1
++
++config ETRAX_ETH1_USE_LEDGRP0
++      bool "Use LED grp 0"
++      depends on ETRAX_NBR_LED_GRP_ONE || ETRAX_NBR_LED_GRP_TWO
++      help
++        Use LED grp 0 for eth1
++
++config ETRAX_ETH1_USE_LEDGRP1
++      bool "Use LED grp 1"
++      depends on ETRAX_NBR_LED_GRP_TWO
++      help
++        Use LED grp 1 for eth1
++
++config ETRAX_ETH1_USE_LEDGRPNULL
++      bool "Use no LEDs for eth1"
++      help
++        Use no LEDs for eth1
++endchoice
++
++choice
+       prompt "Network LED behavior"
+       depends on ETRAX_ETHERNET
+       default ETRAX_NETWORK_LED_ON_WHEN_ACTIVITY
+@@ -56,10 +94,25 @@
+ config ETRAXFS_SERIAL
+       bool "Serial-port support"
+       depends on ETRAX_ARCH_V32
++      select SERIAL_CORE
++      select SERIAL_CORE_CONSOLE
+       help
+         Enables the ETRAX FS serial driver for ser0 (ttyS0)
+         You probably want this enabled.
++config ETRAX_RS485
++      bool "RS-485 support"
++      depends on ETRAXFS_SERIAL
++      help
++        Enables support for RS-485 serial communication.
++
++config ETRAX_RS485_DISABLE_RECEIVER
++      bool "Disable serial receiver"
++      depends on ETRAX_RS485
++      help
++        It is necessary to disable the serial receiver to avoid serial
++        loopback.  Not all products are able to do this in software only.
++
+ config ETRAX_SERIAL_PORT0
+       bool "Serial port 0 enabled"
+       depends on ETRAXFS_SERIAL
+@@ -70,6 +123,31 @@
+         ser0 can use dma4 or dma6 for output and dma5 or dma7 for input.
+ choice
++      prompt "Ser0 default port type "
++      depends on ETRAX_SERIAL_PORT0
++      default ETRAX_SERIAL_PORT0_TYPE_232
++      help
++        Type of serial port.
++
++config ETRAX_SERIAL_PORT0_TYPE_232
++      bool "Ser0 is a RS-232 port"
++      help
++        Configure serial port 0 to be a RS-232 port.
++
++config ETRAX_SERIAL_PORT0_TYPE_485HD
++      bool "Ser0 is a half duplex RS-485 port"
++      depends on ETRAX_RS485
++      help
++        Configure serial port 0 to be a half duplex (two wires) RS-485 port.
++
++config ETRAX_SERIAL_PORT0_TYPE_485FD
++      bool "Ser0 is a full duplex RS-485 port"
++      depends on ETRAX_RS485
++      help
++        Configure serial port 0 to be a full duplex (four wires) RS-485 port.
++endchoice
++
++choice
+       prompt "Ser0 DMA in channel "
+       depends on ETRAX_SERIAL_PORT0
+       default ETRAX_SERIAL_PORT0_NO_DMA_IN
+@@ -139,6 +217,31 @@
+         Enables the ETRAX FS serial driver for ser1 (ttyS1).
+ choice
++      prompt "Ser1 default port type"
++      depends on ETRAX_SERIAL_PORT1
++      default ETRAX_SERIAL_PORT1_TYPE_232
++      help
++        Type of serial port.
++
++config ETRAX_SERIAL_PORT1_TYPE_232
++      bool "Ser1 is a RS-232 port"
++      help
++        Configure serial port 1 to be a RS-232 port.
++
++config ETRAX_SERIAL_PORT1_TYPE_485HD
++      bool "Ser1 is a half duplex RS-485 port"
++      depends on ETRAX_RS485
++      help
++        Configure serial port 1 to be a half duplex (two wires) RS-485 port.
++
++config ETRAX_SERIAL_PORT1_TYPE_485FD
++      bool "Ser1 is a full duplex RS-485 port"
++      depends on ETRAX_RS485
++      help
++        Configure serial port 1 to be a full duplex (four wires) RS-485 port.
++endchoice
++
++choice
+       prompt "Ser1 DMA in channel "
+       depends on ETRAX_SERIAL_PORT1
+       default ETRAX_SERIAL_PORT1_NO_DMA_IN
+@@ -210,6 +313,31 @@
+         Enables the ETRAX FS serial driver for ser2 (ttyS2).
+ choice
++      prompt "Ser2 default port type"
++      depends on ETRAX_SERIAL_PORT2
++      default ETRAX_SERIAL_PORT2_TYPE_232
++      help
++        What DMA channel to use for ser2
++
++config ETRAX_SERIAL_PORT2_TYPE_232
++      bool "Ser2 is a RS-232 port"
++      help
++        Configure serial port 2 to be a RS-232 port.
++
++config ETRAX_SERIAL_PORT2_TYPE_485HD
++      bool "Ser2 is a half duplex RS-485 port"
++      depends on ETRAX_RS485
++      help
++        Configure serial port 2 to be a half duplex (two wires) RS-485 port.
++
++config ETRAX_SERIAL_PORT2_TYPE_485FD
++      bool "Ser2 is a full duplex RS-485 port"
++      depends on ETRAX_RS485
++      help
++        Configure serial port 2 to be a full duplex (four wires) RS-485 port.
++endchoice
++
++choice
+       prompt "Ser2 DMA in channel "
+       depends on ETRAX_SERIAL_PORT2
+       default ETRAX_SERIAL_PORT2_NO_DMA_IN
+@@ -279,6 +407,31 @@
+         Enables the ETRAX FS serial driver for ser3 (ttyS3).
+ choice
++      prompt "Ser3 default port type"
++      depends on ETRAX_SERIAL_PORT3
++      default ETRAX_SERIAL_PORT3_TYPE_232
++      help
++        What DMA channel to use for ser3.
++
++config ETRAX_SERIAL_PORT3_TYPE_232
++      bool "Ser3 is a RS-232 port"
++      help
++        Configure serial port 3 to be a RS-232 port.
++
++config ETRAX_SERIAL_PORT3_TYPE_485HD
++      bool "Ser3 is a half duplex RS-485 port"
++      depends on ETRAX_RS485
++      help
++        Configure serial port 3 to be a half duplex (two wires) RS-485 port.
++
++config ETRAX_SERIAL_PORT3_TYPE_485FD
++      bool "Ser3 is a full duplex RS-485 port"
++      depends on ETRAX_RS485
++      help
++        Configure serial port 3 to be a full duplex (four wires) RS-485 port.
++endchoice
++
++choice
+       prompt "Ser3 DMA in channel "
+       depends on ETRAX_SERIAL_PORT3
+       default ETRAX_SERIAL_PORT3_NO_DMA_IN
+@@ -341,38 +494,6 @@
+       string "Ser 3 CD bit (empty = not used)"
+       depends on ETRAX_SERIAL_PORT3
+-config ETRAX_RS485
+-      bool "RS-485 support"
+-      depends on ETRAX_SERIAL
+-      help
+-        Enables support for RS-485 serial communication.  For a primer on
+-        RS-485, see <http://www.hw.cz/english/docs/rs485/rs485.html>.
+-
+-config ETRAX_RS485_DISABLE_RECEIVER
+-      bool "Disable serial receiver"
+-      depends on ETRAX_RS485
+-      help
+-        It is necessary to disable the serial receiver to avoid serial
+-        loopback.  Not all products are able to do this in software only.
+-        Axis 2400/2401 must disable receiver.
+-
+-config ETRAX_AXISFLASHMAP
+-      bool "Axis flash-map support"
+-      depends on ETRAX_ARCH_V32
+-      select MTD
+-      select MTD_CFI
+-      select MTD_CFI_AMDSTD
+-      select MTD_OBSOLETE_CHIPS
+-      select MTD_AMDSTD
+-      select MTD_CHAR
+-      select MTD_BLOCK
+-      select MTD_PARTITIONS
+-      select MTD_CONCAT
+-      select MTD_COMPLEX_MAPPINGS
+-      help
+-        This option enables MTD mapping of flash devices.  Needed to use
+-        flash memories.  If unsure, say Y.
+-
+ config ETRAX_SYNCHRONOUS_SERIAL
+       bool "Synchronous serial-port support"
+       depends on ETRAX_ARCH_V32
+@@ -405,6 +526,31 @@
+            A synchronous serial port can run in manual or DMA mode.
+            Selecting this option will make it run in DMA mode.
++config ETRAX_AXISFLASHMAP
++      bool "Axis flash-map support"
++      depends on ETRAX_ARCH_V32
++      select MTD
++      select MTD_CFI
++      select MTD_CFI_AMDSTD
++      select MTD_JEDECPROBE
++      select MTD_CHAR
++      select MTD_BLOCK
++      select MTD_PARTITIONS
++      select MTD_CONCAT
++      select MTD_COMPLEX_MAPPINGS
++      help
++        This option enables MTD mapping of flash devices.  Needed to use
++        flash memories.  If unsure, say Y.
++
++config ETRAX_AXISFLASHMAP_MTD0WHOLE
++      bool "MTD0 is whole boot flash device"
++      depends on ETRAX_AXISFLASHMAP
++      default N
++      help
++        When this option is not set, mtd0 refers to the first partition
++        on the boot flash device. When set, mtd0 refers to the whole
++        device, with mtd1 referring to the first partition etc.
++
+ config ETRAX_PTABLE_SECTOR
+       int "Byte-offset of partition table sector"
+       depends on ETRAX_AXISFLASHMAP
+@@ -425,11 +571,19 @@
+         This option enables MTD mapping of NAND flash devices.  Needed to use
+         NAND flash memories.  If unsure, say Y.
++config ETRAX_NANDBOOT
++      bool "Boot from NAND flash"
++      depends on ETRAX_NANDFLASH
++      help
++        This options enables booting from NAND flash devices. 
++        Say Y if your boot code, kernel and root file system is in 
++        NAND flash. Say N if they are in NOR flash.
++
+ config ETRAX_I2C
+       bool "I2C driver"
+       depends on ETRAX_ARCH_V32
+       help
+-        This option enabled the I2C driver used by e.g. the RTC driver.
++        This option enables the I2C driver used by e.g. the RTC driver.
+ config ETRAX_I2C_DATA_PORT
+       string "I2C data pin"
+@@ -476,18 +630,19 @@
+         Remember that you need to setup the port directions appropriately in
+         the General configuration.
+-config ETRAX_PA_BUTTON_BITMASK
+-      hex "PA-buttons bitmask"
++config ETRAX_VIRTUAL_GPIO
++      bool "Virtual GPIO support"
+       depends on ETRAX_GPIO
+-      default "0x02"
+       help
+-        This is a bitmask (8 bits) with information about what bits on PA
+-        that are used for buttons.
+-        Most products has a so called TEST button on PA1, if that is true
+-        use 0x02 here.
+-        Use 00 if there are no buttons on PA.
+-        If the bitmask is <> 00 a button driver will be included in the gpio
+-        driver. ETRAX general I/O support must be enabled.
++        Enables the virtual Etrax general port device (major 120, minor 6).
++        It uses an I/O expander for the I2C-bus.
++
++config ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN
++      int "Virtual GPIO interrupt pin on PA pin"
++      range 0 7
++      depends on ETRAX_VIRTUAL_GPIO
++      help
++        The pin to use on PA for virtual gpio interrupt.
+ config ETRAX_PA_CHANGEABLE_DIR
+       hex "PA user changeable dir mask"
+@@ -584,6 +739,25 @@
+         that a user can change the value on using ioctl's.
+         Bit set = changeable.
++config ETRAX_PV_CHANGEABLE_DIR
++      hex "PV user changeable dir mask"
++      depends on ETRAX_VIRTUAL_GPIO
++      default "0x0000"
++      help
++        This is a bitmask (16 bits) with information of what bits in PV
++        that a user can change direction on using ioctl's.
++        Bit set = changeable.
++        You probably want 0x0000 here, but it depends on your hardware.
++
++config ETRAX_PV_CHANGEABLE_BITS
++      hex "PV user changeable bits mask"
++      depends on ETRAX_VIRTUAL_GPIO
++      default "0x0000"
++      help
++        This is a bitmask (16 bits) with information of what bits in PV
++        that a user can change the value on using ioctl's.
++        Bit set = changeable.
++
+ config ETRAX_IDE
+       bool "ATA/IDE support"
+       depends on ETRAX_ARCH_V32
+@@ -603,11 +777,11 @@
+         select HOTPLUG
+         select PCCARD_NONSTATIC
+         help
+-       Enabled the ETRAX Carbus driver.
++       Enabled the ETRAX Carbus driver. 
+ config PCI
+        bool
+-       depends on ETRAX_CARDBUS
++       depends on ETRAX_CARDBUS     
+        default y
+ config ETRAX_IOP_FW_LOAD
+@@ -623,3 +797,175 @@
+       help
+         This option enables a driver for the stream co-processor
+         for cryptographic operations.
++
++source drivers/mmc/Kconfig
++
++config ETRAX_SPI_MMC
++# Make this one of several "choices" (possible simultaneously but
++# suggested uniquely) when an IOP driver emerges for "real" MMC/SD
++# protocol support.
++      tristate
++      depends on MMC
++      default MMC
++      select SPI
++      select MMC_SPI
++      select ETRAX_SPI_MMC_BOARD
++
++# For the parts that can't be a module (due to restrictions in
++# framework elsewhere).
++config ETRAX_SPI_MMC_BOARD
++       boolean
++       default n
++
++# While the board info is MMC_SPI only, the drivers are written to be
++# independent of MMC_SPI, so we'll keep SPI non-dependent on the
++# MMC_SPI config choices (well, except for a single depends-on-line
++# for the board-info file until a separate non-MMC SPI board file
++# emerges).
++# FIXME: When that happens, we'll need to be able to ask for and
++# configure non-MMC SPI ports together with MMC_SPI ports (if multiple
++# SPI ports are enabled).
++
++config ETRAX_SPI_SSER0
++      tristate "SPI using synchronous serial port 0 (sser0)"
++      depends on ETRAX_SPI_MMC
++      default m if MMC_SPI=m
++      default y if MMC_SPI=y
++      default y if MMC_SPI=n
++      select SPI_ETRAX_SSER
++      help
++        Say Y for an MMC/SD socket connected to synchronous serial port 0,
++        or for devices using the SPI protocol on that port.  Say m if you
++        want to build it as a module, which will be named spi_crisv32_sser.
++        (You need to select MMC separately.)
++
++config ETRAX_SPI_SSER0_DMA
++      bool "DMA for SPI on sser0 enabled"
++      depends on ETRAX_SPI_SSER0
++      depends on !ETRAX_SERIAL_PORT1_DMA4_OUT && !ETRAX_SERIAL_PORT1_DMA5_IN
++      default y
++      help
++        Say Y if using DMA (dma4/dma5) for SPI on synchronous serial port 0.
++
++config ETRAX_SPI_MMC_CD_SSER0_PIN
++      string "MMC/SD card detect pin for SPI on sser0"
++      depends on ETRAX_SPI_SSER0 && MMC_SPI
++      default "pd11"
++      help
++        The pin to use for SD/MMC card detect.  This pin should be pulled up
++        and grounded when a card is present.  If defined as " " (space), no
++        pin is selected.  A card must then always be inserted for proper
++        action.
++
++config ETRAX_SPI_MMC_WP_SSER0_PIN
++      string "MMC/SD card write-protect pin for SPI on sser0"
++      depends on ETRAX_SPI_SSER0 && MMC_SPI
++      default "pd10"
++      help
++        The pin to use for the SD/MMC write-protect signal for a memory
++        card.  If defined as " " (space), the card is considered writable.
++
++config ETRAX_SPI_SSER1
++      tristate "SPI using synchronous serial port 1 (sser1)"
++      depends on ETRAX_SPI_MMC
++      default m if MMC_SPI=m && ETRAX_SPI_SSER0=n
++      default y if MMC_SPI=y && ETRAX_SPI_SSER0=n
++      default y if MMC_SPI=n && ETRAX_SPI_SSER0=n
++      select SPI_ETRAX_SSER
++      help
++        Say Y for an MMC/SD socket connected to synchronous serial port 1,
++        or for devices using the SPI protocol on that port.  Say m if you
++        want to build it as a module, which will be named spi_crisv32_sser.
++        (You need to select MMC separately.)
++
++config ETRAX_SPI_SSER1_DMA
++      bool "DMA for SPI on sser1 enabled"
++      depends on ETRAX_SPI_SSER1 && !ETRAX_ETHERNET_IFACE1
++      depends on !ETRAX_SERIAL_PORT0_DMA6_OUT && !ETRAX_SERIAL_PORT0_DMA7_IN
++      default y
++      help
++        Say Y if using DMA (dma6/dma7) for SPI on synchronous serial port 1.
++
++config ETRAX_SPI_MMC_CD_SSER1_PIN
++      string "MMC/SD card detect pin for SPI on sser1"
++      depends on ETRAX_SPI_SSER1 && MMC_SPI 
++      default "pd12"
++      help
++        The pin to use for SD/MMC card detect.  This pin should be pulled up
++        and grounded when a card is present.  If defined as " " (space), no
++        pin is selected.  A card must then always be inserted for proper
++        action.
++
++config ETRAX_SPI_MMC_WP_SSER1_PIN
++      string "MMC/SD card write-protect pin for SPI on sser1"
++      depends on ETRAX_SPI_SSER1 && MMC_SPI
++      default "pd9"
++      help
++        The pin to use for the SD/MMC write-protect signal for a memory
++        card.  If defined as " " (space), the card is considered writable.
++
++config ETRAX_SPI_GPIO
++      tristate "Bitbanged SPI using gpio pins"
++      depends on ETRAX_SPI_MMC
++      select SPI_ETRAX_GPIO
++      default m if MMC_SPI=m && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n
++      default y if MMC_SPI=y && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n
++      default y if MMC_SPI=n && ETRAX_SPI_SSER0=n && ETRAX_SPI_SSER1=n
++      help
++        Say Y for an MMC/SD socket connected to general I/O pins (but not
++        a complete synchronous serial ports), or for devices using the SPI
++        protocol on general I/O pins.  Slow and slows down the system.
++        Say m to build it as a module, which will be called spi_crisv32_gpio.
++        (You need to select MMC separately.)
++
++# The default match that of sser0, only because that's how it was tested.
++config ETRAX_SPI_CS_PIN
++      string "SPI chip select pin"
++      depends on ETRAX_SPI_GPIO
++      default "pc3"
++      help
++        The pin to use for SPI chip select.
++
++config ETRAX_SPI_CLK_PIN
++      string "SPI clock pin"
++      depends on ETRAX_SPI_GPIO
++      default "pc1"
++      help
++        The pin to use for the SPI clock.
++
++config ETRAX_SPI_DATAIN_PIN
++      string "SPI MISO (data in) pin"
++      depends on ETRAX_SPI_GPIO
++      default "pc16"
++      help
++        The pin to use for SPI data in from the device.
++
++config ETRAX_SPI_DATAOUT_PIN
++      string "SPI MOSI (data out) pin"
++      depends on ETRAX_SPI_GPIO
++      default "pc0"
++      help
++        The pin to use for SPI data out to the device.
++
++config ETRAX_SPI_MMC_CD_GPIO_PIN
++      string "MMC/SD card detect pin for SPI using gpio (space for none)"
++      depends on ETRAX_SPI_GPIO && MMC_SPI
++      default "pd11"
++      help
++        The pin to use for SD/MMC card detect.  This pin should be pulled up
++        and grounded when a card is present.  If defined as " " (space), no
++        pin is selected.  A card must then always be inserted for proper
++        action.
++
++config ETRAX_SPI_MMC_WP_GPIO_PIN
++      string "MMC/SD card write-protect pin for SPI using gpio (space for none)"
++      depends on ETRAX_SPI_GPIO && MMC_SPI
++      default "pd10"
++      help
++        The pin to use for the SD/MMC write-protect signal for a memory
++        card.  If defined as " " (space), the card is considered writable.
++
++# Avoid choices causing non-working configs by conditionalizing the inclusion.
++if ETRAX_SPI_MMC
++source drivers/spi/Kconfig
++endif
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/Makefile linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/Makefile
+--- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/Makefile     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/Makefile     2007-01-29 16:14:16.000000000 +0100
+@@ -11,3 +11,4 @@
+ obj-$(CONFIG_ETRAX_I2C)                       += i2c.o
+ obj-$(CONFIG_ETRAX_SYNCHRONOUS_SERIAL)        += sync_serial.o
+ obj-$(CONFIG_PCI)                     += pci/
++obj-$(CONFIG_ETRAX_SPI_MMC_BOARD)     += board_mmcspi.o
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/axisflashmap.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/axisflashmap.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/axisflashmap.c       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/axisflashmap.c       2007-02-06 17:37:50.000000000 +0100
+@@ -11,7 +11,7 @@
+  * partition split defined below.
+  *
+  * Copy of os/lx25/arch/cris/arch-v10/drivers/axisflashmap.c 1.5
+- * with minor changes.
++ * with quite a few changes now.
+  *
+  */
+@@ -27,6 +27,8 @@
+ #include <linux/mtd/mtdram.h>
+ #include <linux/mtd/partitions.h>
++#include <linux/cramfs_fs.h>
++
+ #include <asm/arch/hwregs/config_defs.h>
+ #include <asm/axisflashmap.h>
+ #include <asm/mmu.h>
+@@ -37,16 +39,24 @@
+ #define FLASH_UNCACHED_ADDR  KSEG_E
+ #define FLASH_CACHED_ADDR    KSEG_F
++#define PAGESIZE (512)
++
+ #if CONFIG_ETRAX_FLASH_BUSWIDTH==1
+ #define flash_data __u8
+ #elif CONFIG_ETRAX_FLASH_BUSWIDTH==2
+ #define flash_data __u16
+ #elif CONFIG_ETRAX_FLASH_BUSWIDTH==4
+-#define flash_data __u16
++#define flash_data __u32
+ #endif
+ /* From head.S */
+-extern unsigned long romfs_start, romfs_length, romfs_in_flash;
++extern unsigned long romfs_in_flash; /* 1 when romfs_start, _length in flash */
++extern unsigned long romfs_start, romfs_length;
++extern unsigned long nand_boot; /* 1 when booted from nand flash */
++
++struct partition_name {
++      char name[6];
++};
+ /* The master mtd for the entire flash. */
+ struct mtd_info* axisflash_mtd = NULL;
+@@ -112,32 +122,20 @@
+       .map_priv_1 = FLASH_UNCACHED_ADDR + MEM_CSE0_SIZE
+ };
+-/* If no partition-table was found, we use this default-set. */
+-#define MAX_PARTITIONS         7
+-#define NUM_DEFAULT_PARTITIONS 3
++#define MAX_PARTITIONS                        7
++#ifdef CONFIG_ETRAX_NANDBOOT
++#define NUM_DEFAULT_PARTITIONS        4
++#define DEFAULT_ROOTFS_PARTITION_NO   2
++#define DEFAULT_MEDIA_SIZE              0x2000000 /* 32 megs */
++#else
++#define NUM_DEFAULT_PARTITIONS                3
++#define DEFAULT_ROOTFS_PARTITION_NO   (-1)
++#define DEFAULT_MEDIA_SIZE              0x800000 /* 8 megs */
++#endif
+-/*
+- * Default flash size is 2MB. CONFIG_ETRAX_PTABLE_SECTOR is most likely the
+- * size of one flash block and "filesystem"-partition needs 5 blocks to be able
+- * to use JFFS.
+- */
+-static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = {
+-      {
+-              .name = "boot firmware",
+-              .size = CONFIG_ETRAX_PTABLE_SECTOR,
+-              .offset = 0
+-      },
+-      {
+-              .name = "kernel",
+-              .size = 0x200000 - (6 * CONFIG_ETRAX_PTABLE_SECTOR),
+-              .offset = CONFIG_ETRAX_PTABLE_SECTOR
+-      },
+-      {
+-              .name = "filesystem",
+-              .size = 5 * CONFIG_ETRAX_PTABLE_SECTOR,
+-              .offset = 0x200000 - (5 * CONFIG_ETRAX_PTABLE_SECTOR)
+-      }
+-};
++#if (MAX_PARTITIONS < NUM_DEFAULT_PARTITIONS)
++#error MAX_PARTITIONS must be >= than NUM_DEFAULT_PARTITIONS
++#endif
+ /* Initialize the ones normally used. */
+ static struct mtd_partition axis_partitions[MAX_PARTITIONS] = {
+@@ -178,6 +176,56 @@
+       },
+ };
++
++/* If no partition-table was found, we use this default-set.
++ * Default flash size is 8MB (NOR). CONFIG_ETRAX_PTABLE_SECTOR is most 
++ * likely the size of one flash block and "filesystem"-partition needs 
++ * to be >=5 blocks to be able to use JFFS.
++ */
++static struct mtd_partition axis_default_partitions[NUM_DEFAULT_PARTITIONS] = {
++      {
++              .name = "boot firmware",
++              .size = CONFIG_ETRAX_PTABLE_SECTOR,
++              .offset = 0
++      },
++      {
++              .name = "kernel",
++              .size = 10 * CONFIG_ETRAX_PTABLE_SECTOR,
++              .offset = CONFIG_ETRAX_PTABLE_SECTOR
++      },
++#define FILESYSTEM_SECTOR (11 * CONFIG_ETRAX_PTABLE_SECTOR)
++#ifdef CONFIG_ETRAX_NANDBOOT
++      {
++              .name = "rootfs",
++              .size = 10 * CONFIG_ETRAX_PTABLE_SECTOR,
++              .offset = FILESYSTEM_SECTOR
++      },
++#undef FILESYSTEM_SECTOR
++#define FILESYSTEM_SECTOR (21 * CONFIG_ETRAX_PTABLE_SECTOR)
++#endif
++      {
++              .name = "rwfs",
++              .size = DEFAULT_MEDIA_SIZE - FILESYSTEM_SECTOR,
++              .offset = FILESYSTEM_SECTOR
++      }
++};
++
++#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE
++/* Main flash device */
++static struct mtd_partition main_partition = {
++      .name = "main",
++      .size = 0,
++      .offset = 0
++};
++#endif
++
++/* Auxilliary partition if we find another flash */
++static struct mtd_partition aux_partition = {
++      .name = "aux",
++      .size = 0,
++      .offset = 0
++};
++
+ /*
+  * Probe a chip select for AMD-compatible (JEDEC) or CFI-compatible flash
+  * chips in that order (because the amd_flash-driver is faster).
+@@ -186,23 +234,23 @@
+ {
+       struct mtd_info *mtd_cs = NULL;
+-      printk(KERN_INFO
++      printk(KERN_INFO 
+              "%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n",
+              map_cs->name, map_cs->size, map_cs->map_priv_1);
+-#ifdef CONFIG_MTD_AMDSTD
+-      mtd_cs = do_map_probe("amd_flash", map_cs);
+-#endif
+ #ifdef CONFIG_MTD_CFI
++      mtd_cs = do_map_probe("cfi_probe", map_cs);
++#endif
++#ifdef CONFIG_MTD_JEDECPROBE
+       if (!mtd_cs) {
+-              mtd_cs = do_map_probe("cfi_probe", map_cs);
++              mtd_cs = do_map_probe("jedec_probe", map_cs);
+       }
+ #endif
+       return mtd_cs;
+ }
+-/*
++/* 
+  * Probe each chip select individually for flash chips. If there are chips on
+  * both cse0 and cse1, the mtd_info structs will be concatenated to one struct
+  * so that MTD partitions can cross chip boundries.
+@@ -217,22 +265,17 @@
+ {
+       struct mtd_info *mtd_cse0;
+       struct mtd_info *mtd_cse1;
+-      struct mtd_info *mtd_nand = NULL;
+       struct mtd_info *mtd_total;
+-      struct mtd_info *mtds[3];
++      struct mtd_info *mtds[2];
+       int count = 0;
+       if ((mtd_cse0 = probe_cs(&map_cse0)) != NULL)
+               mtds[count++] = mtd_cse0;
+       if ((mtd_cse1 = probe_cs(&map_cse1)) != NULL)
+               mtds[count++] = mtd_cse1;
++      
+-#ifdef CONFIG_ETRAX_NANDFLASH
+-      if ((mtd_nand = crisv32_nand_flash_probe()) != NULL)
+-              mtds[count++] = mtd_nand;
+-#endif
+-
+-      if (!mtd_cse0 && !mtd_cse1 && !mtd_nand) {
++      if (!mtd_cse0 && !mtd_cse1) {
+               /* No chip found. */
+               return NULL;
+       }
+@@ -248,7 +291,7 @@
+                */
+               mtd_total = mtd_concat_create(mtds,
+                                             count,
+-                                            "cse0+cse1+nand");
++                                            "cse0+cse1");
+ #else
+               printk(KERN_ERR "%s and %s: Cannot concatenate due to kernel "
+                      "(mis)configuration!\n", map_cse0.name, map_cse1.name);
+@@ -260,57 +303,155 @@
+                       /* The best we can do now is to only use what we found
+                        * at cse0.
+-                       */
++                       */ 
+                       mtd_total = mtd_cse0;
+                       map_destroy(mtd_cse1);
+               }
+       } else {
+-              mtd_total = mtd_cse0? mtd_cse0 : mtd_cse1 ? mtd_cse1 : mtd_nand;
+-
++              mtd_total = mtd_cse0 ? mtd_cse0 : mtd_cse1;
++              
+       }
+       return mtd_total;
+ }
+-extern unsigned long crisv32_nand_boot;
+-extern unsigned long crisv32_nand_cramfs_offset;
+-
+ /*
+  * Probe the flash chip(s) and, if it succeeds, read the partition-table
+  * and register the partitions with MTD.
+  */
+ static int __init init_axis_flash(void)
+ {
+-      struct mtd_info *mymtd;
++      struct mtd_info *main_mtd;
++      struct mtd_info *aux_mtd = NULL;
+       int err = 0;
+       int pidx = 0;
+       struct partitiontable_head *ptable_head = NULL;
+       struct partitiontable_entry *ptable;
+-      int use_default_ptable = 1; /* Until proven otherwise. */
+-      const char *pmsg = KERN_INFO "  /dev/flash%d at 0x%08x, size 0x%08x\n";
+-      static char page[512];
++      int ptable_ok = 0;
++      static char page[PAGESIZE];
+       size_t len;
++      int ram_rootfs_partition = -1; /* -1 => no RAM rootfs partition */
++      int part;
++
++      /* We need a root fs. If it resides in RAM, we need to use an
++       * MTDRAM device, so it must be enabled in the kernel config,
++       * but its size must be configured as 0 so as not to conflict
++       * with our usage.
++       */
++#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0)
++      if (!romfs_in_flash && !nand_boot) {
++              printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM "
++                     "device; configure CONFIG_MTD_MTDRAM with size = 0!\n");
++              panic("This kernel cannot boot from RAM!\n");
++      }
++#endif
+ #ifndef CONFIG_ETRAXFS_SIM
+-      mymtd = flash_probe();
+-      mymtd->read(mymtd, CONFIG_ETRAX_PTABLE_SECTOR, 512, &len, page);
+-      ptable_head = (struct partitiontable_head *)(page + PARTITION_TABLE_OFFSET);
++      main_mtd = flash_probe();
++      if (main_mtd)
++              printk(KERN_INFO "%s: 0x%08x bytes of NOR flash memory.\n",
++                     main_mtd->name, main_mtd->size);
++
++#ifdef CONFIG_ETRAX_NANDFLASH
++      aux_mtd = crisv32_nand_flash_probe();
++      if (aux_mtd)
++              printk(KERN_INFO "%s: 0x%08x bytes of NAND flash memory.\n",
++                      aux_mtd->name, aux_mtd->size);
+-      if (!mymtd) {
++#ifdef CONFIG_ETRAX_NANDBOOT
++      {
++              struct mtd_info *tmp_mtd;
++
++              printk(KERN_INFO "axisflashmap: Set to boot from NAND flash, "
++                     "making NAND flash primary device.\n");
++              tmp_mtd = main_mtd;
++              main_mtd = aux_mtd;
++              aux_mtd = tmp_mtd;
++      }
++#endif /* CONFIG_ETRAX_NANDBOOT */
++#endif /* CONFIG_ETRAX_NANDFLASH */
++
++      if (!main_mtd && !aux_mtd) {
+               /* There's no reason to use this module if no flash chip can
+                * be identified. Make sure that's understood.
+                */
+               printk(KERN_INFO "axisflashmap: Found no flash chip.\n");
+-      } else {
+-              printk(KERN_INFO "%s: 0x%08x bytes of flash memory.\n",
+-                     mymtd->name, mymtd->size);
+-              axisflash_mtd = mymtd;
+       }
+-      if (mymtd) {
+-              mymtd->owner = THIS_MODULE;
++#if 0 /* Dump flash memory so we can see what is going on */
++      if (main_mtd) {
++              int sectoraddr, i;
++              for (sectoraddr = 0; sectoraddr < 2*65536+4096; sectoraddr += PAGESIZE) {
++                      main_mtd->read(main_mtd, sectoraddr, PAGESIZE, &len, page);
++                      printk(KERN_INFO
++                             "Sector at %d (length %d):\n",
++                             sectoraddr, len);
++                      for (i = 0; i < PAGESIZE; i += 16) {
++                              printk(KERN_INFO
++                                     "%02x %02x %02x %02x %02x %02x %02x %02x "
++                                     "%02x %02x %02x %02x %02x %02x %02x %02x\n",
++                                     page[i] & 255, page[i+1] & 255, 
++                                     page[i+2] & 255, page[i+3] & 255, 
++                                     page[i+4] & 255, page[i+5] & 255, 
++                                     page[i+6] & 255, page[i+7] & 255,
++                                     page[i+8] & 255, page[i+9] & 255, 
++                                     page[i+10] & 255, page[i+11] & 255, 
++                                     page[i+12] & 255, page[i+13] & 255, 
++                                     page[i+14] & 255, page[i+15] & 255);
++                      }
++                      
++              }
++      }
++#endif
++
++      if (main_mtd) {
++              main_mtd->owner = THIS_MODULE;
++              axisflash_mtd = main_mtd;
++
++              loff_t ptable_sector = CONFIG_ETRAX_PTABLE_SECTOR;
++
++              pidx++;  /* First partition (rescue) is always set to the default. */
++#ifdef CONFIG_ETRAX_NANDBOOT
++              /* We know where the partition table should be located,
++               * it will be in first good block after that.
++               */
++              int blockstat;
++              do {
++                      blockstat = main_mtd->block_isbad(main_mtd, ptable_sector);
++                      if (blockstat < 0)
++                              ptable_sector = 0; /* read error */
++                      else if (blockstat)
++                              ptable_sector += main_mtd->erasesize;
++              } while (blockstat && ptable_sector);
++#endif
++              if (ptable_sector) {
++                      main_mtd->read(main_mtd, ptable_sector, PAGESIZE, &len, page);
++                      ptable_head = &((struct partitiontable *) page)->head;
++              }
++
++#if 0 /* Dump partition table so we can see what is going on */
++              printk(KERN_INFO
++                     "axisflashmap: flash read %d bytes at 0x%08x, data: "
++                     "%02x %02x %02x %02x %02x %02x %02x %02x\n",
++                     len, CONFIG_ETRAX_PTABLE_SECTOR, 
++                     page[0] & 255, page[1] & 255, 
++                     page[2] & 255, page[3] & 255, 
++                     page[4] & 255, page[5] & 255, 
++                     page[6] & 255, page[7] & 255);
++              printk(KERN_INFO
++                     "axisflashmap: partition table offset %d, data: "
++                     "%02x %02x %02x %02x %02x %02x %02x %02x\n",
++                     PARTITION_TABLE_OFFSET,
++                     page[PARTITION_TABLE_OFFSET+0] & 255,
++                     page[PARTITION_TABLE_OFFSET+1] & 255,
++                     page[PARTITION_TABLE_OFFSET+2] & 255,
++                     page[PARTITION_TABLE_OFFSET+3] & 255,
++                     page[PARTITION_TABLE_OFFSET+4] & 255,
++                     page[PARTITION_TABLE_OFFSET+5] & 255,
++                     page[PARTITION_TABLE_OFFSET+6] & 255,
++                     page[PARTITION_TABLE_OFFSET+7] & 255);
++#endif
+       }
+-      pidx++;  /* First partition is always set to the default. */
+       if (ptable_head && (ptable_head->magic == PARTITION_TABLE_MAGIC)
+           && (ptable_head->size <
+@@ -323,7 +464,6 @@
+               /* Looks like a start, sane length and end of a
+                * partition table, lets check csum etc.
+                */
+-              int ptable_ok = 0;
+               struct partitiontable_entry *max_addr =
+                       (struct partitiontable_entry *)
+                       ((unsigned long)ptable_head + sizeof(*ptable_head) +
+@@ -331,7 +471,7 @@
+               unsigned long offset = CONFIG_ETRAX_PTABLE_SECTOR;
+               unsigned char *p;
+               unsigned long csum = 0;
+-
++              
+               ptable = (struct partitiontable_entry *)
+                       ((unsigned long)ptable_head + sizeof(*ptable_head));
+@@ -343,108 +483,177 @@
+                       csum += *p++;
+                       csum += *p++;
+                       csum += *p++;
+-              }
++              }                
+               ptable_ok = (csum == ptable_head->checksum);
+               /* Read the entries and use/show the info.  */
+-              printk(KERN_INFO " Found a%s partition table at 0x%p-0x%p.\n",
++              printk(KERN_INFO "axisflashmap: "
++                     "Found a%s partition table at 0x%p-0x%p.\n",
+                      (ptable_ok ? " valid" : "n invalid"), ptable_head,
+                      max_addr);
+               /* We have found a working bootblock.  Now read the
+-               * partition table.  Scan the table.  It ends when
+-               * there is 0xffffffff, that is, empty flash.
++               * partition table.  Scan the table.  It ends with 0xffffffff.
+                */
+               while (ptable_ok
+-                     && ptable->offset != 0xffffffff
++                     && ptable->offset != PARTITIONTABLE_END_MARKER
+                      && ptable < max_addr
+-                     && pidx < MAX_PARTITIONS) {
++                     && pidx < MAX_PARTITIONS - 1) {
+-                      axis_partitions[pidx].offset = offset + ptable->offset + (crisv32_nand_boot ? 16384 : 0);
+-                      axis_partitions[pidx].size = ptable->size;
+-
+-                      printk(pmsg, pidx, axis_partitions[pidx].offset,
+-                             axis_partitions[pidx].size);
++                      axis_partitions[pidx].offset = offset + ptable->offset;
++#ifdef CONFIG_ETRAX_NANDFLASH
++                      if (main_mtd->type == MTD_NANDFLASH) {
++                              axis_partitions[pidx].size = 
++                                      (((ptable+1)->offset == 
++                                        PARTITIONTABLE_END_MARKER) ? 
++                                        main_mtd->size : 
++                                        ((ptable+1)->offset + offset)) - 
++                                      (ptable->offset + offset);
++
++                      } else
++#endif /* CONFIG_ETRAX_NANDFLASH */
++                              axis_partitions[pidx].size = ptable->size;
++#ifdef CONFIG_ETRAX_NANDBOOT
++                      /* Save partition number of jffs2 ro partition.
++                       * Needed if RAM booting or root file system in RAM.
++                       */
++                      if (!nand_boot &&
++                          ram_rootfs_partition < 0 && /* not already set */
++                          ptable->type == PARTITION_TYPE_JFFS2 &&
++                          (ptable->flags & PARTITION_FLAGS_READONLY_MASK) == 
++                              PARTITION_FLAGS_READONLY)
++                              ram_rootfs_partition = pidx;
++#endif /* CONFIG_ETRAX_NANDBOOT */
+                       pidx++;
+                       ptable++;
+               }
+-              use_default_ptable = !ptable_ok;
+       }
+-      if (romfs_in_flash) {
+-              /* Add an overlapping device for the root partition (romfs). */
++      /* Decide whether to use default partition table. */
++      /* Only use default table if we actually have a device (main_mtd) */
+-              axis_partitions[pidx].name = "romfs";
+-              if (crisv32_nand_boot) {
+-                      char* data = kmalloc(1024, GFP_KERNEL);
+-                      int len;
+-                      int offset = crisv32_nand_cramfs_offset & ~(1024-1);
+-                      char* tmp;
+-
+-                      mymtd->read(mymtd, offset, 1024, &len, data);
+-                      tmp = &data[crisv32_nand_cramfs_offset % 512];
+-                      axis_partitions[pidx].size = *(unsigned*)(tmp + 4);
+-                      axis_partitions[pidx].offset = crisv32_nand_cramfs_offset;
+-                      kfree(data);
+-              } else {
+-                      axis_partitions[pidx].size = romfs_length;
+-                      axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR;
+-              }
++      struct mtd_partition *partition = &axis_partitions[0];
++      if (main_mtd && !ptable_ok) {
++              memcpy(axis_partitions, axis_default_partitions,
++                     sizeof(axis_default_partitions));
++              pidx = NUM_DEFAULT_PARTITIONS;
++              ram_rootfs_partition = DEFAULT_ROOTFS_PARTITION_NO;
++      }
++      /* Add artificial partitions for rootfs if necessary */
++      if (romfs_in_flash) {
++              /* rootfs is in directly accessible flash memory = NOR flash.
++                 Add an overlapping device for the rootfs partition. */
++              printk(KERN_INFO "axisflashmap: Adding partition for "
++                     "overlapping root file system image\n");
++              axis_partitions[pidx].size = romfs_length;
++              axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR;
++              axis_partitions[pidx].name = "romfs";
+               axis_partitions[pidx].mask_flags |= MTD_WRITEABLE;
+-
+-              printk(KERN_INFO
+-                       " Adding readonly flash partition for romfs image:\n");
+-              printk(pmsg, pidx, axis_partitions[pidx].offset,
+-                     axis_partitions[pidx].size);
++              ram_rootfs_partition = -1;
+               pidx++;
+       }
+-
+-        if (mymtd) {
+-              if (use_default_ptable) {
+-                      printk(KERN_INFO " Using default partition table.\n");
+-                      err = add_mtd_partitions(mymtd, axis_default_partitions,
+-                                               NUM_DEFAULT_PARTITIONS);
+-              } else {
+-                      err = add_mtd_partitions(mymtd, axis_partitions, pidx);
++      else if (romfs_length && !nand_boot) {
++              /* romfs exists in memory, but not in flash, so must be in RAM.
++               * Configure an MTDRAM partition. */
++              if (ram_rootfs_partition < 0) { /* none set yet */
++                      ram_rootfs_partition = pidx; /* put it at the end */
++                      pidx++;
+               }
++              printk(KERN_INFO "axisflashmap: Adding partition for "
++                     "root file system image in RAM\n");
++              axis_partitions[ram_rootfs_partition].size = romfs_length;
++              axis_partitions[ram_rootfs_partition].offset = romfs_start;
++              axis_partitions[ram_rootfs_partition].name = "romfs";
++              axis_partitions[ram_rootfs_partition].mask_flags |= 
++                      MTD_WRITEABLE;
++      }
+-              if (err) {
+-                      panic("axisflashmap could not add MTD partitions!\n");
+-              }
++#ifdef CONFIG_ETRAX_AXISFLASHMAP_MTD0WHOLE
++      if (main_mtd) {
++              main_partition.size = main_mtd->size;
++                err = add_mtd_partitions(main_mtd, &main_partition, 1);
++              if (err)
++                      panic("axisflashmap: Could not initialize "
++                            "partition for whole main mtd device!\n");
+       }
+-/* CONFIG_EXTRAXFS_SIM */
+ #endif
+-      if (!romfs_in_flash) {
+-              /* Create an RAM device for the root partition (romfs). */
++      /* Now, register all partitions with mtd.
++       * We do this one at a time so we can slip in an MTDRAM device
++       * in the proper place if required. */
++
++      for (part = 0; part < pidx; part++) {
++              if (part == ram_rootfs_partition) {
++                      /* add MTDRAM partition here */
++                      struct mtd_info *mtd_ram;
++
++                      mtd_ram = kmalloc(sizeof(struct mtd_info), GFP_KERNEL);
++                      if (!mtd_ram)
++                              panic("axisflashmap: Couldn't allocate memory "
++                                    "for mtd_info!\n");
++                      printk(KERN_INFO "axisflashmap: Adding RAM partition "
++                             "for rootfs image.\n");
++                      err = mtdram_init_device(mtd_ram, 
++                                               (void *)partition[part].offset,
++                                               partition[part].size,
++                                               partition[part].name);
++                      if (err)
++                              panic("axisflashmap: Could not initialize "
++                                    "MTD RAM device!\n");
++                      /* JFFS2 likes to have an erasesize. Keep potential
++                       * JFFS2 rootfs happy by providing one. Since image
++                       * was most likely created for main mtd, use that
++                       * erasesize, if available. Otherwise, make a guess. */
++                      mtd_ram->erasesize = (main_mtd ? main_mtd->erasesize :
++                                                       CONFIG_ETRAX_PTABLE_SECTOR);
++              } else {
++                      err = add_mtd_partitions(main_mtd, 
++                                               &partition[part], 1);
++                      if (err)
++                              panic("axisflashmap: Could not add mtd "
++                                    "partition %d\n", part);
++              }
++      }
+-#if !defined(CONFIG_MTD_MTDRAM) || (CONFIG_MTDRAM_TOTAL_SIZE != 0) || (CONFIG_MTDRAM_ABS_POS != 0)
+-              /* No use trying to boot this kernel from RAM. Panic! */
+-              printk(KERN_EMERG "axisflashmap: Cannot create an MTD RAM "
+-                     "device due to kernel (mis)configuration!\n");
+-              panic("This kernel cannot boot from RAM!\n");
+-#else
+-              struct mtd_info *mtd_ram;
++#endif /* CONFIG_EXTRAXFS_SIM */
+-              mtd_ram = (struct mtd_info *)kmalloc(sizeof(struct mtd_info),
+-                                                   GFP_KERNEL);
+-              if (!mtd_ram) {
+-                      panic("axisflashmap couldn't allocate memory for "
+-                            "mtd_info!\n");
+-              }
++#ifdef CONFIG_ETRAXFS_SIM
++      /* For simulator, always use a RAM partition.
++       * The rootfs will be found after the kernel in RAM,
++       * with romfs_start and romfs_end indicating location and size.
++       */
++      struct mtd_info *mtd_ram;
++
++      mtd_ram = (struct mtd_info *)kmalloc(sizeof(struct mtd_info),
++                                           GFP_KERNEL);
++      if (!mtd_ram) {
++              panic("axisflashmap: Couldn't allocate memory for "
++                    "mtd_info!\n");
++      }
+-              printk(KERN_INFO " Adding RAM partition for romfs image:\n");
+-              printk(pmsg, pidx, romfs_start, romfs_length);
++      printk(KERN_INFO "axisflashmap: Adding RAM partition for romfs, "
++             "at %u, size %u\n",
++             (unsigned) romfs_start, (unsigned) romfs_length);
++
++      err = mtdram_init_device(mtd_ram, (void*)romfs_start, 
++                               romfs_length, "romfs");
++      if (err) {
++              panic("axisflashmap: Could not initialize MTD RAM "
++                    "device!\n");
++      }
++#endif /* CONFIG_EXTRAXFS_SIM */
++
++#ifndef CONFIG_ETRAXFS_SIM
++      if (aux_mtd) {
++              aux_partition.size = aux_mtd->size;
++                err = add_mtd_partitions(aux_mtd, &aux_partition, 1);
++              if (err)
++                      panic("axisflashmap: Could not initialize "
++                            "aux mtd device!\n");
+-              err = mtdram_init_device(mtd_ram, (void*)romfs_start,
+-                                       romfs_length, "romfs");
+-              if (err) {
+-                      panic("axisflashmap could not initialize MTD RAM "
+-                            "device!\n");
+-              }
+-#endif
+       }
++#endif /* CONFIG_EXTRAXFS_SIM */
+       return err;
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/board_mmcspi.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/board_mmcspi.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/board_mmcspi.c       1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/board_mmcspi.c       2007-01-29 15:51:19.000000000 +0100
+@@ -0,0 +1,686 @@
++/*
++ * Somewhat generic "board-side" code to support SPI drivers for chips
++ * with a CRIS v32 and later.  Not really board-specific, and only for
++ * registration of SPI devices for MMC, hence the "mmcspi" part of the
++ * name instead of a proper board name.
++ *
++ * Copyright (c) 2007 Axis Communications AB
++ *
++ * TODO: SDIO interrupt-pin support (though can't be done until
++ * there's support added in both the mmc_spi and the mmc frameworks).
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License, or
++ * (at your option) any later version.
++ */
++
++#include <linux/types.h>
++#include <linux/platform_device.h>
++#include <linux/spi/spi.h>
++#include <linux/spi/mmc_spi.h>
++#include <linux/mmc/host.h>
++#include <asm/arch/board.h>
++#include <asm/arch/pinmux.h>
++#include <asm/arch/dma.h>
++#include <linux/err.h>
++
++#include <asm/io.h>
++
++/* We need some housekeeping.  */
++#define CONCAT_(a, b) a ## b
++#define CONCAT(a, b) CONCAT_(a, b)
++#define CONCAT3(a, b, c) CONCAT(CONCAT(a, b), c)
++
++/* Grr.  Why not define them as usual in autoconf, #ifdef REVEAL_MODULES?  */
++#if !defined(CONFIG_SPI_ETRAX_SSER) && defined(CONFIG_SPI_ETRAX_SSER_MODULE)
++#define CONFIG_SPI_ETRAX_SSER
++#endif
++
++#if !defined(CONFIG_ETRAX_SPI_SSER0) && defined(CONFIG_ETRAX_SPI_SSER0_MODULE)
++#define CONFIG_ETRAX_SPI_SSER0
++#endif
++
++#if !defined(CONFIG_ETRAX_SPI_SSER1) && defined(CONFIG_ETRAX_SPI_SSER1_MODULE)
++#define CONFIG_ETRAX_SPI_SSER1
++#endif
++
++#if !defined(CONFIG_SPI_ETRAX_GPIO) && defined(CONFIG_SPI_ETRAX_GPIO_MODULE)
++#define CONFIG_SPI_ETRAX_GPIO
++#endif
++
++#define CONFIGURED_PIN(x) ((x) != NULL && *(x) != 0 && *(x) != ' ')
++
++/* Helper function to configure an iopin for input.  */
++
++static int crisv32_config_pin_in(struct crisv32_iopin *pin, const char *name)
++{
++      int ret = crisv32_io_get_name(pin, name);
++      if (ret)
++              return ret;
++
++      crisv32_io_set_dir(pin, crisv32_io_dir_in);
++      return 0;
++}
++
++/*
++ * Writable data pointed to by the constant parts of each MMC_SPI bus
++ * interface.  Should only hold MMC-specific state (for the
++ * MMC-specific pins), nothing SPI-generic.
++ */
++
++struct crisv32_mmc_spi_pinstate {
++      struct crisv32_iopin write_protect_pin;
++      struct crisv32_iopin card_detect_pin;
++
++      /* We must poll for card-detect, which we do each:  */
++#define CARD_DETECT_CHECK_INTERVAL (2*HZ)
++      /* We poll using a self-arming workqueue function.  */
++      struct work_struct cd_work;
++
++      /* When we detect a change in card presence, we call this
++       * function, supplied by the mmc_spi framework: */
++      irqreturn_t (*detectfunc)(int, void *);
++
++      /*
++       * We call that function with this parameter as the second
++       * one.  We must assume the other parameters are unused, as we
++       * don't have an interrupt context.
++       */
++      void *detectfunc_param2;
++
++      /* State for the card-detect worker. */
++      int card_present;
++};
++
++/* Rearming "work" function for card-detect polling.  */
++
++static void crisv32_cd_worker(void *x)
++{
++      struct crisv32_mmc_spi_pinstate *state = x;
++
++      /* It's a pull-up, so a card is present when 0.  */
++      int card_present = crisv32_io_rd(&state->card_detect_pin) == 0;
++
++      if (card_present != state->card_present) {
++              state->card_present = card_present;
++              state->detectfunc(0, state->detectfunc_param2);
++      }
++      schedule_delayed_work(&state->cd_work, CARD_DETECT_CHECK_INTERVAL);
++}
++
++/* The parts of MMC-specific configuration common to GPIO and SSER.  */
++
++static int crisv32_mmcspi_config_common(struct spi_device *spidev,
++                                      irqreturn_t (*intfunc)(int, void *),
++                              struct mmc_host *mmc_host,
++                              const struct crisv32_mmc_spi_pindata *pd)
++{
++      int ret = 0;
++      struct crisv32_mmc_spi_pinstate *ps = pd->pinstate;
++
++      /*
++       * If we don't have a card-detect pin, the card must be
++       * present at startup (including insmod/modprobe).  No
++       * re-scans are done if no card is present at that time.
++       */
++      if (CONFIGURED_PIN(pd->card_detect)) {
++              ret = crisv32_config_pin_in(&ps->card_detect_pin,
++                                          pd->card_detect);
++
++              if (ret != 0)
++                      goto bad_card_detect;
++
++              /* Need to cast away const from pd, unfortunately.  */
++              INIT_WORK(&ps->cd_work, crisv32_cd_worker, (void *) ps);
++              ps->card_present = 1;
++              ps->detectfunc = intfunc;
++              ps->detectfunc_param2 = mmc_host;
++              crisv32_cd_worker(ps);
++      } else
++              ps->detectfunc = NULL;
++
++      /*
++       * We set up for checking for presence of a write-protect pin
++       * as pin.port being non-NULL.
++       */
++      memset(&ps->write_protect_pin, 0, sizeof ps->write_protect_pin);
++      if (CONFIGURED_PIN(pd->write_protect)) {
++              ret = crisv32_config_pin_in(&ps->write_protect_pin,
++                                          pd->write_protect);
++
++              if (ret != 0)
++                      goto bad_write_protect;
++      }
++
++      return 0;
++
++ bad_write_protect:
++      if (ps->detectfunc) {
++              cancel_rearming_delayed_work(&ps->cd_work);
++              ps->detectfunc = NULL;
++      }
++
++ bad_card_detect:
++      return ret;
++}
++
++/* A function to undo crisv32_mmcspi_config_common.  */
++
++static void crisv32_mmcspi_deconfig_common(const struct
++                                         crisv32_mmc_spi_pindata *pd)
++{
++      if (pd->pinstate->detectfunc) {
++              cancel_rearming_delayed_work(&pd->pinstate->cd_work);
++              pd->pinstate->detectfunc = NULL;
++      }
++
++      /* We don't have to undo the pin allocations, being GPIO.  */
++}
++
++/* Helper function for write-protect sense.  */
++
++static int crisv32_mmcspi_get_ro_helper(struct crisv32_mmc_spi_pinstate *ps)
++{
++      return ps->write_protect_pin.port != NULL
++              && crisv32_io_rd(&ps->write_protect_pin) != 0;
++}
++
++/* The hardware-interface-specific SPI+MMC parts.  */
++
++#ifdef CONFIG_SPI_ETRAX_SSER
++static const char crisv32_matching_spi_sser_driver_name[] __init_or_module
++ = "spi_crisv32_sser";
++
++/*
++ * Make up something we can use in tables and function without
++ * #ifdef-cluttering.
++ */
++#ifdef CONFIG_ETRAX_SPI_SSER0_DMA
++#define WITH_ETRAX_SPI_SSER0_DMA 1
++#else
++#define WITH_ETRAX_SPI_SSER0_DMA 0
++#endif
++#ifdef CONFIG_ETRAX_SPI_SSER1_DMA
++#define WITH_ETRAX_SPI_SSER1_DMA 1
++#else
++#define WITH_ETRAX_SPI_SSER1_DMA 0
++#endif
++
++/* Write-protect sense for the SSER interface.  */
++
++static int crisv32_mmcspi_sser_get_ro(struct device *dev)
++{
++      struct spi_device *spidev = to_spi_device(dev);
++      struct crisv32_mmc_spi_sser_hwdata *sser_defs
++              = spidev->controller_data;
++      struct crisv32_mmc_spi_pindata *pd = &sser_defs->mmc;
++      return crisv32_mmcspi_get_ro_helper(pd->pinstate);
++}
++
++/* Initialize the MMC-specific parts of the MMC+SPI interface, SSER.  */
++
++static int crisv32_mmcspi_sser_init(struct device *dev,
++                                  irqreturn_t (*intfunc)(int, void *),
++                                  void *xmmc_host)
++{
++      struct mmc_host *mmc_host = xmmc_host;
++      struct spi_device *spidev = to_spi_device(dev);
++      struct crisv32_mmc_spi_sser_hwdata *sser_defs
++              = spidev->controller_data;
++      int ret = crisv32_mmcspi_config_common(spidev, intfunc, mmc_host,
++                                             &sser_defs->mmc);
++      if (ret != 0)
++              return ret;
++
++      mmc_host->f_max = spidev->max_speed_hz;
++
++      /*
++       * Let's just set this to ceil(100e6/65536).  It wouldn't be
++       * hard to change the base frequency to support down to 450Hz,
++       * but there's no apparent requirement from known hardware.
++       * Would also require adjustments to the SPI code, not just
++       * here.
++       *
++       * On second thought, let's not set f_min unconditionally, as
++       * mmc doesn't treat it as a limit, but as the frequency to
++       * use at initialization.  We stick with what mmc_spi sets, if
++       * it fits.
++       */
++      if (mmc_host->f_min < 1526)
++              mmc_host->f_min = 1526;
++
++      dev_info(dev, "CRIS v32 mmc_spi support hooked to SPI on sser%d"
++               " (cd: %s, wp: %s)\n",
++               spidev->master->bus_num,
++               CONFIGURED_PIN(sser_defs->mmc.card_detect)
++               ? sser_defs->mmc.card_detect : "(none)",
++               CONFIGURED_PIN(sser_defs->mmc.write_protect)
++               ? sser_defs->mmc.write_protect : "(none)");
++      return 0;
++}
++
++/* Similarly, to undo crisv32_mmcspi_sser_init.  */
++
++static void crisv32_mmcspi_sser_exit(struct device *dev, void *xmmc_host)
++{
++      struct spi_device *spidev = to_spi_device(dev);
++      struct crisv32_mmc_spi_sser_hwdata *sser_defs
++              = spidev->controller_data;
++
++      crisv32_mmcspi_deconfig_common(&sser_defs->mmc);
++}
++
++/*
++ * Connect sser and DMA channels and return the proper numbers to use.
++ *
++ * This function exists really only to avoid having the following constants
++ * tabled: regi_sserN, SSERn_INTR_VECT, pinmux_sserN, SYNC_SERn_TX_DMA_NBR,
++ * SYNC_SERn_RX_DMA_NBR dma_sserN, regi_dmaM, regi_dmaN, DMAm_INTR_VECT,
++ * DMAn_INTR_VECT.  I might have missed some. :-)
++ */
++static int crisv32_allocate_sser(struct crisv32_regi_n_int *sserp,
++                               struct crisv32_regi_n_int *dmainp,
++                               struct crisv32_regi_n_int *dmaoutp,
++                               u32 sserno)
++{
++      int ret;
++      u32 regi_sser = sserno == 0 ? regi_sser0 : regi_sser1;
++      u32 sser_irq = sserno == 0 ? SSER0_INTR_VECT : SSER1_INTR_VECT;
++      BUG_ON(sserno > 1 || sserp == NULL);
++
++      ret = crisv32_pinmux_alloc_fixed(sserno == 0
++                                       ? pinmux_sser0 : pinmux_sser1);
++      if (ret != 0)
++              return ret;
++
++      sserp->regi = regi_sser;
++      sserp->irq = sser_irq;
++
++      if (dmaoutp) {
++              crisv32_request_dma(sserno == 0
++                                  ? SYNC_SER0_TX_DMA_NBR
++                                  : SYNC_SER1_TX_DMA_NBR,
++                                  "SD/MMC SPI dma tr",
++                                  DMA_VERBOSE_ON_ERROR | DMA_PANIC_ON_ERROR,
++                                  /* Let's be brave and ask for the
++                                     max.  Except it's simplex, so
++                                     let's request half the max
++                                     speed in either direction.  */
++                                  50 * 1000 * 1000 / 8 / 2,
++                                  sserno == 0 ? dma_sser0 : dma_sser1);
++              dmaoutp->regi = sserno == 0
++                      ? CONCAT(regi_dma, SYNC_SER0_TX_DMA_NBR)
++                      : CONCAT(regi_dma, SYNC_SER1_TX_DMA_NBR);
++              dmaoutp->irq = sserno == 0
++                      ? CONCAT3(DMA, SYNC_SER0_TX_DMA_NBR, _INTR_VECT)
++                      : CONCAT3(DMA, SYNC_SER1_TX_DMA_NBR, _INTR_VECT);
++      }
++
++      if (dmainp) {
++              crisv32_request_dma(sserno == 0
++                                  ? SYNC_SER0_RX_DMA_NBR
++                                  : SYNC_SER1_RX_DMA_NBR,
++                                  "SD/MMC SPI dma rec",
++                                  DMA_VERBOSE_ON_ERROR | DMA_PANIC_ON_ERROR,
++                                  50 * 1000 * 1000 / 8 / 2,
++                                  sserno == 0 ? dma_sser0 : dma_sser1);
++              dmainp->regi = sserno == 0
++                      ? CONCAT(regi_dma, SYNC_SER0_RX_DMA_NBR)
++                      : CONCAT(regi_dma, SYNC_SER1_RX_DMA_NBR);
++              dmainp->irq = sserno == 0
++                      ? CONCAT3(DMA, SYNC_SER0_RX_DMA_NBR, _INTR_VECT)
++                      : CONCAT3(DMA, SYNC_SER1_RX_DMA_NBR, _INTR_VECT);
++      }
++
++      return 0;
++}
++
++/* Undo whatever allocations et. al. that crisv32_allocate_sser did.  */
++
++static void crisv32_free_sser(u32 sserno, int with_dma)
++{
++      int ret;
++
++      if (with_dma) {
++              crisv32_free_dma(sserno == 0
++                               ? SYNC_SER0_RX_DMA_NBR
++                               : SYNC_SER1_RX_DMA_NBR);
++              crisv32_free_dma(sserno == 0
++                               ? SYNC_SER0_TX_DMA_NBR
++                               : SYNC_SER1_TX_DMA_NBR);
++      }
++
++      ret = crisv32_pinmux_dealloc_fixed(sserno == 0
++                                         ? pinmux_sser0 : pinmux_sser1);
++
++      if (ret != 0)
++              panic("%s: crisv32_pinmux_dealloc_fixed returned %d\n",
++                     __FUNCTION__, ret);
++}
++
++/* Convenience-macro to avoid typos causing diffs between sser ports.  */
++
++#define SSER_CDATA(n)                                                 \
++      {                                                               \
++              {                                                       \
++                      .using_dma = WITH_ETRAX_SPI_SSER##n##_DMA,      \
++                      .iface_allocate = crisv32_allocate_sser##n,     \
++                      .iface_free = crisv32_free_sser##n              \
++              },                                                      \
++              {                                                       \
++                      .card_detect                                    \
++                              = CONFIG_ETRAX_SPI_MMC_CD_SSER##n##_PIN,\
++                      .write_protect                                  \
++                              = CONFIG_ETRAX_SPI_MMC_WP_SSER##n##_PIN,\
++                      .pinstate                                       \
++                              = &crisv32_mmcspi_sser##n##_pinstate    \
++              }                                                       \
++      }
++
++#ifdef CONFIG_ETRAX_SPI_SSER0
++
++/* Allocate hardware to go with sser0 and fill in the right numbers.  */
++
++static int crisv32_allocate_sser0(struct crisv32_regi_n_int *sserp,
++                                struct crisv32_regi_n_int *dmainp,
++                                struct crisv32_regi_n_int *dmaoutp)
++{
++      return crisv32_allocate_sser(sserp, dmainp, dmaoutp, 0);
++}
++
++/* Undo those allocations.  */
++
++static void crisv32_free_sser0(void)
++{
++      crisv32_free_sser(0, WITH_ETRAX_SPI_SSER0_DMA);
++}
++
++static struct crisv32_mmc_spi_pinstate crisv32_mmcspi_sser0_pinstate;
++static const struct crisv32_mmc_spi_sser_hwdata crisv32_mmcspi_sser0_cdata
++ = SSER_CDATA(0);
++#endif /* CONFIG_ETRAX_SPI_SSER0 */
++
++#ifdef CONFIG_ETRAX_SPI_SSER1
++
++/* Allocate hardware to go with sser0 and fill in the right numbers.  */
++
++static int crisv32_allocate_sser1(struct crisv32_regi_n_int *sserp,
++                                struct crisv32_regi_n_int *dmainp,
++                                struct crisv32_regi_n_int *dmaoutp)
++{
++      return crisv32_allocate_sser(sserp, dmainp, dmaoutp, 1);
++}
++
++/* Undo those allocations.  */
++
++static void crisv32_free_sser1(void)
++{
++      crisv32_free_sser(1, WITH_ETRAX_SPI_SSER1_DMA);
++}
++
++static struct crisv32_mmc_spi_pinstate crisv32_mmcspi_sser1_pinstate;
++static const struct crisv32_mmc_spi_sser_hwdata crisv32_mmcspi_sser1_cdata
++ = SSER_CDATA(1);
++
++#endif /* CONFIG_ETRAX_SPI_SSER1 */
++
++static struct mmc_spi_platform_data crisv32_mmcspi_sser_pdata = {
++      .init = crisv32_mmcspi_sser_init,
++      .exit = __devexit_p(crisv32_mmcspi_sser_exit),
++      .detect_delay = 0,
++      .get_ro = crisv32_mmcspi_sser_get_ro,
++      .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
++      .setpower = NULL
++};
++
++#endif /* CONFIG_SPI_ETRAX_SSER */
++
++#ifdef CONFIG_SPI_ETRAX_GPIO
++
++static const char crisv32_matching_spi_gpio_driver_name[] __init_or_module
++ = "spi_crisv32_gpio";
++
++/* Write-protect sense for the GPIO interface.  */
++
++static int crisv32_mmcspi_gpio_get_ro(struct device *dev)
++{
++      struct spi_device *spidev = to_spi_device(dev);
++      struct crisv32_mmc_spi_gpio_hwdata *gpio_defs
++              = spidev->controller_data;
++      struct crisv32_mmc_spi_pindata *pd = &gpio_defs->mmc;
++
++      return crisv32_mmcspi_get_ro_helper(pd->pinstate);
++}
++
++/* Initialize the MMC-specific parts of the MMC+SPI interface, GPIO.  */
++
++static int crisv32_mmcspi_gpio_init(struct device *dev,
++                                  irqreturn_t (*intfunc)(int, void *),
++                                  void *xmmc_host)
++{
++      struct mmc_host *mmc_host = xmmc_host;
++      struct spi_device *spidev = to_spi_device(dev);
++      struct crisv32_mmc_spi_gpio_hwdata *gpio_defs
++              = spidev->controller_data;
++      int ret = crisv32_mmcspi_config_common(spidev, intfunc, mmc_host,
++                                             &gpio_defs->mmc);
++      if (ret)
++              return ret;
++
++      mmc_host->f_max = spidev->max_speed_hz;
++
++      /*
++       * We don't set f_min, as the mmc code doesn't treat it as a
++       * limit, but as the frequency to use at initialization.  We
++       * stick with what mmc_spi sets.
++       */
++      dev_info(dev, "CRIS v32 mmc_spi support hooked to SPI on GPIO"
++               " (bus #%d, cd: %s, wp: %s)\n",
++               spidev->master->bus_num,
++               CONFIGURED_PIN(gpio_defs->mmc.card_detect)
++               ? gpio_defs->mmc.card_detect : "(none)",
++               CONFIGURED_PIN(gpio_defs->mmc.write_protect)
++               ? gpio_defs->mmc.write_protect : "(none)");
++      return 0;
++}
++
++/* Similarly, to undo crisv32_mmcspi_gpio_init.  */
++
++static void crisv32_mmcspi_gpio_exit(struct device *dev, void *xmmc_host)
++{
++      struct spi_device *spidev = to_spi_device(dev);
++      struct crisv32_mmc_spi_gpio_hwdata *gpio_defs
++              = spidev->controller_data;
++
++      crisv32_mmcspi_deconfig_common(&gpio_defs->mmc);
++}
++
++static const struct mmc_spi_platform_data crisv32_mmcspi_gpio_pdata = {
++      .init = crisv32_mmcspi_gpio_init,
++      .exit = __devexit_p(crisv32_mmcspi_gpio_exit),
++      .detect_delay = 0,
++      .get_ro = crisv32_mmcspi_gpio_get_ro,
++      .ocr_mask = MMC_VDD_32_33|MMC_VDD_33_34,
++      .setpower = NULL
++};
++
++static struct crisv32_mmc_spi_pinstate crisv32_mmcspi_gpio_pinstate;
++static const struct crisv32_mmc_spi_gpio_hwdata crisv32_mmcspi_gpio_cdata = {
++      {
++              .cs     = CONFIG_ETRAX_SPI_CS_PIN,
++              .miso   = CONFIG_ETRAX_SPI_DATAIN_PIN,
++              .mosi   = CONFIG_ETRAX_SPI_DATAOUT_PIN,
++              .sclk   = CONFIG_ETRAX_SPI_CLK_PIN
++      },
++      {
++              .card_detect    = CONFIG_ETRAX_SPI_MMC_CD_GPIO_PIN,
++              .write_protect  = CONFIG_ETRAX_SPI_MMC_WP_GPIO_PIN,
++              .pinstate       = &crisv32_mmcspi_gpio_pinstate
++      }
++};
++#endif /* CONFIG_SPI_ETRAX_GPIO */
++
++struct crisv32_s_b_i_and_driver_info {
++      struct spi_board_info sbi;
++      const char *driver_name;
++
++      /*
++       * We have to adjust the platform device at time of creation
++       * below, to allow the Linux DMA framework to handle DMA.  In
++       * the name of simplicity, we state the dma:able property
++       * twice, a bit redundantly; here and in the controller_data
++       * structure pointed to by spi_board_info.
++       */
++      int dma_able;
++};
++
++/* Convenience-macro to avoid typos causing diffs between sser ports.  */
++
++#define SSER_CONFIG(n)                                                        \
++      {                                                               \
++              /*                                                      \
++               * No meaningful values for .irq or .chip_select,       \
++               * so we leave them out.                                \
++               */                                                     \
++              {                                                       \
++                      .modalias               = "mmc_spi",            \
++                      .platform_data                                  \
++                               = &crisv32_mmcspi_sser_pdata,          \
++                      /*                                              \
++                       * Casting to avoid a compiler const-warning.   \
++                       */                                             \
++                      .controller_data                                \
++                              = ((void *)                             \
++                                 &crisv32_mmcspi_sser##n##_cdata),    \
++                      .max_speed_hz           = 50 * 1000 * 1000,     \
++                      .bus_num                = n,                    \
++                      /*                                              \
++                       * We only provide one mode, so it must be      \
++                       * default.                                     \
++                       */                                             \
++                      .mode                   = SPI_MODE_3            \
++              },                                                      \
++              crisv32_matching_spi_sser_driver_name,                  \
++              WITH_ETRAX_SPI_SSER##n##_DMA                            \
++      }
++
++static const struct crisv32_s_b_i_and_driver_info
++crisv32_mmcspi_devices[] __initdata = {
++#ifdef CONFIG_ETRAX_SPI_SSER0
++      SSER_CONFIG(0),
++#endif
++#ifdef CONFIG_ETRAX_SPI_SSER1
++      SSER_CONFIG(1),
++#endif
++#ifdef CONFIG_SPI_ETRAX_GPIO
++      {
++              {
++                      .modalias               = "mmc_spi",
++                      .platform_data          = &crisv32_mmcspi_gpio_pdata,
++                      /* Casting to avoid a compiler const-warning.  */
++                      .controller_data
++                              = (void *) &crisv32_mmcspi_gpio_cdata,
++
++                      /* No, we can't even reach this number with GPIO.  */
++                      .max_speed_hz           = 5 * 1000 * 1000,
++                      .bus_num                = 2
++              },
++              crisv32_matching_spi_gpio_driver_name,
++              0
++      },
++#endif
++};
++
++/* Must not be __initdata.  */
++static const char controller_data_ptr_name[] = "controller_data_ptr";
++static u64 allmem_mask = ~(u32) 0;
++
++/*
++ * We have to register the SSER and GPIO "platform_device"s separately
++ * from the "spi_board_info"s in order to have the drivers separated
++ * from the hardware instance info.
++ */
++
++static int __init crisv32_register_mmcspi(void)
++{
++      int ret;
++      void *retp;
++      const struct crisv32_s_b_i_and_driver_info *sbip;
++
++      for (sbip = crisv32_mmcspi_devices;
++           sbip < crisv32_mmcspi_devices
++                   + ARRAY_SIZE(crisv32_mmcspi_devices);
++           sbip++) {
++
++              /*
++               * We have to pass the controller data as a device
++               * "resource" (FIXME: too?), so it can be available
++               * for the setup *before* starting the SPI core -
++               * which is where .controller_data makes it to the SPI
++               * structure!
++               */
++              struct resource mmcspi_res = {
++                      .start = (resource_size_t) sbip->sbi.controller_data,
++                      .end = (resource_size_t) sbip->sbi.controller_data,
++                      .name = controller_data_ptr_name
++              };
++
++              /*
++               * Presumably we could pass the irqs and register
++               * addresses and... as individual resources here
++               * instead of privately in spi_board_info
++               * .controller_data, and make that part a bit more
++               * readable.  Maybe not worthwhile.
++               */
++              /* Need to cast away const here.  FIXME: fix the pdrs API.  */
++              retp = platform_device_alloc(sbip->driver_name,
++                                           sbip->sbi.bus_num);
++              if (IS_ERR_VALUE(PTR_ERR(retp)))
++                      return PTR_ERR(retp);
++
++              /*
++               * We can't use platform_device_register_simple as we
++               * want to set stuff in the device to mark that we'd
++               * prefer buffers explicitly passed as DMA-able.
++               * Disabling/omitting the "if" below, cause testing of
++               * the non-DMA-mem execution path of a DMA-capable
++               * driver.
++               */
++              if (sbip->dma_able) {
++                      struct platform_device *pdev = retp;
++
++                      pdev->dev.dma_mask = &allmem_mask;
++                      pdev->dev.coherent_dma_mask = allmem_mask;
++              }
++
++              if ((ret = platform_device_add_resources(retp, &mmcspi_res, 1)) != 0
++                  || (ret = platform_device_add(retp)) != 0) {
++                      platform_device_put(retp);
++                      return ret;
++              }
++
++              /*
++               * The cost of keeping all info rooted at
++               * crisv32_mmcspi_devices is the allocation overhead
++               * for allocating separately each board info (in the
++               * unlikely event that there's more than one).
++               */
++              ret = spi_register_board_info(&sbip->sbi, 1);
++              if (ret != 0)
++                      return ret;
++      }
++
++      return 0;
++}
++#ifdef MODULE
++#error "Non-module because spi_register_board_info is one-way; there's no unregister function"
++#endif
++
++/*
++ * Needs to be called before __initcall/module_init because the SPI
++ * bus scanning happens when the actual driver registers with the SPI
++ * machinery (spi_bitbang_start/spi_register_master), not when the
++ * device registers (spi_register_board_info).
++ */
++subsys_initcall(crisv32_register_mmcspi);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/cryptocop.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/cryptocop.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/cryptocop.c  2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/cryptocop.c  2007-01-09 10:29:20.000000000 +0100
+@@ -1,4 +1,4 @@
+-/* $Id: cryptocop.c,v 1.13 2005/04/21 17:27:55 henriken Exp $
++/* $Id: cryptocop.c,v 1.22 2007/01/09 09:29:20 starvik Exp $
+  *
+  * Stream co-processor driver for the ETRAX FS
+  *
+@@ -1718,7 +1718,7 @@
+  *   i = i + 1
+  * }
+  * i = Nk
+- *
++ * 
+  * while (i < (Nb * (Nr + 1))) {
+  *   temp = w[i - 1]
+  *   if ((i mod Nk) == 0) {
+@@ -1886,7 +1886,7 @@
+ }
+ static irqreturn_t
+-dma_done_interrupt(int irq, void *dev_id, struct pt_regs * regs)
++dma_done_interrupt(int irq, void *dev_id)
+ {
+       struct cryptocop_prio_job *done_job;
+       reg_dma_rw_ack_intr ack_intr = {
+@@ -2226,6 +2226,7 @@
+                    &pj->iop->ctx_out, (char*)virt_to_phys(&pj->iop->ctx_out)));
+       /* Start input DMA. */
++      flush_dma_context(&pj->iop->ctx_in);
+       DMA_START_CONTEXT(regi_dma9, virt_to_phys(&pj->iop->ctx_in));
+       /* Start output DMA. */
+@@ -3459,7 +3460,7 @@
+       int err;
+       int i;
+       static int initialized = 0;
+-
++        
+       if (initialized)
+               return 0;
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/gpio.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/gpio.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/gpio.c       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/gpio.c       2007-01-09 10:29:20.000000000 +0100
+@@ -1,68 +1,15 @@
+-/* $Id: gpio.c,v 1.16 2005/06/19 17:06:49 starvik Exp $
+- *
++/*
+  * ETRAX CRISv32 general port I/O device
+  *
+- * Copyright (c) 1999, 2000, 2001, 2002, 2003 Axis Communications AB
++ * Copyright (c) 1999-2006 Axis Communications AB
+  *
+  * Authors:    Bjorn Wesen      (initial version)
+  *             Ola Knutsson     (LED handling)
+  *             Johan Adolfsson  (read/set directions, write, port G,
+  *                               port to ETRAX FS.
+  *
+- * $Log: gpio.c,v $
+- * Revision 1.16  2005/06/19 17:06:49  starvik
+- * Merge of Linux 2.6.12.
+- *
+- * Revision 1.15  2005/05/25 08:22:20  starvik
+- * Changed GPIO port order to fit packages/devices/axis-2.4.
+- *
+- * Revision 1.14  2005/04/24 18:35:08  starvik
+- * Updated with final register headers.
+- *
+- * Revision 1.13  2005/03/15 15:43:00  starvik
+- * dev_id needs to be supplied for shared IRQs.
+- *
+- * Revision 1.12  2005/03/10 17:12:00  starvik
+- * Protect alarm list with spinlock.
+- *
+- * Revision 1.11  2005/01/05 06:08:59  starvik
+- * No need to do local_irq_disable after local_irq_save.
+- *
+- * Revision 1.10  2004/11/19 08:38:31  starvik
+- * Removed old crap.
+- *
+- * Revision 1.9  2004/05/14 07:58:02  starvik
+- * Merge of changes from 2.4
+- *
+- * Revision 1.8  2003/09/11 07:29:50  starvik
+- * Merge of Linux 2.6.0-test5
+- *
+- * Revision 1.7  2003/07/10 13:25:46  starvik
+- * Compiles for 2.5.74
+- * Lindented ethernet.c
+- *
+- * Revision 1.6  2003/07/04 08:27:46  starvik
+- * Merge of Linux 2.5.74
+- *
+- * Revision 1.5  2003/06/10 08:26:37  johana
+- * Etrax -> ETRAX CRISv32
+- *
+- * Revision 1.4  2003/06/05 14:22:48  johana
+- * Initialise some_alarms.
+- *
+- * Revision 1.3  2003/06/05 10:15:46  johana
+- * New INTR_VECT macros.
+- * Enable interrupts in global config.
+- *
+- * Revision 1.2  2003/06/03 15:52:50  johana
+- * Initial CRIS v32 version.
+- *
+- * Revision 1.1  2003/06/03 08:53:15  johana
+- * Copy of os/lx25/arch/cris/arch-v10/drivers/gpio.c version 1.7.
+- *
+  */
+-
+ #include <linux/module.h>
+ #include <linux/sched.h>
+ #include <linux/slab.h>
+@@ -85,6 +32,13 @@
+ #include <asm/system.h>
+ #include <asm/irq.h>
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++#include "i2c.h"
++
++#define VIRT_I2C_ADDR 0x40
++#endif
++
++
+ /* The following gio ports on ETRAX FS is available:
+  * pa  8 bits, supports interrupts off, hi, low, set, posedge, negedge anyedge
+  * pb 18 bits
+@@ -111,6 +65,10 @@
+ static wait_queue_head_t *gpio_wq;
+ #endif
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++static int virtual_gpio_ioctl(struct file *file, unsigned int cmd,
++                              unsigned long arg);
++#endif
+ static int gpio_ioctl(struct inode *inode, struct file *file,
+                     unsigned int cmd, unsigned long arg);
+ static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
+@@ -148,55 +106,75 @@
+ #define GIO_REG_RD_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg )
+ #define GIO_REG_WR_ADDR(reg) (volatile unsigned long*) (regi_gio + REG_RD_ADDR_gio_##reg )
+ unsigned long led_dummy;
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++static unsigned long virtual_dummy;
++static unsigned long virtual_rw_pv_oe = CONFIG_ETRAX_DEF_GIO_PV_OE;
++static unsigned short cached_virtual_gpio_read = 0;
++#endif
+-static volatile unsigned long *data_out[NUM_PORTS] = {
+-      GIO_REG_WR_ADDR(rw_pa_dout),
+-      GIO_REG_WR_ADDR(rw_pb_dout),
++static volatile unsigned long *data_out[NUM_PORTS] = { 
++      GIO_REG_WR_ADDR(rw_pa_dout), 
++      GIO_REG_WR_ADDR(rw_pb_dout), 
+       &led_dummy,
+-      GIO_REG_WR_ADDR(rw_pc_dout),
+-      GIO_REG_WR_ADDR(rw_pd_dout),
++      GIO_REG_WR_ADDR(rw_pc_dout), 
++      GIO_REG_WR_ADDR(rw_pd_dout), 
+       GIO_REG_WR_ADDR(rw_pe_dout),
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++      &virtual_dummy,
++#endif
+ };
+-static volatile unsigned long *data_in[NUM_PORTS] = {
+-      GIO_REG_RD_ADDR(r_pa_din),
+-      GIO_REG_RD_ADDR(r_pb_din),
++static volatile unsigned long *data_in[NUM_PORTS] = { 
++      GIO_REG_RD_ADDR(r_pa_din), 
++      GIO_REG_RD_ADDR(r_pb_din), 
+       &led_dummy,
+-      GIO_REG_RD_ADDR(r_pc_din),
+-      GIO_REG_RD_ADDR(r_pd_din),
++      GIO_REG_RD_ADDR(r_pc_din), 
++      GIO_REG_RD_ADDR(r_pd_din), 
+       GIO_REG_RD_ADDR(r_pe_din),
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++      &virtual_dummy,
++#endif
+ };
+-static unsigned long changeable_dir[NUM_PORTS] = {
++static unsigned long changeable_dir[NUM_PORTS] = { 
+       CONFIG_ETRAX_PA_CHANGEABLE_DIR,
+       CONFIG_ETRAX_PB_CHANGEABLE_DIR,
+       0,
+       CONFIG_ETRAX_PC_CHANGEABLE_DIR,
+-      CONFIG_ETRAX_PD_CHANGEABLE_DIR,
++      CONFIG_ETRAX_PD_CHANGEABLE_DIR, 
+       CONFIG_ETRAX_PE_CHANGEABLE_DIR,
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++      CONFIG_ETRAX_PV_CHANGEABLE_DIR,
++#endif
+ };
+-static unsigned long changeable_bits[NUM_PORTS] = {
++static unsigned long changeable_bits[NUM_PORTS] = { 
+       CONFIG_ETRAX_PA_CHANGEABLE_BITS,
+       CONFIG_ETRAX_PB_CHANGEABLE_BITS,
+       0,
+       CONFIG_ETRAX_PC_CHANGEABLE_BITS,
+       CONFIG_ETRAX_PD_CHANGEABLE_BITS,
+       CONFIG_ETRAX_PE_CHANGEABLE_BITS,
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++      CONFIG_ETRAX_PV_CHANGEABLE_BITS,
++#endif
+ };
+-static volatile unsigned long *dir_oe[NUM_PORTS] = {
+-      GIO_REG_WR_ADDR(rw_pa_oe),
+-      GIO_REG_WR_ADDR(rw_pb_oe),
++static volatile unsigned long *dir_oe[NUM_PORTS] = { 
++      GIO_REG_WR_ADDR(rw_pa_oe), 
++      GIO_REG_WR_ADDR(rw_pb_oe), 
+       &led_dummy,
+-      GIO_REG_WR_ADDR(rw_pc_oe),
+-      GIO_REG_WR_ADDR(rw_pd_oe),
++      GIO_REG_WR_ADDR(rw_pc_oe), 
++      GIO_REG_WR_ADDR(rw_pd_oe), 
+       GIO_REG_WR_ADDR(rw_pe_oe),
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++      &virtual_rw_pv_oe,
++#endif
+ };
+-static unsigned int
++static unsigned int 
+ gpio_poll(struct file *file,
+         poll_table *wait)
+ {
+@@ -278,7 +256,7 @@
+               return 0;
+       if ((data & priv->highalarm) ||
+-          (~data & priv->lowalarm)) {
++            (~data & priv->lowalarm)) {
+               mask = POLLIN|POLLRDNORM;
+       }
+@@ -288,11 +266,26 @@
+ int etrax_gpio_wake_up_check(void)
+ {
+-      struct gpio_private *priv = alarmlist;
++      struct gpio_private *priv;
+       unsigned long data = 0;
++      unsigned long flags;
+         int ret = 0;
++      spin_lock_irqsave(&alarm_lock, flags);
++      priv = alarmlist;
+       while (priv) {
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++              if (priv->minor == GPIO_MINOR_V) {
++                      data = (unsigned long)cached_virtual_gpio_read;
++              }
++              else {
++                      data = *data_in[priv->minor];
++                      if (priv->minor == GPIO_MINOR_A) {
++                              priv->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN);
++                      }
++              }
++#else
+               data = *data_in[priv->minor];
++#endif
+               if ((data & priv->highalarm) ||
+                   (~data & priv->lowalarm)) {
+                       DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor));
+@@ -301,11 +294,12 @@
+               }
+               priv = priv->next;
+       }
++      spin_unlock_irqrestore(&alarm_lock, flags);
+         return ret;
+ }
+-static irqreturn_t
+-gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++static irqreturn_t 
++gpio_poll_timer_interrupt(int irq, void *dev_id)
+ {
+       if (gpio_some_alarms) {
+               return IRQ_RETVAL(etrax_gpio_wake_up_check());
+@@ -314,14 +308,17 @@
+ }
+ static irqreturn_t
+-gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++gpio_pa_interrupt(int irq, void *dev_id)
+ {
+       reg_gio_rw_intr_mask intr_mask;
+       reg_gio_r_masked_intr masked_intr;
+       reg_gio_rw_ack_intr ack_intr;
+       unsigned long tmp;
+       unsigned long tmp2;
+-
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++      unsigned char enable_gpiov_ack = 0;
++#endif
++      
+       /* Find what PA interrupts are active */
+       masked_intr = REG_RD(gio, regi_gio, r_masked_intr);
+       tmp = REG_TYPE_CONV(unsigned long, reg_gio_r_masked_intr, masked_intr);
+@@ -331,6 +328,17 @@
+       tmp &= (gpio_pa_high_alarms | gpio_pa_low_alarms);
+       spin_unlock(&alarm_lock);
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++      /* Something changed on virtual GPIO. Interrupt is acked by
++       * reading the device.
++       */
++      if (tmp & (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN)) {
++              i2c_read(VIRT_I2C_ADDR, (void *)&cached_virtual_gpio_read,
++                         sizeof(cached_virtual_gpio_read));
++              enable_gpiov_ack = 1;
++      }
++#endif
++
+       /* Ack them */
+       ack_intr = REG_TYPE_CONV(reg_gio_rw_ack_intr, unsigned long, tmp);
+       REG_WR(gio, regi_gio, rw_ack_intr, ack_intr);
+@@ -339,13 +347,21 @@
+       intr_mask = REG_RD(gio, regi_gio, rw_intr_mask);
+       tmp2 = REG_TYPE_CONV(unsigned long, reg_gio_rw_intr_mask, intr_mask);
+       tmp2 &= ~tmp;
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++      /* Do not disable interrupt on virtual GPIO. Changes on virtual
++       * pins are only noticed by an interrupt.
++       */
++      if (enable_gpiov_ack)   {
++              tmp2 |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN);
++      }
++#endif
+       intr_mask = REG_TYPE_CONV(reg_gio_rw_intr_mask, unsigned long, tmp2);
+       REG_WR(gio, regi_gio, rw_intr_mask, intr_mask);
+       if (gpio_some_alarms) {
+               return IRQ_RETVAL(etrax_gpio_wake_up_check());
+       }
+-        return IRQ_NONE;
++      return IRQ_NONE;
+ }
+@@ -358,8 +374,13 @@
+       unsigned long shadow;
+       volatile unsigned long *port;
+       ssize_t retval = count;
+-      /* Only bits 0-7 may be used for write operations but allow all
++      /* Only bits 0-7 may be used for write operations but allow all 
+          devices except leds... */
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++      if (priv->minor == GPIO_MINOR_V) {
++              return -EFAULT;
++      }
++#endif
+       if (priv->minor == GPIO_MINOR_LEDS) {
+               return -EFAULT;
+       }
+@@ -416,25 +437,24 @@
+ static int
+ gpio_open(struct inode *inode, struct file *filp)
+-{
++{     
+       struct gpio_private *priv;
+       int p = iminor(inode);
+       if (p > GPIO_MINOR_LAST)
+               return -EINVAL;
+-      priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private),
++      priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private), 
+                                             GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
++      memset(priv, 0, sizeof(*priv));
+       priv->minor = p;
+-      /* initialize the io/alarm struct and link it into our alarmlist */
++      /* initialize the io/alarm struct */
+-      priv->next = alarmlist;
+-      alarmlist = priv;
+       priv->clk_mask = 0;
+       priv->data_mask = 0;
+       priv->highalarm = 0;
+@@ -443,20 +463,30 @@
+       filp->private_data = (void *)priv;
++      /* link it into our alarmlist */
++      spin_lock_irq(&alarm_lock);
++      priv->next = alarmlist;
++      alarmlist = priv;
++      spin_unlock_irq(&alarm_lock);
++
+       return 0;
+ }
+ static int
+ gpio_release(struct inode *inode, struct file *filp)
+ {
+-      struct gpio_private *p = alarmlist;
+-      struct gpio_private *todel = (struct gpio_private *)filp->private_data;
++      struct gpio_private *p;
++      struct gpio_private *todel;
+       /* local copies while updating them: */
+       unsigned long a_high, a_low;
+       unsigned long some_alarms;
+       /* unlink from alarmlist and free the private structure */
++      spin_lock_irq(&alarm_lock);
++      p = alarmlist;
++      todel = (struct gpio_private *)filp->private_data;
++
+       if (p == todel) {
+               alarmlist = todel->next;
+       } else {
+@@ -473,6 +503,9 @@
+       a_low = 0;
+       while (p) {
+               if (p->minor == GPIO_MINOR_A) {
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++                      p->lowalarm |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN);
++#endif
+                       a_high |= p->highalarm;
+                       a_low |= p->lowalarm;
+               }
+@@ -483,23 +516,30 @@
+               p = p->next;
+       }
+-      spin_lock(&alarm_lock);
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++      /* Variables 'some_alarms' and 'a_low' needs to be set here again
++       * to ensure that interrupt for virtual GPIO is handled.
++       */
++      some_alarms = 1;
++      a_low |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN);
++#endif
++
+       gpio_some_alarms = some_alarms;
+       gpio_pa_high_alarms = a_high;
+       gpio_pa_low_alarms = a_low;
+-      spin_unlock(&alarm_lock);
++      spin_unlock_irq(&alarm_lock);
+       return 0;
+ }
+-/* Main device API. ioctl's to read/set/clear bits, as well as to
++/* Main device API. ioctl's to read/set/clear bits, as well as to 
+  * set alarms to wait for using a subsequent select().
+  */
+ unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg)
+ {
+-      /* Set direction 0=unchanged 1=input,
+-       * return mask with 1=input
++      /* Set direction 0=unchanged 1=input, 
++       * return mask with 1=input 
+        */
+       unsigned long flags;
+       unsigned long dir_shadow;
+@@ -512,6 +552,10 @@
+       if (priv->minor == GPIO_MINOR_A)
+               dir_shadow ^= 0xFF;    /* Only 8 bits */
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++      else if (priv->minor == GPIO_MINOR_V)
++              dir_shadow ^= 0xFFFF;  /* Only 16 bits */
++#endif
+       else
+               dir_shadow ^= 0x3FFFF; /* Only 18 bits */
+       return dir_shadow;
+@@ -546,6 +590,11 @@
+               return -EINVAL;
+       }
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++      if (priv->minor == GPIO_MINOR_V)
++              return virtual_gpio_ioctl(file, cmd, arg);
++#endif
++
+       switch (_IOC_NR(cmd)) {
+       case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
+               // read the port
+@@ -553,8 +602,6 @@
+               break;
+       case IO_SETBITS:
+               local_irq_save(flags);
+-                if (arg & 0x04)
+-                  printk("GPIO SET 2\n");
+               // set changeable bits with a 1 in arg
+               shadow = *data_out[priv->minor];
+               shadow |=  (arg & changeable_bits[priv->minor]);
+@@ -563,8 +610,6 @@
+               break;
+       case IO_CLRBITS:
+               local_irq_save(flags);
+-                if (arg & 0x04)
+-                  printk("GPIO CLR 2\n");
+               // clear changeable bits with a 1 in arg
+               shadow = *data_out[priv->minor];
+               shadow &=  ~(arg & changeable_bits[priv->minor]);
+@@ -574,52 +619,52 @@
+       case IO_HIGHALARM:
+               // set alarm when bits with 1 in arg go high
+               priv->highalarm |= arg;
+-              spin_lock(&alarm_lock);
++              spin_lock_irqsave(&alarm_lock, flags);
+               gpio_some_alarms = 1;
+               if (priv->minor == GPIO_MINOR_A) {
+                       gpio_pa_high_alarms |= arg;
+               }
+-              spin_unlock(&alarm_lock);
++              spin_unlock_irqrestore(&alarm_lock, flags);
+               break;
+       case IO_LOWALARM:
+               // set alarm when bits with 1 in arg go low
+               priv->lowalarm |= arg;
+-              spin_lock(&alarm_lock);
++              spin_lock_irqsave(&alarm_lock, flags);
+               gpio_some_alarms = 1;
+               if (priv->minor == GPIO_MINOR_A) {
+                       gpio_pa_low_alarms |= arg;
+               }
+-              spin_unlock(&alarm_lock);
++              spin_unlock_irqrestore(&alarm_lock, flags);
+               break;
+       case IO_CLRALARM:
+               // clear alarm for bits with 1 in arg
+               priv->highalarm &= ~arg;
+               priv->lowalarm  &= ~arg;
+-              spin_lock(&alarm_lock);
++              spin_lock_irqsave(&alarm_lock, flags);
+               if (priv->minor == GPIO_MINOR_A) {
+-                      if (gpio_pa_high_alarms & arg ||
++                      if (gpio_pa_high_alarms & arg || 
+                           gpio_pa_low_alarms & arg) {
+                               /* Must update the gpio_pa_*alarms masks */
+                       }
+               }
+-              spin_unlock(&alarm_lock);
++              spin_unlock_irqrestore(&alarm_lock, flags);
+               break;
+       case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
+               /* Read direction 0=input 1=output */
+               return *dir_oe[priv->minor];
+       case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
+-              /* Set direction 0=unchanged 1=input,
+-               * return mask with 1=input
++              /* Set direction 0=unchanged 1=input, 
++               * return mask with 1=input 
+                */
+               return setget_input(priv, arg);
+               break;
+       case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
+-              /* Set direction 0=unchanged 1=output,
+-               * return mask with 1=output
++              /* Set direction 0=unchanged 1=output, 
++               * return mask with 1=output 
+                */
+               return setget_output(priv, arg);
+-      case IO_CFG_WRITE_MODE:
++      case IO_CFG_WRITE_MODE: 
+       {
+               unsigned long dir_shadow;
+               dir_shadow = *dir_oe[priv->minor];
+@@ -641,7 +686,7 @@
+               }
+               break;
+       }
+-      case IO_READ_INBITS:
++      case IO_READ_INBITS: 
+               /* *arg is result of reading the input pins */
+               val = *data_in[priv->minor];
+               if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
+@@ -654,7 +699,7 @@
+               if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
+                       return -EFAULT;
+               break;
+-      case IO_SETGET_INPUT:
++      case IO_SETGET_INPUT: 
+               /* bits set in *arg is set to input,
+                * *arg updated with current input pins.
+                */
+@@ -684,6 +729,132 @@
+       return 0;
+ }
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++static int
++virtual_gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
++{
++      unsigned long flags;
++      unsigned short val;
++      unsigned short shadow;
++      struct gpio_private *priv = (struct gpio_private *)file->private_data;
++
++      switch (_IOC_NR(cmd)) {
++      case IO_SETBITS:
++              local_irq_save(flags);
++              // set changeable bits with a 1 in arg
++              i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
++              shadow |= ~*dir_oe[priv->minor];
++              shadow |= (arg & changeable_bits[priv->minor]);
++              i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
++              local_irq_restore(flags);
++              break;
++      case IO_CLRBITS:
++              local_irq_save(flags);
++              // clear changeable bits with a 1 in arg
++              i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
++              shadow |= ~*dir_oe[priv->minor];
++              shadow &= ~(arg & changeable_bits[priv->minor]);
++              i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
++              local_irq_restore(flags);
++              break;
++      case IO_HIGHALARM:
++              // set alarm when bits with 1 in arg go high
++              priv->highalarm |= arg;
++              spin_lock(&alarm_lock);
++              gpio_some_alarms = 1;
++              spin_unlock(&alarm_lock);
++              break;
++      case IO_LOWALARM:
++              // set alarm when bits with 1 in arg go low
++              priv->lowalarm |= arg;
++              spin_lock(&alarm_lock);
++              gpio_some_alarms = 1;
++              spin_unlock(&alarm_lock);
++              break;
++      case IO_CLRALARM:
++              // clear alarm for bits with 1 in arg
++              priv->highalarm &= ~arg;
++              priv->lowalarm  &= ~arg;
++              spin_lock(&alarm_lock);
++              spin_unlock(&alarm_lock);
++              break;
++      case IO_CFG_WRITE_MODE: 
++      {
++              unsigned long dir_shadow;
++              dir_shadow = *dir_oe[priv->minor];
++              
++              priv->clk_mask = arg & 0xFF;
++              priv->data_mask = (arg >> 8) & 0xFF;
++              priv->write_msb = (arg >> 16) & 0x01;
++              /* Check if we're allowed to change the bits and
++               * the direction is correct
++               */
++              if (!((priv->clk_mask & changeable_bits[priv->minor]) &&
++                    (priv->data_mask & changeable_bits[priv->minor]) &&
++                    (priv->clk_mask & dir_shadow) &&
++                    (priv->data_mask & dir_shadow)))
++              {
++                      priv->clk_mask = 0;
++                      priv->data_mask = 0;
++                      return -EPERM;
++              }
++              break;
++      }
++      case IO_READ_INBITS: 
++              /* *arg is result of reading the input pins */
++              val = cached_virtual_gpio_read;
++              val &= ~*dir_oe[priv->minor];
++              if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
++                      return -EFAULT;
++              return 0;
++              break;
++      case IO_READ_OUTBITS:
++               /* *arg is result of reading the output shadow */
++              i2c_read(VIRT_I2C_ADDR, (void *)&val, sizeof(val));
++              val &= *dir_oe[priv->minor];
++              if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
++                      return -EFAULT;
++              break;
++      case IO_SETGET_INPUT:
++      {
++              /* bits set in *arg is set to input,
++               * *arg updated with current input pins.
++               */
++              unsigned short input_mask = ~*dir_oe[priv->minor];
++              if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
++                      return -EFAULT;
++              val = setget_input(priv, val);
++              if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
++                      return -EFAULT;
++              if ((input_mask & val) != input_mask) {
++                      /* Input pins changed. All ports desired as input
++                       * should be set to logic 1.
++                       */
++                      unsigned short change = input_mask ^ val;
++                      i2c_read(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
++                      shadow &= ~change;
++                      shadow |= val;
++                      i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
++              }
++              break;
++      }
++      case IO_SETGET_OUTPUT:
++              /* bits set in *arg is set to output,
++               * *arg updated with current output pins.
++               */
++              if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
++                      return -EFAULT;
++              val = setget_output(priv, val);
++              if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
++                      return -EFAULT;
++              break;
++      default:
++              return -EINVAL;
++      } /* switch */
++  return 0;
++}
++#endif /* CONFIG_ETRAX_VIRTUAL_GPIO */
++
+ static int
+ gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
+ {
+@@ -714,6 +885,66 @@
+       .release     = gpio_release,
+ };
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++static void
++virtual_gpio_init(void)
++{
++      reg_gio_rw_intr_cfg intr_cfg;
++      reg_gio_rw_intr_mask intr_mask;
++      unsigned short shadow;
++
++      shadow = ~virtual_rw_pv_oe; /* Input ports should be set to logic 1 */
++      shadow |= CONFIG_ETRAX_DEF_GIO_PV_OUT;
++      i2c_write(VIRT_I2C_ADDR, (void *)&shadow, sizeof(shadow));
++
++      /* Set interrupt mask and on what state the interrupt shall trigger.
++       * For virtual gpio the interrupt shall trigger on logic '0'.
++       */
++      intr_cfg = REG_RD(gio, regi_gio, rw_intr_cfg);
++      intr_mask = REG_RD(gio, regi_gio, rw_intr_mask);
++
++      switch (CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN) {
++         case 0:
++          intr_cfg.pa0 = regk_gio_lo;
++          intr_mask.pa0 = regk_gio_yes;
++          break;
++         case 1:
++          intr_cfg.pa1 = regk_gio_lo;
++          intr_mask.pa1 = regk_gio_yes;
++          break;
++         case 2:
++          intr_cfg.pa2 = regk_gio_lo;
++          intr_mask.pa2 = regk_gio_yes;
++          break;
++         case 3:
++          intr_cfg.pa3 = regk_gio_lo;
++          intr_mask.pa3 = regk_gio_yes;
++          break;
++         case 4:
++          intr_cfg.pa4 = regk_gio_lo;
++          intr_mask.pa4 = regk_gio_yes;
++          break;
++         case 5:
++          intr_cfg.pa5 = regk_gio_lo;
++          intr_mask.pa5 = regk_gio_yes;
++          break;
++         case 6:
++          intr_cfg.pa6 = regk_gio_lo;
++          intr_mask.pa6 = regk_gio_yes;
++          break;
++         case 7:
++          intr_cfg.pa7 = regk_gio_lo;
++          intr_mask.pa7 = regk_gio_yes;
++          break;
++      }
++
++      REG_WR(gio, regi_gio, rw_intr_cfg, intr_cfg);
++      REG_WR(gio, regi_gio, rw_intr_mask, intr_mask);
++
++      gpio_pa_low_alarms |= (1 << CONFIG_ETRAX_VIRTUAL_GPIO_INTERRUPT_PA_PIN);
++      gpio_some_alarms = 1;
++}
++#endif
+ /* main driver initialization routine, called from mem.c */
+@@ -732,17 +963,18 @@
+       }
+       /* Clear all leds */
+-      LED_NETWORK_SET(0);
++      LED_NETWORK_GRP0_SET(0);
++      LED_NETWORK_GRP1_SET(0);
+       LED_ACTIVE_SET(0);
+       LED_DISK_READ(0);
+       LED_DISK_WRITE(0);
+-      printk("ETRAX FS GPIO driver v2.5, (c) 2003-2005 Axis Communications AB\n");
++      printk("ETRAX FS GPIO driver v2.5, (c) 2003-2006 Axis Communications AB\n");
+       /* We call etrax_gpio_wake_up_check() from timer interrupt and
+        * from cpu_idle() in kernel/process.c
+        * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms
+        * in some tests.
+-       */
++       */  
+       if (request_irq(TIMER_INTR_VECT, gpio_poll_timer_interrupt,
+                       IRQF_SHARED | IRQF_DISABLED,"gpio poll", &alarmlist)) {
+               printk("err: timer0 irq for gpio\n");
+@@ -757,6 +989,10 @@
+       intr_mask.gen_io = 1;
+       REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
++#ifdef CONFIG_ETRAX_VIRTUAL_GPIO
++      virtual_gpio_init();
++#endif
++
+       return res;
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/i2c.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/i2c.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/i2c.c        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/i2c.c        2006-11-06 16:48:06.000000000 +0100
+@@ -8,11 +8,11 @@
+ *!
+ *! Nov 30 1998  Torbjorn Eliasson  Initial version.
+ *!              Bjorn Wesen        Elinux kernel version.
+-*! Jan 14 2000  Johan Adolfsson    Fixed PB shadow register stuff -
++*! Jan 14 2000  Johan Adolfsson    Fixed PB shadow register stuff - 
+ *!                                 don't use PB_I2C if DS1302 uses same bits,
+ *!                                 use PB.
+ *| June 23 2003 Pieter Grimmerink  Added 'i2c_sendnack'. i2c_readreg now
+-*|                                 generates nack on last received byte,
++*|                                 generates nack on last received byte, 
+ *|                                 instead of ack.
+ *|                                 i2c_getack changed data level while clock
+ *|                                 was high, causing DS75 to see  a stop condition
+@@ -22,7 +22,7 @@
+ *! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN
+ *!
+ *!***************************************************************************/
+-/* $Id: i2c.c,v 1.2 2005/05/09 15:29:49 starvik Exp $ */
++/* $Id: i2c.c,v 1.6 2006/11/06 15:48:06 imres Exp $ */
+ /****************** INCLUDE FILES SECTION ***********************************/
+ #include <linux/module.h>
+@@ -60,8 +60,8 @@
+ #define I2C_DATA_HIGH 1
+ #define I2C_DATA_LOW 0
+-#define i2c_enable()
+-#define i2c_disable()
++#define i2c_enable() 
++#define i2c_disable() 
+ /* enable or disable output-enable, to select output or input on the i2c bus */
+@@ -79,6 +79,8 @@
+ #define i2c_delay(usecs) udelay(usecs)
++static DEFINE_SPINLOCK(i2c_lock); /* Protect directions etc */
++
+ /****************** VARIABLE SECTION ************************************/
+ static struct crisv32_iopin cris_i2c_clk;
+@@ -154,7 +156,7 @@
+               } else {
+                       i2c_data(I2C_DATA_LOW);
+               }
+-
++              
+               i2c_delay(CLOCK_LOW_TIME/2);
+               i2c_clk(I2C_CLOCK_HIGH);
+               i2c_delay(CLOCK_HIGH_TIME);
+@@ -213,7 +215,7 @@
+       }
+       i2c_clk(I2C_CLOCK_HIGH);
+       i2c_delay(CLOCK_HIGH_TIME);
+-
++      
+       /*
+        * we leave the clock low, getbyte is usually followed
+        * by sendack/nack, they assume the clock to be low
+@@ -252,6 +254,7 @@
+        * generate ACK clock pulse
+        */
+       i2c_clk(I2C_CLOCK_HIGH);
++#if 0
+       /*
+        * Use PORT PB instead of I2C
+        * for input. (I2C not working)
+@@ -264,6 +267,8 @@
+       i2c_data(1);
+       i2c_disable();
+       i2c_dir_in();
++#endif
++
+       /*
+        * now wait for ack
+        */
+@@ -285,13 +290,15 @@
+     * before we enable our output. If we keep data high
+     * and enable output, we would generate a stop condition.
+     */
++#if 0
+    i2c_data(I2C_DATA_LOW);
+-
++   
+       /*
+        * end clock pulse
+        */
+       i2c_enable();
+       i2c_dir_out();
++#endif
+       i2c_clk(I2C_CLOCK_LOW);
+       i2c_delay(CLOCK_HIGH_TIME/4);
+       /*
+@@ -338,7 +345,7 @@
+        */
+       i2c_data(I2C_DATA_HIGH);
+       i2c_delay(CLOCK_LOW_TIME);
+-
++      
+       i2c_dir_in();
+ }
+@@ -369,24 +376,160 @@
+       i2c_delay(CLOCK_HIGH_TIME);
+       i2c_clk(I2C_CLOCK_LOW);
+       i2c_delay(CLOCK_LOW_TIME);
+-
++      
+       i2c_dir_in();
+ }
+ /*#---------------------------------------------------------------------------
+ *#
++*# FUNCTION NAME: i2c_write
++*#
++*# DESCRIPTION  : Writes a value to an I2C device
++*#
++*#--------------------------------------------------------------------------*/
++int
++i2c_write(unsigned char theSlave, void *data, size_t nbytes)
++{
++      int error, cntr = 3;
++      unsigned char bytes_wrote = 0;
++      unsigned char value;
++      unsigned long flags;
++
++      spin_lock(&i2c_lock);
++
++      do {
++              error = 0;
++              /*
++               * we don't like to be interrupted
++               */
++                local_irq_save(flags);
++
++              i2c_start();
++              /*
++               * send slave address
++               */
++              i2c_outbyte((theSlave & 0xfe));
++              /*
++               * wait for ack
++               */
++              if(!i2c_getack())
++                      error = 1;
++              /*
++               * send data
++               */
++              for (bytes_wrote = 0; bytes_wrote < nbytes; bytes_wrote++) {
++                      memcpy(&value, data + bytes_wrote, sizeof value);
++                      i2c_outbyte(value);
++                      /*
++                       * now it's time to wait for ack
++                       */
++                      if (!i2c_getack())
++                              error |= 4;
++              }
++              /*
++               * end byte stream
++               */
++              i2c_stop();
++              /*
++               * enable interrupt again
++               */
++              local_irq_restore(flags);
++              
++      } while(error && cntr--);
++
++      i2c_delay(CLOCK_LOW_TIME);
++      
++      spin_unlock(&i2c_lock);
++
++      return -error;
++}
++
++/*#---------------------------------------------------------------------------
++*#
++*# FUNCTION NAME: i2c_read
++*#
++*# DESCRIPTION  : Reads a value from an I2C device
++*#
++*#--------------------------------------------------------------------------*/
++int
++i2c_read(unsigned char theSlave, void *data, size_t nbytes)
++{
++      unsigned char b = 0;
++      unsigned char bytes_read = 0;
++      int error, cntr = 3;
++      unsigned long flags;
++
++      spin_lock(&i2c_lock);
++
++      do {
++              error = 0;
++              memset(data, 0, nbytes);
++              /*
++               * we don't like to be interrupted
++               */
++                local_irq_save(flags);
++              /*
++               * generate start condition
++               */
++              i2c_start();
++    
++              /*
++               * send slave address
++               */
++              i2c_outbyte((theSlave | 0x01));
++              /*
++               * wait for ack
++               */
++              if(!i2c_getack())
++                      error = 1;
++              /*
++               * fetch data
++               */
++                for (bytes_read = 0; bytes_read < nbytes; bytes_read++) {
++                      b = i2c_inbyte();
++                      memcpy(data + bytes_read, &b, sizeof b);
++
++                      if (bytes_read < (nbytes - 1)) {
++                              i2c_sendack();
++                      }
++              }
++              /*
++               * last received byte needs to be nacked
++               * instead of acked
++               */
++              i2c_sendnack();
++              /*
++               * end sequence
++               */
++              i2c_stop();
++              /*
++               * enable interrupt again
++               */
++              local_irq_restore(flags);
++              
++      } while(error && cntr--);
++
++      spin_unlock(&i2c_lock);
++
++      return -error;
++}
++
++/*#---------------------------------------------------------------------------
++*#
+ *# FUNCTION NAME: i2c_writereg
+ *#
+ *# DESCRIPTION  : Writes a value to an I2C device
+ *#
+ *#--------------------------------------------------------------------------*/
+ int
+-i2c_writereg(unsigned char theSlave, unsigned char theReg,
++i2c_writereg(unsigned char theSlave, unsigned char theReg, 
+            unsigned char theValue)
+ {
+       int error, cntr = 3;
+       unsigned long flags;
++      spin_lock(&i2c_lock);
++
+       do {
+               error = 0;
+               /*
+@@ -431,10 +574,12 @@
+                * enable interrupt again
+                */
+               local_irq_restore(flags);
+-
++              
+       } while(error && cntr--);
+       i2c_delay(CLOCK_LOW_TIME);
++      
++      spin_unlock(&i2c_lock);
+       return -error;
+ }
+@@ -453,6 +598,8 @@
+       int error, cntr = 3;
+       unsigned long flags;
++      spin_lock(&i2c_lock);
++
+       do {
+               error = 0;
+               /*
+@@ -463,7 +610,7 @@
+                * generate start condition
+                */
+               i2c_start();
+-
++    
+               /*
+                * send slave address
+                */
+@@ -482,7 +629,7 @@
+                * now it's time to wait for ack
+                */
+               if(!i2c_getack())
+-                      error = 1;
++                      error |= 2;
+               /*
+                * repeat start condition
+                */
+@@ -496,7 +643,7 @@
+                * wait for ack
+                */
+               if(!i2c_getack())
+-                      error = 1;
++                      error |= 4;
+               /*
+                * fetch register
+                */
+@@ -514,9 +661,11 @@
+                * enable interrupt again
+                */
+               local_irq_restore(flags);
+-
++              
+       } while(error && cntr--);
++      spin_unlock(&i2c_lock);
++
+       return b;
+ }
+@@ -546,7 +695,7 @@
+       switch (_IOC_NR(cmd)) {
+               case I2C_WRITEREG:
+                       /* write to an i2c slave */
+-                      D(printk("i2cw %d %d %d\n",
++                      D(printk("i2cw %d %d %d\n", 
+                                I2C_ARGSLAVE(arg),
+                                I2C_ARGREG(arg),
+                                I2C_ARGVALUE(arg)));
+@@ -558,18 +707,18 @@
+               {
+                       unsigned char val;
+                       /* read from an i2c slave */
+-                      D(printk("i2cr %d %d ",
++                      D(printk("i2cr %d %d ", 
+                               I2C_ARGSLAVE(arg),
+                               I2C_ARGREG(arg)));
+                       val = i2c_readreg(I2C_ARGSLAVE(arg), I2C_ARGREG(arg));
+                       D(printk("= %d\n", val));
+                       return val;
+-              }
++              }                                           
+               default:
+                       return -EINVAL;
+       }
+-
++      
+       return 0;
+ }
+@@ -583,28 +732,53 @@
+ int __init
+ i2c_init(void)
+ {
+-      int res;
++      static int res = 0;
++      static int first = 1;
++      
++      if (!first) {
++              return res;
++      }
++      first = 0;
+-      /* Setup and enable the Port B I2C interface */
++      /* Setup and enable the DATA and CLK pins */
+-        crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT);
+-        crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT);
++        res = crisv32_io_get_name(&cris_i2c_data, CONFIG_ETRAX_I2C_DATA_PORT);
++      if (res < 0) {
++              return res;
++      }
++      
++        res = crisv32_io_get_name(&cris_i2c_clk, CONFIG_ETRAX_I2C_CLK_PORT);
++      crisv32_io_set_dir(&cris_i2c_clk, crisv32_io_dir_out);
++      
++      return res;
++}
+-      /* register char device */
++int __init
++i2c_register(void)
++{
++
++      int res;
++      
++      res = i2c_init();
++      if (res < 0) {
++              return res;
++      }
++      
++      /* register char device */
+       res = register_chrdev(I2C_MAJOR, i2c_name, &i2c_fops);
+       if(res < 0) {
+               printk(KERN_ERR "i2c: couldn't get a major number.\n");
+               return res;
+       }
+-      printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n");
+-
++      printk(KERN_INFO "I2C driver v2.2, (c) 1999-2004 Axis Communications AB\n");
++      
+       return 0;
+ }
+ /* this makes sure that i2c_init is called during boot */
+-module_init(i2c_init);
++module_init(i2c_register);
+ /****************** END OF FILE i2c.c ********************************/
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/i2c.h linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/i2c.h
+--- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/i2c.h        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/i2c.h        2006-11-06 16:48:06.000000000 +0100
+@@ -3,6 +3,8 @@
+ /* High level I2C actions */
+ int __init i2c_init(void);
++int i2c_write(unsigned char theSlave, void *data, size_t nbytes);
++int i2c_read(unsigned char theSlave, void *data, size_t nbytes);
+ int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue);
+ unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/iop_fw_load.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/iop_fw_load.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/iop_fw_load.c        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/iop_fw_load.c        2005-04-07 11:27:46.000000000 +0200
+@@ -67,12 +67,12 @@
+               return -ENODEV;
+       /* get firmware */
+-      retval = request_firmware(&fw_entry,
+-                                fw_name,
++      retval = request_firmware(&fw_entry, 
++                                fw_name, 
+                                 &iop_spu_device[spu_inst]);
+       if (retval != 0)
+       {
+-              printk(KERN_ERR
++              printk(KERN_ERR 
+                      "iop_load_spu: Failed to load firmware \"%s\"\n",
+                      fw_name);
+               return retval;
+@@ -123,7 +123,7 @@
+       return retval;
+ }
+-int iop_fw_load_mpu(unsigned char *fw_name)
++int iop_fw_load_mpu(unsigned char *fw_name) 
+ {
+       const unsigned int start_addr = 0;
+       reg_iop_mpu_rw_ctrl mpu_ctrl;
+@@ -135,13 +135,13 @@
+       retval = request_firmware(&fw_entry, fw_name, &iop_mpu_device);
+       if (retval != 0)
+       {
+-              printk(KERN_ERR
++              printk(KERN_ERR 
+                      "iop_load_spu: Failed to load firmware \"%s\"\n",
+                      fw_name);
+               return retval;
+       }
+       data = (u32 *) fw_entry->data;
+-
++      
+       /* disable MPU */
+       mpu_ctrl.en = regk_iop_mpu_no;
+       REG_WR(iop_mpu, regi_iop_mpu, rw_ctrl, mpu_ctrl);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/nandflash.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/nandflash.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/nandflash.c  2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/nandflash.c  2006-10-16 14:56:46.000000000 +0200
+@@ -5,8 +5,8 @@
+  *
+  *  Derived from drivers/mtd/nand/spia.c
+  *      Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
+- *
+- * $Id: nandflash.c,v 1.3 2005/06/01 10:57:12 starvik Exp $
++ * 
++ * $Id: nandflash.c,v 1.8 2006/10/16 12:56:46 ricardw 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
+@@ -32,37 +32,53 @@
+ #define ALE_BIT 6
+ #define BY_BIT 7
++/* Bitmask for control pins */
++#define PIN_BITMASK ((1 << CE_BIT) | (1 << CLE_BIT) | (1 << ALE_BIT))
++
++/* Bitmask for mtd nand control bits */
++#define CTRL_BITMASK (NAND_NCE | NAND_CLE | NAND_ALE)
++
++
+ static struct mtd_info *crisv32_mtd = NULL;
+-/*
++/* 
+  *    hardware specific access to control-lines
+ */
+-static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd)
++static void crisv32_hwcontrol(struct mtd_info *mtd, int cmd,
++                            unsigned int ctrl)
+ {
+       unsigned long flags;
+-      reg_gio_rw_pa_dout dout = REG_RD(gio, regi_gio, rw_pa_dout);
++      reg_gio_rw_pa_dout dout;
++      struct nand_chip *this = mtd->priv;
+       local_irq_save(flags);
+-      switch(cmd){
+-              case NAND_CTL_SETCLE:
+-                   dout.data |= (1<<CLE_BIT);
+-                   break;
+-              case NAND_CTL_CLRCLE:
+-                   dout.data &= ~(1<<CLE_BIT);
+-                   break;
+-              case NAND_CTL_SETALE:
+-                   dout.data |= (1<<ALE_BIT);
+-                   break;
+-              case NAND_CTL_CLRALE:
+-                   dout.data &= ~(1<<ALE_BIT);
+-                   break;
+-              case NAND_CTL_SETNCE:
+-                   dout.data |= (1<<CE_BIT);
+-                   break;
+-              case NAND_CTL_CLRNCE:
+-                   dout.data &= ~(1<<CE_BIT);
+-                   break;
++
++      /* control bits change */
++      if (ctrl & NAND_CTRL_CHANGE) {
++              dout = REG_RD(gio, regi_gio, rw_pa_dout);
++              dout.data &= ~PIN_BITMASK;
++
++#if (CE_BIT == 4 && NAND_NCE == 1 &&  \
++     CLE_BIT == 5 && NAND_CLE == 2 && \
++     ALE_BIT == 6 && NAND_ALE == 4)
++              /* Pins in same order as control bits, but shifted.
++               * Optimize for this case; works for 2.6.18 */
++              dout.data |= ((ctrl & CTRL_BITMASK) ^ NAND_NCE) << CE_BIT;
++#else
++              /* the slow way */
++              if (!(ctrl & NAND_NCE))
++                      dout.data |= (1 << CE_BIT);
++              if (ctrl & NAND_CLE)
++                      dout.data |= (1 << CLE_BIT);
++              if (ctrl & NAND_ALE)
++                      dout.data |= (1 << ALE_BIT);
++#endif
++              REG_WR(gio, regi_gio, rw_pa_dout, dout);
+       }
+-      REG_WR(gio, regi_gio, rw_pa_dout, dout);
++
++      /* command to chip */
++      if (cmd != NAND_CMD_NONE)
++              writeb(cmd, this->IO_ADDR_W);
++
+       local_irq_restore(flags);
+ }
+@@ -129,26 +145,26 @@
+       /* Set address of NAND IO lines */
+       this->IO_ADDR_R = read_cs;
+       this->IO_ADDR_W = write_cs;
+-      this->hwcontrol = crisv32_hwcontrol;
++      this->cmd_ctrl = crisv32_hwcontrol;
+       this->dev_ready = crisv32_device_ready;
+       /* 20 us command delay time */
+-      this->chip_delay = 20;
+-      this->eccmode = NAND_ECC_SOFT;
++      this->chip_delay = 20;          
++      this->ecc.mode = 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 (crisv32_mtd, 1)) {
+               err = -ENXIO;
+               goto out_ior;
+       }
+-
++      
+       return crisv32_mtd;
+-
++      
+ out_ior:
+       iounmap((void *)read_cs);
+-      iounmap((void *)write_cs);
++      iounmap((void *)write_cs);      
+ out_mtd:
+       kfree (crisv32_mtd);
+         return NULL;
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/pcf8563.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/pcf8563.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/pcf8563.c    2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/pcf8563.c    2006-10-27 17:22:13.000000000 +0200
+@@ -10,7 +10,7 @@
+  * 400 kbits/s. The built-in word address register is incremented
+  * automatically after each written or read byte.
+  *
+- * Copyright (c) 2002-2003, Axis Communications AB
++ * Copyright (c) 2002-2006, Axis Communications AB
+  * All rights reserved.
+  *
+  * Author: Tobias Anderberg <tobiasa@axis.com>.
+@@ -37,24 +37,27 @@
+ #define PCF8563_MAJOR 121     /* Local major number. */
+ #define DEVICE_NAME   "rtc"   /* Name which is registered in /proc/devices. */
+ #define PCF8563_NAME  "PCF8563"
+-#define DRIVER_VERSION        "$Revision: 1.1 $"
++#define DRIVER_VERSION        "$Revision: 1.9 $"
+ /* Two simple wrapper macros, saves a few keystrokes. */
+ #define rtc_read(x) i2c_readreg(RTC_I2C_READ, x)
+ #define rtc_write(x,y) i2c_writereg(RTC_I2C_WRITE, x, y)
++static DEFINE_SPINLOCK(rtc_lock); /* Protect state etc */
++
+ static const unsigned char days_in_month[] =
+       { 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
+ int pcf8563_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+-int pcf8563_open(struct inode *, struct file *);
+-int pcf8563_release(struct inode *, struct file *);
++
++/* Cache VL bit value read at driver init since writing the RTC_SECOND 
++ * register clears the VL status.
++ */
++static int voltage_low = 0;
+ static struct file_operations pcf8563_fops = {
+       owner: THIS_MODULE,
+       ioctl: pcf8563_ioctl,
+-      open: pcf8563_open,
+-      release: pcf8563_release,
+ };
+ unsigned char
+@@ -62,7 +65,7 @@
+ {
+       unsigned char res = rtc_read(reg);
+-      /* The PCF8563 does not return 0 for unimplemented bits */
++      /* The PCF8563 does not return 0 for unimplemented bits. */
+       switch (reg) {
+               case RTC_SECONDS:
+               case RTC_MINUTES:
+@@ -95,11 +98,6 @@
+ void
+ pcf8563_writereg(int reg, unsigned char val)
+ {
+-#ifdef CONFIG_ETRAX_RTC_READONLY
+-      if (reg == RTC_CONTROL1 || (reg >= RTC_SECONDS && reg <= RTC_YEAR))
+-              return;
+-#endif
+-
+       rtc_write(reg, val);
+ }
+@@ -114,11 +112,13 @@
+       tm->tm_mon  = rtc_read(RTC_MONTH);
+       tm->tm_year = rtc_read(RTC_YEAR);
+-      if (tm->tm_sec & 0x80)
++      if (tm->tm_sec & 0x80) {
+               printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
+                      "information is no longer guaranteed!\n", PCF8563_NAME);
++      }
+-      tm->tm_year  = BCD_TO_BIN(tm->tm_year) + ((tm->tm_mon & 0x80) ? 100 : 0);
++      tm->tm_year  = BCD_TO_BIN(tm->tm_year) +
++                     ((tm->tm_mon & 0x80) ? 100 : 0);
+       tm->tm_sec  &= 0x7F;
+       tm->tm_min  &= 0x7F;
+       tm->tm_hour &= 0x3F;
+@@ -137,8 +137,20 @@
+ int __init
+ pcf8563_init(void)
+ {
++      static int res = 0;
++      static int first = 1;
++
++      if (!first) {
++              return res;
++      }
++      first = 0;
++
+       /* Initiate the i2c protocol. */
+-      i2c_init();
++      res = i2c_init();
++      if (res < 0) {
++              printk(KERN_CRIT "pcf8563_init: Failed to init i2c.\n");
++              return res;
++      }
+       /*
+        * First of all we need to reset the chip. This is done by
+@@ -170,31 +182,28 @@
+       if (rtc_write(RTC_WEEKDAY_ALARM, 0x80) < 0)
+               goto err;
+-      if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) {
+-              printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n",
+-                     PCF8563_NAME, PCF8563_MAJOR);
+-              return -1;
++      /* Check for low voltage, and warn about it. */
++      if (rtc_read(RTC_SECONDS) & 0x80) {
++              voltage_low = 1;
++              printk(KERN_WARNING "%s: RTC Voltage Low - reliable "
++                     "date/time information is no longer guaranteed!\n",
++                     PCF8563_NAME);
+       }
+-      printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME, DRIVER_VERSION);
+-
+-      /* Check for low voltage, and warn about it.. */
+-      if (rtc_read(RTC_SECONDS) & 0x80)
+-              printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
+-                     "information is no longer guaranteed!\n", PCF8563_NAME);
+-
+-      return 0;
++      return res;
+ err:
+       printk(KERN_INFO "%s: Error initializing chip.\n", PCF8563_NAME);
+-      return -1;
++      res = -1;
++      return res;
+ }
+ void __exit
+ pcf8563_exit(void)
+ {
+       if (unregister_chrdev(PCF8563_MAJOR, DEVICE_NAME) < 0) {
+-              printk(KERN_INFO "%s: Unable to unregister device.\n", PCF8563_NAME);
++              printk(KERN_INFO "%s: Unable to unregister device.\n",
++                     PCF8563_NAME);
+       }
+ }
+@@ -203,7 +212,8 @@
+  * POSIX says so!
+  */
+ int
+-pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd, unsigned long arg)
++pcf8563_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
++              unsigned long arg)
+ {
+       /* Some sanity checks. */
+       if (_IOC_TYPE(cmd) != RTC_MAGIC)
+@@ -217,31 +227,35 @@
+               {
+                       struct rtc_time tm;
+-                      memset(&tm, 0, sizeof (struct rtc_time));
++                      spin_lock(&rtc_lock);
++                      memset(&tm, 0, sizeof tm);
+                       get_rtc_time(&tm);
+-                      if (copy_to_user((struct rtc_time *) arg, &tm, sizeof tm)) {
++                      if (copy_to_user((struct rtc_time *) arg, &tm,
++                                       sizeof tm)) {
++                              spin_unlock(&rtc_lock);
+                               return -EFAULT;
+                       }
++                      spin_unlock(&rtc_lock);
++
+                       return 0;
+               }
+-
+               case RTC_SET_TIME:
+               {
+-#ifdef CONFIG_ETRAX_RTC_READONLY
+-                      return -EPERM;
+-#else
+                       int leap;
+                       int year;
+                       int century;
+                       struct rtc_time tm;
++                      memset(&tm, 0, sizeof tm);
+                       if (!capable(CAP_SYS_TIME))
+                               return -EPERM;
+-                      if (copy_from_user(&tm, (struct rtc_time *) arg, sizeof tm))
++                      if (copy_from_user(&tm, (struct rtc_time *) arg,
++                                         sizeof tm)) {
+                               return -EFAULT;
++                      }
+                       /* Convert from struct tm to struct rtc_time. */
+                       tm.tm_year += 1900;
+@@ -253,7 +267,8 @@
+                        * that years divisible by 400 _are_ leap years.
+                        */
+                       year = tm.tm_year;
+-                      leap = (tm.tm_mon == 2) && ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
++                      leap = (tm.tm_mon == 2) && 
++                              ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
+                       /* Perform some sanity checks. */
+                       if ((tm.tm_year < 1970) ||
+@@ -263,19 +278,23 @@
+                           (tm.tm_wday >= 7) ||
+                           (tm.tm_hour >= 24) ||
+                           (tm.tm_min >= 60) ||
+-                          (tm.tm_sec >= 60))
++                          (tm.tm_sec >= 60)) {
+                               return -EINVAL;
++                      }
+                       century = (tm.tm_year >= 2000) ? 0x80 : 0;
+                       tm.tm_year = tm.tm_year % 100;
+                       BIN_TO_BCD(tm.tm_year);
++                      BIN_TO_BCD(tm.tm_mon);
+                       BIN_TO_BCD(tm.tm_mday);
+                       BIN_TO_BCD(tm.tm_hour);
+                       BIN_TO_BCD(tm.tm_min);
+                       BIN_TO_BCD(tm.tm_sec);
+                       tm.tm_mon |= century;
++                      spin_lock(&rtc_lock);
++
+                       rtc_write(RTC_YEAR, tm.tm_year);
+                       rtc_write(RTC_MONTH, tm.tm_mon);
+                       rtc_write(RTC_WEEKDAY, tm.tm_wday); /* Not coded in BCD. */
+@@ -284,36 +303,40 @@
+                       rtc_write(RTC_MINUTES, tm.tm_min);
+                       rtc_write(RTC_SECONDS, tm.tm_sec);
++                      spin_unlock(&rtc_lock);
++
+                       return 0;
+-#endif /* !CONFIG_ETRAX_RTC_READONLY */
+               }
+-
+               case RTC_VLOW_RD:
+-              {
+-                      int vl_bit = 0;
+-
+-                      if (rtc_read(RTC_SECONDS) & 0x80) {
+-                              vl_bit = 1;
+-                              printk(KERN_WARNING "%s: RTC Voltage Low - reliable "
+-                                     "date/time information is no longer guaranteed!\n",
+-                                     PCF8563_NAME);
++                      if (voltage_low) {
++                              printk(KERN_WARNING "%s: RTC Voltage Low - "
++                                     "reliable date/time information is no "
++                                     "longer guaranteed!\n", PCF8563_NAME);
+                       }
+-                      if (copy_to_user((int *) arg, &vl_bit, sizeof(int)))
+-                              return -EFAULT;
++                      if (copy_to_user((int *) arg, &voltage_low, sizeof(int))) {
++                              return -EFAULT;
++                      }
++                      
+                       return 0;
+-              }
+               case RTC_VLOW_SET:
+               {
+-                      /* Clear the VL bit in the seconds register */
++                      /* Clear the VL bit in the seconds register in case 
++                       * the time has not been set already (which would
++                       * have cleared it). This does not really matter 
++                       * because of the cached voltage_low value but do it
++                       * anyway for consistency. */
++
+                       int ret = rtc_read(RTC_SECONDS);
+                       rtc_write(RTC_SECONDS, (ret & 0x7F));
++                      /* Clear the cached value. */
++                      voltage_low = 0;
++
+                       return 0;
+               }
+-
+               default:
+                       return -ENOTTY;
+       }
+@@ -321,17 +344,32 @@
+       return 0;
+ }
+-int
+-pcf8563_open(struct inode *inode, struct file *filp)
++static int __init 
++pcf8563_register(void)
+ {
+-      return 0;
+-}
++      if (pcf8563_init() < 0) {
++              printk(KERN_INFO "%s: Unable to initialize Real-Time Clock "
++                     "Driver, %s\n", PCF8563_NAME, DRIVER_VERSION);
++              return -1;
++      }
++
++      if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) {
++              printk(KERN_INFO "%s: Unable to get major numer %d for RTC "
++                     "device.\n", PCF8563_NAME, PCF8563_MAJOR);
++              return -1;
++      }
++
++      printk(KERN_INFO "%s Real-Time Clock Driver, %s\n", PCF8563_NAME,
++             DRIVER_VERSION);
++
++      /* Check for low voltage, and warn about it. */
++      if (voltage_low) {
++              printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
++                     "information is no longer guaranteed!\n", PCF8563_NAME);
++      }
+-int
+-pcf8563_release(struct inode *inode, struct file *filp)
+-{
+       return 0;
+ }
+-module_init(pcf8563_init);
++module_init(pcf8563_register);
+ module_exit(pcf8563_exit);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/pci/bios.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/pci/bios.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/pci/bios.c   2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/pci/bios.c   2006-10-13 14:43:15.000000000 +0200
+@@ -60,7 +60,7 @@
+       u16 cmd, old_cmd;
+       int idx;
+       struct resource *r;
+-
++        
+       pci_read_config_word(dev, PCI_COMMAND, &cmd);
+       old_cmd = cmd;
+       for(idx=0; idx<6; idx++) {
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/pci/dma.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/pci/dma.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/pci/dma.c    2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/pci/dma.c    2005-10-31 09:48:04.000000000 +0100
+@@ -62,7 +62,7 @@
+ {
+       struct dma_coherent_mem *mem = dev ? dev->dma_mem : NULL;
+       int order = get_order(size);
+-
++      
+       if (mem && vaddr >= mem->virt_base && vaddr < (mem->virt_base + (mem->size << PAGE_SHIFT))) {
+               int page = (vaddr - mem->virt_base) >> PAGE_SHIFT;
+@@ -120,7 +120,7 @@
+ void dma_release_declared_memory(struct device *dev)
+ {
+       struct dma_coherent_mem *mem = dev->dma_mem;
+-
++      
+       if(!mem)
+               return;
+       dev->dma_mem = NULL;
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/drivers/sync_serial.c linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/sync_serial.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/drivers/sync_serial.c        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/drivers/sync_serial.c        2007-01-09 10:29:20.000000000 +0100
+@@ -50,7 +50,7 @@
+ /*    readp          writep                                              */
+ /*                                                                       */
+ /* If the application keeps up the pace readp will be right after writep.*/
+-/* If the application can't keep the pace we have to throw away data.    */
++/* If the application can't keep the pace we have to throw away data.    */ 
+ /* The idea is that readp should be ready with the data pointed out by         */
+ /* Descr[i] when the DMA has filled in Descr[i+1].                       */
+ /* Otherwise we will discard                                           */
+@@ -65,6 +65,7 @@
+ #define IN_DESCR_SIZE 256
+ #define NUM_IN_DESCR (IN_BUFFER_SIZE/IN_DESCR_SIZE)
+ #define OUT_BUFFER_SIZE 4096
++#define NUM_OUT_DESCRS 4
+ #define DEFAULT_FRAME_RATE 0
+ #define DEFAULT_WORD_RATE 7
+@@ -112,7 +113,7 @@
+       dma_descr_data in_descr[NUM_IN_DESCR] __attribute__ ((__aligned__(16)));
+       dma_descr_context in_context __attribute__ ((__aligned__(32)));
+-      dma_descr_data out_descr __attribute__ ((__aligned__(16)));
++      dma_descr_data out_descr[NUM_OUT_DESCRS] __attribute__ ((__aligned__(16)));
+       dma_descr_context out_context __attribute__ ((__aligned__(32)));
+       wait_queue_head_t out_wait_q;
+       wait_queue_head_t in_wait_q;
+@@ -130,9 +131,9 @@
+ static int sync_serial_ioctl(struct inode*, struct file*,
+                            unsigned int cmd, unsigned long arg);
+-static ssize_t sync_serial_write(struct file * file, const char * buf,
++static ssize_t sync_serial_write(struct file * file, const char * buf, 
+                                size_t count, loff_t *ppos);
+-static ssize_t sync_serial_read(struct file *file, char *buf,
++static ssize_t sync_serial_read(struct file *file, char *buf, 
+                               size_t count, loff_t *ppos);
+ #if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \
+@@ -146,8 +147,8 @@
+ static void start_dma(struct sync_port *port, const char* data, int count);
+ static void start_dma_in(sync_port* port);
+ #ifdef SYNC_SER_DMA
+-static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs);
+-static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs);
++static irqreturn_t tr_interrupt(int irq, void *dev_id);
++static irqreturn_t rx_interrupt(int irq, void *dev_id);
+ #endif
+ #if (defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT0) && \
+@@ -157,7 +158,7 @@
+ #define SYNC_SER_MANUAL
+ #endif
+ #ifdef SYNC_SER_MANUAL
+-static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs);
++static irqreturn_t manual_interrupt(int irq, void *dev_id);
+ #endif
+ /* The ports */
+@@ -201,8 +202,8 @@
+ {
+       ports[0].enabled = 0;
+       ports[1].enabled = 0;
+-
+-      if (register_chrdev(SYNC_SERIAL_MAJOR,"sync serial", &sync_serial_fops) <0 )
++      
++      if (register_chrdev(SYNC_SERIAL_MAJOR,"sync serial", &sync_serial_fops) <0 ) 
+       {
+               printk("unable to get major for synchronous serial port\n");
+               return -EBUSY;
+@@ -243,13 +244,13 @@
+       DEBUG(printk("Init sync serial port %d\n", portnbr));
+-      port->port_nbr = portnbr;
++      port->port_nbr = portnbr;       
+       port->init_irqs = 1;
+       port->outp = port->out_buffer;
+       port->output = 1;
+       port->input = 0;
+-
++      
+       port->readp = port->flip;
+       port->writep = port->flip;
+       port->in_buffer_size = IN_BUFFER_SIZE;
+@@ -286,11 +287,16 @@
+       tr_cfg.sample_size = 7;
+       tr_cfg.sh_dir = regk_sser_msbfirst;
+       tr_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no;
++#if 0
+       tr_cfg.rate_ctrl = regk_sser_bulk;
+       tr_cfg.data_pin_use = regk_sser_dout;
++#else
++      tr_cfg.rate_ctrl = regk_sser_iso;
++      tr_cfg.data_pin_use = regk_sser_dout;
++#endif
+       tr_cfg.bulk_wspace = 1;
+       REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg);
+-
++      
+       rec_cfg.sample_size = 7;
+       rec_cfg.sh_dir = regk_sser_msbfirst;
+       rec_cfg.use_dma = port->use_dma ? regk_sser_yes : regk_sser_no;
+@@ -303,17 +309,17 @@
+       int avail;
+       unsigned char *start;
+       unsigned char *end;
+-
++      
+       start = (unsigned char*)port->readp; /* cast away volatile */
+       end = (unsigned char*)port->writep;  /* cast away volatile */
+       /* 0123456789  0123456789
+        *  -----      -    -----
+        *  ^rp  ^wp    ^wp ^rp
+        */
+-
++              
+       if (end >= start)
+               avail = end - start;
+-      else
++      else 
+               avail = port->in_buffer_size - (start - end);
+       return avail;
+ }
+@@ -323,17 +329,17 @@
+       int avail;
+       unsigned char *start;
+       unsigned char *end;
+-
++      
+       start = (unsigned char*)port->readp; /* cast away volatile */
+       end = (unsigned char*)port->writep;  /* cast away volatile */
+       /* 0123456789  0123456789
+        *  -----           -----
+        *  ^rp  ^wp    ^wp ^rp
+        */
+-
++              
+       if (end >= start)
+               avail = end - start;
+-      else
++      else 
+               avail = port->flip + port->in_buffer_size - start;
+       return avail;
+ }
+@@ -343,10 +349,10 @@
+       int dev = iminor(inode);
+       sync_port* port;
+       reg_dma_rw_cfg cfg = {.en = regk_dma_yes};
+-      reg_dma_rw_intr_mask intr_mask = {.data = regk_dma_yes};
+-
+-      DEBUG(printk("Open sync serial port %d\n", dev));
++      reg_dma_rw_intr_mask intr_mask = {.data = regk_dma_yes};        
++      DEBUG(printk("Open sync serial port %d\n", dev)); 
++  
+       if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
+       {
+               DEBUG(printk("Invalid minor %d\n", dev));
+@@ -354,7 +360,7 @@
+       }
+       port = &ports[dev];
+       /* Allow open this device twice (assuming one reader and one writer) */
+-      if (port->busy == 2)
++      if (port->busy == 2) 
+       {
+               DEBUG(printk("Device is busy.. \n"));
+               return -EBUSY;
+@@ -422,8 +428,8 @@
+                                                                DMA_VERBOSE_ON_ERROR,
+                                                                0,
+                                                                dma_sser1)) {
+-                                      free_irq(21, &ports[1]);
+-                                      free_irq(20, &ports[1]);
++                                      free_irq(DMA6_INTR_VECT, &ports[1]);
++                                      free_irq(DMA7_INTR_VECT, &ports[1]);
+                                       printk(KERN_CRIT "Can't allocate sync serial port 3 TX DMA channel");
+                                       return -EBUSY;
+                               } else if (crisv32_request_dma(SYNC_SER1_RX_DMA_NBR,
+@@ -446,7 +452,7 @@
+                       /* Enable DMA IRQs */
+                       REG_WR(dma, port->regi_dmain, rw_intr_mask, intr_mask);
+                       REG_WR(dma, port->regi_dmaout, rw_intr_mask, intr_mask);
+-                      /* Set up wordsize = 2 for DMAs. */
++                      /* Set up wordsize = 1 for DMAs. */
+                       DMA_WR_CMD (port->regi_dmain, regk_dma_set_w_size1);
+                       DMA_WR_CMD (port->regi_dmaout, regk_dma_set_w_size1);
+@@ -497,7 +503,7 @@
+       port = &ports[dev];
+       if (port->busy)
+               port->busy--;
+-      if (!port->busy)
++      if (!port->busy) 
+           /* XXX */ ;
+       return 0;
+ }
+@@ -508,17 +514,29 @@
+       unsigned int mask = 0;
+       sync_port* port;
+       DEBUGPOLL( static unsigned int prev_mask = 0; );
+-
++      
+       port = &ports[dev];
++
++      if (!port->started)
++      {
++              reg_sser_rw_cfg cfg = REG_RD(sser, port->regi_sser, rw_cfg);
++              reg_sser_rw_rec_cfg rec_cfg = REG_RD(sser, port->regi_sser, rw_rec_cfg);
++              cfg.en = regk_sser_yes;
++              rec_cfg.rec_en = port->input;
++              REG_WR(sser, port->regi_sser, rw_cfg, cfg);
++              REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg);
++              port->started = 1;
++      }
++
+       poll_wait(file, &port->out_wait_q, wait);
+       poll_wait(file, &port->in_wait_q, wait);
+       /* Some room to write */
+-      if (port->out_count < OUT_BUFFER_SIZE)
++      if (port->output && port->out_count < OUT_BUFFER_SIZE)
+               mask |=  POLLOUT | POLLWRNORM;
+       /* At least an inbufchunk of data */
+-      if (sync_data_avail(port) >= port->inbufchunk)
++      if (port->input && sync_data_avail(port) >= port->inbufchunk)
+               mask |= POLLIN | POLLRDNORM;
+-
++      
+       DEBUGPOLL(if (mask != prev_mask)
+             printk("sync_serial_poll: mask 0x%08X %s %s\n", mask,
+                    mask&POLLOUT?"POLLOUT":"", mask&POLLIN?"POLLIN":"");
+@@ -531,14 +549,15 @@
+                 unsigned int cmd, unsigned long arg)
+ {
+       int return_val = 0;
++      int dma_w_size = regk_dma_set_w_size1;
+       int dev = iminor(file->f_dentry->d_inode);
+       sync_port* port;
+       reg_sser_rw_tr_cfg tr_cfg;
+       reg_sser_rw_rec_cfg rec_cfg;
+-      reg_sser_rw_frm_cfg frm_cfg;
++      reg_sser_rw_frm_cfg frm_cfg;    
+       reg_sser_rw_cfg gen_cfg;
+       reg_sser_rw_intr_mask intr_mask;
+-
++        
+       if (dev < 0 || dev >= NUMBER_OF_PORTS || !ports[dev].enabled)
+       {
+               DEBUG(printk("Invalid minor %d\n", dev));
+@@ -558,9 +577,32 @@
+       case SSP_SPEED:
+               if (GET_SPEED(arg) == CODEC)
+               {
++                      unsigned int freq;
++
+                       gen_cfg.base_freq = regk_sser_f32;
+-                      /* FREQ = 0 => 4 MHz => clk_div = 7*/
+-                      gen_cfg.clk_div = 6 + (1 << GET_FREQ(arg));
++
++                      /* Clock divider will internally be
++                       * gen_cfg.clk_div + 1.
++                       */
++
++                      freq = GET_FREQ(arg);
++                      switch (freq)
++                      {
++                              case FREQ_32kHz:
++                              case FREQ_64kHz:
++                              case FREQ_128kHz:
++                              case FREQ_256kHz:
++                                      gen_cfg.clk_div = 125 * (1 << (freq - FREQ_256kHz)) - 1;
++                              break;
++                              case FREQ_512kHz:
++                                      gen_cfg.clk_div = 62;
++                              break;
++                              case FREQ_1MHz:
++                              case FREQ_2MHz:
++                              case FREQ_4MHz:
++                                      gen_cfg.clk_div = 8 * (1 << freq) - 1;
++                              break;
++                        }
+               }
+               else
+               {
+@@ -625,87 +667,118 @@
+                       case MASTER_OUTPUT:
+                               port->output = 1;
+                               port->input = 0;
++                              frm_cfg.out_on = regk_sser_tr;
++                              frm_cfg.frame_pin_dir = regk_sser_out;
+                               gen_cfg.clk_dir = regk_sser_out;
+                               break;
+                       case SLAVE_OUTPUT:
+                               port->output = 1;
+                               port->input = 0;
++                              frm_cfg.frame_pin_dir = regk_sser_in;
+                               gen_cfg.clk_dir = regk_sser_in;
+                               break;
+                       case MASTER_INPUT:
+                               port->output = 0;
+                               port->input = 1;
++                              frm_cfg.frame_pin_dir = regk_sser_out;
++                              frm_cfg.out_on = regk_sser_intern_tb;
+                               gen_cfg.clk_dir = regk_sser_out;
+                               break;
+                       case SLAVE_INPUT:
+                               port->output = 0;
+                               port->input = 1;
++                              frm_cfg.frame_pin_dir = regk_sser_in;
+                               gen_cfg.clk_dir = regk_sser_in;
+                               break;
+                       case MASTER_BIDIR:
+                               port->output = 1;
+                               port->input = 1;
++                              frm_cfg.frame_pin_dir = regk_sser_out;
++                              frm_cfg.out_on = regk_sser_intern_tb;
+                               gen_cfg.clk_dir = regk_sser_out;
+                               break;
+                       case SLAVE_BIDIR:
+                               port->output = 1;
+                               port->input = 1;
++                              frm_cfg.frame_pin_dir = regk_sser_in;
+                               gen_cfg.clk_dir = regk_sser_in;
+                               break;
+                       default:
+                               spin_unlock_irq(&port->lock);
+                               return -EINVAL;
+-
++                                                           
+               }
+               if (!port->use_dma || (arg == MASTER_OUTPUT || arg == SLAVE_OUTPUT))
+                       intr_mask.rdav = regk_sser_yes;
+               break;
+       case SSP_FRAME_SYNC:
+-              if (arg & NORMAL_SYNC)
++              if (arg & NORMAL_SYNC) {
++                      frm_cfg.rec_delay = 1;
+                       frm_cfg.tr_delay = 1;
++              }
+               else if (arg & EARLY_SYNC)
+-                      frm_cfg.tr_delay = 0;
++                      frm_cfg.rec_delay = frm_cfg.tr_delay = 0;
++              else if (arg & SECOND_WORD_SYNC) {
++                      frm_cfg.rec_delay = 17;
++                      frm_cfg.tr_delay = 1;
++              }
++              
++                      
+               tr_cfg.bulk_wspace = frm_cfg.tr_delay;
+               frm_cfg.early_wend = regk_sser_yes;
+-              if (arg & BIT_SYNC)
++              if (arg & BIT_SYNC) 
+                       frm_cfg.type = regk_sser_edge;
+               else if (arg & WORD_SYNC)
+                       frm_cfg.type = regk_sser_level;
+               else if (arg & EXTENDED_SYNC)
+                       frm_cfg.early_wend = regk_sser_no;
+-
++              
+               if (arg & SYNC_ON)
+                       frm_cfg.frame_pin_use = regk_sser_frm;
+               else if (arg & SYNC_OFF)
+                       frm_cfg.frame_pin_use = regk_sser_gio0;
+-
+-              if (arg & WORD_SIZE_8)
++              
++              if (arg & WORD_SIZE_8) {
+                       rec_cfg.sample_size = tr_cfg.sample_size = 7;
+-              else if (arg & WORD_SIZE_12)
++                      dma_w_size = regk_dma_set_w_size1;
++              }
++              else if (arg & WORD_SIZE_12) { 
+                       rec_cfg.sample_size = tr_cfg.sample_size = 11;
+-              else if (arg & WORD_SIZE_16)
++                      dma_w_size = regk_dma_set_w_size2;
++              }
++              else if (arg & WORD_SIZE_16) {                  
+                       rec_cfg.sample_size = tr_cfg.sample_size = 15;
+-              else if (arg & WORD_SIZE_24)
++                      dma_w_size = regk_dma_set_w_size2;
++              }
++              else if (arg & WORD_SIZE_24) {
+                       rec_cfg.sample_size = tr_cfg.sample_size = 23;
+-              else if (arg & WORD_SIZE_32)
++                      dma_w_size = regk_dma_set_w_size2;
++              }
++              else if (arg & WORD_SIZE_32) {
+                       rec_cfg.sample_size = tr_cfg.sample_size = 31;
++                      dma_w_size = regk_dma_set_w_size2;
++              }
+               if (arg & BIT_ORDER_MSB)
+                       rec_cfg.sh_dir = tr_cfg.sh_dir = regk_sser_msbfirst;
+               else if (arg & BIT_ORDER_LSB)
+                       rec_cfg.sh_dir = tr_cfg.sh_dir = regk_sser_lsbfirst;
+-
+-              if (arg & FLOW_CONTROL_ENABLE)
++              
++              if (arg & FLOW_CONTROL_ENABLE) {
++                      frm_cfg.status_pin_use = regk_sser_frm;
+                       rec_cfg.fifo_thr = regk_sser_thr16;
+-              else if (arg & FLOW_CONTROL_DISABLE)
++              }
++              else if (arg & FLOW_CONTROL_DISABLE) {
++                      frm_cfg.status_pin_use = regk_sser_gio0;
+                       rec_cfg.fifo_thr = regk_sser_inf;
++              }
+               if (arg & CLOCK_NOT_GATED)
+                       gen_cfg.gate_clk = regk_sser_no;
+               else if (arg & CLOCK_GATED)
+                       gen_cfg.gate_clk = regk_sser_yes;
+-
++              
+               break;
+       case SSP_IPOLARITY:
+               /* NOTE!! negedge is considered NORMAL */
+@@ -713,12 +786,12 @@
+                       rec_cfg.clk_pol = regk_sser_neg;
+               else if (arg & CLOCK_INVERT)
+                       rec_cfg.clk_pol = regk_sser_pos;
+-
++              
+               if (arg & FRAME_NORMAL)
+                       frm_cfg.level = regk_sser_pos_hi;
+               else if (arg & FRAME_INVERT)
+                       frm_cfg.level = regk_sser_neg_lo;
+-
++              
+               if (arg & STATUS_NORMAL)
+                       gen_cfg.hold_pol = regk_sser_pos;
+               else if (arg & STATUS_INVERT)
+@@ -726,15 +799,15 @@
+               break;
+       case SSP_OPOLARITY:
+               if (arg & CLOCK_NORMAL)
+-                      gen_cfg.out_clk_pol = regk_sser_neg;
+-              else if (arg & CLOCK_INVERT)
+                       gen_cfg.out_clk_pol = regk_sser_pos;
+-
++              else if (arg & CLOCK_INVERT)
++                      gen_cfg.out_clk_pol = regk_sser_neg;
++              
+               if (arg & FRAME_NORMAL)
+                       frm_cfg.level = regk_sser_pos_hi;
+               else if (arg & FRAME_INVERT)
+                       frm_cfg.level = regk_sser_neg_lo;
+-
++              
+               if (arg & STATUS_NORMAL)
+                       gen_cfg.hold_pol = regk_sser_pos;
+               else if (arg & STATUS_INVERT)
+@@ -772,9 +845,10 @@
+       if (port->started)
+       {
+-              tr_cfg.tr_en = port->output;
+               rec_cfg.rec_en = port->input;
++              gen_cfg.en = (port->output | port->input);
+       }
++        
+       REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg);
+       REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg);
+@@ -782,11 +856,24 @@
+       REG_WR(sser, port->regi_sser, rw_intr_mask, intr_mask);
+       REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg);
++
++      if (cmd == SSP_FRAME_SYNC && 
++          (arg & (WORD_SIZE_8 | WORD_SIZE_12 | WORD_SIZE_16 | WORD_SIZE_24 | WORD_SIZE_32))) {
++              int en = gen_cfg.en;
++              gen_cfg.en = 0;
++              REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg);
++              /* ##### Should DMA be stoped before we change dma size? */
++              DMA_WR_CMD (port->regi_dmain, dma_w_size);
++              DMA_WR_CMD (port->regi_dmaout, dma_w_size);
++              gen_cfg.en = en;
++              REG_WR(sser, port->regi_sser, rw_cfg, gen_cfg);
++      }
++
+       spin_unlock_irq(&port->lock);
+       return return_val;
+ }
+-static ssize_t sync_serial_write(struct file * file, const char * buf,
++static ssize_t sync_serial_write(struct file * file, const char * buf, 
+                                  size_t count, loff_t *ppos)
+ {
+       int dev = iminor(file->f_dentry->d_inode);
+@@ -807,7 +894,7 @@
+       DEBUGWRITE(printk("W d%d c %lu (%d/%d)\n", port->port_nbr, count, port->out_count, OUT_BUFFER_SIZE));
+       /* Space to end of buffer */
+-      /*
++      /* 
+        * out_buffer <c1>012345<-   c    ->OUT_BUFFER_SIZE
+        *            outp^    +out_count
+                               ^free_outp
+@@ -824,7 +911,7 @@
+       free_outp = outp + port->out_count;
+       spin_unlock_irqrestore(&port->lock, flags);
+       out_buffer = (unsigned long)port->out_buffer;
+-
++      
+       /* Find out where and how much to write */
+       if (free_outp >= out_buffer + OUT_BUFFER_SIZE)
+               free_outp -= OUT_BUFFER_SIZE;
+@@ -834,7 +921,7 @@
+               c = outp - free_outp;
+       if (c > count)
+               c = count;
+-
++      
+ //    DEBUGWRITE(printk("w op %08lX fop %08lX c %lu\n", outp, free_outp, c));
+       if (copy_from_user((void*)free_outp, buf, c))
+               return -EFAULT;
+@@ -854,13 +941,10 @@
+       if (!port->started)
+       {
+               reg_sser_rw_cfg cfg = REG_RD(sser, port->regi_sser, rw_cfg);
+-              reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, rw_tr_cfg);
+               reg_sser_rw_rec_cfg rec_cfg = REG_RD(sser, port->regi_sser, rw_rec_cfg);
+               cfg.en = regk_sser_yes;
+-              tr_cfg.tr_en = port->output;
+               rec_cfg.rec_en = port->input;
+               REG_WR(sser, port->regi_sser, rw_cfg, cfg);
+-              REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg);
+               REG_WR(sser, port->regi_sser, rw_rec_cfg, rec_cfg);
+               port->started = 1;
+       }
+@@ -887,7 +971,7 @@
+       }
+       /* Sleep until all sent */
+-
++      
+       add_wait_queue(&port->out_wait_q, &wait);
+       set_current_state(TASK_INTERRUPTIBLE);
+       spin_lock_irqsave(&port->lock, flags);
+@@ -916,13 +1000,13 @@
+       return count;
+ }
+-static ssize_t sync_serial_read(struct file * file, char * buf,
++static ssize_t sync_serial_read(struct file * file, char * buf, 
+                               size_t count, loff_t *ppos)
+ {
+       int dev = iminor(file->f_dentry->d_inode);
+       int avail;
+       sync_port *port;
+-      unsigned char* start;
++      unsigned char* start; 
+       unsigned char* end;
+       unsigned long flags;
+@@ -949,7 +1033,7 @@
+               port->started = 1;
+       }
+-
++      
+       /* Calculate number of available bytes */
+       /* Save pointers to avoid that they are modified by interrupt */
+       spin_lock_irqsave(&port->lock, flags);
+@@ -958,11 +1042,12 @@
+       spin_unlock_irqrestore(&port->lock, flags);
+       while ((start == end) && !port->full) /* No data */
+       {
++              DEBUGREAD(printk("&"));
+               if (file->f_flags & O_NONBLOCK)
+-              {
++              {  
+                       return -EAGAIN;
+               }
+-
++          
+               interruptible_sleep_on(&port->in_wait_q);
+               if (signal_pending(current))
+               {
+@@ -979,9 +1064,9 @@
+               avail = port->in_buffer_size;
+       else if (end > start)
+               avail = end - start;
+-      else
++      else 
+               avail = port->flip + port->in_buffer_size - start;
+-
++  
+       count = count > avail ? avail : count;
+       if (copy_to_user(buf, start, count))
+               return -EFAULT;
+@@ -1016,7 +1101,7 @@
+               data |= *port->outp++;
+               port->out_count-=2;
+               tr_data.data = data;
+-              REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);
++              REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);  
+               if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
+                       port->outp = port->out_buffer;
+       }
+@@ -1032,7 +1117,7 @@
+       case 24:
+               port->out_count-=3;
+               tr_data.data = *(unsigned short *)port->outp;
+-              REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);
++              REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);             
+               port->outp+=2;
+               tr_data.data = *port->outp++;
+               REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);
+@@ -1042,10 +1127,10 @@
+       case 32:
+               port->out_count-=4;
+               tr_data.data = *(unsigned short *)port->outp;
+-              REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);
++              REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);             
+               port->outp+=2;
+               tr_data.data = *(unsigned short *)port->outp;
+-              REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);
++              REG_WR(sser, port->regi_sser, rw_tr_data, tr_data);             
+               port->outp+=2;
+               if (port->outp >= port->out_buffer + OUT_BUFFER_SIZE)
+                       port->outp = port->out_buffer;
+@@ -1056,15 +1141,27 @@
+ static void start_dma(struct sync_port* port, const char* data, int count)
+ {
++      int i;
++      reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, rw_tr_cfg);
+       port->tr_running = 1;
+-      port->out_descr.buf = (char*)virt_to_phys((char*)data);
+-      port->out_descr.after = port->out_descr.buf + count;
+-      port->out_descr.eol = port->out_descr.intr = 1;
++      for (i = 0; i < NUM_OUT_DESCRS; i++) 
++      {
++              port->out_descr[i].buf = (char*)virt_to_phys(port->out_buffer + 1024*i);
++              port->out_descr[i].after = port->out_descr[i].buf + 1024;
++              port->out_descr[i].eol = 0;
++              port->out_descr[i].intr = 1;
++              port->out_descr[i].next = virt_to_phys(&port->out_descr[i+1]);
++      }
++      port->out_descr[i-1].next = virt_to_phys(&port->out_descr[0]);
+-      port->out_context.saved_data = (dma_descr_data*)virt_to_phys(&port->out_descr);
+-      port->out_context.saved_data_buf = port->out_descr.buf;
++      port->out_context.saved_data = (dma_descr_data*)virt_to_phys(&port->out_descr[0]);
++      port->out_context.saved_data_buf = port->out_descr[0].buf;
+       DMA_START_CONTEXT(port->regi_dmaout, virt_to_phys((char*)&port->out_context));
++
++      tr_cfg.tr_en = regk_sser_yes;
++      REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg);
++
+       DEBUGTXINT(printk("dma %08lX c %d\n", (unsigned long)data, count));
+ }
+@@ -1073,7 +1170,7 @@
+       int i;
+       char* buf;
+       port->writep = port->flip;
+-
++      
+       if (port->writep > port->flip + port->in_buffer_size)
+       {
+               panic("Offset too large in sync serial driver\n");
+@@ -1099,7 +1196,7 @@
+ }
+ #ifdef SYNC_SER_DMA
+-static irqreturn_t tr_interrupt(int irq, void *dev_id, struct pt_regs * regs)
++static irqreturn_t tr_interrupt(int irq, void *dev_id)
+ {
+       reg_dma_r_masked_intr masked;
+       reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes};
+@@ -1108,7 +1205,7 @@
+       unsigned int sentl;
+       int found = 0;
+-      for (i = 0; i < NUMBER_OF_PORTS; i++)
++      for (i = 0; i < NUMBER_OF_PORTS; i++) 
+       {
+               sync_port *port = &ports[i];
+               if (!port->enabled  || !port->use_dma )
+@@ -1133,18 +1230,21 @@
+                               if (c > port->out_count)
+                                       c = port->out_count;
+                               DEBUGTXINT(printk("tx_int DMAWRITE %i %i\n", sentl, c));
+-                              start_dma(port, port->outp, c);
++                              //start_dma(port, port->outp, c);
+                       } else  {
+-                              DEBUGTXINT(printk("tx_int DMA stop %i\n", sentl));
++                              reg_sser_rw_tr_cfg tr_cfg = REG_RD(sser, port->regi_sser, rw_tr_cfg);
++                              DEBUGTXINT(printk("tx_int DMA stop %i\n", sentl));                              
+                               port->tr_running = 0;
++                              tr_cfg.tr_en = regk_sser_no;
++                              REG_WR(sser, port->regi_sser, rw_tr_cfg, tr_cfg);
+                       }
+                       wake_up_interruptible(&port->out_wait_q); /* wake up the waiting process */
+-              }
++              } 
+       }
+       return IRQ_RETVAL(found);
+ } /* tr_interrupt */
+-static irqreturn_t rx_interrupt(int irq, void *dev_id, struct pt_regs * regs)
++static irqreturn_t rx_interrupt(int irq, void *dev_id)
+ {
+       reg_dma_r_masked_intr masked;
+       reg_dma_rw_ack_intr ack_intr = {.data = regk_dma_yes};
+@@ -1152,7 +1252,7 @@
+       int i;
+       int found = 0;
+-      for (i = 0; i < NUMBER_OF_PORTS; i++)
++      for (i = 0; i < NUMBER_OF_PORTS; i++) 
+       {
+               sync_port *port = &ports[i];
+@@ -1164,17 +1264,17 @@
+               if (masked.data) /* Descriptor interrupt */
+               {
+                       found = 1;
+-                      while (REG_RD(dma, port->regi_dmain, rw_data) !=
++                      while (REG_RD(dma, port->regi_dmain, rw_data) != 
+                              virt_to_phys(port->next_rx_desc)) {
+-
++                              DEBUGRXINT(printk("!"));
+                               if (port->writep + port->inbufchunk > port->flip + port->in_buffer_size) {
+                                       int first_size = port->flip + port->in_buffer_size - port->writep;
+                                       memcpy((char*)port->writep, phys_to_virt((unsigned)port->next_rx_desc->buf), first_size);
+                                       memcpy(port->flip, phys_to_virt((unsigned)port->next_rx_desc->buf+first_size), port->inbufchunk - first_size);
+                                       port->writep = port->flip + port->inbufchunk - first_size;
+                               } else {
+-                                      memcpy((char*)port->writep,
+-                                             phys_to_virt((unsigned)port->next_rx_desc->buf),
++                                      memcpy((char*)port->writep, 
++                                             phys_to_virt((unsigned)port->next_rx_desc->buf), 
+                                              port->inbufchunk);
+                                       port->writep += port->inbufchunk;
+                                       if (port->writep >= port->flip + port->in_buffer_size)
+@@ -1184,11 +1284,13 @@
+                                 {
+                                 port->full = 1;
+                                 }
+-
+-                              port->next_rx_desc->eol = 0;
+-                              port->prev_rx_desc->eol = 1;
+-                              port->prev_rx_desc = phys_to_virt((unsigned)port->next_rx_desc);
++                                
++                              port->next_rx_desc->eol = 1;
++                              port->prev_rx_desc->eol = 0;
++                              flush_dma_descr(port->prev_rx_desc,0); // Cache bug workaround
++                              port->prev_rx_desc = port->next_rx_desc;
+                               port->next_rx_desc = phys_to_virt((unsigned)port->next_rx_desc->next);
++                              flush_dma_descr(port->prev_rx_desc,1); // Cache bug workaround
+                               wake_up_interruptible(&port->in_wait_q); /* wake up the waiting process */
+                               DMA_CONTINUE(port->regi_dmain);
+                               REG_WR(dma, port->regi_dmain, rw_ack_intr, ack_intr);
+@@ -1201,7 +1303,7 @@
+ #endif /* SYNC_SER_DMA */
+ #ifdef SYNC_SER_MANUAL
+-static irqreturn_t manual_interrupt(int irq, void *dev_id, struct pt_regs * regs)
++static irqreturn_t manual_interrupt(int irq, void *dev_id)
+ {
+       int i;
+       int found = 0;
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/Makefile linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/Makefile
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/Makefile      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/Makefile      2006-12-06 14:17:02.000000000 +0100
+@@ -1,4 +1,4 @@
+-# $Id: Makefile,v 1.11 2004/12/17 10:16:13 starvik Exp $
++# $Id: Makefile,v 1.13 2006/12/06 13:17:02 starvik Exp $
+ #
+ # Makefile for the linux kernel.
+ #
+@@ -8,7 +8,7 @@
+ obj-y   := entry.o traps.o irq.o debugport.o dma.o pinmux.o \
+          process.o ptrace.o setup.o signal.o traps.o time.o \
+-         arbiter.o io.o
++         arbiter.o io.o cache.o cacheflush.o
+ obj-$(CONFIG_ETRAXFS_SIM) += vcs_hook.o
+@@ -16,6 +16,7 @@
+ obj-$(CONFIG_ETRAX_KGDB) += kgdb.o kgdb_asm.o
+ obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o
+ obj-$(CONFIG_MODULES)    += crisksyms.o
++obj-$(CONFIG_CPU_FREQ)   += cpufreq.o
+ clean:
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/arbiter.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/arbiter.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/arbiter.c     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/arbiter.c     2006-10-13 14:43:13.000000000 +0200
+@@ -6,7 +6,7 @@
+  * bandwidth (e.g. ethernet) and then the remaining slots are divided
+  * on all the active clients.
+  *
+- * Copyright (c) 2004, 2005 Axis Communications AB.
++ * Copyright (c) 2004, 2005, 2006 Axis Communications AB.
+  */
+ #include <asm/arch/hwregs/reg_map.h>
+@@ -44,35 +44,88 @@
+   {regi_marb_bp3}
+ };
+-static int requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS];
+-static int active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS];
++static u8 requested_slots[NBR_OF_REGIONS][NBR_OF_CLIENTS];
++static u8 active_clients[NBR_OF_REGIONS][NBR_OF_CLIENTS];
+ static int max_bandwidth[NBR_OF_REGIONS] = {SDRAM_BANDWIDTH, INTMEM_BANDWIDTH};
+ DEFINE_SPINLOCK(arbiter_lock);
+-static irqreturn_t
++static irqreturn_t 
+ crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs);
+-static void crisv32_arbiter_config(int region)
++/*
++ * "I'm the arbiter, I know the score.
++ *  From square one I'll be watching all 64."
++ * (memory arbiter slots, that is)
++ *
++ *  Or in other words:
++ * Program the memory arbiter slots for "region" according to what's
++ * in requested_slots[] and active_clients[], while minimizing
++ * latency. A caller may pass a non-zero positive amount for
++ * "unused_slots", which must then be the unallocated, remaining
++ * number of slots, free to hand out to any client.
++ */
++
++static void crisv32_arbiter_config(int region, int unused_slots)
+ {
+       int slot;
+       int client;
+       int interval = 0;
+-      int val[NBR_OF_SLOTS];
++
++      /*
++       * This vector corresponds to the hardware arbiter slots (see
++       * the hardware documentation for semantics). We initialize
++       * each slot with a suitable sentinel value outside the valid
++       * range {0 .. NBR_OF_CLIENTS - 1} and replace them with
++       * client indexes. Then it's fed to the hardware.
++       */
++      s8 val[NBR_OF_SLOTS];
+       for (slot = 0; slot < NBR_OF_SLOTS; slot++)
+-          val[slot] = NBR_OF_CLIENTS + 1;
++          val[slot] = -1;
+       for (client = 0; client < NBR_OF_CLIENTS; client++)
+       {
+           int pos;
++          /* Allocate the requested non-zero number of slots, but
++           * also give clients with zero-requests one slot each
++           * while stocks last. We do the latter here, in client
++           * order. This makes sure zero-request clients are the
++           * first to get to any spare slots, else those slots
++           * could, when bandwidth is allocated close to the limit,
++           * all be allocated to low-index non-zero-request clients
++           * in the default-fill loop below. Another positive but
++           * secondary effect is a somewhat better spread of the
++           * zero-bandwidth clients in the vector, avoiding some of
++           * the latency that could otherwise be caused by the
++           * partitioning of non-zero-bandwidth clients at low
++           * indexes and zero-bandwidth clients at high
++           * indexes. (Note that this spreading can only affect the
++           * unallocated bandwidth.)  All the above only matters for
++           * memory-intensive situations, of course.
++           */
+           if (!requested_slots[region][client])
+-             continue;
+-          interval = NBR_OF_SLOTS / requested_slots[region][client];
++          {
++              /*
++               * Skip inactive clients. Also skip zero-slot
++               * allocations in this pass when there are no known
++               * free slots.
++               */
++              if (!active_clients[region][client] || unused_slots <= 0)
++                      continue;
++
++              unused_slots--;
++
++              /* Only allocate one slot for this client. */
++              interval = NBR_OF_SLOTS;
++          }
++          else
++              interval = NBR_OF_SLOTS / requested_slots[region][client];
++
+           pos = 0;
+           while (pos < NBR_OF_SLOTS)
+           {
+-              if (val[pos] != NBR_OF_CLIENTS + 1)
++              if (val[pos] >= 0)
+                  pos++;
+               else
+               {
+@@ -85,7 +138,13 @@
+       client = 0;
+       for (slot = 0; slot < NBR_OF_SLOTS; slot++)
+       {
+-              if (val[slot] == NBR_OF_CLIENTS + 1)
++              /*
++               * Allocate remaining slots in round-robin
++               * client-number order for active clients. For this
++               * pass, we ignore requested bandwidth and previous
++               * allocations.
++               */
++              if (val[slot] < 0)
+               {
+                       int first = client;
+                       while(!active_clients[region][client]) {
+@@ -100,7 +159,7 @@
+                  REG_WR_INT_VECT(marb, regi_marb, rw_ext_slots, slot, val[slot]);
+               else if (region == INT_REGION)
+                  REG_WR_INT_VECT(marb, regi_marb, rw_int_slots, slot, val[slot]);
+-      }
++      }       
+ }
+ extern char _stext, _etext;
+@@ -111,18 +170,28 @@
+       if (initialized)
+               return;
+-
++  
+       initialized = 1;
+-      /* CPU caches are active. */
+-      active_clients[EXT_REGION][10] = active_clients[EXT_REGION][11] = 1;
+-        crisv32_arbiter_config(EXT_REGION);
+-        crisv32_arbiter_config(INT_REGION);
++      /*
++       * CPU caches are always set to active, but with zero
++       * bandwidth allocated. It should be ok to allocate zero
++       * bandwidth for the caches, because DMA for other channels
++       * will supposedly finish, once their programmed amount is
++       * done, and then the caches will get access according to the
++       * "fixed scheme" for unclaimed slots. Though, if for some
++       * use-case somewhere, there's a maximum CPU latency for
++       * e.g. some interrupt, we have to start allocating specific
++       * bandwidth for the CPU caches too.
++       */
++      active_clients[EXT_REGION][10] = active_clients[EXT_REGION][11] = 1; 
++        crisv32_arbiter_config(EXT_REGION, 0);
++        crisv32_arbiter_config(INT_REGION, 0);
+       if (request_irq(MEMARB_INTR_VECT, crisv32_arbiter_irq, IRQF_DISABLED,
+                         "arbiter", NULL))
+               printk(KERN_ERR "Couldn't allocate arbiter IRQ\n");
+-
++        
+ #ifndef CONFIG_ETRAX_KGDB
+         /* Global watch for writes to kernel text segment. */
+         crisv32_arbiter_watch(virt_to_phys(&_stext), &_etext - &_stext,
+@@ -130,6 +199,7 @@
+ #endif
+ }
++/* Main entry for bandwidth allocation. */
+ int crisv32_arbiter_allocate_bandwidth(int client, int region,
+@@ -141,39 +211,76 @@
+       int req;
+       crisv32_arbiter_init();
+-
++      
+       for (i = 0; i < NBR_OF_CLIENTS; i++)
+       {
+               total_assigned += requested_slots[region][i];
+               total_clients += active_clients[region][i];
+       }
+-      req = NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth);
+-      if (total_assigned + total_clients + req + 1 > NBR_OF_SLOTS)
++      /* Avoid division by 0 for 0-bandwidth requests. */
++      req = bandwidth == 0
++              ? 0 : NBR_OF_SLOTS / (max_bandwidth[region] / bandwidth);
++
++      /*
++       * We make sure that there are enough slots only for non-zero
++       * requests. Requesting 0 bandwidth *may* allocate slots,
++       * though if all bandwidth is allocated, such a client won't
++       * get any and will have to rely on getting memory access
++       * according to the fixed scheme that's the default when one
++       * of the slot-allocated clients doesn't claim their slot.
++       */
++      if (total_assigned + req > NBR_OF_SLOTS)
+          return -ENOMEM;
+       active_clients[region][client] = 1;
+       requested_slots[region][client] = req;
+-      crisv32_arbiter_config(region);
++      crisv32_arbiter_config(region, NBR_OF_SLOTS - total_assigned);
+       return 0;
+ }
++/*
++ * Main entry for bandwidth deallocation.
++ *
++ * Strictly speaking, for a somewhat constant set of clients where
++ * each client gets a constant bandwidth and is just enabled or
++ * disabled (somewhat dynamically), no action is necessary here to
++ * avoid starvation for non-zero-allocation clients, as the allocated
++ * slots will just be unused. However, handing out those unused slots
++ * to active clients avoids needless latency if the "fixed scheme"
++ * would give unclaimed slots to an eager low-index client.
++ */
++
++void crisv32_arbiter_deallocate_bandwidth(int client, int region)
++{
++      int i;
++      int total_assigned = 0;
++
++      requested_slots[region][client] = 0;
++      active_clients[region][client] = 0;
++
++      for (i = 0; i < NBR_OF_CLIENTS; i++)
++              total_assigned += requested_slots[region][i];
++
++      crisv32_arbiter_config(region, NBR_OF_SLOTS - total_assigned);
++}
++
+ int crisv32_arbiter_watch(unsigned long start, unsigned long size,
+                           unsigned long clients, unsigned long accesses,
+                           watch_callback* cb)
+ {
+       int i;
+-
++  
+       crisv32_arbiter_init();
+-
++  
+       if (start > 0x80000000) {
+               printk("Arbiter: %lX doesn't look like a physical address", start);
+               return -EFAULT;
+       }
+       spin_lock(&arbiter_lock);
+-
++        
+       for (i = 0; i < NUMBER_OF_BP; i++) {
+               if (!watches[i].used) {
+                       reg_marb_rw_intr_mask intr_mask = REG_RD(marb, regi_marb, rw_intr_mask);
+@@ -214,7 +321,7 @@
+       crisv32_arbiter_init();
+       spin_lock(&arbiter_lock);
+-
++  
+       if ((id < 0) || (id >= NUMBER_OF_BP) || (!watches[id].used)) {
+               spin_unlock(&arbiter_lock);
+               return -EINVAL;
+@@ -239,7 +346,7 @@
+ extern void show_registers(struct pt_regs *regs);
+-static irqreturn_t
++static irqreturn_t 
+ crisv32_arbiter_irq(int irq, void* dev_id, struct pt_regs* regs)
+ {
+       reg_marb_r_masked_intr masked_intr = REG_RD(marb, regi_marb, r_masked_intr);
+@@ -248,10 +355,10 @@
+       reg_marb_bp_r_brk_op r_op;
+       reg_marb_bp_r_brk_first_client r_first;
+       reg_marb_bp_r_brk_size r_size;
+-      reg_marb_bp_rw_ack ack = {0};
++      reg_marb_bp_rw_ack ack = {0};  
+       reg_marb_rw_ack_intr ack_intr = {.bp0=1,.bp1=1,.bp2=1,.bp3=1};
+       struct crisv32_watch_entry* watch;
+-
++  
+       if (masked_intr.bp0) {
+               watch = &watches[0];
+               ack_intr.bp0 = regk_marb_yes;
+@@ -291,6 +398,6 @@
+       if (watch->cb)
+               watch->cb();
+-
++                       
+       return IRQ_HANDLED;
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/asm-offsets.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/asm-offsets.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/asm-offsets.c 2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/asm-offsets.c 2003-06-02 10:39:38.000000000 +0200
+@@ -16,8 +16,8 @@
+ {
+ #define ENTRY(entry) DEFINE(PT_ ## entry, offsetof(struct pt_regs, entry))
+       ENTRY(orig_r10);
+-      ENTRY(r13);
+-      ENTRY(r12);
++      ENTRY(r13); 
++      ENTRY(r12); 
+       ENTRY(r11);
+         ENTRY(r10);
+         ENTRY(r9);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/cache.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/cache.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/cache.c       1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/cache.c       2007-01-23 13:09:57.000000000 +0100
+@@ -0,0 +1,31 @@
++#include <linux/module.h>
++#include <asm/io.h>
++#include <asm/arch/cache.h>
++#include <asm/arch/hwregs/dma.h>
++
++// This file is used to workaround a cache bug, Guinness TR 106
++
++inline void flush_dma_descr(dma_descr_data* descr, int flush_buf)
++{
++      // Flush descriptor to make sure we get correct in_eop and after
++      asm volatile ("ftagd [%0]" :: "r" (descr));
++      // Flush buffer pointed out by descriptor
++      if (flush_buf)
++              cris_flush_cache_range(phys_to_virt((unsigned)descr->buf),                                     (unsigned)(descr->after - descr->buf));
++}
++
++void flush_dma_list(dma_descr_data* descr)
++{
++  while(1)
++  {
++    flush_dma_descr(descr, 1);
++    if (descr->eol)
++      break;
++    descr = phys_to_virt((unsigned)descr->next);
++  }
++}
++
++EXPORT_SYMBOL(flush_dma_list);
++EXPORT_SYMBOL(flush_dma_descr);
++EXPORT_SYMBOL(cris_flush_cache);
++EXPORT_SYMBOL(cris_flush_cache_range);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/cacheflush.S linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/cacheflush.S
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/cacheflush.S  1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/cacheflush.S  2006-12-06 14:17:02.000000000 +0100
+@@ -0,0 +1,93 @@
++      .global cris_flush_cache_range
++cris_flush_cache_range:
++      move.d 1024, $r12
++      cmp.d $r11, $r12
++      bhi cris_flush_1KB
++      nop
++      add.d $r10, $r11
++cris_flush_last:
++      addq 32, $r10
++      cmp.d $r11, $r10
++      blt cris_flush_last
++      ftagd [$r10]
++      ret
++      nop
++cris_flush_1KB:
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ftagd [$r10]
++      addq 32, $r10
++      ba cris_flush_cache_range
++      sub.d $r12, $r11
++
++      .global cris_flush_cache
++cris_flush_cache:
++      moveq 0, $r10
++cris_flush_line:              
++      move.d 16*1024, $r11
++      addq 16, $r10
++      cmp.d $r10, $r11
++      blt cris_flush_line
++      fidxd [$r10]
++      ret
++      nop     
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/cpufreq.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/cpufreq.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/cpufreq.c     1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/cpufreq.c     2006-11-03 11:35:52.000000000 +0100
+@@ -0,0 +1,147 @@
++#include <linux/init.h>
++#include <linux/module.h>
++#include <linux/cpufreq.h>
++#include <asm/arch/hwregs/reg_map.h>
++#include <asm/arch/hwregs/reg_rdwr.h>
++#include <asm/arch/hwregs/config_defs.h>
++#include <asm/arch/hwregs/bif_core_defs.h>
++
++static int
++cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, void *data);
++
++static struct notifier_block cris_sdram_freq_notifier_block = {
++        .notifier_call  = cris_sdram_freq_notifier
++};
++
++static struct cpufreq_frequency_table cris_freq_table[] = {
++      {0x01,  6000},
++      {0x02,  200000},
++      {0,     CPUFREQ_TABLE_END},
++};
++
++static unsigned int cris_freq_get_cpu_frequency(unsigned int cpu)
++{
++      reg_config_rw_clk_ctrl clk_ctrl;
++      clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl);
++      return clk_ctrl.pll ? 200000 : 6000;
++}
++
++static void cris_freq_set_cpu_state (unsigned int state)
++{
++      int i;
++      struct cpufreq_freqs freqs;
++      reg_config_rw_clk_ctrl clk_ctrl;
++      clk_ctrl = REG_RD(config, regi_config, rw_clk_ctrl);
++
++      for_each_cpu(i) {
++              freqs.old = cris_freq_get_cpu_frequency(i);
++              freqs.new = cris_freq_table[state].frequency;
++              freqs.cpu = i;
++      }
++
++      cpufreq_notify_transition(&freqs, CPUFREQ_PRECHANGE);
++ 
++      local_irq_disable();
++
++      // Even though we may be SMP they will share the same clock
++      // so all settings are made on CPU0.
++      if (cris_freq_table[state].frequency == 200000)
++              clk_ctrl.pll = 1;
++      else
++              clk_ctrl.pll = 0;
++      REG_WR(config, regi_config, rw_clk_ctrl, clk_ctrl);
++
++      local_irq_enable();
++
++      cpufreq_notify_transition(&freqs, CPUFREQ_POSTCHANGE);
++};
++
++static int cris_freq_verify (struct cpufreq_policy *policy)
++{
++      return cpufreq_frequency_table_verify(policy, &cris_freq_table[0]);
++}
++
++static int cris_freq_target (struct cpufreq_policy *policy,
++                          unsigned int target_freq,
++                          unsigned int relation)
++{
++      unsigned int newstate = 0;
++
++      if (cpufreq_frequency_table_target(policy, cris_freq_table, target_freq, relation, &newstate))
++              return -EINVAL;
++
++      cris_freq_set_cpu_state(newstate);
++
++      return 0;
++}
++
++static int cris_freq_cpu_init(struct cpufreq_policy *policy)
++{
++      int result;
++
++      /* cpuinfo and default policy values */
++      policy->governor = CPUFREQ_DEFAULT_GOVERNOR;
++      policy->cpuinfo.transition_latency = 1000000; /* 1ms */
++      policy->cur = cris_freq_get_cpu_frequency(0);
++
++      result = cpufreq_frequency_table_cpuinfo(policy, cris_freq_table);
++      if (result)
++              return (result);
++
++      cpufreq_frequency_table_get_attr(cris_freq_table, policy->cpu);
++
++      return 0;
++}
++
++
++static int cris_freq_cpu_exit(struct cpufreq_policy *policy)
++{
++      cpufreq_frequency_table_put_attr(policy->cpu);
++      return 0;
++}
++
++
++static struct freq_attr* cris_freq_attr[] = {
++      &cpufreq_freq_attr_scaling_available_freqs,
++      NULL,
++};
++
++static struct cpufreq_driver cris_freq_driver = {
++      .get    = cris_freq_get_cpu_frequency,
++      .verify = cris_freq_verify,
++      .target = cris_freq_target,
++      .init   = cris_freq_cpu_init,
++      .exit   = cris_freq_cpu_exit,
++      .name   = "cris_freq",
++      .owner  = THIS_MODULE,
++      .attr   = cris_freq_attr,
++};
++
++static int __init cris_freq_init(void)
++{
++      int ret;
++      ret = cpufreq_register_driver(&cris_freq_driver);
++      cpufreq_register_notifier(&cris_sdram_freq_notifier_block,
++                                  CPUFREQ_TRANSITION_NOTIFIER);
++      return ret;
++}
++
++static int
++cris_sdram_freq_notifier(struct notifier_block *nb, unsigned long val, void *data)
++{
++      int i;
++      struct cpufreq_freqs *freqs = data;
++      if (val == CPUFREQ_PRECHANGE) {
++              reg_bif_core_rw_sdram_timing timing = 
++                REG_RD(bif_core, regi_bif_core, rw_sdram_timing);
++              timing.cpd = (freqs->new == 200000 ? 0 : 1);
++
++              if (freqs->new == 200000)
++                      for (i = 0; i < 50000; i++);
++              REG_WR(bif_core, regi_bif_core, rw_sdram_timing, timing);
++      }
++        return 0;
++}
++
++
++module_init(cris_freq_init);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/crisksyms.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/crisksyms.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/crisksyms.c   2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/crisksyms.c   2006-11-21 04:21:34.000000000 +0100
+@@ -3,6 +3,7 @@
+ #include <asm/arch/dma.h>
+ #include <asm/arch/intmem.h>
+ #include <asm/arch/pinmux.h>
++#include <asm/arch/io.h>
+ /* Functions for allocating DMA channels */
+ EXPORT_SYMBOL(crisv32_request_dma);
+@@ -16,7 +17,11 @@
+ /* Functions for handling pinmux */
+ EXPORT_SYMBOL(crisv32_pinmux_alloc);
++EXPORT_SYMBOL(crisv32_pinmux_alloc_fixed);
+ EXPORT_SYMBOL(crisv32_pinmux_dealloc);
++EXPORT_SYMBOL(crisv32_pinmux_dealloc_fixed);
++EXPORT_SYMBOL(crisv32_io_get_name);
++EXPORT_SYMBOL(crisv32_io_get);
+ /* Functions masking/unmasking interrupts */
+ EXPORT_SYMBOL(mask_irq);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/debugport.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/debugport.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/debugport.c   2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/debugport.c   2006-10-13 14:43:13.000000000 +0200
+@@ -4,18 +4,13 @@
+ #include <linux/console.h>
+ #include <linux/init.h>
+-#include <linux/major.h>
+-#include <linux/delay.h>
+-#include <linux/tty.h>
+ #include <asm/system.h>
+-#include <asm/io.h>
++#include <asm/arch/hwregs/reg_rdwr.h>
++#include <asm/arch/hwregs/reg_map.h>
+ #include <asm/arch/hwregs/ser_defs.h>
+ #include <asm/arch/hwregs/dma_defs.h>
+ #include <asm/arch/pinmux.h>
+-#include <asm/irq.h>
+-#include <asm/arch/hwregs/intr_vect_defs.h>
+-
+ struct dbg_port
+ {
+       unsigned char nbr;
+@@ -26,7 +21,7 @@
+       unsigned int bits;
+ };
+-struct dbg_port ports[] =
++struct dbg_port ports[] = 
+ {
+   {
+     0,
+@@ -89,15 +84,6 @@
+ #endif
+ #endif
+-#ifdef CONFIG_ETRAXFS_SIM
+-extern void print_str( const char *str );
+-static char buffer[1024];
+-static char msg[] = "Debug: ";
+-static int buffer_pos = sizeof(msg) - 1;
+-#endif
+-
+-extern struct tty_driver *serial_driver;
+-
+ static void
+ start_port(struct dbg_port* p)
+ {
+@@ -118,7 +104,7 @@
+       /* Set up serial port registers */
+       reg_ser_rw_tr_ctrl tr_ctrl = {0};
+       reg_ser_rw_tr_dma_en tr_dma_en = {0};
+-
++              
+       reg_ser_rw_rec_ctrl rec_ctrl = {0};
+       reg_ser_rw_tr_baud_div tr_baud_div = {0};
+       reg_ser_rw_rec_baud_div rec_baud_div = {0};
+@@ -148,6 +134,7 @@
+               tr_ctrl.data_bits = regk_ser_bits7;
+               rec_ctrl.data_bits = regk_ser_bits7;
+       }
++      
+       REG_WR (ser, p->instance, rw_tr_baud_div, tr_baud_div);
+       REG_WR (ser, p->instance, rw_rec_baud_div, rec_baud_div);
+@@ -156,124 +143,21 @@
+       REG_WR (ser, p->instance, rw_rec_ctrl, rec_ctrl);
+ }
+-/* No debug */
+-#ifdef CONFIG_ETRAX_DEBUG_PORT_NULL
+-
+-static void
+-console_write(struct console *co, const char *buf, unsigned int len)
+-{
+-      return;
+-}
+-
+-/* Target debug */
+-#elif !defined(CONFIG_ETRAXFS_SIM)
+-
+-static void
+-console_write_direct(struct console *co, const char *buf, unsigned int len)
+-{
+-      int i;
+-      reg_ser_r_stat_din stat;
+-      reg_ser_rw_tr_dma_en tr_dma_en, old;
+-
+-      /* Switch to manual mode */
+-      tr_dma_en = old = REG_RD (ser, port->instance, rw_tr_dma_en);
+-      if (tr_dma_en.en == regk_ser_yes) {
+-              tr_dma_en.en = regk_ser_no;
+-              REG_WR(ser, port->instance, rw_tr_dma_en, tr_dma_en);
+-      }
+-
+-      /* Send data */
+-      for (i = 0; i < len; i++) {
+-              /* LF -> CRLF */
+-              if (buf[i] == '\n') {
+-                      do {
+-                              stat = REG_RD (ser, port->instance, r_stat_din);
+-                      } while (!stat.tr_rdy);
+-                      REG_WR_INT (ser, port->instance, rw_dout, '\r');
+-              }
+-              /* Wait until transmitter is ready and send.*/
+-              do {
+-                      stat = REG_RD (ser, port->instance, r_stat_din);
+-              } while (!stat.tr_rdy);
+-              REG_WR_INT (ser, port->instance, rw_dout, buf[i]);
+-      }
+-
+-      /* Restore mode */
+-      if (tr_dma_en.en != old.en)
+-              REG_WR(ser, port->instance, rw_tr_dma_en, old);
+-}
+-
+-static void
+-console_write(struct console *co, const char *buf, unsigned int len)
+-{
+-      if (!port)
+-              return;
+-        console_write_direct(co, buf, len);
+-}
+-
+-
+-
+-#else
+-
+-/* VCS debug */
+-
+-static void
+-console_write(struct console *co, const char *buf, unsigned int len)
+-{
+-      char* pos;
+-      pos = memchr(buf, '\n', len);
+-      if (pos) {
+-              int l = ++pos - buf;
+-              memcpy(buffer + buffer_pos, buf, l);
+-              memcpy(buffer, msg, sizeof(msg) - 1);
+-              buffer[buffer_pos + l] = '\0';
+-              print_str(buffer);
+-              buffer_pos = sizeof(msg) - 1;
+-              if (pos - buf != len) {
+-                      memcpy(buffer + buffer_pos, pos, len - l);
+-                      buffer_pos += len - l;
+-              }
+-      } else {
+-              memcpy(buffer + buffer_pos, buf, len);
+-              buffer_pos += len;
+-      }
+-}
+-
+-#endif
+-
+-int raw_printk(const char *fmt, ...)
+-{
+-      static char buf[1024];
+-      int printed_len;
+-      va_list args;
+-      va_start(args, fmt);
+-      printed_len = vsnprintf(buf, sizeof(buf), fmt, args);
+-      va_end(args);
+-      console_write(NULL, buf, strlen(buf));
+-      return printed_len;
+-}
+-
+-void
+-stupid_debug(char* buf)
+-{
+-  console_write(NULL, buf, strlen(buf));
+-}
+-
+ #ifdef CONFIG_ETRAX_KGDB
+ /* Use polling to get a single character from the kernel debug port */
+ int
+ getDebugChar(void)
+ {
+-      reg_ser_rs_status_data stat;
++      reg_ser_rs_stat_din stat;
+       reg_ser_rw_ack_intr ack_intr = { 0 };
+       do {
+-              stat = REG_RD(ser, kgdb_instance, rs_status_data);
+-      } while (!stat.data_avail);
++              stat = REG_RD(ser, kgdb_port->instance, rs_stat_din);
++      } while (!stat.dav);
+       /* Ack the data_avail interrupt. */
+-      ack_intr.data_avail = 1;
+-      REG_WR(ser, kgdb_instance, rw_ack_intr, ack_intr);
++      ack_intr.dav = 1;
++      REG_WR(ser, kgdb_port->instance, rw_ack_intr, ack_intr);
+       return stat.data;
+ }
+@@ -282,173 +166,18 @@
+ void
+ putDebugChar(int val)
+ {
+-      reg_ser_r_status_data stat;
++      reg_ser_r_stat_din stat;
+       do {
+-              stat = REG_RD (ser, kgdb_instance, r_status_data);
+-      } while (!stat.tr_ready);
+-      REG_WR (ser, kgdb_instance, rw_data_out, REG_TYPE_CONV(reg_ser_rw_data_out, int, val));
++              stat = REG_RD (ser, kgdb_port->instance, r_stat_din);
++      } while (!stat.tr_rdy);
++      REG_WR_INT (ser, kgdb_port->instance, rw_dout, val);
+ }
+ #endif /* CONFIG_ETRAX_KGDB */
+-static int __init
+-console_setup(struct console *co, char *options)
+-{
+-      char* s;
+-
+-      if (options) {
+-              port = &ports[co->index];
+-              port->baudrate = 115200;
+-              port->parity = 'N';
+-              port->bits = 8;
+-              port->baudrate = simple_strtoul(options, NULL, 10);
+-              s = options;
+-              while(*s >= '0' && *s <= '9')
+-                      s++;
+-              if (*s) port->parity = *s++;
+-              if (*s) port->bits   = *s++ - '0';
+-              port->started = 0;
+-              start_port(port);
+-      }
+-      return 0;
+-}
+-
+-/* This is a dummy serial device that throws away anything written to it.
+- * This is used when no debug output is wanted.
+- */
+-static struct tty_driver dummy_driver;
+-
+-static int dummy_open(struct tty_struct *tty, struct file * filp)
+-{
+-      return 0;
+-}
+-
+-static void dummy_close(struct tty_struct *tty, struct file * filp)
+-{
+-}
+-
+-static int dummy_write(struct tty_struct * tty,
+-                       const unsigned char *buf, int count)
+-{
+-      return count;
+-}
+-
+-static int
+-dummy_write_room(struct tty_struct *tty)
+-{
+-      return 8192;
+-}
+-
+-void __init
+-init_dummy_console(void)
+-{
+-      memset(&dummy_driver, 0, sizeof(struct tty_driver));
+-      dummy_driver.driver_name = "serial";
+-      dummy_driver.name = "ttyS";
+-      dummy_driver.major = TTY_MAJOR;
+-      dummy_driver.minor_start = 68;
+-      dummy_driver.num = 1;       /* etrax100 has 4 serial ports */
+-      dummy_driver.type = TTY_DRIVER_TYPE_SERIAL;
+-      dummy_driver.subtype = SERIAL_TYPE_NORMAL;
+-      dummy_driver.init_termios = tty_std_termios;
+-      dummy_driver.init_termios.c_cflag =
+-              B115200 | CS8 | CREAD | HUPCL | CLOCAL; /* is normally B9600 default... */
+-      dummy_driver.flags = TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV;
+-
+-      dummy_driver.open = dummy_open;
+-      dummy_driver.close = dummy_close;
+-      dummy_driver.write = dummy_write;
+-      dummy_driver.write_room = dummy_write_room;
+-      if (tty_register_driver(&dummy_driver))
+-              panic("Couldn't register dummy serial driver\n");
+-}
+-
+-static struct tty_driver*
+-crisv32_console_device(struct console* co, int *index)
+-{
+-      if (port)
+-              *index = port->nbr;
+-        return port ? serial_driver : &dummy_driver;
+-}
+-
+-static struct console sercons = {
+-      name : "ttyS",
+-      write: console_write,
+-      read : NULL,
+-      device : crisv32_console_device,
+-      unblank : NULL,
+-      setup : console_setup,
+-      flags : CON_PRINTBUFFER,
+-      index : -1,
+-      cflag : 0,
+-      next : NULL
+-};
+-static struct console sercons0 = {
+-      name : "ttyS",
+-      write: console_write,
+-      read : NULL,
+-      device : crisv32_console_device,
+-      unblank : NULL,
+-      setup : console_setup,
+-      flags : CON_PRINTBUFFER,
+-      index : 0,
+-      cflag : 0,
+-      next : NULL
+-};
+-
+-static struct console sercons1 = {
+-      name : "ttyS",
+-      write: console_write,
+-      read : NULL,
+-      device : crisv32_console_device,
+-      unblank : NULL,
+-      setup : console_setup,
+-      flags : CON_PRINTBUFFER,
+-      index : 1,
+-      cflag : 0,
+-      next : NULL
+-};
+-static struct console sercons2 = {
+-      name : "ttyS",
+-      write: console_write,
+-      read : NULL,
+-      device : crisv32_console_device,
+-      unblank : NULL,
+-      setup : console_setup,
+-      flags : CON_PRINTBUFFER,
+-      index : 2,
+-      cflag : 0,
+-      next : NULL
+-};
+-static struct console sercons3 = {
+-      name : "ttyS",
+-      write: console_write,
+-      read : NULL,
+-      device : crisv32_console_device,
+-      unblank : NULL,
+-      setup : console_setup,
+-      flags : CON_PRINTBUFFER,
+-      index : 3,
+-      cflag : 0,
+-      next : NULL
+-};
+-
+ /* Register console for printk's, etc. */
+ int __init
+ init_etrax_debug(void)
+ {
+-      static int first = 1;
+-
+-      if (!first) {
+-              unregister_console(&sercons);
+-              register_console(&sercons0);
+-              register_console(&sercons1);
+-              register_console(&sercons2);
+-              register_console(&sercons3);
+-              init_dummy_console();
+-              return 0;
+-      }
+-      first = 0;
+-        register_console(&sercons);
+         start_port(port);
+ #ifdef CONFIG_ETRAX_KGDB
+@@ -456,5 +185,3 @@
+ #endif /* CONFIG_ETRAX_KGDB */
+       return 0;
+ }
+-
+-__initcall(init_etrax_debug);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/dma.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/dma.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/dma.c 2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/dma.c 2007-02-02 08:45:22.000000000 +0100
+@@ -12,6 +12,16 @@
+ #include <asm/system.h>
+ #include <asm/arch/arbiter.h>
++/*
++ * The memory region we allocated bandwidth for is stored in
++ * used_dma_channels as an in-use flag.
++ */
++enum dma_region_allocated_marker {
++  DMA_NO_REGION_ALLOCATED = 0,
++  DMA_INT_REGION_ALLOCATED = 1,
++  DMA_EXT_REGION_ALLOCATED = 2
++};
++
+ static char used_dma_channels[MAX_DMA_CHANNELS];
+ static const char * used_dma_channels_users[MAX_DMA_CHANNELS];
+@@ -74,7 +84,7 @@
+               if (options & DMA_VERBOSE_ON_ERROR) {
+                       printk("Failed to request DMA %i for %s, only 0-%i valid)\n", dmanr, device_id, MAX_DMA_CHANNELS-1);
+               }
+-
++              
+               if (options & DMA_PANIC_ON_ERROR)
+                       panic("request_dma error!");
+               return -EINVAL;
+@@ -202,13 +212,14 @@
+               if (dmanr == 3)
+                       strmux_cfg.dma3 = regk_strmux_ext3;
+               else if (dmanr == 9)
+-                      strmux_cfg.dma9 = regk_strmux_ext2;
++                      strmux_cfg.dma9 = regk_strmux_ext3;
+               else
+-                      panic("Invalid DMA channel for ext2\n");
++                      panic("Invalid DMA channel for ext3\n");
+               break;
+       }
+-      used_dma_channels[dmanr] = 1;
++      used_dma_channels[dmanr] = options & DMA_INT_MEM
++              ? DMA_INT_REGION_ALLOCATED : DMA_EXT_REGION_ALLOCATED;
+       used_dma_channels_users[dmanr] = device_id;
+       REG_WR(config, regi_config, rw_clk_ctrl, clk_ctrl);
+       REG_WR(strmux, regi_strmux, rw_cfg, strmux_cfg);
+@@ -218,7 +229,12 @@
+ void crisv32_free_dma(unsigned int dmanr)
+ {
++      int region;
++
+       spin_lock(&dma_lock);
++      region = used_dma_channels[dmanr] == DMA_INT_REGION_ALLOCATED
++              ? INT_REGION : EXT_REGION;
+       used_dma_channels[dmanr] = 0;
++      crisv32_arbiter_deallocate_bandwidth(dmanr, region);
+       spin_unlock(&dma_lock);
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/entry.S linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/entry.S
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/entry.S       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/entry.S       2007-01-09 10:29:19.000000000 +0100
+@@ -11,7 +11,7 @@
+  *
+  * Stack layout in 'ret_from_system_call':
+  *    ptrace needs to have all regs on the stack.
+- *    if the order here is changed, it needs to be
++ *    if the order here is changed, it needs to be 
+  *    updated in fork.c:copy_process, signal.c:do_signal,
+  *    ptrace.c and ptrace.h
+  *
+@@ -40,7 +40,7 @@
+       .globl sys_call_table
+       ; Check if preemptive kernel scheduling should be done.
+-#ifdef CONFIG_PREEMPT
++#ifdef CONFIG_PREEMPT 
+ _resume_kernel:
+       di
+       ; Load current task struct.
+@@ -81,7 +81,7 @@
+       nop
+       ba  ret_from_sys_call
+       nop
+-
++              
+ ret_from_intr:
+       ;; Check for resched if preemptive kernel, or if we're going back to
+       ;; user-mode. This test matches the user_regs(regs) macro. Don't simply
+@@ -93,7 +93,7 @@
+       bpl     _resume_kernel
+       ; Note that di below is in delay slot.
+-
++      
+ _resume_userspace:
+       di                      ; So need_resched and sigpending don't change.
+@@ -107,19 +107,19 @@
+       nop
+       ba      _Rexit
+       nop
+-
++      
+       ;; The system_call is called by a BREAK instruction, which looks pretty
+       ;; much like any other exception.
+       ;;
+       ;; System calls can't be made from interrupts but we still stack ERP
+       ;; to have a complete stack frame.
+-      ;;
++      ;; 
+       ;; In r9 we have the wanted syscall number. Arguments come in r10,r11,r12,
+       ;; r13,mof,srp
+       ;;
+       ;; This function looks on the _surface_ like spaghetti programming, but it's
+-      ;; really designed so that the fast-path does not force cache-loading of
+-      ;; non-used instructions. Only the non-common cases cause the outlined code
++      ;; really designed so that the fast-path does not force cache-loading of 
++      ;; non-used instructions. Only the non-common cases cause the outlined code 
+       ;; to run..
+ system_call:
+@@ -151,7 +151,7 @@
+       or.d (1<<9), $r0
+       move $r0, $ccs
+ #endif
+-
++      
+       movs.w  -ENOSYS, $r0
+       addoq   +PT_r10, $sp, $acr
+       move.d  $r0, [$acr]
+@@ -166,9 +166,9 @@
+       bmi     _syscall_trace_entry
+       nop
+-_syscall_traced:
++_syscall_traced:      
+       ;; Check for sanity in the requested syscall number.
+-      cmpu.w  NR_syscalls, $r9
++      cmpu.w  NR_syscalls, $r9        
+       bhs     ret_from_sys_call
+       lslq    2, $r9          ;  Multiply by 4, in the delay slot.
+@@ -177,7 +177,7 @@
+       move.d  $sp, $r0
+       subq    4, $sp
+       move.d  $r0, [$sp]
+-
++      
+       ;; The registers carrying parameters (R10-R13) are intact. The optional
+       ;; fifth and sixth parameters is in MOF and SRP respectivly. Put them
+       ;; back on the stack.
+@@ -185,33 +185,33 @@
+       move    $srp, [$sp]
+       subq    4, $sp
+       move    $mof, [$sp]
+-
++      
+       ;; Actually to the system call.
+       addo.d  +sys_call_table, $r9, $acr
+       move.d  [$acr], $acr
+       jsr     $acr
+       nop
+-
++      
+       addq    3*4, $sp                ; Pop the mof, srp and regs parameters.
+       addoq   +PT_r10, $sp, $acr
+       move.d  $r10, [$acr]            ; Save the return value.
+-      moveq   1, $r9                  ; "Parameter" to ret_from_sys_call to
++      moveq   1, $r9                  ; "Parameter" to ret_from_sys_call to 
+                                       ; show it was a sys call.
+-
++      
+       ;; Fall through into ret_from_sys_call to return.
+-
++      
+ ret_from_sys_call:
+       ;; R9 is a parameter:
+       ;;  >= 1 from syscall
+       ;;     0 from irq
+-
++              
+       ;; Get the current task-struct pointer.
+-      movs.w  -8192, $r0      ; THREAD_SIZE == 8192
++      movs.w  -8192, $r0      ; THREAD_SIZE == 8192 
+       and.d   $sp, $r0
+       di              ; Make sure need_resched and sigpending don't change.
+-
++      
+       addoq   +TI_flags, $r0, $acr
+       move.d  [$acr], $r1
+       and.d   _TIF_ALLWORK_MASK, $r1
+@@ -253,14 +253,14 @@
+       move.d  $r1, $r9
+       ba      _resume_userspace
+       nop
+-
++      
+ _work_pending:
+       addoq   +TI_flags, $r0, $acr
+       move.d  [$acr], $r10
+       btstq   TIF_NEED_RESCHED, $r10  ; Need resched?
+       bpl     _work_notifysig         ; No, must be signal/notify.
+       nop
+-
++      
+ _work_resched:
+       move.d  $r9, $r1                ; Preserve R9.
+       jsr     schedule
+@@ -281,28 +281,26 @@
+       ;; Deal with pending signals and notify-resume requests.
+       addoq   +TI_flags, $r0, $acr
+-      move.d  [$acr], $r13            ; The thread_info_flags parameter.
+-      move.d  $r9, $r10               ; do_notify_resume syscall/irq param.
+-      moveq   0, $r11                 ; oldset param - 0 in this case.
+-      move.d  $sp, $r12               ; The regs param.
++      move.d  [$acr], $r12            ; The thread_info_flags parameter.
++      move.d  $sp, $r11               ; The regs param.
+       jsr     do_notify_resume
+-      nop
+-
++      move.d  $r9, $r10               ; do_notify_resume syscall/irq param.
++      
+       ba _Rexit
+       nop
+       ;; We get here as a sidetrack when we've entered a syscall with the
+       ;; trace-bit set. We need to call do_syscall_trace and then continue
+       ;; with the call.
+-
++      
+ _syscall_trace_entry:
+       ;; PT_r10 in the frame contains -ENOSYS as required, at this point.
+-
++      
+       jsr     do_syscall_trace
+       nop
+       ;; Now re-enter the syscall code to do the syscall itself. We need to
+-      ;; restore R9 here to contain the wanted syscall, and the other
++      ;; restore R9 here to contain the wanted syscall, and the other 
+       ;; parameter-bearing registers.
+       addoq   +PT_r9, $sp, $acr
+       move.d  [$acr], $r9
+@@ -318,10 +316,10 @@
+       move    [$acr], $mof
+       addoq   +PT_srp, $sp, $acr
+       move    [$acr], $srp
+-
++      
+       ba      _syscall_traced
+       nop
+-
++      
+       ;; Resume performs the actual task-switching, by switching stack
+       ;; pointers. Input arguments are:
+       ;;
+@@ -331,7 +329,7 @@
+       ;;
+       ;; Returns old current in R10.
+-resume:
++resume:       
+       subq    4, $sp
+       move    $srp, [$sp]             ; Keep old/new PC on the stack.
+       add.d   $r12, $r10              ; R10 = current tasks tss.
+@@ -341,14 +339,14 @@
+       addoq   +THREAD_usp, $r10, $acr
+       move    $usp, [$acr]            ; Save user-mode stackpointer.
+-
++      
+       ;; See copy_thread for the reason why register R9 is saved.
+       subq    10*4, $sp
+       movem   $r9, [$sp]              ; Save non-scratch registers and R9.
+-
++      
+       addoq   +THREAD_ksp, $r10, $acr
+       move.d  $sp, [$acr]             ; Save kernel SP for old task.
+-
++      
+       move.d  $sp, $r10               ; Return last running task in R10.
+       and.d   -8192, $r10             ; Get thread_info from stackpointer.
+       addoq   +TI_task, $r10, $acr
+@@ -360,7 +358,7 @@
+       addoq   +THREAD_usp, $r11, $acr
+       move    [$acr], $usp            ; Restore user-mode stackpointer.
+-
++      
+       addoq   +THREAD_ccs, $r11, $acr
+       move    [$acr], $ccs            ; Restore IRQ enable status.
+       move.d  [$sp+], $acr
+@@ -407,7 +405,7 @@
+       movem   [$sp+], $r13
+       move.d  [$sp+], $acr
+       move    [$sp], $srs
+-      addq    4, $sp
++      addq    4, $sp 
+       move    [$sp+], $mof
+       move    [$sp+], $spc
+       move    [$sp+], $ccs
+@@ -419,7 +417,7 @@
+       .comm   cause_of_death, 4       ;; Don't declare this anywhere.
+-spurious_interrupt:
++spurious_interrupt:   
+       di
+       jump hard_reset_now
+       nop
+@@ -494,31 +492,38 @@
+       ;; thread_info as first parameter
+       move.d  $r9, $r10
+       moveq   5, $r11                 ; SIGTRAP as second argument.
+-      jsr     ugdb_trap_user
++      jsr     ugdb_trap_user       
+       nop
+       jump    ret_from_intr           ; Use the return routine for interrupts.
+       nop
+-
+-gdb_handle_exception:
++      
++gdb_handle_exception: 
+       subq    4, $sp
+       move.d  $r0, [$sp]
+ #ifdef CONFIG_ETRAX_KGDB
+-      move    $ccs, $r0               ; U-flag not affected by previous insns.
++      move    $ccs, $r0               ; U-flag not affected by previous insns. 
+       btstq   16, $r0                 ; Test the U-flag.
+-      bmi     _ugdb_handle_exception  ; Go to user mode debugging.
+-      nop                             ; Empty delay-slot (cannot pop R0 here).
++      bmi     _ugdb_handle_exception  ; Go to user mode debugging. 
++      nop                             ; Empty delay-slot (cannot pop R0 here). 
+       ba      kgdb_handle_exception   ; Go to kernel debugging.
+       move.d  [$sp+], $r0             ; Restore R0 in delay slot.
+ #endif
+-
++      
+ _ugdb_handle_exception:
+       ba      do_sigtrap              ; SIGTRAP the offending process.
+       move.d  [$sp+], $r0             ; Restore R0 in delay slot.
++      .global kernel_execve
++kernel_execve:
++      move.d __NR_execve, $r9
++      break 13
++      ret
++      nop
++              
+       .data
+       .section .rodata,"a"
+-sys_call_table:
++sys_call_table:       
+       .long sys_restart_syscall       ; 0 - old "setup()" system call, used
+                                       ; for restarting.
+       .long sys_exit
+@@ -647,7 +652,7 @@
+       .long sys_adjtimex
+       .long sys_mprotect      /* 125 */
+       .long sys_sigprocmask
+-      .long sys_ni_syscall    /* old "create_module" */
++      .long sys_ni_syscall    /* old "create_module" */ 
+       .long sys_init_module
+       .long sys_delete_module
+       .long sys_ni_syscall    /* 130: old "get_kernel_syms" */
+@@ -789,7 +794,7 @@
+       .long sys_clock_getres
+       .long sys_clock_nanosleep
+       .long sys_statfs64
+-      .long sys_fstatfs64
++      .long sys_fstatfs64     
+       .long sys_tgkill        /* 270 */
+       .long sys_utimes
+       .long sys_fadvise64_64
+@@ -805,7 +810,43 @@
+       .long sys_mq_getsetattr
+       .long sys_ni_syscall            /* reserved for kexec */
+       .long sys_waitid
+-
++      .long sys_ni_syscall            /* 285 */ /* available */
++      .long sys_add_key
++      .long sys_request_key
++      .long sys_keyctl
++      .long sys_ioprio_set
++      .long sys_ioprio_get            /* 290 */
++      .long sys_inotify_init
++      .long sys_inotify_add_watch
++      .long sys_inotify_rm_watch
++      .long sys_migrate_pages
++      .long sys_openat                /* 295 */
++      .long sys_mkdirat
++      .long sys_mknodat
++      .long sys_fchownat
++      .long sys_futimesat
++      .long sys_fstatat64             /* 300 */
++      .long sys_unlinkat
++      .long sys_renameat
++      .long sys_linkat
++      .long sys_symlinkat
++      .long sys_readlinkat            /* 305 */
++      .long sys_fchmodat
++      .long sys_faccessat
++      .long sys_pselect6
++      .long sys_ppoll
++      .long sys_unshare               /* 310 */
++      .long sys_set_robust_list 
++      .long sys_set_robust_list
++      .long sys_get_robust_list
++      .long sys_splice
++      .long sys_sync_file_range
++      .long sys_tee                   /* 315 */
++      .long sys_vmsplice
++      .long sys_move_pages
++      .long sys_getcpu
++      .long sys_epoll_pwait
++                      
+         /*
+          * NOTE!! This doesn't have to be exact - we just have
+          * to make sure we have _enough_ of the "sys_ni_syscall"
+@@ -816,4 +857,4 @@
+       .rept NR_syscalls - (.-sys_call_table) / 4
+               .long sys_ni_syscall
+       .endr
+-
++      
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/fasttimer.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/fasttimer.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/fasttimer.c   2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/fasttimer.c   2007-01-09 10:29:19.000000000 +0100
+@@ -1,110 +1,10 @@
+-/* $Id: fasttimer.c,v 1.11 2005/01/04 11:15:46 starvik Exp $
++/*
+  * linux/arch/cris/kernel/fasttimer.c
+  *
+  * Fast timers for ETRAX FS
+  * This may be useful in other OS than Linux so use 2 space indentation...
+  *
+- * $Log: fasttimer.c,v $
+- * Revision 1.11  2005/01/04 11:15:46  starvik
+- * Don't share timer IRQ.
+- *
+- * Revision 1.10  2004/12/07 09:19:38  starvik
+- * Corrected includes.
+- * Use correct interrupt macros.
+- *
+- * Revision 1.9  2004/05/14 10:18:58  starvik
+- * Export fast_timer_list
+- *
+- * Revision 1.8  2004/05/14 07:58:03  starvik
+- * Merge of changes from 2.4
+- *
+- * Revision 1.7  2003/07/10 12:06:14  starvik
+- * Return IRQ_NONE if irq wasn't handled
+- *
+- * Revision 1.6  2003/07/04 08:27:49  starvik
+- * Merge of Linux 2.5.74
+- *
+- * Revision 1.5  2003/06/05 10:16:22  johana
+- * New INTR_VECT macros.
+- *
+- * Revision 1.4  2003/06/03 08:49:45  johana
+- * Fixed typo.
+- *
+- * Revision 1.3  2003/06/02 12:51:27  johana
+- * Now compiles.
+- * Commented some include files that probably can be removed.
+- *
+- * Revision 1.2  2003/06/02 12:09:41  johana
+- * Ported to ETRAX FS using the trig interrupt instead of timer1.
+- *
+- * Revision 1.3  2002/12/12 08:26:32  starvik
+- * Don't use C-comments inside CVS comments
+- *
+- * Revision 1.2  2002/12/11 15:42:02  starvik
+- * Extracted v10 (ETRAX 100LX) specific stuff from arch/cris/kernel/
+- *
+- * Revision 1.1  2002/11/18 07:58:06  starvik
+- * Fast timers (from Linux 2.4)
+- *
+- * Revision 1.5  2002/10/15 06:21:39  starvik
+- * Added call to init_waitqueue_head
+- *
+- * Revision 1.4  2002/05/28 17:47:59  johana
+- * Added del_fast_timer()
+- *
+- * Revision 1.3  2002/05/28 16:16:07  johana
+- * Handle empty fast_timer_list
+- *
+- * Revision 1.2  2002/05/27 15:38:42  johana
+- * Made it compile without warnings on Linux 2.4.
+- * (includes, wait_queue, PROC_FS and snprintf)
+- *
+- * Revision 1.1  2002/05/27 15:32:25  johana
+- * arch/etrax100/kernel/fasttimer.c v1.8 from the elinux tree.
+- *
+- * Revision 1.8  2001/11/27 13:50:40  pkj
+- * Disable interrupts while stopping the timer and while modifying the
+- * list of active timers in timer1_handler() as it may be interrupted
+- * by other interrupts (e.g., the serial interrupt) which may add fast
+- * timers.
+- *
+- * Revision 1.7  2001/11/22 11:50:32  pkj
+- * * Only store information about the last 16 timers.
+- * * proc_fasttimer_read() now uses an allocated buffer, since it
+- *   requires more space than just a page even for only writing the
+- *   last 16 timers. The buffer is only allocated on request, so
+- *   unless /proc/fasttimer is read, it is never allocated.
+- * * Renamed fast_timer_started to fast_timers_started to match
+- *   fast_timers_added and fast_timers_expired.
+- * * Some clean-up.
+- *
+- * Revision 1.6  2000/12/13 14:02:08  johana
+- * Removed volatile for fast_timer_list
+- *
+- * Revision 1.5  2000/12/13 13:55:35  johana
+- * Added DEBUG_LOG, added som cli() and cleanup
+- *
+- * Revision 1.4  2000/12/05 13:48:50  johana
+- * Added range check when writing proc file, modified timer int handling
+- *
+- * Revision 1.3  2000/11/23 10:10:20  johana
+- * More debug/logging possibilities.
+- * Moved GET_JIFFIES_USEC() to timex.h and time.c
+- *
+- * Revision 1.2  2000/11/01 13:41:04  johana
+- * Clean up and bugfixes.
+- * Created new do_gettimeofday_fast() that gets a timeval struct
+- * with time based on jiffies and *R_TIMER0_DATA, uses a table
+- * for fast conversion of timer value to microseconds.
+- * (Much faster the standard do_gettimeofday() and we don't really
+- * wan't to use the true time - we wan't the "uptime" so timers don't screw up
+- * when we change the time.
+- * TODO: Add efficient support for continuous timers as well.
+- *
+- * Revision 1.1  2000/10/26 15:49:16  johana
+- * Added fasttimer, highresolution timers.
+- *
+- * Copyright (C) 2000,2001 2002, 2003 Axis Communications AB, Lund, Sweden
++ * Copyright (C) 2000-2006 Axis Communications AB, Lund, Sweden
+  */
+ #include <linux/errno.h>
+@@ -128,13 +28,13 @@
+ #include <asm/fasttimer.h>
+ #include <linux/proc_fs.h>
+-/*
+- * timer0 is running at 100MHz and generating jiffies timer ticks
++/* 
++ * timer0 is running at 100MHz and generating jiffies timer ticks 
+  * at 100 or 1000 HZ.
+  * fasttimer gives an API that gives timers that expire "between" the jiffies
+  * giving microsecond resolution (10 ns).
+  * fasttimer uses reg_timer_rw_trig register to get interrupt when
+- * r_time reaches a certain value.
++ * r_time reaches a certain value. 
+  */
+@@ -151,19 +51,19 @@
+ #define SANITYCHECK(x)
+ #endif
+-#define D1(x)
+-#define D2(x)
+-#define DP(x)
++#define D1(x) 
++#define D2(x) 
++#define DP(x) 
+ #define __INLINE__ inline
+-static int fast_timer_running = 0;
+-static int fast_timers_added = 0;
+-static int fast_timers_started = 0;
+-static int fast_timers_expired = 0;
+-static int fast_timers_deleted = 0;
+-static int fast_timer_is_init = 0;
+-static int fast_timer_ints = 0;
++static unsigned int fast_timer_running = 0;
++static unsigned int fast_timers_added = 0;
++static unsigned int fast_timers_started = 0;
++static unsigned int fast_timers_expired = 0;
++static unsigned int fast_timers_deleted = 0;
++static unsigned int fast_timer_is_init = 0;
++static unsigned int fast_timer_ints = 0;
+ struct fast_timer *fast_timer_list = NULL;
+@@ -171,8 +71,8 @@
+ #define DEBUG_LOG_MAX 128
+ static const char * debug_log_string[DEBUG_LOG_MAX];
+ static unsigned long debug_log_value[DEBUG_LOG_MAX];
+-static int debug_log_cnt = 0;
+-static int debug_log_cnt_wrapped = 0;
++static unsigned int debug_log_cnt = 0;
++static unsigned int debug_log_cnt_wrapped = 0;
+ #define DEBUG_LOG(string, value) \
+ { \
+@@ -202,48 +102,33 @@
+ int timer_div_settings[NUM_TIMER_STATS];
+ int timer_delay_settings[NUM_TIMER_STATS];
++struct work_struct fast_work;
+ static void
+-timer_trig_handler(void);
++timer_trig_handler(void* dummy);
+ /* Not true gettimeofday, only checks the jiffies (uptime) + useconds */
+-void __INLINE__ do_gettimeofday_fast(struct timeval *tv)
++void __INLINE__ do_gettimeofday_fast(struct fasttime_t *tv)
+ {
+-  unsigned long sec = jiffies;
+-  unsigned long usec = GET_JIFFIES_USEC();
+-
+-  usec += (sec % HZ) * (1000000 / HZ);
+-  sec = sec / HZ;
+-
+-  if (usec > 1000000)
+-  {
+-    usec -= 1000000;
+-    sec++;
+-  }
+-  tv->tv_sec = sec;
+-  tv->tv_usec = usec;
++  tv->tv_jiff = jiffies;
++  tv->tv_usec = GET_JIFFIES_USEC();
+ }
+-int __INLINE__ timeval_cmp(struct timeval *t0, struct timeval *t1)
++int __INLINE__ timeval_cmp(struct fasttime_t *t0, struct fasttime_t *t1)
+ {
+-  if (t0->tv_sec < t1->tv_sec)
+-  {
++  /* Compare jiffies. Takes care of wrapping */
++  if (time_before(t0->tv_jiff, t1->tv_jiff))
+     return -1;
+-  }
+-  else if (t0->tv_sec > t1->tv_sec)
+-  {
++  else if (time_after(t0->tv_jiff, t1->tv_jiff))
+     return 1;
+-  }
++
++  /* Compare us */
+   if (t0->tv_usec < t1->tv_usec)
+-  {
+     return -1;
+-  }
+   else if (t0->tv_usec > t1->tv_usec)
+-  {
+     return 1;
+-  }
+   return 0;
+ }
+@@ -254,20 +139,23 @@
+   reg_timer_rw_intr_mask intr_mask;
+   reg_timer_rw_trig trig;
+   reg_timer_rw_trig_cfg trig_cfg = { 0 };
+-  reg_timer_r_time r_time;
+-
+-  r_time = REG_RD(timer, regi_timer, r_time);
++  reg_timer_r_time r_time0;
++  reg_timer_r_time r_time1;
++  unsigned char trig_wrap;
++  unsigned char time_wrap;
++  r_time0 = REG_RD(timer, regi_timer, r_time);
++  
+   D1(printk("start_timer_trig : %d us freq: %i div: %i\n",
+             delay_us, freq_index, div));
+   /* Clear trig irq */
+   intr_mask = REG_RD(timer, regi_timer, rw_intr_mask);
+   intr_mask.trig = 0;
+   REG_WR(timer, regi_timer, rw_intr_mask, intr_mask);
+-
+-  /* Set timer values */
++  
++  /* Set timer values and check if trigger wraps. */
+   /* r_time is 100MHz (10 ns resolution) */
+-  trig = r_time + delay_us*(1000/10);
++  trig_wrap = (trig = r_time0 + delay_us*(1000/10)) < r_time0;
+   timer_div_settings[fast_timers_started % NUM_TIMER_STATS] = trig;
+   timer_delay_settings[fast_timers_started % NUM_TIMER_STATS] = delay_us;
+@@ -275,15 +163,17 @@
+   /* Ack interrupt */
+   ack_intr.trig = 1;
+   REG_WR(timer, regi_timer, rw_ack_intr, ack_intr);
+-
++  
+   /* Start timer */
+   REG_WR(timer, regi_timer, rw_trig, trig);
+   trig_cfg.tmr = regk_timer_time;
+   REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg);
+   /* Check if we have already passed the trig time */
+-  r_time = REG_RD(timer, regi_timer, r_time);
+-  if (r_time < trig) {
++  r_time1 = REG_RD(timer, regi_timer, r_time);
++  time_wrap = r_time1 < r_time0;
++
++  if ((trig_wrap && !time_wrap) || (r_time1 < trig)) {
+     /* No, Enable trig irq */
+     intr_mask = REG_RD(timer, regi_timer, rw_intr_mask);
+     intr_mask.trig = 1;
+@@ -291,16 +181,17 @@
+     fast_timers_started++;
+     fast_timer_running = 1;
+   }
+-  else
++  else 
+   {
+     /* We have passed the time, disable trig point, ack intr */
+     trig_cfg.tmr = regk_timer_off;
+     REG_WR(timer, regi_timer, rw_trig_cfg, trig_cfg);
+     REG_WR(timer, regi_timer, rw_ack_intr, ack_intr);
+-    /* call the int routine directly */
+-    timer_trig_handler();
++    /* call the int routine */
++    INIT_WORK(&fast_work, timer_trig_handler, (void*)NULL);
++    schedule_work(&fast_work);
+   }
+-
++  
+ }
+ /* In version 1.4 this function takes 27 - 50 us */
+@@ -327,7 +218,7 @@
+       {
+         printk("timer name: %s data: 0x%08lX already in list!\n", name, data);
+         sanity_failed++;
+-        return;
++        goto done;
+       }
+       else
+       {
+@@ -343,11 +234,11 @@
+   t->name = name;
+   t->tv_expires.tv_usec = t->tv_set.tv_usec + delay_us % 1000000;
+-  t->tv_expires.tv_sec  = t->tv_set.tv_sec  + delay_us / 1000000;
++  t->tv_expires.tv_jiff = t->tv_set.tv_jiff + delay_us / 1000000 / HZ;
+   if (t->tv_expires.tv_usec > 1000000)
+   {
+     t->tv_expires.tv_usec -= 1000000;
+-    t->tv_expires.tv_sec++;
++    t->tv_expires.tv_jiff += HZ;
+   }
+ #ifdef FAST_TIMER_LOG
+   timer_added_log[fast_timers_added % NUM_TIMER_STATS] = *t;
+@@ -388,6 +279,7 @@
+   D2(printk("start_one_shot_timer: %d us done\n", delay_us));
++done:
+   local_irq_restore(flags);
+ } /* start_one_shot_timer */
+@@ -431,26 +323,32 @@
+ /* Timer interrupt handler for trig interrupts */
+ static irqreturn_t
+-timer_trig_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++timer_trig_interrupt(int irq, void *dev_id)
+ {
+   reg_timer_r_masked_intr masked_intr;
+-
+   /* Check if the timer interrupt is for us (a trig int) */
+   masked_intr = REG_RD(timer, regi_timer, r_masked_intr);
+   if (!masked_intr.trig)
+     return IRQ_NONE;
+-  timer_trig_handler();
++  timer_trig_handler(NULL);
+   return IRQ_HANDLED;
+ }
+-static void timer_trig_handler(void)
++static void timer_trig_handler(void* dummy) 
+ {
+   reg_timer_rw_ack_intr ack_intr = { 0 };
+   reg_timer_rw_intr_mask intr_mask;
+   reg_timer_rw_trig_cfg trig_cfg = { 0 };
+   struct fast_timer *t;
+-  unsigned long flags;
++  unsigned long flags;  
++  /* We keep interrupts disabled not only when we modify the 
++   * fast timer list, but any time we hold a reference to a
++   * timer in the list, since del_fast_timer may be called
++   * from (another) interrupt context.  Thus, the only time
++   * when interrupts are enabled is when calling the timer
++   * callback function.
++   */
+   local_irq_save(flags);
+   /* Clear timer trig interrupt */
+@@ -470,16 +368,17 @@
+   fast_timer_running = 0;
+   fast_timer_ints++;
+-  local_irq_restore(flags);
++  fast_timer_function_type *f;
++  unsigned long d;
+   t = fast_timer_list;
+   while (t)
+   {
+-    struct timeval tv;
++    struct fasttime_t tv;
+     /* Has it really expired? */
+     do_gettimeofday_fast(&tv);
+-    D1(printk("t: %is %06ius\n", tv.tv_sec, tv.tv_usec));
++    D1(printk("t: %is %06ius\n", tv.tv_jiff, tv.tv_usec));
+     if (timeval_cmp(&t->tv_expires, &tv) <= 0)
+     {
+@@ -490,7 +389,6 @@
+       fast_timers_expired++;
+       /* Remove this timer before call, since it may reuse the timer */
+-      local_irq_save(flags);
+       if (t->prev)
+       {
+         t->prev->next = t->next;
+@@ -505,11 +403,21 @@
+       }
+       t->prev = NULL;
+       t->next = NULL;
+-      local_irq_restore(flags);
+-      if (t->function != NULL)
++      /* Save function callback data before enabling interrupts,
++       * since the timer may be removed and we don't know how it
++       * was allocated (e.g. ->function and ->data may become
++       * overwritten after deletion if the timer was stack-allocated).
++       */
++      f = t->function;
++      d = t->data;
++
++      if (f != NULL)
+       {
+-        t->function(t->data);
++        /* Run the callback function with interrupts enabled. */
++        local_irq_restore(flags);
++        f(d);
++        local_irq_save(flags);
+       }
+       else
+       {
+@@ -522,16 +430,19 @@
+       D1(printk(".\n"));
+     }
+-    local_irq_save(flags);
+     if ((t = fast_timer_list) != NULL)
+     {
+       /* Start next timer.. */
+-      long us;
+-      struct timeval tv;
++      long us = 0;
++      struct fasttime_t tv;
+       do_gettimeofday_fast(&tv);
+-      us = ((t->tv_expires.tv_sec - tv.tv_sec) * 1000000 +
+-            t->tv_expires.tv_usec - tv.tv_usec);
++
++      /* time_after_eq takes care of wrapping */
++      if (time_after_eq(t->tv_expires.tv_jiff, tv.tv_jiff))
++      us = ((t->tv_expires.tv_jiff - tv.tv_jiff) * 1000000 / HZ +
++            t->tv_expires.tv_usec - tv.tv_usec);
++
+       if (us > 0)
+       {
+         if (!fast_timer_running)
+@@ -541,7 +452,6 @@
+ #endif
+           start_timer_trig(us);
+         }
+-        local_irq_restore(flags);
+         break;
+       }
+       else
+@@ -552,9 +462,10 @@
+         D1(printk("e! %d\n", us));
+       }
+     }
+-    local_irq_restore(flags);
+   }
++  local_irq_restore(flags);
++
+   if (!t)
+   {
+     D1(printk("ttrig stop!\n"));
+@@ -577,28 +488,17 @@
+ void schedule_usleep(unsigned long us)
+ {
+   struct fast_timer t;
+-#ifdef DECLARE_WAITQUEUE
+   wait_queue_head_t sleep_wait;
+   init_waitqueue_head(&sleep_wait);
+-  {
+-  DECLARE_WAITQUEUE(wait, current);
+-#else
+-  struct wait_queue *sleep_wait = NULL;
+-  struct wait_queue wait = { current, NULL };
+-#endif
+   D1(printk("schedule_usleep(%d)\n", us));
+-  add_wait_queue(&sleep_wait, &wait);
+-  set_current_state(TASK_INTERRUPTIBLE);
+   start_one_shot_timer(&t, wake_up_func, (unsigned long)&sleep_wait, us,
+                        "usleep");
+-  schedule();
+-  set_current_state(TASK_RUNNING);
+-  remove_wait_queue(&sleep_wait, &wait);
++  /* Uninterruptible sleep on the fast timer. (The condition is somewhat
++     redundant since the timer is what wakes us up.) */
++  wait_event(sleep_wait, !fast_timer_pending(&t));
++
+   D1(printk("done schedule_usleep(%d)\n", us));
+-#ifdef DECLARE_WAITQUEUE
+-  }
+-#endif
+ }
+ #ifdef CONFIG_PROC_FS
+@@ -638,7 +538,7 @@
+   unsigned long flags;
+   int i = 0;
+   int num_to_show;
+-  struct timeval tv;
++  struct fasttime_t tv;
+   struct fast_timer *t, *nextt;
+   static char *bigbuf = NULL;
+   static unsigned long used;
+@@ -646,7 +546,8 @@
+   if (!bigbuf && !(bigbuf = vmalloc(BIG_BUF_SIZE)))
+   {
+     used = 0;
+-    bigbuf[0] = '\0';
++    if (buf)
++          buf[0] = '\0';
+     return 0;
+   }
+@@ -668,7 +569,7 @@
+     used += sprintf(bigbuf + used, "Fast timer running:    %s\n",
+                     fast_timer_running ? "yes" : "no");
+     used += sprintf(bigbuf + used, "Current time:          %lu.%06lu\n",
+-                    (unsigned long)tv.tv_sec,
++                    (unsigned long)tv.tv_jiff,
+                     (unsigned long)tv.tv_usec);
+ #ifdef FAST_TIMER_SANITY_CHECKS
+     used += sprintf(bigbuf + used, "Sanity failed:         %i\n",
+@@ -717,9 +618,9 @@
+                       "d: %6li us data: 0x%08lX"
+                       "\n",
+                       t->name,
+-                      (unsigned long)t->tv_set.tv_sec,
++                      (unsigned long)t->tv_set.tv_jiff,
+                       (unsigned long)t->tv_set.tv_usec,
+-                      (unsigned long)t->tv_expires.tv_sec,
++                      (unsigned long)t->tv_expires.tv_jiff,
+                       (unsigned long)t->tv_expires.tv_usec,
+                       t->delay_us,
+                       t->data
+@@ -739,9 +640,9 @@
+                       "d: %6li us data: 0x%08lX"
+                       "\n",
+                       t->name,
+-                      (unsigned long)t->tv_set.tv_sec,
++                      (unsigned long)t->tv_set.tv_jiff,
+                       (unsigned long)t->tv_set.tv_usec,
+-                      (unsigned long)t->tv_expires.tv_sec,
++                      (unsigned long)t->tv_expires.tv_jiff,
+                       (unsigned long)t->tv_expires.tv_usec,
+                       t->delay_us,
+                       t->data
+@@ -759,9 +660,9 @@
+                       "d: %6li us data: 0x%08lX"
+                       "\n",
+                       t->name,
+-                      (unsigned long)t->tv_set.tv_sec,
++                      (unsigned long)t->tv_set.tv_jiff,
+                       (unsigned long)t->tv_set.tv_usec,
+-                      (unsigned long)t->tv_expires.tv_sec,
++                      (unsigned long)t->tv_expires.tv_jiff,
+                       (unsigned long)t->tv_expires.tv_usec,
+                       t->delay_us,
+                       t->data
+@@ -772,7 +673,6 @@
+     used += sprintf(bigbuf + used, "Active timers:\n");
+     local_irq_save(flags);
+-    local_irq_save(flags);
+     t = fast_timer_list;
+     while (t != NULL && (used+100 < BIG_BUF_SIZE))
+     {
+@@ -783,15 +683,15 @@
+ /*                      " func: 0x%08lX" */
+                       "\n",
+                       t->name,
+-                      (unsigned long)t->tv_set.tv_sec,
++                      (unsigned long)t->tv_set.tv_jiff,
+                       (unsigned long)t->tv_set.tv_usec,
+-                      (unsigned long)t->tv_expires.tv_sec,
++                      (unsigned long)t->tv_expires.tv_jiff,
+                       (unsigned long)t->tv_expires.tv_usec,
+                       t->delay_us,
+                       t->data
+ /*                      , t->function */
+                       );
+-      local_irq_disable();
++      local_irq_save(flags);
+       if (t->next != nextt)
+       {
+         printk("timer removed!\n");
+@@ -822,7 +722,7 @@
+ static struct fast_timer tr[10];
+ static int exp_num[10];
+-static struct timeval tv_exp[100];
++static struct fasttime_t tv_exp[100];
+ static void test_timeout(unsigned long data)
+ {
+@@ -860,7 +760,7 @@
+   int prev_num;
+   int j;
+-  struct timeval tv, tv0, tv1, tv2;
++  struct fasttime_t tv, tv0, tv1, tv2;
+   printk("fast_timer_test() start\n");
+   do_gettimeofday_fast(&tv);
+@@ -873,7 +773,7 @@
+   {
+     do_gettimeofday_fast(&tv_exp[j]);
+   }
+-  printk("fast_timer_test() %is %06i\n", tv.tv_sec, tv.tv_usec);
++  printk("fast_timer_test() %is %06i\n", tv.tv_jiff, tv.tv_usec);
+   for (j = 0; j < 1000; j++)
+   {
+@@ -883,11 +783,11 @@
+   for (j = 0; j < 100; j++)
+   {
+     printk("%i.%i %i.%i %i.%i %i.%i %i.%i\n",
+-           tv_exp[j].tv_sec,tv_exp[j].tv_usec,
+-           tv_exp[j+1].tv_sec,tv_exp[j+1].tv_usec,
+-           tv_exp[j+2].tv_sec,tv_exp[j+2].tv_usec,
+-           tv_exp[j+3].tv_sec,tv_exp[j+3].tv_usec,
+-           tv_exp[j+4].tv_sec,tv_exp[j+4].tv_usec);
++           tv_exp[j].tv_jiff,tv_exp[j].tv_usec,
++           tv_exp[j+1].tv_jiff,tv_exp[j+1].tv_usec,
++           tv_exp[j+2].tv_jiff,tv_exp[j+2].tv_usec,
++           tv_exp[j+3].tv_jiff,tv_exp[j+3].tv_usec,
++           tv_exp[j+4].tv_jiff,tv_exp[j+4].tv_usec);
+     j += 4;
+   }
+   do_gettimeofday_fast(&tv0);
+@@ -919,9 +819,9 @@
+     }
+   }
+   do_gettimeofday_fast(&tv2);
+-  printk("Timers started    %is %06i\n", tv0.tv_sec, tv0.tv_usec);
+-  printk("Timers started at %is %06i\n", tv1.tv_sec, tv1.tv_usec);
+-  printk("Timers done       %is %06i\n", tv2.tv_sec, tv2.tv_usec);
++  printk("Timers started    %is %06i\n", tv0.tv_jiff, tv0.tv_usec);
++  printk("Timers started at %is %06i\n", tv1.tv_jiff, tv1.tv_usec);
++  printk("Timers done       %is %06i\n", tv2.tv_jiff, tv2.tv_usec);
+   DP(printk("buf0:\n");
+      printk(buf0);
+      printk("buf1:\n");
+@@ -943,9 +843,9 @@
+     printk("%-10s set: %6is %06ius exp: %6is %06ius "
+            "data: 0x%08X func: 0x%08X\n",
+            t->name,
+-           t->tv_set.tv_sec,
++           t->tv_set.tv_jiff,
+            t->tv_set.tv_usec,
+-           t->tv_expires.tv_sec,
++           t->tv_expires.tv_jiff,
+            t->tv_expires.tv_usec,
+            t->data,
+            t->function
+@@ -953,10 +853,10 @@
+     printk("           del: %6ius     did exp: %6is %06ius as #%i error: %6li\n",
+            t->delay_us,
+-           tv_exp[j].tv_sec,
++           tv_exp[j].tv_jiff,
+            tv_exp[j].tv_usec,
+            exp_num[j],
+-           (tv_exp[j].tv_sec - t->tv_expires.tv_sec)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec);
++           (tv_exp[j].tv_jiff - t->tv_expires.tv_jiff)*1000000 + tv_exp[j].tv_usec - t->tv_expires.tv_usec);
+   }
+   proc_fasttimer_read(buf5, NULL, 0, 0, 0);
+   printk("buf5 after all done:\n");
+@@ -966,7 +866,7 @@
+ #endif
+-void fast_timer_init(void)
++int fast_timer_init(void)
+ {
+   /* For some reason, request_irq() hangs when called froom time_init() */
+   if (!fast_timer_is_init)
+@@ -981,10 +881,10 @@
+     proc_register_dynamic(&proc_root, &fasttimer_proc_entry);
+ #endif
+ #endif /* PROC_FS */
+-    if(request_irq(TIMER_INTR_VECT, timer_trig_interrupt, IRQF_DISABLED,
+-                   "fast timer int", NULL))
++    if(request_irq(TIMER_INTR_VECT, timer_trig_interrupt, SA_SHIRQ | SA_INTERRUPT,
++                   "fast timer int", &fast_timer_list))
+     {
+-      printk("err: timer1 irq\n");
++      printk("err: fasttimer irq\n");
+     }
+     fast_timer_is_init = 1;
+ #ifdef FAST_TIMER_TEST
+@@ -992,4 +892,6 @@
+     fast_timer_test();
+ #endif
+   }
++  return 0;
+ }
++__initcall(fast_timer_init);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/head.S linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/head.S
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/head.S        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/head.S        2007-01-09 10:29:19.000000000 +0100
+@@ -4,7 +4,6 @@
+  * Copyright (C) 2003, Axis Communications AB
+  */
+-
+ #define ASSEMBLER_MACROS_ONLY
+ /*
+@@ -12,14 +11,21 @@
+  * -traditional must not be used when assembling this file.
+  */
+ #include <asm/arch/hwregs/reg_rdwr.h>
++#include <asm/arch/memmap.h>  
++#include <asm/arch/hwregs/intr_vect.h>
+ #include <asm/arch/hwregs/asm/mmu_defs_asm.h>
+ #include <asm/arch/hwregs/asm/reg_map_asm.h>
+ #include <asm/arch/hwregs/asm/config_defs_asm.h>
+ #include <asm/arch/hwregs/asm/bif_core_defs_asm.h>
+-
++#include <asm/arch/hwregs/asm/pinmux_defs_asm.h>
++#include <asm/arch/hwregs/asm/gio_defs_asm.h>
++                      
+ #define CRAMFS_MAGIC 0x28cd3d45
++#define JHEAD_MAGIC 0x1FF528A6
++#define JHEAD_SIZE 8
+ #define RAM_INIT_MAGIC 0x56902387
+-#define COMMAND_LINE_MAGIC 0x87109563
++#define COMMAND_LINE_MAGIC 0x87109563 
++#define NAND_BOOT_MAGIC 0x9a9db001
+       ;; NOTE: R8 and R9 carry information from the decompressor (if the
+       ;; kernel was compressed). They must not be used in the code below
+@@ -30,11 +36,10 @@
+       .global romfs_start
+       .global romfs_length
+       .global romfs_in_flash
++      .global nand_boot
+       .global swapper_pg_dir
+-      .global crisv32_nand_boot
+-      .global crisv32_nand_cramfs_offset
+-      ;; Dummy section to make it bootable with current VCS simulator
++      ;; Dummy section to make it bootable with current VCS simulator 
+ #ifdef CONFIG_ETRAXFS_SIM
+       .section ".boot", "ax"
+       ba tstart
+@@ -42,13 +47,13 @@
+ #endif
+       .text
+-tstart:
++tstart:        
+       ;; This is the entry point of the kernel. The CPU is currently in
+       ;; supervisor mode.
+-      ;;
++      ;; 
+       ;; 0x00000000 if flash.
+       ;; 0x40004000 if DRAM.
+-      ;;
++      ;; 
+       di
+       ;; Start clocks for used blocks.
+@@ -72,20 +77,25 @@
+       move.d   REG_ADDR(bif_core, regi_bif_core, rw_grp4_cfg), $r0
+       move.d   CONFIG_ETRAX_MEM_GRP4_CONFIG, $r1
+       move.d   $r1, [$r0]
+-
+-#ifdef CONFIG_ETRAXFS_SIM
++      
++#ifdef CONFIG_ETRAXFS_SIM     
+       ;; Set up minimal flash waitstates
+       move.d 0, $r10
+       move.d REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg), $r11
+       move.d $r10, [$r11]
+-#endif
++#endif        
++#ifdef CONFIG_SMP     
++secondary_cpu_entry: /* Entry point for secondary CPUs */
++      di
++#endif        
++              
+       ;; Setup and enable the MMU. Use same configuration for both the data
+       ;; and the instruction MMU.
+       ;;
+       ;; Note; 3 cycles is needed for a bank-select to take effect. Further;
+       ;; bank 1 is the instruction MMU, bank 2 is the data MMU.
+-#ifndef CONFIG_ETRAXFS_SIM
++#ifndef CONFIG_ETRAXFS_SIM    
+       move.d  REG_FIELD(mmu, rw_mm_kbase_hi, base_e, 8)       \
+               | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 4)     \
+               | REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb), $r0
+@@ -96,7 +106,7 @@
+               | REG_FIELD(mmu, rw_mm_kbase_hi, base_c, 0)     \
+               | REG_FIELD(mmu, rw_mm_kbase_hi, base_b, 0xb)   \
+               | REG_FIELD(mmu, rw_mm_kbase_hi, base_a, 0xa), $r0
+-#endif
++#endif        
+       ;; Temporary map of 0x40 -> 0x40 and 0x00 -> 0x00.
+       move.d  REG_FIELD(mmu, rw_mm_kbase_lo, base_4, 4)  \
+@@ -146,8 +156,8 @@
+               | REG_STATE(mmu, rw_mm_cfg, seg_2, page)        \
+               | REG_STATE(mmu, rw_mm_cfg, seg_1, page)        \
+               | REG_STATE(mmu, rw_mm_cfg, seg_0, linear), $r2
+-#endif
+-
++#endif        
++      
+       ;; Update instruction MMU.
+       move    1, $srs
+       nop
+@@ -165,7 +175,7 @@
+       move    $r0, $s2        ; kbase_hi.
+       move    $r1, $s1        ; kbase_lo
+       move    $r2, $s0        ; mm_cfg, virtual memory configuration.
+-
++      
+       ;; Enable data and instruction MMU.
+       move    0, $srs
+       moveq   0xf, $r0        ;  IMMU, DMMU, DCache, Icache on
+@@ -183,17 +193,11 @@
+       nop
+       nop
+       nop
+-      move    $s10, $r0
++      move    $s12, $r0
+       cmpq    0, $r0
+       beq     master_cpu
+       nop
+ slave_cpu:
+-      ; A slave waits for cpu_now_booting to be equal to CPU ID.
+-      move.d  cpu_now_booting, $r1
+-slave_wait:
+-      cmp.d   [$r1], $r0
+-      bne     slave_wait
+-      nop
+       ; Time to boot-up. Get stack location provided by master CPU.
+       move.d  smp_init_current_idle_thread, $r1
+       move.d  [$r1], $sp
+@@ -203,9 +207,16 @@
+       jsr     smp_callin
+       nop
+ master_cpu:
+-#endif
++      /* Set up entry point for secondary CPUs. The boot ROM has set up
++       * EBP at start of internal memory. The CPU will get there
++       * later when we issue an IPI to them... */
++      move.d MEM_INTMEM_START + IPI_INTR_VECT * 4, $r0
++      move.d secondary_cpu_entry, $r1
++      move.d $r1, [$r0]
++#endif        
+ #ifndef CONFIG_ETRAXFS_SIM
+-      ;; Check if starting from DRAM or flash.
++      ; Check if starting from DRAM (network->RAM boot or unpacked 
++      ; compressed kernel), or directly from flash.
+       lapcq   ., $r0
+       and.d   0x7fffffff, $r0 ; Mask off the non-cache bit.
+       cmp.d   0x10000, $r0    ; Arbitrary, something above this code.
+@@ -238,6 +249,7 @@
+       ;; Copy the text and data section to DRAM. This depends on that the
+       ;; variables used below are correctly set up by the linker script.
+       ;; The calculated value stored in R4 is used below.
++      ;; Leave the cramfs file system (piggybacked after the kernel) in flash.
+       moveq   0, $r0          ; Source.
+       move.d  text_start, $r1 ; Destination.
+       move.d  __vmlinux_end, $r2
+@@ -249,7 +261,7 @@
+       blo     1b
+       nop
+-      ;; Keep CRAMFS in flash.
++      ;; Check for cramfs.
+       moveq   0, $r0
+       move.d  romfs_length, $r1
+       move.d  $r0, [$r1]
+@@ -257,7 +269,8 @@
+       cmp.d   CRAMFS_MAGIC, $r0
+       bne 1f
+       nop
+-
++      
++      ;; Set length and start of cramfs, set romfs_in_flash flag
+       addoq   +4, $r4, $acr
+       move.d  [$acr], $r0
+       move.d  romfs_length, $r1
+@@ -273,35 +286,32 @@
+       nop
+ _inram:
+-      ;; Check if booting from NAND flash (in that case we just remember the offset
+-      ;; into the flash where cramfs should be).
+-      move.d  REG_ADDR(config, regi_config, r_bootsel), $r0
+-      move.d  [$r0], $r0
+-      and.d   REG_MASK(config, r_bootsel, boot_mode), $r0
+-      cmp.d   REG_STATE(config, r_bootsel, boot_mode, nand), $r0
+-      bne     move_cramfs
+-      moveq   1,$r0
+-      move.d  crisv32_nand_boot, $r1
+-      move.d  $r0, [$r1]
+-      move.d  crisv32_nand_cramfs_offset, $r1
+-      move.d  $r9, [$r1]
++      ;; Check if booting from NAND flash; if so, set appropriate flags
++      ;; and move on.
++      cmp.d   NAND_BOOT_MAGIC, $r12
++      bne     move_cramfs     ; not nand, jump
+       moveq   1, $r0
+-      move.d  romfs_in_flash, $r1
++      move.d  nand_boot, $r1  ; tell axisflashmap we're booting from NAND
++      move.d  $r0, [$r1]
++      moveq   0, $r0          ; tell axisflashmap romfs is not in 
++      move.d  romfs_in_flash, $r1 ; (directly accessed) flash
+       move.d  $r0, [$r1]
+-      jump    _start_it
++      jump    _start_it       ; continue with boot
+       nop
+-move_cramfs:
+-      ;; Move the cramfs after BSS.
++move_cramfs:          
++      ;; kernel is in DRAM.
++      ;; Must figure out if there is a piggybacked rootfs image or not.
++      ;; Set romfs_length to 0 => no rootfs image available by default.
+       moveq   0, $r0
+       move.d  romfs_length, $r1
+       move.d  $r0, [$r1]
+-#ifndef CONFIG_ETRAXFS_SIM
++#ifndef CONFIG_ETRAXFS_SIM            
+       ;; The kernel could have been unpacked to DRAM by the loader, but
+-      ;; the cramfs image could still be inte the flash immediately
+-      ;; following the compressed kernel image. The loaded passes the address
+-      ;; of the bute succeeding the last compressed byte in the flash in
++      ;; the cramfs image could still be in the flash immediately
++      ;; following the compressed kernel image. The loader passes the address
++      ;; of the byte succeeding the last compressed byte in the flash in
+       ;; register R9 when starting the kernel.
+       cmp.d   0x0ffffff8, $r9
+       bhs     _no_romfs_in_flash ; R9 points outside the flash area.
+@@ -309,12 +319,14 @@
+ #else
+       ba _no_romfs_in_flash
+       nop
+-#endif
++#endif        
++      ;; cramfs rootfs might to be in flash. Check for it.
+       move.d  [$r9], $r0      ; cramfs_super.magic
+       cmp.d   CRAMFS_MAGIC, $r0
+       bne     _no_romfs_in_flash
+       nop
++      ;; found cramfs in flash. set address and size, and romfs_in_flash flag.
+       addoq   +4, $r9, $acr
+       move.d  [$acr], $r0
+       move.d  romfs_length, $r1
+@@ -330,29 +342,45 @@
+       nop
+ _no_romfs_in_flash:
+-      ;; Look for cramfs.
+-#ifndef CONFIG_ETRAXFS_SIM
++      ;; No romfs in flash, so look for cramfs, or jffs2 with jhead, 
++      ;; after kernel in RAM, as is the case with network->RAM boot.
++      ;; For cramfs, partition starts with magic and length.
++      ;; For jffs2, a jhead is prepended which contains with magic and length.
++      ;; The jhead is not part of the jffs2 partition however.
++#ifndef CONFIG_ETRAXFS_SIM            
+       move.d  __vmlinux_end, $r0
+ #else
+-      move.d  __end, $r0
+-#endif
++      move.d  __end, $r0      
++#endif        
+       move.d  [$r0], $r1
+-      cmp.d   CRAMFS_MAGIC, $r1
+-      bne     2f
++      cmp.d   CRAMFS_MAGIC, $r1 ; cramfs magic?
++      beq     2f                ; yes, jump
++      nop
++      cmp.d   JHEAD_MAGIC, $r1 ; jffs2 (jhead) magic?
++      bne     4f              ; no, skip copy
++      nop
++      addq    4, $r0          ; location of jffs2 size
++      move.d  [$r0+], $r2     ; fetch jffs2 size -> r2
++                              ; r0 now points to start of jffs2
++      ba      3f
+       nop
++2:
++      addoq   +4, $r0, $acr   ; location of cramfs size
++      move.d  [$acr], $r2     ; fetch cramfs size -> r2
++                              ; r0 still points to start of cramfs
++3:
++      ;; Now, move the root fs to after kernel's BSS
+-      addoq   +4, $r0, $acr
+-      move.d  [$acr], $r2
+-      move.d  _end, $r1
++      move.d  _end, $r1       ; start of cramfs -> r1
+       move.d  romfs_start, $r3
+-      move.d  $r1, [$r3]
++      move.d  $r1, [$r3]      ; store at romfs_start (for axisflashmap)
+       move.d  romfs_length, $r3
+-      move.d  $r2, [$r3]
++      move.d  $r2, [$r3]      ; store size at romfs_length
+-#ifndef CONFIG_ETRAXFS_SIM
+-      add.d   $r2, $r0
++#ifndef CONFIG_ETRAXFS_SIM            
++      add.d   $r2, $r0        ; copy from end and downwards
+       add.d   $r2, $r1
+-
++      
+       lsrq    1, $r2          ; Size is in bytes, we copy words.
+       addq    1, $r2
+ 1:
+@@ -364,17 +392,24 @@
+       bne     1b
+       nop
+ #endif
+-
+-2:
++      
++4:
++      ;; BSS move done.
++      ;; Clear romfs_in_flash flag, as we now know romfs is in DRAM
++      ;; Also clear nand_boot flag; if we got here, we know we've not
++      ;; booted from NAND flash.
+       moveq   0, $r0
+       move.d  romfs_in_flash, $r1
+       move.d  $r0, [$r1]
++      moveq   0, $r0
++      move.d  nand_boot, $r1
++      move.d  $r0, [$r1]
+       jump    _start_it       ; Jump to cached code.
+       nop
+-
++      
+ _start_it:
+-
++      
+       ;; Check if kernel command line is supplied
+       cmp.d   COMMAND_LINE_MAGIC, $r10
+       bne     no_command_line
+@@ -383,9 +418,9 @@
+       move.d  256, $r13
+       move.d  cris_command_line, $r10
+       or.d    0x80000000, $r11 ; Make it virtual
+-1:
+-      move.b  [$r11+], $r12
+-      move.b  $r12, [$r10+]
++1:                            
++      move.b  [$r11+], $r1
++      move.b  $r1, [$r10+]    
+       subq    1, $r13
+       bne     1b
+       nop
+@@ -401,7 +436,7 @@
+       move.d  etrax_irv, $r1  ; Set the exception base register and pointer.
+       move.d  $r0, [$r1]
+-#ifndef CONFIG_ETRAXFS_SIM
++#ifndef CONFIG_ETRAXFS_SIM    
+       ;; Clear the BSS region from _bss_start to _end.
+       move.d  __bss_start, $r0
+       move.d  _end, $r1
+@@ -429,17 +464,31 @@
+       .data
+ etrax_irv:
+       .dword 0
++
++; Variables for communication with the Axis flash map driver (axisflashmap),
++; and for setting up memory in arch/cris/kernel/setup.c .
++
++; romfs_start is set to the start of the root file system, if it exists
++; in directly accessible memory (i.e. NOR Flash when booting from Flash,
++; or RAM when booting directly from a network-downloaded RAM image)
+ romfs_start:
+       .dword 0
++
++; romfs_length is set to the size of the root file system image, if it exists
++; in directly accessible memory (see romfs_start). Otherwise it is set to 0.
+ romfs_length:
+       .dword 0
++
++; romfs_in_flash is set to 1 if the root file system resides in directly
++; accessible flash memory (i.e. NOR flash). It is set to 0 for RAM boot
++; or NAND flash boot.
+ romfs_in_flash:
+       .dword 0
+-crisv32_nand_boot:
+-      .dword 0
+-crisv32_nand_cramfs_offset:
+-      .dword 0
++; nand_boot is set to 1 when the kernel has been booted from NAND flash
++nand_boot:
++      .dword 0
++      
+ swapper_pg_dir = 0xc0002000
+       .section ".init.data", "aw"
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/io.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/io.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/io.c  2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/io.c  2006-11-21 00:04:55.000000000 +0100
+@@ -1,7 +1,7 @@
+-/*
++/* 
+  * Helper functions for I/O pins.
+  *
+- * Copyright (c) 2004 Axis Communications AB.
++ * Copyright (c) 2004, 2006 Axis Communications AB.
+  */
+ #include <linux/types.h>
+@@ -15,6 +15,10 @@
+ #include <asm/arch/pinmux.h>
+ #include <asm/arch/hwregs/gio_defs.h>
++#ifndef DEBUG
++#define DEBUG(x)
++#endif
++
+ struct crisv32_ioport crisv32_ioports[] =
+ {
+       {
+@@ -46,13 +50,15 @@
+               (unsigned long*)REG_ADDR(gio, regi_gio, rw_pe_dout),
+               (unsigned long*)REG_ADDR(gio, regi_gio, r_pe_din),
+               18
+-      }
++      } 
+ };
+ #define NBR_OF_PORTS sizeof(crisv32_ioports)/sizeof(struct crisv32_ioport)
+-struct crisv32_iopin crisv32_led1_green;
+-struct crisv32_iopin crisv32_led1_red;
++struct crisv32_iopin crisv32_led_net0_green;
++struct crisv32_iopin crisv32_led_net0_red;
++struct crisv32_iopin crisv32_led_net1_green;
++struct crisv32_iopin crisv32_led_net1_red;
+ struct crisv32_iopin crisv32_led2_green;
+ struct crisv32_iopin crisv32_led2_red;
+ struct crisv32_iopin crisv32_led3_green;
+@@ -76,34 +82,54 @@
+ static int __init crisv32_io_init(void)
+ {
+       int ret = 0;
++
++      u32 i;
++
++      /* Locks *should* be dynamically initialized. */
++      for (i = 0; i < ARRAY_SIZE(crisv32_ioports); i++)
++              spin_lock_init (&crisv32_ioports[i].lock);
++      spin_lock_init (&dummy_port.lock);
++
+       /* Initialize LEDs */
+-      ret += crisv32_io_get_name(&crisv32_led1_green, CONFIG_ETRAX_LED1G);
+-      ret += crisv32_io_get_name(&crisv32_led1_red, CONFIG_ETRAX_LED1R);
++#if (defined(CONFIG_ETRAX_NBR_LED_GRP_ONE) || defined(CONFIG_ETRAX_NBR_LED_GRP_TWO))
++      ret += crisv32_io_get_name(&crisv32_led_net0_green, CONFIG_ETRAX_LED_G_NET0);
++      crisv32_io_set_dir(&crisv32_led_net0_green, crisv32_io_dir_out);
++      if (strcmp(CONFIG_ETRAX_LED_G_NET0, CONFIG_ETRAX_LED_R_NET0)) {
++              ret += crisv32_io_get_name(&crisv32_led_net0_red, CONFIG_ETRAX_LED_R_NET0);
++              crisv32_io_set_dir(&crisv32_led_net0_red, crisv32_io_dir_out);
++      } else
++              crisv32_led_net0_red = dummy_led;
++#endif
++
++#ifdef CONFIG_ETRAX_NBR_LED_GRP_TWO
++      ret += crisv32_io_get_name(&crisv32_led_net1_green, CONFIG_ETRAX_LED_G_NET1);
++      crisv32_io_set_dir(&crisv32_led_net1_green, crisv32_io_dir_out);
++      if (strcmp(CONFIG_ETRAX_LED_G_NET1, CONFIG_ETRAX_LED_R_NET1)) {
++              crisv32_io_get_name(&crisv32_led_net1_red, CONFIG_ETRAX_LED_R_NET1);
++              crisv32_io_set_dir(&crisv32_led_net1_red, crisv32_io_dir_out);
++      } else
++              crisv32_led_net1_red = dummy_led;
++#endif
++
+       ret += crisv32_io_get_name(&crisv32_led2_green, CONFIG_ETRAX_LED2G);
+       ret += crisv32_io_get_name(&crisv32_led2_red, CONFIG_ETRAX_LED2R);
+       ret += crisv32_io_get_name(&crisv32_led3_green, CONFIG_ETRAX_LED3G);
+       ret += crisv32_io_get_name(&crisv32_led3_red, CONFIG_ETRAX_LED3R);
+-      crisv32_io_set_dir(&crisv32_led1_green, crisv32_io_dir_out);
+-      crisv32_io_set_dir(&crisv32_led1_red, crisv32_io_dir_out);
++
+       crisv32_io_set_dir(&crisv32_led2_green, crisv32_io_dir_out);
+       crisv32_io_set_dir(&crisv32_led2_red, crisv32_io_dir_out);
+       crisv32_io_set_dir(&crisv32_led3_green, crisv32_io_dir_out);
+       crisv32_io_set_dir(&crisv32_led3_red, crisv32_io_dir_out);
+-      if (!strcmp(CONFIG_ETRAX_LED1G, CONFIG_ETRAX_LED1R))
+-              crisv32_led1_red = dummy_led;
+-      if (!strcmp(CONFIG_ETRAX_LED2G, CONFIG_ETRAX_LED2R))
+-              crisv32_led2_red = dummy_led;
+-
+       return ret;
+ }
+ __initcall(crisv32_io_init);
+-int crisv32_io_get(struct crisv32_iopin* iopin,
++int crisv32_io_get(struct crisv32_iopin* iopin, 
+                    unsigned int port, unsigned int pin)
+ {
+-      if (port > NBR_OF_PORTS)
++      if (port > NBR_OF_PORTS) 
+               return -EINVAL;
+       if (port > crisv32_ioports[port].pin_count)
+               return -EINVAL;
+@@ -111,14 +137,17 @@
+       iopin->bit = 1 << pin;
+       iopin->port = &crisv32_ioports[port];
+-      if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio))
++      /* Only allocate pinmux gpiopins if port != PORT_A (port 0) */
++        /* NOTE! crisv32_pinmux_alloc thinks PORT_B is port 0 */
++      if (port != 0 && crisv32_pinmux_alloc(port-1, pin, pin, pinmux_gpio)) 
+               return -EIO;
+-
++      DEBUG(printk("crisv32_io_get: Allocated pin %d on port %d\n", pin, port ));
++      
+       return 0;
+ }
+ int crisv32_io_get_name(struct crisv32_iopin* iopin,
+-                         char* name)
++                      const char* name)
+ {
+       int port;
+       int pin;
+@@ -128,7 +157,7 @@
+       if (toupper(*name) < 'A' || toupper(*name) > 'E')
+               return -EINVAL;
+-
++      
+       port = toupper(*name) - 'A';
+       name++;
+       pin = simple_strtoul(name, NULL, 10);
+@@ -139,9 +168,12 @@
+       iopin->bit = 1 << pin;
+       iopin->port = &crisv32_ioports[port];
+-      if (crisv32_pinmux_alloc(port, pin, pin, pinmux_gpio))
++      /* Only allocate pinmux gpiopins if port != PORT_A (port 0) */
++        /* NOTE! crisv32_pinmux_alloc thinks PORT_B is port 0 */
++      if (port != 0 && crisv32_pinmux_alloc(port-1, pin, pin, pinmux_gpio)) 
+               return -EIO;
++      DEBUG(printk("crisv32_io_get_name: Allocated pin %d on port %d\n", pin, port));
+       return 0;
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/irq.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/irq.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/irq.c 2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/irq.c 2006-10-13 14:43:13.000000000 +0200
+@@ -44,10 +44,10 @@
+   cpumask_t mask; /* The CPUs to which the IRQ may be allocated. */
+ };
+-struct cris_irq_allocation irq_allocations[NR_IRQS] =
++struct cris_irq_allocation irq_allocations[NR_IRQS] = 
+   {[0 ... NR_IRQS - 1] = {0, CPU_MASK_ALL}};
+-static unsigned long irq_regs[NR_CPUS] =
++static unsigned long irq_regs[NR_CPUS] = 
+ {
+   regi_irq,
+ #ifdef CONFIG_SMP
+@@ -79,9 +79,9 @@
+ extern void kgdb_init(void);
+ extern void breakpoint(void);
+-/*
+- * Build the IRQ handler stubs using macros from irq.h. First argument is the
+- * IRQ number, the second argument is the corresponding bit in
++/* 
++ * Build the IRQ handler stubs using macros from irq.h. First argument is the 
++ * IRQ number, the second argument is the corresponding bit in 
+  * intr_rw_vect_mask found in asm/arch/hwregs/intr_vect_defs.h.
+  */
+ BUILD_IRQ(0x31, (1 << 0))     /* memarb */
+@@ -139,7 +139,7 @@
+         spin_lock_irqsave(&irq_lock, flags);
+         intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask);
+-
++      
+       /* Remember; 1 let thru, 0 block. */
+       intr_mask &= ~(1 << (irq - FIRST_IRQ));
+@@ -152,10 +152,10 @@
+ {
+       int intr_mask;
+         unsigned long flags;
+-
++        
+         spin_lock_irqsave(&irq_lock, flags);
+         intr_mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask);
+-
++      
+       /* Remember; 1 let thru, 0 block. */
+       intr_mask |= (1 << (irq - FIRST_IRQ));
+@@ -168,7 +168,7 @@
+ {
+       int cpu;
+         unsigned long flags;
+-
++        
+         spin_lock_irqsave(&irq_lock, flags);
+         cpu = irq_allocations[irq - FIRST_IRQ].cpu;
+@@ -178,12 +178,12 @@
+               spin_unlock_irqrestore(&irq_lock, flags);
+               return smp_processor_id();
+         }
+-
++        
+       /* Let the interrupt stay if possible */
+       if (cpu_isset(cpu, irq_allocations[irq - FIRST_IRQ].mask))
+               goto out;
+-
++      
+       /* IRQ must be moved to another CPU. */
+       cpu = first_cpu(irq_allocations[irq - FIRST_IRQ].mask);
+       irq_allocations[irq - FIRST_IRQ].cpu = cpu;
+@@ -287,7 +287,7 @@
+  * interrupt from the CPU and software has to sort out which
+  * interrupts that happened. There are two special cases here:
+  *
+- * 1. Timer interrupts may never be blocked because of the
++ * 1. Timer interrupts may never be blocked because of the 
+  *    watchdog (refer to comment in include/asr/arch/irq.h)
+  * 2. GDB serial port IRQs are unhandled here and will be handled
+  *    as a single IRQ when it strikes again because the GDB
+@@ -304,33 +304,33 @@
+       cpu = smp_processor_id();
+       /* An extra irq_enter here to prevent softIRQs to run after
+-         * each do_IRQ. This will decrease the interrupt latency.
++         * each do_IRQ. This will decrease the interrupt latency. 
+        */
+       irq_enter();
+       /* Get which IRQs that happend. */
+       masked = REG_RD_INT(intr_vect, irq_regs[cpu], r_masked_vect);
+-
++      
+       /* Calculate new IRQ mask with these IRQs disabled. */
+       mask = REG_RD_INT(intr_vect, irq_regs[cpu], rw_mask);
+       mask &= ~masked;
+       /* Timer IRQ is never masked */
+       if (masked & TIMER_MASK)
+-              mask |= TIMER_MASK;
++              mask |= TIMER_MASK; 
+       /* Block all the IRQs */
+       REG_WR_INT(intr_vect, irq_regs[cpu], rw_mask, mask);
+-
++      
+       /* Check for timer IRQ and handle it special. */
+       if (masked & TIMER_MASK) {
+               masked &= ~TIMER_MASK;
+-              do_IRQ(TIMER_INTR_VECT, regs);
++              do_IRQ(TIMER_INTR_VECT, regs);          
+       }
+ #ifdef IGNORE_MASK
+       /* Remove IRQs that can't be handled as multiple. */
+-      masked &= ~IGNORE_MASK;
++      masked &= ~IGNORE_MASK; 
+ #endif
+       /* Handle the rest of the IRQs. */
+@@ -377,7 +377,7 @@
+       irq_desc[TIMER_INTR_VECT].status |= IRQ_PER_CPU;
+       irq_allocations[IPI_INTR_VECT - FIRST_IRQ].cpu = CPU_FIXED;
+       irq_desc[IPI_INTR_VECT].status |= IRQ_PER_CPU;
+-
++          
+       set_exception_vector(0x00, nmi_interrupt);
+       set_exception_vector(0x30, multiple_interrupt);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/kgdb.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/kgdb.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/kgdb.c        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/kgdb.c        2005-07-06 11:40:49.000000000 +0200
+@@ -25,7 +25,7 @@
+  *  kgdb usage notes:
+  *  -----------------
+  *
+- * If you select CONFIG_ETRAX_KGDB in the configuration, the kernel will be
++ * If you select CONFIG_ETRAX_KGDB in the configuration, the kernel will be 
+  * built with different gcc flags: "-g" is added to get debug infos, and
+  * "-fomit-frame-pointer" is omitted to make debugging easier. Since the
+  * resulting kernel will be quite big (approx. > 7 MB), it will be stripped
+@@ -118,7 +118,7 @@
+  *  call to kgdb_init() is necessary in order to allow any breakpoints
+  *  or error conditions to be properly intercepted and reported to gdb.
+  *  Two, a breakpoint needs to be generated to begin communication.  This
+- *  is most easily accomplished by a call to breakpoint().
++ *  is most easily accomplished by a call to breakpoint(). 
+  *
+  *    The following gdb commands are supported:
+  *
+@@ -382,8 +382,8 @@
+ int getDebugChar(void);
+ #ifdef CONFIG_ETRAXFS_SIM
+-int getDebugChar(void)
+-{
++int getDebugChar(void) 
++{ 
+   return socketread();
+ }
+ #endif
+@@ -490,7 +490,7 @@
+ /********************************** Breakpoint *******************************/
+ /* Use an internal stack in the breakpoint and interrupt response routines.
+-   FIXME: How do we know the size of this stack is enough?
++   FIXME: How do we know the size of this stack is enough? 
+    Global so it can be reached from assembler code. */
+ #define INTERNAL_STACK_SIZE 1024
+ char internal_stack[INTERNAL_STACK_SIZE];
+@@ -511,7 +511,7 @@
+ gdb_cris_strcpy(char *s1, const char *s2)
+ {
+       char *s = s1;
+-
++      
+       for (s = s1; (*s++ = *s2++) != '\0'; )
+               ;
+       return s1;
+@@ -522,7 +522,7 @@
+ gdb_cris_strlen(const char *s)
+ {
+       const char *sc;
+-
++      
+       for (sc = s; *sc != '\0'; sc++)
+               ;
+       return (sc - s);
+@@ -534,7 +534,7 @@
+ {
+       const unsigned char uc = c;
+       const unsigned char *su;
+-
++      
+       for (su = s; 0 < n; ++su, --n)
+               if (*su == uc)
+                       return (void *)su;
+@@ -549,15 +549,15 @@
+       char *s1;
+       char *sd;
+       int x = 0;
+-
++      
+       for (s1 = (char*)s; (sd = gdb_cris_memchr(hexchars, *s1, base)) != NULL; ++s1)
+               x = x * base + (sd - hexchars);
+-
++        
+         if (endptr) {
+                 /* Unconverted suffix is stored in endptr unless endptr is NULL. */
+                 *endptr = s1;
+         }
+-
++        
+       return x;
+ }
+@@ -629,7 +629,7 @@
+       } else if (regno == PID) {
+               /* 32-bit register. */
+               *valptr =  *(unsigned int *)((char *)&reg.pid);
+-
++             
+       } else if (regno == SRS) {
+               /* 8-bit register. */
+               *valptr = (unsigned int)(*(unsigned char *)((char *)&reg.srs));
+@@ -726,7 +726,7 @@
+               *buf++ = highhex (ch);
+               *buf++ = lowhex (ch);
+         }
+-
++        
+         /* Terminate properly. */
+       *buf = '\0';
+       return buf;
+@@ -804,7 +804,7 @@
+                       continue;
+               buffer[count] = 0;
+-
++              
+               if (ch == '#') {
+                       xmitcsum = hex(getDebugChar()) << 4;
+                       xmitcsum += hex(getDebugChar());
+@@ -836,7 +836,7 @@
+       int checksum;
+       int runlen;
+       int encode;
+-
++      
+       do {
+               char *src = buffer;
+               putDebugChar('$');
+@@ -905,42 +905,42 @@
+ {
+       char *ptr = output_buffer;
+       unsigned int reg_cont;
+-
++        
+       /* Send trap type (converted to signal) */
+-      *ptr++ = 'T';
++      *ptr++ = 'T';   
+       *ptr++ = highhex(sigval);
+       *ptr++ = lowhex(sigval);
+       if (((reg.exs & 0xff00) >> 8) == 0xc) {
+-
++              
+               /* Some kind of hardware watchpoint triggered. Find which one
+                  and determine its type (read/write/access).  */
+               int S, bp, trig_bits = 0, rw_bits = 0;
+               int trig_mask = 0;
+               unsigned int *bp_d_regs = &sreg.s3_3;
+               /* In a lot of cases, the stopped data address will simply be EDA.
+-                 In some cases, we adjust it to match the watched data range.
++                 In some cases, we adjust it to match the watched data range. 
+                  (We don't want to change the actual EDA though). */
+               unsigned int stopped_data_address;
+               /* The S field of EXS. */
+               S = (reg.exs & 0xffff0000) >> 16;
+-
++              
+               if (S & 1) {
+                       /* Instruction watchpoint. */
+                       /* FIXME: Check against, and possibly adjust reported EDA. */
+               } else {
+                       /* Data watchpoint.  Find the one that triggered. */
+                       for (bp = 0; bp < 6; bp++) {
+-
++                      
+                               /* Dx_RD, Dx_WR in the S field of EXS for this BP. */
+                               int bitpos_trig = 1 + bp * 2;
+                               /* Dx_BPRD, Dx_BPWR in BP_CTRL for this BP. */
+                               int bitpos_config = 2 + bp * 4;
+-
++                      
+                               /* Get read/write trig bits for this BP. */
+                               trig_bits = (S & (3 << bitpos_trig)) >> bitpos_trig;
+-
++                      
+                               /* Read/write config bits for this BP. */
+                               rw_bits = (sreg.s0_3 & (3 << bitpos_config)) >> bitpos_config;
+                               if (trig_bits) {
+@@ -949,11 +949,11 @@
+                                       if ((rw_bits == 0x1 && trig_bits != 0x1) ||
+                                           (rw_bits == 0x2 && trig_bits != 0x2))
+                                               panic("Invalid r/w trigging for this BP");
+-
++                              
+                                       /* Mark this BP as trigged for future reference. */
+                                       trig_mask |= (1 << bp);
+-
+-                                      if (reg.eda >= bp_d_regs[bp * 2] &&
++                              
++                                      if (reg.eda >= bp_d_regs[bp * 2] && 
+                                           reg.eda <= bp_d_regs[bp * 2 + 1]) {
+                                               /* EDA withing range for this BP; it must be the one
+                                                  we're looking for. */
+@@ -972,7 +972,7 @@
+                                       /* Read/write config bits for this BP (needed later). */
+                                       rw_bits = (sreg.s0_3 & (3 << bitpos_config)) >> bitpos_config;
+-
++                              
+                                       if (trig_mask & (1 << bp)) {
+                                               /* EDA within 31 bytes of the configured start address? */
+                                               if (reg.eda + 31 >= bp_d_regs[bp * 2]) {
+@@ -987,12 +987,12 @@
+                                       }
+                               }
+                       }
+-
++                      
+                       /* No match yet? */
+                       BUG_ON(bp >= 6);
+                       /* Note that we report the type according to what the BP is configured
+                          for (otherwise we'd never report an 'awatch'), not according to how
+-                         it trigged. We did check that the trigged bits match what the BP is
++                         it trigged. We did check that the trigged bits match what the BP is 
+                          configured for though. */
+                       if (rw_bits == 0x1) {
+                               /* read */
+@@ -1110,12 +1110,12 @@
+       if (sigval == SIGTRAP) {
+               /* Break 8, single step or hardware breakpoint exception. */
+-
++              
+               /* Check IDX field of EXS. */
+               if (((reg.exs & 0xff00) >> 8) == 0x18) {
+                       /* Break 8. */
+-
++                      
+                         /* Static (compiled) breakpoints must return to the next instruction
+                          in order to avoid infinite loops (default value of ERP). Dynamic
+                          (gdb-invoked) must subtract the size of the break instruction from
+@@ -1132,7 +1132,7 @@
+                                       reg.pc -= 2;
+                               }
+                       }
+-
++                      
+               } else if (((reg.exs & 0xff00) >> 8) == 0x3) {
+                       /* Single step. */
+                       /* Don't fiddle with S1. */
+@@ -1190,10 +1190,10 @@
+               unsigned int *bp_d_regs = &sreg.s3_3;
+               /* The watchpoint allocation scheme is the simplest possible.
+-                 For example, if a region is watched for read and
++                 For example, if a region is watched for read and 
+                  a write watch is requested, a new watchpoint will
+                  be used. Also, if a watch for a region that is already
+-                 covered by one or more existing watchpoints, a new
++                 covered by one or more existing watchpoints, a new 
+                  watchpoint will be used. */
+               /* First, find a free data watchpoint. */
+@@ -1205,13 +1205,13 @@
+                               break;
+                       }
+               }
+-
++                                             
+               if (bp > 5) {
+                       /* We're out of watchpoints. */
+                       gdb_cris_strcpy(output_buffer, error_message[E04]);
+                       return;
+               }
+-
++              
+               /* Configure the control register first. */
+               if (type == '3' || type == '4') {
+                       /* Trigger on read. */
+@@ -1221,11 +1221,11 @@
+                       /* Trigger on write. */
+                       sreg.s0_3 |= (2 << (2 + bp * 4));
+               }
+-
++                                             
+               /* Ugly pointer arithmetics to configure the watched range. */
+               bp_d_regs[bp * 2] = addr;
+               bp_d_regs[bp * 2 + 1] = (addr + len - 1);
+-      }
++      } 
+       /* Set the S1 flag to enable watchpoints. */
+       reg.ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT));
+@@ -1258,7 +1258,7 @@
+                       /* Not in use. */
+                       gdb_cris_strcpy(output_buffer, error_message[E04]);
+                       return;
+-              }
++              }               
+               /* Deconfigure. */
+               sreg.s1_3 = 0;
+               sreg.s2_3 = 0;
+@@ -1268,8 +1268,8 @@
+               unsigned int *bp_d_regs = &sreg.s3_3;
+               /* Try to find a watchpoint that is configured for the
+                  specified range, then check that read/write also matches. */
+-
+-              /* Ugly pointer arithmetic, since I cannot rely on a
++                                       
++              /* Ugly pointer arithmetic, since I cannot rely on a 
+                  single switch (addr) as there may be several watchpoints with
+                  the same start address for example. */
+@@ -1279,7 +1279,7 @@
+                               /* Matching range. */
+                               int bitpos = 2 + bp * 4;
+                               int rw_bits;
+-
++                              
+                               /* Read/write bits for this BP. */
+                               rw_bits = (sreg.s0_3 & (0x3 << bitpos)) >> bitpos;
+@@ -1347,7 +1347,7 @@
+                                       (char *)&sreg + (reg.srs * 16 * sizeof(unsigned int)),
+                                       16 * sizeof(unsigned int));
+                               break;
+-                      }
++                      }       
+                       case 'G':
+                               /* Write registers. GXX..XX
+                                  Each byte of register data  is described by two hex digits.
+@@ -1357,11 +1357,11 @@
+                               hex2mem((char *)&reg, &input_buffer[1], sizeof(registers));
+                               /* Support registers. */
+                               hex2mem((char *)&sreg + (reg.srs * 16 * sizeof(unsigned int)),
+-                                      &input_buffer[1] + sizeof(registers),
++                                      &input_buffer[1] + sizeof(registers), 
+                                       16 * sizeof(unsigned int));
+                               gdb_cris_strcpy(output_buffer, "OK");
+                               break;
+-
++                              
+                       case 'P':
+                               /* Write register. Pn...=r...
+                                  Write register n..., hex value without 0x, with value r...,
+@@ -1393,7 +1393,7 @@
+                                       }
+                               }
+                               break;
+-
++                              
+                       case 'm':
+                               /* Read from memory. mAA..AA,LLLL
+                                  AA..AA is the address and LLLL is the length.
+@@ -1416,7 +1416,7 @@
+                                         mem2hex(output_buffer, addr, len);
+                                 }
+                               break;
+-
++                              
+                       case 'X':
+                               /* Write to memory. XAA..AA,LLLL:XX..XX
+                                  AA..AA is the start address,  LLLL is the number of bytes, and
+@@ -1448,7 +1448,7 @@
+                                       }
+                               }
+                               break;
+-
++                              
+                       case 'c':
+                               /* Continue execution. cAA..AA
+                                  AA..AA is the address where execution is resumed. If AA..AA is
+@@ -1472,15 +1472,15 @@
+                               if ((sreg.s0_3 & 0x3fff) == 0) {
+                                       reg.ccs &= ~(1 << (S_CCS_BITNR + CCS_SHIFT));
+                               }
+-
++                              
+                               return;
+-
++                              
+                       case 's':
+                               /* Step. sAA..AA
+                                  AA..AA is the address where execution is resumed. If AA..AA is
+                                  omitted, resume at the present address. Success: return to the
+                                  executing thread. Failure: will never know. */
+-
++                              
+                               if (input_buffer[1] != '\0') {
+                                       /* FIXME: Doesn't handle address argument. */
+                                       gdb_cris_strcpy(output_buffer, error_message[E04]);
+@@ -1497,7 +1497,7 @@
+                               return;
+                        case 'Z':
+-
++                               
+                                /* Insert breakpoint or watchpoint, Ztype,addr,length.
+                                   Remote protocol says: A remote target shall return an empty string
+                                   for an unrecognized breakpoint or watchpoint packet type. */
+@@ -1522,7 +1522,7 @@
+                                        int addr = gdb_cris_strtol(&input_buffer[3], &lenptr, 16);
+                                        int len = gdb_cris_strtol(lenptr + 1, &dataptr, 16);
+                                        char type = input_buffer[1];
+-
++                                       
+                                        remove_watchpoint(type, addr, len);
+                                        break;
+                                }
+@@ -1537,14 +1537,14 @@
+                               output_buffer[2] = lowhex(sigval);
+                               output_buffer[3] = 0;
+                               break;
+-
++                              
+                       case 'D':
+                               /* Detach from host. D
+                                  Success: OK, and return to the executing thread.
+                                  Failure: will never know */
+                               putpacket("OK");
+                               return;
+-
++                              
+                       case 'k':
+                       case 'r':
+                               /* kill request or reset request.
+@@ -1552,7 +1552,7 @@
+                                  Failure: will never know. */
+                               kill_restart();
+                               break;
+-
++                              
+                       case 'C':
+                       case 'S':
+                       case '!':
+@@ -1570,7 +1570,7 @@
+                                  and ignored (below)? */
+                               gdb_cris_strcpy(output_buffer, error_message[E04]);
+                               break;
+-
++                              
+                       default:
+                               /* The stub should ignore other request and send an empty
+                                  response ($#<checksum>). This way we can extend the protocol and GDB
+@@ -1587,7 +1587,7 @@
+ {
+       reg_intr_vect_rw_mask intr_mask;
+       reg_ser_rw_intr_mask ser_intr_mask;
+-
++      
+       /* Configure the kgdb serial port. */
+ #if defined(CONFIG_ETRAX_KGDB_PORT0)
+       /* Note: no shortcut registered (not handled by multiple_interrupt).
+@@ -1597,9 +1597,9 @@
+       intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+       intr_mask.ser0 = 1;
+       REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+-
++      
+       ser_intr_mask = REG_RD(ser, regi_ser0, rw_intr_mask);
+-      ser_intr_mask.data_avail = regk_ser_yes;
++      ser_intr_mask.dav = regk_ser_yes;
+       REG_WR(ser, regi_ser0, rw_intr_mask, ser_intr_mask);
+ #elif defined(CONFIG_ETRAX_KGDB_PORT1)
+       /* Note: no shortcut registered (not handled by multiple_interrupt).
+@@ -1609,9 +1609,9 @@
+       intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+       intr_mask.ser1 = 1;
+       REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+-
++      
+       ser_intr_mask = REG_RD(ser, regi_ser1, rw_intr_mask);
+-      ser_intr_mask.data_avail = regk_ser_yes;
++      ser_intr_mask.dav = regk_ser_yes;
+       REG_WR(ser, regi_ser1, rw_intr_mask, ser_intr_mask);
+ #elif defined(CONFIG_ETRAX_KGDB_PORT2)
+       /* Note: no shortcut registered (not handled by multiple_interrupt).
+@@ -1621,9 +1621,9 @@
+       intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+       intr_mask.ser2 = 1;
+       REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+-
++      
+       ser_intr_mask = REG_RD(ser, regi_ser2, rw_intr_mask);
+-      ser_intr_mask.data_avail = regk_ser_yes;
++      ser_intr_mask.dav = regk_ser_yes;
+       REG_WR(ser, regi_ser2, rw_intr_mask, ser_intr_mask);
+ #elif defined(CONFIG_ETRAX_KGDB_PORT3)
+       /* Note: no shortcut registered (not handled by multiple_interrupt).
+@@ -1635,7 +1635,7 @@
+       REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+       ser_intr_mask = REG_RD(ser, regi_ser3, rw_intr_mask);
+-      ser_intr_mask.data_avail = regk_ser_yes;
++      ser_intr_mask.dav = regk_ser_yes;
+       REG_WR(ser, regi_ser3, rw_intr_mask, ser_intr_mask);
+ #endif
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/kgdb_asm.S linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/kgdb_asm.S
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/kgdb_asm.S    2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/kgdb_asm.S    2006-10-13 14:43:13.000000000 +0200
+@@ -11,7 +11,7 @@
+       .globl kgdb_handle_exception
+ kgdb_handle_exception:
+-
++      
+ ;; Create a register image of the caller.
+ ;;
+ ;; First of all, save the ACR on the stack since we need it for address calculations.
+@@ -262,7 +262,7 @@
+ ;; Nothing in S15, bank 3
+   clear.d [$acr]
+   addq    4,     $acr
+-
++      
+ ;; Check what got us here: get IDX field of EXS.
+   move $exs,    $r10
+   and.d 0xff00, $r10
+@@ -307,7 +307,7 @@
+ handle_comm:
+   move.d   internal_stack+1020, $sp ; Use the internal stack which grows upwards
+   jsr      handle_exception         ; Interactive routine
+-  nop
++  nop                               
+ ;;
+ ;; Return to the caller
+@@ -345,7 +345,7 @@
+ ;; Nothing in S6 - S7, bank 0.
+   addq    4,      $acr
+   addq    4,      $acr
+-
++      
+   move.d  [$acr], $r0
+   move    $r0,    $s8
+   addq    4,      $acr
+@@ -507,7 +507,7 @@
+    addq    8,      $acr
+    ;; Skip BZ, VR.
+-   addq    2,      $acr
++   addq    2,      $acr   
+    move    [$acr], $pid   ; Restore PID
+    addq    4,      $acr
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/pinmux.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/pinmux.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/pinmux.c      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/pinmux.c      2006-08-11 10:32:21.000000000 +0200
+@@ -1,7 +1,7 @@
+-/*
++/* 
+  * Allocator for I/O pins. All pins are allocated to GPIO at bootup.
+  * Unassigned pins and GPIO pins can be allocated to a fixed interface
+- * or the I/O processor instead.
++ * or the I/O processor instead. 
+  *
+  * Copyright (c) 2004 Axis Communications AB.
+  */
+@@ -33,9 +33,10 @@
+       if (!initialized) {
+               reg_pinmux_rw_pa pa = REG_RD(pinmux, regi_pinmux, rw_pa);
++              REG_WR_INT(pinmux, regi_pinmux, rw_hwprot, 0);
+               initialized = 1;
+-              pa.pa0 = pa.pa1 = pa.pa2 = pa.pa3 =
+-              pa.pa4 = pa.pa5 = pa.pa6 = pa.pa7 = regk_pinmux_yes;
++              pa.pa0 = pa.pa1 = pa.pa2 = pa.pa3 = 
++                      pa.pa4 = pa.pa5 = pa.pa6 = pa.pa7 = regk_pinmux_yes;
+               REG_WR(pinmux, regi_pinmux, rw_pa, pa);
+               crisv32_pinmux_alloc(PORT_B, 0, PORT_PINS - 1, pinmux_gpio);
+               crisv32_pinmux_alloc(PORT_C, 0, PORT_PINS - 1, pinmux_gpio);
+@@ -46,124 +47,137 @@
+       return 0;
+ }
+-int
+-crisv32_pinmux_alloc(int port, int first_pin, int last_pin, enum pin_mode mode)
++/*
++ * must be called with the pinmux_lock held.
++ */
++static int __crisv32_pinmux_alloc(int port, int first_pin, int last_pin,
++                                enum pin_mode mode)
+ {
+       int i;
+-      unsigned long flags;
+-      crisv32_pinmux_init();
+-
+-      if (port > PORTS)
++      if (port >= PORTS ||
++          first_pin < 0 || last_pin >= PORT_PINS || last_pin < first_pin)
+               return -EINVAL;
+-
+-      spin_lock_irqsave(&pinmux_lock, flags);
+-
+-      for (i = first_pin; i <= last_pin; i++)
++      
++      for (i = first_pin; i <= last_pin; i++) 
+       {
+-              if ((pins[port][i] != pinmux_none) && (pins[port][i] != pinmux_gpio) &&
+-                  (pins[port][i] != mode))
++              if ((pins[port][i] != pinmux_none)
++                  && (pins[port][i] != pinmux_gpio)
++                  && (pins[port][i] != mode)) 
+               {
+-                      spin_unlock_irqrestore(&pinmux_lock, flags);
+ #ifdef DEBUG
+                       panic("Pinmux alloc failed!\n");
+ #endif
+                       return -EPERM;
+               }
+       }
+-
++  
+       for (i = first_pin; i <= last_pin; i++)
+               pins[port][i] = mode;
+       crisv32_pinmux_set(port);
+-
+-      spin_unlock_irqrestore(&pinmux_lock, flags);
+-
+       return 0;
+ }
+ int
++crisv32_pinmux_alloc(int port, int first_pin, int last_pin, enum pin_mode mode)
++{
++      int r;
++      unsigned long flags;
++      
++      crisv32_pinmux_init();
++      
++      spin_lock_irqsave(&pinmux_lock, flags);
++      r = __crisv32_pinmux_alloc(port, first_pin, last_pin, mode);
++      spin_unlock_irqrestore(&pinmux_lock, flags);
++      return r;
++}
++
++int 
+ crisv32_pinmux_alloc_fixed(enum fixed_function function)
+ {
+       int ret = -EINVAL;
+       char saved[sizeof pins];
+       unsigned long flags;
+-
++        reg_pinmux_rw_hwprot hwprot;
++        
++      crisv32_pinmux_init();
++      
+       spin_lock_irqsave(&pinmux_lock, flags);
+       /* Save internal data for recovery */
+       memcpy(saved, pins, sizeof pins);
+-
+-      reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
+-
++  
++      hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
++  
+       switch(function)
+       {
+       case pinmux_ser1:
+-              ret = crisv32_pinmux_alloc(PORT_C, 4, 7, pinmux_fixed);
++              ret = __crisv32_pinmux_alloc(PORT_C, 4, 7, pinmux_fixed);
+               hwprot.ser1 = regk_pinmux_yes;
+               break;
+       case pinmux_ser2:
+-              ret = crisv32_pinmux_alloc(PORT_C, 8, 11, pinmux_fixed);
++              ret = __crisv32_pinmux_alloc(PORT_C, 8, 11, pinmux_fixed);
+               hwprot.ser2 = regk_pinmux_yes;
+               break;
+       case pinmux_ser3:
+-              ret = crisv32_pinmux_alloc(PORT_C, 12, 15, pinmux_fixed);
++              ret = __crisv32_pinmux_alloc(PORT_C, 12, 15, pinmux_fixed);
+               hwprot.ser3 = regk_pinmux_yes;
+               break;
+       case pinmux_sser0:
+-              ret = crisv32_pinmux_alloc(PORT_C, 0, 3, pinmux_fixed);
+-              ret |= crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed);
++              ret = __crisv32_pinmux_alloc(PORT_C, 0, 3, pinmux_fixed);
++              ret |= __crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed);
+               hwprot.sser0 = regk_pinmux_yes;
+               break;
+       case pinmux_sser1:
+-              ret = crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed);
++              ret = __crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed);
+               hwprot.sser1 = regk_pinmux_yes;
+               break;
+       case pinmux_ata0:
+-              ret = crisv32_pinmux_alloc(PORT_D, 5, 7, pinmux_fixed);
+-              ret |= crisv32_pinmux_alloc(PORT_D, 15, 17, pinmux_fixed);
++              ret = __crisv32_pinmux_alloc(PORT_D, 5, 7, pinmux_fixed);
++              ret |= __crisv32_pinmux_alloc(PORT_D, 15, 17, pinmux_fixed);
+               hwprot.ata0 = regk_pinmux_yes;
+               break;
+       case pinmux_ata1:
+-              ret = crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed);
+-              ret |= crisv32_pinmux_alloc(PORT_E, 17, 17, pinmux_fixed);
++              ret = __crisv32_pinmux_alloc(PORT_D, 0, 4, pinmux_fixed);
++              ret |= __crisv32_pinmux_alloc(PORT_E, 17, 17, pinmux_fixed);
+               hwprot.ata1 = regk_pinmux_yes;
+               break;
+       case pinmux_ata2:
+-              ret = crisv32_pinmux_alloc(PORT_C, 11, 15, pinmux_fixed);
+-              ret |= crisv32_pinmux_alloc(PORT_E, 3, 3, pinmux_fixed);
++              ret = __crisv32_pinmux_alloc(PORT_C, 11, 15, pinmux_fixed);
++              ret |= __crisv32_pinmux_alloc(PORT_E, 3, 3, pinmux_fixed);
+               hwprot.ata2 = regk_pinmux_yes;
+               break;
+       case pinmux_ata3:
+-              ret = crisv32_pinmux_alloc(PORT_C, 8, 10, pinmux_fixed);
+-              ret |= crisv32_pinmux_alloc(PORT_C, 0, 2, pinmux_fixed);
++              ret = __crisv32_pinmux_alloc(PORT_C, 8, 10, pinmux_fixed);
++              ret |= __crisv32_pinmux_alloc(PORT_C, 0, 2, pinmux_fixed);
+               hwprot.ata2 = regk_pinmux_yes;
+               break;
+       case pinmux_ata:
+-              ret = crisv32_pinmux_alloc(PORT_B, 0, 15, pinmux_fixed);
+-              ret |= crisv32_pinmux_alloc(PORT_D, 8, 15, pinmux_fixed);
++              ret = __crisv32_pinmux_alloc(PORT_B, 0, 15, pinmux_fixed);
++              ret |= __crisv32_pinmux_alloc(PORT_D, 8, 15, pinmux_fixed);
+               hwprot.ata = regk_pinmux_yes;
+               break;
+       case pinmux_eth1:
+-              ret = crisv32_pinmux_alloc(PORT_E, 0, 17, pinmux_fixed);
++              ret = __crisv32_pinmux_alloc(PORT_E, 0, 17, pinmux_fixed);
+               hwprot.eth1 = regk_pinmux_yes;
+               hwprot.eth1_mgm = regk_pinmux_yes;
+               break;
+       case pinmux_timer:
+-              ret = crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed);
++              ret = __crisv32_pinmux_alloc(PORT_C, 16, 16, pinmux_fixed);
+               hwprot.timer = regk_pinmux_yes;
+               spin_unlock_irqrestore(&pinmux_lock, flags);
+               return ret;
+       }
+-
++  
+       if (!ret)
+               REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot);
+       else
+               memcpy(pins, saved, sizeof pins);
+-
+-  spin_unlock_irqrestore(&pinmux_lock, flags);
+-
+-  return ret;
++      
++      spin_unlock_irqrestore(&pinmux_lock, flags);
++      
++      return ret;
+ }
+ void
+@@ -189,33 +203,126 @@
+ #endif
+ }
+-int
+-crisv32_pinmux_dealloc(int port, int first_pin, int last_pin)
++/*
++ * must be called with the pinmux_lock held.
++ */
++static int __crisv32_pinmux_dealloc(int port, int first_pin, int last_pin)
+ {
+       int i;
++  
++      if (port > PORTS)
++              return -EINVAL;
++  
++      for (i = first_pin; i <= last_pin; i++)
++              pins[port][i] = pinmux_none;
++
++      crisv32_pinmux_set(port);
++
++      return 0;
++}
++
++int crisv32_pinmux_dealloc(int port, int first_pin, int last_pin)
++{
++      int r;
+       unsigned long flags;
+       crisv32_pinmux_init();
++      
++      spin_lock_irqsave(&pinmux_lock, flags);
++      r = __crisv32_pinmux_dealloc(port, first_pin, last_pin);
++      spin_unlock_irqrestore(&pinmux_lock, flags);
++      return r;
++}
+-      if (port > PORTS)
+-              return -EINVAL;
++int 
++crisv32_pinmux_dealloc_fixed(enum fixed_function function)
++{
++      int ret = -EINVAL;
++      char saved[sizeof pins];
++      unsigned long flags;
+       spin_lock_irqsave(&pinmux_lock, flags);
+-      for (i = first_pin; i <= last_pin; i++)
+-              pins[port][i] = pinmux_none;
++      /* Save internal data for recovery */
++      memcpy(saved, pins, sizeof pins);
++  
++      reg_pinmux_rw_hwprot hwprot = REG_RD(pinmux, regi_pinmux, rw_hwprot);
++  
++      switch(function)
++      {
++      case pinmux_ser1:
++              ret = __crisv32_pinmux_dealloc(PORT_C, 4, 7);
++              hwprot.ser1 = regk_pinmux_no;
++              break;
++      case pinmux_ser2:
++              ret = __crisv32_pinmux_dealloc(PORT_C, 8, 11);
++              hwprot.ser2 = regk_pinmux_no;
++              break;
++      case pinmux_ser3:
++              ret = __crisv32_pinmux_dealloc(PORT_C, 12, 15);
++              hwprot.ser3 = regk_pinmux_no;
++              break;
++      case pinmux_sser0:
++              ret = __crisv32_pinmux_dealloc(PORT_C, 0, 3);
++              ret |= __crisv32_pinmux_dealloc(PORT_C, 16, 16);
++              hwprot.sser0 = regk_pinmux_no;
++              break;
++      case pinmux_sser1:
++              ret = __crisv32_pinmux_dealloc(PORT_D, 0, 4);
++              hwprot.sser1 = regk_pinmux_no;
++              break;
++      case pinmux_ata0:
++              ret = __crisv32_pinmux_dealloc(PORT_D, 5, 7);
++              ret |= __crisv32_pinmux_dealloc(PORT_D, 15, 17);
++              hwprot.ata0 = regk_pinmux_no;
++              break;
++      case pinmux_ata1:
++              ret = __crisv32_pinmux_dealloc(PORT_D, 0, 4);
++              ret |= __crisv32_pinmux_dealloc(PORT_E, 17, 17);
++              hwprot.ata1 = regk_pinmux_no;
++              break;
++      case pinmux_ata2:
++              ret = __crisv32_pinmux_dealloc(PORT_C, 11, 15);
++              ret |= __crisv32_pinmux_dealloc(PORT_E, 3, 3);
++              hwprot.ata2 = regk_pinmux_no;
++              break;
++      case pinmux_ata3:
++              ret = __crisv32_pinmux_dealloc(PORT_C, 8, 10);
++              ret |= __crisv32_pinmux_dealloc(PORT_C, 0, 2);
++              hwprot.ata2 = regk_pinmux_no;
++              break;
++      case pinmux_ata:
++              ret = __crisv32_pinmux_dealloc(PORT_B, 0, 15);
++              ret |= __crisv32_pinmux_dealloc(PORT_D, 8, 15);
++              hwprot.ata = regk_pinmux_no;
++              break;
++      case pinmux_eth1:
++              ret = __crisv32_pinmux_dealloc(PORT_E, 0, 17);
++              hwprot.eth1 = regk_pinmux_no;
++              hwprot.eth1_mgm = regk_pinmux_no;
++              break;
++      case pinmux_timer:
++              ret = __crisv32_pinmux_dealloc(PORT_C, 16, 16);
++              hwprot.timer = regk_pinmux_no;
++              spin_unlock_irqrestore(&pinmux_lock, flags);
++              return ret;
++      }
++  
++      if (!ret)
++              REG_WR(pinmux, regi_pinmux, rw_hwprot, hwprot);
++      else
++              memcpy(pins, saved, sizeof pins);
+-      crisv32_pinmux_set(port);
+       spin_unlock_irqrestore(&pinmux_lock, flags);
+-
+-      return 0;
++      
++      return ret;
+ }
+ void
+ crisv32_pinmux_dump(void)
+ {
+       int i, j;
+-
++  
+       crisv32_pinmux_init();
+       for (i = 0; i < PORTS; i++)
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/process.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/process.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/process.c     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/process.c     2006-10-13 14:43:13.000000000 +0200
+@@ -74,9 +74,9 @@
+ #else
+ {
+       reg_timer_rw_wd_ctrl wd_ctrl = {0};
+-
++      
+       stop_watchdog();
+-
++      
+       wd_ctrl.key = 16;       /* Arbitrary key. */
+       wd_ctrl.cnt = 1;        /* Minimum time. */
+       wd_ctrl.cmd = regk_timer_start;
+@@ -141,7 +141,7 @@
+ {
+       struct pt_regs *childregs;
+       struct switch_stack *swstack;
+-
++      
+       /*
+        * Put the pt_regs structure at the end of the new kernel stack page and
+        * fix it up. Note: the task_struct doubles as the kernel stack for the
+@@ -152,7 +152,7 @@
+         p->set_child_tid = p->clear_child_tid = NULL;
+         childregs->r10 = 0;   /* Child returns 0 after a fork/clone. */
+-      /* Set a new TLS ?
++      /* Set a new TLS ?  
+        * The TLS is in $mof beacuse it is the 5th argument to sys_clone.
+        */
+       if (p->mm && (clone_flags & CLONE_SETTLS)) {
+@@ -165,20 +165,20 @@
+       /* Paramater to ret_from_sys_call. 0 is don't restart the syscall. */
+       swstack->r9 = 0;
+-      /*
++      /* 
+        * We want to return into ret_from_sys_call after the _resume.
+        * ret_from_fork will call ret_from_sys_call.
+        */
+       swstack->return_ip = (unsigned long) ret_from_fork;
+-
++      
+       /* Fix the user-mode and kernel-mode stackpointer. */
+-      p->thread.usp = usp;
++      p->thread.usp = usp;    
+       p->thread.ksp = (unsigned long) swstack;
+       return 0;
+ }
+-/*
++/* 
+  * Be aware of the "magic" 7th argument in the four system-calls below.
+  * They need the latest stackframe, which is put as the 7th argument by
+  * entry.S. The previous arguments are dummies or actually used, but need
+@@ -200,7 +200,7 @@
+ /* FIXME: Is parent_tid/child_tid really third/fourth argument? Update lib? */
+ asmlinkage int
+-sys_clone(unsigned long newusp, unsigned long flags, int *parent_tid, int *child_tid,
++sys_clone(unsigned long newusp, unsigned long flags, int *parent_tid, int *child_tid, 
+       unsigned long tls, long srp, struct pt_regs *regs)
+ {
+       if (!newusp)
+@@ -209,11 +209,11 @@
+       return do_fork(flags, newusp, regs, 0, parent_tid, child_tid);
+ }
+-/*
++/* 
+  * vfork is a system call in i386 because of register-pressure - maybe
+  * we can remove it and handle it in libc but we put it here until then.
+  */
+-asmlinkage int
++asmlinkage int 
+ sys_vfork(long r10, long r11, long r12, long r13, long mof, long srp,
+       struct pt_regs *regs)
+ {
+@@ -222,7 +222,7 @@
+ /* sys_execve() executes a new program. */
+ asmlinkage int
+-sys_execve(const char *fname, char **argv, char **envp, long r13, long mof, long srp,
++sys_execve(const char *fname, char **argv, char **envp, long r13, long mof, long srp, 
+       struct pt_regs *regs)
+ {
+       int error;
+@@ -254,13 +254,13 @@
+       unsigned long usp = rdusp();
+         printk("ERP: %08lx SRP: %08lx  CCS: %08lx USP: %08lx MOF: %08lx\n",
+               regs->erp, regs->srp, regs->ccs, usp, regs->mof);
+-
++      
+       printk(" r0: %08lx  r1: %08lx   r2: %08lx  r3: %08lx\n",
+               regs->r0, regs->r1, regs->r2, regs->r3);
+-
++      
+       printk(" r4: %08lx  r5: %08lx   r6: %08lx  r7: %08lx\n",
+               regs->r4, regs->r5, regs->r6, regs->r7);
+-
++      
+       printk(" r8: %08lx  r9: %08lx  r10: %08lx r11: %08lx\n",
+               regs->r8, regs->r9, regs->r10, regs->r11);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/ptrace.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/ptrace.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/ptrace.c      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/ptrace.c      2006-03-22 10:56:56.000000000 +0100
+@@ -20,7 +20,7 @@
+ #include <asm/processor.h>
+ #include <asm/arch/hwregs/supp_reg.h>
+-/*
++/* 
+  * Determines which bits in CCS the user has access to.
+  * 1 = access, 0 = no access.
+  */
+@@ -84,7 +84,7 @@
+  *
+  * Make sure the single step bit is not set.
+  */
+-void
++void 
+ ptrace_disable(struct task_struct *child)
+ {
+       unsigned long tmp;
+@@ -105,7 +105,7 @@
+       unsigned long __user *datap = (unsigned long __user *)data;
+       switch (request) {
+-              /* Read word at location address. */
++              /* Read word at location address. */ 
+               case PTRACE_PEEKTEXT:
+               case PTRACE_PEEKDATA: {
+                       unsigned long tmp;
+@@ -122,11 +122,11 @@
+                               tmp = *(unsigned long*)addr;
+                       } else {
+                               copied = access_process_vm(child, addr, &tmp, sizeof(tmp), 0);
+-
++                      
+                               if (copied != sizeof(tmp))
+                                       break;
+                       }
+-
++                      
+                       ret = put_user(tmp,datap);
+                       break;
+               }
+@@ -143,18 +143,18 @@
+                       ret = put_user(tmp, datap);
+                       break;
+               }
+-
++              
+               /* Write the word at location address. */
+               case PTRACE_POKETEXT:
+               case PTRACE_POKEDATA:
+                       ret = 0;
+-
++                      
+                       if (access_process_vm(child, addr, &data, sizeof(data), 1) == sizeof(data))
+                               break;
+-
++                      
+                       ret = -EIO;
+                       break;
+-
++ 
+               /* Write the word at location address in the USER area. */
+               case PTRACE_POKEUSR:
+                       ret = -EIO;
+@@ -178,10 +178,10 @@
+               case PTRACE_SYSCALL:
+               case PTRACE_CONT:
+                       ret = -EIO;
+-
++                      
+                       if (!valid_signal(data))
+                               break;
+-
++                      
+                       /* Continue means no single-step. */
+                       put_reg(child, PT_SPC, 0);
+@@ -198,27 +198,27 @@
+                       else {
+                               clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+                       }
+-
++                      
+                       child->exit_code = data;
+-
++                      
+                       /* TODO: make sure any pending breakpoint is killed */
+                       wake_up_process(child);
+                       ret = 0;
+-
++                      
+                       break;
+-
++              
+               /* Make the child exit by sending it a sigkill. */
+               case PTRACE_KILL:
+                       ret = 0;
+-
++                      
+                       if (child->exit_state == EXIT_ZOMBIE)
+                               break;
+-
++                      
+                       child->exit_code = SIGKILL;
+-
++                      
+                       /* Deconfigure single-step and h/w bp. */
+                       ptrace_disable(child);
+-
++                      
+                       /* TODO: make sure any pending breakpoint is killed */
+                       wake_up_process(child);
+                       break;
+@@ -227,7 +227,7 @@
+               case PTRACE_SINGLESTEP: {
+                       unsigned long tmp;
+                       ret = -EIO;
+-
++                      
+                       /* Set up SPC if not set already (in which case we have
+                          no other choice but to trust it). */
+                       if (!get_reg(child, PT_SPC)) {
+@@ -240,7 +240,7 @@
+                       if (!valid_signal(data))
+                               break;
+-
++                      
+                       clear_tsk_thread_flag(child, TIF_SYSCALL_TRACE);
+                       /* TODO: set some clever breakpoint mechanism... */
+@@ -259,15 +259,15 @@
+               case PTRACE_GETREGS: {
+                       int i;
+                       unsigned long tmp;
+-
++                      
+                       for (i = 0; i <= PT_MAX; i++) {
+                               tmp = get_reg(child, i);
+-
++                              
+                               if (put_user(tmp, datap)) {
+                                       ret = -EFAULT;
+                                       goto out_tsk;
+                               }
+-
++                              
+                               datap++;
+                       }
+@@ -279,22 +279,22 @@
+               case PTRACE_SETREGS: {
+                       int i;
+                       unsigned long tmp;
+-
++                      
+                       for (i = 0; i <= PT_MAX; i++) {
+                               if (get_user(tmp, datap)) {
+                                       ret = -EFAULT;
+                                       goto out_tsk;
+                               }
+-
++                              
+                               if (i == PT_CCS) {
+                                       tmp &= CCS_MASK;
+                                       tmp |= get_reg(child, PT_CCS) & ~CCS_MASK;
+                               }
+-
++                              
+                               put_reg(child, i, tmp);
+                               datap++;
+                       }
+-
++                      
+                       ret = 0;
+                       break;
+               }
+@@ -304,6 +304,7 @@
+                       break;
+       }
++out_tsk:
+       return ret;
+ }
+@@ -311,15 +312,15 @@
+ {
+       if (!test_thread_flag(TIF_SYSCALL_TRACE))
+               return;
+-
++      
+       if (!(current->ptrace & PT_PTRACED))
+               return;
+-
++      
+       /* the 0x80 provides a way for the tracing parent to distinguish
+          between a syscall stop and SIGTRAP delivery */
+       ptrace_notify(SIGTRAP | ((current->ptrace & PT_TRACESYSGOOD)
+                                ? 0x80 : 0));
+-
++      
+       /*
+        * This isn't the same as continuing with a signal, but it will do for
+        * normal use.
+@@ -338,7 +339,7 @@
+   int copied;
+   int opsize = 0;
+-  /* Read the opcode at pc (do what PTRACE_PEEKTEXT would do). */
++  /* Read the opcode at pc (do what PTRACE_PEEKTEXT would do). */  
+   copied = access_process_vm(child, pc, &opcode, sizeof(opcode), 0);
+   if (copied != sizeof(opcode))
+     return 0;
+@@ -361,7 +362,7 @@
+                 opsize = 6;
+         break;
+   default:
+-        panic("ERROR: Couldn't find size of opcode 0x%lx at 0x%lx\n",
++        panic("ERROR: Couldn't find size of opcode 0x%lx at 0x%lx\n", 
+               opcode, pc);
+   }
+@@ -378,7 +379,7 @@
+               /* Delay slot bit set. Report as stopped on proper
+                  instruction. */
+               if (spc) {
+-                      /* Rely on SPC if set. FIXME: We might want to check
++                      /* Rely on SPC if set. FIXME: We might want to check 
+                          that EXS indicates we stopped due to a single-step
+                          exception. */
+                       pc = spc;
+@@ -422,7 +423,7 @@
+       register int old_srs;
+ #ifdef CONFIG_ETRAX_KGDB
+-      /* Ignore write, but pretend it was ok if value is 0
++      /* Ignore write, but pretend it was ok if value is 0 
+          (we don't want POKEUSR/SETREGS failing unnessecarily). */
+       return (data == 0) ? ret : -1;
+ #endif
+@@ -431,7 +432,7 @@
+       if (!bp_owner)
+               bp_owner = pid;
+       else if (bp_owner != pid) {
+-              /* Ignore write, but pretend it was ok if value is 0
++              /* Ignore write, but pretend it was ok if value is 0 
+                  (we don't want POKEUSR/SETREGS failing unnessecarily). */
+               return (data == 0) ? ret : -1;
+       }
+@@ -440,7 +441,7 @@
+       SPEC_REG_RD(SPEC_REG_SRS, old_srs);
+       /* Switch to BP bank. */
+       SUPP_BANK_SEL(BANK_BP);
+-
++      
+       switch (regno - PT_BP) {
+       case 0:
+               SUPP_REG_WR(0, data); break;
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/setup.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/setup.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/setup.c       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/setup.c       2006-10-13 14:43:13.000000000 +0200
+@@ -40,12 +40,12 @@
+       {"ETRAX 100LX", 10, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB
+                            | HAS_MMU | HAS_MMU_BUG},
+-
++      
+       {"ETRAX 100LX v2", 11, 8, HAS_ETHERNET100 | HAS_SCSI | HAS_ATA | HAS_USB
+                               | HAS_MMU},
+-
++      
+       {"ETRAX FS", 32, 32, HAS_ETHERNET100 | HAS_ATA | HAS_MMU},
+-
++      
+       {"Unknown", 0, 0, 0}
+ };
+@@ -67,7 +67,7 @@
+ #endif
+       revision = rdvr();
+-
++      
+       for (i = 0; i < entries; i++) {
+               if (cpinfo[i].rev == revision) {
+                       info = &cpinfo[i];
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/signal.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/signal.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/signal.c      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/signal.c      2006-03-22 10:56:56.000000000 +0100
+@@ -50,7 +50,7 @@
+       unsigned char retcode[8];       /* Trampoline code. */
+ };
+-int do_signal(int restart, sigset_t *oldset, struct pt_regs *regs);
++void do_signal(int restart, struct pt_regs *regs);
+ void keep_debug_flags(unsigned long oldccs, unsigned long oldspc,
+                     struct pt_regs *regs);
+ /*
+@@ -61,74 +61,16 @@
+ sys_sigsuspend(old_sigset_t mask, long r11, long r12, long r13, long mof,
+              long srp, struct pt_regs *regs)
+ {
+-      sigset_t saveset;
+-
+       mask &= _BLOCKABLE;
+-
+       spin_lock_irq(&current->sighand->siglock);
+-
+-      saveset = current->blocked;
+-
++      current->saved_sigmask = current->blocked;
+       siginitset(&current->blocked, mask);
+-
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+-
+-      regs->r10 = -EINTR;
+-
+-      while (1) {
+-              current->state = TASK_INTERRUPTIBLE;
+-              schedule();
+-
+-              if (do_signal(0, &saveset, regs)) {
+-                      /*
+-                       * This point is reached twice: once to call
+-                       * the signal handler, then again to return
+-                       * from the sigsuspend system call. When
+-                       * calling the signal handler, R10 hold the
+-                       * signal number as set by do_signal(). The
+-                       * sigsuspend  call will always return with
+-                       * the restored value above; -EINTR.
+-                       */
+-                      return regs->r10;
+-              }
+-      }
+-}
+-
+-/* Define some dummy arguments to be able to reach the regs argument. */
+-int
+-sys_rt_sigsuspend(sigset_t *unewset, size_t sigsetsize, long r12, long r13,
+-                long mof, long srp, struct pt_regs *regs)
+-{
+-      sigset_t saveset;
+-      sigset_t newset;
+-
+-      if (sigsetsize != sizeof(sigset_t))
+-              return -EINVAL;
+-
+-      if (copy_from_user(&newset, unewset, sizeof(newset)))
+-              return -EFAULT;
+-
+-      sigdelsetmask(&newset, ~_BLOCKABLE);
+-      spin_lock_irq(&current->sighand->siglock);
+-
+-      saveset = current->blocked;
+-      current->blocked = newset;
+-
+-      recalc_sigpending();
+-      spin_unlock_irq(&current->sighand->siglock);
+-
+-      regs->r10 = -EINTR;
+-
+-      while (1) {
+-              current->state = TASK_INTERRUPTIBLE;
+-              schedule();
+-
+-              if (do_signal(0, &saveset, regs)) {
+-                      /* See comment in function above. */
+-                      return regs->r10;
+-              }
+-      }
++        current->state = TASK_INTERRUPTIBLE;
++      schedule();
++        set_thread_flag(TIF_RESTORE_SIGMASK);
++        return -ERESTARTNOHAND;
+ }
+ int
+@@ -263,7 +205,7 @@
+       unsigned long oldccs = regs->ccs;
+       frame = (struct rt_signal_frame *) rdusp();
+-
++      
+       /*
+        * Since the signal is stacked on a dword boundary, the frame
+        * should be dword aligned here as well. It it's not, then the
+@@ -285,7 +227,7 @@
+       recalc_sigpending();
+       spin_unlock_irq(&current->sighand->siglock);
+-
++        
+       if (restore_sigcontext(regs, &frame->uc.uc_mcontext))
+               goto badframe;
+@@ -311,7 +253,7 @@
+       err = 0;
+       usp = rdusp();
+-
++      
+       /*
+        * Copy the registers. They are located first in sc, so it's
+        * possible to use sc directly.
+@@ -351,7 +293,7 @@
+  * which performs the syscall sigreturn(), or a provided user-mode
+  * trampoline.
+   */
+-static void
++static int
+ setup_frame(int sig, struct k_sigaction *ka,  sigset_t *set,
+           struct pt_regs * regs)
+ {
+@@ -388,7 +330,7 @@
+               /* Trampoline - the desired return ip is in the signal return page. */
+               return_ip = cris_signal_return_page;
+-              /*
++              /* 
+                * This is movu.w __NR_sigreturn, r9; break 13;
+                *
+                * WE DO NOT USE IT ANY MORE! It's only left here for historical
+@@ -402,7 +344,7 @@
+       if (err)
+               goto give_sigsegv;
+-
++      
+       /*
+        * Set up registers for signal handler.
+        *
+@@ -417,16 +359,17 @@
+       /* Actually move the USP to reflect the stacked frame. */
+       wrusp((unsigned long)frame);
+-      return;
++      return 0;
+ give_sigsegv:
+       if (sig == SIGSEGV)
+               ka->sa.sa_handler = SIG_DFL;
+-
++      
+       force_sig(SIGSEGV, current);
++        return -EFAULT;
+ }
+-static void
++static int
+ setup_rt_frame(int sig, struct k_sigaction *ka, siginfo_t *info,
+              sigset_t *set, struct pt_regs * regs)
+ {
+@@ -441,11 +384,11 @@
+               goto give_sigsegv;
+       /* TODO: what is the current->exec_domain stuff and invmap ? */
+-
++      
+       err |= __put_user(&frame->info, &frame->pinfo);
+       err |= __put_user(&frame->uc, &frame->puc);
+       err |= copy_siginfo_to_user(&frame->info, info);
+-
++      
+       if (err)
+               goto give_sigsegv;
+@@ -467,7 +410,7 @@
+               /* Trampoline - the desired return ip is in the signal return page. */
+               return_ip = cris_signal_return_page + 6;
+-              /*
++              /* 
+                * This is movu.w __NR_rt_sigreturn, r9; break 13;
+                *
+                * WE DO NOT USE IT ANY MORE! It's only left here for historical
+@@ -478,7 +421,7 @@
+               err |= __put_user(__NR_rt_sigreturn,
+                                 (short __user*)(frame->retcode+2));
+-
++              
+               err |= __put_user(0xe93d, (short __user*)(frame->retcode+4));
+       }
+@@ -503,21 +446,24 @@
+       /* Actually move the usp to reflect the stacked frame. */
+       wrusp((unsigned long)frame);
+-      return;
++      return 0;
+ give_sigsegv:
+       if (sig == SIGSEGV)
+               ka->sa.sa_handler = SIG_DFL;
+-
++      
+       force_sig(SIGSEGV, current);
++        return -EFAULT;
+ }
+ /* Invoke a singal handler to, well, handle the signal. */
+-static inline void
++static inline int
+ handle_signal(int canrestart, unsigned long sig,
+             siginfo_t *info, struct k_sigaction *ka,
+               sigset_t *oldset, struct pt_regs * regs)
+ {
++      int ret;
++
+       /* Check if this got called from a system call. */
+       if (canrestart) {
+               /* If so, check system call restarting. */
+@@ -561,19 +507,23 @@
+       /* Set up the stack frame. */
+       if (ka->sa.sa_flags & SA_SIGINFO)
+-              setup_rt_frame(sig, ka, info, oldset, regs);
++              ret = setup_rt_frame(sig, ka, info, oldset, regs);
+       else
+-              setup_frame(sig, ka, oldset, regs);
++              ret = setup_frame(sig, ka, oldset, regs);
+       if (ka->sa.sa_flags & SA_ONESHOT)
+               ka->sa.sa_handler = SIG_DFL;
+-      spin_lock_irq(&current->sighand->siglock);
+-      sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
+-      if (!(ka->sa.sa_flags & SA_NODEFER))
+-              sigaddset(&current->blocked,sig);
+-      recalc_sigpending();
+-      spin_unlock_irq(&current->sighand->siglock);
++      if (ret == 0) {
++              spin_lock_irq(&current->sighand->siglock);
++              sigorsets(&current->blocked,&current->blocked,&ka->sa.sa_mask);
++              if (!(ka->sa.sa_flags & SA_NODEFER))
++                      sigaddset(&current->blocked,sig);
++              recalc_sigpending();
++              spin_unlock_irq(&current->sighand->siglock);
++      }
++
++      return ret;
+ }
+ /*
+@@ -587,12 +537,13 @@
+  * we can use user_mode(regs) to see if we came directly from kernel or user
+  * mode below.
+  */
+-int
+-do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs)
++void
++do_signal(int canrestart, struct pt_regs *regs)
+ {
+       int signr;
+       siginfo_t info;
+         struct k_sigaction ka;
++      sigset_t *oldset;
+       /*
+        * The common case should go fast, which is why this point is
+@@ -600,17 +551,27 @@
+        * without doing anything.
+        */
+       if (!user_mode(regs))
+-              return 1;
++              return;
+-      if (!oldset)
++        if (test_thread_flag(TIF_RESTORE_SIGMASK))
++              oldset = &current->saved_sigmask;
++      else
+               oldset = &current->blocked;
+       signr = get_signal_to_deliver(&info, &ka, regs, NULL);
+-
++      
+       if (signr > 0) {
+-              /* Deliver the signal. */
+-              handle_signal(canrestart, signr, &info, &ka, oldset, regs);
+-              return 1;
++              /* Whee!  Actually deliver the signal.  */
++              if (handle_signal(canrestart, signr, &info, &ka, oldset, regs)) {
++                      /* a signal was successfully delivered; the saved
++                       * sigmask will have been stored in the signal frame,
++                       * and will be restored by sigreturn, so we can simply
++                       * clear the TIF_RESTORE_SIGMASK flag */
++                      if (test_thread_flag(TIF_RESTORE_SIGMASK))
++                              clear_thread_flag(TIF_RESTORE_SIGMASK);
++              }
++
++              return;
+       }
+       /* Got here from a system call? */
+@@ -621,14 +582,19 @@
+                   regs->r10 == -ERESTARTNOINTR) {
+                       RESTART_CRIS_SYS(regs);
+               }
+-
++              
+               if (regs->r10 == -ERESTART_RESTARTBLOCK){
+                       regs->r10 = __NR_restart_syscall;
+                       regs->erp -= 2;
+               }
+       }
+-
+-      return 0;
++      
++      /* if there's no signal to deliver, we just put the saved sigmask
++       * back */
++      if (test_thread_flag(TIF_RESTORE_SIGMASK)) {
++              clear_thread_flag(TIF_RESTORE_SIGMASK);
++              sigprocmask(SIG_SETMASK, &current->saved_sigmask, NULL);
++      }
+ }
+ asmlinkage void
+@@ -651,7 +617,7 @@
+       sys_kill(ti->task->pid, sig);
+ }
+-void
++void 
+ keep_debug_flags(unsigned long oldccs, unsigned long oldspc,
+                struct pt_regs *regs)
+ {
+@@ -666,7 +632,7 @@
+               regs->ccs |= (1 << (S_CCS_BITNR + CCS_SHIFT));
+               /* Assume the SPC is valid and interesting. */
+               regs->spc = oldspc;
+-
++              
+       } else if (oldccs & (1 << (S_CCS_BITNR + CCS_SHIFT))) {
+               /* If a h/w bp was set in the signal handler we need
+                  to keep the S flag. */
+@@ -679,7 +645,7 @@
+                  have forgotten all about it. */
+               regs->spc = 0;
+               regs->ccs &= ~(1 << (S_CCS_BITNR + CCS_SHIFT));
+-      }
++      }               
+ }
+ /* Set up the trampolines on the signal return page. */
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/smp.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/smp.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/smp.c 2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/smp.c 2007-01-09 10:29:19.000000000 +0100
+@@ -20,6 +20,7 @@
+ #define IPI_SCHEDULE 1
+ #define IPI_CALL 2
+ #define IPI_FLUSH_TLB 4
++#define IPI_BOOT 8
+ #define FLUSH_ALL (void*)0xffffffff
+@@ -30,6 +31,8 @@
+ cpumask_t cpu_online_map = CPU_MASK_NONE;
+ EXPORT_SYMBOL(cpu_online_map);
+ cpumask_t phys_cpu_present_map = CPU_MASK_NONE;
++cpumask_t cpu_possible_map;
++EXPORT_SYMBOL(cpu_possible_map);
+ EXPORT_SYMBOL(phys_cpu_present_map);
+ /* Variables used during SMP boot */
+@@ -55,7 +58,7 @@
+ extern int setup_irq(int, struct irqaction *);
+ /* Mode registers */
+-static unsigned long irq_regs[NR_CPUS] =
++static unsigned long irq_regs[NR_CPUS] = 
+ {
+   regi_irq,
+   regi_irq2
+@@ -97,6 +100,7 @@
+       cpu_set(0, cpu_online_map);
+       cpu_set(0, phys_cpu_present_map);
++        cpu_set(0, cpu_possible_map);
+ }
+ void __init smp_cpus_done(unsigned int max_cpus)
+@@ -109,6 +113,7 @@
+ {
+       unsigned timeout;
+       struct task_struct *idle;
++      cpumask_t cpu_mask = CPU_MASK_NONE;
+       idle = fork_idle(cpuid);
+       if (IS_ERR(idle))
+@@ -120,6 +125,12 @@
+       smp_init_current_idle_thread = task_thread_info(idle);
+       cpu_now_booting = cpuid;
++        /* Kick it */
++      cpu_set(cpuid, cpu_online_map);
++      cpu_set(cpuid, cpu_mask);
++      send_ipi(IPI_BOOT, 0, cpu_mask);
++      cpu_clear(cpuid, cpu_online_map);
++
+       /* Wait for CPU to come online */
+       for (timeout = 0; timeout < 10000; timeout++) {
+               if(cpu_online(cpuid)) {
+@@ -142,7 +153,7 @@
+  * specific stuff such as the local timer and the MMU. */
+ void __init smp_callin(void)
+ {
+-      extern void cpu_idle(void);
++      extern void cpu_idle(void);     
+       int cpu = cpu_now_booting;
+       reg_intr_vect_rw_mask vect_mask = {0};
+@@ -190,8 +201,8 @@
+ /* cache_decay_ticks is used by the scheduler to decide if a process
+  * is "hot" on one CPU. A higher value means a higher penalty to move
+- * a process to another CPU. Our cache is rather small so we report
+- * 1 tick.
++ * a process to another CPU. Our cache is rather small so we report 
++ * 1 tick. 
+  */
+ unsigned long cache_decay_ticks = 1;
+@@ -205,14 +216,14 @@
+ {
+       cpumask_t cpu_mask = CPU_MASK_NONE;
+       cpu_set(cpu, cpu_mask);
+-      send_ipi(IPI_SCHEDULE, 0, cpu_mask);
++      send_ipi(IPI_SCHEDULE, 0, cpu_mask);    
+ }
+ /* TLB flushing
+  *
+  * Flush needs to be done on the local CPU and on any other CPU that
+  * may have the same mapping. The mm->cpu_vm_mask is used to keep track
+- * of which CPUs that a specific process has been executed on.
++ * of which CPUs that a specific process has been executed on. 
+  */
+ void flush_tlb_common(struct mm_struct* mm, struct vm_area_struct* vma, unsigned long addr)
+ {
+@@ -244,7 +255,7 @@
+       cpu_set(smp_processor_id(), mm->cpu_vm_mask);
+ }
+-void flush_tlb_page(struct vm_area_struct *vma,
++void flush_tlb_page(struct vm_area_struct *vma, 
+                          unsigned long addr)
+ {
+       __flush_tlb_page(vma, addr);
+@@ -252,8 +263,8 @@
+ }
+ /* Inter processor interrupts
+- *
+- * The IPIs are used for:
++ * 
++ * The IPIs are used for: 
+  *   * Force a schedule on a CPU
+  *   * FLush TLB on other CPUs
+  *   * Call a function on other CPUs
+@@ -341,7 +352,7 @@
+                    else if (flush_vma == FLUSH_ALL)
+                       __flush_tlb_mm(flush_mm);
+                    else
+-                      __flush_tlb_page(flush_vma, flush_addr);
++                      __flush_tlb_page(flush_vma, flush_addr);  
+       }
+       ipi.vector = 0;
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/time.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/time.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/time.c        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/time.c        2007-01-09 10:29:19.000000000 +0100
+@@ -1,4 +1,4 @@
+-/* $Id: time.c,v 1.19 2005/04/29 05:40:09 starvik Exp $
++/* $Id: time.c,v 1.25 2007/01/09 09:29:19 starvik Exp $
+  *
+  *  linux/arch/cris/arch-v32/kernel/time.c
+  *
+@@ -14,12 +14,14 @@
+ #include <linux/sched.h>
+ #include <linux/init.h>
+ #include <linux/threads.h>
++#include <linux/cpufreq.h>
+ #include <asm/types.h>
+ #include <asm/signal.h>
+ #include <asm/io.h>
+ #include <asm/delay.h>
+ #include <asm/rtc.h>
+ #include <asm/irq.h>
++#include <asm/irq_regs.h>
+ #include <asm/arch/hwregs/reg_map.h>
+ #include <asm/arch/hwregs/reg_rdwr.h>
+@@ -31,7 +33,7 @@
+ #define ETRAX_WD_HZ       763 /* watchdog counts at 763 Hz */
+ #define ETRAX_WD_CNT      ((2*ETRAX_WD_HZ)/HZ + 1) /* Number of 763 counts before watchdog bites */
+-unsigned long timer_regs[NR_CPUS] =
++unsigned long timer_regs[NR_CPUS] = 
+ {
+   regi_timer,
+ #ifdef CONFIG_SMP
+@@ -44,6 +46,15 @@
+ extern int setup_irq(int, struct irqaction *);
+ extern int have_rtc;
++#ifdef CONFIG_CPU_FREQ
++static int
++cris_time_freq_notifier(struct notifier_block *nb, unsigned long val, void *data);
++
++static struct notifier_block cris_time_freq_notifier_block = {
++        .notifier_call  = cris_time_freq_notifier
++};
++#endif
++
+ unsigned long get_ns_in_jiffie(void)
+ {
+       reg_timer_r_tmr0_data data;
+@@ -63,7 +74,7 @@
+       static unsigned long jiffies_p = 0;
+       /*
+-       * cache volatile jiffies temporarily; we have IRQs turned off.
++       * cache volatile jiffies temporarily; we have IRQs turned off. 
+        */
+       unsigned long jiffies_t;
+@@ -82,7 +93,7 @@
+        */
+       if( jiffies_t == jiffies_p ) {
+               if( count > count_p ) {
+-                      /* Timer wrapped, use new count and prescale
++                      /* Timer wrapped, use new count and prescale 
+                        * increase the time corresponding to one jiffie
+                        */
+                       usec_count = 1000000/HZ;
+@@ -101,7 +112,7 @@
+  * The watchdog timer is an 8-bit timer with a configurable start value.
+  * Once started the whatchdog counts downwards with a frequency of 763 Hz
+  * (100/131072 MHz). When the watchdog counts down to 1, it generates an
+- * NMI (Non Maskable Interrupt), and when it counts down to 0, it resets the
++ * NMI (Non Maskable Interrupt), and when it counts down to 0, it resets the 
+  * chip.
+  */
+ /* This gives us 1.3 ms to do something useful when the NMI comes */
+@@ -124,7 +135,7 @@
+ {
+ #if defined(CONFIG_ETRAX_WATCHDOG)
+       reg_timer_rw_wd_ctrl wd_ctrl = { 0 };
+-
++      
+       /* only keep watchdog happy as long as we have memory left! */
+       if(nr_free_pages() > WATCHDOG_MIN_FREE_PAGES) {
+               /* reset the watchdog with the inverse of the old key */
+@@ -139,7 +150,7 @@
+ /* stop the watchdog - we still need the correct key */
+-void
++void 
+ stop_watchdog(void)
+ {
+ #if defined(CONFIG_ETRAX_WATCHDOG)
+@@ -149,7 +160,7 @@
+       wd_ctrl.cmd = regk_timer_stop;
+       wd_ctrl.key = watchdog_key;
+       REG_WR(timer, regi_timer, rw_wd_ctrl, wd_ctrl);
+-#endif
++#endif        
+ }
+ extern void show_registers(struct pt_regs *regs);
+@@ -160,7 +171,8 @@
+ #if defined(CONFIG_ETRAX_WATCHDOG)
+       extern int cause_of_death;
+-      raw_printk("Watchdog bite\n");
++      oops_in_progress = 1;
++      printk("Watchdog bite\n");
+       /* Check if forced restart or unexpected watchdog */
+       if (cause_of_death == 0xbedead) {
+@@ -169,8 +181,9 @@
+       /* Unexpected watchdog, stop the watchdog and dump registers*/
+       stop_watchdog();
+-      raw_printk("Oops: bitten by watchdog\n");
+-        show_registers(regs);
++      printk("Oops: bitten by watchdog\n");
++      show_registers(regs);
++      oops_in_progress = 0;
+ #ifndef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+       reset_watchdog();
+ #endif
+@@ -191,8 +204,9 @@
+ extern void cris_do_profile(struct pt_regs *regs);
+ static inline irqreturn_t
+-timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
++timer_interrupt(int irq, void *dev_id)
+ {
++      struct pt_regs* regs = get_irq_regs();
+       int cpu = smp_processor_id();
+       reg_timer_r_masked_intr masked_intr;
+       reg_timer_rw_ack_intr ack_intr = { 0 };
+@@ -226,7 +240,7 @@
+        * CMOS clock accordingly every ~11 minutes. Set_rtc_mmss() has to be
+        * called as close as possible to 500 ms before the new second starts.
+        *
+-       * The division here is not time critical since it will run once in
++       * The division here is not time critical since it will run once in 
+        * 11 minutes
+        */
+       if ((time_status & STA_UNSYNC) == 0 &&
+@@ -246,7 +260,7 @@
+  */
+ static struct irqaction irq_timer  = {
+-      .mask = timer_interrupt,
++      .handler = timer_interrupt,
+       .flags = IRQF_SHARED | IRQF_DISABLED,
+       .mask = CPU_MASK_NONE,
+       .name = "timer"
+@@ -262,7 +276,7 @@
+       /* Setup the etrax timers
+        * Base frequency is 100MHz, divider 1000000 -> 100 HZ
+-       * We use timer0, so timer1 is free.
++       * We use timer0, so timer1 is free. 
+        * The trig timer is used by the fasttimer API if enabled.
+        */
+@@ -284,11 +298,11 @@
+ {
+       reg_intr_vect_rw_mask intr_mask;
+-      /* probe for the RTC and read it if it exists
+-       * Before the RTC can be probed the loops_per_usec variable needs
+-       * to be initialized to make usleep work. A better value for
+-       * loops_per_usec is calculated by the kernel later once the
+-       * clock has started.
++      /* probe for the RTC and read it if it exists 
++       * Before the RTC can be probed the loops_per_usec variable needs 
++       * to be initialized to make usleep work. A better value for 
++       * loops_per_usec is calculated by the kernel later once the 
++       * clock has started.  
+        */
+       loops_per_usec = 50;
+@@ -297,7 +311,7 @@
+               xtime.tv_sec = 0;
+               xtime.tv_nsec = 0;
+               have_rtc = 0;
+-      } else {
++      } else {                
+               /* get the current time */
+               have_rtc = 1;
+               update_xtime_from_cmos();
+@@ -316,9 +330,9 @@
+       intr_mask = REG_RD(intr_vect, regi_irq, rw_mask);
+       intr_mask.timer = 1;
+       REG_WR(intr_vect, regi_irq, rw_mask, intr_mask);
+-
++  
+       /* now actually register the timer irq handler that calls timer_interrupt() */
+-
++      
+       setup_irq(TIMER_INTR_VECT, &irq_timer);
+       /* enable watchdog if we should use one */
+@@ -330,10 +344,7 @@
+       /* If we use the hardware watchdog, we want to trap it as an NMI
+          and dump registers before it resets us.  For this to happen, we
+          must set the "m" NMI enable flag (which once set, is unset only
+-         when an NMI is taken).
+-
+-         The same goes for the external NMI, but that doesn't have any
+-         driver or infrastructure support yet.  */
++         when an NMI is taken). */
+         {
+           unsigned long flags;
+           local_save_flags(flags);
+@@ -341,4 +352,27 @@
+           local_irq_restore(flags);
+         }
+ #endif
++
++#ifdef CONFIG_CPU_FREQ
++        cpufreq_register_notifier(&cris_time_freq_notifier_block,
++                                  CPUFREQ_TRANSITION_NOTIFIER);
++#endif
++}
++
++#ifdef CONFIG_CPU_FREQ
++static int
++cris_time_freq_notifier(struct notifier_block *nb, unsigned long val, void *data)
++{
++      struct cpufreq_freqs *freqs = data;
++      if (val == CPUFREQ_POSTCHANGE) {
++              reg_timer_r_tmr0_data data;
++              reg_timer_rw_tmr0_div div = (freqs->new * 500) / HZ;
++              do
++              {
++                      data = REG_RD(timer, timer_regs[freqs->cpu], r_tmr0_data);
++              } while (data > 20);
++              REG_WR(timer, timer_regs[freqs->cpu], rw_tmr0_div, div);
++      }
++      return 0;
+ }
++#endif
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/traps.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/traps.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/traps.c       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/traps.c       2006-12-11 14:04:24.000000000 +0100
+@@ -1,50 +1,43 @@
+ /*
+- * Copyright (C) 2003, Axis Communications AB.
++ * Copyright (C) 2003-2006, Axis Communications AB.
+  */
+ #include <linux/ptrace.h>
+ #include <asm/uaccess.h>
+-
+ #include <asm/arch/hwregs/supp_reg.h>
+-
+-extern void reset_watchdog(void);
+-extern void stop_watchdog(void);
+-
+-extern int raw_printk(const char *fmt, ...);
++#include <asm/arch/hwregs/intr_vect_defs.h>
+ void
+ show_registers(struct pt_regs *regs)
+ {
+       /*
+        * It's possible to use either the USP register or current->thread.usp.
+-       * USP might not correspond to the current proccess for all cases this
++       * USP might not correspond to the current process for all cases this
+        * function is called, and current->thread.usp isn't up to date for the
+-       * current proccess. Experience shows that using USP is the way to go.
++       * current process. Experience shows that using USP is the way to go.
+        */
+-      unsigned long usp;
++      unsigned long usp = rdusp();
+       unsigned long d_mmu_cause;
+       unsigned long i_mmu_cause;
+-      usp = rdusp();
++      printk("CPU: %d\n", smp_processor_id());
+-      raw_printk("CPU: %d\n", smp_processor_id());
++      printk("ERP: %08lx SRP: %08lx  CCS: %08lx USP: %08lx MOF: %08lx\n",
++             regs->erp, regs->srp, regs->ccs, usp, regs->mof);
+-      raw_printk("ERP: %08lx SRP: %08lx  CCS: %08lx USP: %08lx MOF: %08lx\n",
+-              regs->erp, regs->srp, regs->ccs, usp, regs->mof);
++      printk(" r0: %08lx  r1: %08lx   r2: %08lx  r3: %08lx\n",
++             regs->r0, regs->r1, regs->r2, regs->r3);
+-      raw_printk(" r0: %08lx  r1: %08lx   r2: %08lx  r3: %08lx\n",
+-              regs->r0, regs->r1, regs->r2, regs->r3);
++      printk(" r4: %08lx  r5: %08lx   r6: %08lx  r7: %08lx\n",
++             regs->r4, regs->r5, regs->r6, regs->r7);
+-      raw_printk(" r4: %08lx  r5: %08lx   r6: %08lx  r7: %08lx\n",
+-              regs->r4, regs->r5, regs->r6, regs->r7);
++      printk(" r8: %08lx  r9: %08lx  r10: %08lx r11: %08lx\n",
++             regs->r8, regs->r9, regs->r10, regs->r11);
+-      raw_printk(" r8: %08lx  r9: %08lx  r10: %08lx r11: %08lx\n",
+-              regs->r8, regs->r9, regs->r10, regs->r11);
++      printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n",
++             regs->r12, regs->r13, regs->orig_r10, regs->acr);
+-      raw_printk("r12: %08lx r13: %08lx oR10: %08lx acr: %08lx\n",
+-              regs->r12, regs->r13, regs->orig_r10, regs->acr);
+-
+-      raw_printk("sp: %08lx\n", regs);
++      printk(" sp: %08lx\n", (unsigned long)regs);
+       SUPP_BANK_SEL(BANK_IM);
+       SUPP_REG_RD(RW_MM_CAUSE, i_mmu_cause);
+@@ -52,18 +45,20 @@
+       SUPP_BANK_SEL(BANK_DM);
+       SUPP_REG_RD(RW_MM_CAUSE, d_mmu_cause);
+-      raw_printk("       Data MMU Cause: %08lx\n", d_mmu_cause);
+-      raw_printk("Instruction MMU Cause: %08lx\n", i_mmu_cause);
++      printk("       Data MMU Cause: %08lx\n", d_mmu_cause);
++      printk("Instruction MMU Cause: %08lx\n", i_mmu_cause);
+-      raw_printk("Process %s (pid: %d, stackpage: %08lx)\n",
+-              current->comm, current->pid, (unsigned long) current);
++      printk("Process %s (pid: %d, stackpage=%08lx)\n",
++             current->comm, current->pid, (unsigned long)current);
+-      /* Show additional info if in kernel-mode. */
++      /*
++       * When in-kernel, we also print out the stack and code at the
++       * time of the fault..
++       */
+       if (!user_mode(regs)) {
+               int i;
+-              unsigned char c;
+-              show_stack(NULL, (unsigned long *) usp);
++              show_stack(NULL, (unsigned long *)usp);
+               /*
+                * If the previous stack-dump wasn't a kernel one, dump the
+@@ -72,7 +67,7 @@
+               if (usp != 0)
+                       show_stack(NULL, NULL);
+-              raw_printk("\nCode: ");
++              printk("\nCode: ");
+               if (regs->erp < PAGE_OFFSET)
+                       goto bad_value;
+@@ -84,76 +79,65 @@
+                * instruction decoding should be in sync at the interesting
+                * point, but small enough to fit on a row. The regs->erp
+                * location is pointed out in a ksymoops-friendly way by
+-               * wrapping the byte for that address in parenthesis.
++               * wrapping the byte for that address in parenthesises.
+                */
+               for (i = -12; i < 12; i++) {
+-                      if (__get_user(c, &((unsigned char *) regs->erp)[i])) {
++                      unsigned char c;
++
++                      if (__get_user(c, &((unsigned char *)regs->erp)[i])) {
+ bad_value:
+-                              raw_printk(" Bad IP value.");
++                              printk(" Bad IP value.");
+                               break;
+                       }
+                       if (i == 0)
+-                              raw_printk("(%02x) ", c);
++                              printk("(%02x) ", c);
+                       else
+-                              raw_printk("%02x ", c);
++                              printk("%02x ", c);
+               }
+-
+-              raw_printk("\n");
++              printk("\n");
+       }
+ }
+-/*
+- * This gets called from entry.S when the watchdog has bitten. Show something
+- * similiar to an Oops dump, and if the kernel if configured to be a nice doggy;
+- * halt instead of reboot.
+- */
+ void
+-watchdog_bite_hook(struct pt_regs *regs)
++arch_enable_nmi(void)
+ {
+-#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+-      local_irq_disable();
+-      stop_watchdog();
+-      show_registers(regs);
+-
+-      while (1)
+-              ; /* Do nothing. */
+-#else
+-      show_registers(regs);
+-#endif
++      unsigned long flags;
++
++      local_save_flags(flags);
++      flags |= (1 << 30); /* NMI M flag is at bit 30 */
++      local_irq_restore(flags);
+ }
+-/* This is normally the Oops function. */
+-void
+-die_if_kernel(const char *str, struct pt_regs *regs, long err)
++extern void (*nmi_handler)(struct pt_regs*);
++void handle_nmi(struct pt_regs* regs)
+ {
+-      if (user_mode(regs))
+-              return;
++  reg_intr_vect_r_nmi r;
+-#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+-      /*
+-       * This printout might take too long and could trigger
+-       * the watchdog normally. If NICE_DOGGY is set, simply
+-       * stop the watchdog during the printout.
+-       */
+-      stop_watchdog();
+-#endif
+-
+-      raw_printk("%s: %04lx\n", str, err & 0xffff);
++  if (nmi_handler)
++    nmi_handler(regs);
+-      show_registers(regs);
+-
+-#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+-      reset_watchdog();
+-#endif
+-
+-      do_exit(SIGSEGV);
++  /* Wait until nmi is no longer active. */
++  do {
++        r = REG_RD(intr_vect, regi_irq, r_nmi);
++  } while (r.ext == regk_intr_vect_on);
+ }
+-void arch_enable_nmi(void)
++#ifdef CONFIG_DEBUG_BUGVERBOSE
++void
++handle_BUG(struct pt_regs *regs)
+ {
+-      unsigned long flags;
+-      local_save_flags(flags);
+-      flags |= (1<<30); /* NMI M flag is at bit 30 */
+-      local_irq_restore(flags);
++      struct bug_frame f;
++      unsigned char c;
++      unsigned long erp = regs->erp;
++
++      if (__copy_from_user(&f, (const void __user *)(erp - 8), sizeof f))
++              return;
++      if (f.prefix != BUG_PREFIX || f.magic != BUG_MAGIC)
++              return;
++      if (__get_user(c, f.filename))
++              f.filename = "<bad filename>";
++
++      printk("kernel BUG at %s:%d!\n", f.filename, f.line);
+ }
++#endif
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/vcs_hook.c linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/vcs_hook.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/vcs_hook.c    2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/vcs_hook.c    1970-01-01 01:00:00.000000000 +0100
+@@ -1,96 +0,0 @@
+-// $Id: vcs_hook.c,v 1.2 2003/08/12 12:01:06 starvik Exp $
+-//
+-// Call simulator hook. This is the part running in the
+-// simulated program.
+-//
+-
+-#include "vcs_hook.h"
+-#include <stdarg.h>
+-#include <asm/arch-v32/hwregs/reg_map.h>
+-#include <asm/arch-v32/hwregs/intr_vect_defs.h>
+-
+-#define HOOK_TRIG_ADDR     0xb7000000   /* hook cvlog model reg address */
+-#define HOOK_MEM_BASE_ADDR 0xa0000000   /* csp4 (shared mem) base addr */
+-
+-#define HOOK_DATA(offset) ((unsigned*) HOOK_MEM_BASE_ADDR)[offset]
+-#define VHOOK_DATA(offset) ((volatile unsigned*) HOOK_MEM_BASE_ADDR)[offset]
+-#define HOOK_TRIG(funcid) do { *((unsigned *) HOOK_TRIG_ADDR) = funcid; } while(0)
+-#define HOOK_DATA_BYTE(offset) ((unsigned char*) HOOK_MEM_BASE_ADDR)[offset]
+-
+-
+-// ------------------------------------------------------------------ hook_call
+-int hook_call( unsigned id, unsigned pcnt, ...) {
+-  va_list ap;
+-  unsigned i;
+-  unsigned ret;
+-#ifdef USING_SOS
+-  PREEMPT_OFF_SAVE();
+-#endif
+-
+-  // pass parameters
+-  HOOK_DATA(0) = id;
+-
+-  /* Have to make hook_print_str a special case since we call with a
+-     parameter of byte type. Should perhaps be a separate
+-     hook_call. */
+-
+-  if (id == hook_print_str) {
+-    int i;
+-    char *str;
+-
+-    HOOK_DATA(1) = pcnt;
+-
+-    va_start(ap, pcnt);
+-    str = (char*)va_arg(ap,unsigned);
+-
+-    for (i=0; i!=pcnt; i++) {
+-      HOOK_DATA_BYTE(8+i) = str[i];
+-    }
+-    HOOK_DATA_BYTE(8+i) = 0;  /* null byte */
+-  }
+-  else {
+-    va_start(ap, pcnt);
+-    for( i = 1; i <= pcnt; i++ ) HOOK_DATA(i) = va_arg(ap,unsigned);
+-    va_end(ap);
+-  }
+-
+-  // read from mem to make sure data has propagated to memory before trigging
+-  *((volatile unsigned*) HOOK_MEM_BASE_ADDR);
+-
+-  // trigger hook
+-  HOOK_TRIG(id);
+-
+-  // wait for call to finish
+-  while( VHOOK_DATA(0) > 0 ) {}
+-
+-  // extract return value
+-
+-  ret = VHOOK_DATA(1);
+-
+-#ifdef USING_SOS
+-  PREEMPT_RESTORE();
+-#endif
+-  return ret;
+-}
+-
+-unsigned
+-hook_buf(unsigned i)
+-{
+-  return (HOOK_DATA(i));
+-}
+-
+-void print_str( const char *str ) {
+-  int i;
+-  for (i=1; str[i]; i++);         /* find null at end of string */
+-  hook_call(hook_print_str, i, str);
+-}
+-
+-// --------------------------------------------------------------- CPU_KICK_DOG
+-void CPU_KICK_DOG(void) {
+-  (void) hook_call( hook_kick_dog, 0 );
+-}
+-
+-// ------------------------------------------------------- CPU_WATCHDOG_TIMEOUT
+-void CPU_WATCHDOG_TIMEOUT( unsigned t ) {
+-  (void) hook_call( hook_dog_timeout, 1, t );
+-}
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/kernel/vcs_hook.h linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/vcs_hook.h
+--- linux-2.6.19.2.old/arch/cris/arch-v32/kernel/vcs_hook.h    2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/kernel/vcs_hook.h    1970-01-01 01:00:00.000000000 +0100
+@@ -1,42 +0,0 @@
+-// $Id: vcs_hook.h,v 1.1 2003/08/12 12:01:06 starvik Exp $
+-//
+-// Call simulator hook functions
+-
+-#ifndef HOOK_H
+-#define HOOK_H
+-
+-int hook_call( unsigned id, unsigned pcnt, ...);
+-
+-enum hook_ids {
+-  hook_debug_on = 1,
+-  hook_debug_off,
+-  hook_stop_sim_ok,
+-  hook_stop_sim_fail,
+-  hook_alloc_shared,
+-  hook_ptr_shared,
+-  hook_free_shared,
+-  hook_file2shared,
+-  hook_cmp_shared,
+-  hook_print_params,
+-  hook_sim_time,
+-  hook_stop_sim,
+-  hook_kick_dog,
+-  hook_dog_timeout,
+-  hook_rand,
+-  hook_srand,
+-  hook_rand_range,
+-  hook_print_str,
+-  hook_print_hex,
+-  hook_cmp_offset_shared,
+-  hook_fill_random_shared,
+-  hook_alloc_random_data,
+-  hook_calloc_random_data,
+-  hook_print_int,
+-  hook_print_uint,
+-  hook_fputc,
+-  hook_init_fd,
+-  hook_sbrk
+-
+-};
+-
+-#endif
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/Makefile linux-2.6.19.2.dev/arch/cris/arch-v32/lib/Makefile
+--- linux-2.6.19.2.old/arch/cris/arch-v32/lib/Makefile 2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/Makefile 2006-10-11 19:29:20.000000000 +0200
+@@ -2,5 +2,5 @@
+ # Makefile for Etrax-specific library files..
+ #
+-lib-y  = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o spinlock.o
++lib-y  = checksum.o checksumcopy.o string.o usercopy.o memset.o csumcpfruser.o spinlock.o delay.o
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/checksum.S linux-2.6.19.2.dev/arch/cris/arch-v32/lib/checksum.S
+--- linux-2.6.19.2.old/arch/cris/arch-v32/lib/checksum.S       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/checksum.S       2005-08-15 15:53:12.000000000 +0200
+@@ -7,15 +7,15 @@
+       .globl  csum_partial
+ csum_partial:
+-
++      
+       ;; r10 - src
+       ;; r11 - length
+       ;; r12 - checksum
+       ;; check for breakeven length between movem and normal word looping versions
+-      ;; we also do _NOT_ want to compute a checksum over more than the
++      ;; we also do _NOT_ want to compute a checksum over more than the 
+       ;; actual length when length < 40
+-
++      
+       cmpu.w  80,$r11
+       blo     _word_loop
+       nop
+@@ -24,17 +24,17 @@
+       ;; this overhead is why we have a check above for breakeven length
+       ;; only r0 - r8 have to be saved, the other ones are clobber-able
+       ;; according to the ABI
+-
++      
+       subq    9*4,$sp
+       subq    10*4,$r11       ; update length for the first loop
+       movem   $r8,[$sp]
+-
++      
+       ;; do a movem checksum
+ _mloop:       movem   [$r10+],$r9     ; read 10 longwords
+       ;; perform dword checksumming on the 10 longwords
+-
++      
+       add.d   $r0,$r12
+       addc    $r1,$r12
+       addc    $r2,$r12
+@@ -48,9 +48,8 @@
+       ;; fold the carry into the checksum, to avoid having to loop the carry
+       ;; back into the top
+-
++      
+       addc    0,$r12
+-      addc    0,$r12          ; do it again, since we might have generated a carry
+       subq    10*4,$r11
+       bge     _mloop
+@@ -68,34 +67,30 @@
+       ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below.
+       ;; r9 and r13 can be used as temporaries.
+-
++      
+       moveq   -1,$r9          ; put 0xffff in r9, faster than move.d 0xffff,r9
+       lsrq    16,$r9
+-
++      
+       move.d  $r12,$r13
+       lsrq    16,$r13         ; r13 = checksum >> 16
+       and.d   $r9,$r12                ; checksum = checksum & 0xffff
+       add.d   $r13,$r12               ; checksum += r13
+-      move.d  $r12,$r13               ; do the same again, maybe we got a carry last add
+-      lsrq    16,$r13
+-      and.d   $r9,$r12
+-      add.d   $r13,$r12
+ _no_fold:
+       cmpq    2,$r11
+       blt     _no_words
+       nop
+-
++      
+       ;; checksum the rest of the words
+-
++      
+       subq    2,$r11
+-
++      
+ _wloop:       subq    2,$r11
+       bge     _wloop
+       addu.w  [$r10+],$r12
+-
++      
+       addq    2,$r11
+-
++              
+ _no_words:
+       ;; see if we have one odd byte more
+       cmpq    1,$r11
+@@ -104,7 +99,7 @@
+       ret
+       move.d  $r12,$r10
+-_do_byte:
++_do_byte:     
+       ;; copy and checksum the last byte
+       addu.b  [$r10],$r12
+       ret
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/checksumcopy.S linux-2.6.19.2.dev/arch/cris/arch-v32/lib/checksumcopy.S
+--- linux-2.6.19.2.old/arch/cris/arch-v32/lib/checksumcopy.S   2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/checksumcopy.S   2005-08-15 15:53:12.000000000 +0200
+@@ -3,23 +3,23 @@
+  * Copyright (c) 1998, 2001, 2003 Axis Communications AB
+  *
+  * Authors:   Bjorn Wesen
+- *
++ * 
+  * csum_partial_copy_nocheck(const char *src, char *dst,
+  *                         int len, unsigned int sum)
+  */
+       .globl  csum_partial_copy_nocheck
+-csum_partial_copy_nocheck:
+-
++csum_partial_copy_nocheck:    
++      
+       ;; r10 - src
+       ;; r11 - dst
+       ;; r12 - length
+       ;; r13 - checksum
+       ;; check for breakeven length between movem and normal word looping versions
+-      ;; we also do _NOT_ want to compute a checksum over more than the
++      ;; we also do _NOT_ want to compute a checksum over more than the 
+       ;; actual length when length < 40
+-
++      
+       cmpu.w  80,$r12
+       blo     _word_loop
+       nop
+@@ -28,19 +28,19 @@
+       ;; this overhead is why we have a check above for breakeven length
+       ;; only r0 - r8 have to be saved, the other ones are clobber-able
+       ;; according to the ABI
+-
++      
+       subq    9*4,$sp
+       subq    10*4,$r12       ; update length for the first loop
+       movem   $r8,[$sp]
+-
++      
+       ;; do a movem copy and checksum
+-
++      
+ 1:    ;; A failing userspace access (the read) will have this as PC.
+ _mloop:       movem   [$r10+],$r9     ; read 10 longwords
+       movem   $r9,[$r11+]     ; write 10 longwords
+       ;; perform dword checksumming on the 10 longwords
+-
++      
+       add.d   $r0,$r13
+       addc    $r1,$r13
+       addc    $r2,$r13
+@@ -54,9 +54,8 @@
+       ;; fold the carry into the checksum, to avoid having to loop the carry
+       ;; back into the top
+-
++      
+       addc    0,$r13
+-      addc    0,$r13          ; do it again, since we might have generated a carry
+       subq    10*4,$r12
+       bge     _mloop
+@@ -74,34 +73,30 @@
+       ;; fold 32-bit checksum into a 16-bit checksum, to avoid carries below
+       ;; r9 can be used as temporary.
+-
++      
+       move.d  $r13,$r9
+       lsrq    16,$r9          ; r0 = checksum >> 16
+       and.d   0xffff,$r13     ; checksum = checksum & 0xffff
+       add.d   $r9,$r13        ; checksum += r0
+-      move.d  $r13,$r9        ; do the same again, maybe we got a carry last add
+-      lsrq    16,$r9
+-      and.d   0xffff,$r13
+-      add.d   $r9,$r13
+-
++      
+ _no_fold:
+       cmpq    2,$r12
+       blt     _no_words
+       nop
+-
++      
+       ;; copy and checksum the rest of the words
+-
++      
+       subq    2,$r12
+-
++      
+ 2:    ;; A failing userspace access for the read below will have this as PC.
+ _wloop:       move.w  [$r10+],$r9
+       addu.w  $r9,$r13
+       subq    2,$r12
+       bge     _wloop
+       move.w  $r9,[$r11+]
+-
++      
+       addq    2,$r12
+-
++              
+ _no_words:
+       ;; see if we have one odd byte more
+       cmpq    1,$r12
+@@ -110,7 +105,7 @@
+       ret
+       move.d  $r13,$r10
+-_do_byte:
++_do_byte:     
+       ;; copy and checksum the last byte
+ 3:    ;; A failing userspace access for the read below will have this as PC.
+       move.b  [$r10],$r9
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/delay.c linux-2.6.19.2.dev/arch/cris/arch-v32/lib/delay.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/lib/delay.c  1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/delay.c  2006-10-16 01:08:41.000000000 +0200
+@@ -0,0 +1,28 @@
++/*
++ * Precise Delay Loops for ETRAX FS
++ *
++ * Copyright (C) 2006 Axis Communications AB.
++ *
++ */
++
++#include <asm/arch/hwregs/reg_map.h>
++#include <asm/arch/hwregs/reg_rdwr.h>
++#include <asm/arch/hwregs/timer_defs.h>
++#include <linux/types.h>
++#include <linux/delay.h>
++#include <linux/module.h>
++
++/*
++ * On ETRAX FS, we can check the free-running read-only 100MHz timer
++ * getting 32-bit 10ns precision, theoretically good for 42.94967295
++ * seconds.  Unsigned arithmetic and careful expression handles
++ * wrapping.
++ */
++
++void cris_delay10ns(u32 n10ns)
++{
++      u32 t0 = REG_RD(timer, regi_timer, r_time);
++      while (REG_RD(timer, regi_timer, r_time) - t0 < n10ns)
++              ;
++}
++EXPORT_SYMBOL(cris_delay10ns);
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/dram_init.S linux-2.6.19.2.dev/arch/cris/arch-v32/lib/dram_init.S
+--- linux-2.6.19.2.old/arch/cris/arch-v32/lib/dram_init.S      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/dram_init.S      2006-11-28 11:06:19.000000000 +0100
+@@ -1,14 +1,14 @@
+-/* $Id: dram_init.S,v 1.4 2005/04/24 18:48:32 starvik Exp $
+- *
++/* $Id: dram_init.S,v 1.9 2006/11/28 10:06:19 ricardw Exp $
++ * 
+  * DRAM/SDRAM initialization - alter with care
+  * This file is intended to be included from other assembler files
+  *
+- * Note: This file may not modify r8 or r9 because they are used to
+- * carry information from the decompresser to the kernel
++ * Note: This file may not modify r8 .. r12  because they are used to 
++ * carry information from the decompressor to the kernel
+  *
+  * Copyright (C) 2000-2003 Axis Communications AB
+  *
+- * Authors:  Mikael Starvik (starvik@axis.com)
++ * Authors:  Mikael Starvik (starvik@axis.com)        
+  */
+ /* Just to be certain the config file is included, we include it here
+@@ -18,13 +18,13 @@
+ #include <asm/arch/hwregs/asm/reg_map_asm.h>
+ #include <asm/arch/hwregs/asm/bif_core_defs_asm.h>
+-
+-      ;; WARNING! The registers r8 and r9 are used as parameters carrying
+-      ;; information from the decompressor (if the kernel was compressed).
++      
++      ;; WARNING! The registers r8 .. r12 are used as parameters carrying
++      ;; information from the decompressor (if the kernel was compressed). 
+       ;; They should not be used in the code below.
+       ; Refer to BIF MDS for a description of SDRAM initialization
+-
++      
+       ; Bank configuration
+       move.d   REG_ADDR(bif_core, regi_bif_core, rw_sdram_cfg_grp0), $r0
+       move.d   CONFIG_ETRAX_SDRAM_GRP0_CONFIG, $r1
+@@ -33,7 +33,7 @@
+       move.d   CONFIG_ETRAX_SDRAM_GRP1_CONFIG, $r1
+       move.d   $r1, [$r0]
+-      ; Calculate value of mrs_data
++      ; Calculate value of mrs_data 
+       ; CAS latency = 2 && bus_width = 32 => 0x40
+       ; CAS latency = 3 && bus_width = 32 => 0x60
+       ; CAS latency = 2 && bus_width = 16 => 0x20
+@@ -43,7 +43,7 @@
+       move.d   CONFIG_ETRAX_SDRAM_COMMAND, $r2
+       bne      _set_timing
+       nop
+-
++      
+       move.d   0x40, $r4       ; Assume 32 bits and CAS latency = 2
+       move.d   CONFIG_ETRAX_SDRAM_TIMING, $r1
+       and.d    0x07, $r1       ; Get CAS latency
+@@ -51,7 +51,7 @@
+       beq      _bw_check
+       nop
+       move.d   0x60, $r4
+-
++      
+ _bw_check:
+       ; Assume that group 0 width is equal to group 1. This assumption
+       ; is wrong for a group 1 only hardware (such as the grand old
+@@ -67,23 +67,27 @@
+       move.d   CONFIG_ETRAX_SDRAM_TIMING, $r1
+       and.d    ~(3 << reg_bif_core_rw_sdram_timing___ref___lsb), $r1
+       move.d   REG_ADDR(bif_core, regi_bif_core, rw_sdram_timing), $r0
+-      move.d   $r1, [$r0]
++      move.d   $r1, [$r0]     
++
++      ; Wait 200us
++      move.d   10000, $r2
++1:    bne      1b
++      subq     1, $r2
+       ; Issue NOP command
+       move.d REG_ADDR(bif_core, regi_bif_core, rw_sdram_cmd), $r5
+       moveq regk_bif_core_nop, $r1
+       move.d $r1, [$r5]
+-
++      
+       ; Wait 200us
+       move.d   10000, $r2
+ 1:    bne      1b
+       subq     1, $r2
+-
++      
+       ; Issue initialization command sequence
+-      move.d   _sdram_commands_start, $r2
+-      and.d    0x000fffff, $r2 ; Make sure commands are read from flash
+-      move.d   _sdram_commands_end,  $r3
+-      and.d    0x000fffff, $r3
++      lapc.d   _sdram_commands_start, $r2     ; position-independent
++      lapc.d   _sdram_commands_end,  $r3      ; position-independent
++
+ 1:    clear.d  $r6
+       move.b   [$r2+], $r6    ; Load command
+       or.d     $r4, $r6       ; Add calculated mrs
+@@ -100,7 +104,7 @@
+       move.d   CONFIG_ETRAX_SDRAM_TIMING, $r1
+       move.d   REG_ADDR(bif_core, regi_bif_core, rw_sdram_timing), $r0
+       move.d   $r1, [$r0]
+-
++      
+       ; Initialization finished
+       ba       _sdram_commands_end
+       nop
+@@ -116,4 +120,4 @@
+       .byte   regk_bif_core_ref ; refresh
+       .byte   regk_bif_core_ref ; refresh
+       .byte   regk_bif_core_mrs ; mrs
+-_sdram_commands_end:
++_sdram_commands_end:          
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/hw_settings.S linux-2.6.19.2.dev/arch/cris/arch-v32/lib/hw_settings.S
+--- linux-2.6.19.2.old/arch/cris/arch-v32/lib/hw_settings.S    2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/hw_settings.S    2006-10-13 14:43:15.000000000 +0200
+@@ -1,25 +1,25 @@
+ /*
+- * $Id: hw_settings.S,v 1.3 2005/04/24 18:36:57 starvik Exp $
+- *
++ * $Id: hw_settings.S,v 1.5 2006/10/13 12:43:15 starvik Exp $
++ * 
+  * This table is used by some tools to extract hardware parameters.
+  * The table should be included in the kernel and the decompressor.
+  * Don't forget to update the tools if you change this table.
+  *
+  * Copyright (C) 2001 Axis Communications AB
+  *
+- * Authors:  Mikael Starvik (starvik@axis.com)
++ * Authors:  Mikael Starvik (starvik@axis.com)        
+  */
+ #include <asm/arch/hwregs/asm/reg_map_asm.h>
+ #include <asm/arch/hwregs/asm/bif_core_defs_asm.h>
+ #include <asm/arch/hwregs/asm/gio_defs_asm.h>
+-
++              
+       .ascii "HW_PARAM_MAGIC" ; Magic number
+       .dword 0xc0004000       ; Kernel start address
+       ; Debug port
+ #ifdef CONFIG_ETRAX_DEBUG_PORT0
+-      .dword 0
++      .dword 0                
+ #elif defined(CONFIG_ETRAX_DEBUG_PORT1)
+       .dword 1
+ #elif defined(CONFIG_ETRAX_DEBUG_PORT2)
+@@ -28,9 +28,9 @@
+       .dword 3
+ #else
+       .dword 4 ; No debug
+-#endif
++#endif                        
+-      ; Register values
++      ; Register values 
+       .dword REG_ADDR(bif_core, regi_bif_core, rw_grp1_cfg)
+       .dword CONFIG_ETRAX_MEM_GRP1_CONFIG
+       .dword REG_ADDR(bif_core, regi_bif_core, rw_grp2_cfg)
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/memset.c linux-2.6.19.2.dev/arch/cris/arch-v32/lib/memset.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/lib/memset.c 2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/memset.c 2003-07-02 05:00:14.000000000 +0200
+@@ -66,7 +66,7 @@
+   {
+     register char *dst __asm__ ("r13") = pdst;
+-
++ 
+   /* This is NONPORTABLE, but since this whole routine is     */
+   /* grossly nonportable that doesn't matter.                 */
+@@ -156,7 +156,7 @@
+   }
+     /* Either we directly starts copying, using dword copying
+-       in a loop, or we copy as much as possible with 'movem'
++       in a loop, or we copy as much as possible with 'movem' 
+        and then the last block (<44 bytes) is copied here.
+        This will work since 'movem' will have updated src,dst,n. */
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/nand_init.S linux-2.6.19.2.dev/arch/cris/arch-v32/lib/nand_init.S
+--- linux-2.6.19.2.old/arch/cris/arch-v32/lib/nand_init.S      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/nand_init.S      2007-01-31 16:52:19.000000000 +0100
+@@ -9,14 +9,16 @@
+ ##
+ ##    Some notes about the bug/feature for future reference:
+ ##        The bootrom copies the first 127 KB from NAND flash to internal
+-##        memory. The problem is that it does a bytewise copy. NAND flashes
+-##        does autoincrement on the address so for a 16-bite device each
+-##        read/write increases the address by two. So the copy loop in the
++##        memory. The problem is that it does a bytewise copy, copying
++##        a single byte from the lowest byte of the bus for each address.
++##        NAND flashes autoincrement on the address so for a 16 bit device
++##        each read/write increases the address by two. So the copy loop in the
+ ##        bootrom will discard every second byte. This is solved by inserting
+-##        zeroes in every second byte in the first erase block.
++##        zeroes in every second byte in the first erase block, in order
++##        to get contiguous code.
+ ##
+ ##        The bootrom also incorrectly assumes that it can read the flash
+-##        linear with only one read command but the flash will actually
++##        linearly with only one read command but the flash will actually
+ ##        switch between normal area and spare area if you do that so we
+ ##        can't trust more than the first 256 bytes.
+ ##
+@@ -29,14 +31,16 @@
+ #include <asm/arch/hwregs/asm/config_defs_asm.h>
+ ;; There are 8-bit NAND flashes and 16-bit NAND flashes.
+-;; We need to treat them slightly different.
+-#if CONFIG_ETRAX_FLASH_BUSWIDTH==2
+-#define PAGE_SIZE 256
++;; We need to treat them slightly differently.
++#if CONFIG_ETRAX_NANDFLASH_BUSWIDTH==2
++#define PAGE_SIZE_ADDRESSES 256
+ #else
+-#error 2
+-#define PAGE_SIZE 512
++#define PAGE_SIZE_ADDRESSES 512
+ #endif
++
++;; Block size for erase
+ #define ERASE_BLOCK 16384
++#define PAGE_SIZE_BYTES 512
+ ;; GPIO pins connected to NAND flash
+ #define CE 4
+@@ -49,6 +53,7 @@
+ #define NAND_WR_ADDR 0x94000000
+ #define READ_CMD 0x00
++#define RESET_CMD 0xFF
+ ;; Readability macros
+ #define CSP_MASK \
+@@ -58,6 +63,10 @@
+       REG_STATE(bif_core, rw_grp3_cfg, gated_csp0, rd) | \
+       REG_STATE(bif_core, rw_grp3_cfg, gated_csp1, wr)
++;; Normally we initialize GPIO and bus interfaces.
++;; This is strictly not necessary; boot ROM does this for us.
++#define INTERFACE_SETUP (1)
++
+ ;;----------------------------------------------------------------------------
+ ;; Macros to set/clear GPIO bits
+@@ -71,16 +80,41 @@
+       move.d $r9, [$r2]
+ .endm
++.macro GPIO_SYNC
++;;    Originally, we read back data written to nand flash in order
++;;    to flush the pipeline. It turned out however, that the real
++;;    culprit was a lack of wait states.
++;;    This macro remains in the code however in case this conclusion
++;;    is wrong too.
++;;
++;;    move.d [$r2], $r9 ; read back to flush pipeline
++.endm
++
+ ;;----------------------------------------------------------------------------
++;; Read value from bus to temporary register to sync with previous write
++;; This generates no signal to the NAND flash, since only chip select lines are
++;; pulled out to the chip, and read is not gated with chip select for the write
++;; area.
+-nand_boot:
+-      ;; Check if nand boot was selected
+-      move.d REG_ADDR(config, regi_config, r_bootsel), $r0
+-      move.d [$r0], $r0
+-      and.d  REG_MASK(config, r_bootsel, boot_mode), $r0
+-      cmp.d  REG_STATE(config, r_bootsel, boot_mode, nand), $r0
+-      bne normal_boot ; No NAND boot
+-      nop
++.macro BUS_SYNC r
++      move.b [$r1], \r
++.endm
++
++;;----------------------------------------------------------------------------
++;; Delay macro
++;; x = delay = 10*x + 20 ns, e.g. DELAY 25 => 270ns delay, max 63 (650 ns)
++;;(@200Mc)
++;; r is temp reg used
++;; Macro currently not used, save for a rainy day.
++
++.macro DELAY x, r
++      clear.d \r
++      addq    (\x),\r         ; addq zero-extends its argument
++7:    bne     7b
++      subq    1, \r
++.endm
++
++;;----------------------------------------------------------------------------
+ copy_nand_to_ram:
+       ;; copy_nand_to_ram
+@@ -88,7 +122,6 @@
+       ;;   r10 - destination
+       ;;   r11 - source offset
+       ;;   r12 - size
+-      ;;   r13 - Address to jump to after completion
+       ;; Note : r10-r12 are clobbered on return
+       ;; Registers used:
+       ;;   r0 - NAND_RD_ADDR
+@@ -96,83 +129,99 @@
+       ;;   r2 - reg_gio_rw_pa_dout
+       ;;   r3 - reg_gio_r_pa_din
+       ;;   r4 - tmp
+-      ;;   r5 - byte counter within a page
+-      ;;   r6 - reg_pinmux_rw_pa
+-      ;;   r7 - reg_gio_rw_pa_oe
+-      ;;   r8 - reg_bif_core_rw_grp3_cfg
++      ;;   r5 - byte counter within a page / tmp2
++      ;;   r6 - r_bootsel masked w/ 0x18
++      ;;   r7 - n/u
++      ;;   r8 - n/u
+       ;;   r9 - reg_gio_rw_pa_dout shadow
+-      move.d 0x90000000, $r0
+-      move.d 0x94000000, $r1
++      move.d NAND_RD_ADDR, $r0
++      move.d NAND_WR_ADDR, $r1
+       move.d REG_ADDR(gio, regi_gio, rw_pa_dout), $r2
+       move.d REG_ADDR(gio, regi_gio, r_pa_din), $r3
+-      move.d REG_ADDR(pinmux, regi_pinmux, rw_pa), $r6
+-      move.d REG_ADDR(gio, regi_gio, rw_pa_oe), $r7
+-      move.d REG_ADDR(bif_core, regi_bif_core, rw_grp3_cfg), $r8
+-#if CONFIG_ETRAX_FLASH_BUSWIDTH==2
++#if CONFIG_ETRAX_NANDFLASH_BUSWIDTH==2
+       lsrq    1, $r11
+ #endif
++
++#if INTERFACE_SETUP
++      ;; Set up pinmux
++      move.d REG_ADDR(pinmux, regi_pinmux, rw_pa), $r5
++      move.d [$r5], $r4
++      or.b 0xf0, $r4          ; bits 4,5,6,7
++      move.d $r4, [$r5]
++
+       ;; Set up GPIO
+-      move.d [$r2], $r9
+-      move.d [$r7], $r4
++      move.d REG_ADDR(gio, regi_gio, rw_pa_oe), $r5
++      move.d [$r5], $r4
+       or.b (1<<ALE) | (1 << CLE) | (1<<CE), $r4
+-      move.d $r4, [$r7]
++      move.d $r4, [$r5]
++#endif
+       ;; Set up bif
+-      move.d [$r8], $r4
+-      and.d CSP_MASK, $r4
++      move.d REG_ADDR(bif_core, regi_bif_core, rw_grp3_cfg), $r5
++      move.d CONFIG_ETRAX_MEM_GRP3_CONFIG, $r4 ; wait states
++      and.d ~CSP_MASK, $r4
+       or.d CSP_VAL, $r4
+-      move.d $r4, [$r8]
++      move.d $r4, [$r5]
++
++      move.d [$r2], $r9       ; fetch PA DOUT to shadow register
++
++      ;; figure out how many address cycles the flash needs
++      move.d REG_ADDR(config, regi_config, r_bootsel), $r5
++      move.d [$r5], $r6
++      andq 0x18, $r6          ; mask out bs3,4 (00=>3, 08=>4, 18=>5 cycles)
+ 1:    ;; Copy one page
+       CLR CE
+       SET CLE
++      GPIO_SYNC
+       moveq   READ_CMD, $r4
+       move.b  $r4, [$r1]
+-      moveq   20, $r4
+-2:    bne     2b
+-      subq    1, $r4
++      BUS_SYNC $r4
+       CLR CLE
+       SET ALE
+-      clear.w [$r1]           ; Column address = 0
+-      move.d  $r11, $r4
++      GPIO_SYNC
++      clear.b [$r1]           ; Column address = 0
++      move.d  $r11, $r4       ; Address
++      lsrq    9, $r4          ; Row address is A9 and up
++      move.b  $r4, [$r1]      ; Row address byte #0
+       lsrq    8, $r4
+-      move.b  $r4, [$r1]      ; Row address
++      cmpq    0x08, $r6       ; 8 => Z, 0 => C, 18h => NZ,NC
++      bcs     4f              ; C (3 cycles) => jump
++      move.b  $r4, [$r1]      ; Row address byte #1 (DELAY SLOT) (no flagset)
++      beq     3f              ; Z (4 cycles) => jump
++      lsrq    8, $r4          ; (DELAY SLOT)
++      move.b  $r4, [$r1]      ; Row address byte #2 (5 cyc only)
+       lsrq    8, $r4
+-      move.b  $r4, [$r1]      ; Row adddress
+-      moveq   20, $r4
+-2:    bne     2b
+-      subq    1, $r4
++3:
++      move.b  $r4, [$r1]      ; Row address byte #3 (5 cyc) or #2 (4 cyc)
++4:
++      BUS_SYNC $r4
+       CLR ALE
++      GPIO_SYNC
++
+ 2:    move.d  [$r3], $r4
+       and.d   1 << BY, $r4
+       beq 2b
+-      movu.w  PAGE_SIZE, $r5
++      nop
++      movu.w  PAGE_SIZE_ADDRESSES, $r5
+ 2:    ; Copy one byte/word
+-#if CONFIG_ETRAX_FLASH_BUSWIDTH==2
++#if CONFIG_ETRAX_NANDFLASH_BUSWIDTH==2
+       move.w  [$r0], $r4
+ #else
+       move.b  [$r0], $r4
+ #endif
+       subq    1, $r5
+       bne     2b
+-#if CONFIG_ETRAX_FLASH_BUSWIDTH==2
++#if CONFIG_ETRAX_NANDFLASH_BUSWIDTH==2
+       move.w  $r4, [$r10+]
+-      subu.w  PAGE_SIZE*2, $r12
+ #else
+       move.b  $r4, [$r10+]
+-      subu.w  PAGE_SIZE, $r12
+ #endif
+-      bpl     1b
+-      addu.w  PAGE_SIZE, $r11
++      subu.w  PAGE_SIZE_BYTES, $r12
++      bhi     1b
++      addu.w  PAGE_SIZE_ADDRESSES, $r11
+-      ;; End of copy
+-      jump    $r13
+-      nop
++      SET CE
+-      ;; This will warn if the code above is too large. If you consider
+-      ;; to remove this you don't understand the bug/feature.
+-      .org 256
+-      .org ERASE_BLOCK
+-
+-normal_boot:
++      ;; End of copy
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/spinlock.S linux-2.6.19.2.dev/arch/cris/arch-v32/lib/spinlock.S
+--- linux-2.6.19.2.old/arch/cris/arch-v32/lib/spinlock.S       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/spinlock.S       2006-05-24 11:38:43.000000000 +0200
+@@ -1,22 +1,22 @@
+ ;; Core of the spinlock implementation
+ ;;
+-;; Copyright (C) 2004 Axis Communications AB.
++;; Copyright (C) 2004 Axis Communications AB. 
+ ;;
+-;; Author: Mikael Starvik
+-
++;; Author: Mikael Starvik 
++       
+       .global cris_spin_lock
+       .global cris_spin_trylock
+       .text
+-
++      
+ cris_spin_lock:
+       clearf  p
+-1:    test.d  [$r10]
++1:    test.b  [$r10]
+       beq     1b
+       clearf  p
+       ax
+-      clear.d [$r10]
++      clear.b [$r10]
+       bcs     1b
+       clearf  p
+       ret
+@@ -24,10 +24,10 @@
+ cris_spin_trylock:
+       clearf  p
+-1:    move.d  [$r10], $r11
++1:    move.b  [$r10], $r11
+       ax
+-      clear.d [$r10]
++      clear.b [$r10]
+         bcs   1b
+         clearf        p
+       ret
+-      move.d  $r11,$r10
++      movu.b  $r11,$r10
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/lib/string.c linux-2.6.19.2.dev/arch/cris/arch-v32/lib/string.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/lib/string.c 2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/lib/string.c 2003-07-02 05:00:14.000000000 +0200
+@@ -48,8 +48,8 @@
+   register char *dst __asm__ ("r13") = pdst;
+   register const char *src __asm__ ("r11") = psrc;
+   register int n __asm__ ("r12") = pn;
+-
+-
++  
++ 
+   /* When src is aligned but not dst, this makes a few extra needless
+      cycles.  I believe it would take as many to check that the
+      re-alignment was unnecessary.  */
+@@ -117,13 +117,13 @@
+       ;; Restore registers from stack                                 \n\
+         movem [$sp+],$r10"
+-     /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n)
++     /* Outputs */ : "=r" (dst), "=r" (src), "=r" (n) 
+      /* Inputs */ : "0" (dst), "1" (src), "2" (n));
+-
++    
+   }
+   /* Either we directly starts copying, using dword copying
+-     in a loop, or we copy as much as possible with 'movem'
++     in a loop, or we copy as much as possible with 'movem' 
+      and then the last block (<44 bytes) is copied here.
+      This will work since 'movem' will have updated src,dst,n. */
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/mm/init.c linux-2.6.19.2.dev/arch/cris/arch-v32/mm/init.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/mm/init.c    2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/mm/init.c    2006-10-13 14:43:14.000000000 +0200
+@@ -34,12 +34,12 @@
+       unsigned long mmu_kbase_hi;
+       unsigned long mmu_kbase_lo;
+       unsigned short mmu_page_id;
+-
+-      /*
++      
++      /* 
+        * Make sure the current pgd table points to something sane, even if it
+        * is most probably not used until the next switch_mm.
+        */
+-      per_cpu(current_pgd, smp_processor_id()) = init_mm.pgd;
++      per_cpu(current_pgd, smp_processor_id()) = init_mm.pgd; 
+ #ifdef CONFIG_SMP
+       {
+@@ -65,7 +65,7 @@
+                      REG_STATE(mmu, rw_mm_cfg, seg_d, page)   |
+                      REG_STATE(mmu, rw_mm_cfg, seg_c, linear) |
+                      REG_STATE(mmu, rw_mm_cfg, seg_b, linear) |
+-#ifndef CONFIG_ETRAXFS_SIM
++#ifndef CONFIG_ETRAXFS_SIM    
+                        REG_STATE(mmu, rw_mm_cfg, seg_a, page)   |
+ #else
+                      REG_STATE(mmu, rw_mm_cfg, seg_a, linear) |
+@@ -115,7 +115,7 @@
+       SUPP_REG_WR(RW_MM_KBASE_HI, mmu_kbase_hi);
+       SUPP_REG_WR(RW_MM_KBASE_LO, mmu_kbase_lo);
+       SUPP_REG_WR(RW_MM_TLB_HI, mmu_page_id);
+-
++      
+       /* Update the data MMU. */
+       SUPP_BANK_SEL(BANK_DM);
+       SUPP_REG_WR(RW_MM_CFG, mmu_config);
+@@ -125,7 +125,7 @@
+       SPEC_REG_WR(SPEC_REG_PID, 0);
+-      /*
++      /* 
+        * The MMU has been enabled ever since head.S but just to make it
+        * totally obvious enable it here as well.
+        */
+@@ -133,7 +133,7 @@
+       SUPP_REG_WR(RW_GC_CFG, 0xf); /* IMMU, DMMU, ICache, DCache on */
+ }
+-void __init
++void __init 
+ paging_init(void)
+ {
+       int i;
+@@ -160,13 +160,13 @@
+       for (i = 1; i < MAX_NR_ZONES; i++)
+               zones_size[i] = 0;
+-      /*
++      /* 
+        * Use free_area_init_node instead of free_area_init, because it is
+-       * designed for systems where the DRAM starts at an address
++       * designed for systems where the DRAM starts at an address 
+        * substantially higher than 0, like us (we start at PAGE_OFFSET). This
+        * saves space in the mem_map page array.
+        */
+       free_area_init_node(0, &contig_page_data, zones_size, PAGE_OFFSET >> PAGE_SHIFT, 0);
+-
++      
+       mem_map = contig_page_data.node_mem_map;
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/mm/intmem.c linux-2.6.19.2.dev/arch/cris/arch-v32/mm/intmem.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/mm/intmem.c  2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/mm/intmem.c  2006-01-02 12:27:04.000000000 +0100
+@@ -27,7 +27,7 @@
+ {
+       static int initiated = 0;
+       if (!initiated) {
+-              struct intmem_allocation* alloc =
++              struct intmem_allocation* alloc = 
+                 (struct intmem_allocation*)kmalloc(sizeof *alloc, GFP_KERNEL);
+               INIT_LIST_HEAD(&intmem_allocations);
+               intmem_virtual = ioremap(MEM_INTMEM_START, MEM_INTMEM_SIZE);
+@@ -44,7 +44,7 @@
+       struct intmem_allocation* allocation;
+       struct intmem_allocation* tmp;
+       void* ret = NULL;
+-
++  
+       preempt_disable();
+       crisv32_intmem_init();
+@@ -55,7 +55,7 @@
+               if (allocation->status == STATUS_FREE &&
+                   allocation->size >= size + alignment) {
+                       if (allocation->size > size + alignment) {
+-                              struct intmem_allocation* alloc =
++                              struct intmem_allocation* alloc = 
+                                       (struct intmem_allocation*)
+                                       kmalloc(sizeof *alloc, GFP_ATOMIC);
+                               alloc->status = STATUS_FREE;
+@@ -73,13 +73,13 @@
+                                       allocation->offset += alignment;
+                                       list_add_tail(&tmp->entry, &allocation->entry);
+                               }
+-                      }
++                      }               
+                       allocation->status = STATUS_ALLOCATED;
+                       allocation->size = size;
+                       ret = (void*)((int)intmem_virtual + allocation->offset);
+               }
+       }
+-      preempt_enable();
++      preempt_enable();        
+       return ret;
+ }
+@@ -96,22 +96,22 @@
+       list_for_each_entry_safe(allocation, tmp, &intmem_allocations, entry) {
+               if (allocation->offset == (int)(addr - intmem_virtual)) {
+-                      struct intmem_allocation* prev =
+-                        list_entry(allocation->entry.prev,
++                      struct intmem_allocation* prev = 
++                        list_entry(allocation->entry.prev, 
+                                    struct intmem_allocation, entry);
+-                      struct intmem_allocation* next =
+-                        list_entry(allocation->entry.next,
++                      struct intmem_allocation* next = 
++                        list_entry(allocation->entry.next, 
+                                    struct intmem_allocation, entry);
+                       allocation->status = STATUS_FREE;
+                       /* Join with prev and/or next if also free */
+-                      if (prev->status == STATUS_FREE) {
++                      if ((prev != &intmem_allocations) && (prev->status == STATUS_FREE)) {
+                               prev->size += allocation->size;
+                               list_del(&allocation->entry);
+                               kfree(allocation);
+                               allocation = prev;
+                       }
+-                      if (next->status == STATUS_FREE) {
++                      if ((next != &intmem_allocations) && (next->status == STATUS_FREE)) {
+                               allocation->size += next->size;
+                               list_del(&next->entry);
+                               kfree(next);
+@@ -125,13 +125,13 @@
+ void* crisv32_intmem_phys_to_virt(unsigned long addr)
+ {
+-      return (void*)(addr - MEM_INTMEM_START+
++      return (void*)(addr - MEM_INTMEM_START+ 
+                      (unsigned long)intmem_virtual);
+ }
+ unsigned long crisv32_intmem_virt_to_phys(void* addr)
+ {
+-      return (unsigned long)((unsigned long )addr -
++      return (unsigned long)((unsigned long )addr - 
+         (unsigned long)intmem_virtual + MEM_INTMEM_START);
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/mm/mmu.S linux-2.6.19.2.dev/arch/cris/arch-v32/mm/mmu.S
+--- linux-2.6.19.2.old/arch/cris/arch-v32/mm/mmu.S     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/mm/mmu.S     2006-08-04 10:10:20.000000000 +0200
+@@ -1,3 +1,5 @@
++; WARNING : The refill handler has been modified, see below !!!
++
+ /*
+  *  Copyright (C) 2003 Axis Communications AB
+  *
+@@ -9,7 +11,7 @@
+ #include <asm/page.h>
+ #include <asm/pgtable.h>
+-
++      
+ ; Save all register. Must save in same order as struct pt_regs.
+ .macro SAVE_ALL
+       subq    12, $sp
+@@ -29,11 +31,11 @@
+       subq    14*4, $sp
+       movem   $r13, [$sp]
+       subq    4, $sp
+-      move.d  $r10, [$sp]
++      move.d  $r10, [$sp]     
+ .endm
+ ; Bus fault handler. Extracts relevant information and calls mm subsystem
+-; to handle the fault.
++; to handle the fault.        
+ .macro        MMU_BUS_FAULT_HANDLER handler, mmu, we, ex
+       .globl  \handler
+ \handler:
+@@ -45,7 +47,7 @@
+       orq     \ex << 1, $r13  ; execute?
+       move    $s3, $r10       ; rw_mm_cause
+       and.d   ~8191, $r10     ; Get faulting page start address
+-
++      
+       jsr     do_page_fault
+       nop
+       ba      ret_from_intr
+@@ -59,15 +61,28 @@
+ ; The code below handles case 1 and calls the mm subsystem for case 2 and 3.
+ ; Do not touch this code without very good reasons and extensive testing.
+ ; Note that the code is optimized to minimize stalls (makes the code harder
+-; to read).
++; to read).           
++;
++; WARNING !!!
++; Modified by Mikael Asker 060725: added a workaround for strange TLB 
++; behavior. If the same PTE is present in more than one set, the TLB 
++; doesn't recognize it and we get stuck in a loop of refill exceptions.  
++; The workaround detects such loops and exits them by flushing
++; the TLB contents. The problem and workaround were verified 
++; in VCS by Mikael Starvik.
+ ;
+ ; Each page is 8 KB. Each PMD holds 8192/4 PTEs (each PTE is 4 bytes) so each
+-; PMD holds 16 MB of virtual memory.
++; PMD holds 16 MB of virtual memory. 
+ ;   Bits  0-12 : Offset within a page
+ ;   Bits 13-23 : PTE offset within a PMD
+ ;   Bits 24-31 : PMD offset within the PGD
+-
++      
+ .macro MMU_REFILL_HANDLER handler, mmu
++      .data
++1:    .dword  0               ; refill_count
++                                ;   == 0 <=> last_refill_cause is invalid
++2:    .dword  0               ; last_refill_cause
++      .text
+       .globl \handler
+ \handler:
+       subq    4, $sp
+@@ -76,40 +91,88 @@
+       subq    4, $sp
+       move    \mmu, $srs      ; Select MMU support register bank
+       move.d  $acr, [$sp]
+-      subq    4, $sp
+-      move.d  $r0, [$sp]
+-#ifdef CONFIG_SMP
++      subq    12, $sp
++      move.d  1b, $acr        ; Point to refill_count
++      movem   $r2, [$sp]
++
++      test.d  [$acr]          ; refill_count == 0 ?
++      beq     5f              ;   yes, last_refill_cause is invalid
++        move.d        $acr, $r1
++
++      ; last_refill_cause is valid, investigate cause
++        addq    4, $r1          ; Point to last_refill_cause
++      move    $s3, $r0        ; Get rw_mm_cause
++      move.d  [$r1], $r2      ; Get last_refill_cause
++      cmp.d   $r0, $r2        ; rw_mm_cause == last_refill_cause ?
++      beq     6f              ;   yes, increment count
++      moveq   1, $r2
++
++        ; rw_mm_cause != last_refill_cause
++      move.d  $r2, [$acr]     ; refill_count = 1
++      move.d  $r0, [$r1]      ; last_refill_cause = rw_mm_cause
++
++3:    ; Probably not in a loop, continue normal processing
++#ifdef CONFIG_SMP     
+       move    $s7, $acr       ; PGD
+ #else
+       move.d  per_cpu__current_pgd, $acr ; PGD
+ #endif
+       ; Look up PMD in PGD
+-      move    $s3, $r0        ; rw_mm_cause
+       lsrq    24, $r0 ; Get PMD index into PGD (bit 24-31)
+       move.d  [$acr], $acr    ; PGD for the current process
+       addi    $r0.d, $acr, $acr
+       move    $s3, $r0        ; rw_mm_cause
+       move.d  [$acr], $acr    ; Get PMD
+-      beq     1f
++      beq     8f
+       ; Look up PTE in PMD
+       lsrq    PAGE_SHIFT, $r0
+       and.w   PAGE_MASK, $acr ; Remove PMD flags
+       and.d   0x7ff, $r0      ; Get PTE index into PMD (bit 13-23)
+       addi    $r0.d, $acr, $acr
+       move.d  [$acr], $acr    ; Get PTE
+-      beq     2f
+-      move.d  [$sp+], $r0     ; Pop r0 in delayslot
++      beq     9f
++      movem   [$sp], $r2      ; Restore r0-r2 in delay slot
++      addq    12, $sp
+       ; Store in TLB
+       move    $acr, $s5
+-      ; Return
++4:    ; Return
+       move.d  [$sp+], $acr
+       move    [$sp], $srs
+       addq    4, $sp
+       rete
+       rfe
+-1:    ; PMD missing, let the mm subsystem fix it up.
+-      move.d  [$sp+], $r0     ; Pop r0
+-2:      ; PTE missing, let the mm subsystem fix it up.
++
++5:      ; last_refill_cause is invalid
++      moveq   1, $r2
++        addq    4, $r1          ; Point to last_refill_cause
++      move.d  $r2, [$acr]     ; refill_count = 1
++      move    $s3, $r0        ; Get rw_mm_cause
++        ba      3b            ; Continue normal processing
++      move.d  $r0,[$r1]       ; last_refill_cause = rw_mm_cause
++
++6:      ; rw_mm_cause == last_refill_cause
++        move.d  [$acr], $r2     ; Get refill_count
++      cmpq    4, $r2          ; refill_count > 4 ?
++      bhi     7f              ;   yes
++      addq    1, $r2          ; refill_count++
++      ba      3b              ; Continue normal processing
++      move.d  $r2, [$acr]
++
++7:    ; refill_count > 4, error
++      subq    4, $sp
++      move    $srp, [$sp]
++      jsr     __flush_tlb_all
++        move.d        $acr, $r0       ; Save pointer to refill_count
++      move    [$sp+], $srp
++      clear.d [$r0]           ; refill_count = 0
++      movem   [$sp], $r2
++      ba      4b              ; Return
++      addq    12, $sp
++
++8:    ; PMD missing, let the mm subsystem fix it up.
++      movem   [$sp], $r2      ; Restore r0-r2
++9:      ; PTE missing, let the mm subsystem fix it up.
++      addq    12, $sp
+       move.d  [$sp+], $acr
+       move    [$sp], $srs
+       addq    4, $sp
+@@ -128,7 +191,7 @@
+       ba      ret_from_intr
+       nop
+ .endm
+-
++                      
+       ; This is the MMU bus fault handlers.
+ MMU_REFILL_HANDLER i_mmu_refill, 1
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/mm/tlb.c linux-2.6.19.2.dev/arch/cris/arch-v32/mm/tlb.c
+--- linux-2.6.19.2.old/arch/cris/arch-v32/mm/tlb.c     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/mm/tlb.c     2006-08-07 12:06:44.000000000 +0200
+@@ -2,7 +2,7 @@
+  * Low level TLB handling.
+  *
+  * Copyright (C) 2000-2003, Axis Communications AB.
+- *
++ *  
+  * Authors:   Bjorn Wesen <bjornw@axis.com>
+  *            Tobias Anderberg <tobiasa@axis.com>, CRISv32 port.
+  */
+@@ -79,7 +79,7 @@
+ void
+ __flush_tlb_mm(struct mm_struct *mm)
+ {
+-      int i;
++      int i; 
+       int mmu;
+       unsigned long flags;
+       unsigned long page_id;
+@@ -90,7 +90,7 @@
+       if (page_id == NO_CONTEXT)
+               return;
+-
++      
+       /* Mark the TLB entries that match the page_id as invalid. */
+       local_save_flags(flags);
+       local_irq_disable();
+@@ -99,15 +99,15 @@
+               SUPP_BANK_SEL(mmu);
+               for (i = 0; i < NUM_TLB_ENTRIES; i++) {
+                       UPDATE_TLB_SEL_IDX(i);
+-
++      
+                       /* Get the page_id */
+                       SUPP_REG_RD(RW_MM_TLB_HI, tlb_hi);
+                       /* Check if the page_id match. */
+                       if ((tlb_hi & 0xff) == page_id) {
+                               mmu_tlb_hi = (REG_FIELD(mmu, rw_mm_tlb_hi, pid,
+-                                                      INVALID_PAGEID)
+-                                          | REG_FIELD(mmu, rw_mm_tlb_hi, vpn,
++                                                      INVALID_PAGEID) 
++                                          | REG_FIELD(mmu, rw_mm_tlb_hi, vpn, 
+                                                       i & 0xf));
+                               UPDATE_TLB_HILO(mmu_tlb_hi, 0);
+@@ -135,7 +135,7 @@
+               return;
+       addr &= PAGE_MASK;
+-
++      
+       /*
+        * Invalidate those TLB entries that match both the mm context and the
+        * requested virtual address.
+@@ -150,11 +150,11 @@
+                       SUPP_REG_RD(RW_MM_TLB_HI, tlb_hi);
+                       /* Check if page_id and address matches */
+-                      if (((tlb_hi & 0xff) == page_id) &&
++                      if (((tlb_hi & 0xff) == page_id) && 
+                           ((tlb_hi & PAGE_MASK) == addr)) {
+                               mmu_tlb_hi = REG_FIELD(mmu, rw_mm_tlb_hi, pid,
+                                                      INVALID_PAGEID) | addr;
+-
++                      
+                               UPDATE_TLB_HILO(mmu_tlb_hi, 0);
+                       }
+               }
+@@ -178,33 +178,35 @@
+ static DEFINE_SPINLOCK(mmu_context_lock);
+ /* Called in schedule() just before actually doing the switch_to. */
+-void
++void 
+ switch_mm(struct mm_struct *prev, struct mm_struct *next,
+         struct task_struct *tsk)
+-{
+-      int cpu = smp_processor_id();
+-
+-      /* Make sure there is a MMU context. */
+-      spin_lock(&mmu_context_lock);
+-      get_mmu_context(next);
+-      cpu_set(cpu, next->cpu_vm_mask);
+-      spin_unlock(&mmu_context_lock);
+-
+-      /*
+-       * Remember the pgd for the fault handlers. Keep a seperate copy of it
+-       * because current and active_mm might be invalid at points where
+-       * there's still a need to derefer the pgd.
+-       */
+-      per_cpu(current_pgd, cpu) = next->pgd;
+-
+-      /* Switch context in the MMU. */
+-        if (tsk && task_thread_info(tsk))
+-        {
+-          SPEC_REG_WR(SPEC_REG_PID, next->context.page_id | task_thread_info(tsk)->tls);
+-        }
+-        else
+-        {
+-          SPEC_REG_WR(SPEC_REG_PID, next->context.page_id);
+-        }
++{     
++      if (prev != next) {
++              int cpu = smp_processor_id();
++              
++              /* Make sure there is a MMU context. */
++              spin_lock(&mmu_context_lock);
++              get_mmu_context(next);
++              cpu_set(cpu, next->cpu_vm_mask);
++              spin_unlock(&mmu_context_lock);
++              
++              /*
++               * Remember the pgd for the fault handlers. Keep a seperate copy of it
++               * because current and active_mm might be invalid at points where
++               * there's still a need to derefer the pgd.
++               */
++              per_cpu(current_pgd, cpu) = next->pgd;
++              
++              /* Switch context in the MMU. */
++              if (tsk && task_thread_info(tsk))
++              {
++                      SPEC_REG_WR(SPEC_REG_PID, next->context.page_id | task_thread_info(tsk)->tls);
++              }
++              else
++              {
++                      SPEC_REG_WR(SPEC_REG_PID, next->context.page_id);
++              }
++      }
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/arch-v32/vmlinux.lds.S linux-2.6.19.2.dev/arch/cris/arch-v32/vmlinux.lds.S
+--- linux-2.6.19.2.old/arch/cris/arch-v32/vmlinux.lds.S        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/arch-v32/vmlinux.lds.S        2006-10-13 14:43:11.000000000 +0200
+@@ -5,11 +5,11 @@
+  * script. It is for example quite vital that all generated sections
+  * that are used are actually named here, otherwise the linker will
+  * put them at the end, where the init stuff is which is FREED after
+- * the kernel has booted.
+- */
++ * the kernel has booted. 
++ */   
+ #include <asm-generic/vmlinux.lds.h>
+-
++              
+ jiffies = jiffies_64;
+ SECTIONS
+ {
+@@ -20,7 +20,7 @@
+       /* The boot section is only necessary until the VCS top level testbench */
+       /* includes both flash and DRAM. */
+       .boot : { *(.boot) }
+-
++      
+       . = DRAM_VIRTUAL_BASE + 0x4000;         /* See head.S and pages reserved at the start. */
+       _text = .;              /* Text and read-only data. */
+@@ -35,7 +35,7 @@
+               *(.text.__*)
+       }
+-      _etext = . ;            /* End of text section. */
++      _etext = . ;            /* End of text section. */ 
+       __etext = .;
+       . = ALIGN(4);           /* Exception table. */
+@@ -59,7 +59,7 @@
+       . = ALIGN(8192);        /* Init code and data. */
+       __init_begin = .;
+-      .init.text : {
++      .init.text : { 
+                  _sinittext = .;
+                  *(.init.text)
+                  _einittext = .;
+@@ -81,7 +81,7 @@
+               *(.initcall5.init);
+               *(.initcall6.init);
+               *(.initcall7.init);
+-              __initcall_end = .;
++              __initcall_end = .;     
+       }
+       .con_initcall.init : {
+@@ -94,20 +94,20 @@
+       __per_cpu_start = .;
+       .data.percpu  : { *(.data.percpu) }
+       __per_cpu_end = .;
+-
++      
+       .init.ramfs : {
+               __initramfs_start = .;
+               *(.init.ramfs)
+               __initramfs_end = .;
+-              /*
++              /* 
+                * We fill to the next page, so we can discard all init
+                * pages without needing to consider what payload might be
+                * appended to the kernel image.
+                */
+-              FILL (0);
++              FILL (0); 
+               . = ALIGN (8192);
+       }
+-
++      
+       __vmlinux_end = .;      /* Last address of the physical file. */
+       __init_end = .;
+diff -urN linux-2.6.19.2.old/arch/cris/kernel/crisksyms.c linux-2.6.19.2.dev/arch/cris/kernel/crisksyms.c
+--- linux-2.6.19.2.old/arch/cris/kernel/crisksyms.c    2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/kernel/crisksyms.c    2006-11-03 13:49:17.000000000 +0100
+@@ -9,7 +9,7 @@
+ #include <linux/kernel.h>
+ #include <linux/string.h>
+ #include <linux/tty.h>
+-
++ 
+ #include <asm/semaphore.h>
+ #include <asm/processor.h>
+ #include <asm/uaccess.h>
+@@ -28,6 +28,7 @@
+ extern void __ashldi3(void);
+ extern void __ashrdi3(void);
+ extern void __lshrdi3(void);
++extern void __negdi2(void);
+ extern void iounmap(volatile void * __iomem);
+ /* Platform dependent support */
+@@ -35,18 +36,7 @@
+ EXPORT_SYMBOL(get_cmos_time);
+ EXPORT_SYMBOL(loops_per_usec);
+-/* String functions */
+-EXPORT_SYMBOL(memcmp);
+-EXPORT_SYMBOL(memmove);
+-EXPORT_SYMBOL(strstr);
+-EXPORT_SYMBOL(strcpy);
+-EXPORT_SYMBOL(strchr);
+-EXPORT_SYMBOL(strcmp);
+-EXPORT_SYMBOL(strlen);
+-EXPORT_SYMBOL(strcat);
+-EXPORT_SYMBOL(strncat);
+-EXPORT_SYMBOL(strncmp);
+-EXPORT_SYMBOL(strncpy);
++EXPORT_SYMBOL(ktime_get_ts);
+ /* Math functions */
+ EXPORT_SYMBOL(__Udiv);
+@@ -56,6 +46,7 @@
+ EXPORT_SYMBOL(__ashldi3);
+ EXPORT_SYMBOL(__ashrdi3);
+ EXPORT_SYMBOL(__lshrdi3);
++EXPORT_SYMBOL(__negdi2);
+ /* Memory functions */
+ EXPORT_SYMBOL(__ioremap);
+@@ -85,4 +76,4 @@
+ EXPORT_SYMBOL(del_fast_timer);
+ EXPORT_SYMBOL(schedule_usleep);
+ #endif
+-
++EXPORT_SYMBOL(csum_partial);
+diff -urN linux-2.6.19.2.old/arch/cris/kernel/irq.c linux-2.6.19.2.dev/arch/cris/kernel/irq.c
+--- linux-2.6.19.2.old/arch/cris/kernel/irq.c  2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/kernel/irq.c  2007-01-09 10:29:20.000000000 +0100
+@@ -92,14 +92,16 @@
+ asmlinkage void do_IRQ(int irq, struct pt_regs * regs)
+ {
+       unsigned long sp;
++      struct pt_regs *old_regs = set_irq_regs(regs);
+       irq_enter();
+       sp = rdsp();
+       if (unlikely((sp & (PAGE_SIZE - 1)) < (PAGE_SIZE/8))) {
+               printk("do_IRQ: stack overflow: %lX\n", sp);
+               show_stack(NULL, (unsigned long *)sp);
+       }
+-      __do_IRQ(irq, regs);
++      __do_IRQ(irq);
+         irq_exit();
++      set_irq_regs(old_regs);
+ }
+ void weird_irq(void)
+diff -urN linux-2.6.19.2.old/arch/cris/kernel/process.c linux-2.6.19.2.dev/arch/cris/kernel/process.c
+--- linux-2.6.19.2.old/arch/cris/kernel/process.c      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/kernel/process.c      2006-06-25 17:00:10.000000000 +0200
+@@ -1,4 +1,4 @@
+-/* $Id: process.c,v 1.21 2005/03/04 08:16:17 starvik Exp $
++/* $Id: process.c,v 1.26 2006/06/25 15:00:10 starvik Exp $
+  * 
+  *  linux/arch/cris/kernel/process.c
+  *
+@@ -8,6 +8,21 @@
+  *  Authors:   Bjorn Wesen (bjornw@axis.com)
+  *
+  *  $Log: process.c,v $
++ *  Revision 1.26  2006/06/25 15:00:10  starvik
++ *  Merge of Linux 2.6.17
++ *
++ *  Revision 1.25  2006/03/22 09:56:56  starvik
++ *  Merge of Linux 2.6.16
++ *
++ *  Revision 1.24  2006/01/04 06:09:48  starvik
++ *  Merge of Linux 2.6.15
++ *
++ *  Revision 1.23  2005/08/29 07:32:19  starvik
++ *  Merge of 2.6.13
++ *
++ *  Revision 1.22  2005/08/18 08:33:18  starvik
++ *  Corrected signature of machine_restart
++ *
+  *  Revision 1.21  2005/03/04 08:16:17  starvik
+  *  Merge of Linux 2.6.11.
+  *
+@@ -195,12 +210,18 @@
+  */
+ void (*pm_idle)(void);
++extern void default_idle(void);
++
++void (*pm_power_off)(void);
++EXPORT_SYMBOL(pm_power_off);
++
+ /*
+  * The idle thread. There's no useful work to be
+  * done, so just try to conserve power and have a
+  * low exit latency (ie sit in a loop waiting for
+  * somebody to say that they'd like to reschedule)
+  */
++
+ void cpu_idle (void)
+ {
+       /* endless idle loop with no priority at all */
+diff -urN linux-2.6.19.2.old/arch/cris/kernel/profile.c linux-2.6.19.2.dev/arch/cris/kernel/profile.c
+--- linux-2.6.19.2.old/arch/cris/kernel/profile.c      2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/kernel/profile.c      2004-10-05 08:22:44.000000000 +0200
+@@ -42,7 +42,7 @@
+   return count;
+ }
+-static ssize_t
++static ssize_t 
+ write_cris_profile(struct file *file, const char __user *buf,
+               size_t count, loff_t *ppos)
+ {
+diff -urN linux-2.6.19.2.old/arch/cris/kernel/ptrace.c linux-2.6.19.2.dev/arch/cris/kernel/ptrace.c
+--- linux-2.6.19.2.old/arch/cris/kernel/ptrace.c       2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/kernel/ptrace.c       2006-03-23 15:54:02.000000000 +0100
+@@ -8,6 +8,9 @@
+  * Authors:   Bjorn Wesen
+  *
+  * $Log: ptrace.c,v $
++ * Revision 1.11  2006/03/23 14:54:02  starvik
++ * Corrected signal handling.
++ *
+  * Revision 1.10  2004/09/22 11:50:01  orjanf
+  * * Moved get_reg/put_reg to arch-specific files.
+  * * Added functions to access debug registers (CRISv32).
+@@ -82,13 +85,13 @@
+ /* notification of userspace execution resumption
+  * - triggered by current->work.notify_resume
+  */
+-extern int do_signal(int canrestart, sigset_t *oldset, struct pt_regs *regs);
++extern int do_signal(int canrestart, struct pt_regs *regs);
+-void do_notify_resume(int canrestart, sigset_t *oldset, struct pt_regs *regs, 
++void do_notify_resume(int canrestart, struct pt_regs *regs, 
+                     __u32 thread_info_flags  )
+ {
+       /* deal with pending signal delivery */
+       if (thread_info_flags & _TIF_SIGPENDING)
+-              do_signal(canrestart,oldset,regs);
++              do_signal(canrestart,regs);
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/kernel/semaphore.c linux-2.6.19.2.dev/arch/cris/kernel/semaphore.c
+--- linux-2.6.19.2.old/arch/cris/kernel/semaphore.c    2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/kernel/semaphore.c    2005-10-31 09:48:05.000000000 +0100
+@@ -4,7 +4,7 @@
+  */
+ #include <linux/sched.h>
+-#include <linux/init.h>
++#include <asm/semaphore.h>
+ #include <asm/semaphore-helper.h>
+ /*
+@@ -95,6 +95,7 @@
+       tsk->state = TASK_RUNNING;              \
+       remove_wait_queue(&sem->wait, &wait);
++
+ void __sched __down(struct semaphore * sem)
+ {
+       DOWN_VAR
+diff -urN linux-2.6.19.2.old/arch/cris/kernel/setup.c linux-2.6.19.2.dev/arch/cris/kernel/setup.c
+--- linux-2.6.19.2.old/arch/cris/kernel/setup.c        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/kernel/setup.c        2007-01-09 10:29:20.000000000 +0100
+@@ -18,7 +18,7 @@
+ #include <linux/screen_info.h>
+ #include <linux/utsname.h>
+ #include <linux/pfn.h>
+-
++#include <linux/cpu.h>
+ #include <asm/setup.h>
+ /*
+@@ -36,6 +36,8 @@
+ extern unsigned long romfs_start, romfs_length, romfs_in_flash; /* from head.S */
++static struct cpu cpu_devices[NR_CPUS];
++
+ extern void show_etrax_copyright(void);               /* arch-vX/kernel/setup.c */
+ /* This mainly sets up the memory area, and can be really confusing.
+@@ -187,4 +189,14 @@
+       .show  = show_cpuinfo,
+ };
++static int __init topology_init(void)
++{
++      int i;
++      
++      for_each_possible_cpu(i) {
++               return register_cpu(&cpu_devices[i], i);
++      }
++}
++
++subsys_initcall(topology_init);
+diff -urN linux-2.6.19.2.old/arch/cris/kernel/sys_cris.c linux-2.6.19.2.dev/arch/cris/kernel/sys_cris.c
+--- linux-2.6.19.2.old/arch/cris/kernel/sys_cris.c     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/kernel/sys_cris.c     2007-01-09 10:29:20.000000000 +0100
+@@ -1,4 +1,4 @@
+-/* $Id: sys_cris.c,v 1.6 2004/03/11 11:38:40 starvik Exp $
++/* $Id: sys_cris.c,v 1.7 2007/01/09 09:29:20 starvik Exp $
+  *
+  * linux/arch/cris/kernel/sys_cris.c
+  *
+@@ -172,3 +172,4 @@
+               return -ENOSYS;
+       }
+ }
++
+diff -urN linux-2.6.19.2.old/arch/cris/kernel/time.c linux-2.6.19.2.dev/arch/cris/kernel/time.c
+--- linux-2.6.19.2.old/arch/cris/kernel/time.c 2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/kernel/time.c 2007-01-09 10:29:20.000000000 +0100
+@@ -1,4 +1,4 @@
+-/* $Id: time.c,v 1.18 2005/03/04 08:16:17 starvik Exp $
++/* $Id: time.c,v 1.23 2007/01/09 09:29:20 starvik Exp $
+  *
+  *  linux/arch/cris/kernel/time.c
+  *
+@@ -172,10 +172,6 @@
+       mon = CMOS_READ(RTC_MONTH);
+       year = CMOS_READ(RTC_YEAR);
+-      printk(KERN_DEBUG
+-             "rtc: sec 0x%x min 0x%x hour 0x%x day 0x%x mon 0x%x year 0x%x\n",
+-             sec, min, hour, day, mon, year);
+-
+       BCD_TO_BIN(sec);
+       BCD_TO_BIN(min);
+       BCD_TO_BIN(hour);
+@@ -208,11 +204,11 @@
+ cris_do_profile(struct pt_regs* regs)
+ {
+-#if CONFIG_SYSTEM_PROFILER
++#ifdef CONFIG_SYSTEM_PROFILER
+         cris_profile_sample(regs);
+ #endif
+-#if CONFIG_PROFILING
++#ifdef CONFIG_PROFILING
+         profile_tick(CPU_PROFILING, regs);
+ #endif
+ }
+@@ -222,10 +218,15 @@
+  */
+ unsigned long long sched_clock(void)
+ {
+-      return (unsigned long long)jiffies * (1000000000 / HZ);
++      unsigned long long ns;
++
++      ns  = jiffies;
++      ns *= 1000000000 / HZ;
++      ns += get_ns_in_jiffie();
++      return ns;
+ }
+-static int
++static int 
+ __init init_udelay(void)
+ {
+       loops_per_usec = (loops_per_jiffy * HZ) / 1000000;
+diff -urN linux-2.6.19.2.old/arch/cris/kernel/traps.c linux-2.6.19.2.dev/arch/cris/kernel/traps.c
+--- linux-2.6.19.2.old/arch/cris/kernel/traps.c        2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/kernel/traps.c        2006-12-11 14:04:23.000000000 +0100
+@@ -1,66 +1,78 @@
+-/* $Id: traps.c,v 1.11 2005/01/24 16:03:19 orjanf Exp $
+- *
++/*
+  *  linux/arch/cris/traps.c
+  *
+- *  Here we handle the break vectors not used by the system call 
+- *  mechanism, as well as some general stack/register dumping 
++ *  Here we handle the break vectors not used by the system call
++ *  mechanism, as well as some general stack/register dumping
+  *  things.
+- * 
+- *  Copyright (C) 2000-2002 Axis Communications AB
++ *
++ *  Copyright (C) 2000-2006 Axis Communications AB
+  *
+  *  Authors:   Bjorn Wesen
+- *           Hans-Peter Nilsson
++ *             Hans-Peter Nilsson
+  *
+  */
+ #include <linux/init.h>
+ #include <linux/module.h>
++
+ #include <asm/pgtable.h>
+ #include <asm/uaccess.h>
++extern void arch_enable_nmi(void);
++extern void stop_watchdog(void);
++extern void reset_watchdog(void);
++extern void show_registers(struct pt_regs *regs);
++
++#ifdef CONFIG_DEBUG_BUGVERBOSE
++extern void handle_BUG(struct pt_regs *regs);
++#else
++#define handle_BUG(regs)
++#endif
++
+ static int kstack_depth_to_print = 24;
+-extern int raw_printk(const char *fmt, ...);
++void (*nmi_handler)(struct pt_regs*);
+-void show_trace(unsigned long * stack)
++void
++show_trace(unsigned long *stack)
+ {
+       unsigned long addr, module_start, module_end;
+       extern char _stext, _etext;
+       int i;
+-        raw_printk("\nCall Trace: ");
++      printk("\nCall Trace: ");
+-        i = 1;
+-        module_start = VMALLOC_START;
+-        module_end = VMALLOC_END;
++      i = 1;
++      module_start = VMALLOC_START;
++      module_end = VMALLOC_END;
+-        while (((long) stack & (THREAD_SIZE-1)) != 0) {
+-              if (__get_user (addr, stack)) {
++      while (((long)stack & (THREAD_SIZE-1)) != 0) {
++              if (__get_user(addr, stack)) {
+                       /* This message matches "failing address" marked
+                          s390 in ksymoops, so lines containing it will
+                          not be filtered out by ksymoops.  */
+-                      raw_printk ("Failing address 0x%lx\n", (unsigned long)stack);
++                      printk("Failing address 0x%lx\n", (unsigned long)stack);
+                       break;
+               }
+               stack++;
+-                /*
+-                 * If the address is either in the text segment of the
+-                 * kernel, or in the region which contains vmalloc'ed
+-                 * memory, it *may* be the address of a calling
+-                 * routine; if so, print it so that someone tracing
+-                 * down the cause of the crash will be able to figure
+-                 * out the call path that was taken.
+-                 */
+-                if (((addr >= (unsigned long) &_stext) &&
+-                     (addr <= (unsigned long) &_etext)) ||
+-                    ((addr >= module_start) && (addr <= module_end))) {
+-                        if (i && ((i % 8) == 0))
+-                                raw_printk("\n       ");
+-                        raw_printk("[<%08lx>] ", addr);
+-                        i++;
+-                }
+-        }
++              /*
++               * If the address is either in the text segment of the
++               * kernel, or in the region which contains vmalloc'ed
++               * memory, it *may* be the address of a calling
++               * routine; if so, print it so that someone tracing
++               * down the cause of the crash will be able to figure
++               * out the call path that was taken.
++               */
++              if (((addr >= (unsigned long)&_stext) &&
++                   (addr <= (unsigned long)&_etext)) ||
++                  ((addr >= module_start) && (addr <= module_end))) {
++                      if (i && ((i % 8) == 0))
++                              printk("\n       ");
++                      printk("[<%08lx>] ", addr);
++                      i++;
++              }
++      }
+ }
+ /*
+@@ -78,109 +90,150 @@
+  * with the ksymoops maintainer.
+  */
+-void 
++void
+ show_stack(struct task_struct *task, unsigned long *sp)
+ {
+-        unsigned long *stack, addr;
+-        int i;
++      unsigned long *stack, addr;
++      int i;
+       /*
+        * debugging aid: "show_stack(NULL);" prints a
+        * back trace.
+        */
+-        if(sp == NULL) {
++      if (sp == NULL) {
+               if (task)
+                       sp = (unsigned long*)task->thread.ksp;
+               else
+                       sp = (unsigned long*)rdsp();
+       }
+-        stack = sp;
++      stack = sp;
+-      raw_printk("\nStack from %08lx:\n       ", (unsigned long)stack);
+-        for(i = 0; i < kstack_depth_to_print; i++) {
+-                if (((long) stack & (THREAD_SIZE-1)) == 0)
+-                        break;
+-                if (i && ((i % 8) == 0))
+-                        raw_printk("\n       ");
+-              if (__get_user (addr, stack)) {
++      printk("\nStack from %08lx:\n       ", (unsigned long)stack);
++      for (i = 0; i < kstack_depth_to_print; i++) {
++              if (((long)stack & (THREAD_SIZE-1)) == 0)
++                      break;
++              if (i && ((i % 8) == 0))
++                      printk("\n       ");
++              if (__get_user(addr, stack)) {
+                       /* This message matches "failing address" marked
+                          s390 in ksymoops, so lines containing it will
+                          not be filtered out by ksymoops.  */
+-                      raw_printk ("Failing address 0x%lx\n", (unsigned long)stack);
++                      printk("Failing address 0x%lx\n", (unsigned long)stack);
+                       break;
+               }
+               stack++;
+-              raw_printk("%08lx ", addr);
+-        }
++              printk("%08lx ", addr);
++      }
+       show_trace(sp);
+ }
+-static void (*nmi_handler)(struct pt_regs*);
+-extern void arch_enable_nmi(void);
++#if 0
++/* displays a short stack trace */
+-void set_nmi_handler(void (*handler)(struct pt_regs*))
++int
++show_stack(void)
+ {
+-  nmi_handler = handler;
+-  arch_enable_nmi();
++      unsigned long *sp = (unsigned long *)rdusp();
++      int i;
++
++      printk("Stack dump [0x%08lx]:\n", (unsigned long)sp);
++      for (i = 0; i < 16; i++)
++              printk("sp + %d: 0x%08lx\n", i*4, sp[i]);
++      return 0;
+ }
++#endif
+-void handle_nmi(struct pt_regs* regs)
++void
++dump_stack(void)
+ {
+-  if (nmi_handler)
+-    nmi_handler(regs);
++      show_stack(NULL, NULL);
++}
++
++EXPORT_SYMBOL(dump_stack);
++
++void
++set_nmi_handler(void (*handler)(struct pt_regs*))
++{
++      nmi_handler = handler;
++      arch_enable_nmi();
+ }
+ #ifdef CONFIG_DEBUG_NMI_OOPS
+-void oops_nmi_handler(struct pt_regs* regs)
++void
++oops_nmi_handler(struct pt_regs* regs)
+ {
+-  stop_watchdog();
+-  raw_printk("NMI!\n");
+-  show_registers(regs);
++      stop_watchdog();
++      oops_in_progress = 1;
++      printk("NMI!\n");
++      show_registers(regs);
++      oops_in_progress = 0;
+ }
+-static int
+-__init oops_nmi_register(void)
++static int __init
++oops_nmi_register(void)
+ {
+-  set_nmi_handler(oops_nmi_handler);
+-  return 0;
++      set_nmi_handler(oops_nmi_handler);
++      return 0;
+ }
+ __initcall(oops_nmi_register);
+ #endif
+-#if 0
+-/* displays a short stack trace */
+-
+-int 
+-show_stack()
++/*
++ * This gets called from entry.S when the watchdog has bitten. Show something
++ * similiar to an Oops dump, and if the kernel is configured to be a nice
++ * doggy, then halt instead of reboot.
++ */
++void
++watchdog_bite_hook(struct pt_regs *regs)
+ {
+-      unsigned long *sp = (unsigned long *)rdusp();
+-      int i;
+-      raw_printk("Stack dump [0x%08lx]:\n", (unsigned long)sp);
+-      for(i = 0; i < 16; i++)
+-              raw_printk("sp + %d: 0x%08lx\n", i*4, sp[i]);
+-      return 0;
+-}
++#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
++      local_irq_disable();
++      stop_watchdog();
++      show_registers(regs);
++
++      while (1)
++              ; /* Do nothing. */
++#else
++      show_registers(regs);
+ #endif
++}
+-void dump_stack(void)
++/* This is normally the Oops function. */
++void
++die_if_kernel(const char *str, struct pt_regs *regs, long err)
+ {
+-      show_stack(NULL, NULL);
+-}
++      if (user_mode(regs))
++              return;
+-EXPORT_SYMBOL(dump_stack);
++#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
++      /*
++       * This printout might take too long and could trigger
++       * the watchdog normally. If NICE_DOGGY is set, simply
++       * stop the watchdog during the printout.
++       */
++      stop_watchdog();
++#endif
+-void __init 
+-trap_init(void)
+-{
+-      /* Nothing needs to be done */
++      handle_BUG(regs);
++
++      printk("%s: %04lx\n", str, err & 0xffff);
++
++      show_registers(regs);
++
++      oops_in_progress = 0;
++
++#ifdef CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
++      reset_watchdog();
++#endif
++      do_exit(SIGSEGV);
+ }
+-void spinning_cpu(void* addr)
++void __init
++trap_init(void)
+ {
+-  raw_printk("CPU %d spinning on %X\n", smp_processor_id(), addr);
+-  dump_stack();
++      /* Nothing needs to be done */
+ }
+diff -urN linux-2.6.19.2.old/arch/cris/mm/fault.c linux-2.6.19.2.dev/arch/cris/mm/fault.c
+--- linux-2.6.19.2.old/arch/cris/mm/fault.c    2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/mm/fault.c    2006-09-29 13:14:06.000000000 +0200
+@@ -1,11 +1,27 @@
+ /*
+  *  linux/arch/cris/mm/fault.c
+  *
+- *  Copyright (C) 2000, 2001  Axis Communications AB
++ *  Copyright (C) 2000-2006  Axis Communications AB
++ *
++ *  Authors:  Bjorn Wesen
+  *
+- *  Authors:  Bjorn Wesen 
+- * 
+  *  $Log: fault.c,v $
++ *  Revision 1.25  2006/09/29 11:14:06  orjanf
++ *  * Use arch-independent macro to get irp/erp for v10/v32.
++ *
++ *  Revision 1.24  2006/09/29 10:58:09  orjanf
++ *  * Added user mode SIGSEGV printk.
++ *
++ *  Revision 1.23  2006/06/20 07:42:56  pkj
++ *  Removed an unnecessary reference to raw_printk().
++ *
++ *  Revision 1.22  2005/08/29 07:32:20  starvik
++ *  Merge of 2.6.13
++ *
++ *  Revision 1.21  2005/07/02 12:29:37  starvik
++ *  Use the generic oops_in_progress instead of the raw_printk hack.
++ *  Moved some functions to achr-independent code.
++ *
+  *  Revision 1.20  2005/03/04 08:16:18  starvik
+  *  Merge of Linux 2.6.11.
+  *
+@@ -135,7 +151,6 @@
+ extern int find_fixup_code(struct pt_regs *);
+ extern void die_if_kernel(const char *, struct pt_regs *, long);
+-extern int raw_printk(const char *fmt, ...);
+ /* debug of low-level TLB reload */
+ #undef DEBUG
+@@ -164,8 +179,8 @@
+  * address.
+  *
+  * error_code:
+- *    bit 0 == 0 means no page found, 1 means protection fault
+- *    bit 1 == 0 means read, 1 means write
++ *      bit 0 == 0 means no page found, 1 means protection fault
++ *      bit 1 == 0 means read, 1 means write
+  *
+  * If this routine detects a bad access, it returns 1, otherwise it
+  * returns 0.
+@@ -180,9 +195,9 @@
+       struct vm_area_struct * vma;
+       siginfo_t info;
+-        D(printk("Page fault for %lX on %X at %lX, prot %d write %d\n",
+-                 address, smp_processor_id(), instruction_pointer(regs),
+-                 protection, writeaccess));
++      D(printk("Page fault for %lX on %X at %lX, prot %d write %d\n",
++               address, smp_processor_id(), instruction_pointer(regs),
++               protection, writeaccess));
+       tsk = current;
+@@ -318,6 +333,8 @@
+               /* info.si_code has been set above */
+               info.si_addr = (void *)address;
+               force_sig_info(SIGSEGV, &info, tsk);
++              printk(KERN_NOTICE "%s (pid %d) segfaults for page address %08lx at pc %08lx\n",
++                     tsk->comm, tsk->pid, address, instruction_pointer(regs));
+               return;
+       }
+@@ -325,7 +342,7 @@
+       /* Are we prepared to handle this kernel fault?
+        *
+-       * (The kernel has valid exception-points in the source 
++       * (The kernel has valid exception-points in the source
+        *  when it acesses user-memory. When it fails in one
+        *  of those points, we find it in a table and do a jump
+        *  to some fixup code that loads an appropriate error
+@@ -340,13 +357,17 @@
+        * terminate things with extreme prejudice.
+        */
+-      if ((unsigned long) (address) < PAGE_SIZE)
+-              raw_printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
+-      else
+-              raw_printk(KERN_ALERT "Unable to handle kernel access");
+-      raw_printk(" at virtual address %08lx\n",address);
++      if (!oops_in_progress) {
++              oops_in_progress = 1;
++              if ((unsigned long) (address) < PAGE_SIZE)
++                      printk(KERN_ALERT "Unable to handle kernel NULL pointer dereference");
++              else
++                      printk(KERN_ALERT "Unable to handle kernel access");
++              printk(" at virtual address %08lx\n",address);
+-      die_if_kernel("Oops", regs, (writeaccess << 1) | protection);
++              die_if_kernel("Oops", regs, (writeaccess << 1) | protection);
++              oops_in_progress = 0;
++      }
+       do_exit(SIGKILL);
+@@ -405,8 +426,8 @@
+               /* Since we're two-level, we don't need to do both
+                * set_pgd and set_pmd (they do the same thing). If
+                * we go three-level at some point, do the right thing
+-               * with pgd_present and set_pgd here. 
+-               * 
++               * with pgd_present and set_pgd here.
++               *
+                * Also, since the vmalloc area is global, we don't
+                * need to copy individual PTE's, it is enough to
+                * copy the pgd pointer into the pte page of the
+diff -urN linux-2.6.19.2.old/arch/cris/mm/init.c linux-2.6.19.2.dev/arch/cris/mm/init.c
+--- linux-2.6.19.2.old/arch/cris/mm/init.c     2007-01-10 20:10:37.000000000 +0100
++++ linux-2.6.19.2.dev/arch/cris/mm/init.c     2006-06-25 17:00:10.000000000 +0200
+@@ -7,6 +7,15 @@
+  *  Authors:  Bjorn Wesen (bjornw@axis.com)
+  *
+  *  $Log: init.c,v $
++ *  Revision 1.14  2006/06/25 15:00:10  starvik
++ *  Merge of Linux 2.6.17
++ *
++ *  Revision 1.13  2005/06/20 05:30:00  starvik
++ *  Remove unnecessary diff to kernel.org tree
++ *
++ *  Revision 1.12  2004/08/16 12:37:24  starvik
++ *  Merge of Linux 2.6.8
++ *
+  *  Revision 1.11  2004/05/28 09:28:56  starvik
+  *  Calculation of loops_per_usec moved because initalization order has changed
+  *  in Linux 2.6.