[iPAQ] Turning display on/off

From: Nils Faerber <nils.a.t.kernelconcepts.de>
Date: Sun Sep 10 2000 - 10:20:48 EDT

Hi!
Just a small question. Has anyone a usable idea how to trun teh display on and
off without using X?
I am currently playing around with microwindows and would like to be able to
turn off the display (incl. backlight) when I am not using it and back on when
I need it again.
Using "bl" I can switch the backlight on and off but when the screensaver is
activated this does not work anymore. The main question is maybe which
screensaver switches the display off? Is this the kernel fb driver? Running
setterm to turn this off does not work over the serial console.

so long
   nils faerber

-- 
kernel concepts
Engel & Faerber GbR      Tel: +49-271-771091-12
Dreisbachstr. 24         Fax: +49-271-771091-19
D-57250 Netphen          D1 : +49-170-2729106
--
Received: from hermes.mvista.com (gateway-490.mvista.com [63.192.220.206])
	by handhelds.org (8.9.3/8.9.3) with ESMTP id WAA17143;
	Fri, 8 Sep 2000 22:49:17 -0700
Received: from mvista.com (IDENT:mtaht@enki.mvista.com [10.0.0.117])
	by hermes.mvista.com (8.9.3/8.9.3) with ESMTP id WAA12680;
	Fri, 8 Sep 2000 22:51:07 -0700
Message-ID: <39B9CFCB.D6B08E0D@mvista.com>
Date: Fri, 08 Sep 2000 22:51:07 -0700
From: Michael Taht <mtaht@mvista.com>
Organization: Engineering Tools Group
X-Mailer: Mozilla 4.72 [en] (X11; U; Linux 2.4.0-test8 i586)
X-Accept-Language: en
MIME-Version: 1.0
CC: ipaq@handhelds.org, linux@handhelds.org, dmalek@mvista.com,
        mtaht@picketwyre.com
References: <0BAECC6787F8D211A5070008C7089B2C011AA05A@syeis01nok>
Content-Type: multipart/mixed;
 boundary="------------C47255F213D8B722A8C02DFB"
Subject: [iPAQ] Kernel hacking with 2.4.0-test8 on the ipaq
Sender: ipaq-admin@handhelds.org
Errors-To: ipaq-admin@handhelds.org
X-BeenThere: ipaq@handhelds.org
X-Mailman-Version: 2.0beta2
Precedence: bulk
List-Id: Discussions of the Compaq iPAQ H3600 family. <ipaq.handhelds.org>
This is a multi-part message in MIME format.
--------------C47255F213D8B722A8C02DFB
Content-Type: text/plain; charset=us-ascii
Content-Transfer-Encoding: 7bit
I've had v0.13 running for a while. I worked up the courage to try and
build my own kernel today.
I leapt on linus's 2.4.0-test8 source when it came out a few hours ago,
patched it to support the touchscreen, sound, and other features of the
ipaq with code from the current handhelds.org kernel (everything except
pcmcia, and I'm certain my solution to memory management was not clean
enough), turned on a bunch of things I wanted to try (irda, mtd, jffs,
etc)... built it using our cross development kit (the hard hat cdk)...
It resulted in a kernel 553052 bytes in size. I tried downloading it,
get no errors on the download itself, but at the conclusion of the
download "I get img_size is too large for region: 00080000".... So I
suppose there's a 512K limit? Or was I running into the minicom problem
(most attempts at downloading failed with the NAK errors)... but thats
all water under the dam now, I cut the kernel down below 512k, adopted
Nils Faerber's little ipaqsendfile script, and transferred a new
kernel..
and the kernel booted only to fail at:
ViFS: Mounted roo2 filesystem). # <--- aggh! I did do some very dubious
things in arm/mm
LINUX RC is
executing                                                           
LINUX RC is
executing                                                           
before
mount                                                                    
/linuxrc: 8: Syntax error: Bad substitution   # always had this error in
v0.13                                 
VFS: Cannot open root device "flash4" or
00:0f                                 
Please append a correct "root=" boot
option                                     
Kernel panic: VFS: Unable to mount root fs on 00:0f
In the spirit of "release early, release often" I'm attaching the
patches required to get this far - I'm curious as to the number of
additional patches needed to catch up to test8, and/or if the skiff
toolchain actually compiles a working kernel. Any takers out there in
handheld-land? remember that it will break and you do get to keep both
pieces.
I'll resume working on this later this weekend. 
______
mike
--------------C47255F213D8B722A8C02DFB
Content-Type: text/plain; charset=us-ascii;
 name="ipaq-2.4.0-test8.patch"
Content-Transfer-Encoding: 7bit
Content-Disposition: inline;
 filename="ipaq-2.4.0-test8.patch"
diff -urNX ignore linux/Makefile linux.new/Makefile
--- linux/Makefile	Wed Aug 23 18:36:46 2000
+++ linux.new/Makefile	Fri Sep  8 20:12:00 2000
@@ -5,7 +5,9 @@
 
 KERNELRELEASE=$(VERSION).$(PATCHLEVEL).$(SUBLEVEL)$(EXTRAVERSION)
 
-ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
+#ARCH := $(shell uname -m | sed -e s/i.86/i386/ -e s/sun4u/sparc64/ -e s/arm.*/arm/ -e s/sa110/arm/)
+
+ARCH := arm
 
 CONFIG_SHELL := $(shell if [ -x "$$BASH" ]; then echo $$BASH; \
 	  else if [ -x /bin/bash ]; then echo /bin/bash; \
@@ -14,11 +16,11 @@
 
 HPATH   	= $(TOPDIR)/include
 FINDHPATH	= $(HPATH)/asm $(HPATH)/linux $(HPATH)/scsi $(HPATH)/net
-
+:q
 HOSTCC  	= gcc
 HOSTCFLAGS	= -Wall -Wstrict-prototypes -O2 -fomit-frame-pointer
 
-CROSS_COMPILE 	=
+CROSS_COMPILE 	= sa110_le-
 
 #
 # Include the make variables (CC, etc...)
@@ -70,13 +72,15 @@
 # images.  Uncomment if you want to place them anywhere other than root.
 #
 
-#export	INSTALL_PATH=/boot
+export	INSTALL_PATH=/boot
 
 #
 # INSTALL_MOD_PATH specifies a prefix to MODLIB for module directory
 # relocations required by build roots.  This is not defined in the
 # makefile but the arguement can be passed to make if needed.
 #
+INSTALL_MOD_PATH := /mnt/new/
+export MOD_PATH
 
 MODLIB	:= $(INSTALL_MOD_PATH)/lib/modules/$(KERNELRELEASE)
 export MODLIB
diff -urNX ignore linux/arch/arm/kernel/arch.c linux.new/arch/arm/kernel/arch.c
--- linux/arch/arm/kernel/arch.c	Sun Aug 13 09:54:15 2000
+++ linux.new/arch/arm/kernel/arch.c	Fri Sep  8 18:58:10 2000
@@ -69,13 +69,16 @@
 }
 
 #ifdef CONFIG_ARCH_RPC
+extern void __init rpc_map_io(void);
+
 MACHINE_START(RISCPC, "Acorn-RiscPC")
 	MAINTAINER("Russell King")
 	BOOT_MEM(0x10000000, 0x03000000, 0xe0000000)
 	BOOT_PARAMS(0x10000100)
 	DISABLE_PARPORT(0)
 	DISABLE_PARPORT(1)
-	FIXUP(fixup_acorn)
+        FIXUP(fixup_acorn)
+	MAPIO(rpc_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_ARCH_ARC
@@ -106,12 +109,15 @@
 	ORIG_VIDEO_LINES = params->u1.s.video_num_rows;
 }
 
+extern void __init footbridge_map_io(void);
+
 MACHINE_START(EBSA285, "EBSA285")
 	MAINTAINER("Russell King")
 	BOOT_MEM(0x00000000, DC21285_ARMCSR_BASE, 0xfe000000)
 	BOOT_PARAMS(0x00000100)
 	VIDEO(0x000a0000, 0x000bffff)
 	FIXUP(fixup_ebsa285)
+	MAPIO(footbridge_map_io)
 MACHINE_END
 #endif
 
@@ -152,6 +158,8 @@
 	}
 }
 
+extern void __init footbridge_map_io(void);
+
 MACHINE_START(NETWINDER, "Rebel-NetWinder")
 	MAINTAINER("Russell King/Rebel.com")
 	BOOT_MEM(0x00000000, DC21285_ARMCSR_BASE, 0xfe000000)
@@ -160,6 +168,7 @@
 	DISABLE_PARPORT(0)
 	DISABLE_PARPORT(2)
 	FIXUP(fixup_netwinder)
+	MAPIO(footbridge_map_io)
 MACHINE_END
 #endif
 
@@ -177,11 +186,14 @@
 	ORIG_Y = 24;
 }
 
+extern void __init footbridge_map_io(void);
+
 MACHINE_START(CATS, "Chalice-CATS")
 	MAINTAINER("Philip Blundell")
 	BOOT_MEM(0x00000000, DC21285_ARMCSR_BASE, 0xfe000000)
 	SOFT_REBOOT
 	FIXUP(fixup_cats)
+	MAPIO(footbridge_map_io)
 MACHINE_END
 #endif
 
@@ -202,10 +214,13 @@
 	*cmdline = boot_command_line;
 }
 
+extern void __init footbridge_map_io(void);
+
 MACHINE_START(CO285, "co-EBSA285")
 	MAINTAINER("Mark van Doesburg")
 	BOOT_MEM(0x00000000, DC21285_ARMCSR_BASE, 0x7cf00000)
 	FIXUP(fixup_coebsa285)
+	MAPIO(footbridge_map_io)
 MACHINE_END
 #endif
 
@@ -231,6 +246,8 @@
 
 
 extern void select_sa1100_io_desc(void);
+extern void __init sa1100_map_io(void);
+
 #define SET_BANK(__nr,__start,__size) \
 	mi->bank[__nr].start = (__start), \
 	mi->bank[__nr].size = (__size), \
@@ -282,21 +299,21 @@
 		ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
 		setup_ramdisk( 1, 0, 0, 8192 );
 		setup_initrd( __phys_to_virt(0xd8000000), 3*1024*1024 );
