## Automatically generated incremental diff
## From:   linux-2.4.21-bk5
## To:     linux-2.4.21-bk6
## Robot:  $Id: make-incremental-diff,v 1.11 2002/02/20 02:59:33 hpa Exp $

diff -urN linux-2.4.21-bk5/Documentation/Configure.help linux-2.4.21-bk6/Documentation/Configure.help
--- linux-2.4.21-bk5/Documentation/Configure.help	2003-07-10 02:54:54.000000000 -0700
+++ linux-2.4.21-bk6/Documentation/Configure.help	2003-07-10 02:54:58.000000000 -0700
@@ -15938,30 +15938,6 @@
 
   If unsure, say N.
 
-Allow direct I/O on files in NFS
-CONFIG_NFS_DIRECTIO
-  There are important applications whose performance or correctness
-  depends on uncached access to file data.  Database clusters (multiple
-  copies of the same instance running on separate hosts) implement their
-  own cache coherency protocol that subsumes the NFS cache protocols.
-  Applications that process datasets considerably larger than the client's
-  memory do not always benefit from a local cache.  A streaming video
-  server, for instance, has no need to cache the contents of a file.
-
-  This option enables applications to perform direct I/O on files in NFS
-  file systems using the O_DIRECT open() flag.  When O_DIRECT is set for
-  files, their data is not cached in the system's page cache.  Direct
-  read and write operations are aligned to block boundaries.  Data is
-  moved to and from user-level application buffers directly.
-
-  Unless your program is designed to use O_DIRECT properly, you are much
-  better off allowing the NFS client to manage caching for you.  Misusing
-  O_DIRECT can cause poor server performance or network storms.  This
-  kernel build option defaults OFF to avoid exposing system administrators
-  unwittingly to a potentially hazardous feature.
-
-  If unsure, say N.
-
 Root file system on NFS
 CONFIG_ROOT_NFS
   If you want your Linux box to mount its whole root file system (the
@@ -16118,7 +16094,7 @@
   say M here and read <file:Documentation/modules.txt>.  If unsure,
   say N.
 
-Apple Macintosh file system support
+Apple HFS file system support
 CONFIG_HFS_FS
   If you say Y here, you will be able to mount Macintosh-formatted
   floppy disks and hard drive partitions with full read-write access.
@@ -16131,6 +16107,21 @@
   compile it as a module, say M here and read
   <file:Documentation/modules.txt>.
 
+Apple HFS+ (Extended HFS) file system support
+CONFIG_HFSPLUS_FS
+  If you say Y here, you will be able to mount extended format
+  Macintosh-formatted hard drive partitions with full read-write access.
+
+  This file system is often called HFS+ and was introduced with
+  MacOS 8. It includes all Mac specific filesystem data such as
+  data forks and creator codes, but it also has several UNIX
+  style features such as file ownership and permissions.
+
+  This file system is also available as a module ( = code which can
+  be inserted in and removed from the running kernel whenever you
+  want). The module is called hfsplus.o. If you want to compile it
+  as a module, say M here and read Documentation/modules.txt.
+
 ROM file system support
 CONFIG_ROMFS_FS
   This is a very small read-only file system mainly intended for
diff -urN linux-2.4.21-bk5/Documentation/kernel-parameters.txt linux-2.4.21-bk6/Documentation/kernel-parameters.txt
--- linux-2.4.21-bk5/Documentation/kernel-parameters.txt	2003-07-10 02:54:54.000000000 -0700
+++ linux-2.4.21-bk6/Documentation/kernel-parameters.txt	2003-07-10 02:54:58.000000000 -0700
@@ -503,7 +503,7 @@
 	plip=		[PPT,NET] Parallel port network link.
 
 	profile=	[KNL] enable kernel profiling via /proc/profile
-			(param:log level).
+			(param: profile step/bucket size as a power of 2)
 
 	prompt_ramdisk=	[RAM] List of RAM disks to prompt for floppy disk
 			before loading.
diff -urN linux-2.4.21-bk5/Makefile linux-2.4.21-bk6/Makefile
--- linux-2.4.21-bk5/Makefile	2003-07-10 02:54:54.000000000 -0700
+++ linux-2.4.21-bk6/Makefile	2003-07-10 02:54:58.000000000 -0700
@@ -1,7 +1,7 @@
 VERSION = 2
 PATCHLEVEL = 4
 SUBLEVEL = 21
-EXTRAVERSION = -bk5
+EXTRAVERSION = -bk6
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
diff -urN linux-2.4.21-bk5/arch/cris/Makefile linux-2.4.21-bk6/arch/cris/Makefile
--- linux-2.4.21-bk5/arch/cris/Makefile	2002-08-02 17:39:42.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/Makefile	2003-07-10 02:54:58.000000000 -0700
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.27 2002/03/08 13:08:50 bjornw Exp $
+#
 # cris/Makefile
 #
 # This file is included by the global makefile so that you can add your own
@@ -56,6 +56,10 @@
 # each others config options
 SUBDIRS += arch/cris/boot/rescue
 endif
+
+# Note that the order of .o files below is important is some cases
+# (because initcalls are executed in the order they are linked)
+# Never add any driver before mm.o!
 CORE_FILES += arch/cris/kernel/kernel.o arch/cris/mm/mm.o
 DRIVERS += arch/cris/drivers/drivers.o
 LIBGCC = $(shell $(CC) $(CFLAGS) -print-file-name=libgcc.a)
diff -urN linux-2.4.21-bk5/arch/cris/boot/compressed/README linux-2.4.21-bk6/arch/cris/boot/compressed/README
--- linux-2.4.21-bk5/arch/cris/boot/compressed/README	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.21-bk6/arch/cris/boot/compressed/README	2003-07-10 02:54:58.000000000 -0700
@@ -1,6 +1,5 @@
 Creation of the self-extracting compressed kernel image (vmlinuz)
 -----------------------------------------------------------------
-$Id: README,v 1.1 2000/11/22 17:20:46 bjornw Exp $
 
 This can be slightly confusing because it's a process with many steps.
 
diff -urN linux-2.4.21-bk5/arch/cris/boot/compressed/misc.c linux-2.4.21-bk6/arch/cris/boot/compressed/misc.c
--- linux-2.4.21-bk5/arch/cris/boot/compressed/misc.c	2001-05-01 16:04:56.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/boot/compressed/misc.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,19 +1,17 @@
 /*
  * misc.c
  *
- * $Id: misc.c,v 1.6 2001/04/09 10:00:21 starvik Exp $
- * 
- * This is a collection of several routines from gzip-1.0.3 
+ * 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.
- * it is the same adress we use when we network load an uncompressed
+ * it is the same address we use when we network load an uncompressed
  * image into DRAM, and it is the address the kernel is linked to live
  * at by etrax100.ld.
  */
@@ -65,8 +63,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);}
@@ -95,20 +93,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)
@@ -196,9 +194,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);
@@ -229,7 +227,7 @@
 decompress_kernel()
 {
 	char revision;
-	
+
 	/* input_data is set in head.S */
 	inbuf = input_data;
 
diff -urN linux-2.4.21-bk5/arch/cris/boot/rescue/head.S linux-2.4.21-bk6/arch/cris/boot/rescue/head.S
--- linux-2.4.21-bk5/arch/cris/boot/rescue/head.S	2002-02-25 11:37:52.000000000 -0800
+++ linux-2.4.21-bk6/arch/cris/boot/rescue/head.S	2003-07-10 02:54:58.000000000 -0700
@@ -1,5 +1,4 @@
-/* $Id: head.S,v 1.10 2001/11/08 15:10:16 starvik Exp $
- * 
+/*
  * Rescue code, made to reside at the beginning of the
  * flash-memory. when it starts, it checks a partition
  * table at the first sector after the rescue sector.
@@ -52,7 +51,7 @@
  * 
  * Bit 0 in flags signifies RW or RO. The rescue code only bothers
  * to check the checksum for RO partitions, since the others will
- * change its data without updating the checksums. A 1 in bit 0
+ * change their data without updating the checksums. A 1 in bit 0
  * means RO, 0 means RW. That way, it is possible to set a partition
  * in RO mode initially, and later mark it as RW, since you can always
  * write 0's to the flash.
@@ -60,7 +59,7 @@
  * During the wait for serial input, the status LED will flash so the
  * user knows something went wrong.
  * 
- * Copyright (C) 1999,2001 Axis Communications AB
+ * Copyright (C) 1999, 2000, 2001, 2002, 2003 Axis Communications AB
  */
 
 #include <linux/config.h>
@@ -130,7 +129,7 @@
 
 	;; first put a jump test to give a possibility of upgrading the rescue code
 	;; without erasing/reflashing the sector. we put a longword of -1 here and if
-	;; its not -1, we jump using the value as jump target. since we can always
+	;; it is not -1, we jump using the value as jump target. since we can always
 	;; change 1's to 0's without erasing the sector, it is possible to add new
 	;; code after this and altering the jumptarget in an upgrade.
 
@@ -150,7 +149,20 @@
 	move.d 0x3, $r0    ; enable = on, phy = mii_clk
 	move.d $r0, [R_NETWORK_GEN_CONFIG]
 #endif
+
+	;; Setup port PA and PB default initial directions and data
+	;; (so we can flash LEDs, and so that DTR and others are set)
+	
+	move.b	CONFIG_ETRAX_DEF_R_PORT_PA_DIR, $r0
+	move.b	$r0, [R_PORT_PA_DIR]
+	move.b	CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r0
+	move.b	$r0, [R_PORT_PA_DATA]
 	
+	move.b	CONFIG_ETRAX_DEF_R_PORT_PB_DIR, $r0
+	move.b	$r0, [R_PORT_PB_DIR]
+	move.b	CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r0
+	move.b	$r0, [R_PORT_PB_DATA]
+
 	;; We need to setup the bus registers before we start using the DRAM
 #include "../../lib/dram_init.S"
 
@@ -192,7 +204,7 @@
 	moveq	-1, $r7
 	
 ploop:	move.d	[$r3+], $r1	; partition offset (from ptable start)
-	bne	notfirst	; check if its the partition containing ptable
+	bne	notfirst	; check if it is the partition containing ptable
 	nop			; yes..
 	move.d	$r8, $r1	; for its checksum check, skip the ptable
 	move.d	[$r3+], $r2	; partition length
@@ -226,18 +238,6 @@
 	;; otherwise fall through to the rescue code.
 	
 do_rescue:
-	;; setup port PA and PB default initial directions and data
-	;; (so we can flash LEDs, and so that DTR and others are set)
-	
-	move.b	CONFIG_ETRAX_DEF_R_PORT_PA_DIR, $r0
-	move.b	$r0, [R_PORT_PA_DIR]
-	move.b	CONFIG_ETRAX_DEF_R_PORT_PA_DATA, $r0
-	move.b	$r0, [R_PORT_PA_DATA]
-	
-	move.b	CONFIG_ETRAX_DEF_R_PORT_PB_DIR, $r0
-	move.b	$r0, [R_PORT_PB_DIR]
-	move.b	CONFIG_ETRAX_DEF_R_PORT_PB_DATA, $r0
-	move.b	$r0, [R_PORT_PB_DATA]
 
 	;; setup the serial port at 115200 baud
 	
diff -urN linux-2.4.21-bk5/arch/cris/boot/rescue/kimagerescue.S linux-2.4.21-bk6/arch/cris/boot/rescue/kimagerescue.S
--- linux-2.4.21-bk5/arch/cris/boot/rescue/kimagerescue.S	2001-10-08 11:43:54.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/boot/rescue/kimagerescue.S	2003-07-10 02:54:58.000000000 -0700
@@ -1,5 +1,4 @@
-/* $Id: kimagerescue.S,v 1.5 2001/10/03 17:15:15 bjornw Exp $
- * 
+/*
  * Rescue code to be prepended on a kimage and copied to the
  * rescue serial port.
  * This is called from the rescue code, it will copy received data to
diff -urN linux-2.4.21-bk5/arch/cris/boot/rescue/testrescue.S linux-2.4.21-bk6/arch/cris/boot/rescue/testrescue.S
--- linux-2.4.21-bk5/arch/cris/boot/rescue/testrescue.S	2001-10-08 11:43:54.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/boot/rescue/testrescue.S	2003-07-10 02:54:58.000000000 -0700
@@ -1,9 +1,8 @@
-/* $Id: testrescue.S,v 1.3 2001/10/03 17:15:15 bjornw Exp $
- *
+/*
  * Simple testcode to download by the rescue block.
  * Just lits some LEDs to show it was downloaded correctly.
- * 
- * Copyright (C) 1999 Axis Communications AB
+ *
+ * Copyright (C) 1999-2003 Axis Communications AB
  */
 
 #define ASSEMBLER_MACROS_ONLY
@@ -16,11 +15,9 @@
 	moveq	-1, $r2
 	move.b	$r2, [R_PORT_PA_DIR]
 	moveq	0, $r2
-	move.b	$r2, [R_PORT_PA_DATA]	
+	move.b	$r2, [R_PORT_PA_DATA]
 
 endless:
 	nop
 	ba	endless
 	nop
-
-	
diff -urN linux-2.4.21-bk5/arch/cris/config.in linux-2.4.21-bk6/arch/cris/config.in
--- linux-2.4.21-bk5/arch/cris/config.in	2003-07-10 02:54:54.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/config.in	2003-07-10 02:54:58.000000000 -0700
@@ -32,14 +32,27 @@
 
 tristate 'Kernel support for ELF binaries' CONFIG_BINFMT_ELF
 
+string 'Kernel command line' CONFIG_ETRAX_CMDLINE "root=/dev/mtdblock3"
+
 bool 'Use kernel gdb debugger' CONFIG_ETRAX_KGDB
 
-bool 'Enable Etrax100 watchdog' CONFIG_ETRAX_WATCHDOG
+bool 'Use Artpec gdb debugger' CONFIG_ETRAX_AGDB
+
+bool 'Enable ETRAX watchdog' CONFIG_ETRAX_WATCHDOG
 if [ "$CONFIG_ETRAX_WATCHDOG" = "y" ]; then
-	bool 'Disable watchdog during Oops printouts' CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
+	bool '  Disable watchdog during Oops printouts' CONFIG_ETRAX_WATCHDOG_NICE_DOGGY
 fi
 
+bool 'Enable ETRAX interrupt debugging' CONFIG_ETRAX_DEBUG_INTERRUPT
+
 bool 'Enable ETRAX fast timer API' CONFIG_ETRAX_FAST_TIMER
+if [ "$CONFIG_ETRAX_FAST_TIMER" = "y" ]; then
+	bool '  Enable fast timer logging' CONFIG_ETRAX_FAST_TIMER_LOGGING
+	bool '  Enable fast timer debug' CONFIG_ETRAX_FAST_TIMER_DEBUG
+	bool '  Enable fast timer sanity checks' CONFIG_ETRAX_FAST_TIMER_SANITY_CHECKS
+fi
+
+bool 'Enable reboot at out of memory' CONFIG_OOM_REBOOT
 
 endmenu
 
@@ -51,7 +64,7 @@
 	 Etrax-100-LX-v2	CONFIG_ETRAX100LX_V2	\
 	 Etrax-100-LX-for-xsim-simulator   CONFIG_SVINTO_SIM" Etrax-100-LX-v1
 
-# Etrax100 LX v1 has a MMU "feature" requiring a low mapping
+# ETRAX 100LX v1 has a MMU "feature" requiring a low mapping
 
 if [ "$CONFIG_ETRAX100LX" = "y" ]; then
 	define_bool CONFIG_CRIS_LOW_MAP y
@@ -64,8 +77,6 @@
 
 int 'Buswidth of flash in bytes' CONFIG_ETRAX_FLASH_BUSWIDTH 2
 
-string 'Root device name' CONFIG_ETRAX_ROOT_DEVICE "/dev/mtdblock3"
-
 choice 'Product LED port' \
 	"Port-PA-LEDs	CONFIG_ETRAX_PA_LEDS	\
 	 Port-PB-LEDs	CONFIG_ETRAX_PB_LEDS	\
@@ -73,36 +84,36 @@
 	 None	CONFIG_ETRAX_NO_LEDS" Port-PA-LEDs
 
 if [ "$CONFIG_ETRAX_NO_LEDS" != "y" ]; then
-  int '  First green LED bit' CONFIG_ETRAX_LED1G 2
-  int '  First red LED bit' CONFIG_ETRAX_LED1R 3
-  int '  Second green LED bit' CONFIG_ETRAX_LED2G 4
-  int '  Second red LED bit' CONFIG_ETRAX_LED2R 5
-  int '  Third green LED bit' CONFIG_ETRAX_LED3G 2
-  int '  Third red LED bit' CONFIG_ETRAX_LED3R 2
+	int '  First green LED bit' CONFIG_ETRAX_LED1G 2
+	int '  First red LED bit' CONFIG_ETRAX_LED1R 3
+	int '  Second green LED bit' CONFIG_ETRAX_LED2G 4
+	int '  Second red LED bit' CONFIG_ETRAX_LED2R 5
+	int '  Third green LED bit' CONFIG_ETRAX_LED3G 2
+	int '  Third red LED bit' CONFIG_ETRAX_LED3R 2
 fi
 
 if [ "$CONFIG_ETRAX_CSP0_LEDS" = "y" ]; then
-  int '  Fourth red LED bit' CONFIG_ETRAX_LED4R 2
-  int '  Fourth green LED bit' CONFIG_ETRAX_LED4G 2
-  int '  Fifth red LED bit' CONFIG_ETRAX_LED5R 2
-  int '  Fifth green LED bit' CONFIG_ETRAX_LED5G 2
-  int '  Sixth red LED bit' CONFIG_ETRAX_LED6R 2
-  int '  Sixth green LED bit' CONFIG_ETRAX_LED6G 2
-  int '  Seventh red LED bit' CONFIG_ETRAX_LED7R 2
-  int '  Seventh green LED bit' CONFIG_ETRAX_LED7G 2
-  int '  Eigth yellow LED bit' CONFIG_ETRAX_LED8Y 2
-  int '  Ninth yellow LED bit' CONFIG_ETRAX_LED9Y 2
-  int '  Tenth yellow LED bit' CONFIG_ETRAX_LED10Y 2
-  int '  Eleventh yellow LED bit' CONFIG_ETRAX_LED11Y 2
-  int '  Twelfth red LED bit' CONFIG_ETRAX_LED12R 2
-fi  
+	int '  Fourth red LED bit' CONFIG_ETRAX_LED4R 2
+	int '  Fourth green LED bit' CONFIG_ETRAX_LED4G 2
+	int '  Fifth red LED bit' CONFIG_ETRAX_LED5R 2
+	int '  Fifth green LED bit' CONFIG_ETRAX_LED5G 2
+	int '  Sixth red LED bit' CONFIG_ETRAX_LED6R 2
+	int '  Sixth green LED bit' CONFIG_ETRAX_LED6G 2
+	int '  Seventh red LED bit' CONFIG_ETRAX_LED7R 2
+	int '  Seventh green LED bit' CONFIG_ETRAX_LED7G 2
+	int '  Eigth yellow LED bit' CONFIG_ETRAX_LED8Y 2
+	int '  Ninth yellow LED bit' CONFIG_ETRAX_LED9Y 2
+	int '  Tenth yellow LED bit' CONFIG_ETRAX_LED10Y 2
+	int '  Eleventh yellow LED bit' CONFIG_ETRAX_LED11Y 2
+	int '  Twelfth red LED bit' CONFIG_ETRAX_LED12R 2
+fi
 
 choice 'Product debug-port' \
 	"Serial-0	CONFIG_ETRAX_DEBUG_PORT0	\
 	 Serial-1	CONFIG_ETRAX_DEBUG_PORT1	\
 	 Serial-2	CONFIG_ETRAX_DEBUG_PORT2	\
 	 Serial-3	CONFIG_ETRAX_DEBUG_PORT3	\
-	 disabled	CONFIG_ETRAX_DEBUG_PORT_NULL" Serial-0 
+	 disabled	CONFIG_ETRAX_DEBUG_PORT_NULL" Serial-0
 
 choice 'Product rescue-port' \
 	"Serial-0	CONFIG_ETRAX_RESCUE_SER0	\
@@ -115,13 +126,13 @@
 
 bool 'SDRAM support' CONFIG_ETRAX_SDRAM
 if [ "$CONFIG_ETRAX_SDRAM" = "n" ]; then
-	hex 'R_DRAM_CONFIG' CONFIG_ETRAX_DEF_R_DRAM_CONFIG 1a200040
-	hex 'R_DRAM_TIMING' CONFIG_ETRAX_DEF_R_DRAM_TIMING 5611
+	hex '  R_DRAM_CONFIG' CONFIG_ETRAX_DEF_R_DRAM_CONFIG 1a200040
+	hex '  R_DRAM_TIMING' CONFIG_ETRAX_DEF_R_DRAM_TIMING 5611
 fi
 
 if [ "$CONFIG_ETRAX_SDRAM" = "y" ]; then
-	hex 'R_SDRAM_CONFIG' CONFIG_ETRAX_DEF_R_SDRAM_CONFIG d2fa7878
-	hex 'R_SDRAM_TIMING' CONFIG_ETRAX_DEF_R_SDRAM_TIMING 80004801
+	hex '  R_SDRAM_CONFIG' CONFIG_ETRAX_DEF_R_SDRAM_CONFIG d2fa7878
+	hex '  R_SDRAM_TIMING' CONFIG_ETRAX_DEF_R_SDRAM_TIMING 80004801
 fi
 
 hex 'R_PORT_PA_DIR' CONFIG_ETRAX_DEF_R_PORT_PA_DIR 1c
@@ -132,13 +143,13 @@
 
 bool 'Software Shutdown Support' CONFIG_ETRAX_SOFT_SHUTDOWN
 if [ "$CONFIG_ETRAX_SOFT_SHUTDOWN" = "y" ]; then
-   int 'Shutdown bit on port CSP0' CONFIG_ETRAX_SHUTDOWN_BIT 12
-   int 'Power button bit on port G' CONFIG_ETRAX_POWERBUTTON_BIT 25
+   int '  Shutdown bit on port CSP0' CONFIG_ETRAX_SHUTDOWN_BIT 12
+   int '  Power button bit on port G' CONFIG_ETRAX_POWERBUTTON_BIT 25
 fi
 
 endmenu
 
-# bring in Etrax built-in drivers
+# bring in ETRAX built-in drivers
 
 source arch/cris/drivers/Config.in
 
@@ -258,7 +269,7 @@
 #bool 'Debug kmalloc/kfree' CONFIG_DEBUG_MALLOC
 bool 'Kernel profiling support' CONFIG_PROFILE
 if [ "$CONFIG_PROFILE" = "y" ]; then
-  int ' Profile shift count' CONFIG_PROFILE_SHIFT 2
+  int '  Profile shift count' CONFIG_PROFILE_SHIFT 2
 fi
 
 source crypto/Config.in
diff -urN linux-2.4.21-bk5/arch/cris/defconfig linux-2.4.21-bk6/arch/cris/defconfig
--- linux-2.4.21-bk5/arch/cris/defconfig	2002-08-02 17:39:42.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/defconfig	2003-07-10 02:54:58.000000000 -0700
@@ -85,8 +85,12 @@
 # CONFIG_ETRAX_ETHERNET_LPSLAVE is not set
 CONFIG_ETRAX_SERIAL=y
 CONFIG_ETRAX_SERIAL_PORT0=y
+CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT=y
+CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN=y
 # CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PB is not set
 CONFIG_ETRAX_SERIAL_PORT1=y
+CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT=y
+CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN=y
 # CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PB is not set
 # CONFIG_ETRAX_SERIAL_PORT2 is not set
 # CONFIG_ETRAX_SERIAL_PORT3 is not set
diff -urN linux-2.4.21-bk5/arch/cris/drivers/Config.in linux-2.4.21-bk6/arch/cris/drivers/Config.in
--- linux-2.4.21-bk5/arch/cris/drivers/Config.in	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/Config.in	2003-07-10 02:54:58.000000000 -0700
@@ -23,15 +23,21 @@
 
 bool 'Serial-port support' CONFIG_ETRAX_SERIAL
 if [ "$CONFIG_ETRAX_SERIAL" = "y" ]; then
-  bool '  Use fast timers for serial DMA flush (experimental)' CONFIG_ETRAX_SERIAL_FAST_TIMER
+  bool '  Use fast timers for serial DMA flush' CONFIG_ETRAX_SERIAL_FAST_TIMER
   if [ "$CONFIG_ETRAX_SERIAL_FAST_TIMER" = "n" ]; then
     bool '  Fast serial port DMA flush' CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
     if [ "$CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST" = "n" ]; then
       int '  Receive flush timeout (ticks) ' CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5
     fi
   fi
+  bool '  Enable external clock on PB6' CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED
+  if [ "$CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED" = "y" ]; then
+    int '  Extern clock frequency (baudrate=clk/8) (Hz)' CONFIG_ETRAX_EXTERN_PB6CLK_FREQ
+  fi
   bool '  Serial port 0 enabled' CONFIG_ETRAX_SERIAL_PORT0
   if [ "$CONFIG_ETRAX_SERIAL_PORT0" = "y" ]; then
+  bool '  Serial port 0 uses DMA6 out' CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
+  bool '  Serial port 0 uses DMA7 in' CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
   choice 'Ser0 DTR, RI, DSR and CD assignment' \
   "No_DTR_RI_DSR_CD      CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_NONE \
    DTR_RI_DSR_CD_on_PA   CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_ON_PA \
@@ -67,6 +73,8 @@
 
   bool '  Serial port 1 enabled' CONFIG_ETRAX_SERIAL_PORT1
   if [ "$CONFIG_ETRAX_SERIAL_PORT1" = "y" ]; then
+  bool '  Serial port 1 uses DMA8 out' CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
+  bool '  Serial port 1 uses DMA9 in' CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
   choice 'Ser1 DTR, RI, DSR and CD assignment' \
   "No_DTR_RI_DSR_CD      CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_NONE \
    DTR_RI_DSR_CD_on_PA   CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_ON_PA \
@@ -106,6 +114,8 @@
   fi
   bool '  Serial port 2 enabled' CONFIG_ETRAX_SERIAL_PORT2
   if [ "$CONFIG_ETRAX_SERIAL_PORT2" = "y" ]; then
+  bool '  Serial port 2 uses DMA2 out' CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+  bool '  Serial port 2 uses DMA3 in' CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
   choice 'Ser2 DTR, RI, DSR and CD assignment' \
   "No_DTR_RI_DSR_CD      CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_NONE \
    DTR_RI_DSR_CD_on_PA   CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_ON_PA \
@@ -139,6 +149,8 @@
   fi
   bool '  Serial port 3 enabled' CONFIG_ETRAX_SERIAL_PORT3
   if [ "$CONFIG_ETRAX_SERIAL_PORT3" = "y" ]; then
+  bool '  Serial port 3 uses DMA4 out' CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
+  bool '  Serial port 3 uses DMA5 in' CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
   choice 'Ser3 DTR, RI, DSR and CD assignment' \
   "No_DTR_RI_DSR_CD      CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_ON_NONE \
    DTR_RI_DSR_CD_on_PA   CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_ON_PA \
@@ -303,6 +315,7 @@
 
 bool 'Real Time Clock support' CONFIG_ETRAX_RTC
 if [ "$CONFIG_ETRAX_RTC" = "y" ]; then
+  bool '  Enable RTC read only mode' CONFIG_ETRAX_RTC_READONLY	 
   choice '  RTC chip' \
     "DS1302 CONFIG_ETRAX_DS1302 \
      PCF8563  CONFIG_ETRAX_PCF8563" DS1302
diff -urN linux-2.4.21-bk5/arch/cris/drivers/axisflashmap.c linux-2.4.21-bk6/arch/cris/drivers/axisflashmap.c
--- linux-2.4.21-bk5/arch/cris/drivers/axisflashmap.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/axisflashmap.c	2003-07-10 02:54:58.000000000 -0700
@@ -11,6 +11,9 @@
  * partition split defined below.
  *
  * $Log: axisflashmap.c,v $
+ * Revision 1.29  2003/04/01 14:12:06  starvik
+ * Added loglevel for lots of printks
+ *
  * Revision 1.28  2002/10/01 08:08:43  jonashg
  * The first partition ends at the start of the partition table.
  *
@@ -300,7 +303,8 @@
 {
 	struct mtd_info *mtd_cs = NULL;
 
-	printk("%s: Probing a 0x%08lx bytes large window at 0x%08lx.\n",
+	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
@@ -387,7 +391,7 @@
 	struct partitiontable_head *ptable_head;
 	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 = KERN_INFO "  /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
@@ -396,7 +400,7 @@
 		panic("axisflashmap found no flash chip!\n");
 	}
 
-	printk("%s: 0x%08x bytes of flash memory.\n",
+	printk(KERN_INFO "%s: 0x%08x bytes of flash memory.\n",
 	       mymtd->name, mymtd->size);
 
 	mymtd->module = THIS_MODULE;
@@ -440,7 +444,7 @@
 		ptable_ok = (csum == ptable_head->checksum);
 
 		/* Read the entries and use/show the info.  */
-		printk(" Found a%s partition table at 0x%p-0x%p.\n",
+		printk(KERN_INFO " Found a%s partition table at 0x%p-0x%p.\n",
 		       (ptable_ok ? " valid" : "n invalid"), ptable_head,
 		       max_addr);
 
@@ -472,14 +476,14 @@
 		axis_partitions[pidx].offset = romfs_start - FLASH_CACHED_ADDR;
 		axis_partitions[pidx].mask_flags |= MTD_WRITEABLE;
 
-		printk(" Adding readonly flash partition for romfs image:\n");
+		printk(KERN_INFO " Adding readonly flash partition for romfs image:\n");
 		printk(pmsg, pidx, axis_partitions[pidx].offset,
 		       axis_partitions[pidx].size);
 		pidx++;
 	}
 
 	if (use_default_ptable) {
-		printk(" Using default partition table.\n");
+		printk(KERN_INFO " Using default partition table.\n");
 		err = add_mtd_partitions(mymtd, axis_default_partitions,
 		                         NUM_DEFAULT_PARTITIONS);
 	} else {
@@ -508,7 +512,7 @@
 			      "mtd_info!\n");
 		}
 
-		printk(" Adding RAM partition for romfs image:\n");
+		printk(KERN_INFO " Adding RAM partition for romfs image:\n");
 		printk(pmsg, pidx, romfs_start, romfs_length);
 
 		err = mtdram_init_device(mtd_ram, (void*)romfs_start, 
diff -urN linux-2.4.21-bk5/arch/cris/drivers/ds1302.c linux-2.4.21-bk6/arch/cris/drivers/ds1302.c
--- linux-2.4.21-bk5/arch/cris/drivers/ds1302.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/ds1302.c	2003-07-10 02:54:58.000000000 -0700
@@ -4,9 +4,22 @@
 *!
 *! DESCRIPTION: Implements an interface for the DS1302 RTC through Etrax I/O
 *!
-*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init, get_rtc_status
+*! Functions exported: ds1302_readreg, ds1302_writereg, ds1302_init
 *!
 *! $Log: ds1302.c,v $
+*! Revision 1.19  2003/06/12 08:02:05  johana
+*! Removed faulty comma from printk.
+*! Fixed warning () -> (void)
+*!
+*! Revision 1.18  2003/04/16 09:02:28  oskarp
+*! * Merged change_branch--rtc_readonly to main.
+*!
+*! Revision 1.17  2003/04/01 14:12:06  starvik
+*! Added loglevel for lots of printks
+*!
+*! Revision 1.16  2003/02/19 15:51:16  cedric
+*! Start the clock from ds1302_init()
+*!
 *! Revision 1.15  2002/10/11 16:14:33  johana
 *! Added CONFIG_ETRAX_DS1302_TRICKLE_CHARGE and initial setting of the
 *! trcklecharge register.
@@ -95,7 +108,7 @@
 *!
 *! (C) Copyright 1999, 2000, 2001  Axis Communications AB, LUND, SWEDEN
 *!
-*! $Id: ds1302.c,v 1.15 2002/10/11 16:14:33 johana Exp $
+*! $Id: ds1302.c,v 1.19 2003/06/12 08:02:05 johana Exp $
 *!
 *!***************************************************************************/
 
@@ -263,12 +276,23 @@
 void
 ds1302_writereg(int reg, unsigned char val) 
 {
-	ds1302_wenable();
-	start();
-	out_byte(0x80 | (reg << 1)); /* write register */
-	out_byte(val);
-	stop();
-	ds1302_wdisable();
+#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();
+		start();
+		out_byte(0x80 | (reg << 1)); /* write register */
+		out_byte(val);
+		stop();
+		ds1302_wdisable();
+	}
 }
 
 void
@@ -405,20 +429,33 @@
 			tcs_val = RTC_TCR_PATTERN | (tcs_val & 0x0F);
 			ds1302_writereg(RTC_TRICKLECHARGER, tcs_val);
 			return 0;
-		}                
+		}
+		case RTC_VLOW_RD:
+		{
+			/* TODO: 
+			 * Implement voltage low detection support 
+			 */
+			printk(KERN_WARNING "DS1302: RTC Voltage Low detection"
+			       " is not supported\n");
+			return 0;
+		}
+		case RTC_VLOW_SET:
+		{
+			/* TODO:
+			 * Nothing to do since Voltage Low detection is not supported
+			 */
+			return 0;
+		}
 		default:
 			return -ENOIOCTLCMD;
 	}
 }
 
-int
-get_rtc_status(char *buf) 
+static void
+print_rtc_status(void) 
 {
-	char *p;
 	struct rtc_time tm;
 
-	p = buf;
-
 	get_rtc_time(&tm);
 
 	/*
@@ -426,13 +463,10 @@
 	 * time or for Universal Standard Time (GMT). Probably local though.
 	 */
 
-	p += sprintf(p,
-		"rtc_time\t: %02d:%02d:%02d\n"
-		"rtc_date\t: %04d-%02d-%02d\n",
-		tm.tm_hour, tm.tm_min, tm.tm_sec,
-		tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
-
-	return  p - buf;
+	printk(KERN_INFO "rtc_time\t: %02d:%02d:%02d\n",
+	       tm.tm_hour, tm.tm_min, tm.tm_sec);
+	printk(KERN_INFO "rtc_date\t: %04d-%02d-%02d\n",
+	       tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
 }
 
 
@@ -466,11 +500,10 @@
 	out_byte(0xc1); /* read RAM byte 0 */
 
 	if((res = in_byte()) == MAGIC_PATTERN) {
-		char buf[100];
 		stop();
 		ds1302_wdisable();
-		printk("%s: RTC found.\n", ds1302_name);
-		printk("%s: SDA, SCL, RST on PB%i, PB%i, %s%i\n",
+		printk(KERN_INFO "%s: RTC found.\n", ds1302_name);
+		printk(KERN_INFO "%s: SDA, SCL, RST on PB%i, PB%i, %s%i\n",
 		       ds1302_name,
 		       CONFIG_ETRAX_DS1302_SDABIT,
 		       CONFIG_ETRAX_DS1302_SCLBIT,
@@ -480,12 +513,10 @@
 		       "PB",
 #endif
 		       CONFIG_ETRAX_DS1302_RSTBIT);
-                get_rtc_status(buf);
-                printk(buf);
+                print_rtc_status();
 		retval = 1;
 	} else {
 		stop();
-		printk("%s: RTC not found.\n", ds1302_name);
 		retval = 0;
 	}
 
@@ -518,9 +549,12 @@
 				   (IO_STATE(R_GEN_CONFIG, g0dir, out)));    
     		*R_GEN_CONFIG = genconfig_shadow;
 #endif
-		if (!ds1302_probe())
+		if (!ds1302_probe()) {
+			printk(KERN_WARNING "%s: RTC not found.\n", ds1302_name);
       			return -1;
+		}
 #else
+		printk(KERN_WARNING "%s: RTC not found.\n", ds1302_name);
     		return -1;
 #endif
   	}
@@ -533,5 +567,7 @@
 	/* Initialise trickle charger */
 	ds1302_writereg(RTC_TRICKLECHARGER,
 			RTC_TCR_PATTERN |(CONFIG_ETRAX_DS1302_TRICKLE_CHARGE & 0x0F));
+	// Start clock by resetting CLOCK_HALT
+	ds1302_writereg(RTC_SECONDS, (ds1302_readreg(RTC_SECONDS) & 0x7F));
 	return 0;
 }
diff -urN linux-2.4.21-bk5/arch/cris/drivers/eeprom.c linux-2.4.21-bk6/arch/cris/drivers/eeprom.c
--- linux-2.4.21-bk5/arch/cris/drivers/eeprom.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/eeprom.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,4 +1,4 @@
-/*!*****************************************************************************
+/******************************************************************************
 *!
 *!  Implements an interface for i2c compatible eeproms to run under linux.
 *!  Supports 2k, 8k(?) and 16k. Uses adaptive timing adjustents by
@@ -20,6 +20,18 @@
 *!                                  in the spin-lock.
 *!
 *!  $Log: eeprom.c,v $
+*!  Revision 1.12  2003/04/09 08:31:14  pkj
+*!  Typo correction (taken from Linux 2.5).
+*!
+*!  Revision 1.11  2003/02/12 20:43:46  johana
+*!  Previous checkin removed beginning of comment.
+*!
+*!  Revision 1.10  2003/02/10 07:18:20  starvik
+*!  Removed misplaced ;
+*!
+*!  Revision 1.9  2003/01/22 06:54:46  starvik
+*!  Fixed warnings issued by GCC 3.2.1
+*!
 *!  Revision 1.8  2001/06/15 13:24:29  jonashg
 *!  * Added verification of pointers from userspace in read and write.
 *!  * Made busy counter volatile.
@@ -203,7 +215,7 @@
      * it will mirror the address space:
      * 1. We read two locations (that are mirrored), 
      *    if the content differs * it's a 16kB EEPROM.
-     * 2. if it doesn't differ - write diferent value to one of the locations,
+     * 2. if it doesn't differ - write different value to one of the locations,
      *    check the other - if content still is the same it's a 2k EEPROM,
      *    restore original data.
      */
@@ -493,7 +505,7 @@
 
 static ssize_t eeprom_read(struct file * file, char * buf, size_t count, loff_t *off)
 {
-  int i, read=0;
+  int read=0;
   unsigned long p = file->f_pos;
 
   unsigned char page;
@@ -519,7 +531,7 @@
   if(!eeprom_address(p))
   {
     printk(KERN_INFO "%s: Read failed to address the eeprom: "
-           "0x%08X (%i) page: %i\n", eeprom_name, p, p, page);
+           "0x%08lX (%li) page: %i\n", eeprom_name, p, p, page);
     i2c_stop();
     
     /* don't forget to wake them up */
@@ -602,7 +614,7 @@
       if(!eeprom_address(p))
       {
         printk(KERN_INFO "%s: Write failed to address the eeprom: "
-               "0x%08X (%i) \n", eeprom_name, p, p);
+               "0x%08lX (%li) \n", eeprom_name, p, p);
         i2c_stop();
         
         /* don't forget to wake them up */
@@ -740,7 +752,7 @@
 
 static int eeprom_address(unsigned long addr)
 {
-  int i, j;
+  int i;
   unsigned char page, offset;
 
   page   = (unsigned char) (addr >> 8);
@@ -795,7 +807,7 @@
   return 1;
 }
 
-/* Reads from current adress. */
+/* Reads from current address. */
 
 static int read_from_eeprom(char * buf, int count)
 {
diff -urN linux-2.4.21-bk5/arch/cris/drivers/ethernet.c linux-2.4.21-bk6/arch/cris/drivers/ethernet.c
--- linux-2.4.21-bk5/arch/cris/drivers/ethernet.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/ethernet.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: ethernet.c,v 1.34 2002/12/13 07:17:44 starvik Exp $
+/* $Id: ethernet.c,v 1.44 2003/07/01 10:55:07 starvik Exp $
  *
  * e100net.c: A network driver for the ETRAX 100LX network controller.
  *
@@ -7,9 +7,52 @@
  * The outline of this driver comes from skeleton.c.
  *
  * $Log: ethernet.c,v $
- * Revision 1.34  2002/12/13 07:17:44  starvik
- * Basic ethtool and MII ioctls
- * Handle out of memory when allocating new buffers
+ * Revision 1.44  2003/07/01 10:55:07  starvik
+ * Never bring down link to make stupid POE equipment happy
+ *
+ * Revision 1.43  2003/04/24 08:26:50  starvik
+ * New LED behaviour: LED off when no link
+ *
+ * Revision 1.42  2003/04/10 07:14:58  starvik
+ * Merge of changes from todo list
+ *
+ * Revision 1.41  2003/04/09 08:31:14  pkj
+ * Typo correction (taken from Linux 2.5).
+ *
+ * Revision 1.40  2003/04/01 14:12:06  starvik
+ * Added loglevel for lots of printks
+ *
+ * Revision 1.39.2.3  2003/04/01 07:51:14  starvik
+ * Default Ethernet Stations Address can be specified with command line option.
+ *
+ * Revision 1.39.2.2  2003/03/31 15:41:07  starvik
+ * Only communicate with transciever when ETRAX is properly configured.
+ *
+ * Revision 1.39.2.1  2003/03/31 14:12:46  starvik
+ * Transmit interrupt always enabled. This has two side effects:
+ *   1. UDP (and possibly other protocols) works on quiet networks
+ *   2. Slightly lower transmit performance
+ *
+ * Revision 1.39  2003/03/06 15:45:39  henriken
+ * Off by one error in group address register setting.
+ *
+ * Revision 1.38  2003/02/27 17:23:07  starvik
+ * Corrected Rev to Revision
+ *
+ * Revision 1.37  2003/02/27 10:52:59  magnusmn
+ * More generic transceiver support.
+ *
+ * Revision 1.33.2.4  2003/02/24 16:51:26  magnusmn
+ * TDK specific registers to check speed/duplex
+ *
+ * Revision 1.33.2.3  2003/02/24 09:16:31  magnusmn
+ * ethernet.c
+ *
+ * Revision 1.33.2.2  2003/02/21 11:57:27  magnusmn
+ * Merging differences between 1.36 and 1.33 into ethernet.c (needed ethtool ioctl)
+ *
+ * Revision 1.33.2.1  2002/12/04 07:54:49  starvik
+ * First attempt to get more generic transceiver support
  *
  * Revision 1.33  2002/10/02 20:16:17  hp
  * SETF, SETS: Use underscored IO_x_ macros rather than incorrect token concatenation
@@ -220,6 +263,16 @@
 	struct sk_buff* skb;
 } etrax_eth_descr;
 
+/* Some transceivers requires special handling */
+struct transceiver_ops
+{
+	unsigned int oui;
+	void (*check_speed)(void);
+	void (*check_duplex)(void);
+};
+
+struct transceiver_ops* transceiver;
+
 /* Duplex settings */
 enum duplex
 {
@@ -240,10 +293,17 @@
 */
 #define MDIO_BASE_STATUS_REG                0x1
 #define MDIO_BASE_CONTROL_REG               0x0
+#define MDIO_PHY_ID_HIGH_REG                0x2
+#define MDIO_PHY_ID_LOW_REG                 0x3
 #define MDIO_BC_NEGOTIATE                0x0200
 #define MDIO_BC_FULL_DUPLEX_MASK         0x0100
 #define MDIO_BC_AUTO_NEG_MASK            0x1000
 #define MDIO_BC_SPEED_SELECT_MASK        0x2000
+#define MDIO_STATUS_100_FD               0x4000
+#define MDIO_STATUS_100_HD               0x2000
+#define MDIO_STATUS_10_FD                0x1000
+#define MDIO_STATUS_10_HD                0x0800
+#define MDIO_STATUS_SPEED_DUPLEX_MASK	 0x7800
 #define MDIO_ADVERTISMENT_REG               0x4
 #define MDIO_ADVERT_100_FD                0x100
 #define MDIO_ADVERT_100_HD                0x080
@@ -257,9 +317,13 @@
 
 /* Broadcom specific */
 #define MDIO_AUX_CTRL_STATUS_REG           0x18
-#define MDIO_FULL_DUPLEX_IND                0x1
-#define MDIO_SPEED                          0x2
-#define MDIO_PHYS_ADDR                      0x0
+#define MDIO_BC_FULL_DUPLEX_IND             0x1
+#define MDIO_BC_SPEED                       0x2
+
+/* TDK specific */
+#define MDIO_TDK_DIAGNOSTIC_REG              18
+#define MDIO_TDK_DIAGNOSTIC_RATE          0x400
+#define MDIO_TDK_DIAGNOSTIC_DPLX          0x800
 
 /* Network flash constants */
 #define NET_FLASH_TIME                  (HZ/50) /* 20 ms */
@@ -303,11 +367,12 @@
 static etrax_eth_descr TxDescList[NBR_OF_TX_DESC] __attribute__ ((aligned(32)));
 
 static unsigned int network_rec_config_shadow = 0;
+static unsigned int mdio_phy_addr; /* Transciever address */
 
 /* Network speed indication. */
 static struct timer_list speed_timer;
 static struct timer_list clear_led_timer;
-static int current_speed; /* Speed read from tranceiver */
+static int current_speed; /* Speed read from transceiver */
 static int current_speed_selection; /* Speed selected by user */
 static int led_next_time;
 static int led_active;
@@ -337,6 +402,7 @@
 static void e100_hardware_send_packet(char *buf, int length);
 static void update_rx_stats(struct net_device_stats *);
 static void update_tx_stats(struct net_device_stats *);
+static int e100_probe_transceiver(void);
 
 static void e100_check_speed(unsigned long dummy);
 static void e100_set_speed(unsigned long speed);
@@ -349,11 +415,25 @@
 static void e100_send_mdio_cmd(unsigned short cmd, int write_cmd);
 static void e100_send_mdio_bit(unsigned char bit);
 static unsigned char e100_receive_mdio_bit(void);
-static void e100_reset_tranceiver(void);
+static void e100_reset_transceiver(void);
 
 static void e100_clear_network_leds(unsigned long dummy);
 static void e100_set_network_leds(int active);
 
+static void broadcom_check_speed(void);
+static void broadcom_check_duplex(void);
+static void tdk_check_speed(void);
+static void tdk_check_duplex(void);
+static void generic_check_speed(void);
+static void generic_check_duplex(void);
+
+struct transceiver_ops transceivers[] = 
+{
+	{0x1018, broadcom_check_speed, broadcom_check_duplex},  /* Broadcom */
+	{0xC039, tdk_check_speed, tdk_check_duplex},            /* TDK */
+	{0x0000, generic_check_speed, generic_check_duplex}     /* Generic, must be last */
+};
+
 #define tx_done(dev) (*R_DMA_CH0_CMD == 0)
 
 /*
@@ -369,11 +449,12 @@
 {
 	int i;
 
-	printk("ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000-2001 Axis Communications AB\n");
+	printk(KERN_INFO
+	       "ETRAX 100LX 10/100MBit ethernet v2.0 (c) 2000-2001 Axis Communications AB\n");
 
 	dev->base_addr = (unsigned int)R_NETWORK_SA_0; /* just to have something to show */
 
-	printk("%s initialized\n", dev->name);
+	printk(KERN_INFO "%s initialized\n", dev->name);
 
 	/* make Linux aware of the new hardware  */
 
@@ -468,7 +549,6 @@
 	current_speed_selection = 0; /* Auto */
 	speed_timer.expires = jiffies + NET_LINK_UP_CHECK_INTERVAL;
 	speed_timer.function = e100_check_speed;
-	add_timer(&speed_timer);
         
 	clear_led_timer.function = e100_clear_network_leds;
         
@@ -476,8 +556,11 @@
 	current_duplex = autoneg;
 	duplex_timer.expires = jiffies + NET_DUPLEX_CHECK_INTERVAL;		
 	duplex_timer.function = e100_check_duplex;
-	add_timer(&duplex_timer);
 
+        /* Initialize group address registers to make sure that no */
+        /* unwanted addresses are matched */
+	*R_NETWORK_GA_0 = 0x00000000;
+	*R_NETWORK_GA_1 = 0x00000000;
 	return 0;
 }
 
@@ -508,8 +591,7 @@
 
 	/* show it in the log as well */
 
-	printk("%s: changed MAC to ", dev->name);
-
+	printk(KERN_INFO "%s: changed MAC to ", dev->name);
 	for (i = 0; i < 5; i++)
 		printk("%02X:", dev->dev_addr[i]);
 
@@ -532,12 +614,6 @@
 {
 	unsigned long flags;
 
-	/* disable the ethernet interface while we configure it */
-
-	*R_NETWORK_GEN_CONFIG =
-		IO_STATE(R_NETWORK_GEN_CONFIG, phy,    mii_clk) |
-		IO_STATE(R_NETWORK_GEN_CONFIG, enable, off);
-
 	/* enable the MDIO output pin */
 
 	*R_NETWORK_MGM_CTRL = IO_STATE(R_NETWORK_MGM_CTRL, mdoe, enable);
@@ -636,7 +712,8 @@
 	/* enable the irq's for ethernet DMA */
 
 	*R_IRQ_MASK2_SET =
-		IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set); 
+		IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set) |
+		IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set);
 
 	*R_IRQ_MASK0_SET =
 		IO_STATE(R_IRQ_MASK0_SET, overrun,       set) |
@@ -665,6 +742,14 @@
 
 	restore_flags(flags);
 	
+	/* Probe for transceiver */
+	if (e100_probe_transceiver())
+		goto grace_exit4;
+
+	/* Start duplex/speed timers */
+	add_timer(&speed_timer);
+	add_timer(&duplex_timer);
+
 	/* We are now ready to accept transmit requeusts from
 	 * the queueing layer of the networking.
 	 */
@@ -684,6 +769,33 @@
 	return -EAGAIN;
 }
 
+static void
+generic_check_speed(void)
+{
+	unsigned long data;
+	data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG);
+	if ((data & MDIO_ADVERT_100_FD) ||
+	    (data & MDIO_ADVERT_100_HD))
+		current_speed = 100;
+	else
+		current_speed = 10;
+}
+
+static void
+tdk_check_speed(void)
+{
+	unsigned long data;
+	data = e100_get_mdio_reg(MDIO_TDK_DIAGNOSTIC_REG);
+	current_speed = (data & MDIO_TDK_DIAGNOSTIC_RATE ? 100 : 10);
+}
+
+static void
+broadcom_check_speed(void)
+{
+	unsigned long data;
+	data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
+	current_speed = (data & MDIO_BC_SPEED ? 100 : 10);
+}
 
 static void
 e100_check_speed(unsigned long dummy)
@@ -695,8 +807,7 @@
 	if (!(data & MDIO_LINK_UP_MASK)) {
 		current_speed = 0;
 	} else {
-		data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
-		current_speed = (data & MDIO_SPEED ? 100 : 10);
+		transceiver->check_speed();
 	}
 	
 	if (old_speed != current_speed)
@@ -761,29 +872,21 @@
 static void
 e100_set_speed(unsigned long speed)
 {
-	current_speed_selection = speed;
-	e100_negotiate();
+	if (speed != current_speed_selection) {
+		current_speed_selection = speed;
+		e100_negotiate();
+	}
 }
 
 static void
 e100_check_duplex(unsigned long dummy)
 {
-	unsigned long data;
-
-	data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);
-        
-	if (data & MDIO_FULL_DUPLEX_IND) {
-		if (!full_duplex) { /* Duplex changed to full? */
-			full_duplex = 1;
-			SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
-			*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
-		}
-	} else { /* half */
-		if (full_duplex) { /* Duplex changed to half? */
-			full_duplex = 0;
-			SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
-			*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
-		}
+	int old_duplex = full_duplex;
+	transceiver->check_duplex();
+	if (old_duplex != full_duplex) { 
+		/* Duplex changed */
+		SETF(network_rec_config_shadow, R_NETWORK_REC_CONFIG, duplex, full_duplex);
+		*R_NETWORK_REC_CONFIG = network_rec_config_shadow;
 	}
 
 	/* Reinitialize the timer. */
@@ -791,13 +894,71 @@
 	add_timer(&duplex_timer);
 }
 
+static void
+generic_check_duplex(void)
+{
+	unsigned long data;
+	data = e100_get_mdio_reg(MDIO_ADVERTISMENT_REG);
+	if ((data & MDIO_ADVERT_100_FD) ||
+	    (data & MDIO_ADVERT_10_FD))
+		full_duplex = 1;
+	else
+		full_duplex = 0;
+}
+
+static void
+tdk_check_duplex(void)
+{
+	unsigned long data;
+	data = e100_get_mdio_reg(MDIO_TDK_DIAGNOSTIC_REG);
+	full_duplex = (data & MDIO_TDK_DIAGNOSTIC_DPLX) ? 1 : 0;
+}
+
+static void
+broadcom_check_duplex(void)
+{
+	unsigned long data;
+	data = e100_get_mdio_reg(MDIO_AUX_CTRL_STATUS_REG);        
+	full_duplex = (data & MDIO_BC_FULL_DUPLEX_IND) ? 1 : 0;
+}
+
 static void 
 e100_set_duplex(enum duplex new_duplex)
 {
-	current_duplex = new_duplex;
-	e100_negotiate();
+	if (new_duplex != current_duplex) {
+		current_duplex = new_duplex;
+		e100_negotiate();
+	}
 }
 
+static int 
+e100_probe_transceiver(void)
+{
+	unsigned int phyid_high;
+	unsigned int phyid_low;
+	unsigned int oui;
+	struct transceiver_ops* ops = NULL;
+
+	/* Probe MDIO physical address */
+	for (mdio_phy_addr = 0; mdio_phy_addr <= 31; mdio_phy_addr++) {
+		if (e100_get_mdio_reg(MDIO_BASE_STATUS_REG) != 0xffff)
+			break;
+	}
+	if (mdio_phy_addr == 32)
+		 return -ENODEV;
+
+	/* Get manufacturer */
+	phyid_high = e100_get_mdio_reg(MDIO_PHY_ID_HIGH_REG);
+	phyid_low = e100_get_mdio_reg(MDIO_PHY_ID_LOW_REG);
+	oui = (phyid_high << 6) | (phyid_low >> 10);
+	
+	for (ops = &transceivers[0]; ops->oui; ops++) {
+		if (ops->oui == oui)
+			break;
+	}
+	transceiver = ops;
+	return 0;
+}
 
 static unsigned short
 e100_get_mdio_reg(unsigned char reg_num)
@@ -807,7 +968,7 @@
 	int bitCounter;
 	
 	/* Start of frame, OP Code, Physical Address, Register Address */
-	cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (MDIO_PHYS_ADDR << 7) |
+	cmd = (MDIO_START << 14) | (MDIO_READ << 12) | (mdio_phy_addr << 7) |
 		(reg_num << 2);
 	
 	e100_send_mdio_cmd(cmd, 0);
@@ -828,7 +989,7 @@
 	int bitCounter;
 	unsigned short cmd;
 
-	cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) |
+	cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (mdio_phy_addr << 7) |
 	      (reg << 2);
 
 	e100_send_mdio_cmd(cmd, 1);
@@ -888,7 +1049,7 @@
 }
 
 static void 
-e100_reset_tranceiver(void)
+e100_reset_transceiver(void)
 {
 	unsigned short cmd;
 	unsigned short data;
@@ -896,7 +1057,7 @@
 
 	data = e100_get_mdio_reg(MDIO_BASE_CONTROL_REG);
 
-	cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (MDIO_PHYS_ADDR << 7) | (MDIO_BASE_CONTROL_REG << 2);
+	cmd = (MDIO_START << 14) | (MDIO_WRITE << 12) | (mdio_phy_addr << 7) | (MDIO_BASE_CONTROL_REG << 2);
 
 	e100_send_mdio_cmd(cmd, 1);
 	
@@ -928,9 +1089,9 @@
 	RESET_DMA(NETWORK_TX_DMA_NBR);
 	WAIT_DMA(NETWORK_TX_DMA_NBR);
 	
-	/* Reset the tranceiver. */
+	/* Reset the transceiver. */
 	
-	e100_reset_tranceiver();
+	e100_reset_transceiver();
 	
 	/* and get rid of the packets that never got an interrupt */
 	while (myFirstTxDesc != myNextTxDesc)
@@ -978,9 +1139,6 @@
 
 	/* Stop queue if full */
 	if (myNextTxDesc == myFirstTxDesc) {
-		/* Enable transmit interrupt to wake up queue */
-		*R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);		
-		*R_IRQ_MASK2_SET = IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set);
 		netif_stop_queue(dev);
 	}
 
@@ -1001,6 +1159,11 @@
 	struct net_local *np = (struct net_local *)dev->priv;
 	unsigned long irqbits = *R_IRQ_MASK2_RD;
  
+	/* Disable RX/TX IRQs to avoid reentrancy */
+	*R_IRQ_MASK2_CLR = 
+	  IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr) |
+	  IO_STATE(R_IRQ_MASK2_CLR, dma1_eop, clr);
+        
 	/* Handle received packets */
 	if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma1_eop, active)) {
 		/* acknowledge the eop interrupt */
@@ -1044,9 +1207,13 @@
 	if (irqbits & IO_STATE(R_IRQ_MASK2_RD, dma0_eop, active)) {
 		/* acknowledge the eop interrupt and wake up queue */
 		*R_DMA_CH0_CLR_INTR = IO_STATE(R_DMA_CH0_CLR_INTR, clr_eop, do);
-		*R_IRQ_MASK2_CLR = IO_STATE(R_IRQ_MASK2_CLR, dma0_eop, clr);		
 		netif_wake_queue(dev);
 	}
+
+	/* Enable RX/TX IRQs again */
+	*R_IRQ_MASK2_SET = 
+	  IO_STATE(R_IRQ_MASK2_SET, dma0_eop, set) |
+	  IO_STATE(R_IRQ_MASK2_SET, dma1_eop, set);
 }
 
 static void
@@ -1183,14 +1350,10 @@
 {
 	struct net_local *np = (struct net_local *)dev->priv;
 
-	printk("Closing %s.\n", dev->name);
+	printk(KERN_INFO "Closing %s.\n", dev->name);
 
 	netif_stop_queue(dev);
 
-	*R_NETWORK_GEN_CONFIG =
-		IO_STATE(R_NETWORK_GEN_CONFIG, phy,    mii_clk) |
-		IO_STATE(R_NETWORK_GEN_CONFIG, enable, off);
-	
 	*R_IRQ_MASK0_CLR =
 		IO_STATE(R_IRQ_MASK0_CLR, overrun, clr) |
 		IO_STATE(R_IRQ_MASK0_CLR, underrun, clr) |
@@ -1221,6 +1384,9 @@
 	update_rx_stats(&np->stats);
 	update_tx_stats(&np->stats);
 
+	/* Stop speed/duplex timers */
+	del_timer(&speed_timer);
+	del_timer(&duplex_timer);
 	return 0;
 }
 
@@ -1233,7 +1399,7 @@
 		case SIOCETHTOOL:
 			return e100_ethtool_ioctl(dev,ifr);
 		case SIOCGMIIPHY: /* Get PHY address */
-			data->phy_id = MDIO_PHYS_ADDR;
+			data->phy_id = mdio_phy_addr;
 			break;
 		case SIOCGMIIREG: /* Read MII register */
 			data->val_out = e100_get_mdio_reg(data->reg_num);
@@ -1252,7 +1418,7 @@
 		case SET_ETH_SPEED_AUTO:              /* Auto negotiate speed */
 			e100_set_speed(0);
 			break;
-		case SET_ETH_DUPLEX_HALF:              /* Hhalf duplex. */
+		case SET_ETH_DUPLEX_HALF:              /* Half duplex. */
 			e100_set_duplex(half);
 			break;
 		case SET_ETH_DUPLEX_FULL:              /* Full duplex. */
@@ -1285,7 +1451,7 @@
 			  SUPPORTED_100baseT_Half | SUPPORTED_100baseT_Full;
 			ecmd.port = PORT_TP;
 			ecmd.transceiver = XCVR_EXTERNAL;
-			ecmd.phy_address = MDIO_PHYS_ADDR;
+			ecmd.phy_address = mdio_phy_addr;
 			ecmd.speed = current_speed;
 			ecmd.duplex = full_duplex ? DUPLEX_FULL : DUPLEX_HALF;
 			ecmd.advertising = ADVERTISED_TP;
@@ -1328,7 +1494,7 @@
 			struct ethtool_drvinfo info;
 			memset((void *) &info, 0, sizeof (info));
 			strncpy(info.driver, "ETRAX 100LX", sizeof(info.driver) - 1);
-			strncpy(info.version, "$Rev$", sizeof(info.version) - 1);
+			strncpy(info.version, "$Revision: 1.44 $", sizeof(info.version) - 1);
 			strncpy(info.fw_version, "N/A", sizeof(info.fw_version) - 1);
 			strncpy(info.bus_info, "N/A", sizeof(info.bus_info) - 1);
 			info.regdump_len = 0;
@@ -1457,7 +1623,7 @@
 			
 			hash_ix &= 0x3f;
 			
-			if (hash_ix > 32) {
+			if (hash_ix >= 32) {
 				hi_bits |= (1 << (hash_ix-32));
 			}
 			else {
@@ -1526,7 +1692,7 @@
 
 	if (!current_speed) {
 		/* Make LED red, link is down */
-		LED_NETWORK_SET(LED_RED);
+		LED_NETWORK_SET(LED_OFF);
 	}
 	else if (light_leds) {
 		if (current_speed == 10) {
@@ -1555,4 +1721,26 @@
 		return -ENODEV;
 }
 
+static int __init
+e100_boot_setup(char* str)
+{
+	struct sockaddr sa = {0};
+	int i;
+
+	/* Parse the colon separated Ethernet station address */
+	for (i = 0; i <  ETH_ALEN; i++) {
+		unsigned int tmp;
+		if (sscanf(str + 3*i, "%2x", &tmp) != 1) {
+			printk(KERN_WARNING "Malformed station address");
+			return 0;
+		}
+		sa.sa_data[i] = (char)tmp;
+	}	
+
+	default_mac = sa;
+	return 1;
+}
+
+__setup("etrax100_eth=", e100_boot_setup);
+
 module_init(etrax_init_module);
diff -urN linux-2.4.21-bk5/arch/cris/drivers/examples/kiobuftest.c linux-2.4.21-bk6/arch/cris/drivers/examples/kiobuftest.c
--- linux-2.4.21-bk5/arch/cris/drivers/examples/kiobuftest.c	2001-04-06 10:42:55.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/examples/kiobuftest.c	2003-07-10 02:54:58.000000000 -0700
@@ -4,18 +4,12 @@
  *
  * It is necessary because the pages the virtual pointers reference, might
  * not exist in memory (could be mapped to the zero-page, filemapped etc)
- * and DMA cannot trigger the MMU to force them in (and would have time 
+ * and DMA cannot trigger the MMU to force them in (and would have time
  * contraints making it impossible to wait for it anyway).
  *
- * Author:  Bjorn Wesen
- *
- * $Log: kiobuftest.c,v $
- * Revision 1.2  2001/02/27 13:52:50  bjornw
- * malloc.h -> slab.h
- *
- * Revision 1.1  2001/01/19 15:57:49  bjornw
- * Example of how to do direct HW -> user-mode DMA
+ * Copyright (c) 2001, 2002, 2003  Axis Communications AB
  *
+ * Author:  Bjorn Wesen
  *
  */
 
@@ -32,55 +26,55 @@
 
 #define KIOBUFTEST_MAJOR 124  /* in the local range, experimental */
 
-
 static ssize_t
 kiobuf_read(struct file *filp, char *buf, size_t len, loff_t *ppos)
 {
+	struct kiobuf *iobuf;
+	int res, i;
 
-     struct kiobuf *iobuf;
-     int res, i;
- 
-     /* Make a kiobuf that maps the entire length the reader has given
-      * us
-      */
-
-     res = alloc_kiovec(1, &iobuf);
-     if (res)
-	     return res;
-     
-     if((res = map_user_kiobuf(READ, iobuf, (unsigned long)buf, len))) {
-	     printk("map_user_kiobuf failed, return %d\n", res);
-	     return res;
-     }
-
-     /* At this point, the virtual area buf[0] -> buf[len-1] will
-      * have corresponding pages mapped in physical memory and locked
-      * until we unmap the kiobuf. They cannot be swapped out or moved
-      * around.
-      */
-
-     printk("nr_pages == %d\noffset == %d\nlength == %d\n",
-	    iobuf->nr_pages, iobuf->offset, iobuf->length);
-
-     for(i = 0; i < iobuf->nr_pages; i++) {
-	     printk("page_add(maplist[%d]) == 0x%x\n", i,
-		    page_address(iobuf->maplist[i]));
-     }
-
-     /* This is the place to create the necessary scatter-gather vector
-      * for the DMA using the iobuf->maplist array and page_address
-      * (don't forget __pa if the DMA needs the actual physical DRAM address)
-      * and run it.
-      */
+	/*
+	 * Make a kiobuf that maps the entire length the reader has given us.
+	 */
 
+	res = alloc_kiovec(1, &iobuf);
+	if (res)
+		return res;
+
+	if ((res = map_user_kiobuf(READ, iobuf, (unsigned long)buf, len))) {
+		printk("map_user_kiobuf failed, return %d\n", res);
+		free_kiovec(1, &iobuf);
+		return res;
+	}
+
+	/*
+	 * At this point, the virtual area buf[0] -> buf[len-1] will have
+	 * corresponding pages mapped in physical memory and locked until
+	 * we unmap the kiobuf. They cannot be swapped out or moved around.
+	 */
+
+	printk("nr_pages == %d\noffset == %d\nlength == %d\n",
+	       iobuf->nr_pages, iobuf->offset, iobuf->length);
+
+	for (i = 0; i < iobuf->nr_pages; i++) {
+		printk("page_add(maplist[%d]) == 0x%x\n", i,
+		       page_address(iobuf->maplist[i]));
+	}
+
+	/*
+	 * This is the place to create the necessary scatter-gather vector
+	 * for the DMA using the iobuf->maplist array and page_address (don't
+	 * forget __pa if the DMA needs the actual physical DRAM address)
+	 * and run it.
+	 */
 
 
 
-     /* Release the mapping and exit */
+	/* Release the mapping and exit */
 
-     unmap_kiobuf(iobuf); /* The unlock_kiobuf is implicit here */
+	unmap_kiobuf(iobuf); /* The unlock_kiobuf is implicit here */
+	free_kiovec(1, &iobuf);
 
-     return len;
+	return len;
 }
 
 
@@ -97,7 +91,7 @@
 	/* register char device */
 
 	res = register_chrdev(KIOBUFTEST_MAJOR, "kiobuftest", &kiobuf_fops);
-	if(res < 0) {
+	if (res < 0) {
 		printk(KERN_ERR "kiobuftest: couldn't get a major number.\n");
 		return res;
 	}
@@ -105,4 +99,11 @@
 	printk("Initializing kiobuf-test device\n");
 }
 
+static void __exit
+kiobuftest_exit(void)
+{
+	unregister_chrdev(KIOBUFTEST_MAJOR, "kiobuftest");
+}
+
 module_init(kiobuftest_init);
+module_exit(kiobuftest_exit);
diff -urN linux-2.4.21-bk5/arch/cris/drivers/gpio.c linux-2.4.21-bk6/arch/cris/drivers/gpio.c
--- linux-2.4.21-bk5/arch/cris/drivers/gpio.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/gpio.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: gpio.c,v 1.21 2002/12/02 08:11:41 starvik Exp $
+/* $Id: gpio.c,v 1.25 2003/06/24 07:24:31 johana Exp $
  *
  * Etrax general port I/O device
  *
@@ -9,6 +9,22 @@
  *             Johan Adolfsson  (read/set directions, write, port G)
  *
  * $Log: gpio.c,v $
+ * Revision 1.25  2003/06/24 07:24:31  johana
+ * Added gpio_pa_irq_enabled_mask to keep track of what interrupts we have
+ * enabled so we anly clear those that we have enabled and let bits/pins
+ * used by other drivers alone.
+ * Update gpio_some_alarms in IO_CLRALARM ioctl().
+ *
+ * Revision 1.24  2003/06/19 08:58:25  johana
+ * Turn off interrupts while fiddling with genconfig_shadow.
+ * Put some printk within D() macro.
+ *
+ * Revision 1.23  2003/04/01 14:12:06  starvik
+ * Added loglevel for lots of printks
+ *
+ * Revision 1.22  2003/01/09 18:03:47  starvik
+ * init_ioremap is now called by kernel before device drivers are initialized
+ *
  * Revision 1.21  2002/12/02 08:11:41  starvik
  * Merge of Linux 2.4.20
  *
@@ -169,6 +185,7 @@
 static struct gpio_private *alarmlist = 0;
 
 static int gpio_some_alarms = 0; /* Set if someone uses alarm */
+static unsigned long gpio_pa_irq_enabled_mask = 0;
 
 /* Port A and B use 8 bit access, but Port G is 32 bit */
 #define NUM_PORTS (GPIO_MINOR_B+1)
@@ -238,13 +255,19 @@
 	unsigned long data;
 	poll_wait(file, &priv->alarm_wq, wait);
 	if (priv->minor == GPIO_MINOR_A) {
+		unsigned long flags;
 		unsigned long tmp;
 		data = *R_PORT_PA_DATA;
 		/* PA has support for high level interrupt -
 		 * lets activate for those low and with highalarm set
 		 */
 		tmp = ~data & priv->highalarm & 0xFF;
-		*R_IRQ_MASK1_SET = (tmp << R_IRQ_MASK1_SET__pa0__BITNR);
+		tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR);
+		save_flags(flags); cli();
+		gpio_pa_irq_enabled_mask |= tmp;
+		*R_IRQ_MASK1_SET = tmp;
+		restore_flags(flags);
+		
 	} else if (priv->minor == GPIO_MINOR_B)
 		data = *R_PORT_PB_DATA;
 	else if (priv->minor == GPIO_MINOR_G)
@@ -293,12 +316,15 @@
 {
 	unsigned long tmp;
 	/* Find what PA interrupts are active */
-	tmp = (*R_IRQ_READ1 >> R_IRQ_READ1__pa0__BITNR) & 0xFF;
+	tmp = (*R_IRQ_READ1);
+
+	/* Find those that we have enabled */
+	tmp &= gpio_pa_irq_enabled_mask;
+
 	/* Clear them.. */
-	/* NOTE: Maybe we need to be more careful here if some other
-	 * driver uses PA interrupt as well?
-	 */
-	*R_IRQ_MASK1_CLR = (tmp << R_IRQ_MASK1_CLR__pa0__BITNR);
+	*R_IRQ_MASK1_CLR = tmp;
+	gpio_pa_irq_enabled_mask &= ~tmp;
+	
 	if (gpio_some_alarms) {
 		etrax_gpio_wake_up_check();
 	}
@@ -437,7 +463,7 @@
 		p = p->next;
 	}
 	gpio_some_alarms = 0;
-		
+	
 	return 0;
 }
 
@@ -459,6 +485,7 @@
 		return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */
 	} else if (priv->minor == GPIO_MINOR_G) {
 		/* We must fiddle with R_GEN_CONFIG to change dir */
+		save_flags(flags); cli();
 		if (((arg & dir_g_in_bits) != arg) && 
 		    (arg & changeable_dir_g)) {
 			arg &= changeable_dir_g;
@@ -483,16 +510,17 @@
 				dir_g_in_bits |= (1<<24);
 				dir_g_out_bits &= ~(1<<24);
 			}
-			printk("gpio: SETINPUT on port G set "
-				"genconfig to 0x%08lX "
-				"in_bits: 0x%08lX "
-				"out_bits: 0x%08lX\n", 
-			       (unsigned long)genconfig_shadow, 
-			       dir_g_in_bits, dir_g_out_bits);
+			D(printk(KERN_INFO "gpio: SETINPUT on port G set "
+				 "genconfig to 0x%08lX "
+				 "in_bits: 0x%08lX "
+				 "out_bits: 0x%08lX\n", 
+				 (unsigned long)genconfig_shadow, 
+				 dir_g_in_bits, dir_g_out_bits));
 			*R_GEN_CONFIG = genconfig_shadow;
 			/* Must be a >120 ns delay before writing this again */
 				
 		}
+		restore_flags(flags);
 		return dir_g_in_bits;
 	}
 	return 0;
@@ -508,7 +536,8 @@
 		restore_flags(flags);
 		return *priv->dir_shadow;
 	} else if (priv->minor == GPIO_MINOR_G) {
-		/* We must fiddle with R_GEN_CONFIG to change dir */			
+		/* We must fiddle with R_GEN_CONFIG to change dir */
+		save_flags(flags); cli();
 		if (((arg & dir_g_out_bits) != arg) &&
 		    (arg & changeable_dir_g)) {
 			/* Set bits in genconfig to set to output */
@@ -532,15 +561,16 @@
 				dir_g_out_bits |= (1<<24);
 				dir_g_in_bits &= ~(1<<24);
 			}
-			printk("gpio: SETOUTPUT on port G set "
-				"genconfig to 0x%08lX "
-				"in_bits: 0x%08lX "
-				"out_bits: 0x%08lX\n", 
-			       (unsigned long)genconfig_shadow, 
-			       dir_g_in_bits, dir_g_out_bits);
+			D(printk(KERN_INFO "gpio: SETOUTPUT on port G set "
+				 "genconfig to 0x%08lX "
+				 "in_bits: 0x%08lX "
+				 "out_bits: 0x%08lX\n", 
+				 (unsigned long)genconfig_shadow, 
+				 dir_g_in_bits, dir_g_out_bits));
 			*R_GEN_CONFIG = genconfig_shadow;
 			/* Must be a >120 ns delay before writing this again */
 		}
+		restore_flags(flags);
 		return dir_g_out_bits & 0x7FFFFFFF;
 	}
 	return 0;
@@ -605,6 +635,20 @@
 		// clear alarm for bits with 1 in arg
 		priv->highalarm &= ~arg;
 		priv->lowalarm  &= ~arg;
+		{
+			/* Must update gpio_some_alarms */
+			struct gpio_private *p = alarmlist;
+			int some_alarms;
+			some_alarms = 0;
+			while (p) {
+				if (p->highalarm | p->lowalarm) {
+					some_alarms = 1;
+					break;
+				}
+				p = p->next;
+			}
+			gpio_some_alarms = some_alarms;
+		}		
 		break;
 	case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
 		/* Read direction 0=input 1=output */
@@ -824,9 +868,11 @@
 	dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g);
 
 
-	printk("GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n",
+	printk(KERN_INFO
+	       "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n",
 	       dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA);
-	printk("GPIO port G: dir: %08lX changeable: %08lX\n", 
+	printk(KERN_INFO
+	       "GPIO port G: dir: %08lX changeable: %08lX\n", 
 	       dir_g_shadow, changeable_dir_g);
 }
 
@@ -835,7 +881,6 @@
 static __init int
 gpio_init(void)
 {
-	extern void init_ioremap(void);
 	int res;
 #if defined (CONFIG_ETRAX_CSP0_LEDS)
 	int i;
@@ -850,8 +895,7 @@
 	}
 
 	/* Clear all leds */
-#if defined (CONFIG_ETRAX_CSP0_LEDS) ||  defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) 
-	init_ioremap();
+#if defined (CONFIG_ETRAX_CSP0_LEDS) ||  defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS)
 	LED_NETWORK_SET(0);
 	LED_ACTIVE_SET(0);
 	LED_DISK_READ(0);
@@ -865,7 +909,8 @@
 
 #endif
 	gpio_init_port_g();
-	printk("ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002 Axis Communications AB\n");
+	printk(KERN_INFO
+	       "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002 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
@@ -873,11 +918,11 @@
 	 */  
 	if (request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt,
 			SA_SHIRQ | SA_INTERRUPT,"gpio poll", NULL)) {
-		printk("err: timer0 irq for gpio\n");
+		printk(KERN_CRIT "err: timer0 irq for gpio\n");
 	}
 	if (request_irq(PA_IRQ_NBR, gpio_pa_interrupt,
 			SA_SHIRQ | SA_INTERRUPT,"gpio PA", NULL)) {
-		printk("err: PA irq for gpio\n");
+		printk(KERN_CRIT "err: PA irq for gpio\n");
 	}
 	
 	return res;
diff -urN linux-2.4.21-bk5/arch/cris/drivers/i2c.c linux-2.4.21-bk6/arch/cris/drivers/i2c.c
--- linux-2.4.21-bk5/arch/cris/drivers/i2c.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/i2c.c	2003-07-10 02:54:58.000000000 -0700
@@ -11,7 +11,26 @@
 *! 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.12  2003/06/23 14:43:47  oskarp
+*! * i2c_sendnack is added.
+*!   - 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.
+*!
+*! Revision 1.11  2003/05/23 09:37:23  oskarp
+*! * Made i2c_init() visible.
+*!   - e.g. pcf856.c uses i2c_init().
+*!
+*! Revision 1.10  2003/04/01 14:12:06  starvik
+*! Added loglevel for lots of printks
+*!
 *! Revision 1.9  2002/10/31 15:32:26  starvik
 *! Update Port B register and shadow even when running with hardware support
 *!   to avoid glitches when reading bits
@@ -53,7 +72,7 @@
 *! (C) Copyright 1999-2002 Axis Communications AB, LUND, SWEDEN
 *!
 *!***************************************************************************/
-/* $Id: i2c.c,v 1.9 2002/10/31 15:32:26 starvik Exp $ */
+/* $Id: i2c.c,v 1.12 2003/06/23 14:43:47 oskarp Exp $ */
 /****************** INCLUDE FILES SECTION ***********************************/
 
 #include <linux/module.h>
@@ -133,7 +152,7 @@
 
 #define i2c_getbit() (((*R_PORT_PB_READ & (1 << SDABIT))) >> SDABIT)
 
-#else
+#else /* !CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C */
 /* enable or disable the i2c interface */
 
 #define i2c_enable() *R_PORT_PB_I2C = (port_pb_i2c_shadow |= IO_MASK(R_PORT_PB_I2C, i2c_en))
@@ -163,7 +182,8 @@
 /* read a bit from the i2c interface */
 
 #define i2c_getbit() (*R_PORT_PB_READ & 0x1)
-#endif
+
+#endif /* CONFIG_ETRAX_I2C_USES_PB_NOT_PB_I2C */
 
 /* use the kernels delay routine */
 
@@ -299,6 +319,12 @@
 	}
 	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
+	 */
+	i2c_clk(I2C_CLOCK_LOW);
 	return aBitByte;
 }
 
@@ -360,6 +386,13 @@
 		i2c_delay(CLOCK_HIGH_TIME/2);
 	}
 
+   /*
+    * our clock is high now, make sure data is low
+    * before we enable our output. If we keep data high
+    * and enable output, we would generate a stop condition.
+    */
+   i2c_data(I2C_DATA_LOW);
+   
 	/*
 	 * end clock pulse
 	 */
@@ -417,6 +450,37 @@
 
 /*#---------------------------------------------------------------------------
 *#
+*# FUNCTION NAME: i2c_sendnack
+*#
+*# DESCRIPTION  : Sends NACK on received data
+*#
+*#--------------------------------------------------------------------------*/
+void
+i2c_sendnack(void)
+{
+	/*
+	 * enable output
+	 */
+	i2c_delay(CLOCK_LOW_TIME);
+	i2c_dir_out();
+	/*
+	 * set data high
+	 */
+	i2c_data(I2C_DATA_HIGH);
+	/*
+	 * generate clock pulse
+	 */
+	i2c_delay(CLOCK_HIGH_TIME/6);
+	i2c_clk(I2C_CLOCK_HIGH);
+	i2c_delay(CLOCK_HIGH_TIME);
+	i2c_clk(I2C_CLOCK_LOW);
+	i2c_delay(CLOCK_LOW_TIME);
+	
+	i2c_dir_in();
+}
+
+/*#---------------------------------------------------------------------------
+*#
 *# FUNCTION NAME: i2c_writereg
 *#
 *# DESCRIPTION  : Writes a value to an I2C device
@@ -546,9 +610,10 @@
 		 */
 		b = i2c_inbyte();
 		/*
-		 * send Ack
+		 * last received byte needs to be nacked
+		 * instead of acked
 		 */
-		i2c_sendack();
+		i2c_sendnack();
 		/*
 		 * end sequence
 		 */
@@ -623,7 +688,7 @@
 	release:  i2c_release,
 };
 
-static int __init
+int __init
 i2c_init(void)
 {
 	int res;
@@ -653,7 +718,7 @@
 		return res;
 	}
 
-	printk("I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n");
+	printk(KERN_INFO "I2C driver v2.2, (c) 1999-2001 Axis Communications AB\n");
 	
 	return 0;
 }
diff -urN linux-2.4.21-bk5/arch/cris/drivers/i2c.h linux-2.4.21-bk6/arch/cris/drivers/i2c.h
--- linux-2.4.21-bk5/arch/cris/drivers/i2c.h	2001-05-19 17:43:05.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/i2c.h	2003-07-10 02:54:58.000000000 -0700
@@ -1,6 +1,8 @@
-/* $Id: i2c.h,v 1.3 2001/03/19 12:43:01 markusl Exp $ */
+
+#include <linux/init.h>
 
 /* High level I2C actions */
+int __init i2c_init(void);
 int i2c_writereg(unsigned char theSlave, unsigned char theReg, unsigned char theValue);
 unsigned char i2c_readreg(unsigned char theSlave, unsigned char theReg);
 
@@ -11,6 +13,3 @@
 unsigned char i2c_inbyte(void);
 int i2c_getack(void);
 void i2c_sendack(void);
-
-
-
diff -urN linux-2.4.21-bk5/arch/cris/drivers/ide.c linux-2.4.21-bk6/arch/cris/drivers/ide.c
--- linux-2.4.21-bk5/arch/cris/drivers/ide.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/ide.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: ide.c,v 1.26 2002/09/17 12:16:59 bjornw Exp $
+/* $Id: ide.c,v 1.30 2003/07/08 07:24:47 pkj Exp $
  *
  * Etrax specific IDE functions, like init and PIO-mode setting etc.
  * Almost the entire ide.c is used for the rest of the Etrax ATA driver.
@@ -8,6 +8,19 @@
  *             Mikael Starvik     (pio setup stuff)
  *
  * $Log: ide.c,v $
+ * Revision 1.30  2003/07/08 07:24:47  pkj
+ * Corrected spelling mistakes originally found in 2.5.x
+ *
+ * Revision 1.29  2003/06/17 13:57:49  starvik
+ * Merge of Linux 2.4.21
+ *
+ * Revision 1.28  2003/01/22 12:41:12  starvik
+ * Added LBA48 support
+ * Fixed typo in e100_ideproc
+ *
+ * Revision 1.27  2003/01/09 18:03:47  starvik
+ * init_ioremap is now called by kernel before device drivers are initialized
+ *
  * Revision 1.26  2002/09/17 12:16:59  bjornw
  * Removed unnecessary cli/sti pair
  *
@@ -151,6 +164,8 @@
 static volatile unsigned long *reset_addr;
 #endif
 
+static int e100_read_command = 0;
+
 #define LOWDB(x)
 #define D(x) 
 
@@ -186,14 +201,9 @@
 			    IO_MASK(R_ATA_STATUS_DATA, tr_rdy)))
 		timeleft--;
 }
-unsigned char 
-IN_BYTE(ide_ioreg_t reg) {
-        /* data was in the lower 16 bits in the status reg */
-        return (unsigned char)(IN_WORD(reg));
-}
 
 unsigned short 
-IN_WORD(ide_ioreg_t reg) {
+IN_BYTE(ide_ioreg_t reg) {
 	int status;
 	int timeleft;
 
@@ -237,7 +247,7 @@
 
 	LOWDB(printk("inb: 0x%x from reg 0x%x\n", status & 0xff, reg));
 
-        return (unsigned short)status;
+        return (unsigned char)status;
 }
 
 /* PIO timing (in R_ATA_CONFIG)
@@ -292,9 +302,15 @@
 #define ATA_PIO0_STROBE 19
 #define ATA_PIO0_HOLD    4
 
-static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive);
-static void e100_ideproc (ide_ide_action_t func, ide_drive_t *drive,
-			  void *buffer, unsigned int length);
+static int e100_dma_check (ide_drive_t *drive);
+static int e100_dma_begin (ide_drive_t *drive);
+static int e100_dma_end (ide_drive_t *drive);
+static int e100_dma_read (ide_drive_t *drive);
+static int e100_dma_write (ide_drive_t *drive);
+static void e100_ide_input_data (ide_drive_t *drive, void *, unsigned int);
+static void e100_ide_output_data (ide_drive_t *drive, void *, unsigned int);
+static void e100_atapi_input_bytes(ide_drive_t *drive, void *, unsigned int);
+static void e100_atapi_output_bytes(ide_drive_t *drive, void *, unsigned int);
 
 /*
  * good_dma_drives() lists the model names (from "hdparm -i")
@@ -309,8 +325,6 @@
 
 static void tune_e100_ide(ide_drive_t *drive, byte pio)
 {
-	unsigned long flags;
-	
 	pio = 4;
 	/* pio = ide_get_best_pio_mode(drive, pio, 4, NULL); */
 	
@@ -374,8 +388,15 @@
 		ide_hwif_t *hwif = &ide_hwifs[h];
 		hwif->chipset = ide_etrax100;
 		hwif->tuneproc = &tune_e100_ide;
-		hwif->dmaproc = &e100_dmaproc;
-		hwif->ideproc = &e100_ideproc;
+                hwif->ata_input_data = &e100_ide_input_data;
+                hwif->ata_output_data = &e100_ide_output_data;
+                hwif->atapi_input_bytes = &e100_atapi_input_bytes;
+                hwif->atapi_output_bytes = &e100_atapi_output_bytes;
+                hwif->ide_dma_check = &e100_dma_check;
+                hwif->ide_dma_end = &e100_dma_end;
+		hwif->ide_dma_write = &e100_dma_write;
+		hwif->ide_dma_read = &e100_dma_read;
+		hwif->ide_dma_begin = &e100_dma_begin;
 	}
 
 	/* actually reset and configure the etrax100 ide/ata interface */
@@ -400,11 +421,9 @@
         REG_SHADOW_SET(R_PORT_G_DATA, port_g_data_shadow, 27, 0);
 #endif 
 #ifdef CONFIG_ETRAX_IDE_CSE1_16_RESET
-        init_ioremap();
         REG_SHADOW_SET(port_cse1_addr, port_cse1_shadow, 16, 0);
 #endif
 #ifdef CONFIG_ETRAX_IDE_CSP0_8_RESET
-        init_ioremap();
         REG_SHADOW_SET(port_csp0_addr, port_csp0_shadow, 8, 0);
 #endif
 #ifdef CONFIG_ETRAX_IDE_PB7_RESET  
@@ -618,7 +637,7 @@
         LED_DISK_WRITE(1);
         
         /* Etrax will set busy = 1 until the multi pio transfer has finished
-         * and tr_rdy = 1 after each succesful word transfer. 
+         * and tr_rdy = 1 after each successful word transfer.
          * When the last byte has been transferred Etrax will first set tr_tdy = 1 
          * and then busy = 0 (not in the same cycle). If we read busy before it
          * has been set to 0 we will think that we should transfer more bytes 
@@ -655,32 +674,6 @@
 	e100_atapi_output_bytes(drive, buffer, wcount << 2);
 }
 
-/*
- * The multiplexor for ide_xxxput_data and atapi calls
- */
-static void 
-e100_ideproc (ide_ide_action_t func, ide_drive_t *drive,
-	      void *buffer, unsigned int length)
-{
-	switch (func) {
-		case ideproc_ide_input_data:
-			e100_ide_input_data(drive, buffer, length);
-			break;
-		case ideproc_ide_output_data:
-			e100_ide_input_data(drive, buffer, length);
-			break;
-		case ideproc_atapi_input_bytes:
-			e100_atapi_input_bytes(drive, buffer, length);
-			break;
-		case ideproc_atapi_output_bytes:
-			e100_atapi_output_bytes(drive, buffer, length);
-			break;
-		default:
-			printk("e100_ideproc: unsupported func %d!\n", func);
-			break;
-	}
-}
-
 /* we only have one DMA channel on the chip for ATA, so we can keep these statically */
 static etrax_dma_descr ata_descrs[MAX_DMA_DESCRS];
 static unsigned int ata_tot_size;
@@ -815,8 +808,8 @@
 	LED_DISK_READ(0);
 	LED_DISK_WRITE(0);
 
-	dma_stat = HWIF(drive)->dmaproc(ide_dma_end, drive);
-	stat = GET_STAT();			/* get drive status */
+	dma_stat = HWIF(drive)->ide_dma_end(drive);
+	stat = HWIF(drive)->INB(IDE_STATUS_REG);		/* get drive status */
 	if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
 		if (!dma_stat) {
 			struct request *rq;
@@ -833,7 +826,7 @@
 }
 
 /*
- * e100_dmaproc() initiates/aborts DMA read/write operations on a drive.
+ * Functions below initiates/aborts DMA read/write operations on a drive.
  *
  * The caller is assumed to have selected the drive and programmed the drive's
  * sector address using CHS or LBA.  All that remains is to prepare for DMA
@@ -848,59 +841,19 @@
  * the caller should revert to PIO for the current request.
  */
 
-static int e100_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+static int e100_dma_check(ide_drive_t *drive)
 {
-        static unsigned int reading; /* static to support ide_dma_begin semantics */
-	int atapi = 0;
-
-	D(printk("e100_dmaproc func %d\n", func));
-
-        switch (func) {
-		case ide_dma_verbose:
-			return 0;
-                case ide_dma_check:
-                        return config_drive_for_dma (drive);
-		case ide_dma_off:
-		case ide_dma_off_quietly:
-			/* ok.. we don't really need to do anything I think. */
-			return 0;
-                case ide_dma_write:
-                        reading = 0;
-			break;
-                case ide_dma_read:
-			reading = 1;
-                        break;
-                case ide_dma_begin:
-			/* begin DMA, used by ATAPI devices which want to issue the 
-			 * appropriate IDE command themselves.
-			 *
-			 * they have already called ide_dma_read/write to set the
-			 * static reading flag, now they call ide_dma_begin to do
-			 * the real stuff. we tell our code below not to issue
-			 * any IDE commands itself and jump into it. 
-			 */
-			atapi++;
-			goto dma_begin;
-		case ide_dma_end: /* returns 1 on error, 0 otherwise */
-			/* TODO: check if something went wrong with the DMA */
-			return 0;
-
-                default:
-                        printk("e100_dmaproc: unsupported func %d\n", func);
-                        return 1;
-        }
-
-	/* ATAPI-devices (not disks) first call ide_dma_read/write to set the direction
-	 * then they call ide_dma_begin after they have issued the appropriate drive command
-	 * themselves to actually start the chipset DMA. so we just return here if we're
-	 * not a diskdrive.
-	 */
-
-        if (drive->media != ide_disk)
-                return 0;
+	return config_drive_for_dma (drive);
+}
 
- dma_begin:
+static int e100_dma_end(ide_drive_t *drive)
+{
+	/* TODO: check if something went wrong with the DMA */
+	return 0;
+}
 
+static int e100_start_dma(ide_drive_t *drive, int atapi, int reading)
+{
 	if(reading) {
 
 		RESET_DMA(ATA_RX_DMA_NBR); /* sometimes the DMA channel get stuck so we need to do this */
@@ -917,8 +870,15 @@
 			ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL);
 			
 			/* issue cmd to drive */
-			
-			OUT_BYTE(WIN_READDMA, IDE_COMMAND_REG);
+                        if ((HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) &&
+			    (drive->addressing == 1)) {
+				ide_task_t *args = HWGROUP(drive)->rq->special;
+				OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG);
+			} else if (drive->addressing) {
+				OUT_BYTE(WIN_READDMA_EXT, IDE_COMMAND_REG);
+			} else {
+				OUT_BYTE(WIN_READDMA, IDE_COMMAND_REG);
+			}
 		}
 
 		/* begin DMA */
@@ -968,8 +928,15 @@
 			ide_set_handler(drive, &etrax_dma_intr, WAIT_CMD, NULL);
 			
 			/* issue cmd to drive */
-			
-			OUT_BYTE(WIN_WRITEDMA, IDE_COMMAND_REG);
+			if ((HWGROUP(drive)->rq->cmd == IDE_DRIVE_TASKFILE) &&
+			    (drive->addressing == 1)) {
+				ide_task_t *args = HWGROUP(drive)->rq->special;
+				OUT_BYTE(args->tfRegister[IDE_COMMAND_OFFSET], IDE_COMMAND_REG);
+			} else if (drive->addressing) {
+				OUT_BYTE(WIN_WRITEDMA_EXT, IDE_COMMAND_REG);
+			} else {
+				OUT_BYTE(WIN_WRITEDMA, IDE_COMMAND_REG);
+			}
 		}
 
 		/* begin DMA */
@@ -994,14 +961,44 @@
 
 		D(printk("dma write of %d bytes.\n", ata_tot_size));
 	}
-
-	/* DMA started successfully */
 	return 0;
 }
 
-/* ide.c calls this, but we don't need to do anything particular */
+static int e100_dma_write(ide_drive_t *drive)
+{
+	e100_read_command = 0;
+	/* ATAPI-devices (not disks) first call ide_dma_read/write to set the direction
+	 * then they call ide_dma_begin after they have issued the appropriate drive command
+	 * themselves to actually start the chipset DMA. so we just return here if we're
+	 * not a diskdrive.
+	 */
+	if (drive->media != ide_disk)
+                return 0;
+	return e100_start_dma(drive, 0, 0);
+}
+
+static int e100_dma_read(ide_drive_t *drive)
+{
+	e100_read_command = 1;
+	/* ATAPI-devices (not disks) first call ide_dma_read/write to set the direction
+	 * then they call ide_dma_begin after they have issued the appropriate drive command
+	 * themselves to actually start the chipset DMA. so we just return here if we're
+	 * not a diskdrive.
+	 */
+	if (drive->media != ide_disk)
+                return 0;
+	return e100_start_dma(drive, 0, 1);
+}
 
-int ide_release_dma (ide_hwif_t *hwif)
+static int e100_dma_begin(ide_drive_t *drive)
 {
-	return 1;
+	/* begin DMA, used by ATAPI devices which want to issue the 
+	 * appropriate IDE command themselves.
+	 *
+	 * they have already called ide_dma_read/write to set the
+	 * static reading flag, now they call ide_dma_begin to do
+	 * the real stuff. we tell our code below not to issue
+	 * any IDE commands itself and jump into it. 
+	 */
+	 return e100_start_dma(drive, 1, e100_read_command);
 }
diff -urN linux-2.4.21-bk5/arch/cris/drivers/lpslave/bintocarr.pl linux-2.4.21-bk6/arch/cris/drivers/lpslave/bintocarr.pl
--- linux-2.4.21-bk5/arch/cris/drivers/lpslave/bintocarr.pl	2001-10-08 11:43:54.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/lpslave/bintocarr.pl	2003-07-10 02:54:58.000000000 -0700
@@ -1,5 +1,5 @@
 #!/usr/bin/perl -w
-# $Id: bintocarr.pl,v 1.4 2001/08/08 08:18:13 bjarne Exp $
+
 # Copy of mkjulbin.pl made by Olof
 # convert a binary stdin to a C-file containing a char array of the input
 # first argument is the symbol name
@@ -27,4 +27,3 @@
 $lensymb = ("_" . ($symbol . "_length"));
 
 print "__asm__(\"\\t.globl $lensymb\\n$lensymb = $bcount\\n\");\n";
-  
diff -urN linux-2.4.21-bk5/arch/cris/drivers/lpslave/e100lpslave.S linux-2.4.21-bk6/arch/cris/drivers/lpslave/e100lpslave.S
--- linux-2.4.21-bk5/arch/cris/drivers/lpslave/e100lpslave.S	2001-07-26 15:10:06.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/lpslave/e100lpslave.S	2003-07-10 02:54:58.000000000 -0700
@@ -1,12 +1,11 @@
-	;; $Id: e100lpslave.S,v 1.3 2001/06/21 16:55:26 olof Exp $
-	;;
-	;; Etrax100 slave network<->parport forwarder
-	;;
-	;; Copyright (c) 1999 Bjorn Wesen, Axis Communications AB
-	;;
-	;; We got 784 bytes (par loader size) to do DMA forwarding
-	;; between DMA0/1 (ethernet) and DMA3/4 (par port 0 RX/1 TX)
-	;;
+;;
+;; Etrax100 slave network<->parport forwarder
+;;
+;; Copyright (c) 1999, 2000, 2001, 2002, 2003 Axis Communications AB
+;;
+;; We got 784 bytes (par loader size) to do DMA forwarding
+;; between DMA0/1 (ethernet) and DMA3/4 (par port 0 RX/1 TX)
+;;
 
 #include <linux/config.h>
 #if 0
diff -urN linux-2.4.21-bk5/arch/cris/drivers/parport.c linux-2.4.21-bk6/arch/cris/drivers/parport.c
--- linux-2.4.21-bk5/arch/cris/drivers/parport.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/parport.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,13 +1,13 @@
-/* $Id: parport.c,v 1.10 2002/10/03 05:54:24 starvik Exp $
- * 
- * Elinux parallel port driver
+/*
+ * Parallel port driver for ETRAX.
+ *
  * NOTE!
  *   Since par0 shares DMA with ser2 and par 1 shares DMA with ser3
  *   this should be handled if both are enabled at the same time.
  *   THIS IS NOT HANDLED YET!
  *
- * Copyright (c) 2001 Axis Communications AB
- * 
+ * Copyright (c) 2001, 2002, 2003 Axis Communications AB
+ *
  * Author: Fredrik Hugosson
  *
  */
@@ -406,7 +406,7 @@
 static void __init
 parport_etrax_show_parallel_version(void)
 {
-	printk("ETRAX 100LX parallel port driver v1.0, (c) 2001 Axis Communications AB\n");
+	printk("ETRAX 100LX parallel port driver v1.0, (c) 2001-2003 Axis Communications AB\n");
 }
 
 #ifdef CONFIG_ETRAX_PAR0_DMA
diff -urN linux-2.4.21-bk5/arch/cris/drivers/pcf8563.c linux-2.4.21-bk6/arch/cris/drivers/pcf8563.c
--- linux-2.4.21-bk5/arch/cris/drivers/pcf8563.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/pcf8563.c	2003-07-10 02:54:58.000000000 -0700
@@ -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-2003, Axis Communications AB
  * All rights reserved.
  *
  * Author: Tobias Anderberg <tobiasa@axis.com>.
  *
- * $Id: pcf8563.c,v 1.4 2002/10/15 09:22:42 tobiasa Exp $
  */
 
 #include <linux/config.h>
@@ -36,19 +35,15 @@
 #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.4 $"
-
-/* 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.15 $"
 
 /* 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 const unsigned char days_in_month[] =
 	{ 0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
 
@@ -64,53 +59,73 @@
 };
 
 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)
-	{
+	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)
 {
-	i2c_writereg(RTC_I2C_WRITE,reg,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);
+		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);
@@ -124,10 +139,14 @@
 pcf8563_init(void)
 {
 	unsigned char ret;
+
+	/* Initiate the i2c protocol. */
+	i2c_init();
+
 	/*
 	 * 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;
@@ -138,37 +157,35 @@
 	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;
 
 	if (register_chrdev(PCF8563_MAJOR, DEVICE_NAME, &pcf8563_fops) < 0) {
-		printk(KERN_INFO "%s: Unable to get major numer %d for RTC device.\n", 
+		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 (rtc_read(RTC_SECONDS) & 0x80)
-		printk(KERN_WARNING "%s: RTC Low Voltage - date/time is not reliable!\n", PCF8563_NAME);
-	
+		printk(KERN_WARNING "%s: RTC Voltage Low - reliable date/time "
+		       "information is no longer guaranteed!\n", PCF8563_NAME);
+
 	return 0;
 
 err:
@@ -200,75 +217,113 @@
 
 	switch (cmd) {
 		case RTC_RD_TIME:
-			{
-				struct rtc_time tm;
+		{
+			struct rtc_time tm;
 
-				get_rtc_time(&tm);
+			get_rtc_time(&tm);
 
-				if (copy_to_user((struct rtc_time *) arg, &tm, sizeof(struct rtc_time))) {
-					return -EFAULT;
-				}
-
-				return 0;
+			if (copy_to_user((struct rtc_time *) arg, &tm, sizeof tm)) {
+				return -EFAULT;
 			}
-			break;
-		case RTC_SET_TIME:
-			{
-				int leap;
-				int century;
-				unsigned long flags;
-				struct rtc_time tm;
-
-				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;
-				
-				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);
 
-				return 0;
+			return 0;
+		}
+
+		case RTC_SET_TIME:
+		{
+#ifdef CONFIG_ETRAX_RTC_READONLY
+			return -EPERM;
+#else
+			int leap;
+			int year;
+			int century;
+			struct rtc_time tm;
+
+			if (!capable(CAP_SYS_TIME))
+				return -EPERM;
+
+			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;
+			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;
+
+			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;
+
+			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);
+
+			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);
 			}
-			break;
+			if (copy_to_user((int *) arg, &vl_bit, sizeof(int)))
+				return -EFAULT;
+
+			return 0;
+		}
+
+		case RTC_VLOW_SET:
+		{
+			/* Clear the VL bit in the seconds register */
+			int ret = rtc_read(RTC_SECONDS);
+
+			rtc_write(RTC_SECONDS, (ret & 0x7F));
+
+			return 0;
+		}
+
 		default:
-				return -ENOTTY;
+			return -ENOTTY;
 	}
 
 	return 0;
 }
 
-int 
+int
 pcf8563_open(struct inode *inode, struct file *filp)
 {
 	MOD_INC_USE_COUNT;
diff -urN linux-2.4.21-bk5/arch/cris/drivers/serial.c linux-2.4.21-bk6/arch/cris/drivers/serial.c
--- linux-2.4.21-bk5/arch/cris/drivers/serial.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/serial.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,12 +1,92 @@
-/* $Id: serial.c,v 1.44 2002/11/21 04:28:06 hp Exp $
+/* $Id: serial.c,v 1.54 2003/07/08 12:42:19 johana Exp $
  *
  * Serial port driver for the ETRAX 100LX chip
  *
- *      Copyright (C) 1998, 1999, 2000, 2001, 2002  Axis Communications AB
+ *    Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003  Axis Communications AB
  *
- *      Many, many authors. Based once upon a time on serial.c for 16x50.
+ *    Many, many authors. Based once upon a time on serial.c for 16x50.
  *
  * $Log: serial.c,v $
+ * Revision 1.54  2003/07/08 12:42:19  johana
+ * Removed some test defines within #if 0.
+ * Moved a comment to correct place.
+ *
+ * Revision 1.53  2003/07/08 09:48:28  starvik
+ * Removed test code
+ *
+ * Revision 1.52  2003/07/08 07:24:47  pkj
+ * Corrected spelling mistakes originally found in 2.5.x
+ *
+ * Revision 1.51  2003/07/07 09:17:12  johana
+ * Config.in:
+ * Don't consider ETRAX_FAST_TIMER experimental.
+ * Add CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED
+ * Add CONFIG_ETRAX_EXTERN_PB6CLK_FREQ
+ * Add CONFIG_ETRAX_SERIAL_PORTx_DMAy_OUT/IN
+ *
+ * serial.h:
+ * Support for with and without DMA.
+ * Added baud_base and custom_divisor for flexible baudrate.
+ * Added flush_time_usec.
+ * Removed unused DMA register pointers.
+ *
+ * serial.c:
+ * Use C99 style initialisation of rs_table, some fields added, some removed.
+ * Always disable interrupts when fiddling with shadow variables.
+ * Support for flexible baudrate using prescaler or external clock.
+ * Only disable the DMA channel if we own it.
+ * Support for either using DMA or char-by-char interrupt.
+ *
+ * DMA changes:
+ * o When transmitting with DMA, don't put all in one descriptor, split
+ *   it so that we can wake up the application before everything is sent.
+ * o When starting a fast timer to flip later, use a longer, fixed time.
+ * o Use flush_time_usec and have a minimum value to prevent excessive load
+ *   on high baudrates.
+ * o Do force_eop_if_needed() after processing to reduce load.
+ * o All DMA interrupts uses SA_SHIRQ | SA_INTERRUPT
+ *
+ * Only have interrupts disabled while processing rx interrupts,
+ * unmask just tx interrupts and unblock serial interrupt and keep
+ * global interrupts enabled while processing tx interrupts to make
+ * char-by-char interrupts usable.
+ *
+ * Flow control:
+ * o Use normal tx interrupt when sending x_char.
+ * o Use throttling within the driver when using DMA.
+ *
+ * Improved debugging with DEBUG_LOG macro.
+ *
+ * Revision 1.50  2003/06/13 10:04:12  johana
+ * Help the user to avoid trouble by:
+ * Preventing FAST_TIMER and FAST_DMA_FLUSH at the same time.
+ * Forcing mixed mode for status/control lines if not all pins are used.
+ *
+ * Som minor changes to reduce diff to os/lx25 version.
+ *
+ * Revision 1.49  2003/05/30 11:31:54  johana
+ * Merged in change-branch--serial9bit that adds CMSPAR support for sticky
+ * parity (mark/space)
+ *
+ * Revision 1.48  2003/05/30 11:03:57  johana
+ * Implemented rs_send_xchar() by disabling the DMA and writing manually.
+ * Added e100_disable_txdma_channel() and e100_enable_txdma_channel().
+ * Fixed rs_throttle() and rs_unthrottle() to properly call rs_send_xchar
+ * instead of setting info->x_char and check the CRTSCTS flag before
+ * controlling the rts pin.
+ *
+ * Revision 1.47  2003/04/09 08:31:14  pkj
+ * Typo correction (taken from Linux 2.5).
+ *
+ * Revision 1.46  2003/04/01 14:12:06  starvik
+ * Added loglevel for lots of printks
+ *
+ * Revision 1.45  2003/01/22 06:54:46  starvik
+ * Fixed warnings issued by GCC 3.2.1
+ *
+ * Revision 1.44.4.1  2003/01/08 06:47:12  starvik
+ * Added mark/space parity (for 9-bit serial)
+ *
  * Revision 1.44  2002/11/21 04:28:06  hp
  * Change static inline to extern inline where otherwise outlined with gcc-3.2
  *
@@ -206,7 +286,7 @@
  *   Items worth noticing:
  *
  *      No Etrax100 port 1 workarounds (does only compile on 2.4 anyway now)
- *      RS485 is not ported (why cant it be done in userspace as on x86 ?)
+ *      RS485 is not ported (why can't it be done in userspace as on x86 ?)
  *      Statistics done through async_icount - if any more stats are needed,
  *      that's the place to put them or in an arch-dep version of it.
  *      timeout_interrupt and the other fast timeout stuff not ported yet
@@ -357,7 +437,7 @@
  *
  */
 
-static char *serial_version = "$Revision: 1.44 $";
+static char *serial_version = "$Revision: 1.54 $";
 
 #include <linux/config.h>
 #include <linux/version.h>
@@ -392,7 +472,7 @@
 
 #include <asm/svinto.h>
 
-/* non-arch dependant serial structures are in linux/serial.h */
+/* non-arch dependent serial structures are in linux/serial.h */
 #include <linux/serial.h>
 /* while we keep our own stuff (struct e100_serial) in a local .h file */
 #include "serial.h"
@@ -406,6 +486,11 @@
 #error "Enable FAST_TIMER to use SERIAL_FAST_TIMER"
 #endif
 #endif
+
+#if defined(CONFIG_ETRAX_FAST_TIMER) && defined(CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST)
+#error "Disable either ETRAX_SERIAL_FLUSH_DMA_FAST or ETRAX_FAST_TIMER"
+#endif
+
 /*
  * All of the compatibilty code so we can compile serial.c against
  * older kernels is hidden in serial_compat.h
@@ -414,8 +499,11 @@
 #include "serial_compat.h"
 #endif
 
+#if 1 /* Set to 0 to avoid inlining - easier to see how size of parts */
 #define _INLINE_ inline
-
+#else
+#define _INLINE_
+#endif
 static DECLARE_TASK_QUEUE(tq_serial);
 
 struct tty_driver serial_driver, callout_driver;
@@ -436,7 +524,7 @@
 //#define SERIAL_DEBUG_DATA
 //#define SERIAL_DEBUG_THROTTLE
 //#define SERIAL_DEBUG_IO  /* Debug for Extra control and status pins */
-#define SERIAL_DEBUG_LINE 0 /* What serport we want to debug */
+#define SERIAL_DEBUG_LINE 3 /* What serport we want to debug */
 
 /* Enable this to use serial interrupts to handle when you
    expect the first received event on the serial port to
@@ -444,14 +532,78 @@
    from eLinux */
 #define SERIAL_HANDLE_EARLY_ERRORS
 
-#define TTY_THROTTLE_LIMIT (TTY_FLIPBUF_SIZE/10)
+/* Defined and used in n_tty.c, but we need it here as well */
+#define TTY_THRESHOLD_THROTTLE 128
 
+/* Due to buffersizes and threshold values, our SERIAL_DESCR_BUF_SIZE
+ * must not be to high or flow control won't work if we leave it to the tty
+ * layer so we have our own throttling in flush_to_flip
+ * TTY_FLIPBUF_SIZE=512,
+ * TTY_THRESHOLD_THROTTLE/UNTHROTTLE=128
+ * BUF_SIZE can't be > 128
+ */
+/* Currently 16 descriptors x 128 bytes = 2048 bytes */
 #define SERIAL_DESCR_BUF_SIZE 256
 
+#define SERIAL_PRESCALE_BASE 3125000 /* 3.125MHz */
+#define DEF_BAUD_BASE SERIAL_PRESCALE_BASE
+
+/* We don't want to load the system with massive fast timer interrupt
+ * on high baudrates so limit it to 250 us (4kHz) */
+#define MIN_FLUSH_TIME_USEC 250
+
 /* Add an x here to log a lot of timer stuff */
 #define TIMERD(x)
+/* Debug details of interrupt handling */
+#define DINTR1(x)  /* irq on/off, errors */
+#define DINTR2(x)    /* tx and rx */
+/* Debug flip buffer stuff */
+#define DFLIP(x)
+/* Debug flow control and overview of data flow */
+#define DFLOW(x)
+#define DBAUD(x)
+#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT
+#define DLOG_INT_TRIG(x) 
+#else
+#define DLOG_INT_TRIG(x)
+#endif
 
+//#define DEBUG_LOG_INCLUDED
+#ifndef DEBUG_LOG_INCLUDED
 #define DEBUG_LOG(line, string, value)
+#else
+struct debug_log_info 
+{
+	unsigned long time;
+	unsigned long timer_data;
+//  int line;
+	const char *string;
+	int value;
+};
+#define DEBUG_LOG_SIZE 4096
+
+struct debug_log_info debug_log[DEBUG_LOG_SIZE];
+int debug_log_pos = 0;
+  
+#define DEBUG_LOG(_line, _string, _value) do { \
+  if ((_line) == SERIAL_DEBUG_LINE) {\
+    debug_log_func(_line, _string, _value); \
+  }\
+}while(0)
+
+void debug_log_func(int line, const char *string, int value)
+{
+	if (debug_log_pos < DEBUG_LOG_SIZE) { 
+		debug_log[debug_log_pos].time = jiffies;
+		debug_log[debug_log_pos].timer_data = *R_TIMER_DATA;
+//    debug_log[debug_log_pos].line = line;
+		debug_log[debug_log_pos].string = string;
+		debug_log[debug_log_pos].value = value;
+		debug_log_pos++; 
+	}
+	/*printk(string, value);*/
+} 
+#endif
 
 #ifndef CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS
 /* Default number of timer ticks before flushing rx fifo 
@@ -461,21 +613,23 @@
 #define CONFIG_ETRAX_SERIAL_RX_TIMEOUT_TICKS 5 
 #endif
 
+unsigned long timer_data_to_ns(unsigned long timer_data);
+
 static void change_speed(struct e100_serial *info);
+static void rs_throttle(struct tty_struct * tty);
 static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
 static int rs_write(struct tty_struct * tty, int from_user,
                     const unsigned char *buf, int count);
-extern inline int raw_write(struct tty_struct * tty, int from_user,
+extern _INLINE_ int rs_raw_write(struct tty_struct * tty, int from_user,
                             const unsigned char *buf, int count);
 #ifdef CONFIG_ETRAX_RS485
 static int e100_write_rs485(struct tty_struct * tty, int from_user,
                             const unsigned char *buf, int count);
 #endif
-static int 
-get_lsr_info(struct e100_serial * info, unsigned int *value);
+static int get_lsr_info(struct e100_serial * info, unsigned int *value);
 
 
-#define DEF_BAUD 0x99   /* 115.2 kbit/s */
+#define DEF_BAUD 115200   /* 115.2 kbit/s */
 #define STD_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
 #define DEF_RX 0x20  /* or SERIAL_CTRL_W >> 8 */
 /* Default value of tx_ctrl register: has txd(bit 7)=1 (idle) as default */
@@ -484,6 +638,7 @@
 /* offsets from R_SERIALx_CTRL */
 
 #define REG_DATA 0
+#define REG_DATA_STATUS32 0 /* this is the 32 bit register R_SERIALx_READ */
 #define REG_TR_DATA 0
 #define REG_STATUS 1
 #define REG_TR_CTRL 1
@@ -519,60 +674,162 @@
  */
 
 
+/* Mask for the irqs possibly enabled in R_IRQ_MASK1_RD etc. */
+static const unsigned long e100_ser_int_mask = 0
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+| IO_MASK(R_IRQ_MASK1_RD, ser0_data) | IO_MASK(R_IRQ_MASK1_RD, ser0_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+| IO_MASK(R_IRQ_MASK1_RD, ser1_data) | IO_MASK(R_IRQ_MASK1_RD, ser1_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+| IO_MASK(R_IRQ_MASK1_RD, ser2_data) | IO_MASK(R_IRQ_MASK1_RD, ser2_ready)
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+| IO_MASK(R_IRQ_MASK1_RD, ser3_data) | IO_MASK(R_IRQ_MASK1_RD, ser3_ready)
+#endif	
+; 
+unsigned long r_alt_ser_baudrate_shadow = 0;
+
 /* this is the data for the four serial ports in the etrax100 */
 /*  DMA2(ser2), DMA4(ser3), DMA6(ser0) or DMA8(ser1) */
 /* R_DMA_CHx_CLR_INTR, R_DMA_CHx_FIRST, R_DMA_CHx_CMD */
 
 static struct e100_serial rs_table[] = {
-	{ DEF_BAUD, (unsigned char *)R_SERIAL0_CTRL, 1U << 12, /* uses DMA 6 and 7 */
-	  R_DMA_CH6_CLR_INTR, R_DMA_CH6_FIRST, R_DMA_CH6_CMD,
-	  R_DMA_CH6_STATUS, R_DMA_CH6_HWSW, R_DMA_CH6_DESCR,
-	  R_DMA_CH7_CLR_INTR, R_DMA_CH7_FIRST, R_DMA_CH7_CMD,
-	  R_DMA_CH7_STATUS, R_DMA_CH7_HWSW, R_DMA_CH7_DESCR,
-	  STD_FLAGS, DEF_RX, DEF_TX, 2,
+	{ .baud        = DEF_BAUD,
+	  .port        = (unsigned char *)R_SERIAL0_CTRL,
+	  .irq         = 1U << 12, /* uses DMA 6 and 7 */
+	  .oclrintradr = R_DMA_CH6_CLR_INTR,
+	  .ofirstadr   = R_DMA_CH6_FIRST,
+	  .ocmdadr     = R_DMA_CH6_CMD,
+	  .ostatusadr  = R_DMA_CH6_STATUS,
+	  .iclrintradr = R_DMA_CH7_CLR_INTR,
+	  .ifirstadr   = R_DMA_CH7_FIRST,
+	  .icmdadr     = R_DMA_CH7_CMD,
+	  .idescradr   = R_DMA_CH7_DESCR,
+	  .flags       = STD_FLAGS,
+	  .rx_ctrl     = DEF_RX,
+	  .tx_ctrl     = DEF_TX,
+	  .iseteop     = 2,
 #ifdef CONFIG_ETRAX_SERIAL_PORT0
-          1
+          .enabled  = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
+	  .dma_out_enabled = 1,
+#else
+	  .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
+	  .dma_in_enabled = 1,
+#else
+	  .dma_in_enabled = 0
+#endif
 #else
-          0
+          .enabled  = 0,
+	  .dma_out_enabled = 0,
+	  .dma_in_enabled = 0	  
 #endif
+
 },  /* ttyS0 */
 #ifndef CONFIG_SVINTO_SIM
-	{ DEF_BAUD, (unsigned char *)R_SERIAL1_CTRL, 1U << 16, /* uses DMA 8 and 9 */
-	  R_DMA_CH8_CLR_INTR, R_DMA_CH8_FIRST, R_DMA_CH8_CMD,
-	  R_DMA_CH8_STATUS, R_DMA_CH8_HWSW, R_DMA_CH8_DESCR,
-	  R_DMA_CH9_CLR_INTR, R_DMA_CH9_FIRST, R_DMA_CH9_CMD,
-	  R_DMA_CH9_STATUS, R_DMA_CH9_HWSW, R_DMA_CH9_DESCR,
-	  STD_FLAGS, DEF_RX, DEF_TX, 3 ,
+	{ .baud        = DEF_BAUD,
+	  .port        = (unsigned char *)R_SERIAL1_CTRL,
+	  .irq         = 1U << 16, /* uses DMA 8 and 9 */
+	  .oclrintradr = R_DMA_CH8_CLR_INTR,
+	  .ofirstadr   = R_DMA_CH8_FIRST,
+	  .ocmdadr     = R_DMA_CH8_CMD,
+	  .ostatusadr  = R_DMA_CH8_STATUS,
+	  .iclrintradr = R_DMA_CH9_CLR_INTR,
+	  .ifirstadr   = R_DMA_CH9_FIRST,
+	  .icmdadr     = R_DMA_CH9_CMD,
+	  .idescradr   = R_DMA_CH9_DESCR,
+	  .flags       = STD_FLAGS,
+	  .rx_ctrl     = DEF_RX,
+	  .tx_ctrl     = DEF_TX,
+	  .iseteop     = 3,
 #ifdef CONFIG_ETRAX_SERIAL_PORT1
-          1
+          .enabled  = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
+	  .dma_out_enabled = 1,
+#else
+	  .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
+	  .dma_in_enabled = 1,
 #else
-          0
+	  .dma_in_enabled = 0
+#endif
+#else
+          .enabled  = 0,
+	  .dma_out_enabled = 0,
+	  .dma_in_enabled = 0
 #endif
 },  /* ttyS1 */
 
-	{ DEF_BAUD, (unsigned char *)R_SERIAL2_CTRL, 1U << 4,  /* uses DMA 2 and 3 */
-	  R_DMA_CH2_CLR_INTR, R_DMA_CH2_FIRST, R_DMA_CH2_CMD,
-	  R_DMA_CH2_STATUS, R_DMA_CH2_HWSW, R_DMA_CH2_DESCR,
-	  R_DMA_CH3_CLR_INTR, R_DMA_CH3_FIRST, R_DMA_CH3_CMD,
-	  R_DMA_CH3_STATUS, R_DMA_CH3_HWSW, R_DMA_CH3_DESCR,
-	  STD_FLAGS, DEF_RX, DEF_TX, 0,
+	{ .baud        = DEF_BAUD,
+	  .port        = (unsigned char *)R_SERIAL2_CTRL,
+	  .irq         = 1U << 4,  /* uses DMA 2 and 3 */
+	  .oclrintradr = R_DMA_CH2_CLR_INTR,
+	  .ofirstadr   = R_DMA_CH2_FIRST,
+	  .ocmdadr     = R_DMA_CH2_CMD,
+	  .ostatusadr  = R_DMA_CH2_STATUS,
+	  .iclrintradr = R_DMA_CH3_CLR_INTR,
+	  .ifirstadr   = R_DMA_CH3_FIRST,
+	  .icmdadr     = R_DMA_CH3_CMD,
+	  .idescradr   = R_DMA_CH3_DESCR,
+	  .flags       = STD_FLAGS,
+	  .rx_ctrl     = DEF_RX,
+	  .tx_ctrl     = DEF_TX,
+	  .iseteop     = 0,
 #ifdef CONFIG_ETRAX_SERIAL_PORT2
-          1
+          .enabled  = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+	  .dma_out_enabled = 1,
+#else
+	  .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
+	  .dma_in_enabled = 1,
 #else
-          0
+	  .dma_in_enabled = 0
+#endif
+#else
+          .enabled  = 0,
+	  .dma_out_enabled = 0,
+	  .dma_in_enabled = 0
 #endif
  },  /* ttyS2 */
 
-	{ DEF_BAUD, (unsigned char *)R_SERIAL3_CTRL, 1U << 8,  /* uses DMA 4 and 5 */
-	  R_DMA_CH4_CLR_INTR, R_DMA_CH4_FIRST, R_DMA_CH4_CMD,
-	  R_DMA_CH4_STATUS, R_DMA_CH4_HWSW, R_DMA_CH4_DESCR,
-	  R_DMA_CH5_CLR_INTR, R_DMA_CH5_FIRST, R_DMA_CH5_CMD,
-	  R_DMA_CH5_STATUS, R_DMA_CH5_HWSW, R_DMA_CH5_DESCR,
-	  STD_FLAGS, DEF_RX, DEF_TX, 1,
+	{ .baud        = DEF_BAUD,
+	  .port        = (unsigned char *)R_SERIAL3_CTRL,
+	  .irq         = 1U << 8,  /* uses DMA 4 and 5 */
+	  .oclrintradr = R_DMA_CH4_CLR_INTR,
+	  .ofirstadr   = R_DMA_CH4_FIRST,
+	  .ocmdadr     = R_DMA_CH4_CMD,
+	  .ostatusadr  = R_DMA_CH4_STATUS,
+	  .iclrintradr = R_DMA_CH5_CLR_INTR,
+	  .ifirstadr   = R_DMA_CH5_FIRST,
+	  .icmdadr     = R_DMA_CH5_CMD,
+	  .idescradr   = R_DMA_CH5_DESCR,
+	  .flags       = STD_FLAGS,
+	  .rx_ctrl     = DEF_RX,
+	  .tx_ctrl     = DEF_TX,
+	  .iseteop     = 1,
 #ifdef CONFIG_ETRAX_SERIAL_PORT3
-          1
+          .enabled  = 1,
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
+	  .dma_out_enabled = 1,
+#else
+	  .dma_out_enabled = 0,
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
+	  .dma_in_enabled = 1,
 #else
-          0
+	  .dma_in_enabled = 0
+#endif
+#else
+          .enabled  = 0,
+	  .dma_out_enabled = 0,
+	  .dma_in_enabled = 0  
 #endif
  }   /* ttyS3 */
 #endif
@@ -627,7 +884,7 @@
  ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
 		(R_PORT_PA_DATA): ( \
  (CONFIG_ETRAX_SER##line##_##pinname##_ON_PB_BIT >= 0)? \
-		(R_PORT_PB_DATA):&dummy_ser[##line##]))
+		(R_PORT_PB_DATA):&dummy_ser[line]))
 
 #define E100_STRUCT_SHADOW(line, pinname) \
  ((CONFIG_ETRAX_SER##line##_##pinname##_ON_PA_BIT >= 0)? \
@@ -646,6 +903,225 @@
 #define DUMMY_CD_MASK  8
 static unsigned char dummy_ser[NR_PORTS] = {0xFF, 0xFF, 0xFF,0xFF};
 
+/* If not all status pins are used or disabled, use mixed mode */
+#ifdef CONFIG_ETRAX_SERIAL_PORT0
+
+#define SER0_PA_BITSUM (CONFIG_ETRAX_SER0_DTR_ON_PA_BIT+CONFIG_ETRAX_SER0_RI_ON_PA_BIT+CONFIG_ETRAX_SER0_DSR_ON_PA_BIT+CONFIG_ETRAX_SER0_CD_ON_PA_BIT)
+
+#if SER0_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER0_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER0_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER0_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER0_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER0_PB_BITSUM (CONFIG_ETRAX_SER0_DTR_ON_PB_BIT+CONFIG_ETRAX_SER0_RI_ON_PB_BIT+CONFIG_ETRAX_SER0_DSR_ON_PB_BIT+CONFIG_ETRAX_SER0_CD_ON_PB_BIT)
+
+#if SER0_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER0_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER0_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER0_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER0_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT0 */
+
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT1
+
+#define SER1_PA_BITSUM (CONFIG_ETRAX_SER1_DTR_ON_PA_BIT+CONFIG_ETRAX_SER1_RI_ON_PA_BIT+CONFIG_ETRAX_SER1_DSR_ON_PA_BIT+CONFIG_ETRAX_SER1_CD_ON_PA_BIT)
+
+#if SER1_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER1_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER1_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER1_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER1_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER1_PB_BITSUM (CONFIG_ETRAX_SER1_DTR_ON_PB_BIT+CONFIG_ETRAX_SER1_RI_ON_PB_BIT+CONFIG_ETRAX_SER1_DSR_ON_PB_BIT+CONFIG_ETRAX_SER1_CD_ON_PB_BIT)
+
+#if SER1_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER1_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER1_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER1_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER1_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT1 */
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT2
+
+#define SER2_PA_BITSUM (CONFIG_ETRAX_SER2_DTR_ON_PA_BIT+CONFIG_ETRAX_SER2_RI_ON_PA_BIT+CONFIG_ETRAX_SER2_DSR_ON_PA_BIT+CONFIG_ETRAX_SER2_CD_ON_PA_BIT)
+
+#if SER2_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER2_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER2_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER2_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER2_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER2_PB_BITSUM (CONFIG_ETRAX_SER2_DTR_ON_PB_BIT+CONFIG_ETRAX_SER2_RI_ON_PB_BIT+CONFIG_ETRAX_SER2_DSR_ON_PB_BIT+CONFIG_ETRAX_SER2_CD_ON_PB_BIT)
+
+#if SER2_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER2_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER2_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER2_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER2_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT2 */
+
+#ifdef CONFIG_ETRAX_SERIAL_PORT3
+
+#define SER3_PA_BITSUM (CONFIG_ETRAX_SER3_DTR_ON_PA_BIT+CONFIG_ETRAX_SER3_RI_ON_PA_BIT+CONFIG_ETRAX_SER3_DSR_ON_PA_BIT+CONFIG_ETRAX_SER3_CD_ON_PA_BIT)
+
+#if SER3_PA_BITSUM != -4
+#  if CONFIG_ETRAX_SER3_DTR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER3_RI_ON_PA_BIT == -1
+#   ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER3_DSR_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER3_CD_ON_PA_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#define SER3_PB_BITSUM (CONFIG_ETRAX_SER3_DTR_ON_PB_BIT+CONFIG_ETRAX_SER3_RI_ON_PB_BIT+CONFIG_ETRAX_SER3_DSR_ON_PB_BIT+CONFIG_ETRAX_SER3_CD_ON_PB_BIT)
+
+#if SER3_PB_BITSUM != -4
+#  if CONFIG_ETRAX_SER3_DTR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#   endif
+# if CONFIG_ETRAX_SER3_RI_ON_PB_BIT == -1
+#   ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#     define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#   endif
+#  endif
+#  if CONFIG_ETRAX_SER3_DSR_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#  if CONFIG_ETRAX_SER3_CD_ON_PB_BIT == -1
+#    ifndef CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED
+#      define CONFIG_ETRAX_SER3_DTR_RI_DSR_CD_MIXED 1
+#    endif
+#  endif
+#endif
+
+#endif /* PORT3 */
+
+
 #if defined(CONFIG_ETRAX_SER0_DTR_RI_DSR_CD_MIXED) || \
     defined(CONFIG_ETRAX_SER1_DTR_RI_DSR_CD_MIXED) || \
     defined(CONFIG_ETRAX_SER2_DTR_RI_DSR_CD_MIXED) || \
@@ -653,6 +1129,7 @@
 #define CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED
 #endif
 
+
 #ifdef CONFIG_ETRAX_SERX_DTR_RI_DSR_CD_MIXED
 /* The pins can be mixed on PA and PB */
 #define CONTROL_PINS_PORT_NOT_USED(line) \
@@ -838,7 +1315,6 @@
 #define E100_RTS_MASK 0x20
 #define E100_CTS_MASK 0x40
 
-
 /* All serial port signals are active low:
  * active   = 0 -> 3.3V to RS-232 driver -> -12V on RS-232 level
  * inactive = 1 -> 0V   to RS-232 driver -> +12V on RS-232 level
@@ -937,6 +1413,9 @@
 
 	/* calc timeout */
 	info->char_time_usec = ((bits * 1000000) / info->baud) + 1;
+	info->flush_time_usec = 4*info->char_time_usec;
+	if (info->flush_time_usec < MIN_FLUSH_TIME_USEC)
+		info->flush_time_usec = MIN_FLUSH_TIME_USEC;
 }
 
 /*
@@ -980,7 +1459,7 @@
 		retval = baud_table[cflag & CBAUD];
 
 	if (retval < 0) {
-		printk("serdriver tried setting invalid baud rate, flags %x.\n", cflag);
+		printk(KERN_WARNING "serdriver tried setting invalid baud rate, flags %x.\n", cflag);
 		retval = 5; /* choose default 9600 instead */
 	}
 
@@ -1036,9 +1515,13 @@
 e100_rts(struct e100_serial *info, int set)
 {
 #ifndef CONFIG_SVINTO_SIM
+	unsigned long flags;
+	save_flags(flags);
+	cli();
 	info->rx_ctrl &= ~E100_RTS_MASK;
 	info->rx_ctrl |= (set ? 0 : E100_RTS_MASK);  /* RTS is active low */
 	info->port[REG_REC_CTRL] = info->rx_ctrl;
+	restore_flags(flags);
 #ifdef SERIAL_DEBUG_IO  
 	printk("ser%i rts %i\n", info->line, set);
 #endif
@@ -1112,6 +1595,7 @@
 #ifdef SERIAL_DEBUG_INTR
 	printk("rxdma_irq(%d): 0\n",info->line);
 #endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ disable_rxdma_irq %i\n", info->line));
 	*R_IRQ_MASK2_CLR = (info->irq << 2) | (info->irq << 3);
 }
 
@@ -1121,6 +1605,7 @@
 #ifdef SERIAL_DEBUG_INTR
 	printk("rxdma_irq(%d): 1\n",info->line);
 #endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ enable_rxdma_irq %i\n", info->line));
 	*R_IRQ_MASK2_SET = (info->irq << 2) | (info->irq << 3);
 }
 
@@ -1132,6 +1617,7 @@
 #ifdef SERIAL_DEBUG_INTR
 	printk("txdma_irq(%d): 0\n",info->line);
 #endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ disable_txdma_irq %i\n", info->line));
 	*R_IRQ_MASK2_CLR = info->irq;
 }
 
@@ -1141,9 +1627,143 @@
 #ifdef SERIAL_DEBUG_INTR
 	printk("txdma_irq(%d): 1\n",info->line);
 #endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ enable_txdma_irq %i\n", info->line));
 	*R_IRQ_MASK2_SET = info->irq;
 }
 
+static _INLINE_ void
+e100_disable_txdma_channel(struct e100_serial *info)
+{
+	unsigned long flags;
+  
+	/* Disable output DMA channel for the serial port in question
+	 * ( set to something other then serialX)
+	 */
+	save_flags(flags);
+	cli();
+	DFLOW(DEBUG_LOG(info->line, "disable_txdma_channel %i\n", info->line));
+	if (info->line == 0) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma6)) ==
+		    IO_STATE(R_GEN_CONFIG, dma6, serial0)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma6);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, unused);
+		}
+	} else if (info->line == 1) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma8)) ==
+		    IO_STATE(R_GEN_CONFIG, dma8, serial1)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma8);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, usb);
+		}
+	} else if (info->line == 2) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma2)) ==
+		    IO_STATE(R_GEN_CONFIG, dma2, serial2)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma2);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, par0);
+		}
+	} else if (info->line == 3) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma4)) ==
+		    IO_STATE(R_GEN_CONFIG, dma4, serial3)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma4);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, par1);
+		}
+	}
+	*R_GEN_CONFIG = genconfig_shadow;
+	restore_flags(flags);
+}
+
+
+static _INLINE_ void
+e100_enable_txdma_channel(struct e100_serial *info)
+{
+	unsigned long flags;
+  
+	save_flags(flags);
+	cli();
+	DFLOW(DEBUG_LOG(info->line, "enable_txdma_channel %i\n", info->line));
+	/* Enable output DMA channel for the serial port in question */
+	if (info->line == 0) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma6);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma6, serial0);
+	} else if (info->line == 1) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma8);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma8, serial1);
+	} else if (info->line == 2) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma2);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma2, serial2);
+	} else if (info->line == 3) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma4);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma4, serial3);
+	}
+	*R_GEN_CONFIG = genconfig_shadow;
+	restore_flags(flags);
+}
+
+static _INLINE_ void
+e100_disable_rxdma_channel(struct e100_serial *info)
+{
+	unsigned long flags;
+  
+	/* Disable input DMA channel for the serial port in question
+	 * ( set to something other then serialX)
+	 */
+	save_flags(flags);
+	cli();  
+	if (info->line == 0) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma7)) ==
+		    IO_STATE(R_GEN_CONFIG, dma7, serial0)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma7);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, unused);
+		}
+	} else if (info->line == 1) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma9)) ==
+		    IO_STATE(R_GEN_CONFIG, dma9, serial1)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma9);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, usb);
+		}
+	} else if (info->line == 2) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma3)) ==
+		    IO_STATE(R_GEN_CONFIG, dma3, serial2)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma3);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, par0);
+		}
+	} else if (info->line == 3) {
+		if ((genconfig_shadow & IO_MASK(R_GEN_CONFIG, dma5)) ==
+		    IO_STATE(R_GEN_CONFIG, dma5, serial3)) {
+			genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma5);
+			genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, par1);
+		}
+	}
+	*R_GEN_CONFIG = genconfig_shadow;
+	restore_flags(flags);
+}
+
+
+static _INLINE_ void
+e100_enable_rxdma_channel(struct e100_serial *info)
+{
+	unsigned long flags;
+  
+	save_flags(flags);
+	cli();  
+	/* Enable input DMA channel for the serial port in question */
+	if (info->line == 0) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma7);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma7, serial0);
+	} else if (info->line == 1) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma9);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma9, serial1);
+	} else if (info->line == 2) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma3);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma3, serial2);
+	} else if (info->line == 3) {
+		genconfig_shadow &=  ~IO_MASK(R_GEN_CONFIG, dma5);
+		genconfig_shadow |= IO_STATE(R_GEN_CONFIG, dma5, serial3);
+	}
+	*R_GEN_CONFIG = genconfig_shadow;
+	restore_flags(flags);
+}
+
+
 #ifdef SERIAL_HANDLE_EARLY_ERRORS
 /* in order to detect and fix errors on the first byte
    we have to use the serial interrupts as well. */
@@ -1154,6 +1774,7 @@
 #ifdef SERIAL_DEBUG_INTR
 	printk("ser_irq(%d): 0\n",info->line);
 #endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ disable data_irq %i\n", info->line));
 	*R_IRQ_MASK1_CLR = (1U << (8+2*info->line));
 }
 
@@ -1166,10 +1787,50 @@
 	       (8+2*info->line),
 	       (1U << (8+2*info->line)));
 #endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ enable data_irq %i\n", info->line));
 	*R_IRQ_MASK1_SET = (1U << (8+2*info->line));
 }
 #endif
 
+static inline void
+e100_disable_serial_tx_ready_irq(struct e100_serial *info) 
+{
+#ifdef SERIAL_DEBUG_INTR
+	printk("ser_tx_irq(%d): 0\n",info->line);
+#endif
+	DINTR1(DEBUG_LOG(info->line,"IRQ disable ready_irq %i\n", info->line));
+	*R_IRQ_MASK1_CLR = (1U << (8+1+2*info->line));
+}
+
+static inline void
+e100_enable_serial_tx_ready_irq(struct e100_serial *info) 
+{
+#ifdef SERIAL_DEBUG_INTR
+	printk("ser_tx_irq(%d): 1\n",info->line);
+	printk("**** %d = %d\n",
+	       (8+1+2*info->line),
+	       (1U << (8+1+2*info->line)));
+#endif
+	DINTR2(DEBUG_LOG(info->line,"IRQ enable ready_irq %i\n", info->line));
+	*R_IRQ_MASK1_SET = (1U << (8+1+2*info->line));
+}
+
+static inline void e100_enable_rx_irq(struct e100_serial *info)
+{
+	if (info->uses_dma_in)
+		e100_enable_rxdma_irq(info);
+	else
+		e100_enable_serial_data_irq(info);
+}
+static inline void e100_disable_rx_irq(struct e100_serial *info)
+{
+	if (info->uses_dma_in)
+		e100_disable_rxdma_irq(info);
+	else
+		e100_disable_serial_data_irq(info);
+}
+
+
 #if defined(CONFIG_ETRAX_RS485)
 /* Enable RS-485 mode on selected port. This is UGLY. */
 static int
@@ -1183,7 +1844,10 @@
 
 	info->rs485.rts_on_send = 0x01 & r->rts_on_send;
 	info->rs485.rts_after_sent = 0x01 & r->rts_after_sent;
-	info->rs485.delay_rts_before_send = r->delay_rts_before_send;
+	if (r->delay_rts_before_send >= 1000)
+		info->rs485.delay_rts_before_send = 1000;
+	else
+		info->rs485.delay_rts_before_send = r->delay_rts_before_send;
 	info->rs485.enabled = r->enabled;
 /*	printk("rts: on send = %i, after = %i, enabled = %i",
 		    info->rs485.rts_on_send,
@@ -1223,7 +1887,7 @@
 	e100_rts(info, info->rs485.rts_after_sent);
 #if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
 	e100_enable_rx(info);
-	e100_enable_rxdma_irq(info);
+	e100_enable_rx_irq(info);	
 #endif
 }
 #endif
@@ -1245,8 +1909,12 @@
 	if (info) {
 		unsigned long flags;
 		unsigned long xoff;
-		
+
 		save_flags(flags); cli();
+		DFLOW(DEBUG_LOG(info->line, "XOFF rs_stop xmit %i\n",
+				CIRC_CNT(info->xmit.head,
+					 info->xmit.tail,SERIAL_XMIT_SIZE)));
+		
 		xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->tty));
 		xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, stop);
 		if (tty->termios->c_iflag & IXON ) {
@@ -1267,6 +1935,9 @@
 		unsigned long xoff;
 
 		save_flags(flags); cli();
+		DFLOW(DEBUG_LOG(info->line, "XOFF rs_start xmit %i\n",
+				CIRC_CNT(info->xmit.head,
+					 info->xmit.tail,SERIAL_XMIT_SIZE)));
 		xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(tty));
 		xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
 		if (tty->termios->c_iflag & IXON ) {
@@ -1274,6 +1945,9 @@
 		}
 	
 		*((unsigned long *)&info->port[REG_XOFF]) = xoff;
+		if (!info->uses_dma_out &&
+		    info->xmit.head != info->xmit.tail && info->xmit.buf)
+			e100_enable_serial_tx_ready_irq(info);
 		
 		restore_flags(flags);
 	}
@@ -1308,6 +1982,8 @@
 rs_sched_event(struct e100_serial *info,
 				    int event)
 {
+	if (info->event & (1 << event))
+		return;
 	info->event |= 1 << event;
 	queue_task(&info->tqueue, &tq_serial);
 	mark_bh(SERIAL_BH);
@@ -1325,7 +2001,7 @@
  */
 
 static void 
-transmit_chars(struct e100_serial *info)
+transmit_chars_dma(struct e100_serial *info)
 {
 	unsigned int c, sentl;
 	struct etrax_dma_descr *descr;
@@ -1333,11 +2009,11 @@
 #ifdef CONFIG_SVINTO_SIM
 	/* This will output too little if tail is not 0 always since
 	 * we don't reloop to send the other part. Anyway this SHOULD be a
-	 * no-op - transmit_chars would never really be called during sim
+	 * no-op - transmit_chars_dma would never really be called during sim
 	 * since rs_write does not write into the xmit buffer then.
 	 */
 	if (info->xmit.tail)
-		printk("Error in serial.c:transmit_chars(), tail!=0\n");
+		printk("Error in serial.c:transmit_chars_dma(), tail!=0\n");
 	if (info->xmit.head != info->xmit.tail) {
 		SIMCOUT(info->xmit.buf + info->xmit.tail,
 			CIRC_CNT(info->xmit.head,
@@ -1359,7 +2035,7 @@
 #endif
 	if (!info->tr_running) {
 		/* weirdo... we shouldn't get here! */
-		printk("Achtung: transmit_chars with !tr_running\n");
+		printk(KERN_WARNING "Achtung: transmit_chars_dma with !tr_running\n");
 		return;
 	}
 
@@ -1375,6 +2051,8 @@
 		/* otherwise we find the amount of data sent here */
 		sentl = descr->hw_len;
 
+	DFLOW(DEBUG_LOG(info->line, "TX %i done\n", sentl));
+		
 	/* update stats */
 	info->icount.tx += sentl;
 
@@ -1392,6 +2070,13 @@
 
 	c = CIRC_CNT_TO_END(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
 
+	/* Don't send all in one DMA transfer - divide it so we wake up
+	 * application before all is sent
+	 */
+
+	if (c >= 4*WAKEUP_CHARS)
+		c = c/2;
+
 	if (c <= 0) {
 		/* our job here is done, don't schedule any new DMA transfer */
 		info->tr_running = 0;
@@ -1411,17 +2096,17 @@
 
 	/* ok we can schedule a dma send of c chars starting at info->xmit.tail */
 	/* set up the descriptor correctly for output */
-
+	DFLOW(DEBUG_LOG(info->line, "TX %i\n", c));
 	descr->ctrl = d_int | d_eol | d_wait; /* Wait needed for tty_wait_until_sent() */
 	descr->sw_len = c;
 	descr->buf = virt_to_phys(info->xmit.buf + info->xmit.tail);
 	descr->status = 0;
 
 	*info->ofirstadr = virt_to_phys(descr); /* write to R_DMAx_FIRST */
-	*info->ocmdadr = 1;       /* dma command start -> R_DMAx_CMD */
+	*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, start);       /* dma command start -> R_DMAx_CMD */
 	
 	/* DMA is now running (hopefully) */
-} /* transmit_chars */
+} /* transmit_chars_dma */
 
 static void 
 start_transmit(struct e100_serial *info)
@@ -1435,15 +2120,17 @@
 	info->tr_descr.hw_len = 0;
 	info->tr_descr.status = 0;
 	info->tr_running = 1;
-
-	transmit_chars(info);
+	if (info->uses_dma_out)
+		transmit_chars_dma(info);
+	else
+		e100_enable_serial_tx_ready_irq(info);
 } /* start_transmit */
 
 #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
 static int serial_fast_timer_started = 0;
 static int serial_fast_timer_expired = 0;
 static void flush_timeout_function(unsigned long data);
-#define START_FLUSH_FAST_TIMER(info, string) {\
+#define START_FLUSH_FAST_TIMER_TIME(info, string, usec) {\
   unsigned long timer_flags; \
   save_flags(timer_flags); \
   cli(); \
@@ -1454,7 +2141,7 @@
     start_one_shot_timer(&fast_timers[info->line], \
                          flush_timeout_function, \
                          (unsigned long)info, \
-                         info->char_time_usec*4, \
+                         (usec), \
                          string); \
   } \
   else { \
@@ -1462,8 +2149,10 @@
   } \
   restore_flags(timer_flags); \
 }
+#define START_FLUSH_FAST_TIMER(info, string) START_FLUSH_FAST_TIMER_TIME(info, string, info->flush_time_usec)
 
 #else
+#define START_FLUSH_FAST_TIMER_TIME(info, string, usec)
 #define START_FLUSH_FAST_TIMER(info, string)
 #endif
 
@@ -1508,18 +2197,27 @@
 add_char_and_flag(struct e100_serial *info, unsigned char data, unsigned char flag)
 {
 	struct etrax_recv_buffer *buffer;
+	if (info->uses_dma_in) {
+		if (!(buffer = alloc_recv_buffer(4)))
+			return 0;
 
-	if (!(buffer = alloc_recv_buffer(4)))
-		return 0;
-
-	buffer->length = 1;
-	buffer->error = flag;
-	buffer->buffer[0] = data;
+		buffer->length = 1;
+		buffer->error = flag;
+		buffer->buffer[0] = data;
 	
-	append_recv_buffer(info, buffer);
-
-	info->icount.rx++;
+		append_recv_buffer(info, buffer);
 
+		info->icount.rx++;
+	} else {
+		struct tty_struct *tty = info->tty;		
+		*tty->flip.char_buf_ptr = data;
+		*tty->flip.flag_buf_ptr = flag;
+		tty->flip.flag_buf_ptr++;
+		tty->flip.char_buf_ptr++;
+		tty->flip.count++;
+		info->icount.rx++;
+	}
+	
 	return 1;
 }
 
@@ -1529,7 +2227,8 @@
 	struct etrax_recv_buffer *buffer = phys_to_virt(descr->buf) - sizeof *buffer;
 
 	if (info->recv_cnt + recvl > 65536) {
-		printk(__FUNCTION__ ": Too much pending incoming serial data! Dropping %u bytes.\n", recvl);
+		printk(KERN_CRIT
+		       "%s: Too much pending incoming serial data! Dropping %u bytes.\n", __FUNCTION__, recvl);
 		return 0;
 	}
 
@@ -1542,7 +2241,7 @@
 	append_recv_buffer(info, buffer);
 
 	if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE)))
-		panic(__FUNCTION__ ": Failed to allocate memory for receive buffer!\n");
+		panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__);
 
 	descr->buf = virt_to_phys(buffer->buffer);
 	
@@ -1579,8 +2278,16 @@
 		/* Reset the status information */
 		descr->status = 0;
 
-		DEBUG_LOG(info->line, "recvl %lu\n", recvl);
-
+		DFLOW(  DEBUG_LOG(info->line, "RX %lu\n", recvl);
+			if (info->tty->stopped) {
+				unsigned char *buf = phys_to_virt(descr->buf);
+				DEBUG_LOG(info->line, "rx 0x%02X\n", buf[0]);
+				DEBUG_LOG(info->line, "rx 0x%02X\n", buf[1]);
+				DEBUG_LOG(info->line, "rx 0x%02X\n", buf[2]);
+			}
+			);
+		
+		
 		/* update stats */
 		info->icount.rx += recvl;
 
@@ -1591,7 +2298,7 @@
 }
 
 static _INLINE_ void 
-receive_chars(struct e100_serial *info)
+receive_chars_dma(struct e100_serial *info)
 {
 	struct tty_struct *tty;
 	unsigned char rstat;
@@ -1623,7 +2330,10 @@
 
 	/* Read the status register to detect errors */
 	rstat = info->port[REG_STATUS];
-
+	if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) {
+		DFLOW(DEBUG_LOG(info->line, "XOFF detect stat %x\n", rstat));
+	}
+		
 	if (rstat & SER_ERROR_MASK) {
 		/* If we got an error, we must reset it by reading the
 		 * data_in field
@@ -1658,7 +2368,7 @@
 	/* Set up the receiving descriptors */
 	for (i = 0; i < SERIAL_RECV_DESCRIPTORS; i++) {
 		if (!(buffer = alloc_recv_buffer(SERIAL_DESCR_BUF_SIZE)))
-			panic(__FUNCTION__ ": Failed to allocate memory for receive buffer!\n");
+			panic("%s: Failed to allocate memory for receive buffer!\n", __FUNCTION__);
 
 		descr[i].ctrl = d_int;
 		descr[i].buf = virt_to_phys(buffer->buffer);
@@ -1691,20 +2401,20 @@
 	 */
 	return;
 #endif
+	info->tty->flip.count = 0;	
+	if (info->uses_dma_in) {
+		/* reset the input dma channel to be sure it works */
+
+		*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
+		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
 
-	/* reset the input dma channel to be sure it works */
-
-	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
-	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-
-	info->tty->flip.count = 0;
-
-	start_recv_dma(info);
+		start_recv_dma(info);
 	
 #ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
-	start_flush_timer();
+		start_flush_timer();
 #endif
+	}
 }
 
 
@@ -1749,25 +2459,25 @@
 	
 	for (i = 0; i < NR_PORTS; i++) {
 		info = rs_table + i;
-		if (!info->enabled || !info->uses_dma) 
+		if (!info->enabled || !info->uses_dma_out) 
 			continue; 
 		/* check for dma_descr (don't need to check for dma_eop in output dma for serial */
 		if (ireg & info->irq) {  
 			/* we can send a new dma bunch. make it so. */
-			DEBUG_LOG(info->line, "tr_interrupt %i\n", i);
+			DINTR2(DEBUG_LOG(info->line, "tr_interrupt %i\n", i));
 			/* Read jiffies_usec first, 
 			 * we want this time to be as late as possible
 			 */
  			PROCSTAT(ser_stat[info->line].tx_dma_ints++);
 			info->last_tx_active_usec = GET_JIFFIES_USEC();
 			info->last_tx_active = jiffies;
-			transmit_chars(info);
+			transmit_chars_dma(info);
 		}
 		
 		/* FIXME: here we should really check for a change in the
 		   status lines and if so call status_handle(info) */
 	}
-}
+} /* tr_interrupt */
 
 /* dma input channel interrupt handler */
 
@@ -1795,18 +2505,18 @@
 	
 	for (i = 0; i < NR_PORTS; i++) {
 		info = rs_table + i;
-		if (!info->enabled || !info->uses_dma) 
+		if (!info->enabled || !info->uses_dma_in) 
 			continue; 
 		/* check for both dma_eop and dma_descr for the input dma channel */
 		if (ireg & ((info->irq << 2) | (info->irq << 3))) {
 			/* we have received something */
-			receive_chars(info);
+			receive_chars_dma(info);
 		}
 		
 		/* FIXME: here we should really check for a change in the
 		   status lines and if so call status_handle(info) */
 	}
-}
+} /* rec_interrupt */
 
 static _INLINE_ int
 force_eop_if_needed(struct e100_serial *info)
@@ -1846,7 +2556,7 @@
 	if (!info->forced_eop) {
 		info->forced_eop = 1;
 		PROCSTAT(ser_stat[info->line].timeout_flush_cnt++);
-		DEBUG_LOG(info->line, "timeout EOP %i\n", info->line);
+		TIMERD(DEBUG_LOG(info->line, "timeout EOP %i\n", info->line));
 		FORCE_EOP(info);
 	}
 
@@ -1860,7 +2570,8 @@
 	struct etrax_recv_buffer *buffer;
 	unsigned int length;
 	unsigned long flags;
-
+	int max_flip_size;
+	
 	if (!info->first_recv_buffer)
 		return;
 
@@ -1873,12 +2584,46 @@
 	}
 
 	length = tty->flip.count;
+	/* Don't flip more than the ldisc has room for.
+	 * The return value from ldisc.receive_room(tty) - might not be up to
+	 * date, the previous flip of up to TTY_FLIPBUF_SIZE might be on the
+	 * processed and not accounted for yet.
+	 * Since we use DMA, 1 SERIAL_DESCR_BUF_SIZE could be on the way.
+	 * Lets buffer data here and let flow control take care of it.
+	 * Since we normally flip large chunks, the ldisc don't react
+	 * with throttle until too late if we flip to much.
+	 */
+	max_flip_size = tty->ldisc.receive_room(tty); 
+	if (max_flip_size < 0)
+		max_flip_size = 0;
+	if (max_flip_size <= (TTY_FLIPBUF_SIZE +         /* Maybe not accounted for */
+			      length + info->recv_cnt +  /* We have this queued */
+			      2*SERIAL_DESCR_BUF_SIZE +    /* This could be on the way */
+			      TTY_THRESHOLD_THROTTLE)) { /* Some slack */
+		/* check TTY_THROTTLED first so it indicates our state */
+		if (!test_and_set_bit(TTY_THROTTLED, &tty->flags)) {
+			DFLOW(DEBUG_LOG(info->line,"flush_to_flip throttles room %lu\n", max_flip_size));
+			rs_throttle(tty);
+		}
+#if 0		
+		else if (max_flip_size <= (TTY_FLIPBUF_SIZE +         /* Maybe not accounted for */
+					   length + info->recv_cnt +  /* We have this queued */
+					   SERIAL_DESCR_BUF_SIZE +    /* This could be on the way */
+					   TTY_THRESHOLD_THROTTLE)) { /* Some slack */
+			DFLOW(DEBUG_LOG(info->line,"flush_to_flip throttles again! %lu\n", max_flip_size));
+			rs_throttle(tty);
+		}
+#endif		
+	}
 
-	while ((buffer = info->first_recv_buffer) && length < TTY_FLIPBUF_SIZE) {
+	if (max_flip_size > TTY_FLIPBUF_SIZE)
+		max_flip_size = TTY_FLIPBUF_SIZE;
+
+	while ((buffer = info->first_recv_buffer) && length < max_flip_size) {
 		unsigned int count = buffer->length;
 
-		if (length + count > TTY_FLIPBUF_SIZE)
-			count = TTY_FLIPBUF_SIZE - length;
+		if (length + count > max_flip_size)
+			count = max_flip_size - length;
 
 		memcpy(tty->flip.char_buf_ptr + length, buffer->buffer, count);
 		memset(tty->flip.flag_buf_ptr + length, TTY_NORMAL, count);
@@ -1886,7 +2631,8 @@
 
 		length += count;
 		info->recv_cnt -= count;
-
+		DFLIP(DEBUG_LOG(info->line,"flip: %i\n", length));
+		
 		if (count == buffer->length) {
 			info->first_recv_buffer = buffer->next;
 			kfree(buffer);
@@ -1901,10 +2647,31 @@
 		info->last_recv_buffer = NULL;
 
 	tty->flip.count = length;
-
+	DFLIP(if (tty->ldisc.chars_in_buffer(tty) > 3500) {
+		DEBUG_LOG(info->line, "ldisc %lu\n",
+			  tty->ldisc.chars_in_buffer(tty));
+		DEBUG_LOG(info->line, "flip.count %lu\n",
+			  tty->flip.count);
+	      }
+	      );
 	restore_flags(flags);
 
 #if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,66)
+	DFLIP(
+	  if (1) {
+	  
+		  if (test_bit(TTY_DONT_FLIP, &tty->flags)) {
+			  DEBUG_LOG(info->line, "*** TTY_DONT_FLIP set flip.count %i ***\n", tty->flip.count);
+			  DEBUG_LOG(info->line, "*** recv_cnt %i\n", info->recv_cnt);
+		  } else {
+		  }
+		  DEBUG_LOG(info->line, "*** rxtot %i\n", info->icount.rx);
+		  DEBUG_LOG(info->line, "ldisc %lu\n", tty->ldisc.chars_in_buffer(tty));
+		  DEBUG_LOG(info->line, "room  %lu\n", tty->ldisc.receive_room(tty));
+	  }
+	  
+	);
+	  
 	/* this includes a check for low-latency */
 	tty_flip_buffer_push(tty);
 #else
@@ -1915,12 +2682,19 @@
 static _INLINE_ void
 check_flush_timeout(struct e100_serial *info)
 {
-	force_eop_if_needed(info);
-
+	/* Flip what we've got (if we can) */
 	flush_to_flip_buffer(info);
 
-	if (info->first_recv_buffer)
-		START_FLUSH_FAST_TIMER(info, "flip");
+	/* We might need to flip later, but not to fast
+	 * since the system is busy processing input... */
+	if (info->first_recv_buffer) 
+		START_FLUSH_FAST_TIMER_TIME(info, "flip", 2000);
+
+	/* Force eop last, since data might have come while we're processing
+	 * and if we started the slow timer above, we won't start a fast
+	 * below.
+	 */
+	force_eop_if_needed(info);	
 }
 
 #ifdef CONFIG_ETRAX_SERIAL_FAST_TIMER
@@ -1960,7 +2734,7 @@
 	
 	for (i = 0; i < NR_PORTS; i++) {
 		info = rs_table + i;
-		if (info->enabled && info->uses_dma) 
+		if (info->enabled && info->uses_dma_in) 
 			check_flush_timeout(info);
 	}
 } /* timeout_interrupt */
@@ -1988,7 +2762,7 @@
 	
 	for (i = 0; i < NR_PORTS; i++) {
 		info = rs_table + i;
-		if (info->uses_dma) 
+		if (info->uses_dma_in) 
 			check_flush_timeout(info);
 	}
 
@@ -2017,7 +2791,7 @@
 
 B= Break character (0x00) with framing error.
 E= Error byte with parity error received after B characters.
-F= "Faked" valid byte received immediatly after B characters.
+F= "Faked" valid byte received immediately after B characters.
 V= Valid byte
 
 1.
@@ -2054,7 +2828,7 @@
 
 To distinguish a V byte in 1. from an F byte in 2. we keep a timestamp
 of the last faulty char (B) and compares it with the current time:
-If the time elapsed time is less then 2*char_time_usec we will assume
+If the time elapsed time is less than 2*char_time_usec we will assume
 it's a faked F char and not a Valid char and set 
 info->errorcode = ERRCODE_SET_BREAK. 
 
@@ -2066,15 +2840,159 @@
 TODO: The break will be delayed until an F or V character is received.
 
 */
+	
+extern _INLINE_
+struct e100_serial * handle_ser_rx_interrupt_no_dma(struct e100_serial *info)
+{
+	unsigned long data_read;
+	struct tty_struct *tty = info->tty;	  
+
+	if (!tty) {
+		printk("!NO TTY!\n");
+		return info;
+	}
+	if (tty->flip.count >= TTY_FLIPBUF_SIZE - TTY_THRESHOLD_THROTTLE) {
+		/* check TTY_THROTTLED first so it indicates our state */
+		if (!test_and_set_bit(TTY_THROTTLED, &tty->flags)) {
+			DFLOW(DEBUG_LOG(info->line, "rs_throttle flip.count: %i\n", tty->flip.count));
+			rs_throttle(tty);
+		}
+	}
+	if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+		DEBUG_LOG(info->line, "force FLIP! %i\n", tty->flip.count);
+		tty->flip.tqueue.routine((void *) tty);
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE) {
+			DEBUG_LOG(info->line, "FLIP FULL! %i\n", tty->flip.count);
+			return info;		/* if TTY_DONT_FLIP is set */
+		}
+	}
+	/* Read data and status at the same time */
+	data_read = *((unsigned long *)&info->port[REG_DATA_STATUS32]);
+more_data:
+	if (data_read & IO_MASK(R_SERIAL0_READ, xoff_detect) ) {
+		DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0));
+	}
+	DINTR2(DEBUG_LOG(info->line, "ser_rx   %c\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read)));
+
+	if (data_read & ( IO_MASK(R_SERIAL0_READ, framing_err) |
+			  IO_MASK(R_SERIAL0_READ, par_err) |
+			  IO_MASK(R_SERIAL0_READ, overrun) )) {
+		/* An error */
+		info->last_rx_active_usec = GET_JIFFIES_USEC();
+		info->last_rx_active = jiffies;
+		DINTR1(DEBUG_LOG(info->line, "ser_rx err stat_data %04X\n", data_read));
+		DLOG_INT_TRIG(
+		if (!log_int_trig1_pos) {
+			log_int_trig1_pos = log_int_pos;
+			log_int(rdpc(), 0, 0);
+		}
+		);
+		
+		
+		if ( ((data_read & IO_MASK(R_SERIAL0_READ, data_in)) == 0) &&
+		     (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) ) {
+			/* Most likely a break, but we get interrupts over and
+			 * over again.
+			 */
 
-extern void _INLINE_ handle_ser_interrupt(struct e100_serial *info)
+			if (!info->break_detected_cnt) {
+				DEBUG_LOG(info->line, "#BRK start\n", 0);
+			}
+			if (data_read & IO_MASK(R_SERIAL0_READ, rxd)) {
+				/* The RX pin is high now, so the break
+				 * must be over, but....
+				 * we can't really know if we will get another
+				 * last byte ending the break or not. 
+				 * And we don't know if the byte (if any) will 
+				 * have an error or look valid.
+				 */
+				DEBUG_LOG(info->line, "# BL BRK\n", 0);
+				info->errorcode = ERRCODE_INSERT_BREAK;
+			}
+			info->break_detected_cnt++;
+		} else {
+			/* The error does not look like a break, but could be
+			 * the end of one
+			 */
+			if (info->break_detected_cnt) {
+				DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
+				info->errorcode = ERRCODE_INSERT_BREAK;
+			} else {
+				if (info->errorcode == ERRCODE_INSERT_BREAK) {
+					info->icount.brk++;
+					*tty->flip.char_buf_ptr = 0;
+					*tty->flip.flag_buf_ptr = TTY_BREAK;
+					tty->flip.flag_buf_ptr++;
+					tty->flip.char_buf_ptr++;
+					tty->flip.count++;
+					info->icount.rx++;
+				}
+				*tty->flip.char_buf_ptr = IO_EXTRACT(R_SERIAL0_READ, data_in, data_read);
+
+				if (data_read & IO_MASK(R_SERIAL0_READ, par_err)) {
+					info->icount.parity++;
+					*tty->flip.flag_buf_ptr = TTY_PARITY;
+				} else if (data_read & IO_MASK(R_SERIAL0_READ, overrun)) {
+					info->icount.overrun++;
+					*tty->flip.flag_buf_ptr = TTY_OVERRUN;
+				} else if (data_read & IO_MASK(R_SERIAL0_READ, framing_err)) {
+					info->icount.frame++;
+					*tty->flip.flag_buf_ptr = TTY_FRAME;
+				}
+				info->errorcode = 0;
+			}
+			info->break_detected_cnt = 0;
+		}
+	} else if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) {
+		/* No error */
+		DLOG_INT_TRIG(
+		if (!log_int_trig1_pos) {
+			if (log_int_pos >= log_int_size) {
+				log_int_pos = 0;
+			}
+			log_int_trig0_pos = log_int_pos;
+			log_int(rdpc(), 0, 0);
+		}
+		);
+		*tty->flip.char_buf_ptr = IO_EXTRACT(R_SERIAL0_READ, data_in, data_read);
+		*tty->flip.flag_buf_ptr = 0;
+	} else {
+		DEBUG_LOG(info->line, "ser_rx int but no data_avail  %08lX\n", data_read);
+	}
+	
+	
+	tty->flip.flag_buf_ptr++;
+	tty->flip.char_buf_ptr++;
+	tty->flip.count++;
+	info->icount.rx++;
+	data_read = *((unsigned long *)&info->port[REG_DATA_STATUS32]);
+	if (data_read & IO_MASK(R_SERIAL0_READ, data_avail)) {
+		DEBUG_LOG(info->line, "ser_rx   %c in loop\n", IO_EXTRACT(R_SERIAL0_READ, data_in, data_read));
+		goto more_data;
+	}
+	
+	tty_flip_buffer_push(info->tty);
+	return info;
+}
+
+extern _INLINE_ 
+struct e100_serial* handle_ser_rx_interrupt(struct e100_serial *info)
 {
-	unsigned char rstat = info->port[REG_STATUS];
+	unsigned char rstat;
 
 #ifdef SERIAL_DEBUG_INTR
 	printk("Interrupt from serport %d\n", i);
 #endif
 /*	DEBUG_LOG(info->line, "ser_interrupt stat %03X\n", rstat | (i << 8)); */
+	if (!info->uses_dma_in) {
+		return handle_ser_rx_interrupt_no_dma(info);
+	}
+	/* DMA is used */
+	rstat = info->port[REG_STATUS];
+	if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) ) {
+		DFLOW(DEBUG_LOG(info->line, "XOFF detect\n", 0));
+	}
+
 	if (rstat & SER_ERROR_MASK) {
 		unsigned char data;
 
@@ -2084,7 +3002,8 @@
 		 * data_in field
 		 */
 		data = info->port[REG_DATA];
-
+		DINTR1(DEBUG_LOG(info->line, "ser_rx!  %c\n", data));
+		DINTR1(DEBUG_LOG(info->line, "ser_rx err stat %02X\n", rstat));
 		if (!data && (rstat & SER_FRAMING_ERR_MASK)) {
 			/* Most likely a break, but we get interrupts over and
 			 * over again.
@@ -2113,15 +3032,22 @@
 				DEBUG_LOG(info->line, "EBRK %i\n", info->break_detected_cnt);
 				info->errorcode = ERRCODE_INSERT_BREAK;
 			} else {
-				if (info->errorcode == ERRCODE_INSERT_BREAK)
+				if (info->errorcode == ERRCODE_INSERT_BREAK) {
+					info->icount.brk++;
 					add_char_and_flag(info, '\0', TTY_BREAK);
+				}
 
-				if (rstat & SER_PAR_ERR_MASK)
+				if (rstat & SER_PAR_ERR_MASK) {
+					info->icount.parity++;
 					add_char_and_flag(info, data, TTY_PARITY);
-				else if (rstat & SER_OVERRUN_MASK)
+				} else if (rstat & SER_OVERRUN_MASK) {
+					info->icount.overrun++;
 					add_char_and_flag(info, data, TTY_OVERRUN);
-				else if (rstat & SER_FRAMING_ERR_MASK)
+				} else if (rstat & SER_FRAMING_ERR_MASK) {
+					info->icount.frame++;
 					add_char_and_flag(info, data, TTY_FRAME);
+				}
+				
 				info->errorcode = 0;
 			}
 			info->break_detected_cnt = 0;
@@ -2145,7 +3071,7 @@
 			if (elapsed_usec < 2*info->char_time_usec) {
 				DEBUG_LOG(info->line, "FBRK %i\n", info->line);
 				/* Report as BREAK (error) and let
-				 * receive_chars() handle it
+				 * receive_chars_dma() handle it
 				 */
 				info->errorcode = ERRCODE_SET_BREAK;
 			} else {
@@ -2155,38 +3081,195 @@
 		}
 
 #ifdef SERIAL_DEBUG_INTR
-		printk("** OK, disabling ser_interupts\n");
+		printk("** OK, disabling ser_interrupts\n");
 #endif
 		e100_disable_serial_data_irq(info);
-
+		DINTR2(DEBUG_LOG(info->line, "ser_rx OK %d\n", info->line));			
 		info->break_detected_cnt = 0;
 
 		PROCSTAT(ser_stat[info->line].ser_ints_ok_cnt++);
-		DEBUG_LOG(info->line, "ser_int OK %d\n", info->line);
 	}
-
 	/* Restarting the DMA never hurts */
 	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, restart);
 	START_FLUSH_FAST_TIMER(info, "ser_int");
-} /* handle_ser_interrupt */
+	return info;
+} /* handle_ser_rx_interrupt */
+
+extern _INLINE_ void handle_ser_tx_interrupt(struct e100_serial *info)
+{
+	unsigned long flags;
+	
+	if (info->x_char) {
+		unsigned char rstat;
+		DFLOW(DEBUG_LOG(info->line, "tx_int: xchar 0x%02X\n", info->x_char));
+		save_flags(flags); cli();
+		rstat = info->port[REG_STATUS];
+		DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat));
+				
+		info->port[REG_TR_DATA] = info->x_char;
+		info->icount.tx++;
+		info->x_char = 0;
+		/* We must enable since it is disabled in ser_interrupt */
+		e100_enable_serial_tx_ready_irq(info);
+		restore_flags(flags);
+		return;
+	} 
+	if (info->uses_dma_out) {
+		unsigned char rstat;
+		int i;
+		/* We only use normal tx interrupt when sending x_char */
+		DFLOW(DEBUG_LOG(info->line, "tx_int: xchar sent\n", 0));
+		save_flags(flags); cli();
+		rstat = info->port[REG_STATUS];
+		DFLOW(DEBUG_LOG(info->line, "stat %x\n", rstat));
+		e100_disable_serial_tx_ready_irq(info);
+		if (info->tty->stopped)
+			rs_stop(info->tty);
+		/* Enable the DMA channel and tell it to continue */
+		e100_enable_txdma_channel(info);
+		/* Wait 12 cycles before doing the DMA command */
+		for(i = 6;  i > 0; i--)
+			nop();
+		
+		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, continue); 
+		restore_flags(flags);
+		return;
+	}
+	/* Normal char-by-char interrupt */
+	if (info->xmit.head == info->xmit.tail
+	    || info->tty->stopped
+	    || info->tty->hw_stopped) {
+		DFLOW(DEBUG_LOG(info->line, "tx_int: stopped %i\n", info->tty->stopped));
+		e100_disable_serial_tx_ready_irq(info);
+		info->tr_running = 0;
+		return;
+	}
+	DINTR2(DEBUG_LOG(info->line, "tx_int %c\n", info->xmit.buf[info->xmit.tail]));
+	/* Send a byte, rs485 timing is critical so turn of ints */
+	save_flags(flags); cli();
+	info->port[REG_TR_DATA] = info->xmit.buf[info->xmit.tail];
+	info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
+	info->icount.tx++;
+	if (info->xmit.head == info->xmit.tail) {
+#if defined(CONFIG_ETRAX_RS485) && defined(CONFIG_ETRAX_FAST_TIMER)
+		if (info->rs485.enabled) {
+			/* Set a short timer to toggle RTS */
+			start_one_shot_timer(&fast_timers_rs485[info->line],
+			                     rs485_toggle_rts_timer_function,
+			                     (unsigned long)info,
+			                     info->char_time_usec*2,
+			                     "RS-485");
+		}
+#endif /* RS485 */
+		info->last_tx_active_usec = GET_JIFFIES_USEC();
+		info->last_tx_active = jiffies;
+		e100_disable_serial_tx_ready_irq(info);
+		DFLOW(DEBUG_LOG(info->line, "tx_int: stop2\n", 0));
+	} else {
+		/* We must enable since it is disabled in ser_interrupt */
+		e100_enable_serial_tx_ready_irq(info);
+	}
+	restore_flags(flags);
+	
+	if (CIRC_CNT(info->xmit.head,
+		     info->xmit.tail,
+		     SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
+		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
 
+} /* handle_ser_tx_interrupt */
+
+/* result of time measurements:
+ * RX duration 54-60 us when doing something, otherwise 6-9 us
+ * ser_int duration: just sending: 8-15 us normally, up to 73 us
+ */
 static void 
 ser_interrupt(int irq, void *dev_id, struct pt_regs *regs)
 {
+	static volatile int tx_started = 0;
 	struct e100_serial *info;
 	int i;
-
+	unsigned long flags;
+	unsigned long irq_mask1_rd;
+	unsigned long data_mask = (1 << (8+2*0)); /* ser0 data_avail */
+	static volatile unsigned long reentered_ready_mask = 0;
+	
+	save_flags(flags); cli();
+	irq_mask1_rd = *R_IRQ_MASK1_RD;
+	/* First handle all rx interrupts with ints disabled */
+	info = rs_table;
+	irq_mask1_rd &= e100_ser_int_mask;
 	for (i = 0; i < NR_PORTS; i++) {
-		info = rs_table + i;
-
-		if (!info->enabled || !info->uses_dma) 
-			continue; 
-
-		/* Which line caused the irq? */
-		if (*R_IRQ_MASK1_RD & (1U << (8+2*info->line))) { 
-			handle_ser_interrupt(info);
+		/* Which line caused the data irq? */
+		if (irq_mask1_rd & data_mask) { 
+			handle_ser_rx_interrupt(info);
 		}
+		info += 1;
+		data_mask <<= 2;
 	}
+	/* Handle tx interrupts with interrupts enabled so we
+	 * can take care of new data interrupts while transmitting
+	 * We protect the tx part with the tx_started flag.
+	 * We disable the tr_ready interrupts we are about to handle and
+	 * unblock the serial interrupt so new serial interrupts may come.
+	 *
+	 * If we get a new interrupt:
+	 *  - it migth be due to synchronous serial ports.
+	 *  - serial irq will be blocked by general irq handler.
+	 *  - async data will be handled above (sync will be ignored).
+	 *  - tx_started flag will prevent us from trying to send again and
+	 *    we will exit fast - no need to unblock serial irq.
+	 *  - Next (sync) serial interrupt handler will be runned with
+	 *    disabled interrupt due to restore_flags() at end of function,
+	 *    so sync handler will not be preempted or reentered.
+	 */
+	if (!tx_started) {
+		unsigned long ready_mask;
+		unsigned long 
+		tx_started = 1;
+		/* Only the tr_ready interrupts left */
+		irq_mask1_rd &= (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) |
+				 IO_MASK(R_IRQ_MASK1_RD, ser1_ready) |
+				 IO_MASK(R_IRQ_MASK1_RD, ser2_ready) |
+				 IO_MASK(R_IRQ_MASK1_RD, ser3_ready));
+		while (irq_mask1_rd) {
+			/* Disable those we are about to handle */
+			*R_IRQ_MASK1_CLR = irq_mask1_rd;
+			/* Unblock the serial interrupt */
+			*R_VECT_MASK_SET = IO_STATE(R_VECT_MASK_SET, serial, set);
+			
+			sti();
+			ready_mask = (1 << (8+1+2*0)); /* ser0 tr_ready */
+			info = rs_table;
+			for (i = 0; i < NR_PORTS; i++) {
+				/* Which line caused the ready irq? */
+				if (irq_mask1_rd & ready_mask) { 
+					handle_ser_tx_interrupt(info);
+				}
+				info += 1;
+				ready_mask <<= 2;
+			}
+			/* handle_ser_tx_interrupt enables tr_ready interrupts */
+			cli();
+			/* Handle reentered TX interrupt */
+			irq_mask1_rd = reentered_ready_mask;
+		}
+		cli();	
+		tx_started = 0;
+	} else {
+		unsigned long ready_mask;
+		ready_mask = irq_mask1_rd & (IO_MASK(R_IRQ_MASK1_RD, ser0_ready) |
+					     IO_MASK(R_IRQ_MASK1_RD, ser1_ready) |
+					     IO_MASK(R_IRQ_MASK1_RD, ser2_ready) |
+					     IO_MASK(R_IRQ_MASK1_RD, ser3_ready));
+		if (ready_mask) {
+			reentered_ready_mask |= ready_mask;
+			/* Disable those we are about to handle */
+			*R_IRQ_MASK1_CLR = ready_mask;
+			DFLOW(DEBUG_LOG(SERIAL_DEBUG_LINE, "ser_int reentered with TX %X\n", ready_mask));
+		}
+	}
+	
+	restore_flags(flags);
 } /* ser_interrupt */
 #endif
 
@@ -2288,25 +3371,40 @@
 	 * Reset the DMA channels and make sure their interrupts are cleared
 	 */
 
-	info->uses_dma = 1;
-	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-	*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-
-	/* Wait until reset cycle is complete */
-	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
-	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
-
-	while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) ==
-	       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+	if (info->dma_in_enabled) {
+		info->uses_dma_in = 1;
+		e100_enable_rxdma_channel(info);
+		
+		*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
 
-	/* Make sure the irqs are cleared */
-	*info->iclrintradr =
-		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
-		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
-	*info->oclrintradr =
-		IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
-		IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+		/* Wait until reset cycle is complete */
+		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->icmdadr) ==
+		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
+
+		/* Make sure the irqs are cleared */
+		*info->iclrintradr =
+			IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
+			IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+	} else {
+		e100_disable_rxdma_channel(info);
+	}
+	
+	if (info->dma_out_enabled) {
+		info->uses_dma_out = 1;
+		e100_enable_txdma_channel(info);
+		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+		
+		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) ==
+		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, reset));
 
+		/* Make sure the irqs are cleared */
+		*info->oclrintradr =
+			IO_STATE(R_DMA_CH6_CLR_INTR, clr_descr, do) |
+			IO_STATE(R_DMA_CH6_CLR_INTR, clr_eop, do);
+	} else {
+		e100_disable_txdma_channel(info);
+	}
+	
 	if (info->tty)
 		clear_bit(TTY_IO_ERROR, &info->tty->flags);
 
@@ -2321,8 +3419,10 @@
 	 * and set the speed and other flags of the serial port
 	 * this will start the rx/tx as well
 	 */
+	
 #ifdef SERIAL_HANDLE_EARLY_ERRORS
-	e100_enable_serial_data_irq(info);
+	if (info->uses_dma_in)
+		e100_enable_serial_data_irq(info);
 #endif	
 	change_speed(info);
 
@@ -2331,9 +3431,10 @@
 	(void)info->port[REG_DATA];
 
 	/* enable the interrupts */
+	if (info->uses_dma_out)
+		e100_enable_txdma_irq(info);
 
-	e100_enable_txdma_irq(info);
-	e100_enable_rxdma_irq(info);
+	e100_enable_rx_irq(info);
 
 	info->tr_running = 0; /* to be sure we don't lock up the transmitter */
 
@@ -2342,10 +3443,10 @@
 	start_receive(info);
 	
 	/* for safety, make sure the descriptors last result is 0 bytes written */
-	
 	info->tr_descr.sw_len = 0;
 	info->tr_descr.hw_len = 0;
 	info->tr_descr.status = 0;
+	
 
 	/* enable RTS/DTR last */
 
@@ -2374,20 +3475,28 @@
 
 #ifndef CONFIG_SVINTO_SIM	
 	/* shut down the transmitter and receiver */
-
+	DFLOW(DEBUG_LOG(info->line, "shutdown %i\n", info->line));
 	e100_disable_rx(info);
 	info->port[REG_TR_CTRL] = (info->tx_ctrl &= ~0x40);
 
-	e100_disable_rxdma_irq(info);
-	e100_disable_txdma_irq(info);
-
-	info->tr_running = 0;
-
-	/* reset both dma channels */
-
-	*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-	*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
-	info->uses_dma = 0;
+	/* disable interrupts, reset dma channels */
+	if (info->uses_dma_in) {
+		e100_disable_rxdma_irq(info);
+		*info->icmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+		info->uses_dma_in = 0;
+	} else {
+		e100_disable_serial_data_irq(info);
+	}
+	
+	if (info->uses_dma_out) {
+		e100_disable_txdma_irq(info);
+		info->tr_running = 0;
+		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
+		info->uses_dma_out = 0;
+	} else {
+		e100_disable_serial_tx_ready_irq(info);
+		info->tr_running = 0;		
+	}
 
 #endif /* CONFIG_SVINTO_SIM */
 
@@ -2435,7 +3544,7 @@
 {
 	unsigned int cflag;
 	unsigned long xoff;
-
+	unsigned long flags;
 	/* first some safety checks */
 	
 	if (!info->tty || !info->tty->termios)
@@ -2444,17 +3553,80 @@
 		return;
 	
 	cflag = info->tty->termios->c_cflag;
-	
+
 	/* possibly, the tx/rx should be disabled first to do this safely */
 	
 	/* change baud-rate and write it to the hardware */
-	
-	info->baud = cflag_to_baud(cflag);
+	if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_CUST) {
+		/* Special baudrate */
+		u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */
+		unsigned long alt_source = 
+				IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) |
+				IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal);
+		/* R_ALT_SER_BAUDRATE selects the source */
+		DBAUD(printk("Custom baudrate: baud_base/divisor %lu/%i\n",
+		       (unsigned long)info->baud_base, info->custom_divisor));
+		if (info->baud_base == SERIAL_PRESCALE_BASE) {
+			/* 0, 2-65535 (0=65536) */
+			u16 divisor = info->custom_divisor;
+			/* R_SERIAL_PRESCALE (upper 16 bits of R_CLOCK_PRESCALE) */
+			/* baudrate is 3.125MHz/custom_divisor */
+			alt_source = 
+				IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, prescale) |
+				IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, prescale);
+			alt_source = 0x11;
+			DBAUD(printk("Writing SERIAL_PRESCALE: divisor %i\n", divisor));
+			*R_SERIAL_PRESCALE = divisor;
+			info->baud = SERIAL_PRESCALE_BASE/divisor;
+		}
+#ifdef CONFIG_ETRAX_EXTERN_PB6CLK_ENABLED
+		else if ((info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8 &&
+			  info->custom_divisor == 1) ||
+			 (info->baud_base==CONFIG_ETRAX_EXTERN_PB6CLK_FREQ &&
+			  info->custom_divisor == 8)) {
+				/* ext_clk selected */
+				alt_source = 
+					IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, extern) |
+					IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, extern);
+				DBAUD(printk("using external baudrate: %lu\n", CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8));
+				info->baud = CONFIG_ETRAX_EXTERN_PB6CLK_FREQ/8;
+			}
+		}
+#endif
+		else
+		{
+			/* Bad baudbase, we don't support using timer0
+			 * for baudrate.
+			 */
+			printk(KERN_WARNING "Bad baud_base/custom_divisor: %lu/%i\n",
+			       (unsigned long)info->baud_base, info->custom_divisor);
+		}
+		r_alt_ser_baudrate_shadow &= ~mask;
+		r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8));
+		*R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow;
+	} else {
+		/* Normal baudrate */
+		/* Make sure we use normal baudrate */
+		u32 mask = 0xFF << (info->line*8); /* Each port has 8 bits */
+		unsigned long alt_source = 
+			IO_STATE(R_ALT_SER_BAUDRATE, ser0_rec, normal) |
+			IO_STATE(R_ALT_SER_BAUDRATE, ser0_tr, normal);
+		r_alt_ser_baudrate_shadow &= ~mask;
+		r_alt_ser_baudrate_shadow |= (alt_source << (info->line*8));
+#ifndef CONFIG_SVINTO_SIM
+		*R_ALT_SER_BAUDRATE = r_alt_ser_baudrate_shadow;
+#endif /* CONFIG_SVINTO_SIM */ 
+
+		info->baud = cflag_to_baud(cflag);
+#ifndef CONFIG_SVINTO_SIM
+		info->port[REG_BAUD] = cflag_to_etrax_baud(cflag);
+#endif /* CONFIG_SVINTO_SIM */
+	}
 	
 #ifndef CONFIG_SVINTO_SIM
-	info->port[REG_BAUD] = cflag_to_etrax_baud(cflag);
 	/* start with default settings and then fill in changes */
-
+	save_flags(flags);
+	cli();
 	/* 8 bit, no/even parity */
 	info->rx_ctrl &= ~(IO_MASK(R_SERIAL0_REC_CTRL, rec_bitnr) |
 			   IO_MASK(R_SERIAL0_REC_CTRL, rec_par_en) |
@@ -2484,14 +3656,26 @@
 		info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par_en, enable);
 	}
 	
-	if (cflag & PARODD) {
-		/* set odd parity */
-		info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd);
-		info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd);
+	if (cflag & CMSPAR) {
+		/* enable stick parity */
+		info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_stick_par, stick);
+		info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_stick_par, stick);
+		if (!(cflag & PARODD)) {
+			/* set mark parity */
+			info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd);
+			info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd);
+		}
+	} else {
+		if (cflag & PARODD) {
+			/* set odd parity */
+			info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, tr_par, odd);
+			info->rx_ctrl |= IO_STATE(R_SERIAL0_REC_CTRL, rec_par, odd);
+		}
 	}
 	
 	if (cflag & CRTSCTS) {
 		/* enable automatic CTS handling */
+		DFLOW(DEBUG_LOG(info->line, "FLOW auto_cts enabled\n", 0));
 		info->tx_ctrl |= IO_STATE(R_SERIAL0_TR_CTRL, auto_cts, active);
 	}
 	
@@ -2507,10 +3691,12 @@
 	xoff = IO_FIELD(R_SERIAL0_XOFF, xoff_char, STOP_CHAR(info->tty));
 	xoff |= IO_STATE(R_SERIAL0_XOFF, tx_stop, enable);
 	if (info->tty->termios->c_iflag & IXON ) {
+		DFLOW(DEBUG_LOG(info->line, "FLOW XOFF enabled 0x%02X\n", STOP_CHAR(info->tty)));
 		xoff |= IO_STATE(R_SERIAL0_XOFF, auto_xoff, enable);
 	}
 	
 	*((unsigned long *)&info->port[REG_XOFF]) = xoff;
+	restore_flags(flags);
 #endif /* !CONFIG_SVINTO_SIM */
 
 	update_char_time(info);
@@ -2543,8 +3729,8 @@
 	restore_flags(flags);
 }
 
-extern inline int 
-raw_write(struct tty_struct * tty, int from_user,
+extern _INLINE_ int 
+rs_raw_write(struct tty_struct * tty, int from_user,
 	  const unsigned char *buf, int count)
 {
 	int	c, ret = 0;
@@ -2558,7 +3744,7 @@
 	
 #ifdef SERIAL_DEBUG_DATA
 	if (info->line == SERIAL_DEBUG_LINE)
-		printk("raw_write (%d), status %d\n", 
+		printk("rs_raw_write (%d), status %d\n", 
 		       count, info->port[REG_STATUS]);
 #endif
 
@@ -2568,6 +3754,9 @@
 	return count;
 #endif
 	save_flags(flags);
+
+	DFLOW(DEBUG_LOG(info->line, "write count %i ", count));
+	DFLOW(DEBUG_LOG(info->line, "ldisc %i\n", tty->ldisc.chars_in_buffer(tty)));
 	
 	/* the cli/restore_flags pairs below are needed because the
 	 * DMA interrupt handler moves the info->xmit values. the memcpy
@@ -2635,6 +3824,7 @@
 	 * this does not need IRQ protection since if tr_running == 0
 	 * the IRQ's are not running anyway for this port.
 	 */
+	DFLOW(DEBUG_LOG(info->line, "write ret %i\n", ret));
 	
 	if (info->xmit.head != info->xmit.tail &&
 	    !tty->stopped &&
@@ -2644,7 +3834,7 @@
 	}
  	
 	return ret;
-} /* raw_write() */
+} /* rs_raw_write() */
 
 static int 
 rs_write(struct tty_struct * tty, int from_user,
@@ -2666,7 +3856,7 @@
 		e100_rts(info, info->rs485.rts_on_send);
 #if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
 		e100_disable_rx(info);
-		e100_disable_rxdma_irq(info);
+		e100_disable_rx_irq(info);
 #endif
 
 		if (info->rs485.delay_rts_before_send > 0) {
@@ -2676,7 +3866,7 @@
 	}
 #endif /* CONFIG_ETRAX_RS485 */
 
-	count = raw_write(tty, from_user, buf, count);
+	count = rs_raw_write(tty, from_user, buf, count);
 
 #if defined(CONFIG_ETRAX_RS485)
 	if (info->rs485.enabled)
@@ -2704,7 +3894,7 @@
 	
 #if defined(CONFIG_ETRAX_RS485_DISABLE_RECEIVER)
 		e100_enable_rx(info);
-		e100_enable_rxdma_irq(info);
+		e100_enable_rx_irq(info);
 #endif
 	}
 #endif /* CONFIG_ETRAX_RS485 */
@@ -2760,20 +3950,33 @@
  * This function is used to send a high-priority XON/XOFF character to
  * the device
  *
- * Since we don't bother to check for info->x_char in transmit_chars yet,
- * we don't really implement this function yet.
+ * Since we use DMA we don't check for info->x_char in transmit_chars_dma(),
+ * but we do it in handle_ser_tx_interrupt().
+ * We disable DMA channel and enable tx ready interrupt and write the
+ * character when possible.
  */
 static void rs_send_xchar(struct tty_struct *tty, char ch)
 {
 	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
+	unsigned long flags;
+	save_flags(flags); cli();
+	if (info->uses_dma_out) {
+		/* Put the DMA on hold and disable the channel */
+		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, hold); 
+		while (IO_EXTRACT(R_DMA_CH6_CMD, cmd, *info->ocmdadr) !=
+		       IO_STATE_VALUE(R_DMA_CH6_CMD, cmd, hold));
+		e100_disable_txdma_channel(info);
+	}
+	
+	/* Must make sure transmitter is not stopped before we can transmit */
+	if (tty->stopped)
+		rs_start(tty);
 
-	printk("serial.c:rs_send_xchar not implemented!\n");
-
+	/* Enable manual transmit interrupt and send from there */
+	DFLOW(DEBUG_LOG(info->line, "rs_send_xchar 0x%02X\n", ch));
 	info->x_char = ch;
-	if (ch) {
-		/* Make sure transmit interrupts are on */
-		/* TODO. */
-	}
+	e100_enable_serial_tx_ready_irq(info);
+	restore_flags(flags);
 }
 
 /*
@@ -2788,50 +3991,49 @@
 rs_throttle(struct tty_struct * tty)
 {
 	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
-	unsigned long flags;
 #ifdef SERIAL_DEBUG_THROTTLE
 	char	buf[64];
 
-	printk("throttle %s: %d....\n", _tty_name(tty, buf),
-	       tty->ldisc.chars_in_buffer(tty));
+	printk("throttle %s: %lu....\n", tty_name(tty, buf),
+	       (unsigned long)tty->ldisc.chars_in_buffer(tty));
 #endif
-
+	DFLOW(DEBUG_LOG(info->line,"rs_throttle %lu\n", tty->ldisc.chars_in_buffer(tty)));
+	
+	/* Do RTS before XOFF since XOFF might take some time */
+	if (tty->termios->c_cflag & CRTSCTS) {
+		/* Turn off RTS line */
+		e100_rts(info, 0);
+	}
 	if (I_IXOFF(tty))
-		info->x_char = STOP_CHAR(tty);
+		rs_send_xchar(tty, STOP_CHAR(tty));
 
-	/* Turn off RTS line (do this atomic) should here be an else ?? */
-
-	save_flags(flags); 
-	cli();
-	e100_rts(info, 0);
-	restore_flags(flags);
 }
 
 static void 
 rs_unthrottle(struct tty_struct * tty)
 {
 	struct e100_serial *info = (struct e100_serial *)tty->driver_data;
-	unsigned long flags;
 #ifdef SERIAL_DEBUG_THROTTLE
 	char	buf[64];
 
-	printk("unthrottle %s: %d....\n", _tty_name(tty, buf),
-	       tty->ldisc.chars_in_buffer(tty));
+	printk("unthrottle %s: %lu....\n", tty_name(tty, buf),
+	       (unsigned long)tty->ldisc.chars_in_buffer(tty));
 #endif
+	DFLOW(DEBUG_LOG(info->line,"rs_unthrottle ldisc %d\n", tty->ldisc.chars_in_buffer(tty)));
+	DFLOW(DEBUG_LOG(info->line,"rs_unthrottle flip.count: %i\n", tty->flip.count));	
+	/* Do RTS before XOFF since XOFF might take some time */
+	if (tty->termios->c_cflag & CRTSCTS) {
+		/* Assert RTS line  */
+		e100_rts(info, 1);
+	}
 
 	if (I_IXOFF(tty)) {
 		if (info->x_char)
 			info->x_char = 0;
 		else
-			info->x_char = START_CHAR(tty);
+			rs_send_xchar(tty, START_CHAR(tty));
 	}
 
-	/* Assert RTS line (do this atomic) */
-
-	save_flags(flags); 
-	cli();
-	e100_rts(info, 1);
-	restore_flags(flags);
 }
 
 /*
@@ -2859,8 +4061,10 @@
 	tmp.port = (int)info->port;
 	tmp.irq = info->irq;
 	tmp.flags = info->flags;
+	tmp.baud_base = info->baud_base;
 	tmp.close_delay = info->close_delay;
 	tmp.closing_wait = info->closing_wait;
+	tmp.custom_divisor = info->custom_divisor;
 	if (copy_to_user(retinfo, &tmp, sizeof(*retinfo)))
 		return -EFAULT;
 	return 0;
@@ -2897,9 +4101,11 @@
 	 * OK, past this point, all the error checking has been done.
 	 * At this point, we start making changes.....
 	 */
-	
+
+	info->baud_base = new_serial.baud_base;	
 	info->flags = ((info->flags & ~ASYNC_FLAGS) |
 		       (new_serial.flags & ASYNC_FLAGS));
+	info->custom_divisor = new_serial.custom_divisor;
 	info->type = new_serial.type;
 	info->close_delay = new_serial.close_delay;
 	info->closing_wait = new_serial.closing_wait;
@@ -3098,18 +4304,20 @@
 
 	info->tx_ctrl &= 0x3F; /* Clear bit 7 (txd) and 6 (tr_enable) */
 	info->port[REG_TR_CTRL] = info->tx_ctrl;
-
+	restore_flags(flags);
 	/* wait for "duration" jiffies */
 
 	schedule();
 
+	cli();
 	info->tx_ctrl |= (0x80 | 0x40); /* Set bit 7 (txd) and 6 (tr_enable) */
 	info->port[REG_TR_CTRL] = info->tx_ctrl;
 
 	/* the DMA gets awfully confused if we toggle the tranceiver like this 
 	 * so we need to reset it 
 	 */
-	*info->ocmdadr = 4;
+	if (info->uses_dma_out)
+		*info->ocmdadr = IO_STATE(R_DMA_CH6_CMD, cmd, reset);
 
 	restore_flags(flags);
 }
@@ -3252,6 +4460,7 @@
 
 	change_speed(info);
 
+	/* Handle turning off CRTSCTS */
 	if ((old_termios->c_cflag & CRTSCTS) &&
 	    !(tty->termios->c_cflag & CRTSCTS)) {
 		tty->hw_stopped = 0;
@@ -3301,12 +4510,13 @@
 		 * one, we've got real problems, since it means the
 		 * serial port won't be shutdown.
 		 */
-		printk("rs_close: bad serial port count; tty->count is 1, "
+		printk(KERN_CRIT
+		       "rs_close: bad serial port count; tty->count is 1, "
 		       "info->count is %d\n", info->count);
 		info->count = 1;
 	}
 	if (--info->count < 0) {
-		printk("rs_close: bad serial port count for ttyS%d: %d\n",
+		printk(KERN_CRIT "rs_close: bad serial port count for ttyS%d: %d\n",
 		       info->line, info->count);
 		info->count = 0;
 	}
@@ -3340,7 +4550,7 @@
 
 #ifndef CONFIG_SVINTO_SIM
 	e100_disable_rx(info);
-	e100_disable_rxdma_irq(info);
+	e100_disable_rx_irq(info);
 
 	if (info->flags & ASYNC_INITIALIZED) {
 		/*
@@ -3676,6 +4886,12 @@
 #ifdef SERIAL_DEBUG_OPEN
 	printk("rs_open ttyS%d successful...\n", info->line);
 #endif
+	DLOG_INT_TRIG( log_int_pos = 0);
+
+	DFLIP(	if (info->line == SERIAL_DEBUG_LINE) {
+			info->icount.rx = 0;
+		} );
+	
 	return 0;
 }
 
@@ -3683,11 +4899,12 @@
  * /proc fs routines....
  */
 
-extern inline int line_info(char *buf, struct e100_serial *info)
+extern _INLINE_ int line_info(char *buf, struct e100_serial *info)
 {
 	char	stat_buf[30];
 	int	ret;
-
+	unsigned long tmp;
+	
 	ret = sprintf(buf, "%d: uart:E100 port:%lX irq:%d",
 		      info->line, (unsigned long)info->port, info->irq);
 
@@ -3716,11 +4933,39 @@
 	ret += sprintf(buf+ret, " tx:%lu rx:%lu",
 		       (unsigned long)info->icount.tx,
 		       (unsigned long)info->icount.rx);
-
+	tmp = CIRC_CNT(info->xmit.head, info->xmit.tail, SERIAL_XMIT_SIZE);
+	if (tmp) {
+		ret += sprintf(buf+ret, " tx_pend:%lu/%lu",
+			       (unsigned long)tmp,
+			       (unsigned long)SERIAL_XMIT_SIZE);
+	}
+	
 	ret += sprintf(buf+ret, " rx_pend:%lu/%lu",
 		       (unsigned long)info->recv_cnt,
 		       (unsigned long)info->max_recv_cnt);
 
+#if 1
+	if (info->tty) {
+		
+		if (info->tty->stopped)
+			ret += sprintf(buf+ret, " stopped:%i",
+				       (int)info->tty->stopped);
+		if (info->tty->hw_stopped)
+			ret += sprintf(buf+ret, " hw_stopped:%i",
+				       (int)info->tty->hw_stopped);
+	}
+	
+	{
+		unsigned char rstat = info->port[REG_STATUS];
+		if (rstat & IO_MASK(R_SERIAL0_STATUS, xoff_detect) )
+			ret += sprintf(buf+ret, " xoff_detect:1");
+	}
+	
+#endif
+	
+
+
+	
 	if (info->icount.frame)
 		ret += sprintf(buf+ret, " fe:%lu",
 			       (unsigned long)info->icount.frame);
@@ -3744,12 +4989,14 @@
 	return ret;
 }
 
+
+
 int rs_read_proc(char *page, char **start, off_t off, int count,
 		 int *eof, void *data)
 {
 	int i, len = 0, l;
 	off_t	begin = 0;
-
+	
 	len += sprintf(page, "serinfo:1.0 driver:%s\n",
 		       serial_version);
 	for (i = 0; i < NR_PORTS && len < 4000; i++) {
@@ -3764,6 +5011,22 @@
 			len = 0;
 		}
 	}
+#ifdef DEBUG_LOG_INCLUDED
+	for (i = 0; i < debug_log_pos; i++) {
+		len += sprintf(page + len, "%-4i %lu.%lu ", i, debug_log[i].time, timer_data_to_ns(debug_log[i].timer_data));
+		len += sprintf(page + len, debug_log[i].string, debug_log[i].value);
+		if (len+begin > off+count)
+			goto done;
+		if (len+begin < off) {
+			begin += len;
+			len = 0;
+		}
+	}
+	len += sprintf(page + len, "debug_log %i/%i  %li bytes\n",
+		       i, DEBUG_LOG_SIZE, begin+len);
+	debug_log_pos = 0;
+#endif
+
 	*eof = 1;
 done:
 	if (off >= len+begin)
@@ -3777,7 +5040,8 @@
 static void 
 show_serial_version(void)
 {
-	printk("ETRAX 100LX serial-driver %s, (c) 2000-2002 Axis Communications AB\r\n",
+	printk(KERN_INFO
+	       "ETRAX 100LX serial-driver %s, (c) 2000-2003 Axis Communications AB\r\n",
 	       &serial_version[11]); /* "$Revision: x.yy" */
 }
 
@@ -3794,7 +5058,7 @@
 	init_bh(SERIAL_BH, do_serial_bh);
 
 	/* Setup the timed flush handler system */
-
+	
 #if !defined(CONFIG_ETRAX_SERIAL_FAST_TIMER) && !defined(CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST)
 	init_timer(&flush_timer);
 	flush_timer.function = timed_flush_handler;
@@ -3868,12 +5132,15 @@
 	/* do some initializing for the separate ports */
   
 	for (i = 0, info = rs_table; i < NR_PORTS; i++,info++) {
-		info->uses_dma = 0;   
+		info->uses_dma_in = 0;
+		info->uses_dma_out = 0;
 		info->line = i;
 		info->tty = 0;
 		info->type = PORT_ETRAX;
 		info->tr_running = 0;
 		info->forced_eop = 0;
+		info->baud_base = DEF_BAUD_BASE;
+		info->custom_divisor = 0;		
 		info->flags = 0;
 		info->close_delay = 5*HZ/10;
 		info->closing_wait = 30*HZ;
@@ -3920,41 +5187,58 @@
 #ifndef CONFIG_SVINTO_SIM
 	/* Not needed in simulator.  May only complicate stuff. */
 	/* hook the irq's for DMA channel 6 and 7, serial output and input, and some more... */
+
+	if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial ", NULL))
+		panic("irq8");
+	
 #ifdef CONFIG_ETRAX_SERIAL_PORT0
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA6_OUT
 	if (request_irq(SER0_DMA_TX_IRQ_NBR, tr_interrupt, SA_INTERRUPT, "serial 0 dma tr", NULL))
 		panic("irq22");
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT0_DMA7_IN
 	if (request_irq(SER0_DMA_RX_IRQ_NBR, rec_interrupt, SA_INTERRUPT, "serial 0 dma rec", NULL))
 		panic("irq23");
 #endif
-#ifdef SERIAL_HANDLE_EARLY_ERRORS
-	if (request_irq(SERIAL_IRQ_NBR, ser_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial ", NULL))
-		panic("irq8");
 #endif
+
 #ifdef CONFIG_ETRAX_SERIAL_PORT1
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA8_OUT
 	if (request_irq(SER1_DMA_TX_IRQ_NBR, tr_interrupt, SA_INTERRUPT, "serial 1 dma tr", NULL))
 		panic("irq24");
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT1_DMA9_IN
 	if (request_irq(SER1_DMA_RX_IRQ_NBR, rec_interrupt, SA_INTERRUPT, "serial 1 dma rec", NULL))
 		panic("irq25");
 #endif
+#endif
 #ifdef CONFIG_ETRAX_SERIAL_PORT2
 	/* DMA Shared with par0 (and SCSI0 and ATA) */
-	if (request_irq(SER2_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ, "serial 2 dma tr", NULL))
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA2_OUT
+	if (request_irq(SER2_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 2 dma tr", NULL))
 		panic("irq18");
-	if (request_irq(SER2_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ, "serial 2 dma rec", NULL))
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT2_DMA3_IN
+	if (request_irq(SER2_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 2 dma rec", NULL))
 		panic("irq19");
 #endif
+#endif
 #ifdef CONFIG_ETRAX_SERIAL_PORT3
 	/* DMA Shared with par1 (and SCSI1 and Extern DMA 0) */
-	if (request_irq(SER3_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ, "serial 3 dma tr", NULL))
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA4_OUT
+	if (request_irq(SER3_DMA_TX_IRQ_NBR, tr_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 3 dma tr", NULL))
 		panic("irq20");
-	if (request_irq(SER3_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ, "serial 3 dma rec", NULL))
+#endif
+#ifdef CONFIG_ETRAX_SERIAL_PORT3_DMA5_IN
+	if (request_irq(SER3_DMA_RX_IRQ_NBR, rec_interrupt, SA_SHIRQ | SA_INTERRUPT, "serial 3 dma rec", NULL))
 		panic("irq21");
 #endif
+#endif
 
 #ifdef CONFIG_ETRAX_SERIAL_FLUSH_DMA_FAST
 	if (request_irq(TIMER1_IRQ_NBR, timeout_interrupt, SA_SHIRQ | SA_INTERRUPT,
 		       "fast serial dma timeout", NULL)) {
-		printk("err: timer1 irq\n");
+		printk(KERN_CRIT "err: timer1 irq\n");
 	}
 #endif
 #endif /* CONFIG_SVINTO_SIM */
diff -urN linux-2.4.21-bk5/arch/cris/drivers/serial.h linux-2.4.21-bk6/arch/cris/drivers/serial.h
--- linux-2.4.21-bk5/arch/cris/drivers/serial.h	2002-08-02 17:39:42.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/serial.h	2003-07-10 02:54:58.000000000 -0700
@@ -23,7 +23,7 @@
  * For definitions of the flags field, see tty.h
  */
 
-#define SERIAL_RECV_DESCRIPTORS 8
+#define SERIAL_RECV_DESCRIPTORS 16
 
 struct etrax_recv_buffer {
 	struct etrax_recv_buffer *next;
@@ -44,30 +44,29 @@
 	volatile u32		*ofirstadr;   /* adr to R_DMA_CHx_FIRST */
 	volatile u8		*ocmdadr;     /* adr to R_DMA_CHx_CMD */
 	const volatile u8	*ostatusadr;  /* adr to R_DMA_CHx_STATUS */
-	volatile u32		*ohwswadr;    /* adr to R_DMA_CHx_HWSW */
-	volatile u32		*odescradr;   /* adr to R_DMA_CHx_DESCR */
 
 	/* Input registers */
 	volatile u8		*iclrintradr; /* adr to R_DMA_CHx_CLR_INTR */
 	volatile u32		*ifirstadr;   /* adr to R_DMA_CHx_FIRST */
 	volatile u8		*icmdadr;     /* adr to R_DMA_CHx_CMD */
-	const volatile u8	*istatusadr;  /* adr to R_DMA_CHx_STATUS */
-	volatile u32		*ihwswadr;    /* adr to R_DMA_CHx_HWSW */
 	volatile u32		*idescradr;   /* adr to R_DMA_CHx_DESCR */
 
 	int			flags;	/* defined in tty.h */
 
-	u8			rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
-	u8			tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
-	u8			iseteop; /* bit number for R_SET_EOP for the input dma */
-
-	int			enabled; /* Set to 1 if the port is enabled in HW config */
-  
+	u8		rx_ctrl; /* shadow for R_SERIALx_REC_CTRL */
+	u8		tx_ctrl; /* shadow for R_SERIALx_TR_CTRL */
+	u8		iseteop; /* bit number for R_SET_EOP for the input dma */
+	u8		enabled; /* Set to 1 if the port is enabled in HW config */
+	
+	u8		dma_out_enabled:1; /* Set to 1 if DMA should be used */
+	u8		dma_in_enabled:1;  /* Set to 1 if DMA should be used */
+	
 	/* end of fields defined in rs_table[] in .c-file */
-
-	int			uses_dma; /* Set to 1 if DMA should be used */
-	unsigned char           forced_eop; /* a fifo eop has been forced */
-
+	u8		uses_dma_in;  /* Set to 1 if DMA is used */
+	u8		uses_dma_out; /* Set to 1 if DMA is used */
+	u8		forced_eop;   /* a fifo eop has been forced */
+	int			baud_base;     /* For special baudrates */
+	int			custom_divisor; /* For special baudrates */
 	struct etrax_dma_descr	tr_descr;
 	struct etrax_dma_descr	rec_descr[SERIAL_RECV_DESCRIPTORS];
 	int			cur_rec_descr;
@@ -108,6 +107,7 @@
 #endif  
 
 	unsigned long		char_time_usec;       /* The time for 1 char, in usecs */
+	unsigned long		flush_time_usec;      /* How often we should flush */
 	unsigned long		last_tx_active_usec;  /* Last tx usec in the jiffies */
 	unsigned long		last_tx_active;       /* Last tx time in jiffies */
 	unsigned long		last_rx_active_usec;  /* Last rx usec in the jiffies */
diff -urN linux-2.4.21-bk5/arch/cris/drivers/sync_serial.c linux-2.4.21-bk6/arch/cris/drivers/sync_serial.c
--- linux-2.4.21-bk5/arch/cris/drivers/sync_serial.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/sync_serial.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,7 +1,7 @@
 /*  
  * Simple synchronous serial port driver for ETRAX 100LX.
  *
- * Synchronous serial ports are used for continous streamed data like audio.
+ * 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
@@ -29,7 +29,7 @@
 #include <asm/system.h>
 #include <asm/sync_serial.h>
 
-/* The receiver is a bit tricky beacuse of the continous stream of data. */
+/* The receiver is a bit tricky beacuse of the continuous stream of data.*/
 /*                                                                       */
 /* Two DMA descriptors are linked together. Each DMA descriptor is       */
 /* responsible for one half of a common buffer.                          */
@@ -256,7 +256,7 @@
 	initialize_port(1);
 	if (ports[0].use_dma) /* Port 0 uses dma, we must manual allocate IRQ */
 	{
-		if (request_irq(8, manual_interrupt, SA_SHIRQ | SA_INTERRUPT, "synchronous serial manual irq", &ports[1]))
+		if (request_irq(8, manual_interrupt, SA_SHIRQ | SA_INTERRUPT, "synchronous serial manual irq", &ports[1]))
 			panic("Can't allocate sync serial manual irq");
 	}
 #endif
diff -urN linux-2.4.21-bk5/arch/cris/drivers/usb-host.c linux-2.4.21-bk6/arch/cris/drivers/usb-host.c
--- linux-2.4.21-bk5/arch/cris/drivers/usb-host.c	2002-11-28 15:53:09.000000000 -0800
+++ linux-2.4.21-bk6/arch/cris/drivers/usb-host.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,10 +1,7 @@
 /*
  * usb-host.c: ETRAX 100LX USB Host Controller Driver (HCD)
  *
- * Copyright (c) 2001 Axis Communications AB.
- *
- * $Id: usb-host.c,v 1.13 2001/11/13 12:06:17 pkj Exp $
- *
+ * Copyright (c) 2002, 2003 Axis Communications AB.
  */
 
 #include <linux/config.h>
@@ -19,6 +16,7 @@
 #include <linux/init.h>
 #include <linux/version.h>
 #include <linux/list.h>
+#include <linux/spinlock.h>
 
 #include <asm/uaccess.h>
 #include <asm/io.h>
@@ -28,24 +26,37 @@
 #include <asm/svinto.h>
 
 #include <linux/usb.h>
+/* Ugly include because we don't live with the other host drivers. */
+#include <../drivers/usb/hcd.h>
+
+#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 20)
+typedef struct urb urb_t, *purb_t;
+typedef struct iso_packet_descriptor iso_packet_descriptor_t;
+typedef struct usb_ctrlrequest devrequest;
+#endif
+
 #include "usb-host.h"
 
 #define ETRAX_USB_HC_IRQ USB_HC_IRQ_NBR
 #define ETRAX_USB_RX_IRQ USB_DMA_RX_IRQ_NBR
 #define ETRAX_USB_TX_IRQ USB_DMA_TX_IRQ_NBR
 
-static const char *usb_hcd_version = "$Revision: 1.13 $";
+static const char *usb_hcd_version = "$Revision: 1.18 $";
 
 #undef KERN_DEBUG
 #define KERN_DEBUG ""
 
+
 #undef USB_DEBUG_RH
-#undef USB_DEBUG_EP
+#undef USB_DEBUG_EPID
+#undef USB_DEBUG_SB
 #undef USB_DEBUG_DESC
+#undef USB_DEBUG_URB
 #undef USB_DEBUG_TRACE
-#undef USB_DEBUG_CTRL
 #undef USB_DEBUG_BULK
+#undef USB_DEBUG_CTRL
 #undef USB_DEBUG_INTR
+#undef USB_DEBUG_ISOC
 
 #ifdef USB_DEBUG_RH
 #define dbg_rh(format, arg...) printk(KERN_DEBUG __FILE__ ": (RH) " format "\n" , ## arg)
@@ -53,10 +64,16 @@
 #define dbg_rh(format, arg...) do {} while (0)
 #endif
 
-#ifdef USB_DEBUG_EP
-#define dbg_ep(format, arg...) printk(KERN_DEBUG __FILE__ ": (EP) " format "\n" , ## arg)
+#ifdef USB_DEBUG_EPID
+#define dbg_epid(format, arg...) printk(KERN_DEBUG __FILE__ ": (EPID) " format "\n" , ## arg)
 #else
-#define dbg_ep(format, arg...) do {} while (0)
+#define dbg_epid(format, arg...) do {} while (0)
+#endif
+
+#ifdef USB_DEBUG_SB
+#define dbg_sb(format, arg...) printk(KERN_DEBUG __FILE__ ": (SB) " format "\n" , ## arg)
+#else
+#define dbg_sb(format, arg...) do {} while (0)
 #endif
 
 #ifdef USB_DEBUG_CTRL
@@ -77,6 +94,12 @@
 #define dbg_intr(format, arg...) do {} while (0)
 #endif
 
+#ifdef USB_DEBUG_ISOC
+#define dbg_isoc(format, arg...) printk(KERN_DEBUG __FILE__ ": (ISOC) " format "\n" , ## arg)
+#else
+#define dbg_isoc(format, arg...) do {} while (0)
+#endif
+
 #ifdef USB_DEBUG_TRACE
 #define DBFENTER (printk(KERN_DEBUG __FILE__ ": Entering: " __FUNCTION__ "\n"))
 #define DBFEXIT  (printk(KERN_DEBUG __FILE__ ": Exiting:  " __FUNCTION__ "\n"))
@@ -85,6 +108,7 @@
 #define DBFEXIT  do {} while (0)
 #endif
 
+
 /*-------------------------------------------------------------------
  Virtual Root Hub
  -------------------------------------------------------------------*/
@@ -158,78 +182,262 @@
 	0xff   /*  __u8  PortPwrCtrlMask; *** 7 ports max *** */
 };
 
+static struct timer_list bulk_start_timer;
+static struct timer_list bulk_eot_timer;
+/* We want the start timer to expire before the eot timer, because the former might start
+   traffic, thus making it unnecessary for the latter to time out. */
+#define BULK_START_TIMER_INTERVAL (HZ/10) /* 100 ms */
+#define BULK_EOT_TIMER_INTERVAL (HZ/10+2) /* 120 ms */
 
 #define OK(x) len = (x); dbg_rh("OK(%d): line: %d", x, __LINE__); break
 #define CHECK_ALIGN(x) if (((__u32)(x)) & 0x00000003) \
 {panic("Alignment check (DWORD) failed at %s:%s:%d\n", __FILE__, __FUNCTION__, __LINE__);}
 
-static unsigned long submit_urb_count = 0;
+#define SLAB_FLAG     (in_interrupt() ? SLAB_ATOMIC : SLAB_KERNEL)
+#define KMALLOC_FLAG  (in_interrupt() ? GFP_ATOMIC : GFP_KERNEL)
+
+/* Most helpful debugging aid */
+#define assert(expr) ((void) ((expr) ? 0 : (err("assert failed at line %d",__LINE__))))
 
-//#define ETRAX_USB_INTR_IRQ
-//#define ETRAX_USB_INTR_ERROR_FATAL
+/* Alternative assert define which stops after a failed assert. */
+/*
+#define assert(expr)                                      \
+{                                                         \
+        if (!(expr)) {                                    \
+                err("assert failed at line %d",__LINE__); \
+                while (1);                                \
+        }                                                 \
+}
+*/
 
-#define RX_BUF_SIZE        32768
-#define RX_DESC_BUF_SIZE   64
-#define NBR_OF_RX_DESC     (RX_BUF_SIZE / RX_DESC_BUF_SIZE)
 
-#define NBR_OF_EP_DESC     32
+/* FIXME: Should RX_BUF_SIZE be a config option, or maybe we should adjust it dynamically?
+   To adjust it dynamically we would have to get an interrupt when we reach the end
+   of the rx descriptor list, or when we get close to the end, and then allocate more
+   descriptors. */
+
+#define NBR_OF_RX_DESC     512
+#define RX_DESC_BUF_SIZE   1024
+#define RX_BUF_SIZE        (NBR_OF_RX_DESC * RX_DESC_BUF_SIZE)
+
+/* The number of epids is, among other things, used for pre-allocating
+   ctrl, bulk and isoc EP descriptors (one for each epid).
+   Assumed to be > 1 when initiating the DMA lists. */
+#define NBR_OF_EPIDS       32
 
+/* Support interrupt traffic intervals up to 128 ms. */
 #define MAX_INTR_INTERVAL 128
 
-static __u32 ep_usage_bitmask;
-static __u32 ep_really_active;
-static __u32 ep_out_traffic;
-
-static unsigned char RxBuf[RX_BUF_SIZE];
-static USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4)));
+/* If periodic traffic (intr or isoc) is to be used, then one entry in the EP table
+   must be "invalid". By this we mean that we shouldn't care about epid attentions
+   for this epid, or at least handle them differently from epid attentions for "valid"
+   epids. This define determines which one to use (don't change it). */   
+#define INVALID_EPID     31
+/* A special epid for the bulk dummys. */
+#define DUMMY_EPID       30
+
+/* This is just a software cache for the valid entries in R_USB_EPT_DATA. */
+static __u32 epid_usage_bitmask;
+
+/* A bitfield to keep information on in/out traffic is needed to uniquely identify
+   an endpoint on a device, since the most significant bit which indicates traffic
+   direction is lacking in the ep_id field (ETRAX epids can handle both in and
+   out traffic on endpoints that are otherwise identical). The USB framework, however,
+   relies on them to be handled separately.  For example, bulk IN and OUT urbs cannot
+   be queued in the same list, since they would block each other. */
+static __u32 epid_out_traffic;
+
+/* DMA IN cache bug. Align the DMA IN buffers to 32 bytes, i.e. a cache line.
+   Since RX_DESC_BUF_SIZE is 1024 is a multiple of 32, all rx buffers will be cache aligned. */
+static volatile unsigned char RxBuf[RX_BUF_SIZE] __attribute__ ((aligned (32)));
+static volatile USB_IN_Desc_t RxDescList[NBR_OF_RX_DESC] __attribute__ ((aligned (4)));
 
+/* Pointers into RxDescList. */
 static volatile USB_IN_Desc_t *myNextRxDesc;
 static volatile USB_IN_Desc_t *myLastRxDesc;
 static volatile USB_IN_Desc_t *myPrevRxDesc;
 
-static USB_EP_Desc_t TxCtrlEPList[NBR_OF_EP_DESC] __attribute__ ((aligned (4)));
-static USB_EP_Desc_t TxBulkEPList[NBR_OF_EP_DESC] __attribute__ ((aligned (4)));
+/* EP descriptors must be 32-bit aligned. */
+static volatile USB_EP_Desc_t TxCtrlEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+static volatile USB_EP_Desc_t TxBulkEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+/* After each enabled bulk EP (IN or OUT) we put two disabled EP descriptors with the eol flag set,
+   causing the DMA to stop the DMA channel. The first of these two has the intr flag set, which
+   gives us a dma8_sub0_descr interrupt. When we receive this, we advance the DMA one step in the
+   EP list and then restart the bulk channel, thus forcing a switch between bulk EP descriptors
+   in each frame. */
+static volatile USB_EP_Desc_t TxBulkDummyEPList[NBR_OF_EPIDS][2] __attribute__ ((aligned (4)));
+
+static volatile USB_EP_Desc_t TxIsocEPList[NBR_OF_EPIDS] __attribute__ ((aligned (4)));
+static volatile USB_SB_Desc_t TxIsocSB_zout __attribute__ ((aligned (4)));
+
+static volatile USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4)));
+static volatile USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4)));
+
+/* A zout transfer makes a memory access at the address of its buf pointer, which means that setting
+   this buf pointer to 0 will cause an access to the flash. In addition to this, setting sw_len to 0
+   results in a 16/32 bytes (depending on DMA burst size) transfer. Instead, we set it to 1, and point
+   it to this buffer. */
+static int zout_buffer[4] __attribute__ ((aligned (4)));
 
-static USB_EP_Desc_t TxIntrEPList[MAX_INTR_INTERVAL] __attribute__ ((aligned (4)));
-static USB_SB_Desc_t TxIntrSB_zout __attribute__ ((aligned (4)));
-
-static struct urb *URB_List[NBR_OF_EP_DESC];
+/* Cache for allocating new EP and SB descriptors. */
 static kmem_cache_t *usb_desc_cache;
+
+/* Cache for the registers allocated in the top half. */
+static kmem_cache_t *top_half_reg_cache;
+
 static struct usb_bus *etrax_usb_bus;
 
-#ifdef USB_DEBUG_DESC
-static void dump_urb (struct urb *urb);
-#endif
+/* This is a circular (double-linked) list of the active urbs for each epid.
+   The head is never removed, and new urbs are linked onto the list as 
+   urb_entry_t elements. Don't reference urb_list directly; use the wrapper
+   functions instead. Note that working with these lists might require spinlock
+   protection. */
+static struct list_head urb_list[NBR_OF_EPIDS];
+
+/* Read about the need and usage of this lock in submit_ctrl_urb. */
+static spinlock_t urb_list_lock;
+
+/* Used when unlinking asynchronously. */
+static struct list_head urb_unlink_list;
+
+/* Wrappers around the list functions (include/linux/list.h). */
+
+static inline int urb_list_empty(int epid)
+{
+        return list_empty(&urb_list[epid]);
+}
+
+/* Returns first urb for this epid, or NULL if list is empty. */
+static inline urb_t *urb_list_first(int epid)
+{
+        urb_t *first_urb = 0;
+
+        if (!urb_list_empty(epid)) {
+                /* Get the first urb (i.e. head->next). */
+                urb_entry_t *urb_entry = list_entry((&urb_list[epid])->next, urb_entry_t, list);
+                first_urb = urb_entry->urb;
+        }
+        return first_urb;
+}
+
+/* Adds an urb_entry last in the list for this epid. */
+static inline void urb_list_add(urb_t *urb, int epid)
+{
+        urb_entry_t *urb_entry = (urb_entry_t *)kmalloc(sizeof(urb_entry_t), KMALLOC_FLAG);
+        assert(urb_entry);
+
+        urb_entry->urb = urb;
+        list_add_tail(&urb_entry->list, &urb_list[epid]);
+}
+
+/* Search through the list for an element that contains this urb. (The list
+   is expected to be short and the one we are about to delete will often be
+   the first in the list.) */
+static inline urb_entry_t *__urb_list_entry(urb_t *urb, int epid)
+{
+        struct list_head *entry;
+        struct list_head *tmp;
+        urb_entry_t *urb_entry;
+
+        list_for_each_safe(entry, tmp, &urb_list[epid]) {
+                urb_entry = list_entry(entry, urb_entry_t, list);
+                assert(urb_entry);
+                assert(urb_entry->urb);
+                
+                if (urb_entry->urb == urb) {
+                        return urb_entry;
+                }
+        }
+        return 0;
+}
+
+/* Delete an urb from the list. */
+static inline void urb_list_del(urb_t *urb, int epid)
+{
+        urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+        assert(urb_entry);
+        
+        /* Delete entry and free. */
+        list_del(&urb_entry->list);
+        kfree(urb_entry);
+}
+
+/* Move an urb to the end of the list. */
+static inline void urb_list_move_last(urb_t *urb, int epid)
+{
+        urb_entry_t *urb_entry = __urb_list_entry(urb, epid);
+        assert(urb_entry);
+
+        list_del(&urb_entry->list);
+        list_add_tail(&urb_entry->list, &urb_list[epid]);
+}
+
+/* For debug purposes only. */
+static inline void urb_list_dump(int epid)
+{
+        struct list_head *entry;
+        struct list_head *tmp;
+        urb_entry_t *urb_entry;
+        int i = 0;
+
+        info("Dumping urb list for epid %d", epid);
+        
+        list_for_each_safe(entry, tmp, &urb_list[epid]) {
+                urb_entry = list_entry(entry, urb_entry_t, list);
+                info("   entry %d, urb = 0x%lx", i, (unsigned long)urb_entry->urb);
+        }
+}
+
 static void init_rx_buffers(void);
-static int etrax_rh_unlink_urb (struct urb *urb);
-static void etrax_rh_send_irq(struct urb *urb);
-static void etrax_rh_init_int_timer(struct urb *urb);
+static int etrax_rh_unlink_urb(urb_t *urb);
+static void etrax_rh_send_irq(urb_t *urb);
+static void etrax_rh_init_int_timer(urb_t *urb);
 static void etrax_rh_int_timer_do(unsigned long ptr);
 
-static void etrax_usb_setup_epid(int epid, char devnum, char endpoint,
-				 char packsize, char slow, char out_traffic);
-static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint,
-				 char slow, int maxp, char out_traffic);
+static int etrax_usb_setup_epid(urb_t *urb);
+static int etrax_usb_lookup_epid(urb_t *urb);
 static int etrax_usb_allocate_epid(void);
 static void etrax_usb_free_epid(int epid);
-static void cleanup_sb(USB_SB_Desc_t *sb);
 
-static void etrax_usb_do_ctrl_hw_add(struct urb *urb, int epid, char maxlen);
-static void etrax_usb_do_bulk_hw_add(struct urb *urb, int epid, char maxlen);
+static int etrax_remove_from_sb_list(urb_t *urb);
 
-static int etrax_usb_submit_ctrl_urb(struct urb *urb);
+static void etrax_usb_add_to_bulk_sb_list(urb_t *urb, int epid);
+static void etrax_usb_add_to_ctrl_sb_list(urb_t *urb, int epid);
+static void etrax_usb_add_to_intr_sb_list(urb_t *urb, int epid);
+static void etrax_usb_add_to_isoc_sb_list(urb_t *urb, int epid); 
+
+static int etrax_usb_submit_bulk_urb(urb_t *urb);
+static int etrax_usb_submit_ctrl_urb(urb_t *urb);
+static int etrax_usb_submit_intr_urb(urb_t *urb);
+static int etrax_usb_submit_isoc_urb(urb_t *urb);
 
-static int etrax_usb_submit_urb(struct urb *urb);
-static int etrax_usb_unlink_urb(struct urb *urb);
+static int etrax_usb_submit_urb(urb_t *urb);
+static int etrax_usb_unlink_urb(urb_t *urb);
 static int etrax_usb_get_frame_number(struct usb_device *usb_dev);
 static int etrax_usb_allocate_dev(struct usb_device *usb_dev);
 static int etrax_usb_deallocate_dev(struct usb_device *usb_dev);
 
 static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs);
 static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs);
-static void etrax_usb_hc_intr_top_half(int irq, void *vhc, struct pt_regs *regs);
+static void etrax_usb_hc_interrupt_top_half(int irq, void *vhc, struct pt_regs *regs);
+static void etrax_usb_hc_interrupt_bottom_half(void *data);
 
-static int etrax_rh_submit_urb (struct urb *urb);
+/* The following is a list of interrupt handlers for the host controller interrupts we use.
+   They are called from etrax_usb_hc_interrupt_bottom_half. */
+static void etrax_usb_hc_isoc_eof_interrupt(void);
+static void etrax_usb_hc_bulk_eot_interrupt(int timer_induced);
+static void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg);
+static void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg);
+static void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg);
+
+static int etrax_rh_submit_urb (urb_t *urb);
+
+/* Forward declaration needed because they are used in the rx interrupt routine. */
+static void etrax_usb_complete_urb(urb_t *urb, int status);
+static void etrax_usb_complete_bulk_urb(urb_t *urb, int status);
+static void etrax_usb_complete_ctrl_urb(urb_t *urb, int status);
+static void etrax_usb_complete_intr_urb(urb_t *urb, int status);
+static void etrax_usb_complete_isoc_urb(urb_t *urb, int status);
 
 static int etrax_usb_hc_init(void);
 static void etrax_usb_hc_cleanup(void);
@@ -243,65 +451,193 @@
 	etrax_usb_unlink_urb
 };
 
-#ifdef USB_DEBUG_DESC
-static void dump_urb(struct urb *urb)
-{
-	printk("\nurb                   :0x%08X\n", urb);
-	printk("next                  :0x%08X\n", urb->next);
-	printk("dev                   :0x%08X\n", urb->dev);
-	printk("pipe                  :0x%08X\n", urb->pipe);
-	printk("status                :%d\n", urb->status);
-	printk("transfer_flags        :0x%08X\n", urb->transfer_flags);
-	printk("transfer_buffer       :0x%08X\n", urb->transfer_buffer);
-	printk("transfer_buffer_length:%d\n", urb->transfer_buffer_length);
-	printk("actual_length         :%d\n", urb->actual_length);
-	printk("setup_packet          :0x%08X\n", urb->setup_packet);
-	printk("start_frame           :%d\n", urb->start_frame);
-	printk("number_of_packets     :%d\n", urb->number_of_packets);
-	printk("interval              :%d\n", urb->interval);
-	printk("error_count           :%d\n", urb->error_count);
-	printk("context               :0x%08X\n", urb->context);
-	printk("complete              :0x%08X\n\n", urb->complete);
-}
-
-static void dump_in_desc(USB_IN_Desc_t *in)
-{
-	printk("\nUSB_IN_Desc at 0x%08X\n", in);
-	printk("  sw_len  : 0x%04X (%d)\n", in->sw_len, in->sw_len);
-	printk("  command : 0x%04X\n", in->command);
-	printk("  next    : 0x%08X\n", in->next);
-	printk("  buf     : 0x%08X\n", in->buf);
-	printk("  hw_len  : 0x%04X (%d)\n", in->hw_len, in->hw_len);
-	printk("  status  : 0x%04X\n\n", in->status);
-}
-
-static void dump_sb_desc(USB_SB_Desc_t *sb)
-{
-	printk("\nUSB_SB_Desc at 0x%08X\n", sb);
-	printk("  sw_len  : 0x%04X (%d)\n", sb->sw_len, sb->sw_len);
-	printk("  command : 0x%04X\n", sb->command);
-	printk("  next    : 0x%08X\n", sb->next);
-	printk("  buf     : 0x%08X\n\n", sb->buf);
+/* Note that these functions are always available in their "__" variants, for use in
+   error situations. The "__" missing variants are controlled by the USB_DEBUG_DESC/
+   USB_DEBUG_URB macros. */
+static void __dump_urb(purb_t purb)
+{
+	printk("\nurb                  :0x%08lx\n", (unsigned long)purb);
+	printk("next                  :0x%08lx\n", (unsigned long)purb->next);
+	printk("dev                   :0x%08lx\n", (unsigned long)purb->dev);
+	printk("pipe                  :0x%08x\n", purb->pipe);
+	printk("status                :%d\n", purb->status);
+	printk("transfer_flags        :0x%08x\n", purb->transfer_flags);
+	printk("transfer_buffer       :0x%08lx\n", (unsigned long)purb->transfer_buffer);
+	printk("transfer_buffer_length:%d\n", purb->transfer_buffer_length);
+	printk("actual_length         :%d\n", purb->actual_length);
+	printk("setup_packet          :0x%08lx\n", (unsigned long)purb->setup_packet);
+	printk("start_frame           :%d\n", purb->start_frame);
+	printk("number_of_packets     :%d\n", purb->number_of_packets);
+	printk("interval              :%d\n", purb->interval);
+	printk("error_count           :%d\n", purb->error_count);
+	printk("context               :0x%08lx\n", (unsigned long)purb->context);
+        printk("complete              :0x%08lx\n\n", (unsigned long)purb->complete);
+}
+
+static void __dump_in_desc(volatile USB_IN_Desc_t *in)
+{
+	printk("\nUSB_IN_Desc at 0x%08lx\n", (unsigned long)in);
+	printk("  sw_len  : 0x%04x (%d)\n", in->sw_len, in->sw_len);
+	printk("  command : 0x%04x\n", in->command);
+	printk("  next    : 0x%08lx\n", in->next);
+	printk("  buf     : 0x%08lx\n", in->buf);
+	printk("  hw_len  : 0x%04x (%d)\n", in->hw_len, in->hw_len);
+        printk("  status  : 0x%04x\n\n", in->status);
+}
+
+static void __dump_sb_desc(volatile USB_SB_Desc_t *sb)
+{
+	char tt = (sb->command & 0x30) >> 4;
+	char *tt_string;
+
+	switch (tt) {
+	case 0:
+		tt_string = "zout";
+		break;
+	case 1:
+		tt_string = "in";
+		break;
+	case 2:
+		tt_string = "out";
+		break;
+	case 3:
+		tt_string = "setup";
+		break;
+	default:
+		tt_string = "unknown (weird)";
+	}
+		
+	printk("\n   USB_SB_Desc at 0x%08lx\n", (unsigned long)sb); 
+        printk("     command : 0x%04x\n", sb->command);
+	printk("        rem     : %d\n", (sb->command & 0x3f00) >> 8);
+	printk("        full    : %d\n", (sb->command & 0x40) >> 6);
+	printk("        tt      : %d (%s)\n", tt, tt_string);
+	printk("        intr    : %d\n", (sb->command & 0x8) >> 3);
+	printk("        eot     : %d\n", (sb->command & 0x2) >> 1);
+	printk("        eol     : %d\n", sb->command & 0x1);
+        printk("     sw_len  : 0x%04x (%d)\n", sb->sw_len, sb->sw_len);
+	printk("     next    : 0x%08lx\n", sb->next);
+        printk("     buf     : 0x%08lx\n\n", sb->buf);
 }
 
 
-static void dump_ep_desc(USB_EP_Desc_t *ep)
-{
-	printk("\nUSB_EP_Desc at 0x%08X\n", ep);
-	printk("  hw_len  : 0x%04X (%d)\n", ep->hw_len, ep->hw_len);
-	printk("  command : 0x%08X\n", ep->command);
-	printk("  sub     : 0x%08X\n", ep->sub);
-	printk("  nep     : 0x%08X\n\n", ep->nep);
+static void __dump_ep_desc(volatile USB_EP_Desc_t *ep)
+{
+	printk("\nUSB_EP_Desc at 0x%08lx\n", (unsigned long)ep);
+        printk("  command : 0x%04x\n", ep->command);
+	printk("     ep_id   : %d\n", (ep->command & 0x1f00) >> 8);
+	printk("     enable  : %d\n", (ep->command & 0x10) >> 4);
+	printk("     intr    : %d\n", (ep->command & 0x8) >> 3);
+	printk("     eof     : %d\n", (ep->command & 0x2) >> 1);
+	printk("     eol     : %d\n", ep->command & 0x1);
+	printk("  hw_len  : 0x%04x (%d)\n", ep->hw_len, ep->hw_len);
+        printk("  next    : 0x%08lx\n", ep->next);
+	printk("  sub     : 0x%08lx\n\n", ep->sub);
+}
+
+static inline void __dump_ep_list(int pipe_type)
+{
+        volatile USB_EP_Desc_t *ep;
+        volatile USB_EP_Desc_t *first_ep;
+        volatile USB_SB_Desc_t *sb;
+
+        switch (pipe_type)
+        {
+        case PIPE_BULK:
+                first_ep = &TxBulkEPList[0];
+                break;
+        case PIPE_CONTROL:
+                first_ep = &TxCtrlEPList[0];
+                break;
+        case PIPE_INTERRUPT:
+                first_ep = &TxIntrEPList[0];
+                break;
+        case PIPE_ISOCHRONOUS:
+                first_ep = &TxIsocEPList[0];
+                break;
+        default:
+		warn("Cannot dump unknown traffic type");
+		return;
+        }
+        ep = first_ep;
+
+        printk("\n\nDumping EP list...\n\n");
+
+        do {
+		__dump_ep_desc(ep);
+		/* Cannot phys_to_virt on 0 as it turns into 80000000, which is != 0. */
+		sb = ep->sub ? phys_to_virt(ep->sub) : 0;                
+		while (sb) {
+			__dump_sb_desc(sb);
+			sb = sb->next ? phys_to_virt(sb->next) : 0;
+		}
+                ep = (volatile USB_EP_Desc_t *)(phys_to_virt(ep->next));
+                
+        } while (ep != first_ep);
+}
+
+static inline void __dump_ept_data(int epid)
+{
+	unsigned long flags;
+	__u32 r_usb_ept_data;
+
+	if (epid < 0 || epid > 31) {
+		printk("Cannot dump ept data for invalid epid %d\n", epid);
+		return;
+	}
+
+	save_flags(flags);
+	cli();
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
+	nop();
+	r_usb_ept_data = *R_USB_EPT_DATA;
+	restore_flags(flags);
+	
+	printk("\nR_USB_EPT_DATA = 0x%x for epid %d :\n", r_usb_ept_data, epid);
+	if (r_usb_ept_data == 0) {
+		/* No need for more detailed printing. */
+		return;
+	}
+	printk("  valid           : %d\n", (r_usb_ept_data & 0x80000000) >> 31);
+	printk("  hold            : %d\n", (r_usb_ept_data & 0x40000000) >> 30);
+	printk("  error_count_in  : %d\n", (r_usb_ept_data & 0x30000000) >> 28);
+	printk("  t_in            : %d\n", (r_usb_ept_data & 0x08000000) >> 27);
+	printk("  low_speed       : %d\n", (r_usb_ept_data & 0x04000000) >> 26);
+	printk("  port            : %d\n", (r_usb_ept_data & 0x03000000) >> 24);
+	printk("  error_code      : %d\n", (r_usb_ept_data & 0x00c00000) >> 22);
+	printk("  t_out           : %d\n", (r_usb_ept_data & 0x00200000) >> 21);
+	printk("  error_count_out : %d\n", (r_usb_ept_data & 0x00180000) >> 19);
+	printk("  max_len         : %d\n", (r_usb_ept_data & 0x0003f800) >> 11);
+	printk("  ep              : %d\n", (r_usb_ept_data & 0x00000780) >> 7);
+	printk("  dev             : %d\n", (r_usb_ept_data & 0x0000003f)); 
 }
 
+static inline void __dump_ept_data_list(void)
+{
+	int i;
 
+	printk("Dumping the whole R_USB_EPT_DATA list\n");
+
+	for (i = 0; i < 32; i++) {
+		__dump_ept_data(i);
+	}
+}
+#ifdef USB_DEBUG_DESC
+#define dump_in_desc(...) __dump_in_desc(...)
+#define dump_sb_desc(...) __dump_sb_desc(...)
+#define dump_ep_desc(...) __dump_ep_desc(...)
 #else
-#define dump_urb(...)     do {} while (0)
 #define dump_in_desc(...) do {} while (0)
 #define dump_sb_desc(...) do {} while (0)
 #define dump_ep_desc(...) do {} while (0)
 #endif
 
+#ifdef USB_DEBUG_URB
+#define dump_urb(x)     __dump_urb(x)
+#else
+#define dump_urb(x)     do {} while (0)
+#endif
+
 static void init_rx_buffers(void)
 {
 	int i;
@@ -315,6 +651,11 @@
 		RxDescList[i].buf = virt_to_phys(RxBuf + (i * RX_DESC_BUF_SIZE));
 		RxDescList[i].hw_len = 0;
 		RxDescList[i].status = 0;
+
+                /* DMA IN cache bug. (struct etrax_dma_descr has the same layout as USB_IN_Desc
+		   for the relevant fields.) */
+                prepare_rx_descriptor((struct etrax_dma_descr*)&RxDescList[i]);
+
 	}
 	
 	RxDescList[i].sw_len = RX_DESC_BUF_SIZE;
@@ -334,367 +675,254 @@
 	DBFEXIT;
 }
 
-static void init_tx_ctrl_ep(void)
+static void init_tx_bulk_ep(void)
 {
 	int i;
 	
 	DBFENTER;
 	
-	for (i = 0; i < (NBR_OF_EP_DESC - 1); i++) {
-		TxCtrlEPList[i].hw_len = 0;
-		TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
-		TxCtrlEPList[i].sub = 0;
-		TxCtrlEPList[i].nep = virt_to_phys(&TxCtrlEPList[i + 1]);
+	for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+                CHECK_ALIGN(&TxBulkEPList[i]);
+		TxBulkEPList[i].hw_len = 0;
+		TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+		TxBulkEPList[i].sub = 0;
+		TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[i + 1]);
+
+		/* Initiate two EPs, disabled and with the eol flag set. No need for any
+		   preserved epid. */
+
+		/* The first one has the intr flag set so we get an interrupt when the DMA
+		   channel is about to become disabled. */
+		CHECK_ALIGN(&TxBulkDummyEPList[i][0]);
+		TxBulkDummyEPList[i][0].hw_len = 0;
+		TxBulkDummyEPList[i][0].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
+						   IO_STATE(USB_EP_command, eol, yes) |
+						   IO_STATE(USB_EP_command, intr, yes));
+		TxBulkDummyEPList[i][0].sub = 0;
+		TxBulkDummyEPList[i][0].next = virt_to_phys(&TxBulkDummyEPList[i][1]);
+
+		/* The second one. */
+		CHECK_ALIGN(&TxBulkDummyEPList[i][1]);
+		TxBulkDummyEPList[i][1].hw_len = 0;
+		TxBulkDummyEPList[i][1].command = (IO_FIELD(USB_EP_command, epid, DUMMY_EPID) |
+						   IO_STATE(USB_EP_command, eol, yes));
+		TxBulkDummyEPList[i][1].sub = 0;
+		/* The last dummy's next pointer is the same as the current EP's next pointer. */
+		TxBulkDummyEPList[i][1].next = virt_to_phys(&TxBulkEPList[i + 1]);
 	}
-	
-	TxCtrlEPList[i].hw_len = 0;
-	TxCtrlEPList[i].command = IO_STATE(USB_EP_command, eol, yes) |
-		IO_FIELD(USB_EP_command, epid, i);
 
-	TxCtrlEPList[i].sub = 0;
-	TxCtrlEPList[i].nep = virt_to_phys(&TxCtrlEPList[0]);
-	
-	*R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]);
-	*R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
-	
+	/* Configure the last one. */
+        CHECK_ALIGN(&TxBulkEPList[i]);
+	TxBulkEPList[i].hw_len = 0;
+	TxBulkEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
+                                   IO_FIELD(USB_EP_command, epid, i));
+	TxBulkEPList[i].sub = 0;
+	TxBulkEPList[i].next = virt_to_phys(&TxBulkEPList[0]);
+
+	/* No need configuring dummy EPs for the last one as it will never be used for
+	   bulk traffic (i == INVALD_EPID at this point). */
+
+	/* Set up to start on the last EP so we will enable it when inserting traffic
+	   for the first time (imitating the situation where the DMA has stopped
+	   because there was no more traffic). */
+	*R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[i]);
+	/* No point in starting the bulk channel yet.
+	 *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start); */
 	DBFEXIT;
 }
 
-static void init_tx_bulk_ep(void)
+static void init_tx_ctrl_ep(void)
 {
 	int i;
 	
 	DBFENTER;
 	
-	for (i = 0; i < (NBR_OF_EP_DESC - 1); i++) {
-		TxBulkEPList[i].hw_len = 0;
-		TxBulkEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
-		TxBulkEPList[i].sub = 0;
-		TxBulkEPList[i].nep = virt_to_phys(&TxBulkEPList[i + 1]);
+	for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+                CHECK_ALIGN(&TxCtrlEPList[i]);
+		TxCtrlEPList[i].hw_len = 0;
+		TxCtrlEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+		TxCtrlEPList[i].sub = 0;
+		TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[i + 1]);
 	}
 	
-	TxBulkEPList[i].hw_len = 0;
-	TxBulkEPList[i].command = IO_STATE(USB_EP_command, eol, yes) |
-		IO_FIELD(USB_EP_command, epid, i);
+        CHECK_ALIGN(&TxCtrlEPList[i]);
+	TxCtrlEPList[i].hw_len = 0;
+	TxCtrlEPList[i].command = (IO_STATE(USB_EP_command, eol, yes) |
+                                   IO_FIELD(USB_EP_command, epid, i));
 
-	TxBulkEPList[i].sub = 0;
-	TxBulkEPList[i].nep = virt_to_phys(&TxBulkEPList[0]);
+	TxCtrlEPList[i].sub = 0;
+	TxCtrlEPList[i].next = virt_to_phys(&TxCtrlEPList[0]);
 	
-	*R_DMA_CH8_SUB0_EP = virt_to_phys(&TxBulkEPList[0]);
-	*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+	*R_DMA_CH8_SUB1_EP = virt_to_phys(&TxCtrlEPList[0]);
+	*R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
 	
 	DBFEXIT;
 }
 
+
 static void init_tx_intr_ep(void)
 {
 	int i;
 
 	DBFENTER;
 
-	TxIntrSB_zout.sw_len = 0;
+        /* Read comment at zout_buffer declaration for an explanation to this. */
+	TxIntrSB_zout.sw_len = 1;
 	TxIntrSB_zout.next = 0;
-	TxIntrSB_zout.buf = 0;
-	TxIntrSB_zout.command = IO_FIELD(USB_SB_command, rem, 0) |
-		IO_STATE(USB_SB_command, tt, zout) |
-		IO_STATE(USB_SB_command, full, yes) |
-		IO_STATE(USB_SB_command, eot, yes) |
-		IO_STATE(USB_SB_command, eol, yes);
+	TxIntrSB_zout.buf = virt_to_phys(&zout_buffer[0]);
+	TxIntrSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                 IO_STATE(USB_SB_command, tt, zout) |
+                                 IO_STATE(USB_SB_command, full, yes) |
+                                 IO_STATE(USB_SB_command, eot, yes) |
+                                 IO_STATE(USB_SB_command, eol, yes));
 
 	for (i = 0; i < (MAX_INTR_INTERVAL - 1); i++) {
+                CHECK_ALIGN(&TxIntrEPList[i]);
 		TxIntrEPList[i].hw_len = 0;
-		TxIntrEPList[i].command = IO_STATE(USB_EP_command, eof, yes) |
-			IO_STATE(USB_EP_command, enable, yes) |
-			IO_FIELD(USB_EP_command, epid, 0);
+		TxIntrEPList[i].command =
+			(IO_STATE(USB_EP_command, eof, yes) |
+                         IO_STATE(USB_EP_command, enable, yes) |
+                         IO_FIELD(USB_EP_command, epid, INVALID_EPID));
 		TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
-		TxIntrEPList[i].nep = virt_to_phys(&TxIntrEPList[i + 1]);
+		TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[i + 1]);
 	}
 	
+	CHECK_ALIGN(&TxIntrEPList[i]);
 	TxIntrEPList[i].hw_len = 0;
 	TxIntrEPList[i].command =
-		IO_STATE(USB_EP_command, eof, yes) |
-		IO_STATE(USB_EP_command, enable, yes) |
-		IO_FIELD(USB_EP_command, epid, 0);
+		(IO_STATE(USB_EP_command, eof, yes) |
+		 IO_STATE(USB_EP_command, eol, yes) |
+		 IO_STATE(USB_EP_command, enable, yes) |
+		 IO_FIELD(USB_EP_command, epid, INVALID_EPID));
 	TxIntrEPList[i].sub = virt_to_phys(&TxIntrSB_zout);
-	TxIntrEPList[i].nep = virt_to_phys(&TxIntrEPList[0]);
+	TxIntrEPList[i].next = virt_to_phys(&TxIntrEPList[0]);
 
 	*R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]);
 	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
-	
 	DBFEXIT;
 }
 
-
-static int etrax_usb_unlink_intr_urb(struct urb *urb)
+static void init_tx_isoc_ep(void)
 {
-	USB_EP_Desc_t *tmp_ep;
-	USB_EP_Desc_t *first_ep;
-	
-	USB_EP_Desc_t *ep_desc;
-	
-	int epid;
-	char devnum;
-	char endpoint;
-	char slow;
-	int maxlen;
-	char out_traffic;
 	int i;
 	
 	DBFENTER;
-
-	devnum = usb_pipedevice(urb->pipe);
-	endpoint = usb_pipeendpoint(urb->pipe);
-	slow = usb_pipeslow(urb->pipe);
-	maxlen = usb_maxpacket(urb->dev, urb->pipe,
-			       usb_pipeout(urb->pipe));
-	out_traffic = usb_pipeout(urb->pipe);
-
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
-	if (epid == -1) {
-		err("Trying to unlink urb that is not in traffic queue!!");
-		return -1;  /* fix this */
-	}
-
-	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, stop);
-	/* Somehow wait for the DMA to finish current activities */
-	i = jiffies + 100;
-	while (jiffies < i);	
 	
-	first_ep = &TxIntrEPList[0];
-	tmp_ep = first_ep;
+        /* Read comment at zout_buffer declaration for an explanation to this. */
+	TxIsocSB_zout.sw_len = 1;
+	TxIsocSB_zout.next = 0;
+	TxIsocSB_zout.buf = virt_to_phys(&zout_buffer[0]);
+	TxIsocSB_zout.command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                 IO_STATE(USB_SB_command, tt, zout) |
+                                 IO_STATE(USB_SB_command, full, yes) |
+                                 IO_STATE(USB_SB_command, eot, yes) |
+                                 IO_STATE(USB_SB_command, eol, yes));
 	
-	do {
-		if (IO_EXTRACT(USB_EP_command, epid, ((USB_EP_Desc_t *)phys_to_virt(tmp_ep->nep))->command)
-		    == epid) {
-			/* Unlink it !!! */
-			dbg_intr("Found urb to unlink for epid %d", epid);
-			
-			ep_desc = phys_to_virt(tmp_ep->nep);
-			tmp_ep->nep = ep_desc->nep;
-			kmem_cache_free(usb_desc_cache, phys_to_virt(ep_desc->sub));
-			kmem_cache_free(usb_desc_cache, ep_desc);
-		}
+	/* The last isochronous EP descriptor is a dummy. */
+	
+	for (i = 0; i < (NBR_OF_EPIDS - 1); i++) {
+                CHECK_ALIGN(&TxIsocEPList[i]);
+		TxIsocEPList[i].hw_len = 0;
+		TxIsocEPList[i].command = IO_FIELD(USB_EP_command, epid, i);
+		TxIsocEPList[i].sub = 0;
+		TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[i + 1]);
+	}
 
-		tmp_ep = phys_to_virt(tmp_ep->nep);
-		
-	} while (tmp_ep != first_ep);
+        CHECK_ALIGN(&TxIsocEPList[i]);
+	TxIsocEPList[i].hw_len = 0;
 
-	/* We should really try to move the EP register to an EP that is not removed
-	   instead of restarting, but this will work too */
-	*R_DMA_CH8_SUB2_EP = virt_to_phys(&TxIntrEPList[0]);
-	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+	/* Must enable the last EP descr to get eof interrupt. */
+	TxIsocEPList[i].command = (IO_STATE(USB_EP_command, enable, yes) |
+                                   IO_STATE(USB_EP_command, eof, yes) |
+                                   IO_STATE(USB_EP_command, eol, yes) |
+                                   IO_FIELD(USB_EP_command, epid, INVALID_EPID));
+	TxIsocEPList[i].sub = virt_to_phys(&TxIsocSB_zout);
+	TxIsocEPList[i].next = virt_to_phys(&TxIsocEPList[0]);
 
-	clear_bit(epid, (void *)&ep_really_active);
-	URB_List[epid] = NULL;
-	etrax_usb_free_epid(epid);
+	*R_DMA_CH8_SUB3_EP = virt_to_phys(&TxIsocEPList[0]);
+	*R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
 	
 	DBFEXIT;
-
-	return 0;
 }
 
-void etrax_usb_do_intr_recover(int epid)
+static void etrax_usb_unlink_intr_urb(urb_t *urb)
 {
-	USB_EP_Desc_t *first_ep, *tmp_ep;
-	
-	first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
-	tmp_ep = first_ep;
-
-	do {
-		if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid &&
-		    !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) {
-			tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes);
-		}
-		
-		tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->nep);
-		
-	} while (tmp_ep != first_ep);
-}
+	volatile USB_EP_Desc_t *first_ep;  /* First EP in the list. */
+	volatile USB_EP_Desc_t *curr_ep;   /* Current EP, the iterator. */
+	volatile USB_EP_Desc_t *next_ep;   /* The EP after current. */
+	volatile USB_EP_Desc_t *unlink_ep; /* The one we should remove from the list. */
 
-static int etrax_usb_submit_intr_urb(struct urb *urb)
-{
-	USB_EP_Desc_t *tmp_ep;
-	USB_EP_Desc_t *first_ep;
-	
 	int epid;
-	char devnum;
-	char endpoint;
-	char maxlen;
-	char out_traffic;
-	char slow;
-	int interval;
-	int i;
-	
-	etrax_urb_priv_t *urb_priv;
-	
-	DBFENTER;
-
-	devnum = usb_pipedevice(urb->pipe);
-	endpoint = usb_pipeendpoint(urb->pipe);
-	maxlen = usb_maxpacket(urb->dev, urb->pipe,
-			       usb_pipeout(urb->pipe));
-	out_traffic = usb_pipeout(urb->pipe);
 
-	slow = usb_pipeslow(urb->pipe);
-	interval = urb->interval;
+        /* Read 8.8.4 in Designer's Reference, "Removing an EP Descriptor from the List". */
 
-	dbg_intr("Intr traffic for dev %d, endpoint %d, maxlen %d, slow %d",
-		 devnum, endpoint, maxlen, slow);
-	
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
-	if (epid == -1) {
-		epid = etrax_usb_allocate_epid();
-		if (epid == -1) {
-			/* We're out of endpoints, return some error */
-			err("We're out of endpoints");
-			return -ENOMEM;
-		}
-		/* Now we have to fill in this ep */
-		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic);
-	}
-	/* Ok, now we got valid endpoint, lets insert some traffic */
+	DBFENTER;
 
-	urb_priv = (etrax_urb_priv_t *)kmalloc(sizeof(etrax_urb_priv_t), GFP_KERNEL);
-	urb_priv->first_sb = 0;
-	urb_priv->rx_offset = 0;
-	urb_priv->eot = 0;
-	INIT_LIST_HEAD(&urb_priv->ep_in_list);
-	urb->hcpriv = urb_priv;
+	epid = ((etrax_urb_priv_t *)urb->hcpriv)->epid;
 
-	/* This is safe since there cannot be any other URB's for this epid */
-	URB_List[epid] = urb;
-#if 0
-	first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
-#else
 	first_ep = &TxIntrEPList[0];
-#endif
+	curr_ep = first_ep;
+	
 
-	/* Round of the interval to 2^n, it is obvious that this code favours
-	   smaller numbers, but that is actually a good thing */
-	for (i = 0; interval; i++) {
-		interval = interval >> 1;
-	}
+        /* Note that this loop removes all EP descriptors with this epid. This assumes
+           that all EP descriptors belong to the one and only urb for this epid. */
 
-	urb->interval = interval = 1 << (i - 1);
+	do {
+                next_ep = (USB_EP_Desc_t *)phys_to_virt(curr_ep->next);
 
-	dbg_intr("Interval rounded to %d", interval);
+		if (IO_EXTRACT(USB_EP_command, epid, next_ep->command) == epid) {
 
-	tmp_ep = first_ep;
-	i = 0;
-	do {
-		if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) {
-			if ((i % interval) == 0) {
-				/* Insert the traffic ep after tmp_ep */
-				USB_EP_Desc_t *traffic_ep;
-				USB_SB_Desc_t *traffic_sb;
+			dbg_intr("Found EP to unlink for epid %d", epid);
+                        
+                        /* This is the one we should unlink. */
+			unlink_ep = next_ep;
 
-				traffic_ep = (USB_EP_Desc_t *)
-					kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
-				traffic_sb = (USB_SB_Desc_t *)
-					kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
-
-				traffic_ep->hw_len = 0;
-				traffic_ep->command = IO_FIELD(USB_EP_command, epid, epid) |
-					IO_STATE(USB_EP_command, enable, yes);
-				traffic_ep->sub = virt_to_phys(traffic_sb);
-
-				if (usb_pipein(urb->pipe)) {
-					traffic_sb->sw_len = urb->transfer_buffer_length ?
-						(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
-					traffic_sb->next = 0;
-					traffic_sb->buf = 0;
-					traffic_sb->command = IO_FIELD(USB_SB_command, rem,
-								       urb->transfer_buffer_length % maxlen) |
-						IO_STATE(USB_SB_command, tt, in) |
-						IO_STATE(USB_SB_command, eot, yes) |
-						IO_STATE(USB_SB_command, eol, yes);
-					
-				} else if (usb_pipeout(urb->pipe)) {
-					traffic_sb->sw_len = urb->transfer_buffer_length;
-					traffic_sb->next = 0;
-					traffic_sb->buf = virt_to_phys(urb->transfer_buffer);
-					traffic_sb->command = IO_FIELD(USB_SB_command, rem, 0) |
-						IO_STATE(USB_SB_command, tt, out) |
-						IO_STATE(USB_SB_command, eot, yes) |
-						IO_STATE(USB_SB_command, eol, yes) |
-						IO_STATE(USB_SB_command, full, yes);
-				}
+                        /* Actually unlink the EP from the DMA list. */
+			curr_ep->next = unlink_ep->next;
 
-				traffic_ep->nep = tmp_ep->nep;
-				tmp_ep->nep = virt_to_phys(traffic_ep);
-				dbg_intr("One ep sucessfully inserted");
-			}
-			i++;
+                        /* Wait until the DMA is no longer at this descriptor. */
+                        while (*R_DMA_CH8_SUB2_EP == virt_to_phys(unlink_ep));
+
+                        /* Now we are free to remove it and its SB descriptor.
+                           Note that it is assumed here that there is only one sb in the
+                           sb list for this ep. */
+			kmem_cache_free(usb_desc_cache, phys_to_virt(unlink_ep->sub));
+			kmem_cache_free(usb_desc_cache, (USB_EP_Desc_t *)unlink_ep);
 		}
-		tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->nep);
-	} while (tmp_ep != first_ep);
 
-	set_bit(epid, (void *)&ep_really_active);
-	
-	*R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+		curr_ep = phys_to_virt(curr_ep->next);
+		
+	} while (curr_ep != first_ep);
 
-	DBFEXIT;
-	
-	return 0;
 }
 
-
-static void handle_intr_transfer_attn(int epid, int status)
+void etrax_usb_do_intr_recover(int epid)
 {
-	struct urb *old_urb;
+	USB_EP_Desc_t *first_ep, *tmp_ep;
 
 	DBFENTER;
+        
+	first_ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB2_EP);
+	tmp_ep = first_ep;
 
-	old_urb = URB_List[epid];
-	
-	/* if (status == 0 && IN) find data and copy to urb */
-	if (status == 0 && usb_pipein(old_urb->pipe)) {
-		unsigned long flags;
-		etrax_urb_priv_t *urb_priv;
-		struct list_head *entry;
-		struct in_chunk *in;
-
-		urb_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
+        /* What this does is simply to walk the list of interrupt
+           ep descriptors and enable those that are disabled. */
+ 
+	do {
+		if (IO_EXTRACT(USB_EP_command, epid, tmp_ep->command) == epid &&
+		    !(tmp_ep->command & IO_MASK(USB_EP_command, enable))) {
+			tmp_ep->command |= IO_STATE(USB_EP_command, enable, yes);
+		}
 		
-		save_flags(flags);
-		cli();
+		tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
+		
+	} while (tmp_ep != first_ep);
 
-		list_for_each(entry, &urb_priv->ep_in_list) {
-			in = list_entry(entry, struct in_chunk, list);
-			memcpy(old_urb->transfer_buffer, in->data, in->length);
-			old_urb->actual_length = in->length;
-			old_urb->status = status;
-			
-			if (old_urb->complete) {
-				old_urb->complete(old_urb);
-			}
-			
-			list_del(entry);
-			kfree(in->data);
-			kfree(in);
-		}		
-		
-		restore_flags(flags);
-
-	} else if (status != 0) {
-		warn("Some sort of error for INTR EP !!!!");
-#ifdef ETRAX_USB_INTR_ERROR_FATAL
-		/* This means that an INTR error is fatal for that endpoint */
-		etrax_usb_unlink_intr_urb(old_urb);
-		old_urb->status = status;
-		if (old_urb->complete) {
-			old_urb->complete(old_urb);
-		}
-#else
-		/* In this case we reenable the disabled endpoint(s) */
-		etrax_usb_do_intr_recover(epid);
-#endif	
-	}
-	
-	DBFEXIT;
+
+        DBFEXIT;
 }
 
-static int etrax_rh_unlink_urb (struct urb *urb)
+static int etrax_rh_unlink_urb (urb_t *urb)
 {
 	etrax_hc_t *hc;
 	
@@ -711,16 +939,12 @@
 	return 0;
 }
 
-static void etrax_rh_send_irq(struct urb *urb)
+static void etrax_rh_send_irq(urb_t *urb)
 {
 	__u16 data = 0;
 	etrax_hc_t *hc = urb->dev->bus->hcpriv;
-//	static prev_wPortStatus_1 = 0;
-//	static prev_wPortStatus_2 = 0;
-	
 /*	DBFENTER; */
-	
-	
+
 /*
   dbg_rh("R_USB_FM_NUMBER   : 0x%08X", *R_USB_FM_NUMBER);
   dbg_rh("R_USB_FM_REMAINING: 0x%08X", *R_USB_FM_REMAINING);
@@ -730,6 +954,8 @@
 	data |= (hc->rh.wPortChange_2) ? (1 << 2) : 0;
 
 	*((__u16 *)urb->transfer_buffer) = cpu_to_le16(data);
+        /* FIXME: Why is actual_length set to 1 when data is 2 bytes?
+           Since only 1 byte is used, why not declare data as __u8? */
 	urb->actual_length = 1;
 	urb->status = 0;
 
@@ -744,7 +970,7 @@
 /*	DBFEXIT; */
 }
 
-static void etrax_rh_init_int_timer(struct urb *urb)
+static void etrax_rh_init_int_timer(urb_t *urb)
 {
 	etrax_hc_t *hc;
 	
@@ -755,6 +981,8 @@
 	init_timer(&hc->rh.rh_int_timer);
 	hc->rh.rh_int_timer.function = etrax_rh_int_timer_do;
 	hc->rh.rh_int_timer.data = (unsigned long)urb;
+        /* FIXME: Is the jiffies resolution enough? All intervals < 10 ms will be mapped
+           to 0, and the rest to the nearest lower 10 ms. */
 	hc->rh.rh_int_timer.expires = jiffies + ((HZ * hc->rh.interval) / 1000);
 	add_timer(&hc->rh.rh_int_timer);
 	
@@ -763,12 +991,12 @@
 
 static void etrax_rh_int_timer_do(unsigned long ptr)
 {
-	struct urb *urb;
+	urb_t *urb;
 	etrax_hc_t *hc;
 	
 /*	DBFENTER; */
 	
-	urb = (struct urb*)ptr;
+	urb = (urb_t*)ptr;
 	hc = urb->dev->bus->hcpriv;
 	
 	if (hc->rh.send) {
@@ -780,44 +1008,81 @@
 /*	DBFEXIT; */
 }
 
-static void etrax_usb_setup_epid(int epid, char devnum, char endpoint, char packsize, char slow, char out_traffic)
+static int etrax_usb_setup_epid(urb_t *urb)
 {
+	int epid;
+	char devnum, endpoint, out_traffic, slow;
+	int maxlen;
 	unsigned long flags;
-	
-	DBFENTER;
 
-	save_flags(flags);
-	cli();
+	DBFENTER;
 	
-	if (test_bit(epid, (void *)&ep_usage_bitmask)) {
-		restore_flags(flags);
-
-		warn("Trying to setup used epid %d", epid);
+	epid = etrax_usb_lookup_epid(urb);
+	if ((epid != -1)){
+		/* An epid that fits this urb has been found. */
 		DBFEXIT;
-		return;
+		return epid;
 	}
 	
-	set_bit(epid, (void *)&ep_usage_bitmask);
+	/* We must find and initiate a new epid for this urb. */
+	epid = etrax_usb_allocate_epid();
+
+	if (epid == -1) {
+		/* Failed to allocate a new epid. */
+		DBFEXIT;
+		return epid;
+	}
+
+	/* We now have a new epid to use. Initiate it. */
+	set_bit(epid, (void *)&epid_usage_bitmask);
 	
+	devnum = usb_pipedevice(urb->pipe);
+	endpoint = usb_pipeendpoint(urb->pipe);
+	slow = usb_pipeslow(urb->pipe);
+	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+        if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+                /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
+                out_traffic = 1;
+        } else {
+                out_traffic = usb_pipeout(urb->pipe);
+        }
+
+	save_flags(flags);
+	cli();
+
 	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
 	nop();
-	*R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) |
-		IO_FIELD(R_USB_EPT_DATA, ep, endpoint) |
-		IO_FIELD(R_USB_EPT_DATA, dev, devnum) |
-		IO_FIELD(R_USB_EPT_DATA, max_len, packsize) |
-		IO_FIELD(R_USB_EPT_DATA, low_speed, slow);
-
-	if (out_traffic)
-		set_bit(epid, (void *)&ep_out_traffic);
-	else
-		clear_bit(epid, (void *)&ep_out_traffic);
 
+	if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+		*R_USB_EPT_DATA_ISO = IO_STATE(R_USB_EPT_DATA_ISO, valid, yes) |
+			/* FIXME: Change any to the actual port? */
+			IO_STATE(R_USB_EPT_DATA_ISO, port, any) |
+			IO_FIELD(R_USB_EPT_DATA_ISO, max_len, maxlen) |
+			IO_FIELD(R_USB_EPT_DATA_ISO, ep, endpoint) |
+			IO_FIELD(R_USB_EPT_DATA_ISO, dev, devnum);
+	} else {
+		*R_USB_EPT_DATA = IO_STATE(R_USB_EPT_DATA, valid, yes) |
+			IO_FIELD(R_USB_EPT_DATA, low_speed, slow) |
+			/* FIXME: Change any to the actual port? */
+			IO_STATE(R_USB_EPT_DATA, port, any) |
+			IO_FIELD(R_USB_EPT_DATA, max_len, maxlen) |
+			IO_FIELD(R_USB_EPT_DATA, ep, endpoint) |
+			IO_FIELD(R_USB_EPT_DATA, dev, devnum);
+	}	
+	
 	restore_flags(flags);
 
-	dbg_ep("Setting up ep_id %d with devnum %d, endpoint %d and max_len %d (%s)",
-	       epid, devnum, endpoint, packsize, out_traffic ? "OUT" : "IN");
+        if (out_traffic) {
+                set_bit(epid, (void *)&epid_out_traffic);
+        } else {
+                clear_bit(epid, (void *)&epid_out_traffic);
+	}
+
+	dbg_epid("Setting up epid %d with devnum %d, endpoint %d and max_len %d (%s)",
+                 epid, devnum, endpoint, maxlen, out_traffic ? "OUT" : "IN");
 
 	DBFEXIT;
+	return epid;
 }
 
 static void etrax_usb_free_epid(int epid)
@@ -826,7 +1091,7 @@
 	
 	DBFENTER;
 
-	if (!test_bit(epid, (void *)&ep_usage_bitmask)) {
+	if (!test_bit(epid, (void *)&epid_usage_bitmask)) {
 		warn("Trying to free unused epid %d", epid);
 		DBFEXIT;
 		return;
@@ -837,56 +1102,81 @@
 
 	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid);
 	nop();
-	while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold))
-		printk("+");
+	while (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold));
+        /* This will, among other things, set the valid field to 0. */
 	*R_USB_EPT_DATA = 0;
-	clear_bit(epid, (void *)&ep_usage_bitmask);
-
 	restore_flags(flags);
 
-	dbg_ep("epid: %d freed", epid);
+	clear_bit(epid, (void *)&epid_usage_bitmask);
+
+
+	dbg_epid("Freed epid %d", epid);
 	
 	DBFEXIT;
 }
 
-
-static int etrax_usb_lookup_epid(unsigned char devnum, char endpoint, char slow, int maxp, char out_traffic)
+static int etrax_usb_lookup_epid(urb_t *urb)
 {
 	int i;
-	unsigned long flags;
 	__u32 data;
-	
+	char devnum, endpoint, slow, out_traffic;
+	int maxlen;
+	unsigned long flags;
+
 	DBFENTER;
 
-	save_flags(flags);
-	cli();
+	devnum = usb_pipedevice(urb->pipe);
+	endpoint = usb_pipeendpoint(urb->pipe);
+	slow = usb_pipeslow(urb->pipe);
+	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+        if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+                /* We want both IN and OUT control traffic to be put on the same EP/SB list. */
+                out_traffic = 1;
+        } else {
+                out_traffic = usb_pipeout(urb->pipe);
+        }
+        
+	/* Step through att epids. */
+	for (i = 0; i < NBR_OF_EPIDS; i++) {
+		if (test_bit(i, (void *)&epid_usage_bitmask) &&
+                    test_bit(i, (void *)&epid_out_traffic) == out_traffic) {
 
-	/* Skip first ep_id since it is reserved when intr. or iso traffic is used */
-	for (i = 0; i < NBR_OF_EP_DESC; i++) {
-		if (test_bit(i, (void *)&ep_usage_bitmask) &&
-		    test_bit(i, (void *)&ep_out_traffic) == out_traffic) {
+			save_flags(flags);
+			cli();
 			*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, i);
 			nop();
-			data = *R_USB_EPT_DATA;
-			if ((IO_MASK(R_USB_EPT_DATA, valid) & data) &&
-			    (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) &&
-			    (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) &&
-			    (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) &&
-			    (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxp)) {
+
+			if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+				data = *R_USB_EPT_DATA_ISO;
 				restore_flags(flags);
-	
-				dbg_ep("Found ep_id %d for devnum %d, endpoint %d (%s)",
-				       i, devnum, endpoint, out_traffic ? "OUT" : "IN");
-				DBFEXIT;
-				return i;
+
+				if ((IO_MASK(R_USB_EPT_DATA_ISO, valid) & data) &&
+                                    (IO_EXTRACT(R_USB_EPT_DATA_ISO, dev, data) == devnum) &&
+                                    (IO_EXTRACT(R_USB_EPT_DATA_ISO, ep, data) == endpoint) &&
+                                    (IO_EXTRACT(R_USB_EPT_DATA_ISO, max_len, data) == maxlen)) {
+                                        dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
+                                                 i, devnum, endpoint, out_traffic ? "OUT" : "IN");
+                                        DBFEXIT;
+                                        return i;
+				}
+			} else {
+				data = *R_USB_EPT_DATA;
+				restore_flags(flags);
+
+				if ((IO_MASK(R_USB_EPT_DATA, valid) & data) &&
+				    (IO_EXTRACT(R_USB_EPT_DATA, dev, data) == devnum) &&
+				    (IO_EXTRACT(R_USB_EPT_DATA, ep, data) == endpoint) &&
+				    (IO_EXTRACT(R_USB_EPT_DATA, low_speed, data) == slow) &&
+				    (IO_EXTRACT(R_USB_EPT_DATA, max_len, data) == maxlen)) {	
+					dbg_epid("Found epid %d for devnum %d, endpoint %d (%s)",
+                                                 i, devnum, endpoint, out_traffic ? "OUT" : "IN");
+					DBFEXIT;
+					return i;
+				}
 			}
 		}
 	}
 
-	restore_flags(flags);
-	
-	dbg_ep("Found no ep_id for devnum %d, endpoint %d (%s)",
-	       devnum, endpoint, out_traffic ? "OUT" : "IN");
 	DBFEXIT;
 	return -1;
 }
@@ -897,434 +1187,993 @@
 	
 	DBFENTER;
 
-	for (i = 0; i < NBR_OF_EP_DESC; i++) {
-		if (!test_bit(i, (void *)&ep_usage_bitmask)) {
-			dbg_ep("Found free ep_id at %d", i);
+	for (i = 0; i < NBR_OF_EPIDS; i++) {
+		if (!test_bit(i, (void *)&epid_usage_bitmask)) {
+			dbg_epid("Found free epid %d", i);
 			DBFEXIT;
 			return i;
 		}
 	}
 
-	dbg_ep("Found no free ep_id's");
+	dbg_epid("Found no free epids");
 	DBFEXIT;
 	return -1;
 }
 
-static int etrax_usb_submit_bulk_urb(struct urb *urb)
+static int etrax_usb_submit_urb(urb_t *urb)
 {
-	int epid;
-	char devnum;
-	char endpoint;
-	char maxlen;
-	char out_traffic;
-	char slow;
-
-	struct urb *tmp_urb;
-	
-	unsigned long flags;
+	etrax_hc_t *hc;
+	int ret = -EINVAL;
 	
 	DBFENTER;
 
-	devnum = usb_pipedevice(urb->pipe);
-	endpoint = usb_pipeendpoint(urb->pipe);
-	maxlen = usb_maxpacket(urb->dev, urb->pipe,
-			       usb_pipeout(urb->pipe));
-	out_traffic = usb_pipeout(urb->pipe);
-	slow = usb_pipeslow(urb->pipe);
+        if (!urb->dev || !urb->dev->bus) {
+                return -ENODEV;
+        }
+	if (urb->next != NULL) {
+                /* Is it possible for urb to be the head of a list of urbs (via the urb's
+                   next pointer), used for example in drivers for isochronous traffic.
+                   I haven't seen a device driver that relies on it being used for submit
+                   or unlink, so we warn about it and ignore it. */
+		warn("Urbs are linked, ignoring.");
+	}
+        if (urb->timeout) {
+                /* FIXME. */
+                warn("urb->timeout specified, ignoring.");
+        }
+        
+	hc = (etrax_hc_t*)urb->dev->bus->hcpriv;
 	
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
-	if (epid == -1) {
-		epid = etrax_usb_allocate_epid();
-		if (epid == -1) {
-			/* We're out of endpoints, return some error */
-			err("We're out of endpoints");
-			return -ENOMEM;
+	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
+		/* This request is for the Virtual Root Hub. */
+		ret = etrax_rh_submit_urb(urb);
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+
+		ret = etrax_usb_submit_bulk_urb(urb);
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+
+		ret = etrax_usb_submit_ctrl_urb(urb);
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+		int bustime;
+                
+		if (urb->bandwidth == 0) {
+			bustime = usb_check_bandwidth(urb->dev, urb);
+			if (bustime < 0) {
+				ret = bustime;
+			} else {
+				ret = etrax_usb_submit_intr_urb(urb);
+				if (ret == 0)
+                                        usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+			}
+                } else {
+                        /* Bandwidth already set. */
+                        ret = etrax_usb_submit_intr_urb(urb);
+		}
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+                int bustime;
+        
+		if (urb->bandwidth == 0) {
+			bustime = usb_check_bandwidth(urb->dev, urb);
+			if (bustime < 0) {
+				ret = bustime;
+			} else {
+				ret = etrax_usb_submit_isoc_urb(urb);
+				if (ret == 0)
+                                        usb_claim_bandwidth(urb->dev, urb, bustime, 0);
+			}
+                } else {
+                        /* Bandwidth already set. */
+                        ret = etrax_usb_submit_isoc_urb(urb);
 		}
-		/* Now we have to fill in this ep */
-		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic);
 	}
-	/* Ok, now we got valid endpoint, lets insert some traffic */
 
-	urb->status = -EINPROGRESS;
+	DBFEXIT;
 
-	save_flags(flags);
-	cli();
-	
-	if (URB_List[epid]) {
-		/* Find end of list and add */
-		for (tmp_urb = URB_List[epid]; tmp_urb->next; tmp_urb = tmp_urb->next)
-			dump_urb(tmp_urb);
+	return ret;
+}
+
+static int etrax_usb_unlink_urb(urb_t *urb)
+{
+        etrax_hc_t *hc;
+        etrax_urb_priv_t *urb_priv;
+        int epid;
+
+	DBFENTER;
+        
+        if (!urb) {
+                return -EINVAL;
+        }        
+        if (!urb->dev || !urb->dev->bus) {
+                return -ENODEV;
+        }
+        if (!urb->hcpriv) {
+                /* This happens if a device driver calls unlink on an urb that
+                   was never submitted (lazy driver). */
+                return 0;
+        }
+        if (urb->transfer_flags & USB_ASYNC_UNLINK) {
+                /* FIXME. */
+                /* If USB_ASYNC_UNLINK is set:
+                   unlink
+                   move to a separate urb list
+                   call complete at next sof with ECONNRESET
+                   
+                   If not:
+                   wait 1 ms
+                   unlink
+                   call complete with ENOENT
+                */
+                warn("USB_ASYNC_UNLINK set, ignoring.");
+        }
+        
+        /* One might think that urb->status = -EINPROGRESS would be a requirement for unlinking,
+           but that doesn't work for interrupt and isochronous traffic since they are completed
+           repeatedly, and urb->status is set then. That may in itself be a bug though. */
+
+        hc = urb->dev->bus->hcpriv;
+        urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+        epid = urb_priv->epid;
+
+        /* Set the urb status (synchronous unlink). */
+        urb->status = -ENOENT;
+        urb_priv->urb_state = UNLINK;
+        
+	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
+		int ret;
+		ret = etrax_rh_unlink_urb(urb);
+		DBFEXIT;
+		return ret;
 
-		tmp_urb->next = urb;
-		restore_flags(flags);
-	} else {
-		/* If this is the first URB, add the URB and do HW add */
-		URB_List[epid] = urb;
-		restore_flags(flags);
-		etrax_usb_do_bulk_hw_add(urb, epid, maxlen);
-	}
+	} else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+
+                dbg_bulk("Unlink of bulk urb (0x%lx)", (unsigned long)urb);
+
+                if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+                        /* The EP was enabled, disable it and wait. */
+                        TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+                        
+                        /* Ah, the luxury of busy-wait. */
+                        while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
+                }
+		/* Kicking dummy list out of the party. */
+		TxBulkEPList[epid].next = virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
+
+        } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+
+                dbg_ctrl("Unlink of ctrl urb (0x%lx)", (unsigned long)urb);
+
+                if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+                        /* The EP was enabled, disable it and wait. */
+                        TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+                        
+                        /* Ah, the luxury of busy-wait. */
+                        while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
+                }
+
+	} else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+
+                dbg_intr("Unlink of intr urb (0x%lx)", (unsigned long)urb);
+
+                /* Separate function because it's a tad more complicated. */
+		etrax_usb_unlink_intr_urb(urb);
+
+        } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
 
+                dbg_isoc("Unlink of isoc urb (0x%lx)", (unsigned long)urb);
+
+                if (TxIsocEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+                        /* The EP was enabled, disable it and wait. */
+                        TxIsocEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
+                        
+                        /* Ah, the luxury of busy-wait. */
+                        while (*R_DMA_CH8_SUB3_EP == virt_to_phys(&TxIsocEPList[epid]));
+                }
+        }
+
+        /* Note that we need to remove the urb from the urb list *before* removing its SB
+           descriptors. (This means that the isoc eof handler might get a null urb when we
+           are unlinking the last urb.) */
+
+        urb_list_del(urb, epid);
+
+        if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+
+                TxBulkEPList[epid].sub = 0;
+                etrax_remove_from_sb_list(urb);
+
+        } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+
+                TxCtrlEPList[epid].sub = 0;
+                etrax_remove_from_sb_list(urb);
+
+        } else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
+
+                /* Sanity check (should never happen). */
+                assert(urb_list_empty(epid));
+
+                /* Release allocated bandwidth. */
+                usb_release_bandwidth(urb->dev, urb, 0);
+
+        } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+
+                if (usb_pipeout(urb->pipe)) {
+                        /* FIXME: If the SB list isn't empty at this point, we need to set up 
+                           the EP descriptor again. */
+                } else {
+                        /* For in traffic there is only one SB descriptor for each EP even
+                           though there may be several urbs (all urbs point at the same SB). */
+                        if (urb_list_empty(epid)) {
+                                /* No more urbs, remove the SB. */
+                                TxIsocEPList[epid].sub = 0;
+                                etrax_remove_from_sb_list(urb);
+                        }
+                }
+                /* Release allocated bandwidth. */
+                usb_release_bandwidth(urb->dev, urb, 1);
+        } 
+        
+        /* Must be done before calling completion handler. */
+        kfree(urb_priv);
+        urb->hcpriv = 0;
+
+        if (urb->complete) {
+                urb->complete(urb);
+        }
+
+        /* Free the epid if urb list is empty. */
+        if (urb_list_empty(epid)) {
+                etrax_usb_free_epid(epid);
+        }
+
+        DBFEXIT;
+        return 0;
+}
+
+static int etrax_usb_get_frame_number(struct usb_device *usb_dev)
+{
+	DBFENTER;
 	DBFEXIT;
+	return (*R_USB_FM_NUMBER & 0x7ff);
+}
 
+static int etrax_usb_allocate_dev(struct usb_device *usb_dev)
+{  
+	DBFENTER;
+	DBFEXIT;
 	return 0;
 }
 
-static void etrax_usb_do_bulk_hw_add(struct urb *urb, int epid, char maxlen)
+static int etrax_usb_deallocate_dev(struct usb_device *usb_dev)
 {
-	USB_SB_Desc_t *sb_desc_1;
+	DBFENTER;
+	DBFEXIT;
+	return 0;
+}
+
+static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs)
+{
+	DBFENTER;
+
+        /* This interrupt handler could be used when unlinking EP descriptors. */
+
+	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) {
+		USB_EP_Desc_t *ep;
+
+		//dbg_bulk("dma8_sub0_descr (BULK) intr.");
+
+		/* It should be safe clearing the interrupt here, since we don't expect to get a new
+		   one until we restart the bulk channel. */
+		*R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do);
 
+		/* Wait while the DMA is running (though we don't expect it to be). */
+		while (*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd));
+
+		/* Advance the DMA to the next EP descriptor. */
+		ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
+
+		//dbg_bulk("descr intr: DMA is at 0x%lx", (unsigned long)ep);
+
+		/* ep->next is already a physical address; no need for a virt_to_phys. */
+		*R_DMA_CH8_SUB0_EP = ep->next;
+
+		/* Start the DMA bulk channel again. */
+		*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+	}
+	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) {
+		dbg_ctrl("dma8_sub1_descr (CTRL) intr.");
+		*R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do);
+	}
+	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) {
+		dbg_intr("dma8_sub2_descr (INTR) intr.");
+		*R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do);
+	}
+	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) {
+		dbg_isoc("dma8_sub3_descr (ISOC) intr.");
+		*R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do);
+	}
+        
+	DBFEXIT;
+}
+
+static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs)
+{
+	urb_t *urb;
 	etrax_urb_priv_t *urb_priv;
+	int epid = 0;
+        unsigned long flags;
 
-	unsigned long flags;
+        /* Isoc diagnostics. */
+        static int curr_fm = 0;
+        static int prev_fm = 0;
+
+        DBFENTER;
+
+        /* Clear this interrupt. */
+	*R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
+
+        /* Note that this while loop assumes that all packets span only
+           one rx descriptor. */
+
+        /* The reason we cli here is that we call the driver's callback functions. */
+        save_flags(flags);
+        cli();
+
+
+ 	while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) {
+
+                epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status);
+                urb = urb_list_first(epid);
+
+                //printk("eop for epid %d, first urb 0x%lx\n", epid, (unsigned long)urb);
+                
+                if (!urb) {
+                        err("No urb for epid %d in rx interrupt", epid);
+			__dump_ept_data(epid);
+                        goto skip_out;
+                }
+
+                /* Note that we cannot indescriminately assert(usb_pipein(urb->pipe)) since
+                   ctrl pipes are not. */
+                
+		if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) {
+			warn("error in rx desc->status, epid %d, first urb = 0x%lx", 
+                             epid, (unsigned long)urb);
+			__dump_in_desc(myNextRxDesc);
+                        etrax_usb_complete_urb(urb, -EPROTO);
+			goto skip_out;
+		}
+
+                urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+                assert(urb_priv);
+                
+                if ((usb_pipetype(urb->pipe) == PIPE_BULK) || 
+                    (usb_pipetype(urb->pipe) == PIPE_CONTROL) ||
+                    (usb_pipetype(urb->pipe) == PIPE_INTERRUPT)) {
+
+                        if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+                                /* We get nodata for empty data transactions, and the rx descriptor's
+                                   hw_len field is not valid in that case. No data to copy in other 
+                                   words. */
+                        } else {
+                                /* Make sure the data fits in the buffer. */
+                                assert(urb_priv->rx_offset + myNextRxDesc->hw_len 
+                                       <= urb->transfer_buffer_length);
+
+                                memcpy(urb->transfer_buffer + urb_priv->rx_offset,
+                                       phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len);	
+                                urb_priv->rx_offset += myNextRxDesc->hw_len;
+                        }
+
+                        if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) {
+                                etrax_usb_complete_urb(urb, 0);
+                        }
+
+                } else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+                        
+                        iso_packet_descriptor_t *packet;
+
+                        if (urb_priv->urb_state == UNLINK) {
+                                info("Ignoring rx data for urb being unlinked.");
+                                goto skip_out;
+                        } else if (urb_priv->urb_state == NOT_STARTED) {
+                                info("What? Got rx data for urb that isn't started?");
+                                goto skip_out;
+                        }
+                                
+                        packet = &urb->iso_frame_desc[urb_priv->isoc_packet_counter];
+                        packet->status = 0;
+
+                         if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+                                /* We get nodata for empty data transactions, and the rx descriptor's
+                                   hw_len field is not valid in that case. We copy 0 bytes however to
+                                   stay in synch. */
+                                packet->actual_length = 0;                                
+                        } else {
+                                packet->actual_length = myNextRxDesc->hw_len;
+                                /* Make sure the data fits in the buffer. */
+                                assert(packet->actual_length <= packet->length);
+                                
+                                memcpy(urb->transfer_buffer + packet->offset,
+                                       phys_to_virt(myNextRxDesc->buf), packet->actual_length);
+                        }
+
+                        /* Increment the packet counter. */
+                        urb_priv->isoc_packet_counter++;	
+                        
+                        /* Note that we don't care about the eot field in the rx descriptor's status.
+                           It will always be set for isoc traffic. */
+			if (urb->number_of_packets == urb_priv->isoc_packet_counter) {
+
+                                /* Out-of-synch diagnostics. */
+                                curr_fm = (*R_USB_FM_NUMBER & 0x7ff);
+                                if (((prev_fm + urb_priv->isoc_packet_counter) % (0x7ff + 1)) != curr_fm) {
+                                        warn("Out of synch? Previous frame = %d, current frame = %d",
+                                             prev_fm, curr_fm);
+                                }
+                                prev_fm = curr_fm;
+
+				/* Complete the urb with status OK. */
+				etrax_usb_complete_isoc_urb(urb, 0);
+
+				/* Must set this to 0 since this urb is still active after
+				   completion. */
+				urb_priv->isoc_packet_counter = 0;
+			}
+                }
+                
+	skip_out:
+
+                /* DMA IN cache bug. Flush the DMA IN buffer from the cache. (struct etrax_dma_descr
+		   has the same layout as USB_IN_Desc for the relevant fields.) */
+                prepare_rx_descriptor((struct etrax_dma_descr*)myNextRxDesc);
+
+		myPrevRxDesc = myNextRxDesc;
+		myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol);
+		myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol);
+		myLastRxDesc = myPrevRxDesc;
+
+		myNextRxDesc->status = 0;
+		myNextRxDesc = phys_to_virt(myNextRxDesc->next);
+	}
+
+        restore_flags(flags);
+        
+        DBFEXIT;
+        
+}
+
+
+/* This function will unlink the SB descriptors associated with this urb. */
+static int etrax_remove_from_sb_list(urb_t *urb)
+{
+	USB_SB_Desc_t *next_sb, *first_sb, *last_sb;
+	etrax_urb_priv_t *urb_priv;
+	int i = 0;
 
 	DBFENTER;
 
-	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), GFP_KERNEL);
-	sb_desc_1 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
+	urb_priv = (etrax_urb_priv_t *)urb->hcpriv; 
+	assert(urb_priv);
 
-	if (usb_pipeout(urb->pipe)) {
+        /* Just a sanity check. Since we don't fiddle with the DMA list the EP descriptor
+	   doesn't really need to be disabled, it's just that we expect it to be. */
+        if (usb_pipetype(urb->pipe) == PIPE_BULK) {
+                assert(!(TxBulkEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+        } else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
+                assert(!(TxCtrlEPList[urb_priv->epid].command & IO_MASK(USB_EP_command, enable)));
+        } 
 
-		dbg_bulk("Bulk transfer for epid %d is OUT", epid);
-		dbg_bulk("transfer_buffer_length == %d", urb->transfer_buffer_length);
-		dbg_bulk("actual_length == %d", urb->actual_length);
+	first_sb = urb_priv->first_sb;
+	last_sb = urb_priv->last_sb;
 	
+        assert(first_sb);
+        assert(last_sb);
+        
+	while (first_sb != last_sb) {
+		next_sb = (USB_SB_Desc_t *)phys_to_virt(first_sb->next);
+		kmem_cache_free(usb_desc_cache, first_sb);
+		first_sb = next_sb;
+		i++;
+	}
+	kmem_cache_free(usb_desc_cache, last_sb);
+	i++;
+	dbg_sb("%d SB descriptors freed", i);
+	/* Compare i with urb->number_of_packets for Isoc traffic. 
+	   Should be same when calling unlink_urb */
+	
+	DBFEXIT;
+	
+	return i;
+}
+
+static int etrax_usb_submit_bulk_urb(urb_t *urb)
+{
+	int epid;
+        int empty;
+        unsigned long flags;
+        
+	DBFENTER;
+
+        /* Epid allocation, empty check and list add must be protected. 
+           Read about this in etrax_usb_submit_ctrl_urb. */
+
+        spin_lock_irqsave(&urb_list_lock, flags);
+	epid = etrax_usb_setup_epid(urb);
+	if (epid == -1) {
+		DBFEXIT;
+                spin_unlock_irqrestore(&urb_list_lock, flags);
+		return -ENOMEM;
+	}
+        empty = urb_list_empty(epid);
+        urb_list_add(urb, epid);
+        spin_unlock_irqrestore(&urb_list_lock, flags);
+
+        /* USB_QUEUE_BULK is UHCI-specific, but we warn anyway. */
+        if (!(urb->transfer_flags & USB_QUEUE_BULK) && !empty) {
+                warn("USB_QUEUE_BULK is not set and urb queue is not empty, ignoring.");
+        }
+
+        dbg_bulk("Adding bulk %s urb 0x%lx to %s list, epid %d", 
+                 usb_pipein(urb->pipe) ? "IN" : "OUT", (unsigned long)urb, empty ? "empty" : "", epid);
+
+        /* Mark the urb as being in progress. */
+	urb->status = -EINPROGRESS;
+
+        if (empty) {
+                etrax_usb_add_to_bulk_sb_list(urb, epid);
+        }
+	        
+	DBFEXIT;
+
+	return 0;
+}
+
+static void etrax_usb_add_to_bulk_sb_list(urb_t *urb, int epid)
+{
+	USB_SB_Desc_t *sb_desc;
+	etrax_urb_priv_t *urb_priv;
+	unsigned long flags;
+	char maxlen;
+
+	DBFENTER;
+
+	dbg_bulk("etrax_usb_add_to_bulk_sb_list, urb 0x%lx", (unsigned long)urb);
+
+	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	
+	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+	assert(urb_priv != NULL);
+        /* This sets rx_offset to 0. */
+        memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+        
+	sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+	assert(sb_desc != NULL);
+	memset(sb_desc, 0, sizeof(USB_SB_Desc_t));
+
+
+	if (usb_pipeout(urb->pipe)) {
+                
+		dbg_bulk("Grabbing bulk OUT, urb 0x%lx, epid %d", (unsigned long)urb, epid);
+                
+                /* This is probably a sanity check of the bulk transaction length
+                   not being larger than 64 kB. */
 		if (urb->transfer_buffer_length > 0xffff) {
-			panic(__FILE__ __FUNCTION__ ":urb->transfer_buffer_length > 0xffff\n");
+			panic("urb->transfer_buffer_length > 0xffff");
 		}
 
-		sb_desc_1->sw_len = urb->transfer_buffer_length;  /* was actual_length */
-		sb_desc_1->command = IO_FIELD(USB_SB_command, rem, 0) |
-			IO_STATE(USB_SB_command, tt, out) |
+		sb_desc->sw_len = urb->transfer_buffer_length;
 
-#if 0
-			IO_STATE(USB_SB_command, full, no) |
-#else
-			IO_STATE(USB_SB_command, full, yes) |
-#endif
+                /* The rem field is don't care if it's not a full-length transfer, so setting
+                   it shouldn't hurt. Also, rem isn't used for OUT traffic. */
+		sb_desc->command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                    IO_STATE(USB_SB_command, tt, out) |
+                                    IO_STATE(USB_SB_command, eot, yes) |
+                                    IO_STATE(USB_SB_command, eol, yes));
+
+                /* The full field is set to yes, even if we don't actually check that this is
+                   a full-length transfer (i.e., that transfer_buffer_length % maxlen = 0).
+                   Setting full prevents the USB controller from sending an empty packet in
+                   that case.  However, if USB_ZERO_PACKET was set we want that. */
+                if (!(urb->transfer_flags & USB_ZERO_PACKET)) {
+                        sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
+                }
 
-			IO_STATE(USB_SB_command, eot, yes) |
-			IO_STATE(USB_SB_command, eol, yes);
-		
-		dbg_bulk("transfer_buffer is at 0x%08X", urb->transfer_buffer);
-		
-		sb_desc_1->buf = virt_to_phys(urb->transfer_buffer);
-		sb_desc_1->next = 0;
+		sb_desc->buf = virt_to_phys(urb->transfer_buffer);
+		sb_desc->next = 0;
 		
 	} else if (usb_pipein(urb->pipe)) {
 
-		dbg_bulk("Transfer for epid %d is IN", epid);
-		dbg_bulk("transfer_buffer_length = %d", urb->transfer_buffer_length);
-		dbg_bulk("rem is calculated to %d", urb->transfer_buffer_length % maxlen);
-		
-		sb_desc_1->sw_len = urb->transfer_buffer_length ?
+                dbg_bulk("Grabbing bulk IN, urb 0x%lx, epid %d", (unsigned long)urb, epid);
+
+		sb_desc->sw_len = urb->transfer_buffer_length ?
 			(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
-		dbg_bulk("sw_len got %d", sb_desc_1->sw_len);
-		dbg_bulk("transfer_buffer is at 0x%08X", urb->transfer_buffer);
-		
-		sb_desc_1->command =
-			IO_FIELD(USB_SB_command, rem,
-				 urb->transfer_buffer_length % maxlen) |
-			IO_STATE(USB_SB_command, tt, in) |
-			IO_STATE(USB_SB_command, eot, yes) |
-			IO_STATE(USB_SB_command, eol, yes);
 		
-		sb_desc_1->buf = 0;
-		sb_desc_1->next = 0;
+                /* The rem field is don't care if it's not a full-length transfer, so setting
+                   it shouldn't hurt. */
+		sb_desc->command =
+			(IO_FIELD(USB_SB_command, rem,
+                                  urb->transfer_buffer_length % maxlen) |
+                         IO_STATE(USB_SB_command, tt, in) |
+                         IO_STATE(USB_SB_command, eot, yes) |
+                         IO_STATE(USB_SB_command, eol, yes));
+		
+		sb_desc->buf = 0;
+		sb_desc->next = 0;
+	}
+	
+	urb_priv->first_sb = sb_desc;
+	urb_priv->last_sb = sb_desc;
+	urb_priv->epid = epid;
 
-		urb_priv->rx_offset = 0;
-		urb_priv->eot = 0;
-	}
-	
-	urb_priv->first_sb = sb_desc_1;
-	
-	urb->hcpriv = (void *)urb_priv;
+	urb->hcpriv = urb_priv;
 	
-	/* Reset toggle bits and reset error count, remeber to di and ei */
-	/* Warning: it is possible that this locking doesn't work with bottom-halves */
-
+	/* Reset toggle bits and reset error count. */
 	save_flags(flags);
 	cli();
 
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop();
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
+        nop();
+
+        /* FIXME: Is this a special case since the hold field is checked,
+           or should we check hold in a lot of other cases as well? */
 	if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
-		panic("Hold was set in %s\n", __FUNCTION__);
+		panic("Hold was set in %s", __FUNCTION__);
 	}
 
+        /* Reset error counters (regardless of which direction this traffic is). */
 	*R_USB_EPT_DATA &=
 		~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
 		  IO_MASK(R_USB_EPT_DATA, error_count_out));
 	
+        /* Software must preset the toggle bits. */
 	if (usb_pipeout(urb->pipe)) {
 		char toggle =
-		usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+                        usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
 		*R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_out);
 		*R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_out, toggle);
 	} else {
 		char toggle =
-		usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
+                        usb_gettoggle(urb->dev, usb_pipeendpoint(urb->pipe), usb_pipeout(urb->pipe));
 		*R_USB_EPT_DATA &= ~IO_MASK(R_USB_EPT_DATA, t_in);
 		*R_USB_EPT_DATA |= IO_FIELD(R_USB_EPT_DATA, t_in, toggle);
 	}
-		
-	/* Enable the EP descr. */
-
-	set_bit(epid, (void *)&ep_really_active);
-	
-	TxBulkEPList[epid].sub = virt_to_phys(sb_desc_1);
+                
+        /* Assert that the EP descriptor is disabled. */
+        assert(!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+
+        /* The reason we set the EP's sub pointer directly instead of
+           walking the SB list and linking it last in the list is that we only
+           have one active urb at a time (the rest are queued). */
+
+	/* Note that we cannot have interrupts running when we have set the SB descriptor
+	   but the EP is not yet enabled.  If a bulk eot happens for another EP, we will
+	   find this EP disabled and with a SB != 0, which will make us think that it's done. */
+	TxBulkEPList[epid].sub = virt_to_phys(sb_desc);
 	TxBulkEPList[epid].hw_len = 0;
-	TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+        /* Note that we don't have to fill in the ep_id field since this
+           was done when we allocated the EP descriptors in init_tx_bulk_ep. */
+
+	/* Check if the dummy list is already with us (if several urbs were queued). */
+	if (TxBulkEPList[epid].next != virt_to_phys(&TxBulkDummyEPList[epid][0])) {
+
+		dbg_bulk("Inviting dummy list to the party for urb 0x%lx, epid %d", 
+			 (unsigned long)urb, epid);
+
+		/* The last EP in the dummy list already has its next pointer set to 
+		   TxBulkEPList[epid].next. */
 
+		/* We don't need to check if the DMA is at this EP or not before changing the
+		   next pointer, since we will do it in one 32-bit write (EP descriptors are
+		   32-bit aligned). */
+		TxBulkEPList[epid].next = virt_to_phys(&TxBulkDummyEPList[epid][0]);
+	}
+        /* Enable the EP descr. */
+	dbg_bulk("Enabling bulk EP for urb 0x%lx, epid %d", (unsigned long)urb, epid);
+        TxBulkEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+
+	/* Everything is set up, safe to enable interrupts again. */
 	restore_flags(flags);
 
+        /* If the DMA bulk channel isn't running, we need to restart it if it
+	   has stopped at the last EP descriptor (DMA stopped because there was
+	   no more traffic) or if it has stopped at a dummy EP with the intr flag
+	   set (DMA stopped because we were too slow in inserting new traffic). */
 	if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
-		*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
-		
-	}
 
+		USB_EP_Desc_t *ep;
+		ep = (USB_EP_Desc_t *)phys_to_virt(*R_DMA_CH8_SUB0_EP);
+		dbg_bulk("DMA channel not running in add");
+		dbg_bulk("DMA is at 0x%lx", (unsigned long)ep);
+
+		if (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[NBR_OF_EPIDS - 1]) ||
+		    (ep->command & 0x8) >> 3) {
+			*R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+			/* Update/restart the bulk start timer since we just started the channel. */
+			mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
+			/* Update/restart the bulk eot timer since we just inserted traffic. */
+			mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+		}
+	}
+        
 	DBFEXIT;
 }
 
-static void handle_bulk_transfer_attn(int epid, int status)
+static void etrax_usb_complete_bulk_urb(urb_t *urb, int status)
 {
-	struct urb *old_urb;
-	etrax_urb_priv_t *hc_priv;
-	unsigned long flags;
-
+	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+	int epid = urb_priv->epid;
+        unsigned long flags;
+        
 	DBFENTER;
 
-	clear_bit(epid, (void *)&ep_really_active);
-	
-	old_urb = URB_List[epid];
-	URB_List[epid] = old_urb->next;
+        if (status)
+                warn("Completing bulk urb with status %d.", status);
+
+	dbg_bulk("Completing bulk urb 0x%lx for epid %d", (unsigned long)urb, epid);
+
+	/* Update the urb list. */	
+        urb_list_del(urb, epid);
 
-	/* if (status == 0 && IN) find data and copy to urb */
-	if (status == 0 && usb_pipein(old_urb->pipe)) {
-		etrax_urb_priv_t *urb_priv;
-		
-		urb_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
-		save_flags(flags);
-		cli();
-		if (urb_priv->eot == 1) {
-			old_urb->actual_length = urb_priv->rx_offset;
+        /* For an IN pipe, we always set the actual length, regardless of whether there was
+           an error or not (which means the device driver can use the data if it wants to). */
+	if (usb_pipein(urb->pipe)) {
+                urb->actual_length = urb_priv->rx_offset;
+	} else {
+		/* Set actual_length for OUT urbs also; the USB mass storage driver seems
+		   to want that. We wouldn't know of any partial writes if there was an error. */
+		if (status == 0) {
+			urb->actual_length = urb->transfer_buffer_length;
 		} else {
-			if (urb_priv->rx_offset == 0) {
-				status = 0;
-			} else {
-				status = -EPROTO;
-			}
-			
-			old_urb->actual_length = 0;
-			err("(BULK) No eot set in IN data!!! rx_offset is: %d", urb_priv->rx_offset);
+			urb->actual_length = 0;
 		}
-		
-		restore_flags(flags);
 	}
 
+        /* FIXME: Is there something of the things below we shouldn't do if there was an error?
+           Like, maybe we shouldn't toggle the toggle bits, or maybe we shouldn't insert more traffic. */
+
 	save_flags(flags);
 	cli();
 		
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop();
-	if (usb_pipeout(old_urb->pipe)) {
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
+        nop();
+
+        /* We need to fiddle with the toggle bits because the hardware doesn't do it for us. */
+	if (usb_pipeout(urb->pipe)) {
 		char toggle =
-		IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA);
-		usb_settoggle(old_urb->dev, usb_pipeendpoint(old_urb->pipe),
-			      usb_pipeout(old_urb->pipe), toggle);
+                        IO_EXTRACT(R_USB_EPT_DATA, t_out, *R_USB_EPT_DATA);
+		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			      usb_pipeout(urb->pipe), toggle);
 	} else {
 		char toggle =
-		IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA);
-		usb_settoggle(old_urb->dev, usb_pipeendpoint(old_urb->pipe),
-			      usb_pipeout(old_urb->pipe), toggle);
+                        IO_EXTRACT(R_USB_EPT_DATA, t_in, *R_USB_EPT_DATA);
+		usb_settoggle(urb->dev, usb_pipeendpoint(urb->pipe),
+			      usb_pipeout(urb->pipe), toggle);
+	}
+        restore_flags(flags);
+
+        /* Remember to free the SBs. */
+	etrax_remove_from_sb_list(urb);
+	kfree(urb_priv);
+        urb->hcpriv = 0;
+        
+	/* If there are any more urb's in the list we'd better start sending */
+	if (!urb_list_empty(epid)) {
+
+                urb_t *new_urb;
+
+                /* Get the first urb. */
+                new_urb = urb_list_first(epid);
+		assert(new_urb);
+
+                dbg_bulk("More bulk for epid %d", epid);
+
+		etrax_usb_add_to_bulk_sb_list(new_urb, epid);
+	}
+
+        urb->status = status;
+
+        /* We let any non-zero status from the layer above have precedence. */
+        if (status == 0) {
+                /* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length)
+                   is to be treated as an error. */
+                if (urb->transfer_flags & USB_DISABLE_SPD) {
+                        if (usb_pipein(urb->pipe) && 
+                            (urb->actual_length != 
+                             usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
+                                urb->status = -EREMOTEIO;
+                        } 
+                }
+        }
+        
+	if (urb->complete) {
+		urb->complete(urb);
 	}
 
-	restore_flags(flags);
-
-	/* If there are any more URB's in the list we'd better start sending */
-	if (URB_List[epid]) {
-		etrax_usb_do_bulk_hw_add(URB_List[epid], epid,
-					 usb_maxpacket(URB_List[epid]->dev, URB_List[epid]->pipe,
-						       usb_pipeout(URB_List[epid]->pipe)));
-	}
-#if 1
-	else {
-		/* This means that this EP is now free, deconfigure it */
+	if (urb_list_empty(epid)) {
+		/* This means that this EP is now free, deconfigure it. */
 		etrax_usb_free_epid(epid);
-	}
-#endif
-	
-	/* Remember to free the SB's */
-	hc_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
-	cleanup_sb(hc_priv->first_sb);
-	kfree(hc_priv);
+		
+		/* No more traffic; time to clean up.
+		   Must set sub pointer to 0, since we look at the sub pointer when handling
+		   the bulk eot interrupt. */
+
+                dbg_bulk("No bulk for epid %d", epid);
+                
+		TxBulkEPList[epid].sub = 0;
+
+		/* Unlink the dummy list. */
 
-	old_urb->status = status;
-	if (old_urb->complete) {
-		old_urb->complete(old_urb);
+		dbg_bulk("Kicking dummy list out of party for urb 0x%lx, epid %d", 
+			 (unsigned long)urb, epid);
+
+		/* No need to wait for the DMA before changing the next pointer.
+		   The modulo NBR_OF_EPIDS isn't actually necessary, since we will never use
+		   the last one (INVALID_EPID) for actual traffic. */
+		TxBulkEPList[epid].next = 
+			virt_to_phys(&TxBulkEPList[(epid + 1) % NBR_OF_EPIDS]);
 	}
 
 	DBFEXIT;
 }
 
-/* ---------------------------------------------------------------------------- */
-
-static int etrax_usb_submit_ctrl_urb(struct urb *urb)
+static int etrax_usb_submit_ctrl_urb(urb_t *urb)
 {
 	int epid;
-	char devnum;
-	char endpoint;
-	char maxlen;
-	char out_traffic;
-	char slow;
-
-	struct urb *tmp_urb;
-	
-	unsigned long flags;
-	
+        int empty;
+        unsigned long flags;
+        
 	DBFENTER;
 
-	devnum = usb_pipedevice(urb->pipe);
-	endpoint = usb_pipeendpoint(urb->pipe);
-	maxlen = usb_maxpacket(urb->dev, urb->pipe,
-			       usb_pipeout(urb->pipe));
-	out_traffic = usb_pipeout(urb->pipe);
-	slow = usb_pipeslow(urb->pipe);
-	
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
+        /* FIXME: Return -ENXIO if there is already a queued urb for this endpoint? */
+
+        /* Epid allocation, empty check and list add must be protected.
+
+           Epid allocation because if we find an existing epid for this endpoint an urb might be
+           completed (emptying the list) before we add the new urb to the list, causing the epid
+           to be de-allocated. We would then start the transfer with an invalid epid -> epid attn.
+
+           Empty check and add because otherwise we might conclude that the list is not empty,
+           after which it becomes empty before we add the new urb to the list, causing us not to 
+           insert the new traffic into the SB list. */
+
+        spin_lock_irqsave(&urb_list_lock, flags);
+	epid = etrax_usb_setup_epid(urb);
 	if (epid == -1) {
-		epid = etrax_usb_allocate_epid();
-		if (epid == -1) {
-			/* We're out of endpoints, return some error */
-			err("We're out of endpoints");
-			return -ENOMEM;
-		}
-		/* Now we have to fill in this ep */
-		etrax_usb_setup_epid(epid, devnum, endpoint, maxlen, slow, out_traffic);
+                spin_unlock_irqrestore(&urb_list_lock, flags);
+		DBFEXIT;
+		return -ENOMEM;
 	}
-	/* Ok, now we got valid endpoint, lets insert some traffic */
+        empty = urb_list_empty(epid);
+        urb_list_add(urb, epid);
+        spin_unlock_irqrestore(&urb_list_lock, flags);
 
+        dbg_ctrl("Adding ctrl urb 0x%lx to %s list, epid %d", 
+                 (unsigned long)urb, empty ? "empty" : "", epid);
+
+        /* Mark the urb as being in progress. */
 	urb->status = -EINPROGRESS;
 
-	save_flags(flags);
-	cli();
-	
-	if (URB_List[epid]) {
-		/* Find end of list and add */
-		for (tmp_urb = URB_List[epid]; tmp_urb->next; tmp_urb = tmp_urb->next)
-			dump_urb(tmp_urb);
-
-		tmp_urb->next = urb;
-		restore_flags(flags);
-	} else {
-		/* If this is the first URB, add the URB and do HW add */
-		URB_List[epid] = urb;
-		restore_flags(flags);
-		etrax_usb_do_ctrl_hw_add(urb, epid, maxlen);
-	}
+        if (empty) {
+                etrax_usb_add_to_ctrl_sb_list(urb, epid);
+        }
 
 	DBFEXIT;
 
 	return 0;
 }
 
-static void etrax_usb_do_ctrl_hw_add(struct urb *urb, int epid, char maxlen)
+static void etrax_usb_add_to_ctrl_sb_list(urb_t *urb, int epid)
 {
-	USB_SB_Desc_t *sb_desc_1;
-	USB_SB_Desc_t *sb_desc_2;
-	USB_SB_Desc_t *sb_desc_3;
+	USB_SB_Desc_t *sb_desc_setup;
+	USB_SB_Desc_t *sb_desc_data;
+	USB_SB_Desc_t *sb_desc_status;
 
 	etrax_urb_priv_t *urb_priv;
 
 	unsigned long flags;
-
+	char maxlen;
+	
 	DBFENTER;
 
-	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), GFP_KERNEL);
-	sb_desc_1 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
-	sb_desc_2 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
-
-	if (!(sb_desc_1 && sb_desc_2)) {
-		panic("kmem_cache_alloc in ctrl_hw_add gave NULL pointers !!!\n");
-	}
-	
-	sb_desc_1->sw_len = 8;
-	sb_desc_1->command = IO_FIELD(USB_SB_command, rem, 0) |
-		IO_STATE(USB_SB_command, tt, setup) |
-		IO_STATE(USB_SB_command, full, yes) |
-		IO_STATE(USB_SB_command, eot, yes);
-	
-	sb_desc_1->buf = virt_to_phys(urb->setup_packet);
-	sb_desc_1->next = virt_to_phys(sb_desc_2);
-	dump_sb_desc(sb_desc_1);
+	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	
+	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+	assert(urb_priv != NULL);
+        /* This sets rx_offset to 0. */
+        memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+        
+	sb_desc_setup = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+	assert(sb_desc_setup != NULL);
+	sb_desc_status = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+	assert(sb_desc_status != NULL);
+
+	/* Initialize the mandatory setup SB descriptor (used only in control transfers) */	
+	sb_desc_setup->sw_len = 8;
+	sb_desc_setup->command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                  IO_STATE(USB_SB_command, tt, setup) |
+                                  IO_STATE(USB_SB_command, full, yes) |
+                                  IO_STATE(USB_SB_command, eot, yes));
+	
+	sb_desc_setup->buf = virt_to_phys(urb->setup_packet);
 
 	if (usb_pipeout(urb->pipe)) {
-		dbg_ctrl("Transfer for epid %d is OUT", epid);
+                dbg_ctrl("Transfer for epid %d is OUT", epid);
 
 		/* If this Control OUT transfer has an optional data stage we add an OUT token
 		   before the mandatory IN (status) token, hence the reordered SB list */
 		
+		sb_desc_setup->next = virt_to_phys(sb_desc_status);
 		if (urb->transfer_buffer) {
-			dbg_ctrl("This OUT transfer has an extra data stage");
-			sb_desc_3 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
 
-			sb_desc_1->next = virt_to_phys(sb_desc_3);
+			dbg_ctrl("This OUT transfer has an extra data stage");
 			
-			sb_desc_3->sw_len = urb->transfer_buffer_length;
-			sb_desc_3->command = IO_STATE(USB_SB_command, tt, out) |
-				IO_STATE(USB_SB_command, full, yes) |
-				IO_STATE(USB_SB_command, eot, yes);
-			sb_desc_3->buf = virt_to_phys(urb->transfer_buffer);
-			sb_desc_3->next = virt_to_phys(sb_desc_2);
-		}
-		
-		sb_desc_2->sw_len = 1;
-		sb_desc_2->command = IO_FIELD(USB_SB_command, rem, 0) |
-			IO_STATE(USB_SB_command, tt, in) |
-			IO_STATE(USB_SB_command, eot, yes) |
-			IO_STATE(USB_SB_command, eol, yes);
-
-		sb_desc_2->buf = 0;
-		sb_desc_2->next = 0;
-		dump_sb_desc(sb_desc_2);
-		
+                        sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+                        assert(sb_desc_data != NULL);
+                        
+                        sb_desc_setup->next = virt_to_phys(sb_desc_data);
+                        
+                        sb_desc_data->sw_len = urb->transfer_buffer_length;
+                        sb_desc_data->command = (IO_STATE(USB_SB_command, tt, out) |
+                                                 IO_STATE(USB_SB_command, full, yes) |
+                                                 IO_STATE(USB_SB_command, eot, yes));
+                        sb_desc_data->buf = virt_to_phys(urb->transfer_buffer);
+                        sb_desc_data->next = virt_to_phys(sb_desc_status);
+		}
+		
+		sb_desc_status->sw_len = 1;
+		sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                           IO_STATE(USB_SB_command, tt, in) |
+                                           IO_STATE(USB_SB_command, eot, yes) |
+                                           IO_STATE(USB_SB_command, eol, yes));
+
+		sb_desc_status->buf = 0;
+		sb_desc_status->next = 0;
+                
 	} else if (usb_pipein(urb->pipe)) {
 
 		dbg_ctrl("Transfer for epid %d is IN", epid);
 		dbg_ctrl("transfer_buffer_length = %d", urb->transfer_buffer_length);
 		dbg_ctrl("rem is calculated to %d", urb->transfer_buffer_length % maxlen);
 
-		sb_desc_3 = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, GFP_KERNEL);
+		sb_desc_data = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+		assert(sb_desc_data != NULL);
 		
-		sb_desc_2->sw_len = urb->transfer_buffer_length ?
+		sb_desc_setup->next = virt_to_phys(sb_desc_data);
+
+		sb_desc_data->sw_len = urb->transfer_buffer_length ?
 			(urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
-		dbg_ctrl("sw_len got %d", sb_desc_2->sw_len);
+		dbg_ctrl("sw_len got %d", sb_desc_data->sw_len);
 		
-		sb_desc_2->command =
-			IO_FIELD(USB_SB_command, rem,
-				 urb->transfer_buffer_length % maxlen) |
-			IO_STATE(USB_SB_command, tt, in) |
-			IO_STATE(USB_SB_command, eot, yes);
-		
-		sb_desc_2->buf = 0;
-		sb_desc_2->next = virt_to_phys(sb_desc_3);
-		dump_sb_desc(sb_desc_2);
-
-		sb_desc_3->sw_len = 1;
-		sb_desc_3->command = IO_FIELD(USB_SB_command, rem, 0) |
-			IO_STATE(USB_SB_command, tt, zout) |
-			IO_STATE(USB_SB_command, full, yes) |
-			IO_STATE(USB_SB_command, eot, yes) |
-			IO_STATE(USB_SB_command, eol, yes);
+		sb_desc_data->command =
+			(IO_FIELD(USB_SB_command, rem,
+                                  urb->transfer_buffer_length % maxlen) |
+                         IO_STATE(USB_SB_command, tt, in) |
+                         IO_STATE(USB_SB_command, eot, yes));
+		
+		sb_desc_data->buf = 0;
+		sb_desc_data->next = virt_to_phys(sb_desc_status);
+
+                /* Read comment at zout_buffer declaration for an explanation to this. */
+		sb_desc_status->sw_len = 1;
+		sb_desc_status->command = (IO_FIELD(USB_SB_command, rem, 0) |
+                                           IO_STATE(USB_SB_command, tt, zout) |
+                                           IO_STATE(USB_SB_command, full, yes) |
+                                           IO_STATE(USB_SB_command, eot, yes) |
+                                           IO_STATE(USB_SB_command, eol, yes));
 				
-		sb_desc_3->buf = 0;
-		sb_desc_3->next = 0;
-		dump_sb_desc(sb_desc_3);
-
-		urb_priv->rx_offset = 0;
-		urb_priv->eot = 0;
+		sb_desc_status->buf = virt_to_phys(&zout_buffer[0]);
+		sb_desc_status->next = 0;
 	}
 	
-	urb_priv->first_sb = sb_desc_1;
-	
-	urb->hcpriv = (void *)urb_priv;
+	urb_priv->first_sb = sb_desc_setup;
+	urb_priv->last_sb = sb_desc_status;
+	urb_priv->epid = epid;
+
+	urb->hcpriv = urb_priv;
 	
 	/* Reset toggle bits and reset error count, remeber to di and ei */
 	/* Warning: it is possible that this locking doesn't work with bottom-halves */
@@ -1332,661 +2181,1059 @@
 	save_flags(flags);
 	cli();
 
-	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop();
+	*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
+        nop();
 	if (*R_USB_EPT_DATA & IO_MASK(R_USB_EPT_DATA, hold)) {
-		panic("Hold was set in %s\n", __FUNCTION__);
+		panic("Hold was set in %s", __FUNCTION__);
 	}
 	
 
+        /* FIXME: Compare with etrax_usb_add_to_bulk_sb_list where the toggle bits
+           are set to a specific value. Why the difference? Read "Transfer and Toggle Bits
+           in Designer's Reference, p. 8 - 11. */
 	*R_USB_EPT_DATA &=
 		~(IO_MASK(R_USB_EPT_DATA, error_count_in) |
 		  IO_MASK(R_USB_EPT_DATA, error_count_out) |
 		  IO_MASK(R_USB_EPT_DATA, t_in) |
 		  IO_MASK(R_USB_EPT_DATA, t_out));
 
-	/* Enable the EP descr. */
+	/* Since we use the rx interrupt to complete ctrl urbs, we can enable interrupts now
+	   (i.e. we don't check the sub pointer on an eot interrupt like we do for bulk traffic). */
+	restore_flags(flags);
 
-	set_bit(epid, (void *)&ep_really_active);
-	
-	TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_1);
+        /* Assert that the EP descriptor is disabled. */
+        assert(!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)));
+
+	/* Set up and enable the EP descriptor. */
+	TxCtrlEPList[epid].sub = virt_to_phys(sb_desc_setup);
 	TxCtrlEPList[epid].hw_len = 0;
 	TxCtrlEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
 
-	restore_flags(flags);
-
-	dump_ep_desc(&TxCtrlEPList[epid]);
-
-	if (!(*R_DMA_CH8_SUB1_CMD & IO_MASK(R_DMA_CH8_SUB1_CMD, cmd))) {
-		*R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
-		
-	}
+        /* We start the DMA sub channel without checking if it's running or not, because:
+           1) If it's already running, issuing the start command is a nop.
+           2) We avoid a test-and-set race condition. */
+        *R_DMA_CH8_SUB1_CMD = IO_STATE(R_DMA_CH8_SUB1_CMD, cmd, start);
 	
 	DBFEXIT;
 }
 
-static int etrax_usb_submit_urb(struct urb *urb)
+static void etrax_usb_complete_ctrl_urb(urb_t *urb, int status)
 {
-	etrax_hc_t *hc;
-	int rval = -EINVAL;
-	
-	DBFENTER;
+        etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+        int epid = urb_priv->epid;
 
-	urb->next = NULL;
+	DBFENTER;
 
-	dump_urb(urb);
-	submit_urb_count++;
-	
-	hc = (etrax_hc_t*) urb->dev->bus->hcpriv;
-	
-	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
-		/* This request if for the Virtual Root Hub */
-		rval = etrax_rh_submit_urb(urb);
-		
-	} else if (usb_pipetype(urb->pipe) == PIPE_CONTROL) {
-		rval = etrax_usb_submit_ctrl_urb(urb);
+        if (status)
+                warn("Completing ctrl urb with status %d.", status);
 
-	} else if (usb_pipetype(urb->pipe) == PIPE_BULK) {
-		rval = etrax_usb_submit_bulk_urb(urb);
+        dbg_ctrl("Completing ctrl epid %d, urb 0x%lx", epid, (unsigned long)urb);
 
-	} else if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
-		int bustime;
+	/* Remove this urb from the list. */
+        urb_list_del(urb, epid);
 
-		if (urb->bandwidth == 0) {
-			bustime = usb_check_bandwidth(urb->dev, urb);
-			if (bustime < 0) {
-				rval = bustime;
-			} else {
-				usb_claim_bandwidth(urb->dev, urb, bustime, 0);
-				rval = etrax_usb_submit_intr_urb(urb);
-			}
-			
-		}
-	} else if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
-		warn("Isochronous traffic is not supported !!!");
-		rval = -EINVAL;
+        /* For an IN pipe, we always set the actual length, regardless of whether there was
+           an error or not (which means the device driver can use the data if it wants to). */
+	if (usb_pipein(urb->pipe)) {
+                urb->actual_length = urb_priv->rx_offset;
 	}
 
-	DBFEXIT;
+        /* FIXME: Is there something of the things below we shouldn't do if there was an error?
+           Like, maybe we shouldn't insert more traffic. */
 
-	return rval;
-}
+	/* Remember to free the SBs. */
+	etrax_remove_from_sb_list(urb);
+	kfree(urb_priv);
+        urb->hcpriv = 0;
+        
+	/* If there are any more urbs in the list we'd better start sending. */
+	if (!urb_list_empty(epid)) {
+                urb_t *new_urb;
+                
+                /* Get the first urb. */
+                new_urb = urb_list_first(epid);
+		assert(new_urb);
 
-static int etrax_usb_unlink_urb(struct urb *urb)
-{
-	etrax_hc_t *hc = urb->dev->bus->hcpriv;
-	int epid;
-	int devnum, endpoint, slow, maxlen, out_traffic;
-	etrax_urb_priv_t *hc_priv;
-	unsigned long flags;
-	
-	DBFENTER;
-	dump_urb(urb);
-	devnum = usb_pipedevice(urb->pipe);
-	endpoint = usb_pipeendpoint(urb->pipe);
-	slow = usb_pipeslow(urb->pipe);
-	maxlen = usb_maxpacket(urb->dev, urb->pipe,
-			       usb_pipeout(urb->pipe));
-	out_traffic = usb_pipeout(urb->pipe);
-
-	epid = etrax_usb_lookup_epid(devnum, endpoint, slow, maxlen, out_traffic);
+                dbg_ctrl("More ctrl for epid %d, first urb = 0x%lx", epid, (unsigned long)new_urb);
 
-	if (epid == -1)
-		return 0;
-	
-	if (usb_pipedevice(urb->pipe) == hc->rh.devnum) {
-		int ret;
-		ret =  etrax_rh_unlink_urb(urb);
-		DBFEXIT;
-		return ret;
-	} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
-		int ret;
-		ret = etrax_usb_unlink_intr_urb(urb);
-		urb->status = -ENOENT;
-		if (urb->complete) {
-			urb->complete(urb);
-		}
-		DBFEXIT;
-		return ret;
+		etrax_usb_add_to_ctrl_sb_list(new_urb, epid);
 	}
-
-	info("Unlink of BULK or CTRL");
-
-	save_flags(flags);
-	cli();
 	
-	for (epid = 0; epid < 32; epid++) {
-		struct urb *u = URB_List[epid];
-		struct urb *prev = NULL;
-		int pos = 0;
-
-		for (; u; u = u->next) {
-			pos++;
-			if (u == urb) {
-				if (!prev) {
-					URB_List[epid] = u->next;
-				} else {
-					prev->next = u->next;
-				}
+	urb->status = status;
 
-				restore_flags(flags);
-
-				if (!prev) {
-					if (usb_pipetype(u->pipe) == PIPE_CONTROL) {
-						if (TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
-							/* The EP was enabled, disable it and wait */
-							TxCtrlEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-							while (*R_DMA_CH8_SUB1_EP == virt_to_phys(&TxCtrlEPList[epid]));
-						}
-					} else if (usb_pipetype(u->pipe) == PIPE_BULK) {
-						if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
-							TxBulkEPList[epid].command &= ~IO_MASK(USB_EP_command, enable);
-							while (*R_DMA_CH8_SUB0_EP == virt_to_phys(&TxBulkEPList[epid]));
-						}
-					}
-				}
-
-				info("Found urb at epid %d, pos %d", epid, pos);
-
-				u->status = -ENOENT;
-				if (u->complete) {
-					u->complete(u);
-				}
-				
-				hc_priv = (etrax_urb_priv_t *)u->hcpriv;
-				cleanup_sb(hc_priv->first_sb);
-				kfree(hc_priv);
+        /* We let any non-zero status from the layer above have precedence. */
+        if (status == 0) {
+                /* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length)
+                   is to be treated as an error. */
+                if (urb->transfer_flags & USB_DISABLE_SPD) {
+                        if (usb_pipein(urb->pipe) && 
+                            (urb->actual_length != 
+                             usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe)))) {
+                                urb->status = -EREMOTEIO;
+                        } 
+                }
+        }
+        
+	if (urb->complete) {
+		urb->complete(urb);
+	}
 
-				DBFEXIT;
-				return 0;
-			}
-			prev = u;
-		}
+	if (urb_list_empty(epid)) {
+		/* No more traffic. Time to clean up. */
+		etrax_usb_free_epid(epid);
+		/* Must set sub pointer to 0. */
+                dbg_ctrl("No ctrl for epid %d", epid);
+		TxCtrlEPList[epid].sub = 0;
 	}
 
-	restore_flags(flags);
-		
 	DBFEXIT;
-	return 0;
 }
 
-static int etrax_usb_get_frame_number(struct usb_device *usb_dev)
+static int etrax_usb_submit_intr_urb(urb_t *urb)
 {
+	
+	int epid;
+	
 	DBFENTER;
-	DBFEXIT;
-	return (*R_USB_FM_NUMBER);
-}
+        
+        if (usb_pipeout(urb->pipe)) {
+                /* Unsupported transfer type.
+                   We don't support interrupt out traffic. (If we do, we can't support
+                   intervals for neither in or out traffic, but are forced to schedule all
+                   interrupt traffic in one frame.) */
+                return -EINVAL;
+        }
 
-static int etrax_usb_allocate_dev(struct usb_device *usb_dev)
-{  
-	DBFENTER;
-	DBFEXIT;
-	return 0;
-}
+	epid = etrax_usb_setup_epid(urb);
+	if (epid == -1) {
+		DBFEXIT;
+		return -ENOMEM;
+	}
 
-static int etrax_usb_deallocate_dev(struct usb_device *usb_dev)
-{
-	DBFENTER;
-	DBFEXIT;
-	return 0;
-}
+	if (!urb_list_empty(epid)) {
+                /* There is already a queued urb for this endpoint. */
+                etrax_usb_free_epid(epid);
+                return -ENXIO;
+        }
+        
+        urb->status = -EINPROGRESS;
 
-static void etrax_usb_tx_interrupt(int irq, void *vhc, struct pt_regs *regs)
-{
-	DBFENTER;
+        dbg_intr("Add intr urb 0x%lx, to list, epid %d", (unsigned long)urb, epid);
 
-	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub0_descr)) {
-		info("dma8_sub0_descr (BULK) intr.");
-		*R_DMA_CH8_SUB0_CLR_INTR = IO_STATE(R_DMA_CH8_SUB0_CLR_INTR, clr_descr, do);
-	}
-	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub1_descr)) {
-		info("dma8_sub1_descr (CTRL) intr.");
-		*R_DMA_CH8_SUB1_CLR_INTR = IO_STATE(R_DMA_CH8_SUB1_CLR_INTR, clr_descr, do);
-	}
-	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub2_descr)) {
-		info("dma8_sub2_descr (INT) intr.");
-		*R_DMA_CH8_SUB2_CLR_INTR = IO_STATE(R_DMA_CH8_SUB2_CLR_INTR, clr_descr, do);
-	}
-	if (*R_IRQ_READ2 & IO_MASK(R_IRQ_READ2, dma8_sub3_descr)) {
-		info("dma8_sub3_descr (ISO) intr.");
-		*R_DMA_CH8_SUB3_CLR_INTR = IO_STATE(R_DMA_CH8_SUB3_CLR_INTR, clr_descr, do);
-	}
+        urb_list_add(urb, epid);
+	etrax_usb_add_to_intr_sb_list(urb, epid);
+
+	return 0;
 	
 	DBFEXIT;
 }
 
-static void etrax_usb_rx_interrupt(int irq, void *vhc, struct pt_regs *regs)
+static void etrax_usb_add_to_intr_sb_list(urb_t *urb, int epid)
 {
-	int epid = 0;
-	struct urb *urb;
+
+	volatile USB_EP_Desc_t *tmp_ep;
+	volatile USB_EP_Desc_t *first_ep;
+	
+	char maxlen;
+	int interval;
+	int i;
+        
 	etrax_urb_priv_t *urb_priv;
-		
-	*R_DMA_CH9_CLR_INTR = IO_STATE(R_DMA_CH9_CLR_INTR, clr_eop, do);
+	
+	DBFENTER;
 
-	while (myNextRxDesc->status & IO_MASK(USB_IN_status, eop)) {
-		if (myNextRxDesc->status & IO_MASK(USB_IN_status, nodata)) {
+	maxlen = usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe));
+	interval = urb->interval;
 
-			goto skip_out;
-		}
+	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+	assert(urb_priv != NULL);
+        memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
+	urb->hcpriv = urb_priv;
 
-		if (myNextRxDesc->status & IO_MASK(USB_IN_status, error)) {
-			
-			goto skip_out;
-		}
-		
-		epid = IO_EXTRACT(USB_IN_status, epid, myNextRxDesc->status);
+	first_ep = &TxIntrEPList[0];
 
-		urb = URB_List[epid];
+	/* Round of the interval to 2^n, it is obvious that this code favours
+	   smaller numbers, but that is actually a good thing */
+        /* FIXME: The "rounding error" for larger intervals will be quite
+           large. For in traffic this shouldn't be a problem since it will only
+           mean that we "poll" more often. */
+	for (i = 0; interval; i++) {
+		interval = interval >> 1;
+	}
+	interval = 1 << (i - 1);
 
-		if (urb && usb_pipein(urb->pipe)) {
-			urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+	dbg_intr("Interval rounded to %d", interval);
 
-			if (usb_pipetype(urb->pipe) == PIPE_INTERRUPT) {
-				struct in_chunk *in;
-				dbg_intr("Packet for epid %d in rx buffers", epid);
-				in = kmalloc(sizeof(struct in_chunk), GFP_ATOMIC);
-				in->length = myNextRxDesc->hw_len;
-				in->data = kmalloc(in->length, GFP_ATOMIC);
-				memcpy(in->data, phys_to_virt(myNextRxDesc->buf), in->length);
-				list_add_tail(&in->list, &urb_priv->ep_in_list);
-#ifndef ETRAX_USB_INTR_IRQ
-				etrax_usb_hc_intr_top_half(irq, vhc, regs);
-#endif
-				
-			} else {
-				if ((urb_priv->rx_offset + myNextRxDesc->hw_len) >
-				    urb->transfer_buffer_length) {
-					err("Packet (epid: %d) in RX buffer (%d) was bigger "
-					    "than the URB has room for (%d)!!!", epid, urb_priv->rx_offset + myNextRxDesc->hw_len, urb->transfer_buffer_length);
-					goto skip_out;
-				}
+	tmp_ep = first_ep;
+	i = 0;
+	do {
+		if (tmp_ep->command & IO_MASK(USB_EP_command, eof)) {
+			if ((i % interval) == 0) {
+				/* Insert the traffic ep after tmp_ep */
+				USB_EP_Desc_t *ep_desc;
+				USB_SB_Desc_t *sb_desc;
+
+				dbg_intr("Inserting EP for epid %d", epid);
+                                
+				ep_desc = (USB_EP_Desc_t *)
+					kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+				sb_desc = (USB_SB_Desc_t *)
+					kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+				assert(ep_desc != NULL);
+                                CHECK_ALIGN(ep_desc);
+				assert(sb_desc != NULL);
 				
-				memcpy(urb->transfer_buffer + urb_priv->rx_offset,
-				       phys_to_virt(myNextRxDesc->buf), myNextRxDesc->hw_len);
+				ep_desc->sub = virt_to_phys(sb_desc);
+				ep_desc->hw_len = 0;
+				ep_desc->command = (IO_FIELD(USB_EP_command, epid, epid) |
+                                                    IO_STATE(USB_EP_command, enable, yes));
+
+
+                                /* Round upwards the number of packets of size maxlen
+                                   that this SB descriptor should receive. */
+                                sb_desc->sw_len = urb->transfer_buffer_length ?
+                                        (urb->transfer_buffer_length - 1) / maxlen + 1 : 0;
+                                sb_desc->next = 0;
+                                sb_desc->buf = 0;
+                                sb_desc->command = 
+                                        (IO_FIELD(USB_SB_command, rem, urb->transfer_buffer_length % maxlen) |
+                                         IO_STATE(USB_SB_command, tt, in) |
+                                         IO_STATE(USB_SB_command, eot, yes) |
+                                         IO_STATE(USB_SB_command, eol, yes));
 				
-				urb_priv->rx_offset += myNextRxDesc->hw_len;
-			}
-			
-			if (myNextRxDesc->status & IO_MASK(USB_IN_status, eot)) {
-				urb_priv->eot = 1;
+				ep_desc->next = tmp_ep->next;
+				tmp_ep->next = virt_to_phys(ep_desc);
 			}
-			
-		} else {
-			err("This is almost fatal, inpacket for epid %d which does not exist "
-			    " or is out!!!\nURB was at 0x%08lX", epid, (unsigned long)urb);
-			
-			goto skip_out;
+			i++;
 		}
+		tmp_ep = (USB_EP_Desc_t *)phys_to_virt(tmp_ep->next);
+	} while (tmp_ep != first_ep);
 
-	skip_out:
-		myPrevRxDesc = myNextRxDesc;
-		myPrevRxDesc->command |= IO_MASK(USB_IN_command, eol);
-		myLastRxDesc->command &= ~IO_MASK(USB_IN_command, eol);
-		myLastRxDesc = myPrevRxDesc;
 
-		myNextRxDesc->status = 0;
-		myNextRxDesc = phys_to_virt(myNextRxDesc->next);
-	}
+        /* Note that first_sb/last_sb doesn't apply to interrupt traffic. */
+        urb_priv->epid = epid;
+
+        /* We start the DMA sub channel without checking if it's running or not, because:
+           1) If it's already running, issuing the start command is a nop.
+           2) We avoid a test-and-set race condition. */
+        *R_DMA_CH8_SUB2_CMD = IO_STATE(R_DMA_CH8_SUB2_CMD, cmd, start);
+        
+	DBFEXIT;
 }
 
 
 
-static void cleanup_sb(USB_SB_Desc_t *sb)
+static void etrax_usb_complete_intr_urb(urb_t *urb, int status)
 {
-	USB_SB_Desc_t *next_sb;
-	
+        etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+        int epid = urb_priv->epid;
+        
 	DBFENTER;
 
-	if (sb == NULL) {
-		err("cleanup_sb was given a NULL pointer");
-		return;
-	}
+        if (status)
+                warn("Completing intr urb with status %d.", status);
 
-	while (!(sb->command & IO_MASK(USB_SB_command, eol))) {
-		next_sb = (USB_SB_Desc_t *)phys_to_virt(sb->next);
-		kmem_cache_free(usb_desc_cache, sb);
-		sb = next_sb;
-	}
+        dbg_intr("Completing intr epid %d, urb 0x%lx", epid, (unsigned long)urb);
 
-	kmem_cache_free(usb_desc_cache, sb);
+        urb->status = status;
+        urb->actual_length = urb_priv->rx_offset;
 
-	DBFEXIT;
+        dbg_intr("interrupt urb->actual_length = %d", urb->actual_length);
 
+        /* We let any non-zero status from the layer above have precedence. */
+        if (status == 0) {
+                /* USB_DISABLE_SPD means that short reads (shorter than the endpoint's max length)
+                   is to be treated as an error. */
+                if (urb->transfer_flags & USB_DISABLE_SPD) {
+                        if (urb->actual_length != 
+                            usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
+                                urb->status = -EREMOTEIO;
+                        }
+                }
+        }
+        
+        if (urb->complete) {
+                urb->complete(urb);
+        }
+			
+        /* Device driver has taken care of the data now. */
+        urb_priv->rx_offset = 0;
+        
+	DBFEXIT;
 }
 
-static void handle_control_transfer_attn(int epid, int status)
+
+static int etrax_usb_submit_isoc_urb(urb_t *urb) 
 {
-	struct urb *old_urb;
-	etrax_urb_priv_t *hc_priv;	
+	int epid;
 
 	DBFENTER;
 
-	clear_bit(epid, (void *)&ep_really_active);
-	
-	old_urb = URB_List[epid];
-	URB_List[epid] = old_urb->next;
-	
-	/* if (status == 0 && IN) find data and copy to urb */
-	if (status == 0 && usb_pipein(old_urb->pipe)) {
-		unsigned long flags;
-		etrax_urb_priv_t *urb_priv;
-
-		urb_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
-		save_flags(flags);
-		cli();
-		if (urb_priv->eot == 1) {
-			old_urb->actual_length = urb_priv->rx_offset;
-			dbg_ctrl("urb_priv->rx_offset: %d in handle_control_attn", urb_priv->rx_offset);
-		} else {
-			status = -EPROTO;
-			old_urb->actual_length = 0;
-			err("(CTRL) No eot set in IN data!!! rx_offset: %d", urb_priv->rx_offset);
-		}
-
-		restore_flags(flags);
-	}
-	
-	/* If there are any more URB's in the list we'd better start sending */
-	if (URB_List[epid]) {
-		etrax_usb_do_ctrl_hw_add(URB_List[epid], epid,
-					 usb_maxpacket(URB_List[epid]->dev, URB_List[epid]->pipe,
-						       usb_pipeout(URB_List[epid]->pipe)));
-	}
-#if 1
-	else {
-		/* This means that this EP is now free, deconfigure it */
-		etrax_usb_free_epid(epid);
+	/* Is there an active epid for this urb ? */	
+	epid = etrax_usb_setup_epid(urb);
+	if (epid == -1) {
+		DBFEXIT;
+		return -ENOMEM;
 	}
-#endif
 	
-	/* Remember to free the SB's */
-	hc_priv = (etrax_urb_priv_t *)old_urb->hcpriv;
-	cleanup_sb(hc_priv->first_sb);
-	kfree(hc_priv);
+        dbg_isoc("Submitting isoc urb = 0x%lx", (unsigned long)urb);
 
-	old_urb->status = status;
-	if (old_urb->complete) {
-		old_urb->complete(old_urb);
-	}
+	/* Ok, now we got valid endpoint, lets insert some traffic */
 
-	DBFEXIT;
-}
+	urb->status = -EINPROGRESS;
 
+	/* Find the last urb in the URB_List and add this urb after that one. 
+	   Also add the traffic, that is do an isoc_hw_add. This is important
+	   to make this in "real time" since isochronous traffic is time sensitive. */
+
+        dbg_isoc("Adding isoc urb to (possibly empty) list");	
+	urb_list_add(urb, epid);
+	etrax_usb_add_to_isoc_sb_list(urb, epid);
+		
+	DBFEXIT;
 
+	return 0;
+}
 
-static void etrax_usb_hc_intr_bottom_half(void *data)
+static void etrax_usb_add_to_isoc_sb_list(urb_t *urb, int epid) 
 {
-	struct usb_reg_context *reg = (struct usb_reg_context *)data;
+
+	int i = 0;	
 	
-	int error_code;
-	int epid;
+	etrax_urb_priv_t *urb_priv; 
+	USB_SB_Desc_t *prev_sb_desc,  *next_sb_desc, *temp_sb_desc; 
 
-	__u32 r_usb_ept_data;
+	DBFENTER;
 
-	etrax_hc_t *hc = reg->hc; 
-	__u16 r_usb_rh_port_status_1;
-	__u16 r_usb_rh_port_status_2;
+	prev_sb_desc = next_sb_desc = temp_sb_desc = NULL; 
 	
-	DBFENTER;
+	urb_priv = kmalloc(sizeof(etrax_urb_priv_t), KMALLOC_FLAG);
+	assert(urb_priv != NULL);
+        memset(urb_priv, 0, sizeof(etrax_urb_priv_t));
 
-	if (reg->r_usb_irq_mask_read & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) {
+	urb->hcpriv = urb_priv;
+	urb_priv->epid = epid;
+	
+	if (usb_pipeout(urb->pipe)) {
 
-		/*
-		  The Etrax RH does not include a wPortChange register, so this has
-		  to be handled in software. See section 11.16.2.6.2 in USB 1.1 spec
-		  for details.
-		*/
+                /* Not implemented yet! */
+		dbg_isoc("Transfer for epid %d is OUT", epid);
 		
-		r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1;
-		r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2;
+		/* Create one SB descriptor for each packet and link them together. */
+		for (i = 0; i < urb->number_of_packets; i++) {	
 		
-		dbg_rh("port_status pending");
-		dbg_rh("r_usb_rh_port_status_1: 0x%04X", r_usb_rh_port_status_1);
-		dbg_rh("r_usb_rh_port_status_2: 0x%04X", r_usb_rh_port_status_2);
+			next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+			assert(next_sb_desc != NULL);
+			
+			next_sb_desc->command = (IO_STATE(USB_SB_command, tt, out) |
+                                                 IO_STATE(USB_SB_command, eot, yes));
+			
+			next_sb_desc->sw_len = urb->iso_frame_desc[i].length;
+			next_sb_desc->buf = virt_to_phys(urb->transfer_buffer + urb->iso_frame_desc[i].offset);
+			
+			/* First SB descriptor that belongs to this urb */	
+			if (i == 0) 
+				urb_priv->first_sb = next_sb_desc;
+			else
+				prev_sb_desc->next = virt_to_phys(next_sb_desc);			
+						
+			prev_sb_desc = next_sb_desc;	
+		}
+		/* Check if full length transfer. */
+		if (urb->iso_frame_desc[urb->number_of_packets - 1].length ==
+		    usb_maxpacket(urb->dev, urb->pipe, usb_pipeout(urb->pipe))) {
+			next_sb_desc->command |= IO_STATE(USB_SB_command, full, yes);
+		}
 
-		/* C_PORT_CONNECTION is set on any transition */
-		hc->rh.wPortChange_1 |=
-			((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) !=
-			 (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ?
-			(1 << RH_PORT_CONNECTION) : 0;
-		
-		hc->rh.wPortChange_2 |=
-			((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) !=
-			 (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ?
-			(1 << RH_PORT_CONNECTION) : 0;
+		next_sb_desc->command |= IO_STATE(USB_SB_command, eol, yes);
+		next_sb_desc->next = 0;
+		urb_priv->last_sb = next_sb_desc;
+	
+	} else if (usb_pipein(urb->pipe)) {
 
-		/* C_PORT_ENABLE is _only_ set on a one to zero transition */
-		hc->rh.wPortChange_1 |=
-			((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE))
-			 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
-			(1 << RH_PORT_ENABLE) : 0;
-		
-		hc->rh.wPortChange_2 |=
-			((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE))
-			 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
-			(1 << RH_PORT_ENABLE) : 0;
-		
-		/* C_PORT_SUSPEND seems difficult, lets ignore it.. (for now) */
-		
-		/* C_PORT_RESET is _only_ set on a transition from the resetting state
-		   to the enabled state */
-		hc->rh.wPortChange_1 |=
-			((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET))
-			 && (r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
-			(1 << RH_PORT_RESET) : 0;
-		
-		hc->rh.wPortChange_2 |=
-			((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET))
-			 && (r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
-			(1 << RH_PORT_RESET) : 0;
-		
-		hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1;
-		hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2;	
+		dbg_isoc("Transfer for epid %d is IN", epid);
+		dbg_isoc("transfer_buffer_length = %d", urb->transfer_buffer_length);
+		dbg_isoc("rem is calculated to %d", urb->iso_frame_desc[urb->number_of_packets - 1].length);
+		
+                /* Note that in descriptors for periodic traffic are not consumed. This means that
+                   the USB controller never propagates in the SB list. In other words, if there already
+                   is an SB descriptor in the list for this EP we don't have to do anything. */
+                if (TxIsocEPList[epid].sub == 0) {
+                        dbg_isoc("Isoc traffic not already running, allocating SB");
+                        
+                        next_sb_desc = (USB_SB_Desc_t*)kmem_cache_alloc(usb_desc_cache, SLAB_FLAG);
+                        assert(next_sb_desc != NULL);
+			
+                        next_sb_desc->command = (IO_STATE(USB_SB_command, tt, in) |
+                                                 IO_STATE(USB_SB_command, eot, yes) | 
+                                                 IO_STATE(USB_SB_command, eol, yes));
+			
+                        next_sb_desc->sw_len = urb->number_of_packets;
+                        next_sb_desc->buf = 0;
+
+                        /* The rem field is don't care for isoc traffic, so we don't set it. */
+
+                        /* Only one SB descriptor that belongs to this urb. */
+                        urb_priv->first_sb = next_sb_desc; 	
+                        urb_priv->last_sb = next_sb_desc; 	
+                
+                } else {
+                
+                        dbg_isoc("Isoc traffic already running, just setting first/last_sb");
+                        
+                        /* Each EP for isoc in will have only one SB descriptor, setup when submitting the
+                           already active urb. Note that even though we may have several first_sb/last_sb
+                           pointing at the same SB descriptor, they are freed only once (when the list has
+                           become empty). */
+                        urb_priv->first_sb = phys_to_virt(TxIsocEPList[epid].sub);
+                        urb_priv->last_sb = phys_to_virt(TxIsocEPList[epid].sub);
+                        return;
+                }
+                
+	}
+	
+	/* Find the spot to insert this urb and add it. */
+	if (TxIsocEPList[epid].sub == 0) {
+		/* First SB descriptor inserted in this list (in or out). */
+		dbg_isoc("Inserting SB desc first in list");
+		TxIsocEPList[epid].sub = virt_to_phys(urb_priv->first_sb);
+	} else {	
+		/* Isochronous traffic is already running, insert new traffic last (only out). */
+		dbg_isoc("Inserting SB desc last in list");
+		temp_sb_desc = phys_to_virt(TxIsocEPList[epid].sub);
+		while (!(temp_sb_desc->command & IO_MASK(USB_SB_command, eol))) {
+			temp_sb_desc = phys_to_virt(temp_sb_desc->next);
+		}
+		/* Next pointer must be set before eol is removed. */
+		temp_sb_desc->next = virt_to_phys(urb_priv->first_sb);	
+		/* Clear the previous end of list flag since there is a new in the
+		   added SB descriptor list. */
+		temp_sb_desc->command &= ~IO_MASK(USB_SB_command, eol);
+	}
+	
+        if (urb->transfer_flags & USB_ISO_ASAP) {
+                /* The isoc transfer should be started as soon as possible. The start_frame
+                   field is a return value if URB_ISO_ASAP was set. Comparing R_USB_FM_NUMBER
+                   with a USB Chief trace shows that the first isoc IN token is sent 2 frames
+                   later. I'm not sure how this affects usage of the start_frame field by the
+                   device driver, or how it affects things when USB_ISO_ASAP is not set, so
+                   therefore there's no compensation for the 2 frame "lag" here. */
+                urb->start_frame = (*R_USB_FM_NUMBER & 0x7ff);
+                TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+                urb_priv->urb_state = STARTED;
+                dbg_isoc("URB_ISO_ASAP set, urb->start_frame set to %d", urb->start_frame);
+        } else {
+                /* Not started yet. */
+                urb_priv->urb_state = NOT_STARTED;
+        }
+        
+       /* We start the DMA sub channel without checking if it's running or not, because:
+          1) If it's already running, issuing the start command is a nop.
+          2) We avoid a test-and-set race condition. */
+        *R_DMA_CH8_SUB3_CMD = IO_STATE(R_DMA_CH8_SUB3_CMD, cmd, start);
+        
+	DBFEXIT;
+}
+
+static void etrax_usb_complete_isoc_urb(urb_t *urb, int status)
+{
+	etrax_urb_priv_t *urb_priv = (etrax_urb_priv_t *)urb->hcpriv; 
+        int epid = urb_priv->epid;
+	
+	DBFENTER;
+
+        if (status)
+                warn("Completing isoc urb with status %d.", status);
+        
+        if (usb_pipein(urb->pipe)) {
+		
+                /* Move this one down the list. */
+                urb_list_move_last(urb, epid);
+                
+                /* Mark the now first urb as started (may already be). */
+                ((etrax_urb_priv_t *)(urb_list_first(epid)->hcpriv))->urb_state = STARTED;
 	}
 
-	for (epid = 0; epid < 32; epid++) {
+	urb->status = status;
+	if (urb->complete) {
+		urb->complete(urb);
+	}
 
-		unsigned long flags;
+	DBFEXIT;
+}
 
-		save_flags(flags);
-		cli();
+static void etrax_usb_complete_urb(urb_t *urb, int status)
+{
+        switch (usb_pipetype(urb->pipe)) {
+        case PIPE_BULK:
+                etrax_usb_complete_bulk_urb(urb, status);
+                break;                
+        case PIPE_CONTROL:
+                etrax_usb_complete_ctrl_urb(urb, status);
+                break;
+        case PIPE_INTERRUPT:
+                etrax_usb_complete_intr_urb(urb, status);
+                break;
+        case PIPE_ISOCHRONOUS:
+                etrax_usb_complete_isoc_urb(urb, status);
+                break;
+        default:
+                err("Unknown pipetype");
+        }
+}
 
-		*R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); nop();
-		r_usb_ept_data = *R_USB_EPT_DATA;
 
-		restore_flags(flags);
 
-		if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
-			warn("Was hold for epid %d", epid);
-			continue;
-		}
+static void etrax_usb_hc_interrupt_top_half(int irq, void *vhc, struct pt_regs *regs)
+{
+	usb_interrupt_registers_t *reg;
+        unsigned long flags;
+	__u32 irq_mask;
+	__u8 status;
+        __u32 epid_attn;
+        __u16 port_status_1;
+	__u16 port_status_2;
+        __u32 fm_number;
 
-		if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) {
-			continue;
-		}
-		
-		
-		if (test_bit(epid, (void *)&reg->r_usb_epid_attn)) {
+	DBFENTER;
 
-			if (URB_List[epid] == NULL) {
-				err("R_USB_EPT_DATA is 0x%08X", r_usb_ept_data);
-				err("submit urb has been called %lu times..", submit_urb_count);
-				err("EPID_ATTN for epid %d, with NULL entry in list", epid);
-				return;
-			}
-			
-			dbg_ep("r_usb_ept_data [%d] == 0x%08X", epid,
-			       r_usb_ept_data);
-			
-			error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code,
-						r_usb_ept_data);
-			
-			if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
-				/* no_error means that this urb was sucessfully sent or that we have
-				   some undefinde error*/
-				
-				if (IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3 ||
-				    IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3) {
-				/* Actually there were transmission errors */
-					warn("Undefined error for epid %d", epid);
-					if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
-						handle_control_transfer_attn(epid, -EPROTO);
-					} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
-						handle_bulk_transfer_attn(epid, -EPROTO);
-					} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
-						handle_intr_transfer_attn(epid, -EPROTO);
-					}
-							   
-				} else {
-
-					if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
-						if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
-							etrax_usb_do_intr_recover(epid);
-						} else {
-							panic("Epid attention for epid %d (none INTR), with no errors and no "
-							      "exessive retry r_usb_status is 0x%02X\n",
-							      epid, reg->r_usb_status);
-						}
-						
-					} else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
-						panic("Epid attention for epid %d, with no errors and no "
-						      "exessive retry r_usb_status is 0x%02X\n",
-						      epid, reg->r_usb_status);
-						
-					}
-					
-					warn("Epid attention for epid %d, with no errors and no "
-					     "exessive retry r_usb_status is 0x%02X",
-					     epid, reg->r_usb_status);
-					warn("OUT error count: %d", IO_EXTRACT(R_USB_EPT_DATA, error_count_out,
-									       r_usb_ept_data));
-					warn("IN  error count: %d", IO_EXTRACT(R_USB_EPT_DATA, error_count_in,
-									       r_usb_ept_data));
-					
+        /* Read critical registers into local variables, do kmalloc afterwards. */
+        save_flags(flags);
+        cli();
+        
+	irq_mask = *R_USB_IRQ_MASK_READ;
+        /* Reading R_USB_STATUS clears the ctl_status interrupt. Note that R_USB_STATUS
+           must be read before R_USB_EPID_ATTN since reading the latter clears the 
+           ourun and perror fields of R_USB_STATUS. */
+	status = *R_USB_STATUS;
+
+        /* Reading R_USB_EPID_ATTN clears the iso_eof, bulk_eot and epid_attn interrupts. */
+	epid_attn = *R_USB_EPID_ATTN;
+
+        /* Reading R_USB_RH_PORT_STATUS_1 and R_USB_RH_PORT_STATUS_2 clears the
+           port_status interrupt. */
+	port_status_1 = *R_USB_RH_PORT_STATUS_1;
+	port_status_2 = *R_USB_RH_PORT_STATUS_2;
+
+        /* Reading R_USB_FM_NUMBER clears the sof interrupt. */
+        /* Note: the lower 11 bits contain the actual frame number, sent with each sof. */
+        fm_number = *R_USB_FM_NUMBER;
 
-				}
-				
-			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) {
-				warn("Stall for epid %d", epid);
-				if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
-					handle_control_transfer_attn(epid, -EPIPE);
-				} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
-					handle_bulk_transfer_attn(epid, -EPIPE);
-				} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
-					handle_intr_transfer_attn(epid, -EPIPE);
-				}
-				
-				
-			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) {
-				panic("USB bus error for epid %d\n", epid);
-				
-			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
-				warn("Buffer error for epid %d", epid);
+        restore_flags(flags);
 
-				if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
-					handle_control_transfer_attn(epid, -EPROTO);
-				} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
-					handle_bulk_transfer_attn(epid, -EPROTO);
-				} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
-					handle_intr_transfer_attn(epid, -EPROTO);
-				}
+	reg = (usb_interrupt_registers_t *)kmem_cache_alloc(top_half_reg_cache, SLAB_ATOMIC);
 
-			}
-		} else if (test_bit(epid, (void *)&ep_really_active)) {
-			/* Should really be else if (testbit(really active)) */
+	assert(reg != NULL);
 
-			if (usb_pipetype(URB_List[epid]->pipe) == PIPE_CONTROL) {
+	reg->hc = (etrax_hc_t *)vhc;
 
-				if (!(TxCtrlEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
-					/* Now we have to verify that this CTRL endpoint got disabled
-					   cause it reached end of list with no error */
-					
-					if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
-					    IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
-						/*
-						  This means that the endpoint has no error, is disabled
-						  and had inserted traffic,
-						  i.e. transfer sucessfully completed
-						*/
-						dbg_ctrl("Last SB for CTRL %d sent sucessfully", epid);
-						handle_control_transfer_attn(epid, 0);
-					}
-				}
-				
-			} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_BULK) {
-				if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable))) {
-					/* Now we have to verify that this BULK endpoint go disabled
-					   cause it reached end of list with no error */
-
-					if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
-					    IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
-						/*
-						  This means that the endpoint has no error, is disabled
-						  and had inserted traffic,
-						  i.e. transfer sucessfully completed
-						*/
-						dbg_bulk("Last SB for BULK %d sent sucessfully", epid);
-						handle_bulk_transfer_attn(epid, 0);
-					}
-				}
-			} else if (usb_pipetype(URB_List[epid]->pipe) == PIPE_INTERRUPT) {
-				handle_intr_transfer_attn(epid, 0);
-			}
-		}
-		
-	}	
+        /* Now put register values into kmalloc'd area. */
+        reg->r_usb_irq_mask_read = irq_mask;
+        reg->r_usb_status = status;
+        reg->r_usb_epid_attn = epid_attn;
+	reg->r_usb_rh_port_status_1 = port_status_1;
+	reg->r_usb_rh_port_status_2 = port_status_2;
+        reg->r_usb_fm_number = fm_number;
+        
+	reg->usb_bh.sync = 0;
+	reg->usb_bh.routine = etrax_usb_hc_interrupt_bottom_half;
+	reg->usb_bh.data = reg;
+
+	queue_task(&reg->usb_bh, &tq_immediate);
+	mark_bh(IMMEDIATE_BH);
+
+	DBFEXIT;
+}
+
+static void etrax_usb_hc_interrupt_bottom_half(void *data)
+{
+	usb_interrupt_registers_t *reg = (usb_interrupt_registers_t *)data;
+        __u32 irq_mask = reg->r_usb_irq_mask_read;
+	
+	DBFENTER;
 
-	kfree(reg);
+        /* Interrupts are handled in order of priority. */
+        if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, epid_attn)) {
+                etrax_usb_hc_epid_attn_interrupt(reg);
+        }
+	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, port_status)) {
+                etrax_usb_hc_port_status_interrupt(reg);
+        }
+        if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, ctl_status)) {
+                etrax_usb_hc_ctl_status_interrupt(reg);
+        }
+	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, iso_eof)) {
+                etrax_usb_hc_isoc_eof_interrupt();
+        } 
+	if (irq_mask & IO_MASK(R_USB_IRQ_MASK_READ, bulk_eot)) {
+                /* Update/restart the bulk start timer since obviously the channel is running. */
+                mod_timer(&bulk_start_timer, jiffies + BULK_START_TIMER_INTERVAL);
+                /* Update/restart the bulk eot timer since we just received an bulk eot interrupt. */
+                mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+                
+                etrax_usb_hc_bulk_eot_interrupt(0);
+        }
+        
+	kmem_cache_free(top_half_reg_cache, reg);
 
 	DBFEXIT;
 }
 
 
-static void etrax_usb_hc_intr_top_half(int irq, void *vhc, struct pt_regs *regs)
+void etrax_usb_hc_isoc_eof_interrupt(void)
 {
-	struct usb_reg_context *reg;
+	urb_t *urb;
+	etrax_urb_priv_t *urb_priv;
+	int epid;
 
 	DBFENTER;
 
-	reg = (struct usb_reg_context *)kmalloc(sizeof(struct usb_reg_context), GFP_ATOMIC);
+	/* Do not check the invalid epid (it has a valid sub pointer). */
+	for (epid = 0; epid < NBR_OF_EPIDS - 1; epid++) {
+	
+		if (TxIsocEPList[epid].sub == 0) {
+			/* Nothing here to see. */
+			continue;
+		}
 
-	if (!(reg)) {
-		panic("kmalloc failed in top_half\n");
-	}
+                /* Get the first urb (if any). */
+		urb = urb_list_first(epid);
+                if (urb == 0) {
+                        warn("Ignoring NULL urb");
+                        continue;
+                }
+
+                /* Sanity check. */
+                assert(usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS);
+
+                urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+		assert(urb_priv);
+
+                if (urb_priv->urb_state == NOT_STARTED) {
+                        
+                        /* If ASAP is not set and urb->start_frame is the current frame,
+                           start the transfer. */
+                        if (!(urb->transfer_flags & USB_ISO_ASAP) && 
+                            (urb->start_frame == (*R_USB_FM_NUMBER & 0x7ff))) {
+
+                                dbg_isoc("Enabling isoc IN EP descr for epid %d", epid);
+                                TxIsocEPList[epid].command |= IO_STATE(USB_EP_command, enable, yes);
+
+                                /* This urb is now active. */
+                                urb_priv->urb_state = STARTED;
+                                continue;
+                        }
+                }
+        }
+	
+	DBFEXIT;
 
-	reg->hc = (etrax_hc_t *)vhc;
-	reg->r_usb_irq_mask_read    = *R_USB_IRQ_MASK_READ;
-	reg->r_usb_status           = *R_USB_STATUS;
+}
 
-#if 0
-	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
-		panic("r_usb_status said perror\n");
-	}
-	if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
-		panic("r_usb_status said ourun !!!\n");
-	}
-#endif
-	
-	reg->r_usb_epid_attn        = *R_USB_EPID_ATTN;
+void etrax_usb_hc_bulk_eot_interrupt(int timer_induced)
+{
+ 	int epid;
+ 
+        /* The technique is to run one urb at a time, wait for the eot interrupt at which
+           point the EP descriptor has been disabled. */
+        
+        DBFENTER;
+        dbg_bulk("bulk eot%s", timer_induced ? ", called by timer" : "");
+
+        for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+
+                if (!(TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) &&
+                    (TxBulkEPList[epid].sub != 0)) {
+
+                        urb_t *urb;
+                        etrax_urb_priv_t *urb_priv;
+                        unsigned long flags;
+                        __u32 r_usb_ept_data;
+
+                        /* Found a disabled EP descriptor which has a non-null sub pointer.
+                           Verify that this ctrl EP descriptor got disabled no errors.
+                           FIXME: Necessary to check error_code? */
+                        dbg_bulk("for epid %d?", epid);
+
+                        /* Get the first urb. */
+                        urb = urb_list_first(epid);
+
+                        /* FIXME: Could this happen for valid reasons? Why did it disappear? Because of
+                           wrong unlinking? */
+                        if (!urb) {
+                                warn("NULL urb for epid %d", epid);
+                                continue;
+                        }
+                        
+                        assert(urb);
+                        urb_priv = (etrax_urb_priv_t *)urb->hcpriv;
+                        assert(urb_priv);
+
+                        /* Sanity checks. */
+                        assert(usb_pipetype(urb->pipe) == PIPE_BULK);
+                        if (phys_to_virt(TxBulkEPList[epid].sub) != urb_priv->last_sb) {
+                                err("bulk endpoint got disabled before reaching last sb");
+                        }
+
+                        /* For bulk IN traffic, there seems to be a race condition between
+                           between the bulk eot and eop interrupts, or rather an uncertainty regarding
+                           the order in which they happen. Normally we expect the eop interrupt from
+                           DMA channel 9 to happen before the eot interrupt.
+
+                           Therefore, we complete the bulk IN urb in the rx interrupt handler instead. */
+                        
+                        if (usb_pipein(urb->pipe)) {
+                                dbg_bulk("in urb, continuing");
+                                continue;
+                        }
+                        
+                        save_flags(flags);
+                        cli();
+                        *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
+                        nop();
+                        r_usb_ept_data = *R_USB_EPT_DATA;
+                        restore_flags(flags);
+                        
+                        if (IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data) ==
+                            IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
+                                /* This means that the endpoint has no error, is disabled
+                                   and had inserted traffic, i.e. transfer successfully completed. */
+                                etrax_usb_complete_bulk_urb(urb, 0);
+                        } else {
+                                /* Shouldn't happen. We expect errors to be caught by epid attention. */
+                                err("Found disabled bulk EP desc, error_code != no_error");
+                        }
+                }
+        }
+        
+        /* Normally, we should find (at least) one disabled EP descriptor with a valid sub pointer.
+           However, because of the uncertainty in the deliverance of the eop/eot interrupts, we may
+           not.  Also, we might find two disabled EPs when handling an eot interrupt, and then find
+           none the next time. */
+
+        DBFEXIT;
+
+}
+
+void etrax_usb_hc_epid_attn_interrupt(usb_interrupt_registers_t *reg)
+{
+        /* This function handles the epid attention interrupt.  There are a variety of reasons
+           for this interrupt to happen (Designer's Reference, p. 8 - 22 for the details):
+
+           invalid ep_id  - Invalid epid in an EP (EP disabled).
+           stall          - Not strictly an error condition (EP disabled).
+           3rd error      - Three successive transaction errors  (EP disabled).
+           buffer ourun   - Buffer overrun or underrun (EP disabled).
+           past eof1      - Intr or isoc transaction proceeds past EOF1.
+           near eof       - Intr or isoc transaction would not fit inside the frame.
+           zout transfer  - If zout transfer for a bulk endpoint (EP disabled).
+           setup transfer - If setup transfer for a non-ctrl endpoint (EP disabled). */
+
+        int epid;
+
+
+        DBFENTER;
+        
+        /* Note that we loop through all epids. We still want to catch errors for 
+           the invalid one, even though we might handle them differently. */
+        for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
 
-	reg->r_usb_rh_port_status_1 = *R_USB_RH_PORT_STATUS_1;
-	reg->r_usb_rh_port_status_2 = *R_USB_RH_PORT_STATUS_2;
+		if (test_bit(epid, (void *)&reg->r_usb_epid_attn)) {
+                        
+                        urb_t *urb;
+                        __u32 r_usb_ept_data;
+                        unsigned long flags;
+                        int error_code;
+
+                        save_flags(flags);
+                        cli();                
+                        *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, epid); 
+                        nop();
+                        /* Note that although there are separate R_USB_EPT_DATA and R_USB_EPT_DATA_ISO
+                           registers, they are located at the same address and are of the same size.
+                           In other words, this read should be ok for isoc also. */
+                        r_usb_ept_data = *R_USB_EPT_DATA;
+                        restore_flags(flags);
+
+                        /* First some sanity checks. */
+                        if (epid == INVALID_EPID) {
+                                /* FIXME: What if it became disabled? Could seriously hurt interrupt
+				   traffic. (Use do_intr_recover.) */
+                                warn("Got epid_attn for INVALID_EPID (%d).", epid);
+				err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
+				err("R_USB_STATUS = 0x%x", reg->r_usb_status);
+				continue;
+                        } else 	if (epid == DUMMY_EPID) {
+                                /* We definitely don't care about these ones. Besides, they are
+				   always disabled, so any possible disabling caused by the
+				   epid attention interrupt is irrelevant. */
+                                warn("Got epid_attn for DUMMY_EPID (%d).", epid);
+				continue;
+                        }
+
+                        /* Get the first urb in the urb list for this epid. We blatantly assume
+                           that only the first urb could have caused the epid attention.
+                           (For bulk and ctrl, only one urb is active at any one time. For intr
+                           and isoc we remove them once they are completed.) */
+                        urb = urb_list_first(epid);
+
+			if (urb == NULL) {
+                                err("Got epid_attn for epid %i with no urb.", epid);
+                                err("R_USB_EPT_DATA = 0x%x", r_usb_ept_data);
+                                err("R_USB_STATUS = 0x%x", reg->r_usb_status);
+				continue;
+			}
+
+                        switch (usb_pipetype(urb->pipe)) {
+                        case PIPE_BULK:
+                                warn("Got epid attn for bulk endpoint, epid %d", epid);
+                                break;                
+                        case PIPE_CONTROL:
+                                warn("Got epid attn for control endpoint, epid %d", epid);
+                                break;
+                        case PIPE_INTERRUPT:
+                                warn("Got epid attn for interrupt endpoint, epid %d", epid);
+                                break;
+                        case PIPE_ISOCHRONOUS:
+                                warn("Got epid attn for isochronous endpoint, epid %d", epid);
+                                break;
+                        }
+                        
+                        if (usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) {        
+                                if (r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, hold)) {
+                                        warn("Hold was set for epid %d.", epid);
+                                        continue;
+                                }
+                        }
+                        
+                        /* Even though error_code occupies bits 22 - 23 in both R_USB_EPT_DATA and 
+                           R_USB_EPT_DATA_ISOC, we separate them here so we don't forget in other places. */
+                        if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+                                error_code = IO_EXTRACT(R_USB_EPT_DATA_ISO, error_code, r_usb_ept_data);
+                        } else {
+                                error_code = IO_EXTRACT(R_USB_EPT_DATA, error_code, r_usb_ept_data);
+                        }
+                        
+			/* Using IO_STATE_VALUE on R_USB_EPT_DATA should be ok for isoc also. */
+			if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, no_error)) {
 
-	reg->usb_bh.sync = 0;
-	reg->usb_bh.routine = etrax_usb_hc_intr_bottom_half;
-	reg->usb_bh.data = reg;
+                                /* Isoc traffic doesn't have error_count_in/error_count_out. */
+				if ((usb_pipetype(urb->pipe) != PIPE_ISOCHRONOUS) &&
+                                    (IO_EXTRACT(R_USB_EPT_DATA, error_count_in, r_usb_ept_data) == 3 ||
+                                     IO_EXTRACT(R_USB_EPT_DATA, error_count_out, r_usb_ept_data) == 3)) {
+				        /* 3rd error. */
+					warn("3rd error for epid %i", epid);
+                                        etrax_usb_complete_urb(urb, -EPROTO);
+
+                                } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
+                                        
+                                        warn("Perror for epid %d", epid);
+                                        
+                                        if (!(r_usb_ept_data & IO_MASK(R_USB_EPT_DATA, valid))) {
+                                                /* invalid ep_id */
+                                                panic("Perror because of invalid epid."
+                                                      " Deconfigured too early?");
+                                        } else {
+                                                /* past eof1, near eof, zout transfer, setup transfer */
+                                                
+                                                /* Dump the urb and the relevant EP descriptor list. */
+
+                                                __dump_urb(urb);
+						__dump_ept_data(epid);
+                                                __dump_ep_list(usb_pipetype(urb->pipe));
+                                                
+                                                panic("Something wrong with DMA descriptor contents."
+                                                      " Too much traffic inserted?");
+                                        }
+                                } else if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
+                                        /* buffer ourun */
+                                        panic("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
+                                }
 
-	queue_task(&reg->usb_bh, &tq_immediate);
-	mark_bh(IMMEDIATE_BH);
+			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, stall)) {
+                                /* Not really a protocol error, just says that the endpoint gave
+                                   a stall response. Note that error_code cannot be stall for isoc. */
+                                if (usb_pipetype(urb->pipe) == PIPE_ISOCHRONOUS) {
+                                        panic("Isoc traffic cannot stall");
+                                }
+                                
+                                warn("Stall for epid %d", epid);
+                                etrax_usb_complete_urb(urb, -EPIPE);
+				
+			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, bus_error)) {
+                                /* Two devices responded to a transaction request. Must be resolved
+                                   by software. FIXME: Reset ports? */
+				panic("Bus error for epid %d."
+                                      " Two devices responded to transaction request",
+                                      epid);
+				
+			} else if (error_code == IO_STATE_VALUE(R_USB_EPT_DATA, error_code, buffer_error)) {
+                                /* DMA overrun or underrun. */
+				warn("Buffer overrun/underrun for epid %d. DMA too busy?", epid);
 
-	DBFEXIT;
+                                /* It seems that error_code = buffer_error in 
+                                   R_USB_EPT_DATA/R_USB_EPT_DATA_ISO and ourun = yes in R_USB_STATUS
+                                   are the same error. */
+                                etrax_usb_complete_urb(urb, -EPROTO);
+                        }               
+                }
+        }
+
+        DBFEXIT;
+        
+}
+
+void etrax_usb_bulk_start_timer_func(unsigned long dummy)
+{
+       
+        /* We might enable an EP descriptor behind the current DMA position when it's about
+           to decide that there are no more bulk traffic and it should stop the bulk channel.
+           Therefore we periodically check if the bulk channel is stopped and there is an
+           enabled bulk EP descriptor, in which case we start the bulk channel. */
+        dbg_bulk("bulk_start_timer timed out.");        
+
+        if (!(*R_DMA_CH8_SUB0_CMD & IO_MASK(R_DMA_CH8_SUB0_CMD, cmd))) {
+                int epid;
+                
+		dbg_bulk("Bulk DMA channel not running.");
+
+                for (epid = 0; epid < NBR_OF_EPIDS; epid++) {
+                        if (TxBulkEPList[epid].command & IO_MASK(USB_EP_command, enable)) {
+                                printk("Found enabled EP for epid %d, starting bulk channel.\n",
+				       epid);
+                                *R_DMA_CH8_SUB0_CMD = IO_STATE(R_DMA_CH8_SUB0_CMD, cmd, start);
+                                
+                                /* Restart the bulk eot timer since we just started the bulk channel. */
+                                mod_timer(&bulk_eot_timer, jiffies + BULK_EOT_TIMER_INTERVAL);
+
+                                /* No need to search any further. */
+                                break;
+                        }
+                }
+        } else {
+		dbg_bulk("Bulk DMA channel running.");
+	}
+}
+
+void etrax_usb_hc_port_status_interrupt(usb_interrupt_registers_t *reg)
+{
+        etrax_hc_t *hc = reg->hc;
+        __u16 r_usb_rh_port_status_1 = reg->r_usb_rh_port_status_1;
+	__u16 r_usb_rh_port_status_2 = reg->r_usb_rh_port_status_2;
+
+        DBFENTER;
+        
+        /* The Etrax RH does not include a wPortChange register, so this has to be handled in software
+           (by saving the old port status value for comparison when the port status interrupt happens).
+           See section 11.16.2.6.2 in the USB 1.1 spec for details. */
+        
+        dbg_rh("hc->rh.prev_wPortStatus_1 = 0x%x", hc->rh.prev_wPortStatus_1);
+        dbg_rh("hc->rh.prev_wPortStatus_2 = 0x%x", hc->rh.prev_wPortStatus_2);
+        dbg_rh("r_usb_rh_port_status_1 = 0x%x", r_usb_rh_port_status_1);
+        dbg_rh("r_usb_rh_port_status_2 = 0x%x", r_usb_rh_port_status_2);
+        
+        /* C_PORT_CONNECTION is set on any transition. */
+        hc->rh.wPortChange_1 |=
+                ((r_usb_rh_port_status_1 & (1 << RH_PORT_CONNECTION)) !=
+                 (hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_CONNECTION))) ?
+                (1 << RH_PORT_CONNECTION) : 0;
+        
+        hc->rh.wPortChange_2 |=
+                ((r_usb_rh_port_status_2 & (1 << RH_PORT_CONNECTION)) !=
+                 (hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_CONNECTION))) ?
+                (1 << RH_PORT_CONNECTION) : 0;
+        
+        /* C_PORT_ENABLE is _only_ set on a one to zero transition, i.e. when
+           the port is disabled, not when it's enabled. */
+        hc->rh.wPortChange_1 |=
+                ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_ENABLE))
+                 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_ENABLE))) ?
+                (1 << RH_PORT_ENABLE) : 0;
+	
+        hc->rh.wPortChange_2 |=
+                ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_ENABLE))
+                 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_ENABLE))) ?
+                (1 << RH_PORT_ENABLE) : 0;
+
+        /* C_PORT_SUSPEND is set to one when the device has transitioned out
+           of the suspended state, i.e. when suspend goes from one to zero. */
+        hc->rh.wPortChange_1 |=
+                ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_SUSPEND))
+                 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_SUSPEND))) ?
+                (1 << RH_PORT_SUSPEND) : 0;
+        
+        hc->rh.wPortChange_2 |=
+                ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_SUSPEND))
+                 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_SUSPEND))) ?
+                (1 << RH_PORT_SUSPEND) : 0;
+
+        
+        /* C_PORT_RESET is set when reset processing on this port is complete. */
+        hc->rh.wPortChange_1 |=
+                ((hc->rh.prev_wPortStatus_1 & (1 << RH_PORT_RESET))
+                 && !(r_usb_rh_port_status_1 & (1 << RH_PORT_RESET))) ?
+                (1 << RH_PORT_RESET) : 0;
+        
+        hc->rh.wPortChange_2 |=
+                ((hc->rh.prev_wPortStatus_2 & (1 << RH_PORT_RESET))
+                 && !(r_usb_rh_port_status_2 & (1 << RH_PORT_RESET))) ?
+                (1 << RH_PORT_RESET) : 0;
+
+        /* Save the new values for next port status change. */
+        hc->rh.prev_wPortStatus_1 = r_usb_rh_port_status_1;
+        hc->rh.prev_wPortStatus_2 = r_usb_rh_port_status_2;	
+
+        dbg_rh("hc->rh.wPortChange_1 set to 0x%x", hc->rh.wPortChange_1);
+        dbg_rh("hc->rh.wPortChange_2 set to 0x%x", hc->rh.wPortChange_2);
+        
+        DBFEXIT;
+
+}
+
+void etrax_usb_hc_ctl_status_interrupt(usb_interrupt_registers_t *reg)
+{
+        DBFENTER;
+
+        /* FIXME: What should we do if we get ourun or perror? Dump the EP and SB
+           list for the corresponding epid? */
+        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, ourun)) {
+                panic("USB controller got ourun.");
+        } 
+        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, perror)) {
+
+                /* Before, etrax_usb_do_intr_recover was called on this epid if it was
+                   an interrupt pipe. I don't see how re-enabling all EP descriptors
+                   will help if there was a programming error. */
+                panic("USB controller got perror.");
+        } 
+
+        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, device_mode)) {
+                /* We should never operate in device mode. */
+                panic("USB controller in device mode.");
+        }
+
+        /* These if-statements could probably be nested. */
+        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, host_mode)) {
+                //info("USB controller in host mode.");
+        }
+        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, started)) {
+                //info("USB controller started.");
+        }
+        if (reg->r_usb_status & IO_MASK(R_USB_STATUS, running)) {
+                info("USB controller running.");
+        }
+
+        DBFEXIT;
+        
 }
 
-static int etrax_rh_submit_urb(struct urb *urb)
+
+static int etrax_rh_submit_urb(urb_t *urb)
 {
 	struct usb_device *usb_dev = urb->dev;
 	etrax_hc_t *hc = usb_dev->bus->hcpriv;
 	unsigned int pipe = urb->pipe;
-	struct usb_ctrlrequest *cmd = (struct usb_ctrlrequest *) urb->setup_packet;
+	devrequest *cmd = (devrequest *) urb->setup_packet;
 	void *data = urb->transfer_buffer;
 	int leni = urb->transfer_buffer_length;
 	int len = 0;
@@ -1999,10 +3246,14 @@
 
 	DBFENTER;
 
+        /* FIXME: What is this interrupt urb that is sent to the root hub? */
 	if (usb_pipetype (pipe) == PIPE_INTERRUPT) {
 		dbg_rh("Root-Hub submit IRQ: every %d ms", urb->interval);
 		hc->rh.urb = urb;
 		hc->rh.send = 1;
+                /* FIXME: We could probably remove this line since it's done
+                   in etrax_rh_init_int_timer. (Don't remove it from
+                   etrax_rh_init_int_timer though.) */
 		hc->rh.interval = urb->interval;
 		etrax_rh_init_int_timer(urb);
 		DBFEXIT;
@@ -2010,15 +3261,22 @@
 		return 0;
 	}
 
-	bmRType_bReq = cmd->bRequestType | cmd->bRequest << 8;
-	wValue = le16_to_cpu(cmd->wValue);
-	wIndex = le16_to_cpu(cmd->wIndex);
-	wLength = le16_to_cpu(cmd->wLength);
-
-	dbg_rh("bmRType_bReq : 0x%04X (%d)", bmRType_bReq, bmRType_bReq);
-	dbg_rh("wValue       : 0x%04X (%d)", wValue, wValue);
-	dbg_rh("wIndex       : 0x%04X (%d)", wIndex, wIndex);
-	dbg_rh("wLength      : 0x%04X (%d)", wLength, wLength);
+#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 20)
+	bmRType_bReq = cmd->bRequestType | (cmd->bRequest << 8);
+        wValue = le16_to_cpu(cmd->wValue);
+        wIndex = le16_to_cpu(cmd->wIndex);
+        wLength = le16_to_cpu(cmd->wLength);
+#else
+        bmRType_bReq = cmd->requesttype | (cmd->request << 8);
+        wValue = le16_to_cpu(cmd->value);
+        wIndex = le16_to_cpu(cmd->index);
+        wLength = le16_to_cpu(cmd->length);
+#endif
+									
+	dbg_rh("bmRType_bReq : 0x%04x (%d)", bmRType_bReq, bmRType_bReq);
+	dbg_rh("wValue       : 0x%04x (%d)", wValue, wValue);
+	dbg_rh("wIndex       : 0x%04x (%d)", wIndex, wIndex);
+	dbg_rh("wLength      : 0x%04x (%d)", wLength, wLength);
 	
 	switch (bmRType_bReq) {
 		
@@ -2050,13 +3308,11 @@
 		if (wIndex == 1) {
 			*((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_1);
 			*((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_1);
-		}
-		else if (wIndex == 2) {
+		} else if (wIndex == 2) {
 			*((__u16*)data) = cpu_to_le16(hc->rh.prev_wPortStatus_2);
 			*((__u16*)data + 1) = cpu_to_le16(hc->rh.wPortChange_2);
-		}
-		else {
-			dbg_rh("RH_GET_STATUS whith invalid wIndex !!");
+		} else {
+			dbg_rh("RH_GET_STATUS whith invalid wIndex!");
 			OK(0);
 		}
 		
@@ -2081,9 +3337,10 @@
 		case (RH_PORT_ENABLE):
 			if (wIndex == 1) {
 
-				dbg_rh("trying to do disable of port 1");
+				dbg_rh("trying to do disable port 1");
 
 				*R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, yes);
+
 				while (hc->rh.prev_wPortStatus_1 &
 				       IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes));
 				*R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
@@ -2091,9 +3348,10 @@
 
 			} else if (wIndex == 2) {
 
-				dbg_rh("trying to do disable of port 2");
+				dbg_rh("trying to do disable port 2");
 				
 				*R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, yes);
+
 				while (hc->rh.prev_wPortStatus_2 &
 				       IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes));
 				*R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
@@ -2101,25 +3359,35 @@
 
 			} else {
 				dbg_rh("RH_CLEAR_FEATURE->RH_PORT_ENABLE "
-				       "with invalid wIndex == %d!!", wIndex);
+				       "with invalid wIndex == %d!", wIndex);
 			}			
 				
 			OK (0);
 		case (RH_PORT_SUSPEND):
-			/* Opposite to suspend should be resume, so well do a resume */
+			/* Opposite to suspend should be resume, so we'll do a resume. */
+                        /* FIXME: USB 1.1, 11.16.2.2 says:
+                           "Clearing the PORT_SUSPEND feature causes a host-initiated resume
+                           on the specified port. If the port is not in the Suspended state,
+                           the hub should treat this request as a functional no-operation."
+                           Shouldn't we check if the port is in a suspended state before
+                           resuming? */
+                        
+                        /* Make sure the controller isn't busy. */
+                        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
 			if (wIndex == 1) {
 				*R_USB_COMMAND =
 					IO_STATE(R_USB_COMMAND, port_sel, port1) |
-					IO_STATE(R_USB_COMMAND, port_cmd, resume)|
+					IO_STATE(R_USB_COMMAND, port_cmd, resume) |
 					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
 			} else if (wIndex == 2) {
 				*R_USB_COMMAND =
 					IO_STATE(R_USB_COMMAND, port_sel, port2) |
-					IO_STATE(R_USB_COMMAND, port_cmd, resume)|
+					IO_STATE(R_USB_COMMAND, port_cmd, resume) |
 					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
 			} else {
 				dbg_rh("RH_CLEAR_FEATURE->RH_PORT_SUSPEND "
-				       "with invalid wIndex == %d!!", wIndex);
+				       "with invalid wIndex == %d!", wIndex);
 			}
 			
 			OK (0);
@@ -2129,26 +3397,22 @@
 			
 			if (wIndex == 1) {
 				hc->rh.wPortChange_1 &= ~(1 << RH_PORT_CONNECTION);
-			}
-			else if (wIndex == 2) {
+			} else if (wIndex == 2) {
 				hc->rh.wPortChange_2 &= ~(1 << RH_PORT_CONNECTION);
-			}
-			else {
+			} else {
 				dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_CONNECTION "
-				       "with invalid wIndex == %d!!", wIndex);
+				       "with invalid wIndex == %d!", wIndex);
 			}
 
 			OK (0);
 		case (RH_C_PORT_ENABLE):
 			if (wIndex == 1) {
 				hc->rh.wPortChange_1 &= ~(1 << RH_PORT_ENABLE);
-			}
-			else if (wIndex == 2) {
+			} else if (wIndex == 2) {
 				hc->rh.wPortChange_2 &= ~(1 << RH_PORT_ENABLE);
-			}
-			else {
+			} else {
 				dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_ENABLE "
-				       "with invalid wIndex == %d!!", wIndex);
+				       "with invalid wIndex == %d!", wIndex);
 			}
 			OK (0);
 		case (RH_C_PORT_SUSPEND):
@@ -2159,15 +3423,11 @@
 		case (RH_C_PORT_RESET):
 			if (wIndex == 1) {
 				hc->rh.wPortChange_1 &= ~(1 << RH_PORT_RESET);
-			}
-			else if (wIndex == 2) {
-				dbg_rh("This is wPortChange before clear: 0x%04X", hc->rh.wPortChange_2);
-				
+			} else if (wIndex == 2) {
 				hc->rh.wPortChange_2 &= ~(1 << RH_PORT_RESET);
-				dbg_rh("This is wPortChange after clear: 0x%04X", hc->rh.wPortChange_2);
 			} else {
 				dbg_rh("RH_CLEAR_FEATURE->RH_C_PORT_RESET "
-				       "with invalid index == %d!!", wIndex);
+				       "with invalid index == %d!", wIndex);
 			}
 
 			OK (0);
@@ -2178,6 +3438,10 @@
 	case RH_SET_FEATURE | RH_OTHER | RH_CLASS:
 		switch (wValue) {
 		case (RH_PORT_SUSPEND):
+
+                        /* Make sure the controller isn't busy. */
+                        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
 			if (wIndex == 1) {
 				*R_USB_COMMAND =
 					IO_STATE(R_USB_COMMAND, port_sel, port1) |
@@ -2189,76 +3453,150 @@
 					IO_STATE(R_USB_COMMAND, port_cmd, suspend) |
 					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
 			} else {
-				dbg_rh("RH_SET_FEATURE->RH_C_PORT_SUSPEND "
-				       "with invalid wIndex == %d!!", wIndex);
+				dbg_rh("RH_SET_FEATURE->RH_PORT_SUSPEND "
+				       "with invalid wIndex == %d!", wIndex);
 			}			
 
 			OK (0);
 		case (RH_PORT_RESET):
 			if (wIndex == 1) {
-				int port1_retry;
 				
-			port1_redo:
+			port_1_reset:
 				dbg_rh("Doing reset of port 1");
+                                
+                                /* Make sure the controller isn't busy. */
+                                while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
 				*R_USB_COMMAND =
+					IO_STATE(R_USB_COMMAND, port_sel, port1) | 
 					IO_STATE(R_USB_COMMAND, port_cmd, reset) |
-					IO_STATE(R_USB_COMMAND, port_sel, port1);
-
-				/* We must once again wait at least 10ms for the device to recover */
+					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
 
-				port1_retry = 0;
-				while (!((*((volatile __u16 *)&hc->rh.prev_wPortStatus_1)) &
-					 IO_STATE(R_USB_RH_PORT_STATUS_1,
-						  enabled, yes))) {
-					printk(""); if (port1_retry++ >= 10000) {goto port1_redo;}
-				}
-				
-				/* This only seems to work if we use printk,
-				   not even schedule() works !!! WHY ?? */
+                                /* We must wait at least 10 ms for the device to recover.
+                                   15 ms should be enough. */
+                                udelay(15000);
+                                
+                                /* Wait for reset bit to go low (should be done by now). */
+                                while (hc->rh.prev_wPortStatus_1 &
+                                       IO_STATE(R_USB_RH_PORT_STATUS_1, reset, yes));
+                                
+                                /* If the port status is
+                                   1) connected and enabled then there is a device and everything is fine
+                                   2) neither connected nor enabled then there is no device, also fine
+                                   3) connected and not enabled then we try again
+                                   (Yes, there are other port status combinations besides these.) */
+
+                                if ((hc->rh.prev_wPortStatus_1 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
+                                    (hc->rh.prev_wPortStatus_1 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
+                                        dbg_rh("Connected device on port 1, but port not enabled?"
+                                               " Trying reset again.");
+                                        goto port_2_reset;
+                                }
+
+                                /* Diagnostic printouts. */
+                                if ((hc->rh.prev_wPortStatus_1 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_1, connected, no)) &&
+                                    (hc->rh.prev_wPortStatus_1 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no))) {
+                                        dbg_rh("No connected device on port 1");
+                                } else if ((hc->rh.prev_wPortStatus_1 & 
+                                            IO_STATE(R_USB_RH_PORT_STATUS_1, connected, yes)) &&
+                                           (hc->rh.prev_wPortStatus_1 & 
+                                            IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, yes))) {
+                                        dbg_rh("Connected device on port 1, port 1 enabled");
+                                }
 
-				udelay(15000);
-			}
-			else if (wIndex == 2) {
-				int port2_retry;
-				
-			port2_redo:
+			} else if (wIndex == 2) {
+                                
+			port_2_reset:
 				dbg_rh("Doing reset of port 2");
 				
+                                /* Make sure the controller isn't busy. */
+                                while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+                                /* Issue the reset command. */
 				*R_USB_COMMAND =
+					IO_STATE(R_USB_COMMAND, port_sel, port2) |
 					IO_STATE(R_USB_COMMAND, port_cmd, reset) |
-					IO_STATE(R_USB_COMMAND, port_sel, port2);
-
-				/* We must once again wait at least 10ms for the device to recover */
+					IO_STATE(R_USB_COMMAND, ctrl_cmd, nop);
 
-				port2_retry = 0;
-				while (!((*((volatile __u16 *)&hc->rh.prev_wPortStatus_2)) &
-					 IO_STATE(R_USB_RH_PORT_STATUS_2,
-						  enabled, yes))) {
-					printk(""); if (port2_retry++ >= 10000) {goto port2_redo;}
-				}
-				
-				/* This only seems to work if we use printk,
-				   not even schedule() works !!! WHY ?? */
+				/* We must wait at least 10 ms for the device to recover.
+                                   15 ms should be enough. */
+                                udelay(15000);
+                                
+                                /* Wait for reset bit to go low (should be done by now). */
+                                while (hc->rh.prev_wPortStatus_2 &
+                                       IO_STATE(R_USB_RH_PORT_STATUS_2, reset, yes));
+                                
+                                /* If the port status is
+                                   1) connected and enabled then there is a device and everything is fine
+                                   2) neither connected nor enabled then there is no device, also fine
+                                   3) connected and not enabled then we try again
+                                   (Yes, there are other port status combinations besides these.) */
+
+                                if ((hc->rh.prev_wPortStatus_2 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
+                                    (hc->rh.prev_wPortStatus_2 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
+                                        dbg_rh("Connected device on port 2, but port not enabled?"
+                                               " Trying reset again.");
+                                        goto port_2_reset;
+                                }
+
+                                /* Diagnostic printouts. */
+                                if ((hc->rh.prev_wPortStatus_2 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_2, connected, no)) &&
+                                    (hc->rh.prev_wPortStatus_2 & 
+                                     IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no))) {
+                                        dbg_rh("No connected device on port 2");
+                                } else if ((hc->rh.prev_wPortStatus_2 & 
+                                            IO_STATE(R_USB_RH_PORT_STATUS_2, connected, yes)) &&
+                                           (hc->rh.prev_wPortStatus_2 & 
+                                            IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, yes))) {
+                                        dbg_rh("Connected device on port 2, port 2 enabled");
+                                }
 
-				udelay(15000);
-			}
+			} else {
+				dbg_rh("RH_SET_FEATURE->RH_PORT_RESET with invalid wIndex = %d", wIndex);
+                        }
+                        
+                        /* Make sure the controller isn't busy. */
+                        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+			/* If all enabled ports were disabled the host controller goes down into
+                           started mode, so we need to bring it back into the running state.
+                           (This is safe even if it's already in the running state.) */
+                        *R_USB_COMMAND =
+                                IO_STATE(R_USB_COMMAND, port_sel, nop) |
+                                IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+                                IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
 
-			/* Try to bring the HC into running state */
-			*R_USB_COMMAND =
-				IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
-			
-			nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-			
 			dbg_rh("...Done");
 			OK(0);
 
 		case (RH_PORT_POWER):
 			OK (0);	/* port power ** */
 		case (RH_PORT_ENABLE):
-			/* There is no rh port enable command in the Etrax USB interface!!!! */
+			/* There is no port enable command in the host controller, so if the
+                           port is already enabled, we do nothing. If not, we reset the port
+                           (with an ugly goto). */
+
+                        if (wIndex == 1) {
+                                if (hc->rh.prev_wPortStatus_1 & 
+                                    IO_STATE(R_USB_RH_PORT_STATUS_1, enabled, no)) {
+                                        goto port_1_reset;
+                                }
+                        } else if (wIndex == 2) {
+                                if (hc->rh.prev_wPortStatus_2 & 
+                                    IO_STATE(R_USB_RH_PORT_STATUS_2, enabled, no)) {
+                                        goto port_2_reset;
+                                }
+                        } else {
+                                dbg_rh("RH_SET_FEATURE->RH_GET_STATUS with invalid wIndex = %d", wIndex);
+                        }
 			OK (0);
-
 		}
 		break;
 		
@@ -2283,8 +3621,10 @@
 						   data, wLength);
 			if (len > 0) {
 				OK(min(leni, len));
-			} else 
+			} else {
 				stat = -EPIPE;
+                        }
+                        
 		}
 		break;
 		
@@ -2309,33 +3649,56 @@
 	urb->status = stat;
 	urb->dev = NULL;
 	if (urb->complete) {
-		urb->complete (urb);
+		urb->complete(urb);
 	}
 	DBFEXIT;
 	
 	return 0;
 }
 
+static void
+etrax_usb_bulk_eot_timer_func(unsigned long dummy)
+{
+        /* Because of a race condition in the top half, we might miss a bulk eot.
+           This timer "simulates" a bulk eot if we don't get one for a while, hopefully
+           correcting the situation. */
+        dbg_bulk("bulk_eot_timer timed out.");
+        etrax_usb_hc_bulk_eot_interrupt(1);
+}
+
 static int __init etrax_usb_hc_init(void)
 {
 	static etrax_hc_t *hc;
 	struct usb_bus *bus;
 	struct usb_device *usb_rh;
-	
+	int i;
+        
 	DBFENTER;
 
-	info("ETRAX 100LX USB-HCD %s (c) 2001 Axis Communications AB\n", usb_hcd_version);
-	
-	hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL);
+	info("ETRAX 100LX USB-HCD %s (c) 2001-2003 Axis Communications AB\n", usb_hcd_version);
+
+ 	hc = kmalloc(sizeof(etrax_hc_t), GFP_KERNEL);
+	assert(hc != NULL);
 
 	/* We use kmem_cache_* to make sure that all DMA desc. are dword aligned */
-	usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0, 0, 0, 0);
-	if (!usb_desc_cache) {
-		panic("USB Desc Cache allocation failed !!!\n");
-	}
-	
+        /* Note that we specify sizeof(USB_EP_Desc_t) as the size, but also allocate
+           SB descriptors from this cache. This is ok since sizeof(USB_EP_Desc_t) ==
+           sizeof(USB_SB_Desc_t). */
+
+	usb_desc_cache = kmem_cache_create("usb_desc_cache", sizeof(USB_EP_Desc_t), 0, 
+                                           SLAB_HWCACHE_ALIGN, 0, 0);
+	assert(usb_desc_cache != NULL);
+	
+	top_half_reg_cache = kmem_cache_create("top_half_reg_cache", 
+					       sizeof(usb_interrupt_registers_t), 
+					       0, SLAB_HWCACHE_ALIGN, 0, 0);
+	assert(top_half_reg_cache != NULL);
+
 	etrax_usb_bus = bus = usb_alloc_bus(&etrax_usb_device_operations);
 	hc->bus = bus;
+#if LINUX_VERSION_CODE >= KERNEL_VERSION (2, 4, 20)
+	bus->bus_name="ETRAX 100LX";
+#endif
 	bus->hcpriv = hc;
 
 	/* Initalize RH to the default address.
@@ -2350,17 +3713,50 @@
 	hc->rh.prev_wPortStatus_2 = 0;
 
 	/* Initialize the intr-traffic flags */
+        /* FIXME: This isn't used. (Besides, the error field isn't initialized.) */
 	hc->intr.sleeping = 0;
 	hc->intr.wq = NULL;
 
-	/* Initially all ep's are free except ep 0 */
-	ep_usage_bitmask = 0;
-	set_bit(0, (void *)&ep_usage_bitmask);
-	ep_really_active = 0;
-	ep_out_traffic = 0;
-
-	memset(URB_List, 0, sizeof(URB_List));
+	epid_usage_bitmask = 0;
+	epid_out_traffic = 0;
 
+        /* Mark the invalid epid as being used. */
+	set_bit(INVALID_EPID, (void *)&epid_usage_bitmask);
+        *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, INVALID_EPID);
+        nop();
+        /* The valid bit should still be set ('invalid' is in our world; not the hardware's). */
+        *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, yes) |
+			   IO_FIELD(R_USB_EPT_DATA, max_len, 1));
+
+	/* Mark the dummy epid as being used. */
+	set_bit(DUMMY_EPID, (void *)&epid_usage_bitmask);
+        *R_USB_EPT_INDEX = IO_FIELD(R_USB_EPT_INDEX, value, DUMMY_EPID);
+        nop();
+        *R_USB_EPT_DATA = (IO_STATE(R_USB_EPT_DATA, valid, no) |
+			   IO_FIELD(R_USB_EPT_DATA, max_len, 1));
+
+        /* Initialize the urb list by initiating a head for each list. */
+        for (i = 0; i < NBR_OF_EPIDS; i++) {
+                INIT_LIST_HEAD(&urb_list[i]);
+        }
+	spin_lock_init(&urb_list_lock);
+
+        INIT_LIST_HEAD(&urb_unlink_list);
+                        
+
+        /* Initiate the bulk start timer. */
+        init_timer(&bulk_start_timer);
+        bulk_start_timer.expires = jiffies + BULK_START_TIMER_INTERVAL;
+        bulk_start_timer.function = etrax_usb_bulk_start_timer_func;
+        add_timer(&bulk_start_timer);
+
+
+        /* Initiate the bulk eot timer. */
+        init_timer(&bulk_eot_timer);
+        bulk_eot_timer.expires = jiffies + BULK_EOT_TIMER_INTERVAL;
+        bulk_eot_timer.function = etrax_usb_bulk_eot_timer_func;
+        add_timer(&bulk_eot_timer);
+        
 	/* This code should really be moved */
 
 	if (request_dma(USB_TX_DMA_NBR, "ETRAX 100LX built-in USB (Tx)")) {
@@ -2375,48 +3771,41 @@
 		etrax_usb_hc_cleanup();
 		DBFEXIT;
 		return -1;
-	}	
-#if 0  /* Moved to head.S */
-	*R_GEN_CONFIG = genconfig_shadow =
-		(genconfig_shadow & ~(IO_MASK(R_GEN_CONFIG, usb1) |
-				      IO_MASK(R_GEN_CONFIG, usb2) |
-				      IO_MASK(R_GEN_CONFIG, dma8) |
-				      IO_MASK(R_GEN_CONFIG, dma9))) |
-		IO_STATE(R_GEN_CONFIG, dma8, usb) |
-		IO_STATE(R_GEN_CONFIG, dma9, usb)
-#ifdef CONFIG_ETRAX_USB_HOST_PORT1
-		| IO_STATE(R_GEN_CONFIG, usb1, select)
-#endif
-#ifdef CONFIG_ETRAX_USB_HOST_PORT2
-		| IO_STATE(R_GEN_CONFIG, usb2, select)
-#endif
-	;
-#endif
+	}
+
+        /* Set up the data structures for USB traffic. Note that this must be done before
+           any interrupt that relies on sane DMA list occurrs. */
+	init_rx_buffers();
+	init_tx_bulk_ep();
+	init_tx_ctrl_ep();
+	init_tx_intr_ep();
+	init_tx_isoc_ep();
+
 	
 	usb_register_bus(hc->bus);
 
-	/* We may have to set more bits, but these are the obvious ones */
+        /* Note that these interrupts are not used. */
 	*R_IRQ_MASK2_SET =
 		IO_STATE(R_IRQ_MASK2_SET, dma8_sub0_descr, set) |
 		IO_STATE(R_IRQ_MASK2_SET, dma8_sub1_descr, set) |
 		IO_STATE(R_IRQ_MASK2_SET, dma8_sub2_descr, set) |
 		IO_STATE(R_IRQ_MASK2_SET, dma8_sub3_descr, set);
 
+        /* Note that the dma9_descr interrupt is not used. */
 	*R_IRQ_MASK2_SET =
 		IO_STATE(R_IRQ_MASK2_SET, dma9_eop, set) |
 		IO_STATE(R_IRQ_MASK2_SET, dma9_descr, set);
 
+	/* FIXME: Enable iso_eof only when isoc traffic is running. */
 	*R_USB_IRQ_MASK_SET = 
-		IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set) |
-		IO_STATE(R_USB_IRQ_MASK_SET, ctl_eot, set) |
+                IO_STATE(R_USB_IRQ_MASK_SET, iso_eof, set) |
 		IO_STATE(R_USB_IRQ_MASK_SET, bulk_eot, set) |
-#ifdef ETRAX_USB_INTR_IRQ
-		IO_STATE(R_USB_IRQ_MASK_SET, intr_eot, set) |
-#endif
 		IO_STATE(R_USB_IRQ_MASK_SET, epid_attn, set) |
-		IO_STATE(R_USB_IRQ_MASK_SET, port_status, set);
+		IO_STATE(R_USB_IRQ_MASK_SET, port_status, set) |
+                IO_STATE(R_USB_IRQ_MASK_SET, ctl_status, set);
+
 	
-	if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_intr_top_half, 0,
+	if (request_irq(ETRAX_USB_HC_IRQ, etrax_usb_hc_interrupt_top_half, 0,
 			"ETRAX 100LX built-in USB (HC)", hc)) {
 		err("Could not allocate IRQ %d for USB", ETRAX_USB_HC_IRQ);
 		etrax_usb_hc_cleanup();
@@ -2440,16 +3829,50 @@
 		return -1;
 	}
 
-	/* Reset the USB interface (configures as HC) */
+        /* R_USB_COMMAND:
+           USB commands in host mode. The fields in this register should all be
+           written to in one write. Do not read-modify-write one field at a time. A
+           write to this register will trigger events in the USB controller and an
+           incomplete command may lead to unpredictable results, and in worst case
+           even to a deadlock in the controller.
+           (Note however that the busy field is read-only, so no need to write to it.) */
+
+        /* Check the busy bit before writing to R_USB_COMMAND. */
+
+        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+ 
+	/* Reset the USB interface. */
 	*R_USB_COMMAND =
-		IO_STATE(R_USB_COMMAND, ctrl_cmd, reset) |
-		IO_STATE(R_USB_COMMAND, port_cmd, reset);
-	
-	nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
-#if 1
-	/* Initate PSTART to all unallocatable bit times */
-	*R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 10000);
-#endif
+                IO_STATE(R_USB_COMMAND, port_sel, nop) |
+		IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+		IO_STATE(R_USB_COMMAND, ctrl_cmd, reset);
+	
+	/* Designer's Reference, p. 8 - 10 says we should Initate R_USB_FM_PSTART to 0x2A30 (10800),
+           to guarantee that control traffic gets 10% of the bandwidth, and periodic transfer may
+           allocate the rest (90%). This doesn't work though. Read on for a lenghty explanation.
+
+           While there is a difference between rev. 2 and rev. 3 of the ETRAX 100LX regarding the NAK
+           behaviour, it doesn't solve this problem. What happens is that a control transfer will not
+           be interrupted in its data stage when PSTART happens (the point at which periodic traffic
+           is started). Thus, if PSTART is set to 10800 and its IN or OUT token is NAKed until just before
+           PSTART happens, it will continue the IN/OUT transfer as long as it's ACKed. After it's done,
+           there may be too little time left for an isochronous transfer, causing an epid attention 
+           interrupt due to perror. The work-around for this is to let the control transfers run at the
+           end of the frame instead of at the beginning, and will be interrupted just fine if it doesn't
+           fit into the frame. However, since there will *always* be a control transfer at the beginning
+           of the frame, regardless of what we set PSTART to, that transfer might be a 64-byte transfer
+           which consumes up to 15% of the frame, leaving only 85% for periodic traffic. The solution to
+           this would be to 'dummy allocate' 5% of the frame with the usb_claim_bandwidth function to make
+           sure that the periodic transfers that are inserted will always fit in the frame.
+
+           The idea was suggested that a control transfer could be split up into several 8 byte transfers,
+           so that it would be interrupted by PSTART, but since this can't be done for an IN transfer this
+           hasn't been implemented.
+
+           The value 11960 is chosen to be just after the SOF token, with a couple of bit times extra
+           for possible bit stuffing. */
+
+        *R_USB_FM_PSTART = IO_FIELD(R_USB_FM_PSTART, value, 11960);
 
 #ifdef CONFIG_ETRAX_USB_HOST_PORT1
 	*R_USB_PORT1_DISABLE = IO_STATE(R_USB_PORT1_DISABLE, disable, no);
@@ -2459,32 +3882,28 @@
 	*R_USB_PORT2_DISABLE = IO_STATE(R_USB_PORT2_DISABLE, disable, no);
 #endif
 	
-	*R_USB_COMMAND =
-		IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config) |
-		IO_STATE(R_USB_COMMAND, port_cmd, reset);
-	
-	nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
+        /* Configure the USB interface as a host controller. */
 	*R_USB_COMMAND =
-		IO_STATE(R_USB_COMMAND, port_sel, port1) |
-		IO_STATE(R_USB_COMMAND, port_cmd, reset);
-
-	nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+                IO_STATE(R_USB_COMMAND, port_sel, nop) |
+                IO_STATE(R_USB_COMMAND, port_cmd, reset) |
+		IO_STATE(R_USB_COMMAND, ctrl_cmd, host_config);
+
+        /* Note: Do not reset any ports here. Await the port status interrupts, to have a controlled
+           sequence of resetting the ports. If we reset both ports now, and there are devices
+           on both ports, we will get a bus error because both devices will answer the set address
+           request. */
 
-	/* Here we must wait at least 10ms so the device has time to recover */
-	udelay(15000);
+        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
-	init_rx_buffers();
-	init_tx_bulk_ep();
-	init_tx_ctrl_ep();
-	init_tx_intr_ep();
-
-	/* This works. It seems like the host_run command only has effect when a device is connected,
-	 i.e. it has to be done when a interrup */
+        /* Start processing of USB traffic. */
 	*R_USB_COMMAND =
+		IO_STATE(R_USB_COMMAND, port_sel, nop) |
+		IO_STATE(R_USB_COMMAND, port_cmd, reset) |
 		IO_STATE(R_USB_COMMAND, ctrl_cmd, host_run);
-	
-	nop(); while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
+
+        while (*R_USB_COMMAND & IO_MASK(R_USB_COMMAND, busy));
 
 	usb_rh = usb_alloc_dev(NULL, hc->bus);
 	hc->bus->root_hub = usb_rh;
@@ -2508,6 +3927,8 @@
 	free_dma(USB_RX_DMA_NBR);
 	usb_deregister_bus(etrax_usb_bus);
 
+        /* FIXME: call kmem_cache_destroy here? */
+
 	DBFEXIT;
 }
 
diff -urN linux-2.4.21-bk5/arch/cris/drivers/usb-host.h linux-2.4.21-bk6/arch/cris/drivers/usb-host.h
--- linux-2.4.21-bk5/arch/cris/drivers/usb-host.h	2001-08-15 01:22:15.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/drivers/usb-host.h	2003-07-10 02:54:58.000000000 -0700
@@ -5,27 +5,27 @@
 #include <linux/list.h>
 
 typedef struct USB_IN_Desc {
-	__u16 sw_len;
-	__u16 command;
-	unsigned long next;
-	unsigned long buf;
-	__u16 hw_len;
-	__u16 status;
+	volatile __u16 sw_len;
+	volatile __u16 command;
+	volatile unsigned long next;
+	volatile unsigned long buf;
+        volatile __u16 hw_len;
+	volatile __u16 status;
 } USB_IN_Desc_t;
 
 typedef struct USB_SB_Desc {
-	__u16 sw_len;
-	__u16 command;
-	unsigned long next;
-	unsigned long buf;
+	volatile __u16 sw_len;
+	volatile __u16 command;
+	volatile unsigned long next;
+	volatile unsigned long buf;
 	__u32 dummy;
 } USB_SB_Desc_t;
 
 typedef struct USB_EP_Desc {
-	__u16 hw_len;
-	__u16 command;
-	unsigned long sub;
-	unsigned long nep;
+	volatile __u16 hw_len;
+	volatile __u16 command;
+	volatile unsigned long sub;
+	volatile unsigned long next;
 	__u32 dummy;
 } USB_EP_Desc_t;
 
@@ -37,10 +37,10 @@
 	int interval;
 	int numports;
 	struct timer_list rh_int_timer;
-	__u16 wPortChange_1;
-	__u16 wPortChange_2;
-	__u16 prev_wPortStatus_1;
-	__u16 prev_wPortStatus_2;
+	volatile __u16 wPortChange_1;
+	volatile __u16 wPortChange_2;
+	volatile __u16 prev_wPortStatus_1;
+	volatile __u16 prev_wPortStatus_2;
 };
 
 struct etrax_usb_intr_traffic {
@@ -55,40 +55,77 @@
 	struct etrax_usb_intr_traffic intr;
 } etrax_hc_t;
 
-typedef enum {idle, eot, nodata}  etrax_usb_rx_state_t;
+typedef enum {
+        STARTED,
+        NOT_STARTED,
+        UNLINK
+} etrax_usb_urb_state_t;
+
+
 
 typedef struct etrax_usb_urb_priv {
+        /* The first_sb field is used for freeing all SB descriptors belonging
+           to an urb. The corresponding ep descriptor's sub pointer cannot be
+           used for this since the DMA advances the sub pointer as it processes
+           the sb list. */
 	USB_SB_Desc_t *first_sb;
+	/* The last_sb field referes to the last SB descriptor that belongs to
+           this urb. This is important to know so we can free the SB descriptors
+	   that ranges between first_sb and last_sb. */	
+	USB_SB_Desc_t *last_sb;
+
+        /* The rx_offset field is used in ctrl and bulk traffic to keep track
+           of the offset in the urb's transfer_buffer where incoming data should be
+           copied to. */
 	__u32 rx_offset;
-	etrax_usb_rx_state_t rx_state;
-	__u8 eot;
-	struct list_head ep_in_list;
-} etrax_urb_priv_t;
 
+	/* Counter used in isochronous transfers to keep track of the
+	   number of packets received/transmitted.  */
+        __u32 isoc_packet_counter;	
+        
+        /* This field is used to pass information about the urb's current state between
+           the various interrupt handlers (thus marked volatile). */
+	volatile etrax_usb_urb_state_t urb_state;
+
+	/* Connection between the submitted urb and ETRAX epid number */
+	__u8 epid;
+
+        /* The rx_data_list field is used for periodic traffic, to hold 
+           received data for later processing in the the complete_urb functions,
+           where the data us copied to the urb's transfer_buffer. Basically, we
+           use this intermediate storage because we don't know when it's safe to
+           reuse the transfer_buffer (FIXME?). */
+	struct list_head rx_data_list;
+} etrax_urb_priv_t;
 
-struct usb_reg_context
+/* This struct is for passing data from the top half to the bottom half. */
+typedef struct usb_interrupt_registers
 {
 	etrax_hc_t *hc;
 	__u32 r_usb_epid_attn;
 	__u8 r_usb_status;
-	__u32 r_usb_rh_port_status_1;
-	__u32 r_usb_rh_port_status_2;
+	__u16 r_usb_rh_port_status_1;
+	__u16 r_usb_rh_port_status_2;
 	__u32 r_usb_irq_mask_read;
+        __u32 r_usb_fm_number;
 	struct tq_struct usb_bh;
-#if 0
-	__u32 r_usb_ept_data[32];
-#endif
-};
+} usb_interrupt_registers_t;
 
-struct in_chunk
+/* This struct holds data we get from the rx descriptors for DMA channel 9
+   for periodic traffic (intr and isoc). */
+typedef struct rx_data
 {
 	void *data;
 	int length;
-	char epid;
 	struct list_head list;
-};
+} rx_data_t;
+
+typedef struct urb_entry
+{
+        urb_t *urb;
+	struct list_head list;
+} urb_entry_t;
 
-  
 /* --------------------------------------------------------------------------- 
    Virtual Root HUB 
    ------------------------------------------------------------------------- */
diff -urN linux-2.4.21-bk5/arch/cris/drivers/virtex.c linux-2.4.21-bk6/arch/cris/drivers/virtex.c
--- linux-2.4.21-bk5/arch/cris/drivers/virtex.c	2002-11-28 15:53:09.000000000 -0800
+++ linux-2.4.21-bk6/arch/cris/drivers/virtex.c	2003-07-10 02:54:58.000000000 -0700
@@ -7,7 +7,7 @@
 *!
 *!              The FPGA can be programmed by copying the bit-file to /dev/fpga.
 *!
-*!                cp fpga.bit > /dev/fpga
+*!                cat fpga.bit > /dev/fpga
 *!
 *!                Kernel log should look like:
 *!                  69900 bytes written
@@ -31,6 +31,9 @@
 *!  
 *! Jul 19 2002  Stefan Lundberg    Initial version.
 *! $Log: virtex.c,v $
+*! Revision 1.2  2003/02/24 07:50:30  fredriko
+*! Bugfixes and cleanups.
+*!
 *! Revision 1.1  2002/06/25 09:58:58  stefanl
 *! New FPGA driver for Platoon
 *!
@@ -40,7 +43,7 @@
 *! (C) Copyright 2002 Axis Communications AB, LUND, SWEDEN
 *!
 *!***************************************************************************/
-/* $Id: virtex.c,v 1.1 2002/06/25 09:58:58 stefanl Exp $ */
+/* $Id: virtex.c,v 1.2 2003/02/24 07:50:30 fredriko Exp $ */
 /****************** INCLUDE FILES SECTION ***********************************/
 
 #include <linux/module.h>
@@ -127,14 +130,20 @@
 
 void start_virtex_program(void)
 {
+  volatile unsigned int i=0;
   unsigned short reg_data=0;
 
   printk("Start writing to FPGA\n");
-  reg_data = SET_CS_BIT(reg_data); // FPGA unselected
+  reg_data = SET_CS_BIT(reg_data); // FPGA unselected, PROGRAM bit not set
+  WRITE_FPGA_PROG_REG(reg_data);
 
+  for(i=0;i<10;i++) { } // at least 300 ns loop
+  
   reg_data = SET_PROGRAM_BIT(reg_data);
   WRITE_FPGA_PROG_REG(reg_data);
 
+  for(i=0;i<10;i++) { } // at least 300 ns loop
+
   while(!READ_INIT); // Wait for init
   
   reg_data = SET_WRITE_BIT(reg_data);
@@ -304,6 +313,7 @@
   }
   if(copy_from_user(ptr, buf, count)) {
     printk("copy_from_user failed\n");
+    kfree(ptr);
     return -EFAULT;
   }
   
@@ -339,7 +349,7 @@
   switch (_IOC_NR(cmd)) {
     case VIRTEX_FPGA_WRITEREG:
       /* write to an FPGA register */
-      VIRTEX_DEBUG(printk("virtex wr %d %d\n", 
+      VIRTEX_DEBUG(printk("virtex wr 0x%x = 0x%x\n", 
                VIRTEX_FPGA_ARGREG(arg),
                VIRTEX_FPGA_ARGVALUE(arg)));
       
@@ -347,12 +357,12 @@
                              VIRTEX_FPGA_ARGVALUE(arg));
     case VIRTEX_FPGA_READREG:
     {
-      unsigned char val;
+      unsigned short val;
       /* read from an FPGA register */
-      VIRTEX_DEBUG(printk("virtex rd %d ", 
+      VIRTEX_DEBUG(printk("virtex rd 0x%x ", 
               VIRTEX_FPGA_ARGREG(arg)));
       val = virtex_readreg(VIRTEX_FPGA_ARGREG(arg));
-      VIRTEX_DEBUG(printk("= %d\n", val));
+      VIRTEX_DEBUG(printk("= 0x%x\n", val));
       return val;
     }					    
     default:
diff -urN linux-2.4.21-bk5/arch/cris/drivers/virtex.h linux-2.4.21-bk6/arch/cris/drivers/virtex.h
--- linux-2.4.21-bk5/arch/cris/drivers/virtex.h	2002-11-28 15:53:09.000000000 -0800
+++ linux-2.4.21-bk6/arch/cris/drivers/virtex.h	2003-07-10 02:54:58.000000000 -0700
@@ -1,7 +1,2 @@
-/* $Id: virtex.h,v 1.1 2002/06/25 09:58:58 stefanl Exp $ */
-
 int virtex_writereg(unsigned short theReg, unsigned short theValue);
 unsigned short virtex_readreg(unsigned short theReg);
-
-
-
diff -urN linux-2.4.21-bk5/arch/cris/kernel/Makefile linux-2.4.21-bk6/arch/cris/kernel/Makefile
--- linux-2.4.21-bk5/arch/cris/kernel/Makefile	2002-11-28 15:53:09.000000000 -0800
+++ linux-2.4.21-bk6/arch/cris/kernel/Makefile	2003-07-10 02:54:58.000000000 -0700
@@ -1,4 +1,3 @@
-# $Id: Makefile,v 1.10 2002/05/27 15:41:40 johana Exp $
 #
 # Makefile for the linux kernel.
 #
@@ -17,21 +16,24 @@
 
 O_TARGET := kernel.o
 
-export-objs := ksyms.o
+export-objs := crisksyms.o
 
 obj-y   := process.o signal.o entry.o traps.o irq.o \
            ptrace.o setup.o time.o sys_cris.o shadows.o \
 	   debugport.o semaphore.o
 
-obj-$(CONFIG_MODULES)    += ksyms.o
+obj-$(CONFIG_MODULES)    += crisksyms.o
 obj-$(CONFIG_ETRAX_KGDB) += kgdb.o
 obj-$(CONFIG_ETRAX_FAST_TIMER) += fasttimer.o
+obj-$(CONFIG_ETRAX_DEBUG_INTERRUPT) += debug.o
 
 # This dependency isn't caught by mkdep.  See entry.S.
 entry.o: entryoffsets.s
 
+# We don't want debug info for the dummy entryoffsets functions, and the
+# assembler gets confused when file directives are .if 0:d out.
 entryoffsets.s: entryoffsets.c
-	$(CC) $(CFLAGS) -S -c $<
+	$(CC) $(subst -g,,$(CFLAGS)) -S -c $<
 
 clean:
 
diff -urN linux-2.4.21-bk5/arch/cris/kernel/crisksyms.c linux-2.4.21-bk6/arch/cris/kernel/crisksyms.c
--- linux-2.4.21-bk5/arch/cris/kernel/crisksyms.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.21-bk6/arch/cris/kernel/crisksyms.c	2003-07-10 02:54:58.000000000 -0700
@@ -0,0 +1,104 @@
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/user.h>
+#include <linux/elfcore.h>
+#include <linux/sched.h>
+#include <linux/in6.h>
+#include <linux/interrupt.h>
+#include <linux/smp_lock.h>
+#include <linux/pm.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/tty.h>
+
+#include <asm/semaphore.h>
+#include <asm/processor.h>
+#include <asm/uaccess.h>
+#include <asm/checksum.h>
+#include <asm/io.h>
+#include <asm/hardirq.h>
+#include <asm/delay.h>
+#include <asm/irq.h>
+#include <asm/pgtable.h>
+#include <asm/fasttimer.h>
+
+extern void dump_thread(struct pt_regs *, struct user *);
+extern unsigned long get_cmos_time(void);
+extern void __Div(void);
+extern void __Mod(void);
+extern void __Udiv(void);
+extern void __Umod(void);
+extern void __ashrdi3(void);
+extern void iounmap(void *addr);
+
+/* Platform dependent support */
+EXPORT_SYMBOL(dump_thread);
+EXPORT_SYMBOL(enable_irq);
+EXPORT_SYMBOL(disable_irq);
+EXPORT_SYMBOL(kernel_thread);
+EXPORT_SYMBOL(get_cmos_time);
+EXPORT_SYMBOL(loops_per_usec);
+
+/* String functions */
+EXPORT_SYMBOL(memcmp);
+EXPORT_SYMBOL(memmove);
+EXPORT_SYMBOL(strtok);
+EXPORT_SYMBOL(strpbrk);
+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);
+
+/* Math functions */
+EXPORT_SYMBOL_NOVERS(__Div);
+EXPORT_SYMBOL_NOVERS(__Mod);
+EXPORT_SYMBOL_NOVERS(__Udiv);
+EXPORT_SYMBOL_NOVERS(__Umod);
+EXPORT_SYMBOL_NOVERS(__ashrdi3);
+
+/* Memory functions */
+EXPORT_SYMBOL(__ioremap);
+EXPORT_SYMBOL(iounmap);
+
+/* Semaphore functions */
+EXPORT_SYMBOL(__up);
+EXPORT_SYMBOL(__down);
+EXPORT_SYMBOL(__down_interruptible);
+EXPORT_SYMBOL(__down_trylock);
+
+/* Export shadow registers for the CPU I/O pins */
+EXPORT_SYMBOL(genconfig_shadow);
+EXPORT_SYMBOL(port_pa_data_shadow);
+EXPORT_SYMBOL(port_pa_dir_shadow);
+EXPORT_SYMBOL(port_pb_data_shadow);
+EXPORT_SYMBOL(port_pb_dir_shadow);
+EXPORT_SYMBOL(port_pb_config_shadow);
+EXPORT_SYMBOL(port_g_data_shadow);
+
+/* Userspace access functions */
+EXPORT_SYMBOL(__copy_user_zeroing);
+EXPORT_SYMBOL(__copy_user);
+
+/* Cache flush functions */
+EXPORT_SYMBOL(flush_etrax_cache);
+EXPORT_SYMBOL(prepare_rx_descriptor);
+
+#undef memcpy
+#undef memset
+extern void * memset(void *, int, __kernel_size_t);
+extern void * memcpy(void *, const void *, __kernel_size_t);
+EXPORT_SYMBOL_NOVERS(memcpy);
+EXPORT_SYMBOL_NOVERS(memset);
+
+#ifdef CONFIG_ETRAX_FAST_TIMER
+/* Fast timer functions */
+EXPORT_SYMBOL(fast_timer_list);
+EXPORT_SYMBOL(start_one_shot_timer);
+EXPORT_SYMBOL(del_fast_timer);
+EXPORT_SYMBOL(schedule_usleep);
+#endif
diff -urN linux-2.4.21-bk5/arch/cris/kernel/debug.c linux-2.4.21-bk6/arch/cris/kernel/debug.c
--- linux-2.4.21-bk5/arch/cris/kernel/debug.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.21-bk6/arch/cris/kernel/debug.c	2003-07-10 02:54:58.000000000 -0700
@@ -0,0 +1,213 @@
+/*
+ * arch/cris/kernel/debug.c
+ * Various debug routines:
+ * o Logging of interrupt enabling/disabling. /proc/debug_interrupt
+ *   gives result and toggles if it is enabled or not.
+ *
+ * Copyright (C) 2003 Axis Communications AB
+ */
+
+#include <linux/config.h>
+#include <asm/system.h>
+#include <linux/init.h>
+#include <linux/proc_fs.h>
+#include <asm/svinto.h>
+
+#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT
+#define LOG_INT_SIZE 8192
+#define DEBUG_INT_PROC_FILE "debug_interrupt"
+#define LOG_INT_SHOW_MIN_USEC 45000
+
+/* These are global and can be used to trig certain events. */
+int log_int_pos = 0;
+int log_int_size = LOG_INT_SIZE;
+int log_int_trig0_pos = 0;
+int log_int_trig1_pos = 0;
+
+int log_int_enable = 0; /* toggled every read of /proc/debug_interrupt */
+
+struct log_int_struct 
+{
+  unsigned long pc;
+  unsigned long ev_timer_data;
+};
+
+static struct log_int_struct log_ints[LOG_INT_SIZE];
+
+//static unsigned long prev_log_int_t = 0;
+static unsigned long prev_logged_ccr = 0;
+static unsigned long prev_ei_timer_data = 0;
+static unsigned long prev_di_timer_data = 0;
+
+#define CCR_EI_BIT 5
+enum {
+	INT_OLD_MASK = 0x01, INT_OLD_BIT = 0,
+	INT_NEW_MASK = 0x02, INT_NEW_BIT = 1,
+	INT_EI_CHANGE_MASK = 0x04, INT_EI_CHANGE_BIT = 2,
+	INT_ACTION_RESTORE = 0x08, INT_ACTION_RESTORE_BIT = 3,
+	INT_ACTION_MISSED = 0x10, INT_ACTION_MISSED_BIT = 4,
+	INT_ACTION_LONG = 0x20,  INT_ACTION_LONG_BIT = 5,
+
+	INT_OLD_EI = INT_OLD_MASK, INT_OLD_EI_BIT = INT_OLD_BIT,
+	INT_NEW_EI = INT_NEW_MASK, INT_NEW_EI_BIT = INT_NEW_BIT,
+	INT_EV_DI = INT_OLD_EI | INT_EI_CHANGE_MASK,
+	INT_EV_NOP_DI = 0,
+	INT_EV_EI = INT_NEW_EI | INT_EI_CHANGE_MASK,
+	INT_EV_NOP_EI = INT_OLD_EI | INT_NEW_EI, 
+	INT_EV_RESTORE_DI= INT_EV_DI | INT_EI_CHANGE_MASK | INT_ACTION_RESTORE,
+	INT_EV_RESTORE_EI= INT_EV_EI | INT_EI_CHANGE_MASK | INT_ACTION_RESTORE,
+	INT_EV_RESTORE_NOP_DI = INT_EV_NOP_DI | INT_ACTION_RESTORE,
+	INT_EV_RESTORE_NOP_EI = INT_EV_NOP_EI | INT_ACTION_RESTORE,
+};
+
+void log_int(unsigned long pc, unsigned long curr_ccr, unsigned long next_ccr)
+{
+	unsigned long t;
+	int ev;
+	static int no_change_cnt = 0;
+	
+	/* Just disable interrupts without logging here,
+	 * the caller will either do ei, di or restore
+	 */
+	__asm__ __volatile__ ("di" : : :"memory");
+	t = *R_TIMER_DATA;
+
+	if (curr_ccr & CCR_EI_MASK) {
+		prev_ei_timer_data = t;		
+		ev = INT_OLD_EI;
+	} else {
+		prev_di_timer_data = t;
+		if ( (((prev_ei_timer_data >> 8) & 0x000000FF) -
+		      ((prev_di_timer_data >> 8) & 0x000000FF)) & ~0x03)
+					    ev = INT_ACTION_LONG;
+		else
+			ev = 0;
+	}
+	if ((curr_ccr ^ prev_logged_ccr) & CCR_EI_MASK)
+		ev |= INT_ACTION_MISSED;
+	if (next_ccr & CCR_EI_MASK)
+		ev |= INT_NEW_EI;
+	if ((curr_ccr ^ next_ccr) & CCR_EI_MASK) {
+		ev |= INT_EI_CHANGE_MASK;
+		no_change_cnt = 0;
+	} else
+		no_change_cnt++;
+
+	prev_logged_ccr = next_ccr;
+	
+	if (log_int_enable &&
+	    ((ev & (INT_EI_CHANGE_MASK | INT_ACTION_MISSED | INT_ACTION_LONG))
+	     ||	(no_change_cnt < 40)) &&
+	    log_int_pos < LOG_INT_SIZE) {
+		int i;
+		i = log_int_pos;
+		log_int_pos++;
+		log_ints[i].pc = pc;
+		log_ints[i].ev_timer_data = (t & 0x00FFFFFF) |
+			((ev & 0xFF) << 24);
+
+	}
+//	__asm__ __volatile__ ("move %0,$ccr" : : "rm" (curr_ccr) : "memory");
+}
+void log_int_di(void)
+{
+  unsigned long pc;
+  unsigned long flags;
+  __asm__ __volatile__ ("move $srp,%0" : "=rm" (pc) : : "memory");
+  __asm__ __volatile__ ("move $ccr,%0" : "=rm" (flags) : : "memory");
+  log_int(pc, flags, 0);
+}
+
+void log_int_ei(void)
+{
+  unsigned long pc;
+  unsigned long flags;
+  __asm__ __volatile__ ("move $srp,%0" : "=rm" (pc) : : "memory");
+  __asm__ __volatile__ ("move $ccr,%0" : "=rm" (flags) : : "memory");
+  log_int(pc, flags, CCR_EI_MASK);
+}
+
+
+static int log_int_read_proc(char *page, char **start, off_t off, int count,
+			     int *eof, void *data)
+{
+	int i, len = 0;
+	off_t	begin = 0;	
+	int j, t0, t1, t, thigh, tlow, tdi0;
+	int di0, ei1;
+	len += sprintf(page + len, "trig0: %i trig1: %i\n", log_int_trig0_pos, log_int_trig1_pos);
+	for (i = 0; i < log_int_pos-1; i++) {
+
+		if (((log_ints[i].ev_timer_data >> 24) & INT_NEW_EI)) {
+			di0 = i;
+			while (di0 < log_int_pos-1 && (((log_ints[di0].ev_timer_data >> 24) & INT_NEW_EI)))
+				di0++;
+			ei1 = di0+1;
+			while (ei1 < log_int_pos-1 && (((log_ints[ei1].ev_timer_data >> 24) & INT_NEW_EI) == 0))
+				ei1++;
+			thigh = timer_data_to_ns(log_ints[ei1].ev_timer_data);
+			tlow = timer_data_to_ns(log_ints[di0].ev_timer_data);
+			tdi0 = timer_data_to_ns(log_ints[di0].ev_timer_data);
+			
+			t = thigh-tlow;
+			j = di0-1;		
+			if ((t > LOG_INT_SHOW_MIN_USEC) || (log_int_trig0_pos-30 < j && j < log_int_trig1_pos+30)) {
+
+			for (; j <= ei1; j++) {
+				t0 = ((log_ints[j+1].ev_timer_data & 0xFF) -
+				      (log_ints[j].ev_timer_data & 0xFF));
+				t1 = (((log_ints[j+1].ev_timer_data >> 8) & 0xFF) -
+				      ((log_ints[j].ev_timer_data >> 8) & 0xFF));
+				if (t1 == 0 || t1 == 1) {
+					if (t0 < 0)
+						t0 += 256;
+				} else {
+					if (t1 < 0)
+						t1 += 256;
+				}
+				thigh = timer_data_to_ns(log_ints[j+1].ev_timer_data);
+				tlow = timer_data_to_ns(log_ints[j].ev_timer_data);
+				t = thigh-tlow;
+				len += sprintf(page + len, "%4i PC %08lX-%08lX %08lX-%08lX %s high %i in %-6i ns %-7i to %-7i = %-5i ns, from first di %i ns %s\n",
+					       j, log_ints[j].pc, log_ints[j+1].pc,
+					       log_ints[j].ev_timer_data, log_ints[j+1].ev_timer_data,
+					       ((log_ints[j].ev_timer_data >> 24) & INT_NEW_EI)!= 0?"ei":"di", t1, t,
+					       tlow, thigh, thigh-tlow, thigh-tdi0,
+					       j==log_int_trig0_pos?"TRIG0":(j==log_int_trig1_pos?"TRIG1":""));
+				if (len+begin > off+count)
+					goto done;
+				if (len+begin < off) {
+					begin += len;
+					len = 0;
+				}
+			}
+			
+			len += sprintf(page + len,"\n");
+			i = ei1-2;
+			}
+			
+		}
+	
+	}
+	log_int_enable = 1;
+	log_int_pos = 0;
+	log_int_trig0_pos = 0;
+	log_int_trig1_pos = 0;
+	*eof = 1;
+done:
+	if (off >= len+begin)
+		return 0;
+	*start = page + (off-begin);
+	return ((count < begin+len-off) ? count : begin+len-off);	
+}
+
+static int __init
+log_int_init(void)
+{
+	create_proc_read_entry (DEBUG_INT_PROC_FILE, 0, 0, log_int_read_proc, NULL);
+	printk(KERN_INFO "/proc/" DEBUG_INT_PROC_FILE " size %i.\r\n",
+	       LOG_INT_SIZE);
+	return 0;
+}
+module_init(log_int_init);
+#endif /* CONFIG_ETRAX_DEBUG_INTERRUPT */
diff -urN linux-2.4.21-bk5/arch/cris/kernel/debugport.c linux-2.4.21-bk6/arch/cris/kernel/debugport.c
--- linux-2.4.21-bk5/arch/cris/kernel/debugport.c	2002-08-02 17:39:42.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/kernel/debugport.c	2003-07-10 02:54:58.000000000 -0700
@@ -12,6 +12,17 @@
  *    init_etrax_debug()
  *
  * $Log: debugport.c,v $
+ * Revision 1.9  2003/02/17 07:10:34  starvik
+ * Last merge was incomplete
+ *
+ * Revision 1.8  2003/02/17 06:59:00  starvik
+ * Merged printk corruption fix
+ *
+ * Revision 1.7.4.1  2003/01/27 10:21:57  starvik
+ * Solved the problem with corrupted debug output
+ *  * Wait until DMA, FIFO and pipe is empty before and after transmissions
+ *  * Buffer data until a FIFO flush can be triggered.
+ *
  * Revision 1.7  2002/04/23 15:35:50  bjornw
  * Cleaned up sercons struct and removed the waitkey ptr (2.4.19-pre)
  *
@@ -31,6 +42,7 @@
 #include <linux/console.h>
 #include <linux/init.h>
 #include <linux/major.h>
+#include <linux/delay.h>
 
 #include <asm/system.h>
 #include <asm/svinto.h>
@@ -94,6 +106,8 @@
 #define DEBUG_DMA_IRQ_CLR IO_STATE(R_IRQ_MASK2_CLR, dma4_descr, clr)
 #endif
 
+#define MIN_SIZE 32 /* Size that triggers the FIFO to flush characters to interface */
+
 /* Write a string of count length to the console (debug port) using DMA, polled
  * for completion. Interrupts are disabled during the whole process. Some
  * caution needs to be taken to not interfere with ttyS business on this port.
@@ -103,8 +117,11 @@
 console_write(struct console *co, const char *buf, unsigned int len)
 {
 	static struct etrax_dma_descr descr;
+	static struct etrax_dma_descr descr2;
+	static char tmp_buf[MIN_SIZE];
+	static int tmp_size = 0;
+
 	unsigned long flags; 
-	int in_progress;
 	
 #ifdef CONFIG_ETRAX_DEBUG_PORT_NULL
         /* no debug printout at all */
@@ -126,6 +143,37 @@
 	restore_flags(flags);
 	return;
 #endif
+	/* To make this work together with the real serial port driver
+	 * we have to make sure that everything is flushed when we leave
+	 * here. The following steps are made to assure this:
+	 * 1. Wait until DMA stops, FIFO is empty and serial port pipeline empty.
+	 * 2. Write at least half the FIFO to trigger flush to serial port.
+	 * 3. Wait until DMA stops, FIFO is empty and serial port pipeline empty.
+         */
+
+	/* Do we have enough characters to make the DMA/FIFO happy? */
+	if (tmp_size + len < MIN_SIZE)
+	{
+		int size = min((int)(MIN_SIZE - tmp_size),(int)len);
+		memcpy(&tmp_buf[tmp_size], buf, size);
+		tmp_size += size;
+		len -= size;
+        
+		/* Pad with space if complete line */
+		if (tmp_buf[tmp_size-1] == '\n')
+		{
+			memset(&tmp_buf[tmp_size-1], ' ', MIN_SIZE - tmp_size);
+			tmp_buf[MIN_SIZE - 1] = '\n';
+			tmp_size = MIN_SIZE;
+			len = 0;
+		}
+		else
+		{
+                  /* Wait for more characters */
+			restore_flags(flags);
+			return;
+		}
+	}
 
 	/* make sure the transmitter is enabled. 
 	 * NOTE: this overrides any setting done in ttySx, to 8N1, no auto-CTS.
@@ -134,37 +182,36 @@
 	 */
 
 	*DEBUG_TR_CTRL = 0x40;
+	while(*DEBUG_OCMD & 7); /* Until DMA is not running */
+	while(*DEBUG_STATUS & 0x7f); /* wait until output FIFO is empty as well */
+	udelay(200); /* Wait for last two characters to leave the serial transmitter */
 
-	/* if the tty has some ongoing business, remember it */
-
-	in_progress = *DEBUG_OCMD & 7;
-
-	if(in_progress) {
-		/* wait until the output dma channel is ready */
-		
-		while(*DEBUG_OCMD & 7) /* nothing */ ;
+	if (tmp_size)
+	{
+		descr.ctrl = len ?  0 : d_eop | d_wait | d_eol;
+		descr.sw_len = tmp_size;
+		descr.buf = virt_to_phys(tmp_buf);
+		descr.next = virt_to_phys(&descr2);
+		descr2.ctrl = d_eop | d_wait | d_eol;
+		descr2.sw_len = len;
+		descr2.buf = virt_to_phys((char*)buf);
+	}
+	else
+	{
+		descr.ctrl = d_eop | d_wait | d_eol;
+		descr.sw_len = len;
+		descr.buf = virt_to_phys((char*)buf);
 	}
 
-	descr.ctrl = d_eol;
-	descr.sw_len = len;
-	descr.buf = __pa(buf);
-
-	*DEBUG_FIRST = __pa(&descr); /* write to R_DMAx_FIRST */
+	*DEBUG_FIRST = virt_to_phys(&descr); /* write to R_DMAx_FIRST */
 	*DEBUG_OCMD = 1;       /* dma command start -> R_DMAx_CMD */
 
 	/* wait until the output dma channel is ready again */
+	while(*DEBUG_OCMD & 7);
+	while(*DEBUG_STATUS & 0x7f);
+	udelay(200);
 
-	while(*DEBUG_OCMD & 7) /* nothing */;
-
-	/* clear pending interrupts so we don't get a surprise below */
-
-	if(in_progress)
-		*DEBUG_OCLRINT = 2;  /* only clear EOP, leave DESCR for the tty */
-	else
-		*DEBUG_OCLRINT = 3;  /* clear both EOP and DESCR */
-
-	while(*DEBUG_STATUS & 0x7f); /* wait until output FIFO is empty as well */
-
+	tmp_size = 0;
 	restore_flags(flags);
 }
 
diff -urN linux-2.4.21-bk5/arch/cris/kernel/entry.S linux-2.4.21-bk6/arch/cris/kernel/entry.S
--- linux-2.4.21-bk5/arch/cris/kernel/entry.S	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/kernel/entry.S	2003-07-10 02:54:58.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: entry.S,v 1.40 2002/12/02 08:11:41 starvik Exp $
+/* $Id: entry.S,v 1.42 2003/07/07 09:00:50 johana Exp $
  *
  *  linux/arch/cris/entry.S
  *
@@ -7,6 +7,12 @@
  *  Authors:	Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: entry.S,v $
+ *  Revision 1.42  2003/07/07 09:00:50  johana
+ *  Added CONFIG_ETRAX_DEBUG_INTERRUPT support.
+ *
+ *  Revision 1.41  2003/06/17 13:57:50  starvik
+ *  Merge of Linux 2.4.21
+ *
  *  Revision 1.40  2002/12/02 08:11:41  starvik
  *  Merge of Linux 2.4.20
  *
@@ -358,6 +364,12 @@
 	
 _Rexit:
 	;; this epilogue MUST match the prologues in multiple_interrupt, irq.h and ptregs.h
+#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT
+	move.d $pc,$r10
+	move $ccr,$r11
+	move.d	[$sp + LDCCR], $r12; regs->dccr	
+	jsr log_int
+#endif
 	pop	$r10		; frametype
 	bne	_RBFexit	; was not CRIS_FRAME_NORMAL, handle otherwise
 	addq	4, $sp		; skip orig_r10, in delayslot
@@ -449,6 +461,22 @@
 	push	$srp		         ; we keep the old/new PC on the stack 
 	add.d	$r12, $r10		 ; r10 = current tasks tss
 	move	$dccr, [$r10+LTHREAD_DCCR] ; save irq enable state
+#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT
+	push $r9
+	push $r10
+	push $r11
+	push $r12
+	push $r13
+	move.d $pc,$r10
+	move $ccr,$r11
+	clear.d $r12
+	jsr log_int
+	pop $r13
+	pop $r12
+	pop $r11
+	pop $r10
+	pop $r9
+#endif
 	di
 
 	move	$usp, [$r10+LTHREAD_USP] ; save user-mode stackpointer
diff -urN linux-2.4.21-bk5/arch/cris/kernel/fasttimer.c linux-2.4.21-bk6/arch/cris/kernel/fasttimer.c
--- linux-2.4.21-bk5/arch/cris/kernel/fasttimer.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/kernel/fasttimer.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,10 +1,16 @@
-/* $Id: fasttimer.c,v 1.5 2002/10/15 06:21:39 starvik Exp $
+/* $Id: fasttimer.c,v 1.7 2003/04/01 14:12:07 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.7  2003/04/01 14:12:07  starvik
+ * Added loglevel for lots of printks
+ *
+ * Revision 1.6  2003/02/10 17:05:44  pkj
+ * Made fast_timer_pending() public.
+ *
  * Revision 1.5  2002/10/15 06:21:39  starvik
  * Added call to init_waitqueue_head
  *
@@ -63,7 +69,7 @@
  * 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,2001,2002,2003 Axis Communications AB, Lund, Sweden
  */
 
 #include <linux/errno.h>
@@ -118,7 +124,7 @@
 static int fast_timer_is_init = 0;
 static int fast_timer_ints = 0;
 
-static struct fast_timer *fast_timer_list = NULL;
+struct fast_timer *fast_timer_list = NULL;
 
 #ifdef DEBUG_LOG_INCLUDED
 #define DEBUG_LOG_MAX 128
@@ -313,7 +319,8 @@
     {
       if (tmp == t)
       {
-        printk("timer name: %s data: 0x%08lX already in list!\n", name, data);
+        printk(KERN_WARNING
+	       "timer name: %s data: 0x%08lX already in list!\n", name, data);
         sanity_failed++;
         return;
       }
@@ -379,11 +386,6 @@
   restore_flags(flags);
 } /* start_one_shot_timer */
 
-static inline int fast_timer_pending (const struct fast_timer * t)
-{
-  return (t->next != NULL) || (t->prev != NULL) || (t == fast_timer_list);
-}
-
 static inline int detach_fast_timer (struct fast_timer *t)
 {
   struct fast_timer *next, *prev;
@@ -770,7 +772,7 @@
       cli();
       if (t->next != nextt)
       {
-        printk("timer removed!\n");
+        printk(KERN_WARNING "timer removed!\n");
       }
       t = nextt;
     }
@@ -951,7 +953,7 @@
     int i;
 #endif
 
-    printk("fast_timer_init()\n");
+    printk(KERN_INFO "fast_timer_init()\n");
 
 #if 0 && defined(FAST_TIMER_TEST)
     for (i = 0; i <= TIMER0_DIV; i++)
@@ -971,7 +973,7 @@
     if(request_irq(TIMER1_IRQ_NBR, timer1_handler, SA_SHIRQ,
                    "fast timer int", NULL))
     {
-      printk("err: timer1 irq\n");
+      printk(KERN_CRIT "err: timer1 irq\n");
     }
     fast_timer_is_init = 1;
 #ifdef FAST_TIMER_TEST
diff -urN linux-2.4.21-bk5/arch/cris/kernel/head.S linux-2.4.21-bk6/arch/cris/kernel/head.S
--- linux-2.4.21-bk5/arch/cris/kernel/head.S	2002-08-02 17:39:42.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/kernel/head.S	2003-07-10 02:54:58.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: head.S,v 1.44 2002/02/05 15:39:57 bjornw Exp $
+/* $Id: head.S,v 1.47 2003/07/08 09:53:35 starvik Exp $
  * 
  * Head of the kernel - alter with care
  *
@@ -7,6 +7,15 @@
  * Authors:	Bjorn Wesen (bjornw@axis.com)
  * 
  * $Log: head.S,v $
+ * Revision 1.47  2003/07/08 09:53:35  starvik
+ * Corrected last CVS log entry
+ *
+ * Revision 1.46  2003/05/15 08:07:51  gunnard
+ * Removed serial port DMA when using AGDB.
+ *
+ * Revision 1.45  2003/04/28 05:31:05  starvik
+ * Added section attributes
+ *
  * Revision 1.44  2002/02/05 15:39:57  bjornw
  * Oops.. non-CRAMFS_MAGIC should jump over the copying, not into it...
  *
@@ -311,7 +320,7 @@
 
 	;; Put this in a suitable section where we can reclaim storage
 	;; after init.
-	.section ".text.init"
+	.section ".text.init", "ax"
 _inflash:
 #ifdef CONFIG_ETRAX_ETHERNET	
 	;; Start MII clock to make sure it is running when tranceiver is reset
@@ -543,14 +552,24 @@
 #if (!defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT0)) \
 	&& !defined(CONFIG_DMA_MEMCPY)
 	; DMA channels 6 and 7 to ser0, kgdb doesnt want DMA
+#if (defined(CONFIG_ETRAX_AGDB))
+	; agdb only wants DMA out, no dma in
+	or.d	  IO_STATE (R_GEN_CONFIG, dma6, serial0),$r0
+#else
 	or.d	  IO_STATE (R_GEN_CONFIG, dma7, serial0)	\
 		| IO_STATE (R_GEN_CONFIG, dma6, serial0),$r0
 #endif
+#endif
 #if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT1)
 	; DMA channels 8 and 9 to ser1, kgdb doesnt want DMA
+#if (defined(CONFIG_ETRAX_AGDB))
+	  ; agdb only wants DMA out, no dma in
+	or.d	  IO_STATE (R_GEN_CONFIG, dma8, serial1),$r0
+#else
 	or.d	  IO_STATE (R_GEN_CONFIG, dma9, serial1)	\
 		| IO_STATE (R_GEN_CONFIG, dma8, serial1),$r0
 #endif	
+#endif	
 #ifdef CONFIG_DMA_MEMCPY
 	; 6/7 memory-memory DMA
 	or.d	  IO_STATE (R_GEN_CONFIG, dma7, intdma6)	\
@@ -561,19 +580,29 @@
 	or.w	IO_STATE (R_GEN_CONFIG, ser2, select),$r0
 #if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT2)
 	; DMA channels 2 and 3 to ser2, kgdb doesnt want DMA
+#if (defined(CONFIG_ETRAX_AGDB))
+	; agdb only wants DMA out, no dma in
+	or.d	  IO_STATE (R_GEN_CONFIG, dma2, serial2),$r0
+#else
 	or.d	  IO_STATE (R_GEN_CONFIG, dma3, serial2)	\
 		| IO_STATE (R_GEN_CONFIG, dma2, serial2),$r0
 #endif
 #endif
+#endif
 #if defined(CONFIG_ETRAX_SERIAL_PORT3) || defined(CONFIG_ETRAX_SYNCHRONOUS_SERIAL_PORT1)
 	; Enable serial port 3
 	or.w	IO_STATE (R_GEN_CONFIG, ser3, select),$r0
 #if !defined(CONFIG_ETRAX_KGDB) || !defined(CONFIG_ETRAX_DEBUG_PORT3)
 	; DMA channels 4 and 5 to ser3, kgdb doesnt want DMA
+#if (defined(CONFIG_ETRAX_AGDB))
+	; agdb only wants DMA out, no dma in
+	or.d	  IO_STATE (R_GEN_CONFIG, dma4, serial3),$r0
+#else
 	or.d	  IO_STATE (R_GEN_CONFIG, dma5, serial3)	\
 		| IO_STATE (R_GEN_CONFIG, dma4, serial3),$r0
 #endif
 #endif
+#endif
 #if defined(CONFIG_ETRAX_PARALLEL_PORT0) || defined(CONFIG_ETRAX_ETHERNET_LPSLAVE)
 	; parport 0 enabled using DMA 2/3
 	or.w	IO_STATE (R_GEN_CONFIG, par0, select),$r0
@@ -840,5 +869,5 @@
 swapper_pg_dir = 0xc0002000
 #endif
 
-	.section ".data.init"
+	.section ".data.init", "aw"
 #include "../lib/hw_settings.S"
diff -urN linux-2.4.21-bk5/arch/cris/kernel/irq.c linux-2.4.21-bk6/arch/cris/kernel/irq.c
--- linux-2.4.21-bk5/arch/cris/kernel/irq.c	2002-02-25 11:37:52.000000000 -0800
+++ linux-2.4.21-bk6/arch/cris/kernel/irq.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,8 +1,7 @@
-/* $Id: irq.c,v 1.18 2001/11/21 13:40:18 bjornw Exp $
- *
+/*
  *	linux/arch/cris/kernel/irq.c
  *
- *      Copyright (c) 2000,2001 Axis Communications AB
+ *      Copyright (c) 2000, 2001, 2002, 2003 Axis Communications AB
  *
  *      Authors: Bjorn Wesen (bjornw@axis.com)
  *
@@ -59,7 +58,7 @@
 disable_irq(unsigned int irq_nr)
 {
 	unsigned long flags;
-	
+
 	save_flags(flags);
 	cli();
 	mask_irq(irq_nr);
@@ -70,6 +69,7 @@
 enable_irq(unsigned int irq_nr)
 {
 	unsigned long flags;
+
 	save_flags(flags);
 	cli();
 	unmask_irq(irq_nr);
@@ -88,7 +88,8 @@
 	return 0;
 }
 
-irqvectptr irq_shortcuts[NR_IRQS]; /* vector of shortcut jumps after the irq prologue */
+/* vector of shortcut jumps after the irq prologue */
+irqvectptr irq_shortcuts[NR_IRQS];
 
 /* don't use set_int_vector, it bypasses the linux interrupt handlers. it is
  * global just so that the kernel gdb can use it.
@@ -104,8 +105,8 @@
 	etrax_irv->v[n + 0x20] = (irqvectptr)addr;
 }
 
-/* the breakpoint vector is obviously not made just like the normal irq handlers
- * but needs to contain _code_ to jump to addr.
+/* the breakpoint vector is obviously not made just like the normal irq
+ * handlers but needs to contain _code_ to jump to addr.
  *
  * the BREAK n instruction jumps to IBR + n * 8
  */
@@ -117,7 +118,7 @@
 	unsigned long *jaddr = (unsigned long *)(jinstr + 1);
 
 	/* if you don't know what this does, do not touch it! */
-	
+
 	*jinstr = 0x0d3f;
 	*jaddr = (unsigned long)addr;
 
@@ -163,18 +164,18 @@
 BUILD_IRQ(25, 0x2000000)
 /* IRQ 26-30 are reserved */
 BUILD_IRQ(31, 0x80000000)
- 
+
 /*
- * Pointers to the low-level handlers 
+ * Pointers to the low-level handlers
  */
 
 static void (*interrupt[NR_IRQS])(void) = {
 	NULL, NULL, IRQ2_interrupt, IRQ3_interrupt,
 	IRQ4_interrupt, IRQ5_interrupt, IRQ6_interrupt, IRQ7_interrupt,
 	IRQ8_interrupt, IRQ9_interrupt, IRQ10_interrupt, IRQ11_interrupt,
-	IRQ12_interrupt, IRQ13_interrupt, NULL, NULL,	
-	IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt,	
-	IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt,	
+	IRQ12_interrupt, IRQ13_interrupt, NULL, NULL,
+	IRQ16_interrupt, IRQ17_interrupt, IRQ18_interrupt, IRQ19_interrupt,
+	IRQ20_interrupt, IRQ21_interrupt, IRQ22_interrupt, IRQ23_interrupt,
 	IRQ24_interrupt, IRQ25_interrupt, NULL, NULL, NULL, NULL, NULL,
 	IRQ31_interrupt
 };
@@ -183,9 +184,9 @@
 	NULL, NULL, sIRQ2_interrupt, sIRQ3_interrupt,
 	sIRQ4_interrupt, sIRQ5_interrupt, sIRQ6_interrupt, sIRQ7_interrupt,
 	sIRQ8_interrupt, sIRQ9_interrupt, sIRQ10_interrupt, sIRQ11_interrupt,
-	sIRQ12_interrupt, sIRQ13_interrupt, NULL, NULL,	
-	sIRQ16_interrupt, sIRQ17_interrupt, sIRQ18_interrupt, sIRQ19_interrupt,	
-	sIRQ20_interrupt, sIRQ21_interrupt, sIRQ22_interrupt, sIRQ23_interrupt,	
+	sIRQ12_interrupt, sIRQ13_interrupt, NULL, NULL,
+	sIRQ16_interrupt, sIRQ17_interrupt, sIRQ18_interrupt, sIRQ19_interrupt,
+	sIRQ20_interrupt, sIRQ21_interrupt, sIRQ22_interrupt, sIRQ23_interrupt,
 	sIRQ24_interrupt, sIRQ25_interrupt, NULL, NULL, NULL, NULL, NULL,
 	sIRQ31_interrupt
 };
@@ -230,7 +231,7 @@
 
 	for (i = 0; i < NR_IRQS; i++) {
 		action = irq_action[i];
-		if (!action) 
+		if (!action)
 			continue;
 		len += sprintf(buf+len, "%2d: %10u %c %s",
 			i, kstat.irqs[0][i],
@@ -286,9 +287,9 @@
 }
 
 /* this function links in a handler into the chain of handlers for the
-   given irq, and if the irq has never been registred, the appropriate
-   handler is entered into the interrupt vector
-*/
+ * given irq, and if the irq has never been registred, the appropriate
+ * handler is entered into the interrupt vector
+ */
 
 int setup_etrax_irq(int irq, struct irqaction * new)
 {
@@ -322,37 +323,39 @@
 	*p = new;
 
 	if (!shared) {
-		/* if the irq wasn't registred before, enter it into the vector table
-		   and unmask it physically 
-		*/
+		/* if the irq wasn't registred before, enter it into the vector
+		 * table and unmask it physically
+		 */
 		set_int_vector(irq, interrupt[irq], sinterrupt[irq]);
 		unmask_irq(irq);
 	}
-	
+
 	restore_flags(flags);
 	return 0;
 }
 
 /* this function is called by a driver to register an irq handler
-   Valid flags:
-   SA_INTERRUPT -> it's a fast interrupt, handler called with irq disabled and
-                   no signal checking etc is performed upon exit
-   SA_SHIRQ -> the interrupt can be shared between different handlers, the handler
-                is required to check if the irq was "aimed" at it explicitely
-   SA_RANDOM -> the interrupt will add to the random generators entropy
-*/
+ * Valid flags:
+ *   SA_INTERRUPT: it's a fast interrupt, handler called with irq disabled and
+ *                 no signal checking etc is performed upon exit
+ *   SA_SHIRQ:     the interrupt can be shared between different handlers, the
+ *                 handler is required to check if the irq was "aimed" at it
+ *                 explicitely
+ *   SA_RANDOM:    the interrupt will add to the random generators entropy
+ */
 
-int request_irq(unsigned int irq, 
+int request_irq(unsigned int irq,
 		void (*handler)(int, void *, struct pt_regs *),
-		unsigned long irqflags, 
+		unsigned long irqflags,
 		const char * devname,
 		void *dev_id)
 {
 	int retval;
 	struct irqaction * action;
 
-	/* interrupts 0 and 1 are hardware breakpoint and NMI and we can't support
-	   these yet. interrupt 15 is the multiple irq, it's special. */
+	/* interrupts 0 and 1 are hardware breakpoint and NMI and we can't
+	 * support these yet. interrupt 15 is the multiple irq, it's special.
+	 */
 
 	if(irq < 2 || irq == 15 || irq >= NR_IRQS)
 		return -EINVAL;
@@ -362,7 +365,7 @@
 
 	/* allocate and fill in a handler structure and setup the irq */
 
-	action = (struct irqaction *)kmalloc(sizeof(struct irqaction), GFP_KERNEL);
+	action = kmalloc(sizeof *action, GFP_KERNEL);
 	if (!action)
 		return -ENOMEM;
 
@@ -379,7 +382,7 @@
 		kfree(action);
 	return retval;
 }
-		
+
 void free_irq(unsigned int irq, void *dev_id)
 {
 	struct irqaction * action, **p;
@@ -415,9 +418,9 @@
 	while(1);
 }
 
-/* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks and
-   setting the irq vector table to point to bad_interrupt ptrs.
-*/
+/* init_IRQ() is called by start_kernel and is responsible for fixing IRQ masks
+ * and setting the irq vector table to point to bad_interrupt ptrs.
+ */
 
 void system_call(void);  /* from entry.S */
 void do_sigtrap(void); /* from entry.S */
@@ -442,26 +445,27 @@
 
 	for(i = 0; i < NR_IRQS; i++)
 		irq_shortcuts[i] = NULL;
-        
-        for (i = 0; i < 256; i++)
-               etrax_irv->v[i] = weird_irq;
-
-        /* the entries in the break vector contain actual code to be
-           executed by the associated break handler, rather than just a jump
-           address. therefore we need to setup a default breakpoint handler
-           for all breakpoints */
+
+	for (i = 0; i < 256; i++)
+		etrax_irv->v[i] = weird_irq;
+
+	/* the entries in the break vector contain actual code to be
+	 * executed by the associated break handler, rather than just a jump
+	 * address. therefore we need to setup a default breakpoint handler
+	 * for all breakpoints
+	 */
 
 	for (i = 0; i < 16; i++)
-                set_break_vector(i, do_sigtrap);
-        
+		set_break_vector(i, do_sigtrap);
+
 	/* set all etrax irq's to the bad handlers */
 	for (i = 2; i < NR_IRQS; i++)
 		set_int_vector(i, bad_interrupt[i], 0);
-        
+
 	/* except IRQ 15 which is the multiple-IRQ handler on Etrax100 */
 
 	set_int_vector(15, multiple_interrupt, 0);
-	
+
 	/* 0 and 1 which are special breakpoint/NMI traps */
 
 	set_int_vector(0, hwbreakpoint, 0);
@@ -475,17 +479,17 @@
 
 	set_break_vector(13, system_call);
 
-        /* setup a breakpoint handler for debugging used for both user and
-           kernel mode debugging  (which is why it is not inside an ifdef
-           CONFIG_ETRAX_KGDB) */
-        set_break_vector(8, gdb_handle_breakpoint);
+	/* setup a breakpoint handler for debugging used for both user and
+	 * kernel mode debugging  (which is why it is not inside an ifdef
+	 * CONFIG_ETRAX_KGDB)
+	 */
+	set_break_vector(8, gdb_handle_breakpoint);
 
 #ifdef CONFIG_ETRAX_KGDB
 	/* setup kgdb if its enabled, and break into the debugger */
 	kgdb_init();
 	breakpoint();
 #endif
-
 }
 
 #if defined(CONFIG_PROC_FS) && defined(CONFIG_SYSCTL)
diff -urN linux-2.4.21-bk5/arch/cris/kernel/kgdb.c linux-2.4.21-bk6/arch/cris/kernel/kgdb.c
--- linux-2.4.21-bk5/arch/cris/kernel/kgdb.c	2002-11-28 15:53:09.000000000 -0800
+++ linux-2.4.21-bk6/arch/cris/kernel/kgdb.c	2003-07-10 02:54:58.000000000 -0700
@@ -18,6 +18,9 @@
 *! Jul 21 1999  Bjorn Wesen     eLinux port
 *!
 *! $Log: kgdb.c,v $
+*! Revision 1.8  2003/04/09 08:31:15  pkj
+*! Typo correction (taken from Linux 2.5).
+*!
 *! Revision 1.7  2002/07/12 09:14:56  bjornw
 *! Corrected typo
 *!
@@ -58,7 +61,7 @@
 *!
 *!---------------------------------------------------------------------------
 *!
-*! $Id: kgdb.c,v 1.7 2002/07/12 09:14:56 bjornw Exp $
+*! $Id: kgdb.c,v 1.8 2003/04/09 08:31:15 pkj Exp $
 *!
 *! (C) Copyright 1999, Axis Communications AB, LUND, SWEDEN
 *!
@@ -152,7 +155,7 @@
  *    (IPL too high, disabled, ...)
  *
  *  - The gdb stub is currently not reentrant, i.e. errors that happen therein
- *    (e.g. accesing invalid memory) may not be caught correctly. This could
+ *    (e.g. accessing invalid memory) may not be caught correctly. This could
  *    be removed in future by introducing a stack of struct registers.
  *
  */
@@ -1486,7 +1489,7 @@
   move.d   $r0,[reg+0x62]   ; Save the return address in BRP
   move     $usp,[reg+0x66]  ; USP
 
-;; get the serial character (from debugport.c) and check if its a ctrl-c
+;; get the serial character (from debugport.c) and check if it is a ctrl-c
 
   jsr getDebugChar
   cmp.b 3, $r10
diff -urN linux-2.4.21-bk5/arch/cris/kernel/process.c linux-2.4.21-bk6/arch/cris/kernel/process.c
--- linux-2.4.21-bk5/arch/cris/kernel/process.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/kernel/process.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: process.c,v 1.23 2002/10/14 18:29:27 johana Exp $
+/* $Id: process.c,v 1.24 2003/03/06 14:19:32 pkj Exp $
  * 
  *  linux/arch/cris/kernel/process.c
  *
@@ -8,6 +8,9 @@
  *  Authors:   Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: process.c,v $
+ *  Revision 1.24  2003/03/06 14:19:32  pkj
+ *  Use a cpu_idle() function identical to the one used by i386.
+ *
  *  Revision 1.23  2002/10/14 18:29:27  johana
  *  Call etrax_gpio_wake_up_check() from cpu_idle() to reduce gpio latency
  *  from ~15 ms to ~6 ms.
@@ -114,7 +117,17 @@
  * region by enable_hlt/disable_hlt.
  */
 
-static int hlt_counter=0;
+static int hlt_counter;
+
+/*
+ * Powermanagement idle function, if any..
+ */
+void (*pm_idle)(void);
+
+/*
+ * Power off function, if any
+ */
+void (*pm_power_off)(void);
 
 void disable_hlt(void)
 {
@@ -125,19 +138,42 @@
 {
 	hlt_counter--;
 }
-#ifdef CONFIG_ETRAX_GPIO
-void etrax_gpio_wake_up_check(void); /* drivers/gpio.c */
-#endif
 
-int cpu_idle(void *unused)
+/*
+ * We use this if we don't have any better
+ * idle routine..
+ */
+static void default_idle(void)
 {
-	while(1) {
-		current->counter = -100;
 #ifdef CONFIG_ETRAX_GPIO
-		/* This can reduce latency from 15 ms to 6 ms */
-		etrax_gpio_wake_up_check(); /* drivers/gpio.c */
+	extern void etrax_gpio_wake_up_check(void); /* drivers/gpio.c */
+
+	/* This can reduce latency from 15 ms to 6 ms */
+	etrax_gpio_wake_up_check(); /* drivers/gpio.c */
 #endif
+}
+
+/*
+ * 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 */
+	init_idle();
+	current->nice = 20;
+	current->counter = -100;
+
+	while(1) {
+		void (*idle)(void) = pm_idle;
+		if (!idle)
+			idle = default_idle;
+		while (!current->need_resched)
+			idle();
 		schedule();
+		check_pgt_cache();
 	}
 }
 
@@ -191,6 +227,8 @@
 
 void machine_power_off(void)
 {
+	if (pm_power_off)
+		pm_power_off();
 }
 
 /*
diff -urN linux-2.4.21-bk5/arch/cris/kernel/setup.c linux-2.4.21-bk6/arch/cris/kernel/setup.c
--- linux-2.4.21-bk5/arch/cris/kernel/setup.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/kernel/setup.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,9 +1,8 @@
-/* $Id: setup.c,v 1.27 2002/10/11 12:57:15 pkj Exp $
- *
+/*
  *  linux/arch/cris/kernel/setup.c
  *
  *  Copyright (C) 1995  Linus Torvalds
- *  Copyright (c) 2001  Axis Communications AB
+ *  Copyright (c) 2001, 2002, 2003  Axis Communications AB
  */
 
 /*
@@ -94,7 +93,8 @@
 		memory_start = (unsigned long) &_end;
 	} else {
 		/* otherwise the free area starts after the ROM filesystem */
-		printk("ROM fs in RAM, size %lu bytes\n", romfs_length);
+		printk(KERN_INFO "ROM fs in RAM, size %lu bytes\n",
+		       romfs_length);
 		memory_start = romfs_start + romfs_length;
 	}
 
@@ -169,9 +169,14 @@
 
 	*cmdline_p = command_line;
 
+#ifdef CONFIG_ETRAX_CMDLINE
+	strncpy(command_line, CONFIG_ETRAX_CMDLINE, COMMAND_LINE_SIZE);
+#elif defined(CONFIG_ETRAX_ROOT_DEVICE)
 	strncpy(command_line, "root=", COMMAND_LINE_SIZE);
 	strncpy(command_line+5, CONFIG_ETRAX_ROOT_DEVICE,
 			COMMAND_LINE_SIZE-5);
+#endif
+	command_line[COMMAND_LINE_SIZE - 1] = '\0';
 
 	/* Save command line copy for /proc/cmdline */
 	
@@ -180,7 +185,7 @@
 
 	/* give credit for the CRIS port */
 
-	printk("Linux/CRIS port on ETRAX 100LX (c) 2001, 2002 Axis Communications AB\n");
+	printk(KERN_INFO "Linux/CRIS port on ETRAX 100LX (c) 2001, 2002 Axis Communications AB\n");
 
 }
 
diff -urN linux-2.4.21-bk5/arch/cris/kernel/shadows.c linux-2.4.21-bk6/arch/cris/kernel/shadows.c
--- linux-2.4.21-bk5/arch/cris/kernel/shadows.c	2001-05-01 16:04:56.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/kernel/shadows.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,6 +1,5 @@
-/* $Id: shadows.c,v 1.2 2001/03/15 14:25:16 bjornw Exp $
- * 
- * Various shadow registers. Defines for these are in include/asm-etrax100/io.h
+/*
+ * Various shadow registers. Defines for these are in include/asm-cris/io.h
  */
 
 /* Shadows for internal Etrax-registers */
diff -urN linux-2.4.21-bk5/arch/cris/kernel/signal.c linux-2.4.21-bk6/arch/cris/kernel/signal.c
--- linux-2.4.21-bk5/arch/cris/kernel/signal.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/kernel/signal.c	2003-07-10 02:54:58.000000000 -0700
@@ -528,7 +528,7 @@
 			case -ERESTARTNOHAND:
 				/* ERESTARTNOHAND means that the syscall should only be
 				   restarted if there was no handler for the signal, and since
-				   we only get here if there is a handler, we dont restart */
+				   we only get here if there is a handler, we don't restart */
 				regs->r10 = -EINTR;
 				break;
 
diff -urN linux-2.4.21-bk5/arch/cris/kernel/sys_cris.c linux-2.4.21-bk6/arch/cris/kernel/sys_cris.c
--- linux-2.4.21-bk5/arch/cris/kernel/sys_cris.c	2001-07-26 15:10:06.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/kernel/sys_cris.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,5 +1,4 @@
-/* $Id: sys_cris.c,v 1.10 2001/06/27 21:16:15 hp Exp $
- *
+/*
  * linux/arch/cris/kernel/sys_cris.c
  *
  * This file contains various random system calls that
diff -urN linux-2.4.21-bk5/arch/cris/kernel/time.c linux-2.4.21-bk6/arch/cris/kernel/time.c
--- linux-2.4.21-bk5/arch/cris/kernel/time.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/kernel/time.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,9 +1,8 @@
-/* $Id: time.c,v 1.17 2002/11/15 14:49:24 oskarp Exp $
- *
+/*
  *  linux/arch/cris/kernel/time.c
  *
  *  Copyright (C) 1991, 1992, 1995  Linus Torvalds
- *  Copyright (C) 1999, 2000, 2001, 2002 Axis Communications AB
+ *  Copyright (C) 1999, 2000, 2001, 2002, 2003 Axis Communications AB
  *
  * 1994-07-02    Alan Modra
  *	fixed set_rtc_mmss, fixed time.year for >= 2000, new mktime
@@ -18,15 +17,16 @@
  * Linux/CRIS specific code:
  *
  * Authors:    Bjorn Wesen
- *             Johan Adolfsson  
+ *             Johan Adolfsson
+ *
  * 2002-03-04    Johan Adolfsson
- *      Use prescale timer at 25000 Hz instead of the baudrate timer at 
- *      19200 to get rid of the 64ppm to fast timer (and we get better 
+ *      Use prescale timer at 25000 Hz instead of the baudrate timer at
+ *      19200 to get rid of the 64ppm to fast timer (and we get better
  *      resolution within a jiffie as well.
  * 2002-03-05    Johan Adolfsson
  *      Use prescaler in do_slow_gettimeoffset() to get 1 us resolution (40ns)
  * 2002-09-06    Johan Adolfsson
- *      Handle lost ticks by checking wall_jiffies, more efficient code 
+ *      Handle lost ticks by checking wall_jiffies, more efficient code
  *      by using local vars and not the pointer argument.
  *
  */
@@ -103,6 +103,60 @@
 }
 
 
+/* Convert the clkdiv_low and clkdivb_high fiels in timer_data
+ * (from *R_TIMER_DATA) to nanoseconds (67 ns resolution)
+ */
+unsigned long timer_data_to_ns(unsigned long timer_data) 
+{
+/* low (clkdiv_low lsb toggles with 7.3728MHz so it counts
+ * with 14.7456 MHz = 67.816 ns (0-17361ns)
+ * high (clkdiv_high lsb toggles with 38.4kHz so it counts
+ * with 76.8kHz = 13020.833 ns (0-3333333 ns)
+ * By checking bit 9,8,7 we can now how to compensate the low value
+ * to get a 67 ns resolution all the way.
+Example of R_TIMER_DATA values:
+     bit 98 7   low      9 87     offset
+0289DC00 00 000 0        0 00       0
+0289DC41 00 010 64       0 01       0
+0289DC81 00 100 128      0 10       0
+0289DDC0 01 110 192      1 11       0        13020 = 192        
+0289DD01 01 000 0    256 1 00     +256    
+0289DD41 01 010 64   320 1 01     +256        
+
+0288DE80 10 100 128  384 0 10  0: -128             26040= 384  
+0288DEC1 10 110 192  448 0 11  64 -128            
+0288DE01 10 000 0    512      128 +128    
+0288DF40 11 010 64   576      192 +128             39060 
+0288DF81 11 100 128  640      256 +128   
+0288DFC1 11 110 192  704      320 +128
+                              ..393		 
+*/
+	
+	static const short timer_data_add[8] = {
+		0,   /* 00 0 */
+		0,   /* 00 1 */
+		256, /* 01 0 */
+		0,   /* 01 1 */
+		128, /* 10 0 */
+		-128,/* 10 1 */
+		128, /* 11 0 */
+		128  /* 11 1 */
+	};  
+	unsigned long ns;
+	unsigned long low;
+	unsigned long high;
+  
+	high = (((timer_data & 0x0000FE00)>>8) * 13020833)/1000;
+	ns = high;
+  
+	low = timer_data & 0xFF;
+	low += timer_data_add[(timer_data >>7) & 0x7];
+	ns += (low * 67816)/1000;
+	return ns;
+} /* timer_data_to_ns */
+
+
+
 
 #if CRIS_TEST_TIMERS 
 #define NS_TEST_SIZE 4000
@@ -274,7 +328,7 @@
 	int retval = 0;
 	int real_seconds, real_minutes, cmos_minutes;
 
-	printk("set_rtc_mmss(%lu)\n", nowtime);
+	printk(KERN_INFO "set_rtc_mmss(%lu)\n", nowtime);
 
 	if(!have_rtc)
 		return 0;
@@ -460,7 +514,8 @@
 	mon = CMOS_READ(RTC_MONTH);
 	year = CMOS_READ(RTC_YEAR);
 
-	printk("rtc: sec 0x%x min 0x%x hour 0x%x day 0x%x mon 0x%x year 0x%x\n", 
+	printk(KERN_INFO
+	       "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);
@@ -585,7 +640,7 @@
 	/* enable watchdog if we should use one */
 
 #if defined(CONFIG_ETRAX_WATCHDOG) && !defined(CONFIG_SVINTO_SIM)
-	printk("Enabling watchdog...\n");
+	printk(KERN_INFO "Enabling watchdog...\n");
 	start_watchdog();
 
 	/* If we use the hardware watchdog, we want to trap it as an NMI
diff -urN linux-2.4.21-bk5/arch/cris/kernel/traps.c linux-2.4.21-bk6/arch/cris/kernel/traps.c
--- linux-2.4.21-bk5/arch/cris/kernel/traps.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/kernel/traps.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,12 +1,11 @@
-/* $Id: traps.c,v 1.18 2002/12/02 08:11:41 starvik 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,2001 Axis Communications AB
+ *
+ *  Copyright (C) 2000, 2001, 2002, 2003 Axis Communications AB
  *
  *  Authors:   Bjorn Wesen
  *  	       Hans-Peter Nilsson
@@ -216,6 +215,8 @@
  * 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)
@@ -239,9 +240,6 @@
 void 
 die_if_kernel(const char * str, struct pt_regs * regs, long err)
 {
-	extern void reset_watchdog(void);
-	extern void stop_watchdog(void);
-
 	if(user_mode(regs))
 		return;
 
diff -urN linux-2.4.21-bk5/arch/cris/lib/checksum.S linux-2.4.21-bk6/arch/cris/lib/checksum.S
--- linux-2.4.21-bk5/arch/cris/lib/checksum.S	2001-10-08 11:43:54.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/lib/checksum.S	2003-07-10 02:54:58.000000000 -0700
@@ -1,6 +1,6 @@
-/* $Id: checksum.S,v 1.6 2001/10/01 14:47:35 bjornw Exp $
+/*
  * A fast checksum routine using movem
- * Copyright (c) 1998-2001 Axis Communications AB
+ * Copyright (c) 1998-2003 Axis Communications AB
  *
  * csum_partial(const unsigned char * buff, int len, unsigned int sum)
  */
diff -urN linux-2.4.21-bk5/arch/cris/lib/checksumcopy.S linux-2.4.21-bk6/arch/cris/lib/checksumcopy.S
--- linux-2.4.21-bk5/arch/cris/lib/checksumcopy.S	2001-10-08 11:43:54.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/lib/checksumcopy.S	2003-07-10 02:54:58.000000000 -0700
@@ -1,9 +1,9 @@
-/* $Id: checksumcopy.S,v 1.7 2001/10/01 14:47:35 bjornw Exp $
+/*
  * A fast checksum+copy routine using movem
- * Copyright (c) 1998, 2001 Axis Communications AB
+ * Copyright (c) 1998-2003 Axis Communications AB
  *
  * Authors:	Bjorn Wesen
- * 
+ *
  * csum_partial_copy_nocheck(const char *src, char *dst,
  *		             int len, unsigned int sum)
  */
diff -urN linux-2.4.21-bk5/arch/cris/lib/dmacopy.c linux-2.4.21-bk6/arch/cris/lib/dmacopy.c
--- linux-2.4.21-bk5/arch/cris/lib/dmacopy.c	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.21-bk6/arch/cris/lib/dmacopy.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,5 +1,4 @@
-/* $Id: dmacopy.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $ 
- *
+/*
  * memcpy for large blocks, using memory-memory DMA channels 6 and 7 in Etrax
  */
 
@@ -13,11 +12,11 @@
 		 unsigned int pn)
 {
 	static etrax_dma_descr indma, outdma;
-	
+
 	D(printk("dma_memcpy %d bytes... ", pn));
 
 #if 0
-	*R_GEN_CONFIG = genconfig_shadow = 
+	*R_GEN_CONFIG = genconfig_shadow =
 		(genconfig_shadow & ~0x3c0000) |
 		IO_STATE(R_GEN_CONFIG, dma6, intdma7) |
 		IO_STATE(R_GEN_CONFIG, dma7, intdma6);
@@ -32,12 +31,9 @@
 	*R_DMA_CH7_FIRST = &outdma;
 	*R_DMA_CH6_CMD = IO_STATE(R_DMA_CH6_CMD, cmd, start);
 	*R_DMA_CH7_CMD = IO_STATE(R_DMA_CH7_CMD, cmd, start);
-	
+
 	while(*R_DMA_CH7_CMD == 1) /* wait for completion */ ;
 
 	D(printk("done\n"));
 
 }
-
-
-
diff -urN linux-2.4.21-bk5/arch/cris/lib/dram_init.S linux-2.4.21-bk6/arch/cris/lib/dram_init.S
--- linux-2.4.21-bk5/arch/cris/lib/dram_init.S	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/lib/dram_init.S	2003-07-10 02:54:58.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: dram_init.S,v 1.13 2002/10/30 07:42:28 starvik Exp $
+/* $Id: dram_init.S,v 1.14 2003/03/31 07:07:08 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.14  2003/03/31 07:07:08  starvik
+ * Corrected calculation of end of sdram init commands
+ *
  * Revision 1.13  2002/10/30 07:42:28  starvik
  * Always read SDRAM command sequence from flash
  *
@@ -148,7 +151,7 @@
 	move.d   _sdram_commands_start, $r2
 	and.d    0x00ffffff, $r2 ; Make sure commands are read from flash
 	move.d   _sdram_commands_end,  $r3
-	and.d    0x00ffffff, $r2
+	and.d    0x00ffffff, $r3
 1:	clear.d  $r4
 	move.b   [$r2+], $r4
 	lslq     9, $r4	; Command starts at bit 9
diff -urN linux-2.4.21-bk5/arch/cris/lib/hw_settings.S linux-2.4.21-bk6/arch/cris/lib/hw_settings.S
--- linux-2.4.21-bk5/arch/cris/lib/hw_settings.S	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/lib/hw_settings.S	2003-07-10 02:54:58.000000000 -0700
@@ -1,13 +1,11 @@
 /*
- * $Id: hw_settings.S,v 1.5 2002/08/15 09:16:06 bjornw 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, 2002 Axis Communications AB
+ * Copyright (C) 2001, 2002, 2003 Axis Communications AB
  *
- * Authors:  Mikael Starvik (starvik@axis.com)	
+ * Authors:  Mikael Starvik (starvik@axis.com)
  */
 
 #define PA_SET_VALUE ((CONFIG_ETRAX_DEF_R_PORT_PA_DIR << 8) | \
@@ -15,13 +13,13 @@
 #define PB_SET_VALUE ((CONFIG_ETRAX_DEF_R_PORT_PB_CONFIG << 16) | \
 		(CONFIG_ETRAX_DEF_R_PORT_PB_DIR << 8) | \
 		(CONFIG_ETRAX_DEF_R_PORT_PB_DATA))
-	
+
 	.ascii "HW_PARAM_MAGIC"	; Magic number
 	.dword 0xc0004000	; Where to load the kernel image (physical!)
 
 	; 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)
@@ -30,7 +28,7 @@
 	.dword 3
 #else
 	.dword 4 ; No debug
-#endif			
+#endif
 
 	; SDRAM or EDO DRAM?
 #ifdef CONFIG_ETRAX_SDRAM
@@ -39,7 +37,7 @@
 	.dword 0
 #endif
 
-	; Register values 
+	; Register values
 	.dword R_WAITSTATES
 	.dword CONFIG_ETRAX_DEF_R_WAITSTATES
 	.dword R_BUS_CONFIG
@@ -56,7 +54,7 @@
 	.dword CONFIG_ETRAX_DEF_R_DRAM_TIMING
 #endif
 	.dword R_PORT_PA_SET
-	.dword PA_SET_VALUE 
+	.dword PA_SET_VALUE
 	.dword R_PORT_PB_SET
 	.dword PB_SET_VALUE
 	.dword 0 ; No more register values
diff -urN linux-2.4.21-bk5/arch/cris/lib/old_checksum.c linux-2.4.21-bk6/arch/cris/lib/old_checksum.c
--- linux-2.4.21-bk5/arch/cris/lib/old_checksum.c	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.21-bk6/arch/cris/lib/old_checksum.c	2003-07-10 02:54:58.000000000 -0700
@@ -1,5 +1,4 @@
-/* $Id: old_checksum.c,v 1.1 2000/07/10 16:25:21 bjornw Exp $
- *
+/*
  * INET		An implementation of the TCP/IP protocol suite for the LINUX
  *		operating system.  INET is implemented using the  BSD Socket
  *		interface as the means of communication with the user level.
diff -urN linux-2.4.21-bk5/arch/cris/mm/fault.c linux-2.4.21-bk6/arch/cris/mm/fault.c
--- linux-2.4.21-bk5/arch/cris/mm/fault.c	2002-11-28 15:53:09.000000000 -0800
+++ linux-2.4.21-bk6/arch/cris/mm/fault.c	2003-07-10 02:54:58.000000000 -0700
@@ -6,6 +6,10 @@
  *  Authors:  Bjorn Wesen 
  * 
  *  $Log: fault.c,v $
+ *  Revision 1.22  2003/07/07 09:07:04  johana
+ *  Added special CONFIG_ETRAX_DEBUG_INTERRUPT handling here
+ *  to deal with a di in entry.S
+ *
  *  Revision 1.21  2002/05/28 14:24:56  bjornw
  *  Corrected typo
  *
@@ -68,6 +72,7 @@
  *
  */
 
+#include <linux/config.h>
 #include <linux/signal.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
@@ -126,6 +131,9 @@
 	int errcode;
 	unsigned long address;
 
+#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT /* The di is actually in entry.S */
+	log_int(rdpc(), regs->dccr, 0);
+#endif
 	cause = *R_MMU_CAUSE;
 	select = *R_TLB_SELECT;
 
diff -urN linux-2.4.21-bk5/arch/cris/mm/init.c linux-2.4.21-bk6/arch/cris/mm/init.c
--- linux-2.4.21-bk5/arch/cris/mm/init.c	2002-08-02 17:39:42.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/mm/init.c	2003-07-10 02:54:58.000000000 -0700
@@ -7,6 +7,15 @@
  *  Authors:  Bjorn Wesen (bjornw@axis.com)
  *
  *  $Log: init.c,v $
+ *  Revision 1.38  2003/04/01 14:12:08  starvik
+ *  Added loglevel for lots of printks
+ *
+ *  Revision 1.37  2003/01/22 06:54:47  starvik
+ *  Fixed warnings issued by GCC 3.2.1
+ *
+ *  Revision 1.36  2003/01/09 17:59:55  starvik
+ *  Added init_ioremap to initcalls
+ *
  *  Revision 1.35  2002/05/17 05:33:59  starvik
  *  Limit cache flush range to the size of the cache
  *
@@ -113,6 +122,7 @@
 #include <linux/swap.h>
 #include <linux/smp.h>
 #include <linux/bootmem.h>
+#include <linux/init.h>
 
 #include <asm/system.h>
 #include <asm/segment.h>
@@ -208,7 +218,7 @@
 	int i;
 	unsigned long zones_size[MAX_NR_ZONES];
 
-	printk("Setting up paging and the MMU.\n");
+	printk(KERN_INFO "Setting up paging and the MMU.\n");
 	
 	/* clear out the init_mm.pgd that will contain the kernel's mappings */
 
@@ -404,7 +414,8 @@
         datasize =  (unsigned long) &_edata - (unsigned long) &_etext;
         initsize =  (unsigned long) &__init_end - (unsigned long) &__init_begin;
 	
-        printk("Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, "
+        printk(KERN_INFO
+	       "Memory: %luk/%luk available (%dk kernel code, %dk reserved, %dk data, "
 	       "%dk init)\n" ,
 	       (unsigned long) nr_free_pages() << (PAGE_SHIFT-10),
 	       max_mapnr << (PAGE_SHIFT-10),
@@ -425,47 +436,40 @@
 	return;
 }
 
-/* Initialize remaps of some I/O-ports. This is designed to be callable
- * multiple times from the drivers init-sections, because we don't know
- * beforehand which driver will get initialized first.
+/* Initialize remaps of some I/O-ports. It is important that this
+ * is called before any driver is initialized.
  */
 
-void 
-init_ioremap(void)
+static int 
+__init init_ioremap(void)
 {
   
 	/* Give the external I/O-port addresses their values */
 
-        static int initialized = 0;
-  
-        if( !initialized ) {
-                initialized++;
-            
 #ifdef CONFIG_CRIS_LOW_MAP
-               /* Simply a linear map (see the KSEG map above in paging_init) */
-               port_cse1_addr = (volatile unsigned long *)(MEM_CSE1_START | 
-                                                           MEM_NON_CACHEABLE);
-               port_csp0_addr = (volatile unsigned long *)(MEM_CSP0_START |
-                                                           MEM_NON_CACHEABLE);
-               port_csp4_addr = (volatile unsigned long *)(MEM_CSP4_START |
-                                                           MEM_NON_CACHEABLE);
-#else
-               /* Note that nothing blows up just because we do this remapping 
-                * it's ok even if the ports are not used or connected 
-                * to anything (or connected to a non-I/O thing) */        
-               port_cse1_addr = (volatile unsigned long *)
-                 ioremap((unsigned long)(MEM_CSE1_START | 
-                                         MEM_NON_CACHEABLE), 16);
-               port_csp0_addr = (volatile unsigned long *)
-                 ioremap((unsigned long)(MEM_CSP0_START |
-                                         MEM_NON_CACHEABLE), 16);
-               port_csp4_addr = (volatile unsigned long *)
-                 ioremap((unsigned long)(MEM_CSP4_START |
-                                         MEM_NON_CACHEABLE), 16);
-#endif	
-        }
+	/* Simply a linear map (see the KSEG map above in paging_init) */
+	port_cse1_addr = (volatile unsigned long *)(MEM_CSE1_START |
+	                                            MEM_NON_CACHEABLE);
+	port_csp0_addr = (volatile unsigned long *)(MEM_CSP0_START |
+	                                            MEM_NON_CACHEABLE);
+	port_csp4_addr = (volatile unsigned long *)(MEM_CSP4_START |
+	                                            MEM_NON_CACHEABLE);
+#else						    
+	/* Note that nothing blows up just because we do this remapping 
+	 * it's ok even if the ports are not used or connected 
+	 * to anything (or connected to a non-I/O thing) */        
+	port_cse1_addr = (volatile unsigned long *)
+	ioremap((unsigned long)(MEM_CSE1_START | MEM_NON_CACHEABLE), 16);
+	port_csp0_addr = (volatile unsigned long *)
+	ioremap((unsigned long)(MEM_CSP0_START | MEM_NON_CACHEABLE), 16);
+	port_csp4_addr = (volatile unsigned long *)
+	ioremap((unsigned long)(MEM_CSP4_START | MEM_NON_CACHEABLE), 16);
+#endif
+	return 0;
 }
 
+__initcall(init_ioremap);
+
 /* Helper function for the two below */
 
 static inline void
@@ -481,7 +485,7 @@
 	length = length > 8192 ? 8192 : length;  /* No need to flush more than cache size */
 
 	while(length > 0) {
-		short tmp = *flushadr;           /* dummy read to flush */
+		*flushadr; /* dummy read to flush */
 		flushadr += (32/sizeof(short));  /* a cacheline is 32 bytes */
 		length -= 32;
 	}
@@ -524,7 +528,7 @@
                 totalram_pages++;
         }
         printk (KERN_INFO "Freeing unused kernel memory: %luk freed\n", 
-		(&__init_end - &__init_begin) >> 10);
+		(unsigned long)((&__init_end - &__init_begin) >> 10));
 }
 
 void 
diff -urN linux-2.4.21-bk5/arch/cris/mm/ioremap.c linux-2.4.21-bk6/arch/cris/mm/ioremap.c
--- linux-2.4.21-bk5/arch/cris/mm/ioremap.c	2003-06-13 07:51:29.000000000 -0700
+++ linux-2.4.21-bk6/arch/cris/mm/ioremap.c	2003-07-10 02:54:58.000000000 -0700
@@ -146,7 +146,7 @@
 	 */
 	offset = phys_addr & ~PAGE_MASK;
 	phys_addr &= PAGE_MASK;
-	size = PAGE_ALIGN(last_addr) - phys_addr;
+	size = PAGE_ALIGN(last_addr+1) - phys_addr;
 
 	/*
 	 * Ok, go for it..
diff -urN linux-2.4.21-bk5/arch/i386/kernel/acpi.c linux-2.4.21-bk6/arch/i386/kernel/acpi.c
--- linux-2.4.21-bk5/arch/i386/kernel/acpi.c	2003-07-10 02:54:54.000000000 -0700
+++ linux-2.4.21-bk6/arch/i386/kernel/acpi.c	2003-07-10 02:54:58.000000000 -0700
@@ -129,6 +129,8 @@
 	printk(KERN_INFO PREFIX "Local APIC address 0x%08x\n",
 		madt->lapic_address);
 
+	acpi_madt_oem_check(madt->header.oem_id, madt->header.oem_table_id);
+
 	return 0;
 }
 
diff -urN linux-2.4.21-bk5/arch/i386/kernel/io_apic.c linux-2.4.21-bk6/arch/i386/kernel/io_apic.c
--- linux-2.4.21-bk5/arch/i386/kernel/io_apic.c	2003-07-10 02:54:54.000000000 -0700
+++ linux-2.4.21-bk6/arch/i386/kernel/io_apic.c	2003-07-10 02:54:58.000000000 -0700
@@ -1732,6 +1732,13 @@
 		apic_id = reg_00.ID;
 	}
 
+	/* XAPICs do not need unique IDs */
+	if (clustered_apic_mode == CLUSTERED_APIC_XAPIC){
+		printk(KERN_INFO "IOAPIC[%d]: Assigned apic_id %d\n", 
+			ioapic, apic_id);
+		return apic_id;
+	}
+
 	/*
 	 * Every APIC in a system must have a unique ID or we get lots of nice 
 	 * 'stuck on smp_invalidate_needed IPI wait' messages.
diff -urN linux-2.4.21-bk5/arch/i386/kernel/mpparse.c linux-2.4.21-bk6/arch/i386/kernel/mpparse.c
--- linux-2.4.21-bk5/arch/i386/kernel/mpparse.c	2003-07-10 02:54:54.000000000 -0700
+++ linux-2.4.21-bk6/arch/i386/kernel/mpparse.c	2003-07-10 02:54:58.000000000 -0700
@@ -1252,6 +1252,23 @@
 	io_apic_set_pci_routing(ioapic, ioapic_pin, irq);
 }
 
+/* Hook from generic ACPI tables.c */
+void __init acpi_madt_oem_check(char *oem_id, char *oem_table_id)
+{
+	if (!strncmp(oem_id, "IBM", 3) &&
+	    (!strncmp(oem_table_id, "SERVIGIL", 8) ||
+	     !strncmp(oem_table_id, "EXA", 3) ||
+	     !strncmp(oem_table_id, "RUTHLESS", 8))){
+		clustered_apic_mode = CLUSTERED_APIC_XAPIC;
+		apic_broadcast_id = APIC_BROADCAST_ID_XAPIC;
+		int_dest_addr_mode = APIC_DEST_PHYSICAL;
+		int_delivery_mode = dest_Fixed;
+		esr_disable = 1;
+		/*Start cyclone clock*/
+		cyclone_setup(0);
+	}
+}
+
 #ifdef CONFIG_ACPI_PCI
 
 void __init mp_parse_prt (void)
diff -urN linux-2.4.21-bk5/arch/ppc/boot/chrp/main.c linux-2.4.21-bk6/arch/ppc/boot/chrp/main.c
--- linux-2.4.21-bk5/arch/ppc/boot/chrp/main.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.21-bk6/arch/ppc/boot/chrp/main.c	2003-07-10 02:54:58.000000000 -0700
@@ -43,6 +43,8 @@
 
 static char scratch[SCRATCH_SIZE];	/* 1MB of scratch space for gunzip */
 
+typedef void (*kernel_start_t)(int, int, void *, unsigned int, unsigned int);
+
 void
 chrpboot(int a1, int a2, void *prom)
 {
@@ -93,7 +95,7 @@
     sa = (unsigned long)PROG_START;
     printf("start address = 0x%x\n\r", sa);
 
-    (*(void (*)())sa)(a1, a2, prom, initrd_start, initrd_size);
+    (*(kernel_start_t)sa)(a1, a2, prom, initrd_start, initrd_size);
 
     printf("returned?\n\r");
 
diff -urN linux-2.4.21-bk5/arch/ppc/boot/common/Makefile linux-2.4.21-bk6/arch/ppc/boot/common/Makefile
--- linux-2.4.21-bk5/arch/ppc/boot/common/Makefile	2002-08-02 17:39:43.000000000 -0700
+++ linux-2.4.21-bk6/arch/ppc/boot/common/Makefile	2003-07-10 02:54:58.000000000 -0700
@@ -11,7 +11,6 @@
 USE_STANDARD_AS_RULE	:= true
 
 coffcrt0.o:
-	$(CC) -I$(TOPDIR)/arch/$(ARCH)/kernel $(AFLAGS) -DXCOFF \
-		-traditional -c -o coffcrt0.o crt0.S
+	$(CC) $(AFLAGS) -DXCOFF -traditional -c -o coffcrt0.o crt0.S
 
 include $(TOPDIR)/Rules.make
diff -urN linux-2.4.21-bk5/arch/ppc/boot/pmac/Makefile linux-2.4.21-bk6/arch/ppc/boot/pmac/Makefile
--- linux-2.4.21-bk5/arch/ppc/boot/pmac/Makefile	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.21-bk6/arch/ppc/boot/pmac/Makefile	2003-07-10 02:54:58.000000000 -0700
@@ -30,8 +30,6 @@
 
 TFTPIMAGE=/tftpboot/zImage.pmac$(END)
 
-AFLAGS_../common/crt0.o		+= -I$(TOPDIR)/arch/$(ARCH)/kernel
-
 ../common/coffcrt0.o:
 	$(MAKE) -C ../common coffcrt0.o
 
diff -urN linux-2.4.21-bk5/arch/ppc/boot/pmac/chrpmain.c linux-2.4.21-bk6/arch/ppc/boot/pmac/chrpmain.c
--- linux-2.4.21-bk5/arch/ppc/boot/pmac/chrpmain.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.21-bk6/arch/ppc/boot/pmac/chrpmain.c	2003-07-10 02:54:58.000000000 -0700
@@ -38,6 +38,8 @@
 
 #define SCRATCH_SIZE	(128 << 10)
 
+typedef void (*kernel_start_t)(int, int, void *);
+
 void boot(int a1, int a2, void *prom)
 {
     unsigned sa, len;
@@ -87,7 +89,7 @@
     sa = (unsigned long)PROG_START;
     printf("start address = 0x%x\n", sa);
 
-    (*(void (*)())sa)(a1, a2, prom);
+    (*(kernel_start_t)sa)(a1, a2, prom);
 
     printf("returned?\n");
 
diff -urN linux-2.4.21-bk5/arch/ppc/boot/pmac/coffmain.c linux-2.4.21-bk6/arch/ppc/boot/pmac/coffmain.c
--- linux-2.4.21-bk5/arch/ppc/boot/pmac/coffmain.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.21-bk6/arch/ppc/boot/pmac/coffmain.c	2003-07-10 02:54:58.000000000 -0700
@@ -44,6 +44,8 @@
 
 static char heap[SCRATCH_SIZE];
 
+typedef void (*kernel_start_t)(int, int, void *);
+
 void boot(int a1, int a2, void *prom)
 {
     unsigned sa, len;
@@ -92,7 +94,7 @@
     sa = (unsigned long)PROG_START;
     printf("start address = 0x%x\n", sa);
 
-    (*(void (*)())sa)(a1, a2, prom);
+    (*(kernel_start_t)sa)(a1, a2, prom);
 
     printf("returned?\n");
 
diff -urN linux-2.4.21-bk5/arch/ppc/boot/pmac/start.c linux-2.4.21-bk6/arch/ppc/boot/pmac/start.c
--- linux-2.4.21-bk5/arch/ppc/boot/pmac/start.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.21-bk6/arch/ppc/boot/pmac/start.c	2003-07-10 02:54:58.000000000 -0700
@@ -11,7 +11,7 @@
 extern int strlen(const char *s);
 extern void boot(int a1, int a2, void *prom);
 
-int (*prom)();
+int (*prom)(void *args);
 
 void *chosen_handle;
 void *stdin;
@@ -26,7 +26,7 @@
 void
 start(int a1, int a2, void *promptr)
 {
-    prom = (int (*)()) promptr;
+    prom = (int (*)(void *)) promptr;
     chosen_handle = finddevice("/chosen");
     if (chosen_handle == (void *) -1)
 	exit();
diff -urN linux-2.4.21-bk5/arch/ppc/boot/prep/Makefile linux-2.4.21-bk6/arch/ppc/boot/prep/Makefile
--- linux-2.4.21-bk5/arch/ppc/boot/prep/Makefile	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.21-bk6/arch/ppc/boot/prep/Makefile	2003-07-10 02:54:58.000000000 -0700
@@ -39,10 +39,6 @@
 
 # Extra include search dirs
 CFLAGS_kbd.o			+= -I$(TOPDIR)/drivers/char
-AFLAGS_head.o			+= -I$(TOPDIR)/arch/$(ARCH)/kernel
-AFLAGS_../common/util.o			+= -I$(TOPDIR)/arch/$(ARCH)/kernel
-AFLAGS_../common/relocate.o	+= -I$(TOPDIR)/arch/$(ARCH)/kernel
-AFLAGS_../simple/legacy.o	+= -I$(TOPDIR)/arch/$(ARCH)/kernel
 
 all: zImage
 
diff -urN linux-2.4.21-bk5/arch/ppc/kernel/setup.c linux-2.4.21-bk6/arch/ppc/kernel/setup.c
--- linux-2.4.21-bk5/arch/ppc/kernel/setup.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.21-bk6/arch/ppc/kernel/setup.c	2003-07-10 02:54:58.000000000 -0700
@@ -89,7 +89,7 @@
 int icache_bsize;
 int ucache_bsize;
 
-#ifdef CONFIG_VGA_CONSOLE
+#if defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_FB)
 struct screen_info screen_info = {
 	0, 25,			/* orig-x, orig-y */
 	0,			/* unused */
@@ -101,7 +101,7 @@
 	1,			/* orig-video-isVGA */
 	16			/* orig-video-points */
 };
-#endif /* CONFIG_VGA_CONSOLE */
+#endif /* CONFIG_VGA_CONSOLE || CONFIG_FB */
 
 void machine_restart(char *cmd)
 {
diff -urN linux-2.4.21-bk5/arch/ppc/platforms/prep_setup.c linux-2.4.21-bk6/arch/ppc/platforms/prep_setup.c
--- linux-2.4.21-bk5/arch/ppc/platforms/prep_setup.c	2003-06-13 07:51:31.000000000 -0700
+++ linux-2.4.21-bk6/arch/ppc/platforms/prep_setup.c	2003-07-10 02:54:58.000000000 -0700
@@ -659,8 +659,7 @@
 prep_init_vesa(void)
 {
 #if defined(CONFIG_PREP_RESIDUAL) && \
-	(defined(CONFIG_FB_VGA16) || defined(CONFIG_FB_VGA_16_MODULE) || \
-	 defined(CONFIG_FB_VESA))
+	(defined(CONFIG_VGA_CONSOLE) || defined(CONFIG_FB))
 	PPC_DEVICE *vgadev;
 
 	vgadev = residual_find_device(~0, NULL, DisplayController, SVGAController,
diff -urN linux-2.4.21-bk5/arch/sparc/kernel/sys_sparc.c linux-2.4.21-bk6/arch/sparc/kernel/sys_sparc.c
--- linux-2.4.21-bk5/arch/sparc/kernel/sys_sparc.c	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.21-bk6/arch/sparc/kernel/sys_sparc.c	2003-07-10 02:54:58.000000000 -0700
@@ -123,7 +123,10 @@
 	if (call <= SEMCTL)
 		switch (call) {
 		case SEMOP:
-			err = sys_semop (first, (struct sembuf *)ptr, second);
+			err = sys_semtimedop (first, (struct sembuf *)ptr, second, NULL);
+			goto out;
+		case SEMTIMEDOP:
+			err = sys_semtimedop (first, (struct sembuf *)ptr, second, (const struct timespec *) fifth);
 			goto out;
 		case SEMGET:
 			err = sys_semget (first, second, third);
diff -urN linux-2.4.21-bk5/arch/sparc64/kernel/sys_sparc.c linux-2.4.21-bk6/arch/sparc64/kernel/sys_sparc.c
--- linux-2.4.21-bk5/arch/sparc64/kernel/sys_sparc.c	2003-07-10 02:54:55.000000000 -0700
+++ linux-2.4.21-bk6/arch/sparc64/kernel/sys_sparc.c	2003-07-10 02:54:58.000000000 -0700
@@ -182,7 +182,10 @@
 	if (call <= SEMCTL)
 		switch (call) {
 		case SEMOP:
-			err = sys_semop (first, (struct sembuf *)ptr, second);
+			err = sys_semtimedop (first, (struct sembuf *)ptr, second, NULL);
+			goto out;
+		case SEMTIMEDOP:
+			err = sys_semtimedop (first, (struct sembuf *)ptr, second, (const struct timespec *) fifth);
 			goto out;
 		case SEMGET:
 			err = sys_semget (first, second, (int)third);
diff -urN linux-2.4.21-bk5/arch/sparc64/kernel/sys_sparc32.c linux-2.4.21-bk6/arch/sparc64/kernel/sys_sparc32.c
--- linux-2.4.21-bk5/arch/sparc64/kernel/sys_sparc32.c	2003-07-10 02:54:55.000000000 -0700
+++ linux-2.4.21-bk6/arch/sparc64/kernel/sys_sparc32.c	2003-07-10 02:54:58.000000000 -0700
@@ -32,6 +32,7 @@
 #include <linux/smb_mount.h>
 #include <linux/ncp_fs.h>
 #include <linux/quota.h>
+#include <linux/quotacompat.h>
 #include <linux/module.h>
 #include <linux/sunrpc/svc.h>
 #include <linux/nfsd/nfsd.h>
@@ -762,6 +763,38 @@
 	return err;
 }
 
+static __inline__ void *alloc_user_space(long len)
+{
+	struct pt_regs *regs = current->thread.kregs;
+	unsigned long usp = regs->u_regs[UREG_I6];
+
+	if (!(current->thread.flags & SPARC_FLAG_32BIT))
+		usp += STACK_BIAS;
+
+	return (void *) (usp - len);
+}
+
+struct timespec32 {
+	s32    tv_sec;
+	s32    tv_nsec;
+};
+                
+static int sys32_semtimedop(int semid, struct sembuf *tsems, int nsems,
+			    const struct timespec32 *timeout32)
+{
+	struct timespec32 t32;
+	struct timespec *t64 = alloc_user_space(sizeof(*t64));
+
+	if (copy_from_user(&t32, timeout32, sizeof(t32)))
+		return -EFAULT;
+
+	if (put_user(t32.tv_sec, &t64->tv_sec) ||
+	    put_user(t32.tv_nsec, &t64->tv_nsec))
+		return -EFAULT;
+
+	return sys_semtimedop(semid, tsems, nsems, t64);
+}
+
 asmlinkage int sys32_ipc (u32 call, int first, int second, int third, u32 ptr, u32 fifth)
 {
 	int version, err;
@@ -773,8 +806,10 @@
 		switch (call) {
 		case SEMOP:
 			/* struct sembuf is the same on 32 and 64bit :)) */
-			err = sys_semop (first, (struct sembuf *)AA(ptr), second);
+			err = sys_semtimedop (first, (struct sembuf *)AA(ptr), second, NULL);
 			goto out;
+		case SEMTIMEDOP:
+			err = sys32_semtimedop (first, (struct sembuf *)AA(ptr), second, (const struct timespec32 *) AA(fifth));
 		case SEMGET:
 			err = sys_semget (first, second, third);
 			goto out;
@@ -1945,11 +1980,6 @@
 	return ret;
 }
 
-struct timespec32 {
-	s32    tv_sec;
-	s32    tv_nsec;
-};
-                
 extern asmlinkage int sys_sched_rr_get_interval(pid_t pid, struct timespec *interval);
 
 asmlinkage int sys32_sched_rr_get_interval(__kernel_pid_t32 pid, struct timespec32 *interval)
diff -urN linux-2.4.21-bk5/drivers/char/drm/drm_agpsupport.h linux-2.4.21-bk6/drivers/char/drm/drm_agpsupport.h
--- linux-2.4.21-bk5/drivers/char/drm/drm_agpsupport.h	2003-06-13 07:51:32.000000000 -0700
+++ linux-2.4.21-bk6/drivers/char/drm/drm_agpsupport.h	2003-07-10 02:54:59.000000000 -0700
@@ -283,8 +283,15 @@
 			break;
 		case VIA_APOLLO_PRO: 	head->chipset = "VIA Apollo Pro";
 			break;
+/*
+ * Alan merged agp_backend.h which removes VIA_APOLLO_P4X400, but 
+ * didnt update this file. I just commented it out so we dont 
+ * get compile errors on -pre4 and it can be fixed in -pre5.
+ * 
 		case VIA_APOLLO_P4X400:	head->chipset = "VIA Apollo P4X400";
 			break;
+	
+*/
 
 		case SIS_GENERIC:	head->chipset = "SiS";           break;
 		case AMD_GENERIC:	head->chipset = "AMD";           break;
diff -urN linux-2.4.21-bk5/fs/Config.in linux-2.4.21-bk6/fs/Config.in
--- linux-2.4.21-bk5/fs/Config.in	2003-07-10 02:54:56.000000000 -0700
+++ linux-2.4.21-bk6/fs/Config.in	2003-07-10 02:55:00.000000000 -0700
@@ -19,13 +19,13 @@
 
 dep_tristate 'Amiga FFS file system support (EXPERIMENTAL)' CONFIG_AFFS_FS $CONFIG_EXPERIMENTAL
 
-dep_tristate 'Apple Macintosh file system support (EXPERIMENTAL)' CONFIG_HFS_FS $CONFIG_EXPERIMENTAL
+dep_tristate 'Apple HFS file system support (EXPERIMENTAL)' CONFIG_HFS_FS $CONFIG_EXPERIMENTAL
+
+dep_tristate 'Apple HFS+ (Extended HFS) file system support (EXPERIMENTAL)' CONFIG_HFSPLUS_FS $CONFIG_EXPERIMENTAL
 
 dep_tristate 'BeOS file systemv(BeFS) support (read only) (EXPERIMENTAL)' CONFIG_BEFS_FS $CONFIG_EXPERIMENTAL
 dep_mbool '  Debug Befs' CONFIG_BEFS_DEBUG $CONFIG_BEFS_FS
 
-dep_tristate 'Apple Extended HFS file system support (EXPERIMENTAL)' CONFIG_HFSPLUS_FS $CONFIG_EXPERIMENTAL
-
 dep_tristate 'BFS file system support (EXPERIMENTAL)' CONFIG_BFS_FS $CONFIG_EXPERIMENTAL
 
 tristate 'Ext3 journalling file system support' CONFIG_EXT3_FS
@@ -106,7 +106,6 @@
    dep_tristate 'InterMezzo file system support (replicating fs) (EXPERIMENTAL)' CONFIG_INTERMEZZO_FS $CONFIG_INET $CONFIG_EXPERIMENTAL
    dep_tristate 'NFS file system support' CONFIG_NFS_FS $CONFIG_INET
    dep_mbool '  Provide NFSv3 client support' CONFIG_NFS_V3 $CONFIG_NFS_FS
-   dep_mbool '  Allow direct I/O on NFS files (EXPERIMENTAL)' CONFIG_NFS_DIRECTIO $CONFIG_NFS_FS $CONFIG_EXPERIMENTAL
    dep_bool '  Root file system on NFS' CONFIG_ROOT_NFS $CONFIG_NFS_FS $CONFIG_IP_PNP
 
    dep_tristate 'NFS server support' CONFIG_NFSD $CONFIG_INET
diff -urN linux-2.4.21-bk5/fs/block_dev.c linux-2.4.21-bk6/fs/block_dev.c
--- linux-2.4.21-bk5/fs/block_dev.c	2003-07-10 02:54:56.000000000 -0700
+++ linux-2.4.21-bk6/fs/block_dev.c	2003-06-13 07:51:37.000000000 -0700
@@ -131,9 +131,8 @@
 	return 0;
 }
 
-static int blkdev_direct_IO(int rw, struct file * filp, struct kiobuf * iobuf, unsigned long blocknr, int blocksize)
+static int blkdev_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize)
 {
-	struct inode * inode = filp->f_dentry->d_inode->i_mapping->host;
 	return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, blkdev_get_block);
 }
 
diff -urN linux-2.4.21-bk5/fs/ext2/inode.c linux-2.4.21-bk6/fs/ext2/inode.c
--- linux-2.4.21-bk5/fs/ext2/inode.c	2003-07-10 02:54:56.000000000 -0700
+++ linux-2.4.21-bk6/fs/ext2/inode.c	2003-06-13 07:51:37.000000000 -0700
@@ -592,9 +592,8 @@
 {
 	return generic_block_bmap(mapping,block,ext2_get_block);
 }
-static int ext2_direct_IO(int rw, struct file * filp, struct kiobuf * iobuf, unsigned long blocknr, int blocksize)
+static int ext2_direct_IO(int rw, struct inode * inode, struct kiobuf * iobuf, unsigned long blocknr, int blocksize)
 {
-	struct inode * inode = filp->f_dentry->d_inode->i_mapping->host;
 	return generic_direct_IO(rw, inode, iobuf, blocknr, blocksize, ext2_get_block);
 }
 struct address_space_operations ext2_aops = {
diff -urN linux-2.4.21-bk5/fs/nfs/Makefile linux-2.4.21-bk6/fs/nfs/Makefile
--- linux-2.4.21-bk5/fs/nfs/Makefile	2003-07-10 02:54:56.000000000 -0700
+++ linux-2.4.21-bk6/fs/nfs/Makefile	2001-11-09 14:28:15.000000000 -0800
@@ -14,7 +14,6 @@
 
 obj-$(CONFIG_ROOT_NFS) += nfsroot.o mount_clnt.o      
 obj-$(CONFIG_NFS_V3) += nfs3proc.o nfs3xdr.o
-obj-$(CONFIG_NFS_DIRECTIO) += direct.o
 
 obj-m   := $(O_TARGET)
 
diff -urN linux-2.4.21-bk5/fs/nfs/direct.c linux-2.4.21-bk6/fs/nfs/direct.c
--- linux-2.4.21-bk5/fs/nfs/direct.c	2003-07-10 02:54:56.000000000 -0700
+++ linux-2.4.21-bk6/fs/nfs/direct.c	1969-12-31 16:00:00.000000000 -0800
@@ -1,382 +0,0 @@
-/*
- * linux/fs/nfs/direct.c
- *
- * High-performance direct I/O for the NFS client
- *
- * When an application requests uncached I/O, all read and write requests
- * are made directly to the server; data stored or fetched via these
- * requests is not cached in the Linux page cache.  The client does not
- * correct unaligned requests from applications.  All requested bytes are
- * held on permanent storage before a direct write system call returns to
- * an application.  Applications that manage their own data caching, such
- * as databases, make very good use of direct I/O on local file systems.
- *
- * Solaris implements an uncached I/O facility called directio() that
- * is used for backups and sequential I/O to very large files.  Solaris
- * also supports uncaching whole NFS partitions with "-o forcedirectio,"
- * an undocumented mount option.
- *
- * Note that I/O to read in executables (e.g. kernel_read) cannot use
- * direct (kiobuf) reads because there is no vma backing the passed-in
- * data buffer.
- *
- * Designed by Jeff Kimmel, Chuck Lever, and Trond Myklebust.
- *
- * Initial implementation:	12/2001 by Chuck Lever <cel@netapp.com>
- *
- * TODO:
- *
- * 1.  Use concurrent asynchronous network requests rather than
- *     serialized synchronous network requests for normal (non-sync)
- *     direct I/O.
- */
-
-#include <linux/config.h>
-#include <linux/sched.h>
-#include <linux/kernel.h>
-#include <linux/file.h>
-#include <linux/errno.h>
-#include <linux/nfs_fs.h>
-#include <linux/smp_lock.h>
-#include <linux/sunrpc/clnt.h>
-#include <linux/iobuf.h>
-
-#include <asm/system.h>
-#include <asm/uaccess.h>
-
-#define NFSDBG_FACILITY		(NFSDBG_PAGECACHE | NFSDBG_VFS)
-#define VERF_SIZE		(2 * sizeof(__u32))
-
-static inline int
-nfs_direct_read_rpc(struct file *file, struct nfs_readargs *arg)
-{
-	int result;
-	struct inode * inode = file->f_dentry->d_inode;
-	struct nfs_fattr fattr;
-        struct rpc_message msg;
-        struct nfs_readres res = { &fattr, arg->count, 0 };
-
-#ifdef CONFIG_NFS_V3
-	msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ?
-						NFS3PROC_READ : NFSPROC_READ;
-#else
-	msg.rpc_proc = NFSPROC_READ;
-#endif
-	msg.rpc_argp = arg;
-        msg.rpc_resp = &res;
-
-	lock_kernel();
-        msg.rpc_cred = nfs_file_cred(file);
-        fattr.valid = 0;
-        result = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
-	nfs_refresh_inode(inode, &fattr);
-	unlock_kernel();
-
-	return result;
-}
-
-static inline int
-nfs_direct_write_rpc(struct file *file, struct nfs_writeargs *arg,
-	struct nfs_writeverf *verf)
-{
-	int result;
-	struct inode *inode = file->f_dentry->d_inode;
-	struct nfs_fattr fattr;
-        struct rpc_message msg;
-        struct nfs_writeres res = { &fattr, verf, 0 };
-
-#ifdef CONFIG_NFS_V3
-	msg.rpc_proc = (NFS_PROTO(inode)->version == 3) ?
-						NFS3PROC_WRITE : NFSPROC_WRITE;
-#else
-	msg.rpc_proc = NFSPROC_WRITE;
-#endif
-	msg.rpc_argp = arg;
-        msg.rpc_resp = &res;
-
-	lock_kernel();
-	msg.rpc_cred = get_rpccred(nfs_file_cred(file));
-	fattr.valid = 0;
-        result = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
-	nfs_write_attributes(inode, &fattr);
-	put_rpccred(msg.rpc_cred);
-	unlock_kernel();
-
-#ifdef CONFIG_NFS_V3
-	if (NFS_PROTO(inode)->version == 3) {
-		if (result > 0) {
-			if ((arg->stable == NFS_FILE_SYNC) &&
-			    (verf->committed != NFS_FILE_SYNC)) {
-				printk(KERN_ERR
-				"%s: server didn't sync stable write request\n",
-				__FUNCTION__);
-				return -EIO;
-			}
-
-			if (result != arg->count) {
-				printk(KERN_INFO
-					"%s: short write, count=%u, result=%d\n",
-					__FUNCTION__, arg->count, result);
-			}
-		}
-		return result;
-	} else {
-#endif
-        	verf->committed = NFS_FILE_SYNC; /* NFSv2 always syncs data */
-		if (result == 0)
-			return arg->count;
-		return result;
-#ifdef CONFIG_NFS_V3
-	}
-#endif
-}
-
-#ifdef CONFIG_NFS_V3
-static inline int
-nfs_direct_commit_rpc(struct inode *inode, loff_t offset, size_t count,
-	struct nfs_writeverf *verf)
-{
-	int result;
-	struct nfs_fattr fattr;
-	struct nfs_writeargs	arg = { NFS_FH(inode), offset, count, 0, 0,
-					NULL };
-	struct nfs_writeres	res = { &fattr, verf, 0 };
-	struct rpc_message	msg = { NFS3PROC_COMMIT, &arg, &res, NULL };
-
-	fattr.valid = 0;
-
-	lock_kernel();
-	result = rpc_call_sync(NFS_CLIENT(inode), &msg, 0);
-	nfs_write_attributes(inode, &fattr);
-	unlock_kernel();
-
-	return result;
-}
-#else
-static inline int
-nfs_direct_commit_rpc(struct inode *inode, loff_t offset, size_t count,
-	struct nfs_writeverf *verf)
-{
-	return 0;
-}
-#endif
-
-/*
- * Walk through the iobuf and create an iovec for each "rsize" bytes.
- */
-static int
-nfs_direct_read(struct file *file, struct kiobuf *iobuf, loff_t offset,
-	size_t count)
-{
-	int curpage, total;
-	int result = 0;
-	struct inode *inode = file->f_dentry->d_inode;
-	int rsize = NFS_SERVER(inode)->rsize;
-	struct page *pages[NFS_READ_MAXIOV];
-	struct nfs_readargs args = { NFS_FH(inode), offset, 0, iobuf->offset,
-				     pages };
-
-	total = 0;
-	curpage = 0;
-        while (count) {
-		int len, request;
-		struct page **dest = pages;
-
-                request = count;
-                if (count > rsize)
-                        request = rsize;
-		args.count = request;
-		args.offset = offset;
-		args.pgbase = (iobuf->offset + total) & ~PAGE_MASK;
-		len = PAGE_SIZE - args.pgbase;
-
-		do {
-			struct page *page = iobuf->maplist[curpage];
-
-			if (curpage >= iobuf->nr_pages || !page) {
-				result = -EFAULT;
-				goto out_err;
-			}
-
-			*dest++ = page;
-			/* zero after the first iov */
-			if (request < len)
-				break;
-			request -= len;
-			len = PAGE_SIZE;
-			curpage++;
-		} while (request != 0);
-
-                result = nfs_direct_read_rpc(file, &args);
-
-                if (result < 0)
-                        break;
-
-                total += result;
-                if (result < args.count)   /* NFSv2ism */
-                        break;
-                count -= result;
-                offset += result;
-        };
-out_err:
-	if (!total)
-		return result;
-	return total;
-}
-
-/*
- * Walk through the iobuf and create an iovec for each "wsize" bytes.
- * If only one network write is necessary, or if the O_SYNC flag or
- * 'sync' mount option are present, or if this is a V2 inode, use
- * FILE_SYNC.  Otherwise, use UNSTABLE and finish with a COMMIT.
- *
- * The mechanics of this function are much the same as nfs_direct_read,
- * with the added complexity of committing unstable writes.
- */
-static int
-nfs_direct_write(struct file *file, struct kiobuf *iobuf,
-	loff_t offset, size_t count)
-{
-	int curpage, total;
-	int need_commit = 0;
-	int result = 0;
-	loff_t save_offset = offset;
-	struct inode *inode = file->f_dentry->d_inode;
-	int wsize = NFS_SERVER(inode)->wsize;
-	struct nfs_writeverf first_verf, ret_verf;
-	struct page *pages[NFS_WRITE_MAXIOV];
-        struct nfs_writeargs args = { NFS_FH(inode), 0, 0, NFS_FILE_SYNC, 0,
-				pages };
-
-#ifdef CONFIG_NFS_V3
-	if ((NFS_PROTO(inode)->version == 3) && (count > wsize) &&
-							(!IS_SYNC(inode)))
-		args.stable = NFS_UNSTABLE;
-#endif
-
-retry:
-	total = 0;
-	curpage = 0;
-        while (count) {
-		int len, request;
-		struct page **dest = pages;
-
-                request = count;
-                if (count > wsize)
-                        request = wsize;
-		args.count = request;
-		args.offset = offset;
-		args.pgbase = (iobuf->offset + total) & ~PAGE_MASK;
-		len = PAGE_SIZE - args.pgbase;
-
-		do {
-			struct page *page = iobuf->maplist[curpage];
-
-			if (curpage >= iobuf->nr_pages || !page) {
-				result = -EFAULT;
-				goto out_err;
-			}
-
-			*dest++ = page;
-			/* zero after the first iov */
-			if (request < len)
-				break;
-			request -= len;
-			len = PAGE_SIZE;
-			curpage++;
-		} while (request != 0);
-
-                result = nfs_direct_write_rpc(file, &args, &ret_verf);
-
-                if (result < 0)
-                        break;
-
-		if (!total)
-			memcpy(&first_verf.verifier, &ret_verf.verifier,
-								VERF_SIZE);
-		if (ret_verf.committed != NFS_FILE_SYNC) {
-			need_commit = 1;
-			if (memcmp(&first_verf.verifier, &ret_verf.verifier,
-								VERF_SIZE))
-				goto print_retry;
-		}
-
-                total += result;
-                count -= result;
-                offset += result;
-        };
-
-out_err:
-	/*
-	 * Commit data written so far, even in the event of an error
-	 */
-	if (need_commit) {
-		if (nfs_direct_commit_rpc(inode, save_offset,
-					iobuf->length - count, &ret_verf))
-			goto print_retry;
-		if (memcmp(&first_verf.verifier, &ret_verf.verifier,
-								VERF_SIZE))
-			goto print_retry;
-	}
-
-	if (!total)
-		return result;
-	return total;
-
-print_retry:
-	printk(KERN_INFO "%s: detected server restart; retrying with FILE_SYNC\n",
-			__FUNCTION__);
-	args.stable = NFS_FILE_SYNC;
-	offset = save_offset;
-	count = iobuf->length;
-	goto retry;
-}
-
-/*
- * Read or write data, moving the data directly to/from the
- * application's buffer without caching in the page cache.
- *
- * Rules for direct I/O
- *
- * 1.  block size = 512 bytes or more
- * 2.  file byte offset is block aligned
- * 3.  byte count is a multiple of block size
- * 4.  user buffer is not aligned
- * 5.  user buffer is faulted in and pinned
- *
- * These are verified before we get here.
- */
-int
-nfs_direct_IO(int rw, struct file *file, struct kiobuf *iobuf,
-	unsigned long blocknr, int blocksize)
-{
-	int result = -EINVAL;
-	size_t count = iobuf->length;
-	struct dentry *dentry = file->f_dentry;
-	struct inode *inode = dentry->d_inode;
-	loff_t offset = blocknr << inode->i_blkbits;
-
-	switch (rw) {
-	case READ:
-		dfprintk(VFS,
-			"NFS: direct_IO(READ) (%s/%s) off/cnt(%Lu/%d)\n",
-				dentry->d_parent->d_name.name,
-					dentry->d_name.name, offset, count);
-
-		result = nfs_direct_read(file, iobuf, offset, count);
-		break;
-	case WRITE:
-		dfprintk(VFS,
-			"NFS: direct_IO(WRITE) (%s/%s) off/cnt(%Lu/%d)\n",
-				dentry->d_parent->d_name.name,
-					dentry->d_name.name, offset, count);
-
-		result = nfs_direct_write(file, iobuf, offset, count);
-		break;
-	default:
-		break;
-	}
-
-	dfprintk(VFS, "NFS: direct_IO result = %d\n", result);
-	return result;
-}
diff -urN linux-2.4.21-bk5/fs/nfs/file.c linux-2.4.21-bk6/fs/nfs/file.c
--- linux-2.4.21-bk5/fs/nfs/file.c	2003-07-10 02:54:56.000000000 -0700
+++ linux-2.4.21-bk6/fs/nfs/file.c	2003-06-13 07:51:37.000000000 -0700
@@ -16,7 +16,6 @@
  *  nfs regular file handling functions
  */
 
-#include <linux/config.h>
 #include <linux/sched.h>
 #include <linux/kernel.h>
 #include <linux/errno.h>
@@ -201,9 +200,6 @@
 	sync_page: nfs_sync_page,
 	writepage: nfs_writepage,
 	prepare_write: nfs_prepare_write,
-#ifdef CONFIG_NFS_DIRECTIO
-	direct_IO: nfs_direct_IO,
-#endif
 	commit_write: nfs_commit_write
 };
 
diff -urN linux-2.4.21-bk5/fs/nfs/write.c linux-2.4.21-bk6/fs/nfs/write.c
--- linux-2.4.21-bk5/fs/nfs/write.c	2003-07-10 02:54:56.000000000 -0700
+++ linux-2.4.21-bk6/fs/nfs/write.c	2003-07-10 02:55:00.000000000 -0700
@@ -123,6 +123,23 @@
 }
 
 /*
+ * This function will be used to simulate weak cache consistency
+ * under NFSv2 when the NFSv3 attribute patch is included.
+ * For the moment, we just call nfs_refresh_inode().
+ */
+static __inline__ int
+nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr)
+{
+	if ((fattr->valid & NFS_ATTR_FATTR) && !(fattr->valid & NFS_ATTR_WCC)) {
+		fattr->pre_size  = NFS_CACHE_ISIZE(inode);
+		fattr->pre_mtime = NFS_CACHE_MTIME(inode);
+		fattr->pre_ctime = NFS_CACHE_CTIME(inode);
+		fattr->valid |= NFS_ATTR_WCC;
+	}
+	return nfs_refresh_inode(inode, fattr);
+}
+
+/*
  * Write a page synchronously.
  * Offset is the data offset within the page.
  */
diff -urN linux-2.4.21-bk5/fs/quota_v2.c linux-2.4.21-bk6/fs/quota_v2.c
--- linux-2.4.21-bk5/fs/quota_v2.c	1969-12-31 16:00:00.000000000 -0800
+++ linux-2.4.21-bk6/fs/quota_v2.c	2003-07-10 02:55:00.000000000 -0700
@@ -0,0 +1,690 @@
+/*
+ *	vfsv0 quota IO operations on file
+ */
+
+#include <linux/errno.h>
+#include <linux/fs.h>
+#include <linux/dqblk_v2.h>
+#include <linux/quotaio_v2.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <asm/byteorder.h>
+#include <asm/uaccess.h>
+
+#define __QUOTA_V2_PARANOIA
+
+typedef char *dqbuf_t;
+
+#define GETIDINDEX(id, depth) (((id) >> ((V2_DQTREEDEPTH-(depth)-1)*8)) & 0xff)
+#define GETENTRIES(buf) ((struct v2_disk_dqblk *)(((char *)buf)+sizeof(struct v2_disk_dqdbheader)))
+
+/* Check whether given file is really vfsv0 quotafile */
+static int v2_check_quota_file(struct super_block *sb, int type)
+{
+	struct v2_disk_dqheader dqhead;
+	struct file *f = sb_dqopt(sb)->files[type];
+	mm_segment_t fs;
+	ssize_t size;
+	loff_t offset = 0;
+	static const uint quota_magics[] = V2_INITQMAGICS;
+	static const uint quota_versions[] = V2_INITQVERSIONS;
+ 
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	size = f->f_op->read(f, (char *)&dqhead, sizeof(struct v2_disk_dqheader), &offset);
+	set_fs(fs);
+	if (size != sizeof(struct v2_disk_dqheader))
+		return 0;
+	if (le32_to_cpu(dqhead.dqh_magic) != quota_magics[type] ||
+	    le32_to_cpu(dqhead.dqh_version) != quota_versions[type])
+		return 0;
+	return 1;
+}
+
+/* Read information header from quota file */
+static int v2_read_file_info(struct super_block *sb, int type)
+{
+	mm_segment_t fs;
+	struct v2_disk_dqinfo dinfo;
+	struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
+	struct file *f = sb_dqopt(sb)->files[type];
+	ssize_t size;
+	loff_t offset = V2_DQINFOOFF;
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	size = f->f_op->read(f, (char *)&dinfo, sizeof(struct v2_disk_dqinfo), &offset);
+	set_fs(fs);
+	if (size != sizeof(struct v2_disk_dqinfo)) {
+		printk(KERN_WARNING "Can't read info structure on device %s.\n",
+			kdevname(f->f_dentry->d_sb->s_dev));
+		return -1;
+	}
+	info->dqi_bgrace = le32_to_cpu(dinfo.dqi_bgrace);
+	info->dqi_igrace = le32_to_cpu(dinfo.dqi_igrace);
+	info->dqi_flags = le32_to_cpu(dinfo.dqi_flags);
+	info->u.v2_i.dqi_blocks = le32_to_cpu(dinfo.dqi_blocks);
+	info->u.v2_i.dqi_free_blk = le32_to_cpu(dinfo.dqi_free_blk);
+	info->u.v2_i.dqi_free_entry = le32_to_cpu(dinfo.dqi_free_entry);
+	return 0;
+}
+
+/* Write information header to quota file */
+static int v2_write_file_info(struct super_block *sb, int type)
+{
+	mm_segment_t fs;
+	struct v2_disk_dqinfo dinfo;
+	struct mem_dqinfo *info = sb_dqopt(sb)->info+type;
+	struct file *f = sb_dqopt(sb)->files[type];
+	ssize_t size;
+	loff_t offset = V2_DQINFOOFF;
+
+	info->dqi_flags &= ~DQF_INFO_DIRTY;
+	dinfo.dqi_bgrace = cpu_to_le32(info->dqi_bgrace);
+	dinfo.dqi_igrace = cpu_to_le32(info->dqi_igrace);
+	dinfo.dqi_flags = cpu_to_le32(info->dqi_flags & DQF_MASK);
+	dinfo.dqi_blocks = cpu_to_le32(info->u.v2_i.dqi_blocks);
+	dinfo.dqi_free_blk = cpu_to_le32(info->u.v2_i.dqi_free_blk);
+	dinfo.dqi_free_entry = cpu_to_le32(info->u.v2_i.dqi_free_entry);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	size = f->f_op->write(f, (char *)&dinfo, sizeof(struct v2_disk_dqinfo), &offset);
+	set_fs(fs);
+	if (size != sizeof(struct v2_disk_dqinfo)) {
+		printk(KERN_WARNING "Can't write info structure on device %s.\n",
+			kdevname(f->f_dentry->d_sb->s_dev));
+		return -1;
+	}
+	return 0;
+}
+
+static void disk2memdqb(struct mem_dqblk *m, struct v2_disk_dqblk *d)
+{
+	m->dqb_ihardlimit = le32_to_cpu(d->dqb_ihardlimit);
+	m->dqb_isoftlimit = le32_to_cpu(d->dqb_isoftlimit);
+	m->dqb_curinodes = le32_to_cpu(d->dqb_curinodes);
+	m->dqb_itime = le64_to_cpu(d->dqb_itime);
+	m->dqb_bhardlimit = le32_to_cpu(d->dqb_bhardlimit);
+	m->dqb_bsoftlimit = le32_to_cpu(d->dqb_bsoftlimit);
+	m->dqb_curspace = le64_to_cpu(d->dqb_curspace);
+	m->dqb_btime = le64_to_cpu(d->dqb_btime);
+}
+
+static void mem2diskdqb(struct v2_disk_dqblk *d, struct mem_dqblk *m, qid_t id)
+{
+	d->dqb_ihardlimit = cpu_to_le32(m->dqb_ihardlimit);
+	d->dqb_isoftlimit = cpu_to_le32(m->dqb_isoftlimit);
+	d->dqb_curinodes = cpu_to_le32(m->dqb_curinodes);
+	d->dqb_itime = cpu_to_le64(m->dqb_itime);
+	d->dqb_bhardlimit = cpu_to_le32(m->dqb_bhardlimit);
+	d->dqb_bsoftlimit = cpu_to_le32(m->dqb_bsoftlimit);
+	d->dqb_curspace = cpu_to_le64(m->dqb_curspace);
+	d->dqb_btime = cpu_to_le64(m->dqb_btime);
+	d->dqb_id = cpu_to_le32(id);
+}
+
+static dqbuf_t getdqbuf(void)
+{
+	dqbuf_t buf = kmalloc(V2_DQBLKSIZE, GFP_KERNEL);
+	if (!buf)
+		printk(KERN_WARNING "VFS: Not enough memory for quota buffers.\n");
+	return buf;
+}
+
+static inline void freedqbuf(dqbuf_t buf)
+{
+	kfree(buf);
+}
+
+static ssize_t read_blk(struct file *filp, uint blk, dqbuf_t buf)
+{
+	mm_segment_t fs;
+	ssize_t ret;
+	loff_t offset = blk<<V2_DQBLKSIZE_BITS;
+
+	memset(buf, 0, V2_DQBLKSIZE);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = filp->f_op->read(filp, (char *)buf, V2_DQBLKSIZE, &offset);
+	set_fs(fs);
+	return ret;
+}
+
+static ssize_t write_blk(struct file *filp, uint blk, dqbuf_t buf)
+{
+	mm_segment_t fs;
+	ssize_t ret;
+	loff_t offset = blk<<V2_DQBLKSIZE_BITS;
+
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = filp->f_op->write(filp, (char *)buf, V2_DQBLKSIZE, &offset);
+	set_fs(fs);
+	return ret;
+
+}
+
+/* Remove empty block from list and return it */
+static int get_free_dqblk(struct file *filp, struct mem_dqinfo *info)
+{
+	dqbuf_t buf = getdqbuf();
+	struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
+	int ret, blk;
+
+	if (!buf)
+		return -ENOMEM;
+	if (info->u.v2_i.dqi_free_blk) {
+		blk = info->u.v2_i.dqi_free_blk;
+		if ((ret = read_blk(filp, blk, buf)) < 0)
+			goto out_buf;
+		info->u.v2_i.dqi_free_blk = le32_to_cpu(dh->dqdh_next_free);
+	}
+	else {
+		memset(buf, 0, V2_DQBLKSIZE);
+		if ((ret = write_blk(filp, info->u.v2_i.dqi_blocks, buf)) < 0)	/* Assure block allocation... */
+			goto out_buf;
+		blk = info->u.v2_i.dqi_blocks++;
+	}
+	mark_info_dirty(info);
+	ret = blk;
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Insert empty block to the list */
+static int put_free_dqblk(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+	struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
+	int err;
+
+	dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_blk);
+	dh->dqdh_prev_free = cpu_to_le32(0);
+	dh->dqdh_entries = cpu_to_le16(0);
+	info->u.v2_i.dqi_free_blk = blk;
+	mark_info_dirty(info);
+	if ((err = write_blk(filp, blk, buf)) < 0)	/* Some strange block. We had better leave it... */
+		return err;
+	return 0;
+}
+
+/* Remove given block from the list of blocks with free entries */
+static int remove_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+	dqbuf_t tmpbuf = getdqbuf();
+	struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
+	uint nextblk = le32_to_cpu(dh->dqdh_next_free), prevblk = le32_to_cpu(dh->dqdh_prev_free);
+	int err;
+
+	if (!tmpbuf)
+		return -ENOMEM;
+	if (nextblk) {
+		if ((err = read_blk(filp, nextblk, tmpbuf)) < 0)
+			goto out_buf;
+		((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = dh->dqdh_prev_free;
+		if ((err = write_blk(filp, nextblk, tmpbuf)) < 0)
+			goto out_buf;
+	}
+	if (prevblk) {
+		if ((err = read_blk(filp, prevblk, tmpbuf)) < 0)
+			goto out_buf;
+		((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_next_free = dh->dqdh_next_free;
+		if ((err = write_blk(filp, prevblk, tmpbuf)) < 0)
+			goto out_buf;
+	}
+	else {
+		info->u.v2_i.dqi_free_entry = nextblk;
+		mark_info_dirty(info);
+	}
+	freedqbuf(tmpbuf);
+	dh->dqdh_next_free = dh->dqdh_prev_free = cpu_to_le32(0);
+	if (write_blk(filp, blk, buf) < 0)	/* No matter whether write succeeds block is out of list */
+		printk(KERN_ERR "VFS: Can't write block (%u) with free entries.\n", blk);
+	return 0;
+out_buf:
+	freedqbuf(tmpbuf);
+	return err;
+}
+
+/* Insert given block to the beginning of list with free entries */
+static int insert_free_dqentry(struct file *filp, struct mem_dqinfo *info, dqbuf_t buf, uint blk)
+{
+	dqbuf_t tmpbuf = getdqbuf();
+	struct v2_disk_dqdbheader *dh = (struct v2_disk_dqdbheader *)buf;
+	int err;
+
+	if (!tmpbuf)
+		return -ENOMEM;
+	dh->dqdh_next_free = cpu_to_le32(info->u.v2_i.dqi_free_entry);
+	dh->dqdh_prev_free = cpu_to_le32(0);
+	if ((err = write_blk(filp, blk, buf)) < 0)
+		goto out_buf;
+	if (info->u.v2_i.dqi_free_entry) {
+		if ((err = read_blk(filp, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0)
+			goto out_buf;
+		((struct v2_disk_dqdbheader *)tmpbuf)->dqdh_prev_free = cpu_to_le32(blk);
+		if ((err = write_blk(filp, info->u.v2_i.dqi_free_entry, tmpbuf)) < 0)
+			goto out_buf;
+	}
+	freedqbuf(tmpbuf);
+	info->u.v2_i.dqi_free_entry = blk;
+	mark_info_dirty(info);
+	return 0;
+out_buf:
+	freedqbuf(tmpbuf);
+	return err;
+}
+
+/* Find space for dquot */
+static uint find_free_dqentry(struct dquot *dquot, int *err)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info+dquot->dq_type;
+	uint blk, i;
+	struct v2_disk_dqdbheader *dh;
+	struct v2_disk_dqblk *ddquot;
+	struct v2_disk_dqblk fakedquot;
+	dqbuf_t buf;
+
+	*err = 0;
+	if (!(buf = getdqbuf())) {
+		*err = -ENOMEM;
+		return 0;
+	}
+	dh = (struct v2_disk_dqdbheader *)buf;
+	ddquot = GETENTRIES(buf);
+	if (info->u.v2_i.dqi_free_entry) {
+		blk = info->u.v2_i.dqi_free_entry;
+		if ((*err = read_blk(filp, blk, buf)) < 0)
+			goto out_buf;
+	}
+	else {
+		blk = get_free_dqblk(filp, info);
+		if ((int)blk < 0) {
+			*err = blk;
+			return 0;
+		}
+		memset(buf, 0, V2_DQBLKSIZE);
+		info->u.v2_i.dqi_free_entry = blk;	/* This is enough as block is already zeroed and entry list is empty... */
+		mark_info_dirty(info);
+	}
+	if (le16_to_cpu(dh->dqdh_entries)+1 >= V2_DQSTRINBLK)	/* Block will be full? */
+		if ((*err = remove_free_dqentry(filp, info, buf, blk)) < 0) {
+			printk(KERN_ERR "VFS: find_free_dqentry(): Can't remove block (%u) from entry free list.\n", blk);
+			goto out_buf;
+		}
+	dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)+1);
+	memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk));
+	/* Find free structure in block */
+	for (i = 0; i < V2_DQSTRINBLK && memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk)); i++);
+#ifdef __QUOTA_V2_PARANOIA
+	if (i == V2_DQSTRINBLK) {
+		printk(KERN_ERR "VFS: find_free_dqentry(): Data block full but it shouldn't.\n");
+		*err = -EIO;
+		goto out_buf;
+	}
+#endif
+	if ((*err = write_blk(filp, blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: find_free_dqentry(): Can't write quota data block %u.\n", blk);
+		goto out_buf;
+	}
+	dquot->dq_off = (blk<<V2_DQBLKSIZE_BITS)+sizeof(struct v2_disk_dqdbheader)+i*sizeof(struct v2_disk_dqblk);
+	freedqbuf(buf);
+	return blk;
+out_buf:
+	freedqbuf(buf);
+	return 0;
+}
+
+/* Insert reference to structure into the trie */
+static int do_insert_tree(struct dquot *dquot, uint *treeblk, int depth)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+	dqbuf_t buf;
+	int ret = 0, newson = 0, newact = 0;
+	u32 *ref;
+	uint newblk;
+
+	if (!(buf = getdqbuf()))
+		return -ENOMEM;
+	if (!*treeblk) {
+		ret = get_free_dqblk(filp, info);
+		if (ret < 0)
+			goto out_buf;
+		*treeblk = ret;
+		memset(buf, 0, V2_DQBLKSIZE);
+		newact = 1;
+	}
+	else {
+		if ((ret = read_blk(filp, *treeblk, buf)) < 0) {
+			printk(KERN_ERR "VFS: Can't read tree quota block %u.\n", *treeblk);
+			goto out_buf;
+		}
+	}
+	ref = (u32 *)buf;
+	newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
+	if (!newblk)
+		newson = 1;
+	if (depth == V2_DQTREEDEPTH-1) {
+#ifdef __QUOTA_V2_PARANOIA
+		if (newblk) {
+			printk(KERN_ERR "VFS: Inserting already present quota entry (block %u).\n", ref[GETIDINDEX(dquot->dq_id, depth)]);
+			ret = -EIO;
+			goto out_buf;
+		}
+#endif
+		newblk = find_free_dqentry(dquot, &ret);
+	}
+	else
+		ret = do_insert_tree(dquot, &newblk, depth+1);
+	if (newson && ret >= 0) {
+		ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(newblk);
+		ret = write_blk(filp, *treeblk, buf);
+	}
+	else if (newact && ret < 0)
+		put_free_dqblk(filp, info, buf, *treeblk);
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Wrapper for inserting quota structure into tree */
+static inline int dq_insert_tree(struct dquot *dquot)
+{
+	int tmp = V2_DQTREEOFF;
+	return do_insert_tree(dquot, &tmp, 0);
+}
+
+/*
+ *	We don't have to be afraid of deadlocks as we never have quotas on quota files...
+ */
+static int v2_write_dquot(struct dquot *dquot)
+{
+	int type = dquot->dq_type;
+	struct file *filp;
+	mm_segment_t fs;
+	loff_t offset;
+	ssize_t ret;
+	struct v2_disk_dqblk ddquot;
+
+	if (!dquot->dq_off)
+		if ((ret = dq_insert_tree(dquot)) < 0) {
+			printk(KERN_ERR "VFS: Error %d occured while creating quota.\n", ret);
+			return ret;
+		}
+	filp = sb_dqopt(dquot->dq_sb)->files[type];
+	offset = dquot->dq_off;
+	mem2diskdqb(&ddquot, &dquot->dq_dqb, dquot->dq_id);
+	fs = get_fs();
+	set_fs(KERNEL_DS);
+	ret = filp->f_op->write(filp, (char *)&ddquot, sizeof(struct v2_disk_dqblk), &offset);
+	set_fs(fs);
+	if (ret != sizeof(struct v2_disk_dqblk)) {
+		printk(KERN_WARNING "VFS: dquota write failed on dev %s\n", kdevname(dquot->dq_dev));
+		if (ret >= 0)
+			ret = -ENOSPC;
+	}
+	else
+		ret = 0;
+	dqstats.writes++;
+	return ret;
+}
+
+/* Free dquot entry in data block */
+static int free_dqentry(struct dquot *dquot, uint blk)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+	struct v2_disk_dqdbheader *dh;
+	dqbuf_t buf = getdqbuf();
+	int ret = 0;
+
+	if (!buf)
+		return -ENOMEM;
+	if (dquot->dq_off >> V2_DQBLKSIZE_BITS != blk) {
+		printk(KERN_ERR "VFS: Quota structure has offset to other block (%u) than it should (%u).\n", blk, (uint)(dquot->dq_off >> V2_DQBLKSIZE_BITS));
+		goto out_buf;
+	}
+	if ((ret = read_blk(filp, blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: Can't read quota data block %u\n", blk);
+		goto out_buf;
+	}
+	dh = (struct v2_disk_dqdbheader *)buf;
+	dh->dqdh_entries = cpu_to_le16(le16_to_cpu(dh->dqdh_entries)-1);
+	if (!le16_to_cpu(dh->dqdh_entries)) {	/* Block got free? */
+		if ((ret = remove_free_dqentry(filp, info, buf, blk)) < 0 ||
+		    (ret = put_free_dqblk(filp, info, buf, blk)) < 0) {
+			printk(KERN_ERR "VFS: Can't move quota data block (%u) to free list.\n", blk);
+			goto out_buf;
+		}
+	}
+	else {
+		memset(buf+(dquot->dq_off & ((1 << V2_DQBLKSIZE_BITS)-1)), 0, sizeof(struct v2_disk_dqblk));
+		if (le16_to_cpu(dh->dqdh_entries) == V2_DQSTRINBLK-1) {
+			/* Insert will write block itself */
+			if ((ret = insert_free_dqentry(filp, info, buf, blk)) < 0) {
+				printk(KERN_ERR "VFS: Can't insert quota data block (%u) to free entry list.\n", blk);
+				goto out_buf;
+			}
+		}
+		else
+			if ((ret = write_blk(filp, blk, buf)) < 0) {
+				printk(KERN_ERR "VFS: Can't write quota data block %u\n", blk);
+				goto out_buf;
+			}
+	}
+	dquot->dq_off = 0;	/* Quota is now unattached */
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Remove reference to dquot from tree */
+static int remove_tree(struct dquot *dquot, uint *blk, int depth)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	struct mem_dqinfo *info = sb_dqopt(dquot->dq_sb)->info + dquot->dq_type;
+	dqbuf_t buf = getdqbuf();
+	int ret = 0;
+	uint newblk;
+	u32 *ref = (u32 *)buf;
+	
+	if (!buf)
+		return -ENOMEM;
+	if ((ret = read_blk(filp, *blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: Can't read quota data block %u\n", *blk);
+		goto out_buf;
+	}
+	newblk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
+	if (depth == V2_DQTREEDEPTH-1) {
+		ret = free_dqentry(dquot, newblk);
+		newblk = 0;
+	}
+	else
+		ret = remove_tree(dquot, &newblk, depth+1);
+	if (ret >= 0 && !newblk) {
+		int i;
+		ref[GETIDINDEX(dquot->dq_id, depth)] = cpu_to_le32(0);
+		for (i = 0; i < V2_DQBLKSIZE && !buf[i]; i++);	/* Block got empty? */
+		if (i == V2_DQBLKSIZE) {
+			put_free_dqblk(filp, info, buf, *blk);
+			*blk = 0;
+		}
+		else
+			if ((ret = write_blk(filp, *blk, buf)) < 0)
+				printk(KERN_ERR "VFS: Can't write quota tree block %u.\n", *blk);
+	}
+out_buf:
+	freedqbuf(buf);
+	return ret;	
+}
+
+/* Delete dquot from tree */
+static int v2_delete_dquot(struct dquot *dquot)
+{
+	uint tmp = V2_DQTREEOFF;
+
+	if (!dquot->dq_off)	/* Even not allocated? */
+		return 0;
+	return remove_tree(dquot, &tmp, 0);
+}
+
+/* Find entry in block */
+static loff_t find_block_dqentry(struct dquot *dquot, uint blk)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	dqbuf_t buf = getdqbuf();
+	loff_t ret = 0;
+	int i;
+	struct v2_disk_dqblk *ddquot = GETENTRIES(buf);
+
+	if (!buf)
+		return -ENOMEM;
+	if ((ret = read_blk(filp, blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+		goto out_buf;
+	}
+	if (dquot->dq_id)
+		for (i = 0; i < V2_DQSTRINBLK && le32_to_cpu(ddquot[i].dqb_id) != dquot->dq_id; i++);
+	else {	/* ID 0 as a bit more complicated searching... */
+		struct v2_disk_dqblk fakedquot;
+
+		memset(&fakedquot, 0, sizeof(struct v2_disk_dqblk));
+		for (i = 0; i < V2_DQSTRINBLK; i++)
+			if (!le32_to_cpu(ddquot[i].dqb_id) && memcmp(&fakedquot, ddquot+i, sizeof(struct v2_disk_dqblk)))
+				break;
+	}
+	if (i == V2_DQSTRINBLK) {
+		printk(KERN_ERR "VFS: Quota for id %u referenced but not present.\n", dquot->dq_id);
+		ret = -EIO;
+		goto out_buf;
+	}
+	else
+		ret = (blk << V2_DQBLKSIZE_BITS) + sizeof(struct v2_disk_dqdbheader) + i * sizeof(struct v2_disk_dqblk);
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Find entry for given id in the tree */
+static loff_t find_tree_dqentry(struct dquot *dquot, uint blk, int depth)
+{
+	struct file *filp = sb_dqopt(dquot->dq_sb)->files[dquot->dq_type];
+	dqbuf_t buf = getdqbuf();
+	loff_t ret = 0;
+	u32 *ref = (u32 *)buf;
+
+	if (!buf)
+		return -ENOMEM;
+	if ((ret = read_blk(filp, blk, buf)) < 0) {
+		printk(KERN_ERR "VFS: Can't read quota tree block %u.\n", blk);
+		goto out_buf;
+	}
+	ret = 0;
+	blk = le32_to_cpu(ref[GETIDINDEX(dquot->dq_id, depth)]);
+	if (!blk)	/* No reference? */
+		goto out_buf;
+	if (depth < V2_DQTREEDEPTH-1)
+		ret = find_tree_dqentry(dquot, blk, depth+1);
+	else
+		ret = find_block_dqentry(dquot, blk);
+out_buf:
+	freedqbuf(buf);
+	return ret;
+}
+
+/* Find entry for given id in the tree - wrapper function */
+static inline loff_t find_dqentry(struct dquot *dquot)
+{
+	return find_tree_dqentry(dquot, V2_DQTREEOFF, 0);
+}
+
+static int v2_read_dquot(struct dquot *dquot)
+{
+	int type = dquot->dq_type;
+	struct file *filp;
+	mm_segment_t fs;
+	loff_t offset;
+	struct v2_disk_dqblk ddquot;
+	int ret = 0;
+
+	filp = sb_dqopt(dquot->dq_sb)->files[type];
+
+#ifdef __QUOTA_V2_PARANOIA
+	if (!filp || !dquot->dq_sb) {	/* Invalidated quota? */
+		printk(KERN_ERR "VFS: Quota invalidated while reading!\n");
+		return -EIO;
+	}
+#endif
+	offset = find_dqentry(dquot);
+	if (offset <= 0) {	/* Entry not present? */
+		if (offset < 0)
+			printk(KERN_ERR "VFS: Can't read quota structure for id %u.\n", dquot->dq_id);
+		dquot->dq_off = 0;
+		dquot->dq_flags |= DQ_FAKE;
+		memset(&dquot->dq_dqb, 0, sizeof(struct mem_dqblk));
+		ret = offset;
+	}
+	else {
+		dquot->dq_off = offset;
+		fs = get_fs();
+		set_fs(KERNEL_DS);
+		if ((ret = filp->f_op->read(filp, (char *)&ddquot, sizeof(struct v2_disk_dqblk), &offset)) != sizeof(struct v2_disk_dqblk)) {
+			if (ret >= 0)
+				ret = -EIO;
+			printk(KERN_ERR "VFS: Error while reading quota structure for id %u.\n", dquot->dq_id);
+			memset(&ddquot, 0, sizeof(struct v2_disk_dqblk));
+		}
+		else
+			ret = 0;
+		set_fs(fs);
+		disk2memdqb(&dquot->dq_dqb, &ddquot);
+	}
+	dqstats.reads++;
+	return ret;
+}
+
+/* Commit changes of dquot to disk - it might also mean deleting it when quota became fake one and user has no blocks... */
+static int v2_commit_dquot(struct dquot *dquot)
+{
+	/* We clear the flag everytime so we don't loop when there was an IO error... */
+	dquot->dq_flags &= ~DQ_MOD;
+	if (dquot->dq_flags & DQ_FAKE && !(dquot->dq_dqb.dqb_curinodes | dquot->dq_dqb.dqb_curspace))
+		return v2_delete_dquot(dquot);
+	else
+		return v2_write_dquot(dquot);
+}
+
+static struct quota_format_ops v2_format_ops = {
+	check_quota_file:	v2_check_quota_file,
+	read_file_info:		v2_read_file_info,
+	write_file_info:	v2_write_file_info,
+	free_file_info:		NULL,
+	read_dqblk:		v2_read_dquot,
+	commit_dqblk:		v2_commit_dquot,
+};
+
+static struct quota_format_type v2_quota_format = {
+	qf_fmt_id:	QFMT_VFS_V0,
+	qf_ops:		&v2_format_ops,
+	qf_owner:	THIS_MODULE
+};
+
+static int __init init_v2_quota_format(void)
+{
+	return register_quota_format(&v2_quota_format);
+}
+
+static void __exit exit_v2_quota_format(void)
+{
+	unregister_quota_format(&v2_quota_format);
+}
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_v2_quota_format);
+module_exit(exit_v2_quota_format);
diff -urN linux-2.4.21-bk5/include/asm-cris/atomic.h linux-2.4.21-bk6/include/asm-cris/atomic.h
--- linux-2.4.21-bk5/include/asm-cris/atomic.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/atomic.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,5 +1,3 @@
-/* $Id: atomic.h,v 1.4 2002/11/20 18:20:17 hp Exp $ */
-
 #ifndef __ASM_CRIS_ATOMIC__
 #define __ASM_CRIS_ATOMIC__
 
diff -urN linux-2.4.21-bk5/include/asm-cris/bitops.h linux-2.4.21-bk6/include/asm-cris/bitops.h
--- linux-2.4.21-bk5/include/asm-cris/bitops.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/bitops.h	2003-07-10 02:55:00.000000000 -0700
@@ -42,6 +42,7 @@
  */
 
 #define set_bit(nr, addr)    (void)test_and_set_bit(nr, addr)
+#define __set_bit(nr, addr)    (void)__test_and_set_bit(nr, addr)
 
 /*
  * clear_bit - Clears a bit in memory
@@ -55,6 +56,7 @@
  */
 
 #define clear_bit(nr, addr)  (void)test_and_clear_bit(nr, addr)
+#define __clear_bit(nr, addr)  (void)__test_and_clear_bit(nr, addr)
 
 /*
  * change_bit - Toggle a bit in memory
@@ -105,6 +107,18 @@
 	return retval;
 }
 
+extern inline int __test_and_set_bit(int nr, void *addr)
+{
+	unsigned int mask, retval;
+	unsigned int *adr = (unsigned int *)addr;
+	
+	adr += nr >> 5;
+	mask = 1 << (nr & 0x1f);
+	retval = (mask & *adr) != 0;
+	*adr |= mask;
+	return retval;
+}
+
 /*
  * clear_bit() doesn't provide any barrier for the compiler.
  */
diff -urN linux-2.4.21-bk5/include/asm-cris/bugs.h linux-2.4.21-bk6/include/asm-cris/bugs.h
--- linux-2.4.21-bk5/include/asm-cris/bugs.h	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.21-bk6/include/asm-cris/bugs.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,8 +1,7 @@
-/* $Id: bugs.h,v 1.2 2001/01/17 17:03:18 bjornw Exp $
- *
+/*
  *  include/asm-cris/bugs.h
  *
- *  Copyright (C) 2001 Axis Communications AB
+ *  Copyright (C) 2001, 2002, 2003 Axis Communications AB
  */
 
 /*
diff -urN linux-2.4.21-bk5/include/asm-cris/byteorder.h linux-2.4.21-bk6/include/asm-cris/byteorder.h
--- linux-2.4.21-bk5/include/asm-cris/byteorder.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/byteorder.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,5 +1,3 @@
-/* $Id: byteorder.h,v 1.2 2002/11/20 18:20:17 hp Exp $ */
-
 #ifndef _CRIS_BYTEORDER_H
 #define _CRIS_BYTEORDER_H
 
@@ -15,14 +13,14 @@
 extern __inline__ __const__ __u32 ___arch__swab32(__u32 x)
 {
 	__asm__ ("swapwb %0" : "=r" (x) : "0" (x));
-  
+
 	return(x);
 }
 
 extern __inline__ __const__ __u16 ___arch__swab16(__u16 x)
 {
 	__asm__ ("swapb %0" : "=r" (x) : "0" (x));
-	
+
 	return(x);
 }
 
@@ -43,5 +41,3 @@
 #include <linux/byteorder/little_endian.h>
 
 #endif
-
-
diff -urN linux-2.4.21-bk5/include/asm-cris/delay.h linux-2.4.21-bk6/include/asm-cris/delay.h
--- linux-2.4.21-bk5/include/asm-cris/delay.h	2001-10-08 11:43:54.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/delay.h	2003-07-10 02:55:00.000000000 -0700
@@ -39,6 +39,13 @@
 	__delay(usecs * loops_per_usec);
 }
 
+/* ETRAX 100 is really too slow to sleep for nanosecs. */
+/* Divide with 1024 to avoid a real division that would take >1 us... */
+extern __inline__ void ndelay(unsigned long nsecs)
+{
+	__delay(1 + nsecs * loops_per_usec / 1024);
+}
+
 #endif /* defined(_CRIS_DELAY_H) */
 
 
diff -urN linux-2.4.21-bk5/include/asm-cris/dma.h linux-2.4.21-bk6/include/asm-cris/dma.h
--- linux-2.4.21-bk5/include/asm-cris/dma.h	2001-07-04 11:50:39.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/dma.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: dma.h,v 1.2 2001/05/09 12:17:42 johana Exp $
+/*
  * linux/include/asm/dma.h: Defines for using and allocating dma channels.
  */
 
diff -urN linux-2.4.21-bk5/include/asm-cris/eshlibld.h linux-2.4.21-bk6/include/asm-cris/eshlibld.h
--- linux-2.4.21-bk5/include/asm-cris/eshlibld.h	2001-04-06 10:51:19.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/eshlibld.h	2003-07-10 02:55:00.000000000 -0700
@@ -9,10 +9,9 @@
 *!
 *!---------------------------------------------------------------------------
 *!
-*! (C) Copyright 1998, 1999 Axis Communications AB, LUND, SWEDEN
+*! (C) Copyright 1998-2003 Axis Communications AB, LUND, SWEDEN
 *!
 *!**************************************************************************/
-/* $Id: eshlibld.h,v 1.2 2001/02/23 13:47:33 bjornw Exp $ */
 
 #ifndef _cris_relocate_h
 #define _cris_relocate_h
@@ -111,4 +110,3 @@
 
 #endif _cris_relocate_h
 /********************** END OF FILE eshlibld.h *****************************/
-
diff -urN linux-2.4.21-bk5/include/asm-cris/etraxgpio.h linux-2.4.21-bk6/include/asm-cris/etraxgpio.h
--- linux-2.4.21-bk5/include/asm-cris/etraxgpio.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.21-bk6/include/asm-cris/etraxgpio.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,4 +1,3 @@
-/* $Id: etraxgpio.h,v 1.8 2002/06/17 15:53:07 johana Exp $ */
 /*
  * The following devices are accessable using this driver using
  * GPIO_MAJOR (120) and a couple of minor numbers:
@@ -6,11 +5,12 @@
  * /dev/gpiob  minor 1, 8 bit GPIO, each bit can change direction
  * /dev/leds   minor 2, Access to leds depending on kernelconfig
  * /dev/gpiog  minor 3
-         g0dir, g8_15dir, g16_23dir, g24 dir configurable in R_GEN_CONFIG
-         g1-g7 and g25-g31 is both input and outputs but on different pins
-         Also note that some bits change pins depending on what interfaces 
-         are enabled.
+ *       g0dir, g8_15dir, g16_23dir, g24 dir configurable in R_GEN_CONFIG
+ *       g1-g7 and g25-g31 is both input and outputs but on different pins
+ *       Also note that some bits change pins depending on what interfaces
+ *       are enabled.
  */
+
 #ifndef _ASM_ETRAXGPIO_H
 #define _ASM_ETRAXGPIO_H
 
@@ -41,7 +41,7 @@
 
 /* GPIO direction ioctl's */
 #define IO_READDIR    0x8  /* Read direction 0=input 1=output  (obsolete) */
-#define IO_SETINPUT   0x9  /* Set direction for bits set, 0=unchanged 1=input, 
+#define IO_SETINPUT   0x9  /* Set direction for bits set, 0=unchanged 1=input,
                               returns mask with current inputs (obsolete) */
 #define IO_SETOUTPUT  0xA  /* Set direction for bits set, 0=unchanged 1=output,
                               returns mask with current outputs (obsolete)*/
@@ -55,11 +55,11 @@
 #define IO_GET_PWR_BT 0xE
 
 /* Bit toggling in driver settings */
-/* bit set in low byte0 is CLK mask (0x00FF), 
-   bit set in byte1 is DATA mask    (0xFF00) 
+/* bit set in low byte0 is CLK mask (0x00FF),
+   bit set in byte1 is DATA mask    (0xFF00)
    msb, data_mask[7:0] , clk_mask[7:0]
  */
-#define IO_CFG_WRITE_MODE 0xF 
+#define IO_CFG_WRITE_MODE 0xF
 #define IO_CFG_WRITE_MODE_VALUE(msb, data_mask, clk_mask) \
   ( (((msb)&1) << 16) | (((data_mask) &0xFF) << 8) | ((clk_mask) & 0xFF) )
 
@@ -76,6 +76,4 @@
                                * *arg updated with current output pins.
                                */
 
-
-
 #endif
diff -urN linux-2.4.21-bk5/include/asm-cris/etraxi2c.h linux-2.4.21-bk6/include/asm-cris/etraxi2c.h
--- linux-2.4.21-bk5/include/asm-cris/etraxi2c.h	2001-04-06 10:51:19.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/etraxi2c.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,5 +1,3 @@
-/* $Id: etraxi2c.h,v 1.1 2001/01/18 15:49:57 bjornw Exp $ */
-
 #ifndef _LINUX_ETRAXI2C_H
 #define _LINUX_ETRAXI2C_H
 
diff -urN linux-2.4.21-bk5/include/asm-cris/etraxvirtex.h linux-2.4.21-bk6/include/asm-cris/etraxvirtex.h
--- linux-2.4.21-bk5/include/asm-cris/etraxvirtex.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.21-bk6/include/asm-cris/etraxvirtex.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,5 +1,3 @@
-/* $Id: etraxvirtex.h,v 1.1 2002/06/25 10:01:05 stefanl Exp $ */
-
 #ifndef _LINUX_ETRAXVIRTEX_FPGA_H
 #define _LINUX_ETRAXVIRTEX_FPGA_H
 
diff -urN linux-2.4.21-bk5/include/asm-cris/fasttimer.h linux-2.4.21-bk6/include/asm-cris/fasttimer.h
--- linux-2.4.21-bk5/include/asm-cris/fasttimer.h	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.21-bk6/include/asm-cris/fasttimer.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,16 +1,17 @@
-/* $Id: fasttimer.h,v 1.2 2002/05/28 17:45:34 johana Exp $
+/*
  * linux/include/asm-cris/fasttimer.h
  *
  * Fast timers for ETRAX100LX
  * This may be useful in other OS than Linux so use 2 space indentation...
- * Copyright (C) 2000, 2002 Axis Communications AB
+ * Copyright (C) 2000, 2001, 2002, 2003 Axis Communications AB
  */
+
 #include <linux/config.h>
 #include <linux/time.h> /* struct timeval */
 #include <linux/timex.h>
 
-/* The timer0 values gives 52us resolution (1/19200) or higher 
- * but interrupts at HZ 
+/* The timer0 values gives 52us resolution (1/19200) or higher
+ * but interrupts at HZ
  */
 /* We use timer1 to generate interrupts at desired times. */
 
@@ -18,7 +19,7 @@
 
 typedef void fast_timer_function_type(unsigned long);
 
-struct fast_timer{ /* Close to timer_list */
+struct fast_timer { /* Close to timer_list */
   struct fast_timer *next;
   struct fast_timer *prev;
   struct timeval tv_set;
@@ -29,6 +30,8 @@
   const char *name;
 };
 
+extern struct fast_timer *fast_timer_list;
+
 void start_one_shot_timer(struct fast_timer *t,
                           fast_timer_function_type *function,
                           unsigned long data,
@@ -38,6 +41,10 @@
 int del_fast_timer(struct fast_timer * t);
 /* return 1 if deleted */
 
+extern inline int fast_timer_pending (const struct fast_timer * t)
+{
+  return (t->next != NULL) || (t->prev != NULL) || (t == fast_timer_list);
+}
 
 void schedule_usleep(unsigned long us);
 
diff -urN linux-2.4.21-bk5/include/asm-cris/ide.h linux-2.4.21-bk6/include/asm-cris/ide.h
--- linux-2.4.21-bk5/include/asm-cris/ide.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/ide.h	2003-07-10 02:55:00.000000000 -0700
@@ -17,6 +17,9 @@
 #ifdef __KERNEL__
 
 #include <asm/svinto.h>
+#include <asm/io.h>
+#include <asm-generic/ide_iops.h>
+
 
 /* ETRAX 100 can support 4 IDE busses on the same pins (serialized) */
 
diff -urN linux-2.4.21-bk5/include/asm-cris/io.h linux-2.4.21-bk6/include/asm-cris/io.h
--- linux-2.4.21-bk5/include/asm-cris/io.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/io.h	2003-07-10 02:55:00.000000000 -0700
@@ -258,6 +258,8 @@
 
 #define IO_SPACE_LIMIT 0xffff
 #define inb(x) (0)
+#define inw(x) (0)
+#define inl(x) (0)
 #define outb(x,y)
 #define outw(x,y)
 #define outl(x,y)
diff -urN linux-2.4.21-bk5/include/asm-cris/irq.h linux-2.4.21-bk6/include/asm-cris/irq.h
--- linux-2.4.21-bk5/include/asm-cris/irq.h	2002-02-25 11:38:11.000000000 -0800
+++ linux-2.4.21-bk6/include/asm-cris/irq.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,7 +1,7 @@
 /*
  * Interrupt handling assembler and defines for Linux/CRIS
  *
- * Copyright (c) 2000, 2001 Axis Communications AB
+ * Copyright (c) 2000, 2001, 2002, 2003 Axis Communications AB
  *
  */
 
@@ -18,60 +18,60 @@
 #include <asm/sv_addr_ag.h>
 
 #define NR_IRQS 32
-#define SOME_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, some)   /* 0 ? */
-#define NMI_IRQ_NBR         IO_BITNR(R_VECT_MASK_RD, nmi)    /* 1 */
-#define TIMER0_IRQ_NBR      IO_BITNR(R_VECT_MASK_RD, timer0) /* 2 */
-#define TIMER1_IRQ_NBR      IO_BITNR(R_VECT_MASK_RD, timer1) /* 3 */
+#define SOME_IRQ_NBR           IO_BITNR(R_VECT_MASK_RD, some)   /* 0 ? */
+#define NMI_IRQ_NBR            IO_BITNR(R_VECT_MASK_RD, nmi)    /* 1 */
+#define TIMER0_IRQ_NBR         IO_BITNR(R_VECT_MASK_RD, timer0) /* 2 */
+#define TIMER1_IRQ_NBR         IO_BITNR(R_VECT_MASK_RD, timer1) /* 3 */
 /* mio, ata, par0, scsi0 on 4 */
 /* par1, scsi1 on 5 */
 #define NETWORK_STATUS_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, network) /* 6 */
 
-#define SERIAL_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, serial) /* 8 */
-#define PA_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, pa) /* 11 */
+#define SERIAL_IRQ_NBR         IO_BITNR(R_VECT_MASK_RD, serial) /* 8 */
+#define PA_IRQ_NBR             IO_BITNR(R_VECT_MASK_RD, pa) /* 11 */
 /* extdma0 and extdma1 is at irq 12 and 13 and/or same as dma5 and dma6 ? */
-#define EXTDMA0_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, ext_dma0)
-#define EXTDMA1_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, ext_dma1)
+#define EXTDMA0_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, ext_dma0)
+#define EXTDMA1_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, ext_dma1)
+
+#define BUS_FAULT_IRQ_NBR      14
+#define MULTIPLE_IRQ_NBR       15
 
 /* dma0-9 is irq 16..25 */
 /* 16,17: network */
-#define DMA0_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma0)
-#define DMA1_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma1)
+#define DMA0_TX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma0)
+#define DMA1_RX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma1)
 #define NETWORK_DMA_TX_IRQ_NBR DMA0_TX_IRQ_NBR
 #define NETWORK_DMA_RX_IRQ_NBR DMA1_RX_IRQ_NBR
 
 /* 18,19: dma2 and dma3 shared by par0, scsi0, ser2 and ata */
-#define DMA2_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma2)
-#define DMA3_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma3)
-#define SER2_DMA_TX_IRQ_NBR DMA2_TX_IRQ_NBR
-#define SER2_DMA_RX_IRQ_NBR DMA3_RX_IRQ_NBR
+#define DMA2_TX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma2)
+#define DMA3_RX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma3)
+#define SER2_DMA_TX_IRQ_NBR    DMA2_TX_IRQ_NBR
+#define SER2_DMA_RX_IRQ_NBR    DMA3_RX_IRQ_NBR
 
 /* 20,21: dma4 and dma5 shared by par1, scsi1, ser3 and extdma0 */
-#define DMA4_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma4)
-#define DMA5_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma5)
-#define SER3_DMA_TX_IRQ_NBR DMA4_TX_IRQ_NBR
-#define SER3_DMA_RX_IRQ_NBR DMA5_RX_IRQ_NBR
+#define DMA4_TX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma4)
+#define DMA5_RX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma5)
+#define SER3_DMA_TX_IRQ_NBR    DMA4_TX_IRQ_NBR
+#define SER3_DMA_RX_IRQ_NBR    DMA5_RX_IRQ_NBR
 
 /* 22,23: dma6 and dma7 shared by ser0, extdma1 and mem2mem */
-#define DMA6_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma6)
-#define DMA7_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma7)
-#define SER0_DMA_TX_IRQ_NBR DMA6_TX_IRQ_NBR
-#define SER0_DMA_RX_IRQ_NBR DMA7_RX_IRQ_NBR
+#define DMA6_TX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma6)
+#define DMA7_RX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma7)
+#define SER0_DMA_TX_IRQ_NBR    DMA6_TX_IRQ_NBR
+#define SER0_DMA_RX_IRQ_NBR    DMA7_RX_IRQ_NBR
 #define MEM2MEM_DMA_TX_IRQ_NBR DMA6_TX_IRQ_NBR
 #define MEM2MEM_DMA_RX_IRQ_NBR DMA7_RX_IRQ_NBR
 
 /* 24,25: dma8 and dma9 shared by ser1 and usb */
-#define DMA8_TX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma8)
-#define DMA9_RX_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, dma9)
-#define SER1_DMA_TX_IRQ_NBR DMA8_TX_IRQ_NBR
-#define SER1_DMA_RX_IRQ_NBR DMA9_RX_IRQ_NBR
-#define USB_DMA_TX_IRQ_NBR DMA8_TX_IRQ_NBR
-#define USB_DMA_RX_IRQ_NBR DMA9_RX_IRQ_NBR
+#define DMA8_TX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma8)
+#define DMA9_RX_IRQ_NBR        IO_BITNR(R_VECT_MASK_RD, dma9)
+#define SER1_DMA_TX_IRQ_NBR    DMA8_TX_IRQ_NBR
+#define SER1_DMA_RX_IRQ_NBR    DMA9_RX_IRQ_NBR
+#define USB_DMA_TX_IRQ_NBR     DMA8_TX_IRQ_NBR
+#define USB_DMA_RX_IRQ_NBR     DMA9_RX_IRQ_NBR
 
 /* usb: controller at irq 31 + uses DMA8 and DMA9 */
-#define USB_HC_IRQ_NBR IO_BITNR(R_VECT_MASK_RD, usb)
-
-
-
+#define USB_HC_IRQ_NBR         IO_BITNR(R_VECT_MASK_RD, usb)
 
 
 extern void disable_irq(unsigned int);
@@ -94,7 +94,7 @@
 
 #define __STR(x) #x
 #define STR(x) __STR(x)
- 
+
 /* SAVE_ALL saves registers so they match pt_regs */
 
 #define SAVE_ALL \
@@ -102,34 +102,34 @@
   "push $srp\n\t"       /* push subroutine return pointer */ \
   "push $dccr\n\t"      /* push condition codes */ \
   "push $mof\n\t"       /* push multiply overflow reg */ \
-  "di\n\t"             /* need to disable irq's at this point */\
+  "di\n\t"              /* need to disable irq's at this point */\
   "subq 14*4,$sp\n\t"   /* make room for r0-r13 */ \
   "movem $r13,[$sp]\n\t" /* push the r0-r13 registers */ \
   "push $r10\n\t"       /* push orig_r10 */ \
   "clear.d [$sp=$sp-4]\n\t"  /* frametype - this is a normal stackframe */
 
-  /* BLOCK_IRQ and UNBLOCK_IRQ do the same as mask_irq and unmask_irq in irq.c */
+/* BLOCK_IRQ and UNBLOCK_IRQ do the same as mask_irq and unmask_irq in irq.c */
 
 #define BLOCK_IRQ(mask,nr) \
   "move.d " #mask ",$r0\n\t" \
-  "move.d $r0,[0xb00000d8]\n\t" 
-  
+  "move.d $r0,[0xb00000d8]\n\t"
+
 #define UNBLOCK_IRQ(mask) \
   "move.d " #mask ",$r0\n\t" \
-  "move.d $r0,[0xb00000dc]\n\t" 
+  "move.d $r0,[0xb00000dc]\n\t"
 
 #define IRQ_NAME2(nr) nr##_interrupt(void)
 #define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
 #define sIRQ_NAME(nr) IRQ_NAME2(sIRQ##nr)
 #define BAD_IRQ_NAME(nr) IRQ_NAME2(bad_IRQ##nr)
 
-  /* the asm IRQ handler makes sure the causing IRQ is blocked, then it calls
-   * do_IRQ (with irq disabled still). after that it unblocks and jumps to
-   * ret_from_intr (entry.S)
-   *
-   * The reason the IRQ is blocked is to allow an sti() before the handler which
-   * will acknowledge the interrupt is run.
-   */
+/* the asm IRQ handler makes sure the causing IRQ is blocked, then it calls
+ * do_IRQ (with irq disabled still). after that it unblocks and jumps to
+ * ret_from_intr (entry.S)
+ *
+ * The reason the IRQ is blocked is to allow an sti() before the handler which
+ * will acknowledge the interrupt is run.
+ */
 
 #define BUILD_IRQ(nr,mask) \
 void IRQ_NAME(nr); \
@@ -154,19 +154,20 @@
           "reti\n\t" \
           "nop\n");
 
-/* This is subtle. The timer interrupt is crucial and it should not be disabled for 
- * too long. However, if it had been a normal interrupt as per BUILD_IRQ, it would
- * have been BLOCK'ed, and then softirq's are run before we return here to UNBLOCK.
- * If the softirq's take too much time to run, the timer irq won't run and the 
- * watchdog will kill us.
+/* This is subtle. The timer interrupt is crucial and it should not be disabled
+ * for too long. However, if it had been a normal interrupt as per BUILD_IRQ,
+ * it would have been BLOCK'ed, and then softirq's are run before we return
+ * here to UNBLOCK. If the softirq's take too much time to run, the timer irq
+ * won't run and the watchdog will kill us.
  *
- * Furthermore, if a lot of other irq's occur before we return here, the multiple_irq
- * handler is run and it prioritizes the timer interrupt. However if we had BLOCK'ed
- * it here, we would not get the multiple_irq at all.
+ * Furthermore, if a lot of other irq's occur before we return here, the
+ * multiple_irq handler is run and it prioritizes the timer interrupt. However
+ * if we had BLOCK'ed it here, we would not get the multiple_irq at all.
  *
- * The non-blocking here is based on the knowledge that the timer interrupt is 
- * registred as a fast interrupt (SA_INTERRUPT) so that we _know_ there will not
- * be an sti() before the timer irq handler is run to acknowledge the interrupt.
+ * The non-blocking here is based on the knowledge that the timer interrupt is
+ * registred as a fast interrupt (SA_INTERRUPT) so that we _know_ there will
+ * not be an sti() before the timer irq handler is run to acknowledge the
+ * interrupt.
  */
 
 #define BUILD_TIMER_IRQ(nr,mask) \
@@ -191,5 +192,3 @@
           "nop\n");
 
 #endif  /* _ASM_IRQ_H */
-
-
diff -urN linux-2.4.21-bk5/include/asm-cris/namei.h linux-2.4.21-bk6/include/asm-cris/namei.h
--- linux-2.4.21-bk5/include/asm-cris/namei.h	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.21-bk6/include/asm-cris/namei.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,4 +1,4 @@
-/* $Id: namei.h,v 1.1 2000/07/10 16:32:31 bjornw Exp $
+/*
  * linux/include/asm-cris/namei.h
  *
  * Included from linux/fs/namei.c
@@ -9,7 +9,7 @@
 
 /* used to find file-system prefixes for doing emulations
  * see for example asm-sparc/namei.h
- * we dont use it...
+ * we don't use it...
  */
 
 #define __emul_prefix() NULL
diff -urN linux-2.4.21-bk5/include/asm-cris/pci.h linux-2.4.21-bk6/include/asm-cris/pci.h
--- linux-2.4.21-bk5/include/asm-cris/pci.h	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.21-bk6/include/asm-cris/pci.h	2003-07-10 02:55:00.000000000 -0700
@@ -5,5 +5,6 @@
  * includes it even if CONFIG_PCI is not set.
  */
 
+#define PCI_DMA_BUS_IS_PHYS	(0)
 #endif /* __ASM_CRIS_PCI_H */
 
diff -urN linux-2.4.21-bk5/include/asm-cris/posix_types.h linux-2.4.21-bk6/include/asm-cris/posix_types.h
--- linux-2.4.21-bk5/include/asm-cris/posix_types.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/posix_types.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,5 +1,3 @@
-/* $Id: posix_types.h,v 1.2 2002/09/26 14:44:59 hp Exp $ */
-
 /* We cheat a bit and use our C-coded bitops functions from asm/bitops.h */
 /* I guess we should write these in assembler because they are used often. */
 
diff -urN linux-2.4.21-bk5/include/asm-cris/processor.h linux-2.4.21-bk6/include/asm-cris/processor.h
--- linux-2.4.21-bk5/include/asm-cris/processor.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/processor.h	2003-07-10 02:55:00.000000000 -0700
@@ -65,7 +65,7 @@
 /*
  * At user->kernel entry, the pt_regs struct is stacked on the top of the kernel-stack.
  * This macro allows us to find those regs for a task.
- * Notice that subsequent pt_regs stackings, like recursive interrupts occuring while
+ * Notice that subsequent pt_regs stackings, like recursive interrupts occurring while
  * we're in the kernel, won't affect this - only the first user->kernel transition
  * registers are reached by this.
  */
diff -urN linux-2.4.21-bk5/include/asm-cris/rs485.h linux-2.4.21-bk6/include/asm-cris/rs485.h
--- linux-2.4.21-bk5/include/asm-cris/rs485.h	2002-08-02 17:39:45.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/rs485.h	2003-07-10 02:55:00.000000000 -0700
@@ -5,7 +5,7 @@
 struct rs485_control {
         unsigned short rts_on_send;
         unsigned short rts_after_sent;
-        __u32 delay_rts_before_send;
+        unsigned long delay_rts_before_send;
         unsigned short enabled;
 #ifdef __KERNEL__
         int disable_serial_loopback;
@@ -14,7 +14,7 @@
 
 /* Used with ioctl() TIOCSERWRRS485 */
 struct rs485_write {
-        __u16 outc_size;
+        unsigned short outc_size;
         unsigned char *outc;
 };
 
diff -urN linux-2.4.21-bk5/include/asm-cris/rtc.h linux-2.4.21-bk6/include/asm-cris/rtc.h
--- linux-2.4.21-bk5/include/asm-cris/rtc.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/rtc.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,9 +1,6 @@
-/* $Id: rtc.h,v 1.8 2002/11/20 14:27:25 oskarp Exp $ */
-
 #ifndef __RTC_H__
 #define __RTC_H__
 
-
 #include <linux/config.h>
 
 #ifdef CONFIG_ETRAX_DS1302
@@ -20,13 +17,13 @@
    /* Bits in CONTROL register. */
 #  define RTC_CONTROL_WRITEPROTECT 	0x80
 #  define RTC_TRICKLECHARGER 		8
-  
+
   /* Bits in TRICKLECHARGER register TCS TCS TCS TCS DS DS RS RS. */
 #  define RTC_TCR_PATTERN 	0xA0	/* 1010xxxx */
 #  define RTC_TCR_1DIOD 	0x04	/* xxxx01xx */
 #  define RTC_TCR_2DIOD 	0x08	/* xxxx10xx */
 #  define RTC_TCR_DISABLED 	0x00	/* xxxxxx00 Disabled */
-#  define RTC_TCR_2KOHM 	0x01	/* xxxxxx01 2KOhm */
+#  define RTC_TCR_2KOHM 	0x01	/* xxxxxx01 2kOhm */
 #  define RTC_TCR_4KOHM 	0x02	/* xxxxxx10 4kOhm */
 #  define RTC_TCR_8KOHM 	0x03	/* xxxxxx11 8kOhm */
 
@@ -36,11 +33,11 @@
 #  define RTC_I2C_WRITE		0xa2
 
    /* Phillips PCF8563 registers. */
-#  define RTC_CONTROL1		0x00		/* Control/Status register 1. */
-#  define RTC_CONTROL2		0x01		/* Control/Status register 2. */
-#  define RTC_CLOCKOUT_FREQ	0x0d		/* CLKOUT frequency. */
-#  define RTC_TIMER_CONTROL	0x0e		/* Timer control. */
-#  define RTC_TIMER_CNTDOWN	0x0f		/* Timer countdown. */
+#  define RTC_CONTROL1		0x00	/* Control/Status register 1. */
+#  define RTC_CONTROL2		0x01	/* Control/Status register 2. */
+#  define RTC_CLOCKOUT_FREQ	0x0d	/* CLKOUT frequency. */
+#  define RTC_TIMER_CONTROL	0x0e	/* Timer control. */
+#  define RTC_TIMER_CNTDOWN	0x0f	/* Timer countdown. */
 
    /* BCD encoded clock registers. */
 #  define RTC_SECONDS		0x02
@@ -53,7 +50,26 @@
 #  define RTC_MINUTE_ALARM	0x09
 #  define RTC_HOUR_ALARM	0x0a
 #  define RTC_DAY_ALARM		0x0b
-#  define RTC_WEEKDAY_ALARM 0x0c	/* Not coded in BCD! */
+#  define RTC_WEEKDAY_ALARM	0x0c	/* Not coded in BCD! */
+
+/* Control/Status 2 bits */
+#  define RTC_CTRL2_TIE		0x01	/* Timer Interrupt Enable */
+#  define RTC_CTRL2_AIE		0x02	/* Alarm Interrupt Enable */
+#  define RTC_CTRL2_TF		0x04	/* Timer Flag */
+#  define RTC_CTRL2_AF		0x08	/* Alarm Flag */
+#  define RTC_CTRL2_TP		0x10	/* Timer Pulse */
+
+/* Timer Control bits */
+#  define RTC_TC_TD0		0x01
+#  define RTC_TC_TD1		0x02
+#  define RTC_TC_TE		0x80	/* Timer Enable */
+
+/* Timer source clock frequency */
+#  define RTC_TC_FREQ_MASK	(RTC_TC_TD0 | RTC_TC_TD1)
+#  define RTC_TC_FREQ_4096	(0)
+#  define RTC_TC_FREQ_64	(RTC_TC_TD0)
+#  define RTC_TC_FREQ_1		(RTC_TC_TD1)
+#  define RTC_TC_FREQ_1_60	(RTC_TC_TD0 | RTC_TC_TD1)
 
 #endif
 
@@ -84,7 +100,7 @@
 
 /*
  * The struct used to pass data via the following ioctl. Similar to the
- * struct tm in <time.h>, but it needs to be here so that the kernel 
+ * struct tm in <time.h>, but it needs to be here so that the kernel
  * source is self contained, allowing cross-compiles, etc. etc.
  */
 struct rtc_time {
@@ -101,9 +117,11 @@
 
 /* ioctl() calls that are permitted to the /dev/rtc interface. */
 #define RTC_MAGIC 'p'
-#define RTC_RD_TIME		_IOR(RTC_MAGIC, 0x09, struct rtc_time)	/* Read RTC time. */
-#define RTC_SET_TIME		_IOW(RTC_MAGIC, 0x0a, struct rtc_time)	/* Set RTC time. */
-#define RTC_SET_CHARGE  	_IOW(RTC_MAGIC, 0x0b, int) 		
-#define RTC_MAX_IOCTL 0x0b
+#define RTC_RD_TIME     _IOR(RTC_MAGIC, 0x09, struct rtc_time)	/* Read RTC time. */
+#define RTC_SET_TIME    _IOW(RTC_MAGIC, 0x0a, struct rtc_time)	/* Set RTC time. */
+#define RTC_SET_CHARGE  _IOW(RTC_MAGIC, 0x0b, int)
+#define RTC_VLOW_RD     _IOR(RTC_MAGIC, 0x11, int)  /* Voltage Low detector */
+#define RTC_VLOW_SET    _IO(RTC_MAGIC, 0x12)        /* Clear voltage low information */
+#define RTC_MAX_IOCTL 0x12
 
 #endif /* __RTC_H__ */
diff -urN linux-2.4.21-bk5/include/asm-cris/semaphore-helper.h linux-2.4.21-bk6/include/asm-cris/semaphore-helper.h
--- linux-2.4.21-bk5/include/asm-cris/semaphore-helper.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/semaphore-helper.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,7 +1,6 @@
-/* $Id: semaphore-helper.h,v 1.4 2002/11/20 18:20:17 hp Exp $
- *
+/*
  * SMP- and interrupt-safe semaphores helper functions. Generic versions, no
- * optimizations whatsoever... 
+ * optimizations whatsoever...
  *
  */
 
@@ -73,5 +72,3 @@
 }
 
 #endif /* _ASM_SEMAPHORE_HELPER_H */
-
-
diff -urN linux-2.4.21-bk5/include/asm-cris/semaphore.h linux-2.4.21-bk6/include/asm-cris/semaphore.h
--- linux-2.4.21-bk5/include/asm-cris/semaphore.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/semaphore.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,5 +1,3 @@
-/* $Id: semaphore.h,v 1.5 2002/12/02 08:14:08 starvik Exp $ */
-
 /* On the i386 these are coded in asm, perhaps we should as well. Later.. */
 
 #ifndef _CRIS_SEMAPHORE_H
@@ -15,7 +13,7 @@
 #include <asm/atomic.h>
 
 /*
- * CRIS semaphores, implemented in C-only so far. 
+ * CRIS semaphores, implemented in C-only so far.
  */
 
 int printk(const char *fmt, ...);
@@ -140,7 +138,7 @@
  * jumps for both down() and up().
  */
 extern inline void up(struct semaphore * sem)
-{  
+{
 	unsigned long flags;
 	int wakeup;
 
diff -urN linux-2.4.21-bk5/include/asm-cris/sigcontext.h linux-2.4.21-bk6/include/asm-cris/sigcontext.h
--- linux-2.4.21-bk5/include/asm-cris/sigcontext.h	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.21-bk6/include/asm-cris/sigcontext.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,16 +1,14 @@
-/* $Id: sigcontext.h,v 1.1 2000/07/10 16:32:31 bjornw Exp $ */
-
 #ifndef _ASM_CRIS_SIGCONTEXT_H
 #define _ASM_CRIS_SIGCONTEXT_H
 
 #include <asm/ptrace.h>
 
-/* This struct is saved by setup_frame in signal.c, to keep the current context while
-   a signal handler is executed. It's restored by sys_sigreturn.
-   
-   To keep things simple, we use pt_regs here even though normally you just specify
-   the list of regs to save. Then we can use copy_from_user on the entire regs instead
-   of a bunch of get_user's as well...
+/* This struct is saved by setup_frame in signal.c, to keep the current context
+   while a signal handler is executed. It's restored by sys_sigreturn.
+
+   To keep things simple, we use pt_regs here even though normally you just
+   specify the list of regs to save. Then we can use copy_from_user on the
+   entire regs instead of a bunch of get_user's as well...
 
 */
 
@@ -21,4 +19,3 @@
 };
 
 #endif
-
diff -urN linux-2.4.21-bk5/include/asm-cris/system.h linux-2.4.21-bk6/include/asm-cris/system.h
--- linux-2.4.21-bk5/include/asm-cris/system.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/system.h	2003-07-10 02:55:00.000000000 -0700
@@ -14,6 +14,15 @@
 #define switch_to(prev,next,last) last = resume(prev,next, \
 					 (int)&((struct task_struct *)0)->thread)
 
+/* read the CPU PC register */
+
+extern inline unsigned long rdpc(void)
+{ 
+	unsigned long pc;
+	__asm__ volatile ("move.d $pc,%0" : "=rm" (pc));
+	return pc;
+}
+
 /* read the CPU version register */
 
 extern inline unsigned long rdvr(void) { 
@@ -54,6 +63,7 @@
 struct __xchg_dummy { unsigned long a[100]; };
 #define __xg(x) ((struct __xchg_dummy *)(x))
 
+#ifdef CONFIG_ETRAX_DEBUG_INTERRUPT
 #if 0
 /* use these and an oscilloscope to see the fraction of time we're running with IRQ's disabled */
 /* it assumes the LED's are on port 0x90000000 of course. */
@@ -62,6 +72,60 @@
 #define save_flags(x) __asm__ __volatile__ ("move $ccr,%0" : "=rm" (x) : : "memory");
 #define restore_flags(x) __asm__ __volatile__ ("move %0,$ccr\n\tbtstq 5,%0\n\tbpl 1f\n\tnop\n\tpush $r0\n\tmoveq 0,$r0\n\tmove.d $r0,[0x90000000]\n\tpop $r0\n1:\n" : : "r" (x) : "memory");
 #else
+
+/* Log when interrupts are turned on and off and who did it. */
+#define CCR_EI_MASK (1 << 5)
+/* in debug.c */
+extern int log_int_pos;
+extern int log_int_size;
+extern int log_int_enable;
+extern int log_int_trig0_pos;
+extern int log_int_trig1_pos;
+extern void log_int(unsigned long pc, unsigned long prev_ccr, unsigned long next_ccr);
+
+/* If you only want to log changes - change to 1 to a 0 below */
+#define LOG_INT(pc, curr_ccr, next_ccr) do { \
+  if (1 || (curr_ccr ^ next_ccr) & CCR_EI_MASK) \
+          log_int((pc), curr_ccr, next_ccr); \
+  }while(0)
+
+#define __save_flags(x) __asm__ __volatile__ ("move $ccr,%0" : "=rm" (x) : : "memory");
+
+extern inline void __cli(void)
+{
+  unsigned long pc = rdpc();  
+  unsigned long curr_ccr; __save_flags(curr_ccr); 
+  LOG_INT(pc, curr_ccr, 0); 
+  __asm__ __volatile__ ( "di" : : :"memory");
+}
+
+
+extern inline void __sti(void)
+{
+  unsigned long pc = rdpc();  
+  unsigned long curr_ccr; __save_flags(curr_ccr); 
+  LOG_INT(pc, curr_ccr, CCR_EI_MASK); 
+  __asm__ __volatile__ ( "ei" : : :"memory");
+}
+
+extern inline void __restore_flags(unsigned long x)
+{
+  unsigned long pc = rdpc();
+  unsigned long curr_ccr; __save_flags(curr_ccr);
+  LOG_INT(pc, curr_ccr, x);   
+  __asm__ __volatile__ ("move %0,$ccr" : : "rm" (x) : "memory");
+}
+
+/* For spinlocks etc */
+#define local_irq_save(x) do { __save_flags(x); __cli(); }while (0)
+#define local_irq_restore(x) restore_flags(x)
+
+#define local_irq_disable()  cli()
+#define local_irq_enable()   sti()
+
+#endif
+
+#else
 #define __cli() __asm__ __volatile__ ( "di" : : :"memory");
 #define __sti() __asm__ __volatile__ ( "ei" : : :"memory");
 #define __save_flags(x) __asm__ __volatile__ ("move $ccr,%0" : "=rm" (x) : : "memory");
diff -urN linux-2.4.21-bk5/include/asm-cris/termbits.h linux-2.4.21-bk6/include/asm-cris/termbits.h
--- linux-2.4.21-bk5/include/asm-cris/termbits.h	2001-02-08 16:32:44.000000000 -0800
+++ linux-2.4.21-bk6/include/asm-cris/termbits.h	2003-07-10 02:55:00.000000000 -0700
@@ -1,5 +1,3 @@
-/* $Id: termbits.h,v 1.1 2000/07/10 16:32:31 bjornw Exp $ */
-
 #ifndef __ARCH_ETRAX100_TERMBITS_H__
 #define __ARCH_ETRAX100_TERMBITS_H__
 
@@ -128,6 +126,7 @@
 #define  B1843200 0010006
 #define  B6250000 0010007
 #define CIBAUD	  002003600000	/* input baud rate (not used) */
+#define CMSPAR	  010000000000		/* mark or space (stick) parity, PARODD => mark parity */
 #define CRTSCTS	  020000000000		/* flow control */
 
 /* c_lflag bits */
diff -urN linux-2.4.21-bk5/include/asm-cris/timex.h linux-2.4.21-bk6/include/asm-cris/timex.h
--- linux-2.4.21-bk5/include/asm-cris/timex.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/timex.h	2003-07-10 02:55:00.000000000 -0700
@@ -26,6 +26,8 @@
 
 unsigned long get_ns_in_jiffie(void);
 
+unsigned long timer_data_to_ns(unsigned long timer_data);
+
 extern inline unsigned long get_us_in_jiffie_highres(void)
 {
 	return get_ns_in_jiffie()/1000;
diff -urN linux-2.4.21-bk5/include/asm-cris/uaccess.h linux-2.4.21-bk6/include/asm-cris/uaccess.h
--- linux-2.4.21-bk5/include/asm-cris/uaccess.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-cris/uaccess.h	2003-07-10 02:55:00.000000000 -0700
@@ -3,6 +3,15 @@
  *	       Hans-Peter Nilsson (hp@axis.com)
  *
  * $Log: uaccess.h,v $
+ * Revision 1.12  2003/06/17 14:00:42  starvik
+ * Merge of Linux 2.4.21
+ *
+ * Revision 1.11  2003/06/04 19:36:45  hp
+ * Remove unused copy-pasted register clobber from __asm_clear
+ *
+ * Revision 1.10  2003/04/09 08:22:38  pkj
+ * Typo correction (taken from Linux 2.5).
+ *
  * Revision 1.9  2002/11/20 18:20:17  hp
  * Make all static inline functions extern inline.
  *
@@ -129,7 +138,7 @@
  * This gets kind of ugly. We want to return _two_ values in "get_user()"
  * and yet we don't want to do any pointers, because that is too much
  * of a performance impact. Thus we have a few rather ugly macros here,
- * and hide all the uglyness from the user.
+ * and hide all the ugliness from the user.
  *
  * The "__xxx" versions of the user access functions are versions that
  * do not verify the address space, that must have been done previously
@@ -775,7 +784,7 @@
 		"	.previous"			\
 		: "=r" (to), "=r" (ret)			\
 		: "0" (to), "1" (ret)			\
-		: "r9", "memory")
+		: "memory")
 
 #define __asm_clear_1(to, ret) \
 	__asm_clear(to, ret,			\
diff -urN linux-2.4.21-bk5/include/asm-i386/acpi.h linux-2.4.21-bk6/include/asm-i386/acpi.h
--- linux-2.4.21-bk5/include/asm-i386/acpi.h	2003-07-10 02:54:56.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-i386/acpi.h	2003-07-10 02:55:00.000000000 -0700
@@ -165,6 +165,9 @@
 /* early initialization routine */
 extern void acpi_reserve_bootmem(void);
 
+/* Check for special HW using OEM name lists */
+extern void acpi_madt_oem_check(char *oem_id, char *oem_table_id);
+
 #endif /*CONFIG_ACPI_SLEEP*/
 
 
diff -urN linux-2.4.21-bk5/include/asm-ppc/posix_types.h linux-2.4.21-bk6/include/asm-ppc/posix_types.h
--- linux-2.4.21-bk5/include/asm-ppc/posix_types.h	2003-06-13 07:51:38.000000000 -0700
+++ linux-2.4.21-bk6/include/asm-ppc/posix_types.h	2003-07-10 02:55:00.000000000 -0700
@@ -8,7 +8,7 @@
  */
 
 typedef unsigned int	__kernel_dev_t;
-typedef unsigned int	__kernel_ino_t;
+typedef unsigned long	__kernel_ino_t;
 typedef unsigned int	__kernel_mode_t;
 typedef unsigned short	__kernel_nlink_t;
 typedef long		__kernel_off_t;
diff -urN linux-2.4.21-bk5/include/asm-sparc/ipc.h linux-2.4.21-bk6/include/asm-sparc/ipc.h
--- linux-2.4.21-bk5/include/asm-sparc/ipc.h	1999-12-20 22:05:52.000000000 -0800
+++ linux-2.4.21-bk6/include/asm-sparc/ipc.h	2003-07-10 02:55:00.000000000 -0700
@@ -14,6 +14,7 @@
 #define SEMOP		 1
 #define SEMGET		 2
 #define SEMCTL		 3
+#define SEMTIMEDOP	 4
 #define MSGSND		11
 #define MSGRCV		12
 #define MSGGET		13
diff -urN linux-2.4.21-bk5/include/asm-sparc64/ipc.h linux-2.4.21-bk6/include/asm-sparc64/ipc.h
--- linux-2.4.21-bk5/include/asm-sparc64/ipc.h	1999-12-20 22:05:52.000000000 -0800
+++ linux-2.4.21-bk6/include/asm-sparc64/ipc.h	2003-07-10 02:55:00.000000000 -0700
@@ -14,6 +14,7 @@
 #define SEMOP		 1
 #define SEMGET		 2
 #define SEMCTL		 3
+#define SEMTIMEDOP	 4
 #define MSGSND		11
 #define MSGRCV		12
 #define MSGGET		13
diff -urN linux-2.4.21-bk5/include/linux/fs.h linux-2.4.21-bk6/include/linux/fs.h
--- linux-2.4.21-bk5/include/linux/fs.h	2003-07-10 02:54:56.000000000 -0700
+++ linux-2.4.21-bk6/include/linux/fs.h	2003-07-10 02:55:00.000000000 -0700
@@ -395,7 +395,7 @@
 	int (*flushpage) (struct page *, unsigned long);
 	int (*releasepage) (struct page *, int);
 #define KERNEL_HAS_O_DIRECT /* this is for modules out of the kernel */
-	int (*direct_IO)(int, struct file *, struct kiobuf *, unsigned long, int);
+	int (*direct_IO)(int, struct inode *, struct kiobuf *, unsigned long, int);
 	void (*removepage)(struct page *); /* called when page gets removed from the inode */
 };
 
@@ -1537,6 +1537,9 @@
 extern int inode_change_ok(struct inode *, struct iattr *);
 extern int inode_setattr(struct inode *, struct iattr *);
 
+/* kernel/fork.c */
+extern int unshare_files(void);
+
 /*
  * Common dentry functions for inclusion in the VFS
  * or in other stackable file systems.  Some of these
diff -urN linux-2.4.21-bk5/include/linux/nfs_fs.h linux-2.4.21-bk6/include/linux/nfs_fs.h
--- linux-2.4.21-bk5/include/linux/nfs_fs.h	2003-07-10 02:54:56.000000000 -0700
+++ linux-2.4.21-bk6/include/linux/nfs_fs.h	2003-06-13 07:51:38.000000000 -0700
@@ -274,11 +274,6 @@
 #define NFS_TestClearPageSync(page)	test_and_clear_bit(PG_fs_1, &(page)->flags)
 
 /*
- * linux/fs/nfs/direct.c
- */
-extern int  nfs_direct_IO(int, struct file *, struct kiobuf *, unsigned long, int);
-
-/*
  * linux/fs/mount_clnt.c
  * (Used only by nfsroot module)
  */
@@ -307,23 +302,6 @@
 	return __nfs_refresh_inode(inode,fattr);
 }
 
-/*
- * This function will be used to simulate weak cache consistency
- * under NFSv2 when the NFSv3 attribute patch is included.
- * For the moment, we just call nfs_refresh_inode().
- */
-static __inline__ int
-nfs_write_attributes(struct inode *inode, struct nfs_fattr *fattr)
-{
-	if ((fattr->valid & NFS_ATTR_FATTR) && !(fattr->valid & NFS_ATTR_WCC)) {
-		fattr->pre_size  = NFS_CACHE_ISIZE(inode);
-		fattr->pre_mtime = NFS_CACHE_MTIME(inode);
-		fattr->pre_ctime = NFS_CACHE_CTIME(inode);
-		fattr->valid |= NFS_ATTR_WCC;
-	}
-	return nfs_refresh_inode(inode, fattr);
-}
-
 static inline loff_t
 nfs_size_to_loff_t(__u64 size)
 {
diff -urN linux-2.4.21-bk5/include/linux/nfs_xdr.h linux-2.4.21-bk6/include/linux/nfs_xdr.h
--- linux-2.4.21-bk5/include/linux/nfs_xdr.h	2003-07-10 02:54:56.000000000 -0700
+++ linux-2.4.21-bk6/include/linux/nfs_xdr.h	2002-11-28 15:53:15.000000000 -0800
@@ -59,7 +59,7 @@
 /* Arguments to the read call.
  * Note that NFS_READ_MAXIOV must be <= (MAX_IOVEC-2) from sunrpc/xprt.h
  */
-#define NFS_READ_MAXIOV		(9)
+#define NFS_READ_MAXIOV 8
 
 struct nfs_readargs {
 	struct nfs_fh *		fh;
@@ -78,7 +78,7 @@
 /* Arguments to the write call.
  * Note that NFS_WRITE_MAXIOV must be <= (MAX_IOVEC-2) from sunrpc/xprt.h
  */
-#define NFS_WRITE_MAXIOV	(9)
+#define NFS_WRITE_MAXIOV        8
 struct nfs_writeargs {
 	struct nfs_fh *		fh;
 	__u64			offset;
diff -urN linux-2.4.21-bk5/include/net/pkt_sched.h linux-2.4.21-bk6/include/net/pkt_sched.h
--- linux-2.4.21-bk5/include/net/pkt_sched.h	2002-08-02 17:39:46.000000000 -0700
+++ linux-2.4.21-bk6/include/net/pkt_sched.h	2003-07-10 02:55:00.000000000 -0700
@@ -59,7 +59,7 @@
 	int 			(*enqueue)(struct sk_buff *, struct Qdisc *);
 	struct sk_buff *	(*dequeue)(struct Qdisc *);
 	int 			(*requeue)(struct sk_buff *, struct Qdisc *);
-	int			(*drop)(struct Qdisc *);
+	unsigned int		(*drop)(struct Qdisc *);
 
 	int			(*init)(struct Qdisc *, struct rtattr *arg);
 	void			(*reset)(struct Qdisc *);
diff -urN linux-2.4.21-bk5/kernel/ksyms.c linux-2.4.21-bk6/kernel/ksyms.c
--- linux-2.4.21-bk5/kernel/ksyms.c	2003-07-10 02:54:56.000000000 -0700
+++ linux-2.4.21-bk6/kernel/ksyms.c	2003-07-10 02:55:00.000000000 -0700
@@ -590,6 +590,7 @@
 
 EXPORT_SYMBOL(tasklist_lock);
 EXPORT_SYMBOL(pidhash);
+EXPORT_SYMBOL(unshare_files);
 
 /* debug */
 EXPORT_SYMBOL(dump_stack);
diff -urN linux-2.4.21-bk5/mm/filemap.c linux-2.4.21-bk6/mm/filemap.c
--- linux-2.4.21-bk5/mm/filemap.c	2003-07-10 02:54:56.000000000 -0700
+++ linux-2.4.21-bk6/mm/filemap.c	2003-07-10 02:55:00.000000000 -0700
@@ -1621,7 +1621,7 @@
 		if (retval)
 			break;
 
-		retval = mapping->a_ops->direct_IO(rw, filp, iobuf, (offset+progress) >> blocksize_bits, blocksize);
+		retval = mapping->a_ops->direct_IO(rw, inode, iobuf, (offset+progress) >> blocksize_bits, blocksize);
 
 		if (rw == READ && retval > 0)
 			mark_dirty_kiobuf(iobuf, retval);
diff -urN linux-2.4.21-bk5/mm/oom_kill.c linux-2.4.21-bk6/mm/oom_kill.c
--- linux-2.4.21-bk5/mm/oom_kill.c	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.21-bk6/mm/oom_kill.c	2003-07-10 02:55:00.000000000 -0700
@@ -61,6 +61,10 @@
 
 	if (!p->mm)
 		return 0;
+
+	if (p->flags & PF_MEMDIE)
+		return 0;
+
 	/*
 	 * The memory size of the process is the basis for the badness.
 	 */
diff -urN linux-2.4.21-bk5/net/ipv4/ipconfig.c linux-2.4.21-bk6/net/ipv4/ipconfig.c
--- linux-2.4.21-bk5/net/ipv4/ipconfig.c	2002-11-28 15:53:15.000000000 -0800
+++ linux-2.4.21-bk6/net/ipv4/ipconfig.c	2003-07-10 02:55:00.000000000 -0700
@@ -1114,6 +1114,10 @@
 				       "nameserver %u.%u.%u.%u\n",
 				       NIPQUAD(ic_nameservers[i]));
 	}
+	if (ic_servaddr != INADDR_NONE)
+		len += sprintf(buffer + len,
+			       "bootserver %u.%u.%u.%u\n",
+			       NIPQUAD(ic_servaddr));
 
 	if (offset > len)
 		offset = len;
diff -urN linux-2.4.21-bk5/net/netsyms.c linux-2.4.21-bk6/net/netsyms.c
--- linux-2.4.21-bk5/net/netsyms.c	2003-07-10 02:54:57.000000000 -0700
+++ linux-2.4.21-bk6/net/netsyms.c	2003-07-10 02:55:00.000000000 -0700
@@ -560,6 +560,7 @@
 #ifdef CONFIG_NET_SCHED
 PSCHED_EXPORTLIST;
 EXPORT_SYMBOL(pfifo_qdisc_ops);
+EXPORT_SYMBOL(bfifo_qdisc_ops);
 EXPORT_SYMBOL(register_qdisc);
 EXPORT_SYMBOL(unregister_qdisc);
 EXPORT_SYMBOL(qdisc_get_rtab);
diff -urN linux-2.4.21-bk5/net/packet/af_packet.c linux-2.4.21-bk6/net/packet/af_packet.c
--- linux-2.4.21-bk5/net/packet/af_packet.c	2002-08-02 17:39:46.000000000 -0700
+++ linux-2.4.21-bk6/net/packet/af_packet.c	2003-07-10 02:55:00.000000000 -0700
@@ -1378,8 +1378,13 @@
 		po = sk->protinfo.af_packet;
 
 		switch (msg) {
-		case NETDEV_DOWN:
 		case NETDEV_UNREGISTER:
+#ifdef CONFIG_PACKET_MULTICAST
+			if (po->mclist)
+				packet_dev_mclist(dev, po->mclist, -1);
+			// fallthrough
+#endif
+		case NETDEV_DOWN:
 			if (dev->ifindex == po->ifindex) {
 				spin_lock(&po->bind_lock);
 				if (po->running) {
@@ -1396,10 +1401,6 @@
 				}
 				spin_unlock(&po->bind_lock);
 			}
-#ifdef CONFIG_PACKET_MULTICAST
-			if (po->mclist)
-				packet_dev_mclist(dev, po->mclist, -1);
-#endif
 			break;
 		case NETDEV_UP:
 			spin_lock(&po->bind_lock);
@@ -1409,10 +1410,6 @@
 				po->running = 1;
 			}
 			spin_unlock(&po->bind_lock);
-#ifdef CONFIG_PACKET_MULTICAST
-			if (po->mclist)
-				packet_dev_mclist(dev, po->mclist, +1);
-#endif
 			break;
 		}
 	}
diff -urN linux-2.4.21-bk5/net/sched/sch_atm.c linux-2.4.21-bk6/net/sched/sch_atm.c
--- linux-2.4.21-bk5/net/sched/sch_atm.c	2003-07-10 02:54:57.000000000 -0700
+++ linux-2.4.21-bk6/net/sched/sch_atm.c	2003-07-10 02:55:00.000000000 -0700
@@ -547,15 +547,16 @@
 }
 
 
-static int atm_tc_drop(struct Qdisc *sch)
+static unsigned int atm_tc_drop(struct Qdisc *sch)
 {
 	struct atm_qdisc_data *p = PRIV(sch);
 	struct atm_flow_data *flow;
+	unsigned int len;
 
 	DPRINTK("atm_tc_drop(sch %p,[qdisc %p])\n",sch,p);
 	for (flow = p->flows; flow; flow = flow->next)
-		if (flow->q->ops->drop && flow->q->ops->drop(flow->q))
-			return 1;
+		if (flow->q->ops->drop && (len = flow->q->ops->drop(flow->q)))
+			return len;
 	return 0;
 }
 
diff -urN linux-2.4.21-bk5/net/sched/sch_cbq.c linux-2.4.21-bk6/net/sched/sch_cbq.c
--- linux-2.4.21-bk5/net/sched/sch_cbq.c	2003-07-10 02:54:57.000000000 -0700
+++ linux-2.4.21-bk6/net/sched/sch_cbq.c	2003-07-10 02:55:00.000000000 -0700
@@ -1231,11 +1231,12 @@
 	}
 }
 
-static int cbq_drop(struct Qdisc* sch)
+static unsigned int cbq_drop(struct Qdisc* sch)
 {
 	struct cbq_sched_data *q = (struct cbq_sched_data *)sch->data;
 	struct cbq_class *cl, *cl_head;
 	int prio;
+	unsigned int len;
 
 	for (prio = TC_CBQ_MAXPRIO; prio >= 0; prio--) {
 		if ((cl_head = q->active[prio]) == NULL)
@@ -1243,9 +1244,9 @@
 
 		cl = cl_head;
 		do {
-			if (cl->q->ops->drop && cl->q->ops->drop(cl->q)) {
+			if (cl->q->ops->drop && (len = cl->q->ops->drop(cl->q))) {
 				sch->q.qlen--;
-				return 1;
+				return len;
 			}
 		} while ((cl = cl->next_alive) != cl_head);
 	}
diff -urN linux-2.4.21-bk5/net/sched/sch_dsmark.c linux-2.4.21-bk6/net/sched/sch_dsmark.c
--- linux-2.4.21-bk5/net/sched/sch_dsmark.c	2001-12-21 09:42:06.000000000 -0800
+++ linux-2.4.21-bk6/net/sched/sch_dsmark.c	2003-07-10 02:55:00.000000000 -0700
@@ -301,17 +301,18 @@
 }
 
 
-static int dsmark_drop(struct Qdisc *sch)
+static unsigned int dsmark_drop(struct Qdisc *sch)
 {
 	struct dsmark_qdisc_data *p = PRIV(sch);
-
+	unsigned int len;
+	
 	DPRINTK("dsmark_reset(sch %p,[qdisc %p])\n",sch,p);
 	if (!p->q->ops->drop)
 		return 0;
-	if (!p->q->ops->drop(p->q))
+	if (!(len = p->q->ops->drop(p->q)))
 		return 0;
 	sch->q.qlen--;
-	return 1;
+	return len;
 }
 
 
diff -urN linux-2.4.21-bk5/net/sched/sch_fifo.c linux-2.4.21-bk6/net/sched/sch_fifo.c
--- linux-2.4.21-bk5/net/sched/sch_fifo.c	2001-12-21 09:42:06.000000000 -0800
+++ linux-2.4.21-bk6/net/sched/sch_fifo.c	2003-07-10 02:55:00.000000000 -0700
@@ -80,16 +80,17 @@
 	return skb;
 }
 
-static int
+static unsigned int 
 fifo_drop(struct Qdisc* sch)
 {
 	struct sk_buff *skb;
 
 	skb = __skb_dequeue_tail(&sch->q);
 	if (skb) {
-		sch->stats.backlog -= skb->len;
+		unsigned int len = skb->len;
+		sch->stats.backlog -= len;
 		kfree_skb(skb);
-		return 1;
+		return len;
 	}
 	return 0;
 }
diff -urN linux-2.4.21-bk5/net/sched/sch_gred.c linux-2.4.21-bk6/net/sched/sch_gred.c
--- linux-2.4.21-bk5/net/sched/sch_gred.c	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.21-bk6/net/sched/sch_gred.c	2003-07-10 02:55:00.000000000 -0700
@@ -259,8 +259,7 @@
 	return NULL;
 }
 
-static int
-gred_drop(struct Qdisc* sch)
+static unsigned int gred_drop(struct Qdisc* sch)
 {
 	struct sk_buff *skb;
 
@@ -269,20 +268,21 @@
 
 	skb = __skb_dequeue_tail(&sch->q);
 	if (skb) {
-		sch->stats.backlog -= skb->len;
+		unsigned int len = skb->len;
+		sch->stats.backlog -= len;
 		sch->stats.drops++;
 		q= t->tab[(skb->tc_index&0xf)];
 		if (q) {
-			q->backlog -= skb->len;
+			q->backlog -= len;
 			q->other++;
 			if (!q->backlog && !t->eqp)
 				PSCHED_GET_TIME(q->qidlestart);
-			} else {
-				D2PRINTK("gred_dequeue: skb has bad tcindex %x\n",skb->tc_index&0xf); 
-			}
+		} else {
+			D2PRINTK("gred_dequeue: skb has bad tcindex %x\n",skb->tc_index&0xf); 
+		}
 
 		kfree_skb(skb);
-		return 1;
+		return len;
 	}
 
 	q=t->tab[t->def];
diff -urN linux-2.4.21-bk5/net/sched/sch_htb.c linux-2.4.21-bk6/net/sched/sch_htb.c
--- linux-2.4.21-bk5/net/sched/sch_htb.c	2003-07-10 02:54:57.000000000 -0700
+++ linux-2.4.21-bk6/net/sched/sch_htb.c	2003-07-10 02:55:00.000000000 -0700
@@ -1070,7 +1070,7 @@
 }
 
 /* try to drop from each class (by prio) until one succeed */
-static int htb_drop(struct Qdisc* sch)
+static unsigned int htb_drop(struct Qdisc* sch)
 {
 	struct htb_sched *q = (struct htb_sched *)sch->data;
 	int prio;
@@ -1078,14 +1078,15 @@
 	for (prio = TC_HTB_NUMPRIO - 1; prio >= 0; prio--) {
 		struct list_head *p;
 		list_for_each (p,q->drops+prio) {
-			struct htb_class *cl = list_entry(p,struct htb_class,
-					un.leaf.drop_list);
+			struct htb_class *cl = list_entry(p, struct htb_class,
+							  un.leaf.drop_list);
+			unsigned int len;
 			if (cl->un.leaf.q->ops->drop && 
-				cl->un.leaf.q->ops->drop(cl->un.leaf.q)) {
+				(len = cl->un.leaf.q->ops->drop(cl->un.leaf.q))) {
 				sch->q.qlen--;
 				if (!cl->un.leaf.q->q.qlen)
 					htb_deactivate (q,cl);
-				return 1;
+				return len;
 			}
 		}
 	}
diff -urN linux-2.4.21-bk5/net/sched/sch_ingress.c linux-2.4.21-bk6/net/sched/sch_ingress.c
--- linux-2.4.21-bk5/net/sched/sch_ingress.c	2003-07-10 02:54:57.000000000 -0700
+++ linux-2.4.21-bk6/net/sched/sch_ingress.c	2003-07-10 02:55:00.000000000 -0700
@@ -190,7 +190,7 @@
 	return 0;
 }
 
-static int ingress_drop(struct Qdisc *sch)
+static unsigned int ingress_drop(struct Qdisc *sch)
 {
 #ifdef DEBUG_INGRESS
 	struct ingress_qdisc_data *p = PRIV(sch);
diff -urN linux-2.4.21-bk5/net/sched/sch_prio.c linux-2.4.21-bk6/net/sched/sch_prio.c
--- linux-2.4.21-bk5/net/sched/sch_prio.c	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.21-bk6/net/sched/sch_prio.c	2003-07-10 02:55:00.000000000 -0700
@@ -124,18 +124,18 @@
 
 }
 
-static int
-prio_drop(struct Qdisc* sch)
+static unsigned int prio_drop(struct Qdisc* sch)
 {
 	struct prio_sched_data *q = (struct prio_sched_data *)sch->data;
 	int prio;
+	unsigned int len;
 	struct Qdisc *qdisc;
 
 	for (prio = q->bands-1; prio >= 0; prio--) {
 		qdisc = q->queues[prio];
-		if (qdisc->ops->drop(qdisc)) {
+		if ((len = qdisc->ops->drop(qdisc)) != 0) {
 			sch->q.qlen--;
-			return 1;
+			return len;
 		}
 	}
 	return 0;
diff -urN linux-2.4.21-bk5/net/sched/sch_red.c linux-2.4.21-bk6/net/sched/sch_red.c
--- linux-2.4.21-bk5/net/sched/sch_red.c	2001-12-21 09:42:06.000000000 -0800
+++ linux-2.4.21-bk6/net/sched/sch_red.c	2003-07-10 02:55:00.000000000 -0700
@@ -342,19 +342,19 @@
 	return NULL;
 }
 
-static int
-red_drop(struct Qdisc* sch)
+static unsigned int red_drop(struct Qdisc* sch)
 {
 	struct sk_buff *skb;
 	struct red_sched_data *q = (struct red_sched_data *)sch->data;
 
 	skb = __skb_dequeue_tail(&sch->q);
 	if (skb) {
-		sch->stats.backlog -= skb->len;
+		unsigned int len = skb->len;
+		sch->stats.backlog -= len;
 		sch->stats.drops++;
 		q->st.other++;
 		kfree_skb(skb);
-		return 1;
+		return len;
 	}
 	PSCHED_GET_TIME(q->qidlestart);
 	return 0;
diff -urN linux-2.4.21-bk5/net/sched/sch_sfq.c linux-2.4.21-bk6/net/sched/sch_sfq.c
--- linux-2.4.21-bk5/net/sched/sch_sfq.c	2003-06-13 07:51:39.000000000 -0700
+++ linux-2.4.21-bk6/net/sched/sch_sfq.c	2003-07-10 02:55:00.000000000 -0700
@@ -209,11 +209,12 @@
 	sfq_link(q, x);
 }
 
-static int sfq_drop(struct Qdisc *sch)
+static unsigned int sfq_drop(struct Qdisc *sch)
 {
 	struct sfq_sched_data *q = (struct sfq_sched_data *)sch->data;
 	sfq_index d = q->max_depth;
 	struct sk_buff *skb;
+	unsigned int len;
 
 	/* Queue is full! Find the longest slot and
 	   drop a packet from it */
@@ -221,12 +222,13 @@
 	if (d > 1) {
 		sfq_index x = q->dep[d+SFQ_DEPTH].next;
 		skb = q->qs[x].prev;
+		len = skb->len;
 		__skb_unlink(skb, &q->qs[x]);
 		kfree_skb(skb);
 		sfq_dec(q, x);
 		sch->q.qlen--;
 		sch->stats.drops++;
-		return 1;
+		return len;
 	}
 
 	if (d == 1) {
@@ -235,13 +237,14 @@
 		q->next[q->tail] = q->next[d];
 		q->allot[q->next[d]] += q->quantum;
 		skb = q->qs[d].prev;
+		len = skb->len;
 		__skb_unlink(skb, &q->qs[d]);
 		kfree_skb(skb);
 		sfq_dec(q, d);
 		sch->q.qlen--;
 		q->ht[q->hash[d]] = SFQ_DEPTH;
 		sch->stats.drops++;
-		return 1;
+		return len;
 	}
 
 	return 0;
diff -urN linux-2.4.21-bk5/net/sched/sch_tbf.c linux-2.4.21-bk6/net/sched/sch_tbf.c
--- linux-2.4.21-bk5/net/sched/sch_tbf.c	2001-12-21 09:42:06.000000000 -0800
+++ linux-2.4.21-bk6/net/sched/sch_tbf.c	2003-07-10 02:55:00.000000000 -0700
@@ -7,6 +7,8 @@
  *		2 of the License, or (at your option) any later version.
  *
  * Authors:	Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
+ *		Dmitry Torokhov <dtor@mail.ru> - allow attaching inner qdiscs -
+ *						 original idea by Martin Devera
  *
  */
 
@@ -123,62 +125,63 @@
 	long	ptokens;		/* Current number of P tokens */
 	psched_time_t	t_c;		/* Time check-point */
 	struct timer_list wd_timer;	/* Watchdog timer */
+	struct Qdisc	*qdisc;		/* Inner qdisc, default - bfifo queue */
 };
 
 #define L2T(q,L)   ((q)->R_tab->data[(L)>>(q)->R_tab->rate.cell_log])
 #define L2T_P(q,L) ((q)->P_tab->data[(L)>>(q)->P_tab->rate.cell_log])
 
-static int
-tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch)
+static int tbf_enqueue(struct sk_buff *skb, struct Qdisc* sch)
 {
 	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
+	int ret;
 
-	if (skb->len > q->max_size)
-		goto drop;
-	__skb_queue_tail(&sch->q, skb);
-	if ((sch->stats.backlog += skb->len) <= q->limit) {
-		sch->stats.bytes += skb->len;
-		sch->stats.packets++;
-		return 0;
-	}
-
-	/* Drop action: undo the things that we just did,
-	 * i.e. make tail drop
-	 */
-
-	__skb_unlink(skb, &sch->q);
-	sch->stats.backlog -= skb->len;
-
-drop:
-	sch->stats.drops++;
+	if (skb->len > q->max_size || sch->stats.backlog + skb->len > q->limit) {
+		sch->stats.drops++;
 #ifdef CONFIG_NET_CLS_POLICE
-	if (sch->reshape_fail==NULL || sch->reshape_fail(skb, sch))
+		if (sch->reshape_fail == NULL || sch->reshape_fail(skb, sch))
 #endif
-		kfree_skb(skb);
-	return NET_XMIT_DROP;
-}
-
-static int
-tbf_requeue(struct sk_buff *skb, struct Qdisc* sch)
-{
-	__skb_queue_head(&sch->q, skb);
+			kfree_skb(skb);
+	
+		return NET_XMIT_DROP;
+	}
+	
+	if ((ret = q->qdisc->enqueue(skb, q->qdisc)) != 0) {
+		sch->stats.drops++;
+		return ret;
+	}	
+	
+	sch->q.qlen++;
 	sch->stats.backlog += skb->len;
+	sch->stats.bytes += skb->len;
+	sch->stats.packets++;
 	return 0;
 }
 
-static int
-tbf_drop(struct Qdisc* sch)
+static int tbf_requeue(struct sk_buff *skb, struct Qdisc* sch)
 {
-	struct sk_buff *skb;
+	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
+	int ret;
+	
+	if ((ret = q->qdisc->ops->requeue(skb, q->qdisc)) == 0) {
+		sch->q.qlen++; 
+		sch->stats.backlog += skb->len;
+	}
+	
+	return ret;
+}
 
-	skb = __skb_dequeue_tail(&sch->q);
-	if (skb) {
-		sch->stats.backlog -= skb->len;
+static unsigned int tbf_drop(struct Qdisc* sch)
+{
+	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
+	unsigned int len;
+	
+	if ((len = q->qdisc->ops->drop(q->qdisc)) != 0) {
+		sch->q.qlen--;
+		sch->stats.backlog -= len;
 		sch->stats.drops++;
-		kfree_skb(skb);
-		return 1;
 	}
-	return 0;
+	return len;
 }
 
 static void tbf_watchdog(unsigned long arg)
@@ -189,19 +192,19 @@
 	netif_schedule(sch->dev);
 }
 
-static struct sk_buff *
-tbf_dequeue(struct Qdisc* sch)
+static struct sk_buff *tbf_dequeue(struct Qdisc* sch)
 {
 	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
 	struct sk_buff *skb;
 	
-	skb = __skb_dequeue(&sch->q);
+	skb = q->qdisc->dequeue(q->qdisc);
 
 	if (skb) {
 		psched_time_t now;
 		long toks;
 		long ptoks = 0;
-
+		unsigned int len = skb->len;
+		
 		PSCHED_GET_TIME(now);
 
 		toks = PSCHED_TDIFF_SAFE(now, q->t_c, q->buffer, 0);
@@ -210,18 +213,19 @@
 			ptoks = toks + q->ptokens;
 			if (ptoks > (long)q->mtu)
 				ptoks = q->mtu;
-			ptoks -= L2T_P(q, skb->len);
+			ptoks -= L2T_P(q, len);
 		}
 		toks += q->tokens;
 		if (toks > (long)q->buffer)
 			toks = q->buffer;
-		toks -= L2T(q, skb->len);
+		toks -= L2T(q, len);
 
 		if ((toks|ptoks) >= 0) {
 			q->t_c = now;
 			q->tokens = toks;
 			q->ptokens = ptoks;
-			sch->stats.backlog -= skb->len;
+			sch->stats.backlog -= len;
+			sch->q.qlen--;
 			sch->flags &= ~TCQ_F_THROTTLED;
 			return skb;
 		}
@@ -245,20 +249,25 @@
 		   This is the main idea of all FQ algorithms
 		   (cf. CSZ, HPFQ, HFSC)
 		 */
-		__skb_queue_head(&sch->q, skb);
-
+		
+		if (q->qdisc->ops->requeue(skb, q->qdisc) != NET_XMIT_SUCCESS) {
+			/* When requeue fails skb is dropped */ 
+			sch->q.qlen--;
+			sch->stats.backlog -= len;
+			sch->stats.drops++;
+		}	
+		
 		sch->flags |= TCQ_F_THROTTLED;
 		sch->stats.overlimits++;
 	}
 	return NULL;
 }
 
-
-static void
-tbf_reset(struct Qdisc* sch)
+static void tbf_reset(struct Qdisc* sch)
 {
 	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
 
+	qdisc_reset(q->qdisc);
 	skb_queue_purge(&sch->q);
 	sch->stats.backlog = 0;
 	PSCHED_GET_TIME(q->t_c);
@@ -268,6 +277,31 @@
 	del_timer(&q->wd_timer);
 }
 
+static struct Qdisc *tbf_create_dflt_qdisc(struct net_device *dev, u32 limit)
+{
+	struct Qdisc *q = qdisc_create_dflt(dev, &bfifo_qdisc_ops);
+        struct rtattr *rta;
+	int ret;
+	
+	if (q) {
+		rta = kmalloc(RTA_LENGTH(sizeof(struct tc_fifo_qopt)), GFP_KERNEL);
+		if (rta) {
+			rta->rta_type = RTM_NEWQDISC;
+			rta->rta_len = RTA_LENGTH(sizeof(struct tc_fifo_qopt)); 
+			((struct tc_fifo_qopt *)RTA_DATA(rta))->limit = limit;
+			
+			ret = q->ops->change(q, rta);
+			kfree(rta);
+			
+			if (ret == 0)
+				return q;
+		}
+		qdisc_destroy(q);
+	}
+
+	return NULL;	
+}
+
 static int tbf_change(struct Qdisc* sch, struct rtattr *opt)
 {
 	int err = -EINVAL;
@@ -276,6 +310,7 @@
 	struct tc_tbf_qopt *qopt;
 	struct qdisc_rate_table *rtab = NULL;
 	struct qdisc_rate_table *ptab = NULL;
+	struct Qdisc *child = NULL;
 	int max_size,n;
 
 	if (rtattr_parse(tb, TCA_TBF_PTAB, RTA_DATA(opt), RTA_PAYLOAD(opt)) ||
@@ -308,8 +343,14 @@
 	}
 	if (max_size < 0)
 		goto done;
+	
+	if (q->qdisc == &noop_qdisc) {
+		if ((child = tbf_create_dflt_qdisc(sch->dev, qopt->limit)) == NULL)
+			goto done;
+	}
 
 	sch_tree_lock(sch);
+	if (child) q->qdisc = child;
 	q->limit = qopt->limit;
 	q->mtu = qopt->mtu;
 	q->max_size = max_size;
@@ -342,6 +383,8 @@
 	init_timer(&q->wd_timer);
 	q->wd_timer.function = tbf_watchdog;
 	q->wd_timer.data = (unsigned long)sch;
+
+	q->qdisc = &noop_qdisc;
 	
 	if ((err = tbf_change(sch, opt)) != 0) {
 		MOD_DEC_USE_COUNT;
@@ -359,6 +402,9 @@
 		qdisc_put_rtab(q->P_tab);
 	if (q->R_tab)
 		qdisc_put_rtab(q->R_tab);
+	
+	qdisc_destroy(q->qdisc);
+	q->qdisc = &noop_qdisc;
 
 	MOD_DEC_USE_COUNT;
 }
@@ -391,10 +437,93 @@
 	return -1;
 }
 
+static int tbf_dump_class(struct Qdisc *sch, unsigned long cl,
+	       		  struct sk_buff *skb, struct tcmsg *tcm)
+{
+	struct tbf_sched_data *q = (struct tbf_sched_data*)sch->data;
+
+	if (cl != 1) 	/* only one class */ 
+		return -ENOENT;
+    
+	tcm->tcm_parent = TC_H_ROOT;
+	tcm->tcm_handle = 1;
+	tcm->tcm_info = q->qdisc->handle;
+
+	return 0;
+}
+
+static int tbf_graft(struct Qdisc *sch, unsigned long arg, struct Qdisc *new,
+		     struct Qdisc **old)
+{
+	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
+
+	if (new == NULL)
+		new = &noop_qdisc;
+
+	sch_tree_lock(sch);	
+	*old = xchg(&q->qdisc, new);
+	qdisc_reset(*old);
+	sch_tree_unlock(sch);
+	
+	return 0;
+}
+
+static struct Qdisc *tbf_leaf(struct Qdisc *sch, unsigned long arg)
+{
+	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
+	return q->qdisc;
+}
+
+static unsigned long tbf_get(struct Qdisc *sch, u32 classid)
+{
+	return 1;
+}
+
+static void tbf_put(struct Qdisc *sch, unsigned long arg)
+{
+}
+
+static int tbf_change_class(struct Qdisc *sch, u32 classid, u32 parentid, 
+			struct rtattr **tca, unsigned long *arg)
+{
+	return -ENOSYS;
+}
+
+static int tbf_delete(struct Qdisc *sch, unsigned long arg)
+{
+	return -ENOSYS;
+}
+
+static void tbf_walk(struct Qdisc *sch, struct qdisc_walker *walker)
+{
+	struct tbf_sched_data *q = (struct tbf_sched_data *)sch->data;
+
+	if (!walker->stop) {
+		if (walker->count >= walker->skip) 
+			if (walker->fn(sch, (unsigned long)q, walker) < 0) { 
+				walker->stop = 1;
+				return;
+			}
+		walker->count++;
+	}
+}
+
+static struct Qdisc_class_ops tbf_class_ops =
+{
+	.graft		= 	tbf_graft,
+	.leaf		=	tbf_leaf,
+	.get		=	tbf_get,
+	.put		=	tbf_put,
+	.change		=	tbf_change_class,
+	.delete		=	tbf_delete,
+	.walk		=	tbf_walk,
+	.dump		=	tbf_dump_class,
+};
+
 struct Qdisc_ops tbf_qdisc_ops =
 {
 	NULL,
-	NULL,
+	&tbf_class_ops,
 	"tbf",
 	sizeof(struct tbf_sched_data),
 