-	}
-
-        else if (machine_is_cerf()) {
-                // 16Meg Ram.
-                SET_BANK( 0, 0xc0000000, 8*1024*1024 );
-                SET_BANK( 1, 0xc8000000, 8*1024*1024 );			// comment this out for 8MB Cerfs
-                mi->nr_banks = 2;
-
-                ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
-                setup_ramdisk(1,  0, 0, 8192);
-                // Save 2Meg for RAMDisk
-                setup_initrd(0xc0500000, 3*1024*1024);
-        }
-
-	else if (machine_is_empeg()) {
+	} else if (machine_is_bitsy()) {
+		SET_BANK( 0, 0xc0000000, 16*1024*1024 );
+		/* nothing more to do here */
+        } else if (machine_is_cerf()) {
+                 // 16Meg Ram.
+                 SET_BANK( 0, 0xc0000000, 8*1024*1024 );
+                 SET_BANK( 1, 0xc8000000, 8*1024*1024 );			// comment this out for 8MB Cerfs
+                 mi->nr_banks = 2;
+ 
+                 ROOT_DEV = MKDEV(RAMDISK_MAJOR,0);
+                 setup_ramdisk(1,  0, 0, 8192);
+                 // Save 2Meg for RAMDisk
+                 setup_initrd(0xc0500000, 3*1024*1024);
+ 
+	} else if (machine_is_empeg()) {
 		SET_BANK( 0, 0xc0000000, 4*1024*1024 );
 		SET_BANK( 1, 0xc8000000, 4*1024*1024 );
 		mi->nr_banks = 2;
@@ -382,6 +399,7 @@
 MACHINE_START(ASSABET, "Intel-Assabet")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_BITSY
@@ -389,12 +407,14 @@
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	BOOT_PARAMS(0xc0000100)
 	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_BRUTUS
 MACHINE_START(BRUTUS, "Intel Brutus (SA1100 eval board)")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_CERF
@@ -402,18 +422,21 @@
 	MAINTAINER("Pieter Truter")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_EMPEG
 MACHINE_START(EMPEG, "empeg MP3 Car Audio Player")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_GRAPHICSCLIENT
 MACHINE_START(GRAPHICSCLIENT, "ADS GraphicsClient")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_ITSY
@@ -421,48 +444,56 @@
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	BOOT_PARAMS(0xc0000100
 	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_LART
 MACHINE_START(LART, "LART")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_NANOENGINE
 MACHINE_START(NANOENGINE, "BSE nanoEngine")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_PLEB
 MACHINE_START(PLEB, "PLEB")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_THINCLIENT
 MACHINE_START(THINCLIENT, "ADS ThinClient")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_TIFON
 MACHINE_START(TIFON, "Tifon")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_VICTOR
 MACHINE_START(VICTOR, "VisuAide Victor")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_SA1100_XP860
 MACHINE_START(XP860, "XP860")
 	BOOT_MEM(0xc0000000, 0x80000000, 0xf8000000)
 	FIXUP(fixup_sa1100)
+	MAPIO(sa1100_map_io)
 MACHINE_END
 #endif
 #endif
@@ -483,14 +514,20 @@
         setup_initrd( __phys_to_virt(0xf1000000), 0x00162b0d);
 }
 
+extern void __init l7200_map_io(void);
+
 MACHINE_START(L7200, "LinkUp Systems L7200SDB")
 	MAINTAINER("Steve Hill")
 	BOOT_MEM(0xf0000000, 0x80040000, 0xd0000000)
 	FIXUP(fixup_l7200)
+	MAPIO(l7200_map_io)
 MACHINE_END
 #endif
 
 #ifdef CONFIG_ARCH_EBSA110
+
+extern void __init ebsa110_map_io(void);
+
 MACHINE_START(EBSA110, "EBSA110")
 	MAINTAINER("Russell King")
 	BOOT_MEM(0x00000000, 0xe0000000, 0xe0000000)
@@ -498,18 +535,27 @@
 	DISABLE_PARPORT(0)
 	DISABLE_PARPORT(2)
 	SOFT_REBOOT
+	MAPIO(ebsa110_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_ARCH_NEXUSPCI
+
+extern void __init nexuspci_map_io(void);
+
 MACHINE_START(NEXUSPCI, "FTV/PCI")
 	MAINTAINER("Philip Blundell")
 	BOOT_MEM(0x40000000, 0x10000000, 0xe0000000)
+	MAPIO(nexuspci_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_ARCH_TBOX
+
+extern void __init tbox_map_io(void);
+
 MACHINE_START(TBOX, "unknown-TBOX")
 	MAINTAINER("Philip Blundell")
 	BOOT_MEM(0x80000000, 0x00400000, 0xe0000000)
+	MAPIO(tbox_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_ARCH_CLPS7110
@@ -528,22 +574,34 @@
 MACHINE_END
 #endif
 #ifdef CONFIG_ARCH_CLPS7500
+
+extern void __init clps7500_map_io(void);
+
 MACHINE_START(CLPS7500, "CL-PS7500")
 	MAINTAINER("Philip Blundell")
 	BOOT_MEM(0x10000000, 0x03000000, 0xe0000000)
+	MAPIO(clps7500_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_ARCH_SHARK
+
+extern void __init shark_map_io(void);
+
 MACHINE_START(SHARK, "Shark")
 	MAINTAINER("Alexander Schulz")
 	BOOT_MEM(0x08000000, 0x40000000, 0xe0000000)
 	VIDEO(0x06000000, 0x061fffff)
+	MAPIO(shark_map_io)
 MACHINE_END
 #endif
 #ifdef CONFIG_ARCH_PERSONAL_SERVER
+
+extern void __init footbridge_map_io(void);
+
 MACHINE_START(PERSONAL_SERVER, "Compaq Personal Server")
 	MAINTAINER("Jamey Hicks / George France")
 	BOOT_MEM(0x00000000, DC21285_ARMCSR_BASE, 0xfe000000)
 	BOOT_PARAMS(0x00000100)
+	MAPIO(footbridge_map_io)
 MACHINE_END
 #endif
diff -urNX ignore linux/arch/arm/kernel/arch.h linux.new/arch/arm/kernel/arch.h
--- linux/arch/arm/kernel/arch.h	Fri May 12 11:21:20 2000
+++ linux.new/arch/arm/kernel/arch.h	Fri Sep  8 18:55:49 2000
@@ -30,6 +30,7 @@
 	void		(*fixup)(struct machine_desc *,
 				 struct param_struct *, char **,
 				 struct meminfo *);
+        void (*map_io)(void);/* IO mapping function */
 };
 
 /*
@@ -67,6 +68,10 @@
 
 #define FIXUP(_func)				\
 	fixup:		_func,
+
+#define MAPIO(_func)				\
+	map_io:		_func,
+
 
 #define MACHINE_END				\
 };
Binary files linux/arch/arm/kernel/arch.o and linux.new/arch/arm/kernel/arch.o differ
Binary files linux/arch/arm/kernel/armksyms.o and linux.new/arch/arm/kernel/armksyms.o differ
Binary files linux/arch/arm/kernel/dma.o and linux.new/arch/arm/kernel/dma.o differ
Binary files linux/arch/arm/kernel/entry-armv.o and linux.new/arch/arm/kernel/entry-armv.o differ
Binary files linux/arch/arm/kernel/head-armv.o and linux.new/arch/arm/kernel/head-armv.o differ
Binary files linux/arch/arm/kernel/hw-sa1100.o and linux.new/arch/arm/kernel/hw-sa1100.o differ
Binary files linux/arch/arm/kernel/init_task.o and linux.new/arch/arm/kernel/init_task.o differ
Binary files linux/arch/arm/kernel/irq.o and linux.new/arch/arm/kernel/irq.o differ
Binary files linux/arch/arm/kernel/kernel.o and linux.new/arch/arm/kernel/kernel.o differ
Binary files linux/arch/arm/kernel/leds-sa1100.o and linux.new/arch/arm/kernel/leds-sa1100.o differ
Binary files linux/arch/arm/kernel/process.o and linux.new/arch/arm/kernel/process.o differ
Binary files linux/arch/arm/kernel/ptrace.o and linux.new/arch/arm/kernel/ptrace.o differ
Binary files linux/arch/arm/kernel/semaphore.o and linux.new/arch/arm/kernel/semaphore.o differ
Binary files linux/arch/arm/kernel/setup.o and linux.new/arch/arm/kernel/setup.o differ
Binary files linux/arch/arm/kernel/signal.o and linux.new/arch/arm/kernel/signal.o differ
Binary files linux/arch/arm/kernel/sys_arm.o and linux.new/arch/arm/kernel/sys_arm.o differ
Binary files linux/arch/arm/kernel/time.o and linux.new/arch/arm/kernel/time.o differ
Binary files linux/arch/arm/kernel/traps.o and linux.new/arch/arm/kernel/traps.o differ
Binary files linux/arch/arm/lib/backtrace.o and linux.new/arch/arm/lib/backtrace.o differ
Binary files linux/arch/arm/lib/changebit.o and linux.new/arch/arm/lib/changebit.o differ
Binary files linux/arch/arm/lib/clearbit.o and linux.new/arch/arm/lib/clearbit.o differ
diff -urNX ignore linux/arch/arm/lib/constants.h linux.new/arch/arm/lib/constants.h
--- linux/arch/arm/lib/constants.h	Wed Dec 31 16:00:00 1969
+++ linux.new/arch/arm/lib/constants.h	Fri Sep  8 21:42:27 2000
@@ -0,0 +1,28 @@
+/*
+ * *** This file is automatically generated from getconsdata.c.  Do not edit! ***
+ */
+#define TSK_SIGPENDING 8
+#define TSK_ADDR_LIMIT 12
+#define TSK_NEED_RESCHED 20
+#define TSK_PTRACE 24
+#define TSK_USED_MATH 548
+#define TSS_SAVE 760
+#define TSS_FPESAVE 600
+#define TSS_DOMAIN 764
+#define HPTE_TYPE_SMALL 2
+#define HPTE_AP_READ 2720
+#define HPTE_AP_WRITE 1360
+#define LPTE_PRESENT 1
+#define LPTE_YOUNG 2
+#define LPTE_BUFFERABLE 4
+#define LPTE_CACHEABLE 8
+#define LPTE_USER 16
+#define LPTE_WRITE 32
+#define LPTE_EXEC 64
+#define LPTE_DIRTY 128
+#define PAGE_SZ 4096
+#define KSWI_BASE 9437184
+#define KSWI_SYS_BASE 10420224
+#define SYS_ERROR0 10420224
+#define PGOFF_SHIFT 0
+#define PGOFF_MASK 0
Binary files linux/arch/arm/lib/copy_page.o and linux.new/arch/arm/lib/copy_page.o differ
Binary files linux/arch/arm/lib/csumipv6.o and linux.new/arch/arm/lib/csumipv6.o differ
Binary files linux/arch/arm/lib/csumpartial.o and linux.new/arch/arm/lib/csumpartial.o differ
Binary files linux/arch/arm/lib/csumpartialcopy.o and linux.new/arch/arm/lib/csumpartialcopy.o differ
Binary files linux/arch/arm/lib/csumpartialcopyuser.o and linux.new/arch/arm/lib/csumpartialcopyuser.o differ
Binary files linux/arch/arm/lib/delay.o and linux.new/arch/arm/lib/delay.o differ
Binary files linux/arch/arm/lib/findbit.o and linux.new/arch/arm/lib/findbit.o differ
Binary files linux/arch/arm/lib/getconsdata.o and linux.new/arch/arm/lib/getconsdata.o differ
Binary files linux/arch/arm/lib/io-footbridge.o and linux.new/arch/arm/lib/io-footbridge.o differ
Binary files linux/arch/arm/lib/io.o and linux.new/arch/arm/lib/io.o differ
Binary files linux/arch/arm/lib/lib.o and linux.new/arch/arm/lib/lib.o differ
Binary files linux/arch/arm/lib/memchr.o and linux.new/arch/arm/lib/memchr.o differ
Binary files linux/arch/arm/lib/memcpy.o and linux.new/arch/arm/lib/memcpy.o differ
Binary files linux/arch/arm/lib/memset.o and linux.new/arch/arm/lib/memset.o differ
Binary files linux/arch/arm/lib/memzero.o and linux.new/arch/arm/lib/memzero.o differ
Binary files linux/arch/arm/lib/setbit.o and linux.new/arch/arm/lib/setbit.o differ
Binary files linux/arch/arm/lib/strchr.o and linux.new/arch/arm/lib/strchr.o differ
Binary files linux/arch/arm/lib/strncpy_from_user.o and linux.new/arch/arm/lib/strncpy_from_user.o differ
Binary files linux/arch/arm/lib/strnlen_user.o and linux.new/arch/arm/lib/strnlen_user.o differ
Binary files linux/arch/arm/lib/strrchr.o and linux.new/arch/arm/lib/strrchr.o differ
Binary files linux/arch/arm/lib/testchangebit.o and linux.new/arch/arm/lib/testchangebit.o differ
Binary files linux/arch/arm/lib/testclearbit.o and linux.new/arch/arm/lib/testclearbit.o differ
Binary files linux/arch/arm/lib/testsetbit.o and linux.new/arch/arm/lib/testsetbit.o differ
Binary files linux/arch/arm/lib/uaccess.o and linux.new/arch/arm/lib/uaccess.o differ
Binary files linux/arch/arm/mm/consistent.o and linux.new/arch/arm/mm/consistent.o differ
Binary files linux/arch/arm/mm/extable.o and linux.new/arch/arm/mm/extable.o differ
Binary files linux/arch/arm/mm/fault-armv.o and linux.new/arch/arm/mm/fault-armv.o differ
Binary files linux/arch/arm/mm/init.o and linux.new/arch/arm/mm/init.o differ
Binary files linux/arch/arm/mm/ioremap.o and linux.new/arch/arm/mm/ioremap.o differ
diff -urNX ignore linux/arch/arm/mm/map.h linux.new/arch/arm/mm/map.h
--- linux/arch/arm/mm/map.h	Tue Apr 25 16:54:39 2000
+++ linux.new/arch/arm/mm/map.h	Fri Sep  8 19:41:04 2000
@@ -19,7 +19,11 @@
 extern struct map_desc	io_desc[];
 extern unsigned int	io_desc_size;
 
+#define LAST_DESC \
+  { last: 1 }
+
 struct meminfo;
 
 extern void create_memmap_holes(struct meminfo *);
 extern void pagetable_init(struct meminfo *);
+extern void iotable_init(struct map_desc *);
diff -urNX ignore linux/arch/arm/mm/mm-armo.c linux.new/arch/arm/mm/mm-armo.c
--- linux/arch/arm/mm/mm-armo.c	Tue Apr 25 16:54:39 2000
+++ linux.new/arch/arm/mm/mm-armo.c	Fri Sep  8 19:33:01 2000
@@ -162,9 +162,15 @@
 		pgd_val(swapper_pg_dir[i]) = 0;
 }
 
+void __init iotable_init(struct map_desc *io_desc)
+{
+	/* nothing to do */
+}
+
 /*
  * We never have holes in the memmap
  */
 void __init create_memmap_holes(struct meminfo *mi)
 {
 }
+
Binary files linux/arch/arm/mm/mm-armv.o and linux.new/arch/arm/mm/mm-armv.o differ
diff -urNX ignore linux/arch/arm/mm/mm-sa1100.c linux.new/arch/arm/mm/mm-sa1100.c
--- linux/arch/arm/mm/mm-sa1100.c	Sun Aug 13 09:54:15 2000
+++ linux.new/arch/arm/mm/mm-sa1100.c	Fri Sep  8 20:05:59 2000
@@ -29,6 +29,8 @@
  
 #define SIZE(x) (sizeof(x) / sizeof(x[0]))
 
+/* MTAHT - using this #define did not work for me for some reason, so I ended up hard coding this data into the 
+   BITSY config FIXME */
 
 #define SA1100_STD_IO_MAPPING \
  /* virtual     physical    length      domain     r  w  c  b */ \
@@ -41,8 +43,24 @@
   { 0xf8000000, 0x80000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCM */ \
   { 0xfa000000, 0x90000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* SCM */ \
   { 0xfc000000, 0xa0000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* MER */ \
-  { 0xfe000000, 0xb0000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }  /* LCD + DMA */
+  { 0xfe000000, 0xb0000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* LCD + DMA */ \
+  LAST_DESC, \
+};
 
+ 
+static struct map_desc standard_io_desc[] __initdata = {
+ /* virtual     physical    length      domain     r  w  c  b */ \
+  { 0xe0000000, 0x20000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA0 IO */ \
+  { 0xe4000000, 0x30000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA1 IO */ \
+  { 0xe8000000, 0x28000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA0 attr */ \
+  { 0xec000000, 0x38000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA1 attr */ \
+  { 0xf0000000, 0x2c000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA0 mem */ \
+  { 0xf4000000, 0x3c000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA1 mem */ \
+  { 0xf8000000, 0x80000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCM */ \
+  { 0xfa000000, 0x90000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* SCM */ \
+  { 0xfc000000, 0xa0000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* MER */ \
+  { 0xfe000000, 0xb0000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* LCD + DMA */
+};
 
 static struct map_desc assabet_io_desc[] __initdata = {
 #ifdef CONFIG_SA1100_ASSABET
@@ -67,7 +85,16 @@
 #ifdef CONFIG_SA1100_BITSY
   { 0xd0000000, 0x00000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* Flash bank 0 */
   { 0xdc000000, 0x49000000, 0x02000000, DOMAIN_IO, 1, 1, 0, 0 }, /* EGPIO 0 */
-  SA1100_STD_IO_MAPPING
+  { 0xe0000000, 0x20000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA0 IO */ \
+  { 0xe4000000, 0x30000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA1 IO */ \
+  { 0xe8000000, 0x28000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA0 attr */ \
+  { 0xec000000, 0x38000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA1 attr */ \
+  { 0xf0000000, 0x2c000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA0 mem */ \
+  { 0xf4000000, 0x3c000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA1 mem */ \
+  { 0xf8000000, 0x80000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCM */ \
+  { 0xfa000000, 0x90000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* SCM */ \
+  { 0xfc000000, 0xa0000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* MER */ \
+  { 0xfe000000, 0xb0000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* LCD + DMA */ 
 #endif
 };
 
@@ -140,7 +167,17 @@
 };
 
 static struct map_desc default_io_desc[] __initdata = {
-  SA1100_STD_IO_MAPPING
+ /* virtual     physical    length      domain     r  w  c  b */ \
+  { 0xe0000000, 0x20000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA0 IO */ \
+  { 0xe4000000, 0x30000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA1 IO */ \
+  { 0xe8000000, 0x28000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA0 attr */ \
+  { 0xec000000, 0x38000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA1 attr */ \
+  { 0xf0000000, 0x2c000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA0 mem */ \
+  { 0xf4000000, 0x3c000000, 0x04000000, DOMAIN_IO, 1, 1, 0, 0 }, /* PCMCIA1 mem */ \
+  { 0xf8000000, 0x80000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* PCM */ \
+  { 0xfa000000, 0x90000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* SCM */ \
+  { 0xfc000000, 0xa0000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* MER */ \
+  { 0xfe000000, 0xb0000000, 0x02000000, DOMAIN_IO, 0, 1, 0, 0 }, /* LCD + DMA */
 };
 
 
@@ -150,6 +187,42 @@
  */
 struct map_desc io_desc[20] __initdata = {};
 unsigned int io_desc_size;
+void iotable_init(struct map_desc *a) {
+}
+
+
+void __init sa1100_map_io(void)
+{
+	struct map_desc *desc = NULL;
+
+	iotable_init(standard_io_desc);
+
+	if (machine_is_assabet())
+		desc = assabet_io_desc;
+	else if (machine_is_nanoengine())
+		desc = nanoengine_io_desc;
+	else if (machine_is_bitsy())
+		desc = bitsy_io_desc;
+	else if (machine_is_cerf())
+		desc = cerf_io_desc;
+	else if (machine_is_empeg())
+		desc = empeg_io_desc;
+	else if (machine_is_graphicsclient())
+		desc = graphicsclient_io_desc;
+	else if (machine_is_lart())
+	        desc = lart_io_desc;
+	else if (machine_is_thinclient())
+		desc = thinclient_io_desc;
+	else if (machine_is_tifon())
+		desc = tifon_io_desc;
+	else if (machine_is_victor())
+		desc = victor_io_desc;
+	else if (machine_is_xp860())
+		desc = xp860_io_desc;
+
+	if (desc)
+		iotable_init(desc);
+}
 
 void __init select_sa1100_io_desc(void)
 {
Binary files linux/arch/arm/mm/mm-sa1100.o and linux.new/arch/arm/mm/mm-sa1100.o differ
Binary files linux/arch/arm/mm/mm.o and linux.new/arch/arm/mm/mm.o differ
Binary files linux/arch/arm/mm/proc-sa110.o and linux.new/arch/arm/mm/proc-sa110.o differ
Binary files linux/arch/arm/mm/small_page.o and linux.new/arch/arm/mm/small_page.o differ
Binary files linux/arch/arm/nwfpe/double_cpdo.o and linux.new/arch/arm/nwfpe/double_cpdo.o differ
Binary files linux/arch/arm/nwfpe/entry.o and linux.new/arch/arm/nwfpe/entry.o differ
Binary files linux/arch/arm/nwfpe/extended_cpdo.o and linux.new/arch/arm/nwfpe/extended_cpdo.o differ
Binary files linux/arch/arm/nwfpe/fpa11.o and linux.new/arch/arm/nwfpe/fpa11.o differ
Binary files linux/arch/arm/nwfpe/fpa11_cpdo.o and linux.new/arch/arm/nwfpe/fpa11_cpdo.o differ
Binary files linux/arch/arm/nwfpe/fpa11_cpdt.o and linux.new/arch/arm/nwfpe/fpa11_cpdt.o differ
Binary files linux/arch/arm/nwfpe/fpa11_cprt.o and linux.new/arch/arm/nwfpe/fpa11_cprt.o differ
Binary files linux/arch/arm/nwfpe/fpmodule.o and linux.new/arch/arm/nwfpe/fpmodule.o differ
Binary files linux/arch/arm/nwfpe/fpopcode.o and linux.new/arch/arm/nwfpe/fpopcode.o differ
Binary files linux/arch/arm/nwfpe/math-emu.o and linux.new/arch/arm/nwfpe/math-emu.o differ
Binary files linux/arch/arm/nwfpe/single_cpdo.o and linux.new/arch/arm/nwfpe/single_cpdo.o differ
Binary files linux/arch/arm/nwfpe/softfloat.o and linux.new/arch/arm/nwfpe/softfloat.o differ
diff -urNX ignore linux/arch/arm/vmlinux.lds linux.new/arch/arm/vmlinux.lds
--- linux/arch/arm/vmlinux.lds	Wed Dec 31 16:00:00 1969
+++ linux.new/arch/arm/vmlinux.lds	Fri Sep  8 21:48:56 2000
@@ -0,0 +1,97 @@
+/* ld script to make ARM Linux kernel
+ * taken from the i386 version by Russell King
+ * Written by Martin Mares <mj@atrey.karlin.mff.cuni.cz>
+ */
+OUTPUT_ARCH(arm)
+ENTRY(stext)
+SECTIONS
+{
+	. = 0xC0008000;
+	.init : {			/* Init code and data		*/
+		__init_begin = .;
+			*(.text.init)
+		__proc_info_begin = .;
+			*(.proc.info)
+		__proc_info_end = .;
+		__arch_info_begin = .;
+			*(.arch.info)
+		__arch_info_end = .;
+			*(.data.init)
+		. = ALIGN(16);
+		__setup_start = .;
+			*(.setup.init)
+		__setup_end = .;
+		__initcall_start = .;
+			*(.initcall.init)
+		__initcall_end = .;
+		. = ALIGN(4096);
+		__init_end = .;
+	}
+
+	/DISCARD/ : {			/* Exit code and data		*/
+		*(.text.exit)
+		*(.data.exit)
+		*(.exitcall.exit)
+	}
+
+	.text : {			/* Real text segment		*/
+		_text = .;		/* Text and read-only data	*/
+			*(.text)
+			*(.fixup)
+			*(.gnu.warning)
+			*(.text.lock)	/* out-of-line lock text */
+			*(.rodata)
+			*(.kstrtab)
+		. = ALIGN(16);
+		__start___ex_table = .;	/* Exception table		*/
+			*(__ex_table)
+		__stop___ex_table = .;
+
+		__start___ksymtab = .;	/* Kernel symbol table		*/
+			*(__ksymtab)
+		__stop___ksymtab = .;
+
+		*(.got)			/* Global offset table		*/
+
+		_etext = .;		/* End of text section		*/
+	}
+
+	. = ALIGN(8192);
+
+	.data : {
+		/*
+		 * first, the init task union, aligned
+		 * to an 8192 byte boundary.
+		 */
+		*(.init.task)
+
+		/*
+		 * then the cacheline aligned data
+		 */
+		. = ALIGN(32);
+		*(.data.cacheline_aligned)
+
+		/*
+		 * and the usual data section
+		 */
+		*(.data)
+		CONSTRUCTORS
+
+		_edata = .;
+	}
+
+	.bss : {
+		__bss_start = .;	/* BSS				*/
+		*(.bss)
+		*(COMMON)
+		_end = . ;
+	}
+					/* Stabs debugging sections.	*/
+	.stab 0 : { *(.stab) }
+	.stabstr 0 : { *(.stabstr) }
+	.stab.excl 0 : { *(.stab.excl) }
+	.stab.exclstr 0 : { *(.stab.exclstr) }
+	.stab.index 0 : { *(.stab.index) }
+	.stab.indexstr 0 : { *(.stab.indexstr) }
+	.comment 0 : { *(.comment) }
+}
diff -urNX ignore linux/drivers/block/Config.in linux.new/drivers/block/Config.in
--- linux/drivers/block/Config.in	Tue Jul 25 18:23:48 2000
+++ linux.new/drivers/block/Config.in	Fri Sep  8 20:29:48 2000
@@ -59,4 +59,6 @@
 fi
 dep_bool '  Initial RAM disk (initrd) support' CONFIG_BLK_DEV_INITRD $CONFIG_BLK_DEV_RAM
 
+tristate 'Flash Memory block device support' CONFIG_BLK_DEV_FLASH
+
 endmenu
diff -urNX ignore linux/drivers/block/Makefile linux.new/drivers/block/Makefile
--- linux/drivers/block/Makefile	Sun Aug  6 11:23:40 2000
+++ linux.new/drivers/block/Makefile	Fri Sep  8 20:30:16 2000
@@ -44,6 +44,8 @@
 
 obj-$(CONFIG_BLK_DEV_NBD)	+= nbd.o
 
+obj-$(CONFIG_BLK_DEV_FLASH)     += flash_mem.o
+
 ifeq ($(CONFIG_PARIDE),y)
 SUB_DIRS	+= paride
 MOD_IN_SUB_DIRS	+= paride
Binary files linux/drivers/block/blkpg.o and linux.new/drivers/block/blkpg.o differ
Binary files linux/drivers/block/block.o and linux.new/drivers/block/block.o differ
Binary files linux/drivers/block/elevator.o and linux.new/drivers/block/elevator.o differ
diff -urNX ignore linux/drivers/block/flash_mem.c linux.new/drivers/block/flash_mem.c
--- linux/drivers/block/flash_mem.c	Wed Dec 31 16:00:00 1969
+++ linux.new/drivers/block/flash_mem.c	Fri Sep  8 20:31:23 2000
@@ -0,0 +1,982 @@
+/*
+ * Flash block driver
+ * Copyright (C) 1999 Nicolas Pitre <nico@cam.org>
+ *
+ * Portions are Copyright (C) 2000 Lernout & Hauspie Speech Products, N.V.
+ *    released under GNU Public Licence (GPL) version 2.
+ * 
+ * Portions are (C) 2000 The Delft University of Technology
+ *    released under GNU GPL version 2 or higher.
+ *
+ * This is a block device driver for Intel Flash memory.
+ *
+ * 1999-02-21	Stephane Dalton		Added write functions
+ * 1999-11-22	Nicolas Pitre		Multiple access arrangement support
+ *					Added EBSA285-like board  support
+ * 2000-03-26	Nicolas Pitre		Write buffer support
+ *					ADS ThinClient parallel Flash support
+ * 2000-04-15	Nicolas Pitre		Fixed write corruption problem when
+ *					partitions weren't aligned on 
+ *					flash sector boundaries.
+ * 2000-05-25	Erik Bunce		Modify to use CFI commands to 
+ *					retrieve chip type and verify
+ *					flash sector size.
+ *
+ * 30-jul-2000  Erik Mouw               Added LART Flash support
+ *                                      Minor module cleanup
+ *                                      (yes, I like unambiguous date entries)
+ * 2000-08-21	Erik Bunce		Slightly more flexible flash handling
+ *
+ */
+
+
+#include <linux/config.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioctl.h>
+#include <linux/blkpg.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/uaccess.h>
+#include <asm/delay.h>
+#include <asm/arch/hardware.h>
+
+#define MAJOR_NR 60
+#define DEVICE_NAME "flash"
+#define DEVICE_REQUEST flash_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+#define DEVICE_NO_RANDOM
+#include <linux/blk.h>
+
+#include "flash_mem.h"
+
+
+/* Flash mapping start */
+#define FLASH_START		0xd0000000
+
+
+static unsigned char *flash_start[FLASH_PARTITIONS];
+static int flash_hardsec[FLASH_PARTITIONS];
+static int flash_blocksizes[FLASH_PARTITIONS];
+static int flash_sizes[FLASH_PARTITIONS];
+static int flash_sectsize = FLASH_SECTSIZE;
+
+/* features*/
+#define USE_WRITE_BUFFER
+#define WRITE_VERIFY
+#if 1
+#define FLASH_DEBUG_CFI
+#if 0
+#define FLASH_VERBOSE
+#define FLASH_DEBUG
+#endif
+#endif
+
+/* cache structure */
+static struct flash_sect_cache_struct {
+	enum { UNUSED, CLEAN, DIRTY, BAD } state;
+	char *buf;
+	char *src;
+} flash_cache;
+
+
+/* Flash commands.. */
+#define FLASH_CMD_READ		_CMD( 0x00FF )
+#define FLASH_CMD_READ_ID_CODES _CMD( 0x0090 )
+#define FLASH_CMD_READ_QUERY	_CMD( 0x0098 )
+#define FLASH_CMD_ERASE		_CMD( 0x0020 )
+#define FLASH_CMD_CONFIRM	_CMD( 0x00D0 )
+#define FLASH_CMD_CLEAR		_CMD( 0x0050 )
+#define FLASH_CMD_WRITE		_CMD( 0x0040 )
+#define FLASH_CMD_WR_BUFF	_CMD( 0x00e8 )
+#define FLASH_CMD_STATUS	_CMD( 0x0070 )
+
+#define FLASH_STATUS_READY	_CMD( 0x0080 )
+
+#define FLASH_WR_BUFF_SIZE	16
+
+#define ERROR_VOLTAGE_RANGE	_CMD( 0x0008 )
+#define ERROR_DEVICE_PROTECT	_CMD( 0x0002 )
+#define ERROR_PROGRAMMING	_CMD( 0x0010 )
+
+#define USEC		1000000		/* usec per sec */
+#define POLL_TIME	10		/* usec */
+#define POLL_LOOP_LIMIT	3*USEC/POLL_TIME  /* 3 sec */
+
+
+#define SECTOR(d)	((d) & ~(flash_sectsize-1))
+#define OFFSET(d)	((d) & (flash_sectsize-1))
+
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+
+
+/*
+ *  Function:   full_status_check
+ *  Author:     Stephane Dalton
+ *  Parameters: in->    flash status value
+ *              out->   TRUE: status ok, FALSE: problem
+ */
+static inline int full_status_check( unsigned long status_reg )
+{
+   if( status_reg & ERROR_VOLTAGE_RANGE ){
+      printk("Flash driver: programming voltage error!\n");
+      return FALSE;
+   }
+   if( status_reg & ERROR_DEVICE_PROTECT ){
+      printk("Flash driver: device is write protected!\n");
+      return FALSE;
+   }
+   if( status_reg & ERROR_PROGRAMMING ){
+      printk("Flash driver: programming error!\n");
+      return FALSE;
+   }
+   return TRUE;
+}                     
+
+
+/* 
+ * Function: flash_status_check
+ * 	Poll for status until it's ready.  Return FALSE if some error 
+ * 	detected, TRUE otherwise.
+ * Note: This really really has to be redesigned to avoid busy waits.
+ */
+
+static int flash_status_check( volatile FLASH_t *flash_ptr )
+{
+	FLASH_t status;
+	int timeout = POLL_LOOP_LIMIT;
+
+	/* just to be sure status is actually updated */
+	{
+		FLASH_t x;
+		x = *flash_ptr;
+		x = *flash_ptr;
+		x = *flash_ptr;
+	}
+
+	for( status = *flash_ptr;
+	     (status & FLASH_STATUS_READY) != FLASH_STATUS_READY;
+	     status = *flash_ptr ) 
+	{
+		if( timeout-- < 0 ){
+			printk( "flash_mem: flash seems dead!\n" );
+			goto error;
+		}
+		udelay(POLL_TIME);
+	} 
+	if( !full_status_check(status) ) {
+		error:
+		*flash_ptr = FLASH_CMD_CLEAR;
+		*flash_ptr = FLASH_CMD_READ;
+		return FALSE;
+	}
+	return TRUE;
+}
+
+
+/*
+ * Function: verify_data
+ * 	Verify flash data correctness, displaying bad data if there is some.
+ */
+
+static inline int verify_data(	char *flash_addr, 
+				char *data_addr, 
+				unsigned long data_len )
+{
+	int i;
+	volatile unsigned long *l_flash_ptr = (unsigned long *)flash_addr;
+	unsigned long *l_data_ptr  = (unsigned long *)data_addr;
+
+	for( i = 0; i < data_len; i += sizeof(long) ) {
+		if( *l_flash_ptr++ != *l_data_ptr++ ) {
+			l_flash_ptr--; l_data_ptr--;
+			printk( "flash_mem: data error @ %p: data is 0x%08lX, "
+				"should be 0x%08lX from %p\n",
+				l_flash_ptr, *l_flash_ptr, 
+				*l_data_ptr, l_data_ptr );
+			return FALSE;
+		}
+	}
+	return TRUE;
+}
+
+
+/*
+ *  Function:   erase_sector
+ *  Author:     Stephane Dalton
+ *  Parameters: in->    address within the flash sector to erase
+ *              out->   TRUE: sector erased, FALSE otherwise
+ */
+static int erase_sector( char *flash_addr )
+{
+	volatile FLASH_t *flash_ptr;
+	int res;
+
+#ifdef FLASH_VERBOSE
+	printk("Erasing sector %p\n", flash_addr);
+#endif
+
+	WP_VPP_ON();
+	flash_ptr = FLASH_PTR(flash_addr, 0);
+	*flash_ptr = FLASH_CMD_CLEAR;
+	*flash_ptr = FLASH_CMD_ERASE;
+	*flash_ptr = FLASH_CMD_CONFIRM;
+	res = flash_status_check( flash_ptr );
+	*flash_ptr = FLASH_CMD_READ;
+	WP_VPP_OFF();
+	return res;
+}
+
+
+/*
+ *  Function:   write_sector
+ *  Author:     Stephane Dalton
+ *  Parameters: in->    flash addr to write to
+ *			data addr to read from
+ *              out->   TRUE: sector written, FALSE otherwise
+ */
+static int write_sector( char *flash_addr, char *data_addr )
+{
+	volatile FLASH_t *flash_ptr;
+	FLASH_t *data_ptr;
+	unsigned long i;
+	int error = FALSE;
+
+#ifdef FLASH_VERBOSE
+	printk( "Writing sector %p with data at %p\n", 
+		flash_addr, data_addr);
+#endif
+
+	WP_VPP_ON();
+	data_ptr = (FLASH_t *)data_addr;
+
+	flash_ptr = FLASH_PTR(flash_addr, 0);
+	*flash_ptr = FLASH_CMD_CLEAR;
+
+	i = 0;
+	while( i < flash_sectsize ) {
+#ifdef USE_WRITE_BUFFER
+		int pollmax = 1<<20;
+		int cnt = FLASH_WR_BUFF_SIZE;
+
+		flash_ptr = FLASH_PTR(flash_addr, i);
+		do {
+			*flash_ptr = FLASH_CMD_WR_BUFF;
+			pollmax--;
+		} while( pollmax && (*flash_ptr & FLASH_STATUS_READY) != FLASH_STATUS_READY );
+		error = !flash_status_check( flash_ptr );
+		if( error )  break;
+
+		*flash_ptr = _CMD(cnt-1);
+		while( cnt-- ) {
+			flash_ptr = FLASH_PTR(flash_addr, i);
+			i += sizeof(FLASH_t);
+			*flash_ptr = *data_ptr++;
+		}
+		*flash_ptr = FLASH_CMD_CONFIRM;
+		error = !flash_status_check( flash_ptr );
+		if( error )  break;
+#else
+		flash_ptr = FLASH_PTR(flash_addr, i);
+		i += sizeof(FLASH_t);
+		*flash_ptr = FLASH_CMD_WRITE;
+		*flash_ptr = *data_ptr++;
+		error = !flash_status_check( flash_ptr );
+		if( error )  break;
+#endif
+	}
+
+	flash_ptr = FLASH_PTR(flash_addr, 0);
+	*flash_ptr = FLASH_CMD_READ;
+	WP_VPP_OFF();
+
+	if( error ) return FALSE;
+
+#ifdef WRITE_VERIFY
+	return verify_data(flash_addr, data_addr, flash_sectsize);
+#else
+	return TRUE;
+#endif
+}
+
+
+/*
+ *  Function:   write_cached_data
+ *  Author:     Stephane Dalton
+ *  Parameters: 
+ *		out ->	status code
+ *  Abstract:   Write back the data cached by the driver to flash
+ */
+static int write_cached_data(void)
+{
+	if(flash_cache.state == DIRTY){
+		if( !erase_sector(flash_cache.src) ||
+		    !write_sector(flash_cache.src, flash_cache.buf) )
+		{
+		    flash_cache.state = BAD;
+		    return -EIO;
+		}
+
+		flash_cache.state = CLEAN;
+	}
+	return 0;
+}
+
+
+/*
+ *  Function:  	flash_cached_read 
+ *  Author:     Stephane Dalton
+ *  Parameters: 
+ *		in ->	buf: 	where to put read data;
+ *			minor: 	minor number aka partition we have to read from;
+ *			offset:	data offset in this partition;
+ *			len:	the size of the read;
+ *  Abstract:   If the requested data is already in the cache,
+ *		the driver read from there instead of the flash itself
+ */
+static int flash_cached_read(	char *buf, 
+				int minor,
+				int offset,
+				int len )
+{
+    while( len > 0 ){
+	char *flash_abspos = flash_start[minor] + offset;
+	char *flash_sect = (char*)((long)flash_abspos & ~(flash_sectsize-1));
+	int sect_offset = flash_abspos - flash_sect;
+	int size = flash_sectsize - sect_offset;
+	if( size > len ) size = len;
+
+	/*
+	 * Check if the requested data is already cached
+	 * Read the requested amount of data from our internal cache if it
+	 * contains what we want, otherwise we read the data directly 
+	 * from flash.
+	 */
+	if( flash_cache.src != flash_sect) {
+		copy_from_flash:
+		memcpy( buf, flash_abspos, size );
+	}
+	/* From here, the cache should contain our data */
+	else if( flash_cache.state == DIRTY || flash_cache.state == CLEAN ) {
+		memcpy(buf, flash_cache.buf + sect_offset, size);
+	}else if( flash_cache.state == BAD ) {
+		return -EIO;
+	}else{
+		goto copy_from_flash;
+	}
+
+	len -= size;
+	buf += size;
+	offset += size;
+    }
+    return 0;
+}
+
+
+/*
+ *  Function:   flash_cached_write
+ *  Author:     Stephane Dalton
+ *  Parameters: 
+ *		in ->	buf: 	where to get the data to be written;
+ *			minor: 	minor number aka partition to write to;
+ *			offset: where within this partition start the writing;
+ *			len:	the size of the write;
+ *  Abstract:   We have to cache written date to prevent overerasing 
+ *			the flash.  The typical exemple is when using a 
+ *			blocksize of 4k and consecutively writing a 64k block 
+ *			which would generate 16 erase/write cycles for a same 
+ *			flash sector.
+ *			A better solution is to cache the flash sector 
+ *			currently being written.  To do so, if the sector 
+ *			requested is different from the previous one, 
+ *			write the cached sector and read the requested one.
+ *			Then the block to write is copied in the cache buffer.
+ */
+static int flash_cached_write(	const char *buf,
+				int minor,
+				int offset,
+				int len )
+{
+    while( len > 0 ) {
+	int err;
+	char *flash_abspos = flash_start[minor] + offset;
+	char *flash_sect = (char*)((long)flash_abspos & ~(flash_sectsize-1));
+	int sect_offset = flash_abspos - flash_sect;
+	int size = flash_sectsize - sect_offset;
+	if( size > len ) size = len;
+
+	if( flash_cache.src != flash_sect) {
+		/*
+		 * We have to write previously cached data to the flash 
+		 * and read the requested sector in the cache.
+		 */	
+		if( flash_cache.state == DIRTY ) {
+			err = write_cached_data();
+			if( err ) return err;
+		}
+
+		/* 
+		 * Get the correct flash sector corresponding to the 
+		 * requested offset 
+		 */
+		memcpy(	flash_cache.buf, flash_sect, flash_sectsize );
+		flash_cache.src = flash_sect;
+		flash_cache.state = CLEAN;
+	}
+
+	/* Write the requested amount of data to our internal cache */	
+	memcpy( flash_cache.buf + sect_offset, buf, size );
+	flash_cache.state = DIRTY;
+
+	len -= size;
+	buf += size;
+	offset += size;
+    }
+    return 0;
+}
+
+
+/*
+ *  Function:  	flash_request
+ *  Author:     Nicola Pitre
+ *  Abstract:	Flash block request routine
+ */
+static void flash_request(request_queue_t * q)
+{
+    unsigned int minor;
+    int offset, len;
+
+    for(;;) {
+	INIT_REQUEST;
+
+	minor = MINOR(CURRENT->rq_dev);
+	if (minor >= FLASH_PARTITIONS) {
+	    printk( "flash: out of partition range (minor = %d)\n", minor );
+	    end_request(0);
+	    continue;
+	}
+
+	offset = CURRENT->sector << 9;
+	len = CURRENT->current_nr_sectors << 9;
+	if ((offset + len) > flash_length[minor]) {
+	    printk( "flash_request: access beyond end of partition\n" );
+	    end_request(0);
+	    continue;
+	}
+
+#ifdef FLASH_DEBUG
+	printk( "flash_request: %s for part %d at %lX, size %ld from 0x%08X\n", 
+		CURRENT->cmd == READ ? "read" : 
+		CURRENT->cmd == WRITE ? "write" : "unknown", 
+		minor,
+		flash_start[minor] + offset, len,
+		CURRENT->buffer);
+#endif
+
+	switch( CURRENT->cmd ) {
+	    case READ:
+		if( flash_cached_read(	CURRENT->buffer,
+					minor, 
+					offset, 
+					len) != 0) 
+		{
+		    end_request(0);
+		}else{
+		    end_request(1);
+		}
+		break;
+
+	    case WRITE:
+		if( flash_cached_write(	CURRENT->buffer, 
+					minor,
+					offset, 
+					len) != 0) 
+		{
+		    end_request(0);
+		}else{
+		    end_request(1);	
+		}
+		break;
+
+	    default:
+		end_request(0);
+		break;
+	}
+    }
+} 
+
+
+/*
+ *  Function:  	flash_ioctl	
+ *  Author:     Nicola Pitre
+ */
+static int flash_ioctl(	struct inode *inode, 
+			struct file *file, 
+			unsigned int cmd, 
+			unsigned long arg)
+{
+    switch (cmd) {
+      case BLKFLSBUF:
+	if (!capable(CAP_SYS_ADMIN)) return -EACCES;
+	invalidate_buffers(inode->i_rdev);
+	break;
+
+      case BLKGETSIZE:
+	/* Return device size */
+	return put_user( flash_length[MINOR(inode->i_rdev)] / 512, 
+			 (long *) arg );
+
+      case BLKROSET:
+      case BLKROGET:
+      case BLKSSZGET:
+	return blk_ioctl(inode->i_rdev, cmd, arg);
+
+      default:
+	printk( "flash: unimplemented ioctl(0x%08X)\n", cmd );
+	return -EINVAL;
+    }
+
+    return 0;
+}
+
+
+/*
+ *  Function:	flash_open	
+ *  Author:     Nicola Pitre
+ */
+static int flash_open(struct inode * inode, struct file * filp)
+{
+    if (DEVICE_NR(inode->i_rdev) >= FLASH_PARTITIONS) return -ENXIO;
+    MOD_INC_USE_COUNT;
+    return 0;
+}
+
+
+/*
+ *  Function:	flash_release
+ *  Author:     Nicola Pitre
+ *  Note:	We sync kernel buffers as well as our local cache too.
+ */
+static int flash_release(struct inode * inode, struct file * filp)
+{
+    int ret;
+
+    sync_dev(inode->i_rdev);
+    ret = write_cached_data();
+#ifdef FLASH_DEBUG
+    printk("flash_release() called\n");
+#endif
+    MOD_DEC_USE_COUNT;
+    return ret;
+}
+
+
+/*
+ *  Function:   flash_mem_init
+ *  Author:     Nicola Pitre
+ *  Abstract:   This is the registration and initialization 
+ *		function for the flash driver
+ */
+
+static struct block_device_operations flash_fops = {
+	ioctl:	flash_ioctl,
+	open:	flash_open,
+	release:	flash_release,
+};
+
+#ifdef FLASH_HAS_CFI
+static struct flash_cfi_info flash_info;
+
+/*
+ *  Function:  	flash_retrieve_cfi_info
+ *  Author:     Erik Bunce (ebunce@lhsl.com)
+ *  Abstract:   Attempt to retrieve information about the current 
+ *		flash devices using the Common Flash Interface (CFI).
+ *  Notes: This has currently only been tested with Intel parts...
+ */
+static void flash_retrieve_cfi_info(void)
+{
+	FLASH_t* flash_cmd_ptr = FLASH_PTR((char*)FLASH_START, 0);
+	FLASH_t* flash_ptr = FLASH_PTR((char*)FLASH_START, 0 * sizeof(FLASH_t));
+#define FLASH_LOBYTE(x) (flash_ptr[(x)] & 0x000000FFL)
+
+#ifdef FLASH_DEBUG_CFI
+	u8 testmanid;
+	u8 testdevid;
+#endif
+	
+	/* Clear the information structure */
+	memset((void*)&flash_info, 0, sizeof(flash_info));
+	
+	/* Clear any status register info */
+	*flash_cmd_ptr = FLASH_CMD_CLEAR;
+
+	/* Put flash into READ ID code mode */
+	*flash_cmd_ptr = FLASH_CMD_READ_ID_CODES;
+	udelay(POLL_TIME);
+
+	/* Get the manufacturer code */
+	flash_info.manufacturer_code = FLASH_LOBYTE(0x00);
+
+	flash_ptr = FLASH_PTR((char*)FLASH_START, 1 * sizeof(FLASH_t));
+	/* Get the device code */
+	flash_info.device_code = FLASH_LOBYTE(0x00);
+
+	/* Put flash into READ QUERY mode */
+	*flash_cmd_ptr = FLASH_CMD_READ_QUERY;
+	udelay(POLL_TIME);
+
+	flash_ptr = FLASH_PTR((char*)FLASH_START, 0 * sizeof(FLASH_t));
+
+#ifdef FLASH_DEBUG_CFI
+	testmanid = FLASH_LOBYTE(0x00);
+	testdevid = FLASH_LOBYTE(0x01);
+#endif
+	
+	/* Retrieve CFI Ident string */
+	flash_info.cfi_ident[0] = (char)FLASH_LOBYTE(0x10);
+	flash_info.cfi_ident[1] = (char)FLASH_LOBYTE(0x11);
+	flash_info.cfi_ident[2] = (char)FLASH_LOBYTE(0x12);
+	flash_info.cfi_ident[3] = (char)0;
+
+	flash_info.is_cfi_supported = ((flash_info.cfi_ident[0] == 'Q') &&
+				       (flash_info.cfi_ident[1] == 'R') &&
+				       (flash_info.cfi_ident[2] == 'Y'));
+		
+	if (flash_info.is_cfi_supported != 0) {
+		/* Retrieve CFI Ident extended command set info */
+		flash_info.primary_vendor_command_code = FLASH_LOBYTE(0x13);
+		flash_info.primary_vendor_command_code |=
+			(FLASH_LOBYTE(0x14) << 8);
+
+		flash_info.primary_extended_query_table = 
+			FLASH_LOBYTE(0x15);
+		flash_info.primary_extended_query_table |=
+			(FLASH_LOBYTE(0x16) << 8);
+		
+		flash_info.alternate_vendor_command_code = 
+			FLASH_LOBYTE(0x17);
+		flash_info.alternate_vendor_command_code |=
+			(FLASH_LOBYTE(0x18) << 8);
+		
+		flash_info.alternate_extended_query_table = 
+			FLASH_LOBYTE(0x19);
+		flash_info.alternate_extended_query_table |=
+			(FLASH_LOBYTE(0x1A) << 8);
+		
+		/* Retrieve System Interface Information */
+		flash_info.vcc_min_program_erase = FLASH_LOBYTE(0x1B);
+		flash_info.vcc_max_program_erase = FLASH_LOBYTE(0x1C);
+		flash_info.vpp_min_program_erase = FLASH_LOBYTE(0x1D);
+		flash_info.vpp_max_program_erase = FLASH_LOBYTE(0x1E);
+		flash_info.typ_single_word_timeout = FLASH_LOBYTE(0x1F);
+		flash_info.typ_max_buffer_write_timeout =
+			FLASH_LOBYTE(0x20);
+		flash_info.typ_block_erase_timeout = FLASH_LOBYTE(0x21);
+		flash_info.typ_full_chip_erase_timeout = FLASH_LOBYTE(0x22);
+		flash_info.max_word_program_timeout = FLASH_LOBYTE(0x23);
+		flash_info.max_buffer_write_timeout = FLASH_LOBYTE(0x24);
+		flash_info.max_block_erase_timeout = FLASH_LOBYTE(0x25);
+		flash_info.max_chip_erase_timeout = FLASH_LOBYTE(0x26);
+		
+		/* Retrieve Device Geometry Info */
+		flash_info.device_size = FLASH_LOBYTE(0x27);
+		flash_info.x8_async_ifc = FLASH_LOBYTE(0x28);
+		flash_info.x16_async_ifc = FLASH_LOBYTE(0x29);
+		flash_info.max_write_buffer_size = FLASH_LOBYTE(0x2A);
+		flash_info.max_write_buffer_size |= 
+			(FLASH_LOBYTE(0x2B) << 8);
+		flash_info.num_erase_block_regions = FLASH_LOBYTE(0x2C);
+		flash_info.num_erase_blocks_r1 = FLASH_LOBYTE(0x2D);
+		flash_info.num_erase_blocks_r1 |= 
+			(FLASH_LOBYTE(0x2E) << 8);
+		flash_info.size_erase_blocks_r1 = FLASH_LOBYTE(0x2F);
+		flash_info.size_erase_blocks_r1 |= 
+			(FLASH_LOBYTE(0x30) << 8);
+	}
+		
+#ifdef FLASH_DEBUG_CFI
+	printk("Flash CFI Info: ****************************\n");
+	printk("\tmanufacturer_code = %#04x (%#04x)\n", 
+	       flash_info.manufacturer_code, testmanid);
+	printk("\tdevice_code = %#04x (%#04x)\n", 
+	       flash_info.device_code, testdevid);
+	printk("\tcfi_ident = %s\n", flash_info.cfi_ident);
+	printk("\tis_cfi_supported = %d\n", flash_info.is_cfi_supported);
+	if (flash_info.is_cfi_supported != 0) {
+		int tmpval1, tmpval2;
+
+#ifdef FLASH_DEBUG
+		/* Print rest of CFI Ident */
+		printk("\tprimary_vendor_command_code = %#04x\n", 
+		       flash_info.primary_vendor_command_code);
+		printk("\tprimary_extended_query_table = %#04x\n",
+		       flash_info.primary_extended_query_table);
+		printk("\talternate_vendor_command_code = %#04x\n",
+		       flash_info.alternate_vendor_command_code);
+		printk("\talternate_extended_query_table = %#04x\n",
+		       flash_info.alternate_extended_query_table);
+#endif
+		
+		/* Print CFI Geometry Info */
+		tmpval1 = 1 << flash_info.device_size;
+		tmpval2 = tmpval1 / 1024 / 1024;
+		printk("\tdevice_size = %#02x (%d bytes, %d MB, %d Mb)\n",
+		       flash_info.device_size,
+		       tmpval1, tmpval2, tmpval2 * 8);
+#ifdef FLASH_VERBOSE
+		printk("\tx8_async_ifc = %#02x\n", 
+		       flash_info.x8_async_ifc);
+		printk("\tx16_async_ifc = %#02x\n", 
+		       flash_info.x16_async_ifc);
+#endif
+
+		printk("\tmax_write_buffer_size = %d (%d bytes)\n", 
+		       flash_info.max_write_buffer_size,
+		       1 << flash_info.max_write_buffer_size);
+		printk("\tnum_erase_block_regions = %d\n",
+		       flash_info.num_erase_block_regions);
+		printk("\tnum_erase_blocks_r1 = %#04x (%d blocks)\n",
+		       flash_info.num_erase_blocks_r1,
+		       flash_info.num_erase_blocks_r1);
+		printk("\tsize_erase_blocks_r1 = %#04x (%d bytes)\n",
+		       flash_info.size_erase_blocks_r1,
+		       flash_info.size_erase_blocks_r1 * 256);
+
+#ifdef FLASH_VERBOSE
+		/* System Interface Information */
+		printk("\tvcc_min_program_erase = %d.%d V\n",
+		       (flash_info.vcc_min_program_erase & 0xF0) >> 4,
+		       flash_info.vcc_min_program_erase & 0x0F);
+		printk("\tvcc_max_program_erase = %d.%d V\n",
+		       (flash_info.vcc_max_program_erase & 0xF0) >> 4,
+		       flash_info.vcc_max_program_erase & 0x0F);
+		printk("\tvpp_min_program_erase = %d.%d V\n",
+		       (flash_info.vpp_min_program_erase & 0xF0) >> 4,
+		       flash_info.vpp_min_program_erase & 0x0F);
+		printk("\tvpp_max_program_erase = %d.%d V\n",
+		       (flash_info.vpp_max_program_erase & 0xF0) >> 4,
+		       flash_info.vpp_max_program_erase & 0x0F);
+
+		printk("\ttyp_single_word_timeout = %d (%d usec)\n",
+		       flash_info.typ_single_word_timeout,
+		       1 << flash_info.typ_single_word_timeout);
+		printk("\ttyp_max_buffer_write_timeout = %d (%d usec)\n",
+		       flash_info.typ_max_buffer_write_timeout,
+		       1 << flash_info.typ_max_buffer_write_timeout);
+		printk("\ttyp_block_erase_timeout = %d (%d msec)\n",
+		       flash_info.typ_block_erase_timeout,
+		       1 << flash_info.typ_block_erase_timeout);
+		printk("\ttyp_full_chip_erase_timeout = %d (%d msec)\n",
+		       flash_info.typ_full_chip_erase_timeout,
+		       1 << flash_info.typ_full_chip_erase_timeout);
+
+		printk("\tmax_word_program_timeout = %d (%d usec)\n",
+		       flash_info.max_word_program_timeout,
+		       ((1 << flash_info.max_word_program_timeout) *
+			(1 << flash_info.typ_single_word_timeout)));
+		printk("\tmax_buffer_write_timeout = %d (%d usec)\n",
+		       flash_info.max_buffer_write_timeout,
+		       ((1 << flash_info.max_buffer_write_timeout) *
+			(1 << flash_info.typ_max_buffer_write_timeout)));
+		printk("\tmax_block_erase_timeout = %d (%d msec)\n",
+		       flash_info.max_block_erase_timeout,
+		       ((1 << flash_info.max_block_erase_timeout) * 
+			(1 << flash_info.typ_block_erase_timeout)));
+		printk("\tmax_chip_erase_timeout = %d (%d msec)\n",
+		       flash_info.max_chip_erase_timeout,
+		       ((1 << flash_info.max_chip_erase_timeout) *
+			(1 << flash_info.typ_full_chip_erase_timeout)));
+#endif
+	}
+	
+#endif /* FLASH_DEBUG */
+	
+	/* Clear any status register info */
+	*flash_cmd_ptr = FLASH_CMD_CLEAR;
+
+	/* Return flash to read mode */
+	*flash_cmd_ptr = FLASH_CMD_READ;
+}
+#endif /* FLASH_HAS_CFI */
+
+int __init flash_mem_init(void)
+{
+	int		i;
+
+#ifdef FLASH_HAS_CFI
+	flash_retrieve_cfi_info();
+	
+	if (flash_info.is_cfi_supported != 0) {
+		/* Set true flash sector size */
+		flash_sectsize = SECTSIZE_FROM_CFI( FLASH_CHIPS_PER_BUS,
+						    flash_info.size_erase_blocks_r1);
+
+		/* find appropriate flash */
+		for (i = 0; flashInfoList[i] != NULL; ++i) {
+			if ((flash_info.manufacturer_code == 
+			     flashInfoList[i]->manufacturer_code) &&
+			    (flash_info.device_code == 
+			     flashInfoList[i]->device_code)) {
+				/* Found flash */
+				printk("FLASH Driver: using %s devices\n",
+				       flashInfoList[i]->deviceName);
+				
+				memcpy(flash_length,
+				       flashInfoList[i]->flash_length,
+				       sizeof(flash_length));
+				break;
+			}
+		}
+
+		/* verify flash */
+		if (flashInfoList[i] == NULL) {
+			memcpy((void*)flash_length,
+			       (void*)flash_length_default,
+			       sizeof(flash_length_default));
+			
+			printk(KERN_WARNING
+			       "FLASH Driver: Unknown flash type (manufacturer = %#04x, device_code = %#04x!\n",
+			       flash_info.manufacturer_code,
+			       flash_info.device_code);
+		}
+			
+#ifdef FLASH_DEBUG
+		printk("FLASH Driver: flash_sectsize = %d bytes (%d KB)\n",
+		       flash_sectsize, flash_sectsize / 1024);
+#endif
+	}
+#else
+	/* use default flash partitioning */
+	memcpy((void*)flash_length,
+	       (void*)flash_length_default,
+	       sizeof(flash_length_default));
+#endif /* FLASH_HAS_CFI */
+
+	if (register_blkdev(MAJOR_NR, DEVICE_NAME, &flash_fops)) {
+		printk("FLASHDISK: Could not get major %d", MAJOR_NR);
+		return -EIO;
+	}
+
+	/*
+	 * We allocate the cache here, even if it might never get used, 
+	 * because memory fragmentation could make the allocation fail 
+	 * at a later time.
+	 */
+	flash_cache.buf = (char *)__get_free_pages( GFP_KERNEL,
+						    get_order(flash_sectsize));
+	if( flash_cache.buf == 0 ) {
+		printk( "Flash driver: mem allocation error\n" );
+		unregister_blkdev(MAJOR_NR, DEVICE_NAME);
+		return -ENOMEM;
+	}
+
+	flash_cache.state = UNUSED;
+	flash_cache.src = 0;
+
+	blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &flash_request);
+
+	for (i = 0; i < FLASH_PARTITIONS; i++) {
+		flash_start[i] = i ? (flash_start[i-1] + flash_length[i-1]) 
+				 : (unsigned char *)FLASH_START;
+		flash_hardsec[i] = BLOCK_SIZE;
+		flash_blocksizes[i] = BLOCK_SIZE;
+		flash_sizes[i] = flash_length[i] / 1024;
+	}
+
+	hardsect_size[MAJOR_NR] = flash_hardsec;
+	blksize_size[MAJOR_NR] = flash_blocksizes;
+	blk_size[MAJOR_NR] = flash_sizes;
+
+	for (i = 0; i < FLASH_PARTITIONS; i++)
+		register_disk(  NULL, MKDEV(MAJOR_NR,i), 1, 
+				&flash_fops, flash_length[i]>>9 );
+
+#ifdef FLASH_DEBUG
+	printk("Flash Driver Partition Table\n");
+	for (i = 0; i < FLASH_PARTITIONS; i++)
+		printk("\t%d\tstart = %#08x\tlength = %#08x\n",
+		       i, flash_start[i], flash_length[i]);
+#endif
+
+#ifdef CONFIG_SA1100_VICTOR
+/*
+ *	Initialize GPIO to communicate with the flash WPP
+ */
+	GPDR |= GPIO_FLASH_WP;
+	GPCR = GPIO_FLASH_WP;
+	MSC0 =  0x5389538c;			/* clock speed */
+#endif
+
+	printk("FLASH driver initialized\n" );
+
+	return(0);
+}
+
+
+
+/*
+ *  Function:   init_module
+ *  Author:     Stephane Dalton
+ *  Abstract:   Flash module initialisation function
+ */
+static int __init flash_init_module(void)
+{
+    return( flash_mem_init() );
+}
+
+
+/*
+ *  Function:  	cleanup_module 
+ *  Author:     Stephane Dalton
+ *  Abstract:   Clean the structure used by the initialisation 
+ *		function and frees the memory associated with the 
+ *		caching system
+ */
+static void __exit flash_cleanup_module(void)
+{
+    int i;
+ 
+    for (i = 0 ; i < FLASH_PARTITIONS; i++)
+      destroy_buffers(MKDEV(MAJOR_NR, i));
+    unregister_blkdev(MAJOR_NR, DEVICE_NAME);
+    blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR));
+    flash_hardsec[i] = (int)NULL;
+    blk_size[MAJOR_NR] = NULL;
+    blksize_size[MAJOR_NR] = NULL;
+
+    if( flash_cache.buf )
+	free_pages((void*)flash_cache.buf, get_order(flash_sectsize));
+    flash_cache.buf = NULL;
+}
+
+
+#ifdef MODULE
+
+/* The default module stuff */
+module_init(flash_init_module);
+module_exit(flash_cleanup_module);
+
+MODULE_AUTHOR("Nicolas Pitre");
+MODULE_DESCRIPTION("Intel Flash memory driver");
+
+EXPORT_NO_SYMBOLS;
+
+#endif
+
diff -urNX ignore linux/drivers/block/flash_mem.h linux.new/drivers/block/flash_mem.h
--- linux/drivers/block/flash_mem.h	Wed Dec 31 16:00:00 1969
+++ linux.new/drivers/block/flash_mem.h	Fri Sep  8 20:31:23 2000
@@ -0,0 +1,364 @@
+/*
+ * Flash block driver
+ * Copyright (C) 1999-2000 Nicolas Pitre <nico@cam.org>
+ *
+ * This is a block device driver for Intel Flash memory.
+ */
+
+#define MAX_FLASH_PARTITIONS 8
+
+typedef struct FlashInfo {
+	char* deviceName;
+	u8 manufacturer_code;
+	u8 device_code;
+	int flash_length[MAX_FLASH_PARTITIONS];
+} FlashInfo;
+
+static int flash_length[MAX_FLASH_PARTITIONS] = { 0, 0, 0, 0, 0,
+						  0, 0, 0 };
+
+/* 
+ * Partition definitions... 
+ */
+#if defined( CONFIG_SA1100_VICTOR )
+
+#define FLASH_PARTITIONS	4
+static int flash_length_default[FLASH_PARTITIONS] = { 0x10000, 0x40000, 0x1a0000, 0x10000 };
+
+#elif defined( CONFIG_SA1100_THINCLIENT ) || (CONFIG_SA1100_GRAPHICSCLIENT)
+
+#define FLASH_PARTITIONS	3
+// static int flash_length[FLASH_PARTITIONS] = { 0x80000, 0x780000, 0x800000 };
+static int flash_length_default[FLASH_PARTITIONS] = { 0x100000, 0x300000, 0xc00000 };
+
+
+#elif defined( CONFIG_VIPCI )
+
+#define FLASH_PARTITIONS	4
+static int flash_length_default[FLASH_PARTITIONS] = { 0x10000, 0x80000, 0x730000, 0x40000 };
+
+#elif defined( CONFIG_SA1100_ASSABET )
+
+#define FLASH_PARTITIONS	5
+/* 
+ * Memory config with 28F160S3 FastFlash (As comes on Phase 4 Assabet)
+ * (Align @ 2 x 65K = 128K boundaries)
+ * Angel 1.2 <= 128 K
+ * zImage 2.3.99-pre8 <= 768 K
+ * ramdisk_img <= 3 M
+ * smalldisk <=  128 K
+ * EMPTY = 0
+ */
+static FlashInfo flash_28F160S3 = {
+	"28F160S3",
+	0xB0, 0xD0,
+	{ 0x00020000, 0x000c0000, 0x00300000, 0x00020000, 
+	  0x0, 0x0, 0x0, 0x0 }
+};
+
+/* 
+ * Memory config with 28F128J3A StrataFlash 
+ * (Align @ 2 x 128K = 256K boundaries)
+ * Angel 1.2 <= 256 K
+ * initrd <= 256K
+ * zImage 2.4.0-test1 <= 768 K
+ * root <= 14 M
+ * bigdisk <= 16.75 M
+ */
+static FlashInfo flash_28F128J3A = {
+	"28F128J3A",
+	0x89, 0x18,
+	{ 0x00040000, 0x000c0000, 0x00040000, 0x00e00000, 0x010c0000,
+	  0x0, 0x0, 0x0 }
+};
+
+static FlashInfo* flashInfoList[] = {
+	&flash_28F160S3,
+	&flash_28F128J3A,
+	NULL
+};
+
+static int flash_length_default[FLASH_PARTITIONS] = { 0, 0, 0, 0, 0 };
+
+#define FLASH_HAS_CFI
+
+#elif defined( CONFIG_SA1100_BITSY )
+
+#define FLASH_PARTITIONS	7
+
+/* 
+ * Memory config with 28F128J3A StrataFlash 
+ * (Align @ 2 x 128K = 256K boundaries)
+ * bootldr <= 256 K
+ * kernel <= 512K
+ * params <= 256 K
+ * ramdisk <= 3 M
+ * cramfs <= 4M
+ * cramfs-usr <= 8 M
+ * other5 <= 16 M
+ */
+static FlashInfo flash_28F128J3A = {
+	"28F128J3A",
+	0x89, 0x18,
+	{ 0x00040000, 0x00080000, 0x00040000, 0x00300000, 0x00400000,
+	  0x00800000, 0x01000000, 0x0 }
+};
+
+/* 
+ * Memory config with 28F640J3A StrataFlash 
+ * (Align @ 2 x 128K = 256K boundaries)
+ * bootldr <= 256 K
+ * kernel <= 512K
+ * params <= 256 K
+ * ramdisk <= 3 M
+ * cramfs <= 4M
+ * cramfs-usr <= 8 M
+ * 
+ */
+static FlashInfo flash_28F640J3A = {
+	"28F640J3A",
+	0x89, 0x17,
+	{ 0x00040000, 0x00080000, 0x00040000, 0x00300000, 0x00400000,
+	  0x0800000, 0x0, 0x0 }
+};
+
+
+static FlashInfo* flashInfoList[] = {
+	&flash_28F128J3A,
+	&flash_28F640J3A,
+	NULL
+};
+static int flash_length_default[FLASH_PARTITIONS] = { 0, 0, 0, 0, 0, 0, 0 };
+
+#define FLASH_HAS_CFI
+
+#elif defined( CONFIG_SA1100_CERF )
+
+#define FLASH_PARTITIONS	2
+static int flash_length_default[FLASH_PARTITIONS] = { 0x00800000, 0x00800000 };
+
+#elif defined( CONFIG_ARCH_PERSONAL_SERVER )
+
+#define FLASH_PARTITIONS	4
+static int flash_length_default[FLASH_PARTITIONS] = { 0x00020000, 0x000e0000, 0x00600000, 0x00200000 };
+#elif defined( CONFIG_SA1100_LART )
+
+/*
+ * Flash config on LART:
+ *  0-128K: blob
+ *  128K-1024K: linux kernel
+ *  1024K-4096K: ramdisk
+ */
+
+#define FLASH_PARTITIONS	3
+
+static int flash_length_default[FLASH_PARTITIONS] = { 0x020000, 0x0e0000, 0x300000 };
+#else
+#error Missing partition definition
+#endif
+
+
+
+
+/*
+ *  Macros to toggle WP and VPP pins for programming flash
+ */
+#ifdef CONFIG_SA1100_VICTOR
+#define GPIO_FLASH_WP	(GPIO_GPIO4|GPIO_GPIO5)
+#define WP_VPP_ON()	GPSR = GPIO_FLASH_WP
+#define WP_VPP_OFF()	GPCR = GPIO_FLASH_WP
+#elif defined(CONFIG_SA1100_BITSY)
+#define WP_VPP_ON()	set_bitsy_egpio(EGPIO_BITSY_VPP_ON)
+#define WP_VPP_OFF()	clr_bitsy_egpio(EGPIO_BITSY_VPP_ON)
+#else
+#define WP_VPP_ON()
+#define WP_VPP_OFF()
+#endif
+
+
+/* 
+ * SECTSIZE_FROM_K() - is used to calculate the sector size by the number
+ * of chips that compose the bus width, and the size in K of an erase
+ * block on each chip.
+ */
+#define SECTSIZE_FROM_K(no_chips, no_k) ((no_chips) * (no_k) * 1024)
+
+/* SECTSIZE_FROM_CFI() - is used to calculate the sector size based on
+ * the number of chips that compose the bus width and the information
+ * retrieved about the chips using the CFI commands
+ */
+#define SECTSIZE_FROM_CFI(no_chips, size_erase_blocks) \
+	((no_chips) * (size_erase_blocks) * 256)
+
+/*
+ * Flash access methods
+ * (FLASH_SECTSIZE benifits from autodetection and validation where CFI is 
+ *  available)
+ */
+#if defined( CONFIG_SA1100_VICTOR )
+
+#define	FLASH_CHIPS_PER_BUS	1
+#define FLASH_SECTSIZE		SECTSIZE_FROM_K( FLASH_CHIPS_PER_BUS, 64 )
+#define _CMD( x )  (x)
+
+typedef unsigned short FLASH_t;
+
+static inline FLASH_t *FLASH_PTR( char *base, unsigned long i)
+{
+	return (FLASH_t *)(base + i);
+}
+
+#elif defined( CONFIG_SA1100_CERF )
+
+#define FLASH_SECTSIZE		(1*128*1024)
+#define _CMD( x )  (x)
+
+typedef unsigned short FLASH_t;
+
+static inline FLASH_t *FLASH_PTR( char *base, unsigned long i)
+{
+	return (FLASH_t *)(base + i);
+}
+
+#elif defined( CONFIG_SA1100_ASSABET )
+
+/* two paralel 16-bit access */
+#define	FLASH_CHIPS_PER_BUS	2
+#if 1
+/* 28F160S3 FastFlash has 64 K blocks */
+#define FLASH_SECTSIZE		SECTSIZE_FROM_K( FLASH_CHIPS_PER_BUS, 64 )
+#else
+/* 28F128J3A StrataFlash has 128 K blocks */
+#define FLASH_SECTSIZE		SECTSIZE_FROM_K( FLASH_CHIPS_PER_BUS, 128 )
+#endif
+#define _CMD( x )  ((x)|((x)<<16))
+
+typedef unsigned int FLASH_t;
+
+static inline FLASH_t *FLASH_PTR( char *base, unsigned long i)
+{
+	return (FLASH_t *)(base + i);
+}
+
+#elif defined( CONFIG_SA1100_THINCLIENT ) || defined(CONFIG_SA1100_GRAPHICSCLIENT) || defined(CONFIG_SA1100_BITSY) || defined(CONFIG_ARCH_PERSONAL_SERVER)
+
+/* two paralel 16-bit access */
+#define	FLASH_CHIPS_PER_BUS	2
+#define FLASH_SECTSIZE		SECTSIZE_FROM_K( FLASH_CHIPS_PER_BUS, 128 )
+#define _CMD( x )  ((x)|((x)<<16))
+
+typedef unsigned int FLASH_t;
+
+static inline FLASH_t *FLASH_PTR( char *base, unsigned long i)
+{
+	return (FLASH_t *)(base + i);
+}
+
+#elif defined( CONFIG_VIPCI1 )
+
+#define	FLASH_CHIPS_PER_BUS	1
+#define FLASH_SECTSIZE		SECTSIZE_FROM_K( FLASH_CHIPS_PER_BUS, 64 )
+#define _CMD( x )  (x)
+
+typedef unsigned char FLASH_t;
+
+#include <asm/dec21285.h>
+
+static inline FLASH_t *FLASH_PTR( char *base, unsigned long i)
+{
+	*CSR_ROMWRITEREG = i;
+	i &= ~(4-sizeof(FLASH_t));
+	return (FLASH_t *)(base + i);
+}
+
+#elif defined( CONFIG_VIPCI2 )
+
+/* two paralel 16-bit access */
+#define	FLASH_CHIPS_PER_BUS	2
+#define FLASH_SECTSIZE		SECTSIZE_FROM_K( FLASH_CHIPS_PER_BUS, 64 )
+#define _CMD( x )  ((x)|((x)<<16))
+
+typedef unsigned int FLASH_t;
+
+static inline FLASH_t *FLASH_PTR( char *base, unsigned long i)
+{
+	return (FLASH_t *)(base + i);
+}
+
+#elif defined( CONFIG_SA1100_LART )
+
+/*
+ * LART has two Intel 28F160F3 device in parallel with some very weird
+ * address and data line connections (because of PCB routings)
+ */
+
+#warning "FIXME: check correct flash devices for LART"
+#define	FLASH_CHIPS_PER_BUS	2
+#define FLASH_SECTSIZE		SECTSIZE_FROM_K( FLASH_CHIPS_PER_BUS, 64 )
+
+/* What's this supposed to do? -- Erik */
+#define _CMD( x )  (x)
+/* #define FLASH_HAS_CFI */
+
+typedef unsigned int FLASH_t;
+
+static inline FLASH_t *FLASH_PTR( char *base, unsigned long i)
+{
+	return (FLASH_t *)(base + i);
+}
+
+#else
+#error Missing pointer access definition
+#endif
+
+
+#ifdef FLASH_HAS_CFI
+struct flash_cfi_info {
+	u8 manufacturer_code; /* 89 = Intel, D0 = Intel */
+	u8 device_code;
+
+	/* CFI Identification */
+	char cfi_ident[4];
+	u8 is_cfi_supported;
+	
+	u16 primary_vendor_command_code;
+	u16 primary_extended_query_table;
+	u16 alternate_vendor_command_code;
+	u16 alternate_extended_query_table;
+	
+	/* CFI System Interface Info */
+	u8 vcc_min_program_erase;
+	u8 vcc_max_program_erase;
+	u8 vpp_min_program_erase;
+	u8 vpp_max_program_erase;
+	u8 typ_single_word_timeout;
+	u8 typ_max_buffer_write_timeout;
+	u8 typ_block_erase_timeout;
+	u8 typ_full_chip_erase_timeout;
+	u8 max_word_program_timeout;
+	u8 max_buffer_write_timeout;
+	u8 max_block_erase_timeout;
+	u8 max_chip_erase_timeout;
+
+	/* Device Gemoetry Info */
+	u8 device_size;  /* 0x27 2^n bytes */
+	u8 x8_async_ifc; /* 0x28 x8 async flash ifc */
+	u8 x16_async_ifc; /* 0x29 x16 async flash ifc */
+	u8 max_write_buffer_size; /* 0x2A-0x2B 2^n max write buffer bytes */
+	u8 num_erase_block_regions; /* 0x2C Erase block regions */
+	u16 num_erase_blocks_r1; /* 0x2D-0x2E Number of Erase blocks */
+	u16 size_erase_blocks_r1; /* 0x2F-0x30 Size of Erase blocks */
+
+	char pri_ident[4];
+	
+	/* Intel-Specific Extended Query */
+	u8 primary_extended_major_vers;
+	u8 primary_extended_minor_vers;
+	u8 optional_feature_and_command_support;
+	u8 vcc_highest_perform_program_erase; /* P+0x0C */
+	u8 vpp_highest_perform_program_erase; /* P+0x0D */
+
+	/* Intel-Specific Extension - Burst Read Info */
+	u8 page_mode_read; /* P+0x13 size of page mode reads in 2^n bytes */
+};
+#endif
Binary files linux/drivers/block/flash_mem.o and linux.new/drivers/block/flash_mem.o differ
Binary files linux/drivers/block/genhd.o and linux.new/drivers/block/genhd.o differ
Binary files linux/drivers/block/ll_rw_blk.o and linux.new/drivers/block/ll_rw_blk.o differ
Binary files linux/drivers/block/loop.o and linux.new/drivers/block/loop.o differ
Binary files linux/drivers/block/nbd.o and linux.new/drivers/block/nbd.o differ
Binary files linux/drivers/block/rd.o and linux.new/drivers/block/rd.o differ
Binary files linux/drivers/cdrom/cdrom.o and linux.new/drivers/cdrom/cdrom.o differ
diff -urNX ignore linux/drivers/char/Config.in linux.new/drivers/char/Config.in
--- linux/drivers/char/Config.in	Tue Aug 22 11:29:02 2000
+++ linux.new/drivers/char/Config.in	Fri Sep  8 18:07:29 2000
@@ -8,6 +8,14 @@
 if [ "$CONFIG_VT" = "y" ]; then
    bool '  Support for console on virtual terminal' CONFIG_VT_CONSOLE
 fi
+if [ "$CONFIG_ARCH_SA1100" = "y" ]; then
+  tristate 'SA1100 serial port support' CONFIG_SERIAL_SA1100
+  if [ "$CONFIG_SERIAL_SA1100" = "y" ]; then
+    bool '  Console on SA1100 serial port' CONFIG_SERIAL_SA1100_CONSOLE
+  fi
+  tristate 'UCB1200 touchscreen support' CONFIG_TOUCHSCREEN_UCB1200
+  tristate 'Compaq iPAQ H3600 (Bitsy) touchscreen support' CONFIG_TOUCHSCREEN_BITSY
+fi
 tristate 'Standard/generic (8250/16550 and compatible UARTs) serial support' CONFIG_SERIAL
 if [ "$CONFIG_SERIAL" = "y" ]; then
    bool '  Support for console on serial port' CONFIG_SERIAL_CONSOLE
diff -urNX ignore linux/drivers/char/Makefile linux.new/drivers/char/Makefile
--- linux/drivers/char/Makefile	Tue Aug 22 11:41:14 2000
+++ linux.new/drivers/char/Makefile	Fri Sep  8 19:07:33 2000
@@ -65,7 +65,6 @@
 
 ifeq ($(ARCH),arm)
   ifneq ($(CONFIG_PC_KEYMAP),y)
-    KEYMAP   =
   endif
   ifneq ($(CONFIG_PC_KEYB),y)
     KEYBD    =
@@ -101,7 +100,9 @@
 obj-$(CONFIG_VT) += vt.o vc_screen.o consolemap.o consolemap_deftbl.o $(CONSOLE) selection.o
 obj-$(CONFIG_SERIAL) += $(SERIAL)
 obj-$(CONFIG_SERIAL_21285) += serial_21285.o
-
+obj-$(CONFIG_SERIAL_SA1100) += serial_sa1100.o
+obj-$(CONFIG_TOUCHSCREEN_UCB1200) += ucb1200_ts.o
+obj-$(CONFIG_TOUCHSCREEN_BITSY) += h3650_ts.o
 ifndef CONFIG_SUN_KEYBOARD
   obj-$(CONFIG_VT) += keyboard.o $(KEYMAP) $(KEYBD)
 else
Binary files linux/drivers/char/char.o and linux.new/drivers/char/char.o differ
Binary files linux/drivers/char/console.o and linux.new/drivers/char/console.o differ
Binary files linux/drivers/char/consolemap.o and linux.new/drivers/char/consolemap.o differ
Binary files linux/drivers/char/consolemap_deftbl.o and linux.new/drivers/char/consolemap_deftbl.o differ
Binary files linux/drivers/char/defkeymap.o and linux.new/drivers/char/defkeymap.o differ
diff -urNX ignore linux/drivers/char/h3650_ts.c linux.new/drivers/char/h3650_ts.c
--- linux/drivers/char/h3650_ts.c	Wed Dec 31 16:00:00 1969
+++ linux.new/drivers/char/h3650_ts.c	Fri Sep  8 18:10:13 2000
@@ -0,0 +1,1158 @@
+/*
+*
+* Driver for the h3600 Touch Screen and other Atmel controlled devices.
+*
+* Copyright 2000 Compaq Computer Corporation.
+*
+* Use consistent with the GNU GPL is permitted,
+* provided that this copyright notice is
+* preserved in its entirety in all copies and derived works.
+*
+* COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED,
+* AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS
+* FITNESS FOR ANY PARTICULAR PURPOSE.
+*
+* Author: Charles Flynn.
+*
+*/
+
+#ifndef __KERNEL__
+#  define __KERNEL__
+#endif
+#ifndef MODULE
+#  define MODULE
+#endif
+
+#include <linux/module.h>
+#include <linux/version.h>
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/delay.h>
+#include <linux/poll.h>
+#include <asm/uaccess.h>        /* get_user,copy_to_user */
+#include <linux/string.h>
+
+/* SA1100 serial defines */
+#include <asm/arch/hardware.h>
+#include <asm/arch/serial_reg.h>
+#include <asm/arch/irqs.h>
+
+#include <linux/h3650_ts.h>
+
+#define _INLINE_ inline
+/* Note this define only works if TXBUF_MAX is a power of 2 */
+#define INCBUF(x,mod) ( (++x) & ( mod - 1) )
+
+/* ++++++++++++local function defines ++++++++++++*/
+static int checkDevice(struct inode *pInode);
+
+static int initSerial( void );
+
+/* These are the touch screen functions */
+static int tsEvent(ID *);	/* event handler */
+static int tsRead(ID *);	/* file operations _read() */
+static int tsReadRaw(ID *);	/* file operations _read() */
+static int tsIoctl(void * , unsigned int , unsigned long );
+static int tsInit(ID *);
+
+static int keyEvent(ID *);	/* buttons event handler */
+static int keyRead(ID *);	/* file operations _read() */
+
+static int ackRes(ID *);	/* generic cmd Acknowledgement */
+static int verEvent(ID * );	/* Version response handler */
+static int epromRdEvent(ID * );/* read eeprom */
+
+/* ++++++++++++local function defines ++++++++++++*/
+
+
+/* +++++++++++++globals ++++++++++++++++*/
+int gMajor = 0;			/* TODO dynamic major for now */
+/* This is the pointer to the UART receiving the ATMEL serial data */
+unsigned long   port = (unsigned long)&Ser1UTCR0;
+
+
+static VER_RET verRet;		/* The structure returned to the client */
+
+static KEY_DEV keyDev;		/*  bit7=1=on bit7=0=off bits0:3=key num */
+unsigned char keyRet;
+
+static TS_DEV tsDev;
+static TS_RET tsRet = {0,0,0,0};
+
+static EPROM_READ epromRdRet;
+
+#if 1	/* Not implemented yet */
+static EPROM_WRITE epromWrRet;
+
+static BAT_DEV battRet;
+
+static IICRD_DEV iicRdRet;
+
+static IICWR_DEV iicWrRet;
+#endif
+
+
+/*
+	++++++++++++++ start of driver structures ++++++++++++++
+	Each Atmel microcontroller device is represented in this array.
+*/
+/* TODO TODO put g_Events into file private data */
+EVENT g_Events[MAX_MINOR] = {
+{ tsRead,tsIoctl,TOUCHS_ID,0,0},	/* TS minor = 0 */
+{ tsReadRaw,tsIoctl,TOUCHS_ID,0,0},	/* TSRAW minor = 1 */
+{ keyRead,0,KEYBD_ID,0,0}  		/* KEY minor = 2 */
+};
+
+ID g_Ids[MAX_ID] = {
+
+{
+0, (void *)&verRet, verEvent,0,0,sizeof(VER_RET),0
+}, /*ID=0 VERSION*/
+
+{ 0,0,0,0,0,0,0 },	/* ID=1 N/A */
+
+{	/* ID=2 keybd */
+(void *)&keyDev,(void *)&keyRet,keyEvent,0,sizeof(KEY_DEV),sizeof(keyRet),0
+},
+
+{	/* ID=3 Touch Screen */
+(void *)&tsDev,(void *)&tsRet,tsEvent,tsInit,sizeof(TS_DEV),sizeof(tsRet),0
+},
+
+{	/* ID=4 EEPROM READ */
+0, (void *)&epromRdRet,epromRdEvent,0, 0,sizeof(epromRdRet),0
+},
+
+{	/* ID=5 EEPROM WRITE */
+0, (void *)&epromWrRet,0,0, 0,sizeof(epromWrRet),0
+},
+
+{ 0,0,0,0,0,0,0 },	/* ID=6 N/A */
+{ 0,0,0,0,0,0,0 },	/* ID=7 N/A */
+
+{ 0, 0, ackRes ,0, 0,0,0 }, /* ID=8 LED */
+
+{	/* ID=9 Battery Status */
+0, (void *)&battRet,0,0, 0,sizeof(battRet),0
+},
+
+{ 0,0,0,0,0,0,0 },	/* ID=10 N/A */
+
+{	/* ID=11 IIC Read */
+0, (void *)&iicRdRet,0,0, 0,sizeof(iicRdRet),0
+},
+
+{	/* ID=12 IIC Write */
+0, (void *)&iicWrRet,0,0, 0,sizeof(iicWrRet),0
+},
+
+{ 0, 0, ackRes ,0, 0,0,0 } /* ID=13 Front Light */
+
+};
+
+/* ++++++++++++++ end driver structures ++++++++++++++*/
+
+static RXDEV rxdev;		/* receive ISR state */
+static TXDEV txdev;		/* send */
+/* +++++++++++++globals ++++++++++++++++*/
+
+
+/* +++++++++++++File operations ++++++++++++++*/
+static int h3600_fasync(int fd, struct file *filp, int mode);
+static ssize_t h3600_read(struct file * , char * , size_t , loff_t * l);
+static int h3600_open(struct inode * inode, struct file * filp);
+
+static int h3600_ioctl(struct inode * inode, struct file *filp
+				   ,unsigned int cmd , unsigned long arg);
+static unsigned int h3600_poll(struct file * filp, struct poll_table_struct * wait);
+
+static void eventIsr(int , void *, struct pt_regs *);	/* ISR :*/
+
+struct file_operations ts_fops = {
+	read:           h3600_read,
+        poll:           h3600_poll,
+	ioctl:		h3600_ioctl,
+        fasync:         h3600_fasync,
+	open:		h3600_open,
+};
+
+/*
+	sendBytes
+*/
+static _INLINE_ void sendBytes(const char * buff, size_t count,int id)
+{
+    unsigned char chksum;
+    unsigned int space,head,tail;
+
+    /* TODO TODO TODO (event << 4) compiles even though event is NOT defined */
+    chksum = ( (unsigned char)((id << 4) | count ) );
+
+    while(1)	/* this loop guarantees the payload is sent */
+    {
+	/* check if enough space - if not wait until the ISR frees some */
+	head = txdev.head;
+	tail = txdev.tail;
+	/* size is the number of bytes waiting to be transmitted */
+	space = ( head > tail ) ? head - tail : tail - head;
+	space = TXBUF_MAX - space;
+	if ( space >= ( count + FRAME_OVERHEAD ) )
+	{
+	    unsigned int cr3;
+	    unsigned cnt = count;
+	    txdev.buf[head]= (unsigned char)CHAR_SOF;
+	    head=INCBUF(head,TXBUF_MAX);
+	    txdev.buf[head]= chksum;
+	    head=INCBUF(head,TXBUF_MAX);
+	    while(cnt--)
+	    {
+		txdev.buf[head]= *buff;	
+		head=INCBUF(head,TXBUF_MAX);
+		chksum += (unsigned char)(*buff++);
+	    }
+	    txdev.buf[head]=chksum;
+	    txdev.head=INCBUF(head,TXBUF_MAX);
+#if 0
+	    {
+	    unsigned i;
+	    printk("\nsendBytes: head=%d tail=%d\n",txdev.head,txdev.tail);
+		for( i=tail ; i != txdev.head ; i = INCBUF(i,TXBUF_MAX) )
+		{
+		    printk("%d:%02x\n",i,txdev.buf[i]);
+		}
+	    }
+#endif
+	    /* if not enabled then enable Tx IRQs */
+	    cr3 = ( (volatile unsigned long *)port)[UTCR3];
+	    if ( !( cr3 & UTCR3_TIE) )
+	        ( (volatile unsigned long *)port)[UTCR3] = cr3 | UTCR3_TIE;
+	    break;	/* all done */	
+	}
+    }
+}
+
+/* +++++++++++++File operations ++++++++++++++*/
+
+static int h3600_fasync(int fd, struct file *filp, int mode)
+{
+    /* TODO TODO put this data into file private data */
+    int minor = checkDevice( filp->f_dentry->d_inode);
+    if ( minor == - 1)
+	return -ENODEV;
+#if 0
+    printk("fasync:called minor=%d\n",minor);
+#endif
+
+    return( fasync_helper(fd, filp, mode, &g_Events[minor].aq) );
+}
+
+
+int h3600_ioctl(struct inode * inode, struct file *filp,
+				   unsigned int cmd , unsigned long arg)
+{
+    char buff[28];	/* TODO magic number see sizeof(EPROM_READ/WRITE) */
+    int minor;
+    unsigned rxucnt,txcnt=0,sn,id;
+    int status = 0;	/* TODO success */
+    ID * pid;
+
+
+    minor = checkDevice( inode );
+    if ( minor == - 1)
+	return -ENODEV;
+
+#if 0
+    printk("h3600_ioctl:minor=%08x cmd=%d\n",minor,cmd);
+#endif
+
+
+    /*
+	We transmit the 1st 'txcnt' bytes from this buffer to the Atmel as data
+	if pid->lenRet == 0 then copy 'txcnt' bytes from user.
+	We return the Atmel response in 'pid->pReturn'
+
+	This means the 1st txcnt bytes of the copied buffer MUST
+		be the ATMEl cmd sequence.
+    */
+    switch (cmd)
+    {
+	case LED_ON:
+	    txcnt=sizeof(LED_IN);
+#if 1
+		id=NLED_ID;
+#else
+		id=7;	/* test -guarantees no response and hence timeout */
+#endif
+	    break;
+	case GET_VERSION:
+	    id=VERSION_ID;
+	    break;
+	case READ_EPROM:
+	    txcnt=2;  /* TODO 2=magic */
+	    id=EEPROM_READ_ID;
+	    break;
+	case BLITE_ON:
+	    txcnt=sizeof(BLITE_IN);
+	    id=13;
+	    break;
+	default:
+	{
+	    EVENT * pev = &g_Events[minor];
+    	    ID * pid = &g_Ids[pev->id];
+	    if( pev->processIoctl )
+		return ( (*pev->processIoctl)(pid->pdev,cmd,arg) );
+	    return 0;
+	}
+    }
+
+    pid = &g_Ids[id];
+    rxucnt = pid->lenRet;
+    if( txcnt > rxucnt )
+	rxucnt = txcnt;
+    if( rxucnt )
+	__copy_from_user(buff,(char *)arg,rxucnt );
+    txcnt &= (0x0f);
+#if 0
+{
+    unsigned i;
+    printk("copy_from_user: rxcnt=%d\n",rxucnt);
+    for(i=0 ; i < rxucnt ;i++)
+	printk("%02x",buff[i]);
+}
+#endif
+    
+    sendBytes( buff, txcnt, id );
+    /* now wait on response */
+    sn = pid->sn;
+    interruptible_sleep_on_timeout(&pid->wq, (2 * HZ) ); /*wait 2 secs*/
+#if 0
+    printk("TxSN=%d ESN=%d lenRet=%d\n",sn, pid->sn,pid->lenRet );
+#endif
+    if ( pid->sn == ( sn + 1 ) )
+    {
+	if( rxucnt)
+	    __copy_to_user((char *)arg,(char *)pid->pReturn,rxucnt);
+    }
+    else
+	status = -EIO;
+
+    return status;
+}
+
+/*
+	Generic poll
+*/
+unsigned int h3600_poll(struct file * filp, struct poll_table_struct * wait)
+{
+    int minor;
+    EVENT * pev;
+    ID * pid;
+
+    minor = checkDevice( filp->f_dentry->d_inode);
+    if ( minor == - 1)
+	return -ENODEV;
+
+#if 0
+    printk("h3600_poll:\n");
+#endif
+
+    pev = &g_Events[minor];
+    pid = &g_Ids[pev->id];
+    poll_wait(filp,&pid->wq,wait);
+    return ( pev->head == pev->tail) ? 0 : (POLLIN | POLLRDNORM);
+}
+
+/*
+	Generic read operation.	
+*/
+
+ssize_t h3600_read(struct file * filp, char * buf, size_t count, loff_t * l)
+{
+    int nonBlocking = filp->f_flags & O_NONBLOCK;
+    int minor;
+    EVENT * pev;
+    ID * pid;
+
+
+    minor = checkDevice( filp->f_dentry->d_inode );
+    if ( minor == - 1)
+	return -ENODEV;
+
+
+    pev = &g_Events[minor];
+    pid = &g_Ids[pev->id];
+
+#if 0
+    printk("h3600_read: minor=%d id=%d\n",minor,pev->id);
+#endif
+
+    if( nonBlocking )
+    {
+	if( ( pev->head != pev->tail ) && pev->processRead )
+	{
+	    int count;
+	    count = (*pev->processRead)(pid);
+	    /* returns length to be copied otherwise errno -Exxx */
+	    if( count > 0 )
+		/* TODO TODO use __copy or copy ? */
+    		__copy_to_user(buf,(char *)pid->pReturn,count );
+	    return count;
+	}
+	else
+	    return -EINVAL;
+
+    }
+    else
+    {
+	/* check , when woken, there is a complete event to read */
+retry:
+	if( ( pev->head != pev->tail ) && pev->processRead )
+	{
+	    int rcount;
+	    rcount = (*pev->processRead)(pid);
+	    /* returns length to be copied otherwise errno -Exxx */
+	    if( rcount > 0 )
+	    {
+    		__copy_to_user(buf,(char *)pid->pReturn,rcount );
+	    }
+	    return rcount;	/* TODO fmtLen if success -Exxx if error */
+	}
+	else
+	{
+     	    interruptible_sleep_on(&pid->wq);
+	    goto retry;
+	}
+
+	
+    }
+}
+
+int h3600_open(struct inode * inode, struct file * filp)
+{
+    int minor;
+    EVENT * pev;
+
+    minor = checkDevice( inode );
+    if ( minor == - 1)
+	return -ENODEV;
+
+#if 0
+    printk("h3600_open:minor=%d\n",minor);
+#endif
+
+    /* initialise event buffer */
+    pev = &g_Events[minor];
+    pev->tail = pev->head;
+
+    return 0;
+}
+
+/* int __init h3600_init(void) */
+int __init init_module(void)
+{
+	/* TODO TODO what happens if there is an error - see pdriver */
+	int result;
+	unsigned i;
+	ID * pid;
+	printk("init_module:\n");
+
+        /* register our character device */
+        printk("ts_dev: init_module registering char device\n");
+        result = register_chrdev(0, MOD_NAME, &ts_fops);
+        if (result < 0) {
+            printk("ts_dev:can't get major number\n");
+            return result;
+        }
+
+	if( gMajor == 0 )
+		gMajor = result;
+
+	initSerial();		/* do this first before irq management */
+	
+	/* Now register our IRQ - ID must be unique if sharing an IRQ line*/
+	/* TODO TODO IRQ not shared tale out */
+#if 0
+	free_irq(DEV_IRQ, NULL);
+	result = request_irq(DEV_IRQ, eventIsr, SA_SHIRQ | SA_INTERRUPT,
+				DEV_IRQ_NAME, DEV_IRQ_ID);
+#endif
+	result = request_irq(DEV_IRQ, eventIsr, SA_SHIRQ | SA_INTERRUPT,
+				DEV_IRQ_NAME, DEV_IRQ_ID);
+	if (!result)
+	{	
+       	    printk("init_module successful init major= %d irq=%d\n",
+		gMajor,DEV_IRQ);
+
+	    for(i=0 ; i < MAX_ID ; i++)
+	    {
+		/* Zero device specific structures */
+		pid = &g_Ids[i];
+		pid->sn=0;
+		memset(pid->pdev,0,pid->lenDev);		
+		init_waitqueue_head(&pid->wq);
+		memset(pid->pReturn,0,pid->lenRet);		
+		if( pid->initDev )
+			(*pid->initDev)( pid );
+	    }
+	}
+	else
+		printk("init_module error from irq %d\n", result);
+	return 0;
+}
+
+void cleanup_module(void)
+{
+	printk("cleanup_module:\n");
+
+	free_irq(DEV_IRQ, DEV_IRQ_ID);
+	unregister_chrdev(gMajor, MOD_NAME);
+}
+
+
+/*
+	Returns the Minor number from the inode.
+*/
+static int checkDevice(struct inode *pInode)
+{
+	int minor;
+	kdev_t dev = pInode->i_rdev;
+
+	if( MAJOR(dev) != gMajor)
+	{
+		printk("checkDevice bad major = %d\n",MAJOR(dev) );
+		return -1;
+	}
+	minor = MINOR(dev);
+
+	if ( minor < MAX_MINOR )
+		return minor;
+	else
+	{
+		printk("checkDevice bad minor = %d\n",minor );
+		return -1;
+	}
+}
+
+/* +++++++++++++++++ Start of ISR code ++++++++++++++++++++++++++ */
+
+
+/*
+	Sets up serial port 1
+	baud rate = 115k
+	8 bits no parity 1 stop bit
+*/
+
+int initSerial( )
+{
+    unsigned int status;
+
+    /* TODO registers read in 32bit chunks - need to do this for the sa1110? */
+
+    /* Clean up CR3 for now */
+    ( (volatile unsigned long *)port)[UTCR3]= 0;
+
+    /* 8 bits no parity 1 stop bit */
+    ( (volatile unsigned long *)port)[UTCR0] = 0x08;
+
+    /* Set baud rate MS nibble to zero. */
+    ( (volatile unsigned long *)port)[UTCR1] = 0;
+
+    /* Set baud rate LS nibble to 115K bits/sec */
+    ( (volatile unsigned long *)port)[UTCR2]=0x1;
+
+    /*
+	  SR0 has sticky bits, must write ones to them to clear.
+	    the only way you can clear a status reg is to write a '1' to clear.
+    */
+    ( (volatile unsigned long *)port)[UTSR0]=0xff;
+
+    /*
+	    enable rx fifo interrupt (SAM 11-116/7)
+	    If RIE=0 then sr0.RFS and sr0.RID are ignored ELSE
+	    if RIE=1 then whenever sro.RFS OR sr0.RID are high an
+		interrupt will be generated.
+	    Also enable the transmitter and receiver.
+    */
+    status = UTCR3_TXE | UTCR3_RXE | UTCR3_RIE;
+    ( (volatile unsigned long *)port)[UTCR3]=status;
+
+
+    /*
+	    initialize Serial channel protocol frame
+    */
+    rxdev.state = STATE_SOF;
+    rxdev.event = rxdev.len = 0;
+    memset(&rxdev.buf[0],0,sizeof(rxdev.buf));
+    txdev.head = txdev.tail=0;
+    return 0;	/* TODO how do we denote an error */
+
+} // End of function initSerial
+
+static  _INLINE_ void ClearInterrupt(void)
+{
+	/* SR0 has sticky bits, must write ones to them to clear */
+	( (volatile unsigned long *)port)[UTSR0]=0xff;
+}
+
+
+/*
+	This function takes a single byte and detects the frame
+*/
+static _INLINE_ void processChar(unsigned char rxchar)
+{
+    switch ( rxdev.state )
+    {
+		/*
+			We are looking for the SOF in this state.
+		*/
+		case STATE_SOF:
+			if ( rxchar == CHAR_SOF )
+			{
+				/* found it - next byte is the id and len */
+				rxdev.state=STATE_ID;
+			}
+			break;
+		/*
+			We now expect the id and len byte
+		*/
+		case STATE_ID:
+			rxdev.event = (rxchar & 0xf0) >> 4 ;
+			rxdev.len = (rxchar & 0x0f);
+			rxdev.idx=0;
+			if (rxdev.event >= MAX_ID )
+			{
+				/* increment error counter 1 */
+				printk("Error1\n");
+				++rxdev.counter[0];
+				rxdev.state = STATE_SOF;
+				break;
+			}
+			rxdev.chksum = rxchar;
+			rxdev.state=( rxdev.len > 0 ) ? STATE_DATA : STATE_EOF;
+			break;
+		/*
+			We now expect 'len' data bytes.
+		*/
+		case STATE_DATA:
+			rxdev.chksum += rxchar;
+			rxdev.buf[rxdev.idx]= rxchar;
+			if( ++rxdev.idx == rxdev.len )
+			{
+				/* look for the EOF char next */
+				rxdev.state = STATE_EOF;
+			}
+			break;
+		/*
+			We now expect to find the STATE_EOF
+			TODO TODO not sure if this is an STATE_EOF marker
+			or simply the checksum byte
+		*/
+		case STATE_EOF:
+			rxdev.state = STATE_SOF;
+			if (rxchar == CHAR_EOF || rxchar == rxdev.chksum )
+			{
+				ID * pid = &g_Ids[(unsigned)rxdev.event];
+				if( pid->processEvent )
+				{
+				  /*printk(" [%d]\n",(unsigned)rxdev.event);*/
+				  if ( (*pid->processEvent)(pid) == 0 )
+				      wake_up_interruptible(&pid->wq);
+				}
+			}
+			else
+			{
+			    ++rxdev.counter[1];
+			    printk("\nbadFrame ");
+			}
+			break;
+		default:
+			++rxdev.counter[2];
+			printk("Error3\n");
+			break;
+
+    }	/* end switch */
+
+}  /* end of in-line function */
+
+
+/*
+
+	Frame format
+  byte	  1	  2		  3 		 len + 4
+	+-------+---------------+---------------+--=------------+
+	|SOF	|id	|len	| len bytes	| Chksum	|
+	+-------+---------------+---------------+--=------------+
+  bit	0     7  8    11 12   15 16
+
+	+-------+---------------+-------+
+	|SOF	|id	|0	|Chksum	| - Note Chksum does not include SOF
+	+-------+---------------+-------+
+  bit	0     7  8    11 12   15 16
+
+*/
+
+static void eventIsr(int irq, void *dev_id, struct pt_regs *regs)
+{
+    unsigned status0;	/* UTSR0 */
+    int rxval;
+
+    /* TODO check if we need to share */
+    if ( strcmp( (char *)dev_id, DEV_IRQ_ID) )
+	goto exit;;
+
+    /* ascertain reason for interrupt and handle accordingly */
+    status0 = ( (volatile unsigned long *)port)[UTSR0];
+    if( (status0 & UTSR0_RID) || (status0 & UTSR0_RFS) )
+    {
+	/* Rx idle or RxFIFO above high water mark and needs service */
+	unsigned status1;
+#if 0
+	printk("IRQ Rx - ");
+#endif
+	do
+	{
+	    /* process all bytes in Rx FIFO */
+	    rxval = ( (volatile unsigned long *)port)[UART_RX];
+	    if (!(rxval & 0x700)) 
+	    {
+#ifdef DEBUG
+		printk("%02x",(unsigned char)rxval );
+#else
+		processChar( (unsigned char)rxval);
+#endif
+	    }
+	    else
+	    {
+		/* found parity,frame or overrun error */
+		printk("Bad Char\n");
+	    }
+
+            status1 = ( (volatile unsigned long *)port)[UTSR1];
+	
+	} while (status1 & UTSR1_RNE );
+#ifdef DEBUG
+	printk("\n");
+#endif
+
+    }
+    else if ( status0 & UTSR0_TFS )
+    {
+	unsigned int cr3;
+	unsigned int tail=txdev.tail;
+	unsigned int head=txdev.head;
+	/* Tx FIFO below low water mark and needs replenished */
+#ifdef DEBUG
+	printk("IRQ Tx - ");
+#endif
+	while( ( (volatile unsigned long *)port)[UTSR1] & UTSR1_TNF )
+	{
+	    /* load another char into the Tx FIFO */
+            ( (volatile unsigned long *)port)[UART_TX] = txdev.buf[tail];
+#ifdef DEBUG
+	    printk("%02x",txdev.buf[tail]);
+#endif
+	    if ( (tail=INCBUF(tail,TXBUF_MAX) ) == head )
+	    {
+		/* nothing more to Tx - disable interrupts */
+                cr3 = ( (volatile unsigned long *)port)[UTCR3] & ~UTCR3_TIE;
+                ( (volatile unsigned long *)port)[UTCR3] = cr3;
+		/* printk(" TxIRQ off head=%d tail=%d\n",head,tail); */
+		break;
+	    }
+	}
+	txdev.tail=tail;
+#ifdef DEBUG
+	printk("\n");
+#endif
+	
+    }
+    else if ( status0 & UTSR0_EIF )
+    {
+	/* Detected a Parity,Frame or Overrun error */
+	printk("IRQ EIF\n");
+    }
+    else
+    {
+	/* must be a Begin/End of break that caused IRQ */
+	printk("IRQ RBB | REB\n");
+    }
+
+
+exit:
+
+    ClearInterrupt();
+
+}	//end of function
+
+
+
+/* +++++++++++++++++ end of ISR code ++++++++++++++++++++++++++ */
+/****************** start of device handlers section ***********************/
+
+/*
+	Buttons - returned as a single byte
+	7 6 5 4 3 2 1 0
+	S x x x N N N N
+
+	S	switch state ( 0=pressed 1=released)
+	x	Unused.
+	NNNN	switch number 0-15
+
+*/
+static int keyEvent(ID * pDev )		/* touch screen event handler */
+{
+  KEY_DEV * pKeyDev = (KEY_DEV *)pDev->pdev;
+  EVENT * pev = &g_Events[KEY_MINOR];
+  unsigned char * pKey = &pKeyDev->buf[pev->head];
+
+  *pKey=rxdev.buf[0];	/* store current key state */
+
+#if 0
+  /* debug only */
+{
+  unsigned char * state;
+  state = ( *pKey & KEY_OFF ) ? "OFF" : "ON";
+  printk("KERNEL[%02x]KEY %d is %s\n", *pKey, *pKey & KEY_NUM, state );
+}
+#endif
+
+    pev->head=INCBUF(pev->head,MAX_KEY_EVENTS );
+    /* Send an interrupt (SIGIO) to the client's signal handler */
+    if (pev->aq)
+	kill_fasync(&pev->aq, SIGIO, POLL_IN);
+
+  return 0;
+
+}
+
+static int keyRead(ID * pDev)
+{
+    /* generic line for all readers */
+    KEY_DEV * pKeyDev = (KEY_DEV *)pDev->pdev;
+    EVENT * pev = &g_Events[KEY_MINOR];
+    unsigned char * pKey = &pKeyDev->buf[pev->tail];
+    keyRet = *pKey;
+    pev->tail=INCBUF(pev->tail,MAX_KEY_EVENTS );
+    return 1;	/* TODO make this a sizeof() */
+}
+
+
+/*	ID=3 TOUCHSCREEN EVENTS
+	TouchScreen event data is formatted as shown below:-
+
+	+-------+-------+-------+-------+
+	| Xmsb	| Xlsb	| Ymsb	| Ylsb	|
+	+-------+-------+-------+-------+
+	byte 0	  1	  2	  3
+
+*/
+
+static int tsEvent(ID * pDev )		/* touch screen event handler */
+{
+  unsigned head;
+  /* This is where we will store the event */
+  EVENT * pev = &g_Events[TS_MINOR];
+  EVENT * pevraw = &g_Events[TSRAW_MINOR];
+  TS_DEV * pTsDev = ( TS_DEV * )pDev->pdev;
+  TS_EVENT * pEvent;
+
+  head = pev->head;
+  pEvent = &pTsDev->events[head];
+
+  if( rxdev.len == TS_DATA_LEN )
+  {
+    /*
+	There are 3 sub-events within the TS_EVENT
+	PEN_UP		- user has just lifted pen off screen.
+	PEN_FLEETING	- user has just put pen DOWN for the first time.
+			- we ignore this event.
+	PEN_DOWN	- this is a true sample.
+    */
+    switch(pTsDev->penStatus)
+    {
+	case PEN_FLEETING:
+	    /* TODO how many fleeting before we pronounce a PEN_DOWN? */
+	    pTsDev->penStatus = PEN_DOWN;
+	    /* OK to use this data -  pass thru to next case*/
+	case PEN_DOWN:
+	    {
+		/* store raw x,y & pressure to ts_dev.event[head] */
+		unsigned  short x,y;
+		/* Extract RAW coords from event data  */
+		/* TODO TODO check this!! */
+		x = rxdev.buf[0]; x <<= 8; x += rxdev.buf[1];
+		y = rxdev.buf[2]; y <<= 8; y += rxdev.buf[3];
+		/* dont calibrate it until the user asks for it */
+		pEvent->x = x;
+		pEvent->y = y;
+		pEvent->pressure = 1;
+		/* increment heads */
+		pev->head = INCBUF(head,MAX_TS_EVENTS);
+		pevraw->head=pev->head;
+#if 0
+	        printk("PEN_DOWN: x=%d y=%d p=%d ++h=%d:%d\n",
+			pEvent->x,pEvent->y,
+			pEvent->pressure,pev->head,pevraw->head);
+#endif
+	    }
+	    break;
+	case PEN_UP:
+	    /* Pen was previously UP dont trust this sample */
+	    pTsDev->penStatus = PEN_FLEETING;
+#if 0
+	    printk("PEN_FLEETING\n");
+#endif
+	    return 1; 
+	default:
+	    pTsDev->penStatus = PEN_UP;
+	    printk("PEN_UNKNOWN\n");
+	    return -1;
+    }
+
+  }
+  else if (rxdev.len == 0)
+  {
+    /* PEN_UP - since we have no data in the frame */
+    pTsDev->penStatus = PEN_UP;
+    pEvent->x = pEvent->y = pEvent->pressure = 0;
+    /* increment heads */
+    pev->head = INCBUF(head,MAX_TS_EVENTS);
+    pevraw->head=pev->head;
+#if 0
+    printk("PEN_UP head=%d\n",pev->head);
+#endif
+  }
+  else
+  {
+    /* we have a length over or under run */
+    printk("LENGTH_UNKNOWN\n");
+    return -1;
+  }
+
+  /* There is an event to report - Async notification */
+  if (pev->aq)
+	kill_fasync(&pev->aq, SIGIO, POLL_IN);
+  if (pevraw->aq)
+	kill_fasync(&pevraw->aq, SIGIO, POLL_IN);
+
+  return 0;	/* event to report */
+
+} /* end of tsEvent() */
+
+
+/*
+	Read() function for the touch screen.
+	IN:  raw data
+	OUT: Calibrated data pointed to by pReturn.
+*/
+    #define X_JITTER        1
+    #define Y_JITTER        1
+static int tsRead(ID * pDev)
+{
+	/* generic line for all readers */
+    TS_DEV * pTsDev = (TS_DEV *)pDev->pdev;
+    EVENT * pev = &g_Events[TS_MINOR];
+    TS_EVENT raw = pTsDev->events[pev->tail];
+    TS_RET * fmt = (TS_RET *)pDev->pReturn;
+    static unsigned int xprev=0;
+    static unsigned int yprev=0;	
+
+    /*
+	calibrate the raw data
+    */
+    if (  raw.pressure )
+    {
+	int x,y,dx,dy;	
+	/* do this only if x,y and p are non-zero */
+        fmt->x = ( (pTsDev->cal.xscale * raw.x) >> 8 ) + pTsDev->cal.xtrans ;
+        fmt->y = ( (pTsDev->cal.yscale * raw.y) >> 8 ) + pTsDev->cal.ytrans ;
+        fmt->pressure = raw.pressure;
+#if 0
+	printk("tsRead: x=%d y=%d rawX=%d rawY=%d\n",
+		fmt->x,fmt->y,raw.x,raw.y );
+#endif
+        dx = ((x = fmt->x - xprev) < 0 ? -x : x);
+        /* is the coordinate change greater than the acceptable jitter? */
+        if( dx > X_JITTER )
+            xprev = fmt->x;
+        else
+            fmt->x = xprev;
+
+        dy = ((y = fmt->y - yprev) < 0 ? -y : y);
+        if( dy > Y_JITTER )
+            yprev = fmt->y;
+        else
+            fmt->y = yprev;	
+    }
+    else
+    {
+	fmt->x = fmt->y = fmt->pressure = 0;	/* PEN UP */
+    }
+    /* TODO there may be other stuff to copy to the return buffer */
+
+    pev->tail=INCBUF(pev->tail,MAX_TS_EVENTS);
+
+#if 0
+    printk("\ntsRead: h=%d ++t=%d x=%d y=%d p=%d\n",
+	pDev->head,pDev->tail,fmt->x,fmt->y,fmt->pressure);
+#endif
+
+    return sizeof(TS_RET);		/* success */
+}
+
+/*
+	RAW Touch screen data - used by the calibration utility.
+	Read() function for the touch screen.
+	IN:  raw data
+	OUT: Calibrated data pointed to by pReturn.
+*/
+static int tsReadRaw(ID * pDev)
+{
+	/* generic line for all readers */
+    TS_DEV * pTsDev = (TS_DEV *)pDev->pdev;
+    EVENT * pev = &g_Events[TSRAW_MINOR];
+    TS_EVENT raw = pTsDev->events[pev->tail];
+    TS_RET * ret = (TS_RET *)pDev->pReturn;
+
+#if 0
+	printk("tsReadRaw:rawX=%d rawY=%d\n", raw.x,raw.y );
+#endif
+	/* Raw data including PEN_UP */
+    ret->x = raw.x;
+    ret->y = raw.y;
+    ret->pressure = raw.pressure;
+
+    /* TODO there may be other stuff to copy to the return buffer */
+
+    pev->tail=INCBUF(pev->tail,MAX_TS_EVENTS);
+
+#if 0
+    printk("\ntsReadRaw:h=%d ++t=%d p=%d\n",pev->head,pev->tail,ret->pressure);
+#endif
+
+    return sizeof(TS_RET);		/* success */
+}
+/*
+	ts_ioctl - ioctl function for touch screen device
+*/
+static int tsIoctl( void * pDevice, unsigned int cmd, unsigned long arg)
+{
+    TS_DEV * pdev = (TS_DEV *)pDevice;
+
+    printk("tsIoctl: cmd %04x pdev=%p\n",cmd,pdev);
+
+    switch(cmd)
+    {
+	case TS_GET_RATE:	/* TODO TODO */
+	    return pdev->rate;
+	    break;
+	case TS_SET_RATE:	/* TODO TODO */
+	    pdev->rate = arg;
+	    break;
+	case TS_GET_CAL:
+	    printk("TS_GET_CAL\n");
+	    __copy_to_user((char *)arg, (char *)&pdev->cal, sizeof(TS_CAL) );
+	    break;
+	case TS_SET_CAL:
+	    printk("\n\nTS_SET_CAL: ");
+	    __copy_from_user( (char *)&pdev->cal,(char *)arg ,sizeof(TS_CAL) );
+	    
+	    printk("xscale=%d xtrans=%d yscale=%d ytrans=%d\n\n",
+		pdev->cal.xscale,pdev->cal.xtrans,
+		pdev->cal.yscale,pdev->cal.ytrans);
+	    break;
+	default:
+	    printk("IOCTL: unknowncmd %04x\n",cmd);
+	    return -EINVAL;
+    }
+
+    return 0;
+}
+
+static int tsInit(ID * pDev)
+{
+    TS_DEV * pTsDev = (TS_DEV *)pDev->pdev;
+
+    printk("tsInit: not implemented yet\n");
+
+    /* TODO this is zero'd at init_module - need to do it again? */
+
+    pTsDev->penStatus=PEN_DOWN;
+    /* default calibration */
+    pTsDev->cal.xscale = -93;
+    pTsDev->cal.xtrans = 346;
+    pTsDev->cal.yscale = -64;
+    pTsDev->cal.ytrans = 251;
+
+    return 0;
+}
+
+/* 
+	Stub functions 
+*/
+#if 0
+static int xxInit(ID * pdev )
+{
+	printk("xxInit: not implemented\n");
+	return 0;
+}
+
+#endif
+
+/*
+	generic response handler - assumes no data - simply an Ack
+	All it does is increment a MOD255 sequence number.
+*/
+static int ackRes(ID * pDev )
+{
+  ++pDev->sn;
+#if 0
+  printk("ackRes sn=%d\n",pDev->sn);
+#endif
+  return 0;
+}
+
+/*
+	Response handler for GET version number.
+	Ver Number returned as 4 bytes XX.YY
+	NOTE: Implemented only for the 4-byte version format.
+*/
+static int verEvent(ID * pDev )
+{
+  VER_RET * pVer = (VER_RET *)pDev->pReturn;
+  unsigned short x;
+  /* TODO TODO this may be reversed check!! */
+  x = rxdev.buf[0]; x <<= 8; x += rxdev.buf[1];
+  pVer->maj = x;
+  x = rxdev.buf[2]; x <<= 8; x += rxdev.buf[3];
+  pVer->min = x;
+  ++pDev->sn;
+  printk("Version %d.%d sn=%d len=%d\n",pVer->maj,pVer->min,pDev->sn,rxdev.len);
+#if 0
+  printk("rx0=%02x,rx1=%02x,rx2=%02x,rx3=%02x\n",
+  rxdev.buf[0], rxdev.buf[1],
+  rxdev.buf[2], rxdev.buf[3] );
+#endif
+ return 0;
+}
+
+static int epromRdEvent(ID * pDev)
+{
+  EPROM_READ *pER = (EPROM_READ *)pDev->pReturn;
+ {
+	unsigned i;
+	printk("epromRd: len=%d\n",rxdev.len);
+	for (i=0 ; i < rxdev.len ; i++ )
+		printk("%02x",rxdev.buf[i]);
+	
+	printk("\n\n");
+    /* TEST ONLY */
+    pER->len=2;
+    pER->addr=1;
+    pER->buff[0]=0x0304;
+    pER->buff[1]=0x0405;
+ }
+
+    ++pDev->sn;
+    return 0;
+}
Binary files linux/drivers/char/h3650_ts.o and linux.new/drivers/char/h3650_ts.o differ
Binary files linux/drivers/char/keyboard.o and linux.new/drivers/char/keyboard.o differ
Binary files linux/drivers/char/mem.o and linux.new/drivers/char/mem.o differ
Binary files linux/drivers/char/misc.o and linux.new/drivers/char/misc.o differ
Binary files linux/drivers/char/n_tty.o and linux.new/drivers/char/n_tty.o differ
Binary files linux/drivers/char/pty.o and linux.new/drivers/char/pty.o differ
Binary files linux/drivers/char/random.o and linux.new/drivers/char/random.o differ
Binary files linux/drivers/char/raw.o and linux.new/drivers/char/raw.o differ
Binary files linux/drivers/char/rtc.o and linux.new/drivers/char/rtc.o differ
diff -urNX ignore linux/drivers/char/sa1100_ts.h linux.new/drivers/char/sa1100_ts.h
--- linux/drivers/char/sa1100_ts.h	Wed Dec 31 16:00:00 1969
+++ linux.new/drivers/char/sa1100_ts.h	Fri Sep  8 19:57:51 2000
@@ -0,0 +1,111 @@
+/*
+ * Touchscreen Driver (SA1100+UCB1200) Header File
+ * Copyright (c) Compaq Computer Corporation, 1998, 1999
+ *
+ * Authors: Carl Waldspurger and Larry Brakmo.
+ *
+ * Updated to linux-2.4.0-test6-rmk2-np1 by Tak-Shing Chan
+ */
+
+#ifndef _SA1100_TS_H
+#define _SA1100_TS_H
+
+/*
+ * Codec registers
+ */
+#define CODEC_REG_IO_DATA		(0)
+#define CODEC_REG_IO_DIRECTION		(1)
+#define CODEC_REG_RISE_INT_ENABLE	(2)
+#define CODEC_REG_FALL_INT_ENABLE	(3)
+#define CODEC_REG_INT_STATUS		(4)
+#define CODEC_REG_TELECOM_CTL_A		(5)
+#define CODEC_REG_TELECOM_CTL_B		(6)
+#define CODEC_REG_AUDIO_CTL_A		(7)
+#define CODEC_REG_AUDIO_CTL_B		(8)
+#define CODEC_REG_TS_CTL		(9)
+#define CODEC_REG_ADC_CTL		(10)
+#define CODEC_REG_ADC_DATA		(11)
+#define CODEC_REG_ID			(12)
+#define CODEC_REG_MODE			(13)
+
+/*
+ * Codec registers 2, 3 and 4: interrupt related registers
+ */
+#define ADC_INT			(1 << 11)
+#define TSPX_INT		(1 << 12)
+#define TSMX_INT		(1 << 13)
+
+/*
+ * Codec register 9: Touchscreen control register
+ */
+#define TSMX_POW		(1 << 0)
+#define TSPX_POW		(1 << 1)
+#define TSMY_POW		(1 << 2)
+#define TSPY_POW		(1 << 3)
+#define TSMX_GND		(1 << 4)
+#define TSPX_GND		(1 << 5)
+#define TSMY_GND		(1 << 6)
+#define TSPY_GND		(1 << 7)
+#define TSC_MODE_MASK		(3 << 8)
+#define TSC_MODE_INT		(0 << 8)
+#define TSC_MODE_PRESSURE	(1 << 8)
+#define TSC_MODE_POSITION	(1 << 9)
+#define TSC_BIAS_ENA		(1 << 11)
+#define TSPX_LOW		(1 << 12)
+#define TSMX_LOW		(1 << 13)
+
+/*
+ * Codec register 10: ADC control register
+ */
+#define ADC_SYNC_ENA		(1 << 0)
+#define ADC_INPUT_MASK		(7 << 2)
+#define ADC_INPUT_TSPX		(0 << 2)
+#define ADC_INPUT_TSMX		(1 << 2)
+#define ADC_INPUT_TSPY		(2 << 2)
+#define ADC_INPUT_TSMY		(3 << 2)
+#define ADC_INPUT_AD0		(4 << 2)
+#define ADC_INPUT_AD1		(5 << 2)
+#define ADC_INPUT_AD2		(6 << 2)
+#define ADC_INPUT_AD3		(7 << 2)
+#define ADC_START		(1 << 7)
+#define ADC_ENA			(1 << 15)
+
+/*
+ * Codec register 11: ADC data register
+ */
+#define ADC_DATA_SHIFT_VAL	(5)
+#define GET_ADC_DATA(x)		(((x) >> ADC_DATA_SHIFT_VAL) & 0x3ff)
+#define ADC_DAT_VAL		(1 << 15)
+#define ADC_ENA_TYPE 		(ADC_SYNC_ENA | ADC_ENA)
+
+/*
+ * Useful operations
+ */
+
+static inline ushort
+codec_read(ushort addr)
+{
+    ulong	flags;
+    ushort	data;
+
+    save_flags_cli(flags);
+    while (!(Ser4MCSR & MCSR_CWC));
+    Ser4MCDR2 = ((addr & 0xf) << FShft(MCDR2_ADD)) | MCDR2_Rd;
+    while (!(Ser4MCSR & MCSR_CRC));
+    data = Ser4MCDR2 & 0xffff;
+    restore_flags(flags);
+    return data;
+}
+
+static inline void
+codec_write(ushort addr, ushort data)
+{
+    ulong	flags;
+
+    save_flags_cli(flags);
+    while (!(Ser4MCSR & MCSR_CWC));
+    Ser4MCDR2 = ((addr & 0xf) << FShft(MCDR2_ADD)) | MCDR2_Wr | (data & 0xffff);
+    restore_flags(flags);
+}
+
+#endif /* _SA1100_TS_H */
Binary files linux/drivers/char/selection.o and linux.new/drivers/char/selection.o differ
diff -urNX ignore linux/drivers/char/serial_sa1100.c linux.new/drivers/char/serial_sa1100.c
--- linux/drivers/char/serial_sa1100.c	Wed Dec 31 16:00:00 1969
+++ linux.new/drivers/char/serial_sa1100.c	Fri Sep  8 19:05:03 2000
@@ -0,0 +1,2306 @@
+/*
+ *  linux/drivers/char/serial_sa1100.c
+ *
+ *  Modified from serial.c
+ *
+ *  1/99: Changes for SA1100 internal async UARTs
+ *        Hugo Fiennes <altman@empeg.com>
+ *  Lots of changes, based on Deborah Wallace's Itsy/Brutus port, but
+ *  with fixes which seem to cure repeated byte problems.
+ *  This is no longer a 16x50 UART driver...
+ *
+ *  2/99: Cleanup: Now this can coexist with the generic serial driver
+ *	  and cleanly integrate into regular driver location.
+ *	  Nicolas Pitre <nico@visuaide.com>
+ *
+ *  6.6.99: handling Break conditions to break endless loops in rs_interrupt_single
+ *          Keith  keith@keith-koep.com
+ *
+ *  2000/04/09: 
+ *	Multi-machine support, further cleanup.
+ *	  Nicolas Pitre <nico@cam.org>
+ *  2000/06/29:
+ * 	Completed serial console support, further cleanup
+ * 	  Nicolas Pitre <nico@cam.org>
+ */
+
+/*
+ * Serial driver configuration section.  Here are the various options:
+ *
+ * SERIAL_PARANOIA_CHECK
+ * 		Check the magic number for the async_structure where
+ * 		ever possible.
+ */
+
+#undef SERIAL_PARANOIA_CHECK
+#define SERIAL_DO_RESTART
+
+/* Set of debugging defines */
+
+#undef SERIAL_DEBUG_INTR
+#undef SERIAL_DEBUG_OPEN
+#undef SERIAL_DEBUG_FLOW
+#undef SERIAL_DEBUG_RS_WAIT_UNTIL_SENT
+
+#define RS_ISR_PASS_LIMIT 256
+
+#define SERIAL_INLINE
+  
+#if defined(MODULE) && defined(SERIAL_DEBUG_MCOUNT)
+#define DBG_CNT(s) printk("(%s): [%x] refc=%d, serc=%d, ttyc=%d -> %s\n", \
+ kdevname(tty->device), (info->flags), serial_refcount,info->count,tty->count,s)
+#else
+#define DBG_CNT(s)
+#endif
+
+/*
+ * End of serial driver configuration section.
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <linux/tty.h>
+#include <linux/tty_flip.h>
+#include <linux/serial.h>
+#include <linux/serialP.h>
+#include <linux/major.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#ifdef CONFIG_SERIAL_SA1100_CONSOLE
+#include <linux/console.h>
+#endif
+
+#include <asm/system.h>
+#include <asm/irq.h>
+#include <asm/uaccess.h>
+#include <asm/bitops.h>
+#include <asm/arch/hardware.h>
+#include <asm/arch/serial_reg.h>
+#include <asm/mach-types.h>
+
+#ifdef SERIAL_INLINE
+#define _INLINE_ inline
+#endif
+
+static char *serial_name = "SA1100 serial driver";
+static char *serial_version = "1.3";
+
+static DECLARE_TASK_QUEUE(tq_serial);
+
+static struct tty_driver serial_driver, callout_driver;
+static int serial_refcount;
+
+/* number of characters left in xmit buffer before we ask for more */
+#define WAKEUP_CHARS 256
+
+#ifdef CONFIG_SERIAL_SA1100_CONSOLE
+static struct console sercons;
+#endif
+
+static void autoconfig(struct serial_state * info, int adv);
+static void change_speed(struct async_struct *info);
+static void rs_wait_until_sent(struct tty_struct *tty, int timeout);
+
+#define BASE_BAUD (3686400 / 16)
+#define STD_COM_FLAGS (ASYNC_BOOT_AUTOCONF | ASYNC_SKIP_TEST)
+
+#define NR_PORTS	3
+#define SA1100_UART1 \
+  (struct serial_state){ 0, BASE_BAUD, (unsigned long)&Ser1UTCR0, IRQ_Ser1UART, STD_COM_FLAGS }
+#define SA1100_UART2 \
+  (struct serial_state){ 0, BASE_BAUD, (unsigned long)&Ser2UTCR0, IRQ_Ser2ICP, STD_COM_FLAGS }
+#define SA1100_UART3 \
+  (struct serial_state){ 0, BASE_BAUD, (unsigned long)&Ser3UTCR0, IRQ_Ser3UART, STD_COM_FLAGS }
+
+static struct serial_state rs_table[NR_PORTS];
+static struct tty_struct *serial_table[NR_PORTS];
+static struct termios *serial_termios[NR_PORTS];
+static struct termios *serial_termios_locked[NR_PORTS];
+
+#ifndef MIN
+#define MIN(a,b)	((a) < (b) ? (a) : (b))
+#endif
+
+/* This is to be compatible with struct async_struct from serialP.h
+ * using the read_status_mask and ignore_status_mask fields.
+ * Since we need them twice we split them into two 16 bits parts.
+ * Those fields are int i.e. 32 bits and we only need 8 bits for each part
+ * so we're OK.
+ * Ex: instead of info->read_status1_mask, use _V1(info->read_status_mask).
+ */
+#define _V0( mask )	(((unsigned short *)(&(mask)))[0])
+#define _V1( mask )	(((unsigned short *)(&(mask)))[1])
+
+/*
+ * tmp_buf is used as a temporary buffer by serial_write.  We need to
+ * lock it in case the copy_from_user blocks while swapping in a page,
+ * and some other program tries to do a serial write at the same time.
+ * Since the lock will only come under contention when the system is
+ * swapping and available memory is low, it makes sense to share one
+ * buffer across all the serial ports, since it significantly saves
+ * memory if large numbers of serial ports are open.
+ */
+static unsigned char *tmp_buf;
+static DECLARE_MUTEX(tmp_buf_sem);
+
+static inline int serial_paranoia_check(struct async_struct *info,
+					kdev_t device, const char *routine)
+{
+#ifdef SERIAL_PARANOIA_CHECK
+	static const char *badmagic =
+		"Warning: bad magic number for serial struct (%s) in %s\n";
+	static const char *badinfo =
+		"Warning: null async_struct for (%s) in %s\n";
+
+	if (!info) {
+		printk(badinfo, kdevname(device), routine);
+		return 1;
+	}
+	if (info->magic != SERIAL_MAGIC) {
+		printk(badmagic, kdevname(device), routine);
+		return 1;
+	}
+#endif
+	return 0;
+}
+
+/* These all read/write 32 bits as this is what the SA1100 data book says to do */
+static inline unsigned int serial_in(struct async_struct *info, int offset)
+{
+	return ((volatile unsigned long *)info->port)[offset];
+}
+
+static inline void serial_out(struct async_struct *info, int offset, int value)
+{
+	((volatile unsigned long *)info->port)[offset] = value;
+}
+
+
+/*
+ * ------------------------------------------------------------
+ * rs_stop() and rs_start()
+ *
+ * This routines are called before setting or resetting tty->stopped.
+ * They enable or disable transmitter interrupts, as necessary.
+ * ------------------------------------------------------------
+ */
+static void rs_stop(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+
+	if (serial_paranoia_check(info, tty->device, "rs_stop"))
+		return;
+	
+	save_flags_cli(flags);
+	if (info->IER & UTCR3_TIE) {
+		info->IER &= ~UTCR3_TIE;
+		serial_out(info, UTCR3, info->IER);
+	}
+	restore_flags(flags);
+}
+
+static void rs_start(struct tty_struct *tty)
+{
+	struct async_struct *info = (struct async_struct *)tty->driver_data;
+	unsigned long flags;
+	
+	if (serial_paranoia_check(info, tty->device, "rs_start"))
+		return;
+	
+	save_flags_cli(flags);
+	if (info->xmit.head != info->xmit.tail
+	    && info->xmit.buf
+	    && !(info->IER & UTCR3_TIE)) {
+		info->IER |= UTCR3_TIE;
+		serial_out(info, UTCR3, info->IER);
+	}
+	restore_flags(flags);
+}
+
+/*
+ * ----------------------------------------------------------------------
+ *
+ * Here starts the interrupt handling routines.  All of the following
+ * subroutines are declared as inline and are folded into
+ * rs_interrupt().  They were separated out for readability's sake.
+ *
+ * Note: rs_interrupt() is a "fast" interrupt, which means that it
+ * runs with interrupts turned off.  People who may want to modify
+ * rs_interrupt() should try to keep the interrupt handler as fast as
+ * possible.  After you are done making modifications, it is not a bad
+ * idea to do:
+ * 
+ * gcc -S -DKERNEL -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer serial.c
+ *
+ * and look at the resulting assemble code in serial.s.
+ *
+ * 				- Ted Ts'o (tytso@mit.edu), 7-Mar-93
+ * -----------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used by the interrupt handler to schedule
+ * processing in the software interrupt portion of the driver.
+ */
+static _INLINE_ void rs_sched_event(struct async_struct *info,
+				  int event)
+{
+	info->event |= 1 << event;
+	queue_task(&info->tqueue, &tq_serial);
+	mark_bh(SERIAL_BH);
+}
+
+static _INLINE_ void receive_chars(struct async_struct *info,
+				 int *status0, int *status1)
+{
+	struct tty_struct *tty = info->tty;
+	unsigned char ch;
+	int ignored = 0;
+	struct	async_icount *icount;
+
+	icount = &info->state->icount;
+	do {
+		ch = serial_in(info, UART_RX);
+		if (tty->flip.count >= TTY_FLIPBUF_SIZE)
+			break;
+		*tty->flip.char_buf_ptr = ch;
+		icount->rx++;
+		
+#ifdef SERIAL_DEBUG_INTR
+		printk("DR%02x:%02x/%02x...", ch, *status0, *status1);
+#endif
+		*tty->flip.flag_buf_ptr = 0;
+		if (
+                    (*status1 & (UTSR1_PRE | UTSR1_FRE | UTSR1_ROR))) {
+			/*
+			 * For statistics only
+			 */
+			if (*status1 & UTSR1_PRE)
+				icount->parity++;
+			else if (*status1 & UTSR1_FRE)
+				icount->frame++;
+			if (*status1 & UTSR1_ROR)
+				icount->overrun++;
+
+			/*
+			 * Now check to see if character should be
+			 * ignored, and mask off conditions which
+			 * should be ignored.
+			 */
+			if (*status1 & _V1(info->ignore_status_mask)) {
+				if (++ignored > 100)
+					break;
+				goto ignore_char;
+			}
+			*status1 &= _V1(info->read_status_mask);
+		
+			if (*status0 & (UTSR0_RBB)) {
+#ifdef SERIAL_DEBUG_INTR
+				printk("handling break....");
+#endif
+				*tty->flip.flag_buf_ptr = TTY_BREAK;
+				if (info->flags & ASYNC_SAK)
+					do_SAK(tty);
+			} else if (*status1 & UTSR1_PRE)
+				*tty->flip.flag_buf_ptr = TTY_PARITY;
+			else if (*status1 & UTSR1_FRE)
+				*tty->flip.flag_buf_ptr = TTY_FRAME;
+			if (*status1 & UTSR1_ROR) {
+				/*
+				 * Overrun is special, since it's
+				 * reported immediately, and doesn't
+				 * affect the current character
+				 */
+				if (tty->flip.count < TTY_FLIPBUF_SIZE) {
+					tty->flip.count++;
+					tty->flip.flag_buf_ptr++;
+					tty->flip.char_buf_ptr++;
+					*tty->flip.flag_buf_ptr = TTY_OVERRUN;
+				}
+			}
+		}
+		tty->flip.flag_buf_ptr++;
+		tty->flip.char_buf_ptr++;
+		tty->flip.count++;
+	ignore_char:
+		*status1 = serial_in(info, UTSR1);
+	} while (*status1 & UTSR1_RNE);
+	tty_flip_buffer_push(tty);
+}
+
+static _INLINE_ void transmit_chars(struct async_struct *info, int *intr_done)
+{
+	int count;
+	
+	if (info->x_char) {
+		serial_out(info, UART_TX, info->x_char);
+		info->state->icount.tx++;
+		info->x_char = 0;
+		if (intr_done)
+			*intr_done = 0;
+		return;
+	}
+	if (info->xmit.head == info->xmit.tail
+	    || info->tty->stopped
+	    || info->tty->hw_stopped) {
+   	        if (info->IER&UTCR3_TIE) {
+		    info->IER &= ~UTCR3_TIE;
+		    serial_out(info, UTCR3, info->IER);
+                }
+ 
+                /* No more TFS events to service */
+		_V0(info->read_status_mask)&=~UTSR0_TFS;
+		return;
+	}
+	
+	/* Tried using FIFO (not checking TNF) for fifo fill: still had the
+	 * '4 bytes repeated' problem.
+	 */
+	count = info->xmit_fifo_size;
+	while(serial_in(info, UTSR1) & UTSR1_TNF) {
+		serial_out(info, UART_TX, info->xmit.buf[info->xmit.tail]);
+		info->xmit.tail = (info->xmit.tail + 1) & (SERIAL_XMIT_SIZE-1);
+                info->state->icount.tx++;
+		if (info->xmit.head == info->xmit.tail)
+			break;
+	};
+
+	if (CIRC_CNT(info->xmit.head,
+		     info->xmit.tail,
+		     SERIAL_XMIT_SIZE) < WAKEUP_CHARS)
+		rs_sched_event(info, RS_EVENT_WRITE_WAKEUP);
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("THRE...");
+#endif
+	if (intr_done)
+		*intr_done = 0;
+
+	if (info->xmit.head == info->xmit.tail) {
+	        if (info->IER & UTCR3_TIE) {
+		    info->IER &= ~UTCR3_TIE;
+		    serial_out(info, UTCR3, info->IER);
+		}
+
+		/* No more TFS events to service */
+                _V0(info->read_status_mask)&=~UTSR0_TFS;
+	}
+}
+
+/*
+ * This is the serial driver's interrupt routine for a single port
+ */
+static void rs_interrupt_single(int irq, void *dev_id, struct pt_regs * regs)
+{
+	int status0, status1;
+	int pass_counter = 0;
+	struct async_struct * info;
+	
+#ifdef SERIAL_DEBUG_INTR
+	printk("rs_interrupt_single(%d)...", irq);
+#endif
+	
+	info = (struct async_struct *)dev_id;
+	if (!info || !info->tty)
+		return;
+
+	/* We want to know about TFS events for now: we'll clear this later
+	   if we have nothing to send */
+	_V0(info->read_status_mask)|=UTSR0_TFS;
+	do {
+		status0 = serial_in(info, UTSR0) & _V0(info->read_status_mask);
+		status1 = serial_in(info, UTSR1) & _V1(info->read_status_mask);
+#ifdef SERIAL_DEBUG_INTR
+		printk("status0 = %x (%x), status1 = %x (%x)...", status0, serial_in(info,UTSR0), status1, serial_in(info,UTSR1));
+#endif
+		if (status1 & UTSR1_RNE)
+			receive_chars(info, &status0, &status1);
+		if (status0 & UTSR0_TFS)
+			transmit_chars(info, 0);
+		if (status0 & (UTSR0_RBB | UTSR0_REB)) {
+		        struct async_icount *icount=&info->state->icount;
+			struct tty_struct *tty = info->tty;
+
+		        if (status0 & UTSR0_RBB) {
+			        icount->brk++;
+
+				/* Clear break signal by writing break bit */
+				serial_out(info,UTSR0,UTSR0_RBB);
+#ifdef SERIAL_DEBUG_INTR
+				printk("handling break....");
+#endif
+				*tty->flip.flag_buf_ptr = TTY_BREAK;
+				if (info->flags & ASYNC_SAK)
+					do_SAK(tty);
+			} else if (status0 & UTSR0_REB) {
+                                /* Clear break signal by writing bit */
+			        serial_out(info,UTSR0,UTSR0_REB);
+			}
+		}
+		/* Clear receiver idle state if necessary */
+		if (status0 & UTSR0_RID) {
+			/* need to write a 1 back to clear it */
+			serial_out(info, UTSR0, UTSR0_RID);
+		}
+		if (pass_counter++ > RS_ISR_PASS_LIMIT) {
+			if (status0 || status1) printk("out-status0 = %x, status1 = %x...", status0, status1);
+			printk("rs_single loop break.\n");
+			break;
+		}
+		status0 = serial_in(info, UTSR0) & _V0(info->read_status_mask);
+		status1 = serial_in(info, UTSR1) & _V1(info->read_status_mask);
+#ifdef SERIAL_DEBUG_INTR
+		if (status0 || status1) printk("out-status0 = %x, status1 = %x...", status0, status1);
+#endif
+
+	} while (status0 || status1);
+	info->last_active = jiffies;
+
+#ifdef SERIAL_DEBUG_INTR
+	printk("end.\n");
+#endif
+}
+
+/*
+ * -------------------------------------------------------------------
+ * Here ends the serial interrupt routines.
+ * -------------------------------------------------------------------
+ */
+
+/*
+ * This routine is used to handle the "bottom half" processing for the
+ * serial driver, known also the "software interrupt" processing.
+ * This processing is done at the kernel interrupt level, after the
+ * rs_interrupt() has returned, BUT WITH INTERRUPTS TURNED ON.  This
+ * is where time-consuming activities which can not be done in the
+ * interrupt driver proper are done; the interrupt driver schedules
+ * them using rs_sched_event(), and they get done here.
+ */
+static void do_serial_bh(void)
+{
+	run_task_queue(&tq_serial);
+}
+
+static void do_softint(void *private_)
+{
+	struct async_struct	*info = (struct async_struct *) private_;
+	struct tty_struct	*tty;
+	
+	tty = info->tty;
+	if (!tty)
+		return;
+
+	if (test_and_clear_bit(RS_EVENT_WRITE_WAKEUP, &info->event)) {
+		if ((tty->flags & (1 << TTY_DO_WRITE_WAKEUP)) &&
+		    tty->ldisc.write_wakeup)
+			(tty->ldisc.write_wakeup)(tty);
+		wake_up_interruptible(&tty->write_wait);
+	}
+}
+
+/*
+ * ---------------------------------------------------------------
+ * Low level utility subroutines for the serial driver:  routines to
+ * figure out the appropriate timeout for an interrupt chain, routines
+ * to initialize and startup a serial port, and routines to shutdown a
+ * serial port.  Useful stuff like that.
+ * ---------------------------------------------------------------
+ */
+
+static int startup(struct async_struct * info)
+{
+	unsigned long flags;
+	int	retval=0;
+	struct serial_state *state= info->state;
+	unsigned long page;
+
+	page = get_free_page(GFP_KERNEL);
+	if (!page)
+		return -ENOMEM;
+
+	save_flags_cli(flags);
+
+	if (info->flags & ASYNC_INITIALIZED) {
+		free_page(page);
+		goto errout;
+	}
+
+	if (!state->port || !state->type) {
+		if (info->tty)
+			set_bit(TTY_IO_ERROR, &info->tty->flags);
+		free_page(page);
+		goto errout;
+	}
+	if (info->xmit.buf)
+		free_page(page);
+	else
+		info->xmit.buf = (unsigned char *) page;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("starting up ttys%d (irq %d)...", info->line, state->irq);
+#endif
+
+	retval = request_irq(state->irq, rs_interrupt_single, SA_INTERRUPT,
+			     "serial", (void*)info);
+	if (retval) {
+		if (capable(CAP_SYS_ADMIN)) {
+			if (info->tty)
+				set_bit(TTY_IO_ERROR,
+					&info->tty->flags);
+			retval = 0;
+		}
+		goto errout;
+	}
+
+	if (info->tty)
+		clear_bit(TTY_IO_ERROR, &info->tty->flags);
+	info->xmit.head = info->xmit.tail = 0;
+
+	/*
+	 * Set up the tty->alt_speed kludge
+	 */
+	if (info->tty) {
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_HI)
+			info->tty->alt_speed = 57600;
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_VHI)
+			info->tty->alt_speed = 115200;
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_SHI)
+			info->tty->alt_speed = 230400;
+		if ((info->flags & ASYNC_SPD_MASK) == ASYNC_SPD_WARP)
+			info->tty->alt_speed = 460800;
+	}
+	
+	/*
+	 * and set the speed of the serial port
+	 */
+	change_speed(info);
+
+	if (machine_is_bitsy()) {
+	  clr_bitsy_egpio(EGPIO_BITSY_IR_ON);
+	}
+
+	/*
+	 * Finally, enable interrupts
+	 */
+	info->IER = UTCR3_TXE | UTCR3_RXE | UTCR3_RIE;
+	serial_out(info, UTSR0, 0xff);
+	serial_out(info, UTCR3, info->IER);
+	
+	info->flags |= ASYNC_INITIALIZED;
+	restore_flags(flags);
+	return 0;
+	
+errout:
+	restore_flags(flags);
+	return retval;
+}
+
+/*
+ * This routine will shutdown a serial port; interrupts are disabled, and
+ * DTR is dropped if the hangup on close termio flag is on.
+ */
+static void shutdown(struct async_struct * info)
+{
+	unsigned long	flags;
+	struct serial_state *state;
+
+	if (!(info->flags & ASYNC_INITIALIZED))
+		return;
+
+	state = info->state;
+
+#ifdef SERIAL_DEBUG_OPEN
+	printk("Shutting down serial port %d (irq %d)....", info->line,
+	       state->irq);
+#endif
+	
+	save_flags_cli(flags);
+
+	info->IER &= ~(UTCR3_RIE|UTCR3_TIE|UTCR3_BRK);
+	serial_out(info, UTCR3, info->IER);	/* disable all intrs */
+	
+	free_irq(state->irq, (void*)info);
+
+	if (info->xmit.buf) {
+		unsigned long pg = (unsigned long) info->xmit.buf;
+		info->xmit.buf = 0;
+		free_page(pg);
+	}
+
+	if (info->tty)
+		set_bit(TTY_IO_ERROR, &info->tty->flags);
+
+#ifdef CONFIG_SERIAL_SA1100_CONSOLE
+	if (sercons.index != state->line) 
+#endif
+		serial_out(info, U