Index: linux-2.6/include/asm-arm/ams-delta-fiq.h
===================================================================
--- linux-2.6/include/asm-arm/ams-delta-fiq.h	(revision 0)
+++ linux-2.6/include/asm-arm/ams-delta-fiq.h	(revision 419)
@@ -0,0 +1,53 @@
+/*
+ * include/asm-arm/ams-delta-fiq.h
+ *
+ * Taken from the original Amstrad modifications to fiq.h
+ *
+ * Copyright (c) 2004 Amstrad Plc
+ * Copyright (c) 2006 Matt Callow
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+//These are the offsets from the begining of the
+//fiq_buffer. They are here as the buffer and header
+//need to be accessed by drivers servicing devices which
+//generate GPIO interrupts - e.g. qwerty, modem, smartcard
+
+#define FIQ_MASK                        0
+#define FIQ_STATE                       1
+#define FIQ_CHAR_CNT            2
+#define FIQ_FRNT_OFFSET 3
+#define FIQ_BACK_OFFSET 4
+#define FIQ_BUF_LEN                     5
+#define FIQ_CHAR                        6
+#define FIQ_MISSED_CHARS        7
+#define FIQ_BUFFER_START        8
+#define FIQ_GPIO_INT_MASK       9
+#define FIQ_CHAR_HICNT          10
+#define FIQ_IRQ_PEND            11
+#define FIQ_SIR_CODE_L1         12
+#define IRQ_SIR_CODE_L2         13
+
+#define FIQ_CNT_INT_00          14
+#define FIQ_CNT_INT_CHAR                15
+#define FIQ_CNT_INT_MDM         16
+#define FIQ_CNT_INT_FIQ         17
+#define FIQ_CNT_INT_04          18
+#define FIQ_CNT_INT_05          19
+#define FIQ_CNT_INT_KBD 20
+#define FIQ_CNT_INT_07          21
+#define FIQ_CNT_INT_08          22
+#define FIQ_CNT_INT_09          23
+#define FIQ_CNT_INT_10          24
+#define FIQ_CNT_INT_11          25
+#define FIQ_CNT_INT_12          26
+#define FIQ_CNT_INT_13          27
+#define FIQ_CNT_INT_14          28
+#define FIQ_CNT_INT_15          29
+
+#define FIQ_CIRC_BUFF           30      /*Start of circular buffer */
+
+extern void __init ams_delta_init_fiq(void);
Index: linux-2.6/include/asm-arm/arch-omap/irqs.h
===================================================================
--- linux-2.6/include/asm-arm/arch-omap/irqs.h	(revision 418)
+++ linux-2.6/include/asm-arm/arch-omap/irqs.h	(revision 419)
@@ -271,4 +271,7 @@
 #define NR_IRQS                 IH_BOARD_BASE
 #endif
 
+#ifdef CONFIG_FIQ
+#define FIQ_START 1024 
 #endif
+#endif
Index: linux-2.6/include/asm-arm/arch-omap/board-ams-delta.h
===================================================================
--- linux-2.6/include/asm-arm/arch-omap/board-ams-delta.h	(revision 418)
+++ linux-2.6/include/asm-arm/arch-omap/board-ams-delta.h	(revision 419)
@@ -50,9 +50,15 @@
 #define AMS_DELTA_LATCH2_NAND_NWE	0x0020
 #define AMS_DELTA_LATCH2_NAND_ALE	0x0040
 #define AMS_DELTA_LATCH2_NAND_CLE	0x0080
+#define AMD_DELTA_LATCH2_KEYBRD_PWR     0x0100
+#define AMD_DELTA_LATCH2_KEYBRD_DATA    0x0200
+#define AMD_DELTA_LATCH2_SMRTCRD_RSTIN  0x0400
+#define AMD_DELTA_LATCH2_SMRTCRD_CMDVCC 0x0800
 #define AMS_DELTA_LATCH2_MODEM_NRESET	0x1000
 #define AMS_DELTA_LATCH2_MODEM_CODEC	0x2000
 
+#define AMS_DELTA_KEYBRD_DATA_GPIO_PIN  0
+#define AMS_DELTA_KEYBRD_CLK_GPIO_PIN   1
 #define AMS_DELTA_GPIO_PIN_NAND_RB	12
 
 #ifndef __ASSEMBLY__
Index: linux-2.6/arch/arm/mach-omap1/Kconfig
===================================================================
--- linux-2.6/arch/arm/mach-omap1/Kconfig	(revision 418)
+++ linux-2.6/arch/arm/mach-omap1/Kconfig	(revision 419)
@@ -94,6 +94,14 @@
 	  Support for the Amstrad E3 (codename Delta) videophone. Say Y here
 	  if you have such a device.
 
+config AMS_DELTA_FIQ
+	bool "Fast Interrupt Request (FIQ) support for the E3"
+	depends MACH_AMS_DELTA
+	select FIQ
+	help
+	  Provide a FIQ handler for the E3. This redirects the gpio IRQ to FIQ
+	  And is required to use the E3 mailboard
+
 config MACH_OMAP_GENERIC
 	bool "Generic OMAP board"
 	depends on ARCH_OMAP1 && (ARCH_OMAP15XX || ARCH_OMAP16XX)
Index: linux-2.6/arch/arm/mach-omap1/ams-delta-fiq.c
===================================================================
--- linux-2.6/arch/arm/mach-omap1/ams-delta-fiq.c	(revision 0)
+++ linux-2.6/arch/arm/mach-omap1/ams-delta-fiq.c	(revision 419)
@@ -0,0 +1,155 @@
+/*
+ *  Amstrad E3 FIQ handling
+ *
+ *  Copyright (c) 2006 Matt Callow
+ *  Copyright (c) 2004 Amstrad Plc
+ *  Copyright (C) 2001 RidgeRun, Inc.
+ *  
+ * Parts of this code are taken from linux/arch/arm/mach-omap/irq.c in the MontaVista
+ * 2.4 kernel (and the Amstrad changes therein)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/kernel_stat.h>
+#include <asm/arch/gpio.h>
+#include <asm/fiq.h>
+#include <asm/ams-delta-fiq.h>
+
+#include <asm/hardware.h>
+#include <asm/mach-types.h>
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+#include <asm/mach/irq.h>
+
+#include <asm/arch/board-ams-delta.h>
+#include <asm/arch/gpio.h>
+#include <asm/arch/mux.h>
+#include <asm/arch/board.h>
+#include <asm/arch/common.h>
+
+static struct fiq_handler fh = {
+	name: "ams-delta-fiq"
+};
+
+//This buffer is shared between FIQ and IRQ contexts.
+//The FIQ and IRQ isrs can both read and write it.
+//It is structured as a header section several 32bit slots
+//Followed by the circular buffer where the FIQ isr stores
+//characters received from the qwerty keyboard.
+//See ams-delta-fiq.h for details of offsets
+unsigned int fiq_buffer[1024];
+static unsigned int fiq_buffer_irq[FIQ_CIRC_BUFF];
+
+
+static irqreturn_t deferred_fiq(int irq, void *dev_id,
+				     struct pt_regs *regs)
+{
+	int list_index;int buffer_offset;
+	int irq_num;
+	const unsigned int cpu = smp_processor_id();
+
+
+	//Call the interrupt handler for each GPIO interrupt where the FIQ interrupt counter > the IRQ counter
+	for(	buffer_offset = FIQ_CNT_INT_MDM; buffer_offset >= FIQ_CNT_INT_CHAR; buffer_offset--){
+		while(fiq_buffer[buffer_offset] > fiq_buffer_irq[buffer_offset]){
+			list_index = buffer_offset - FIQ_CNT_INT_00;
+			irq_num = list_index + IH_GPIO_BASE;
+			if (irq_desc[irq_num].action->handler != NULL) {
+				//printk("deferred_fiq %d %p %p\n", irq_num, &irq_desc[irq_num], irq_desc[irq_num].action->handler);
+				irq_desc[irq_num].action->handler(irq_num, irq_desc[irq_num].action->dev_id, regs);
+				// keep /proc/interrupts up to date
+				kstat_cpu(cpu).irqs[irq_num]++;
+
+				//Increment the IRQ count to ensure one IRQ call per FIQ
+				//There is a corresponding increment in the FIQ handler
+				//Having and IRQ & FIQ level counters avoids any races
+				//There is a possibility that two calls to this handler
+				//occur when keyboard and modem interrupts are close together. Although
+				//both interrupts will be serviced during the first one. This should have
+				//no serious effect apart from an unecessary call through here on occasion, but
+				//that's better than missing one.
+				fiq_buffer_irq[buffer_offset]++;
+			}
+			else{
+				printk(KERN_WARNING "!!!- NULL HANDLER for[%d]!!!\n", irq_num );
+				fiq_buffer_irq[buffer_offset]++;
+			}
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+void __init ams_delta_init_fiq(void)
+{
+	int retval;
+        void *fiqhandler_start;
+        unsigned int fiqhandler_length;
+        extern unsigned char qwerty_fiqin_start, qwerty_fiqin_end;
+	struct  pt_regs FIQ_regs;
+	unsigned long val, offset;
+	int i;
+
+
+	fiqhandler_start = &qwerty_fiqin_start;
+        fiqhandler_length = &qwerty_fiqin_end - &qwerty_fiqin_start;
+	printk(KERN_INFO "Installing fiq handler from %p, length 0x%x\r\n", fiqhandler_start, fiqhandler_length);
+
+        retval = claim_fiq(&fh);
+        if(retval) {
+                printk(KERN_ERR "ams_delta_init_fiq(): couldn't claim FIQ. ret = %d\n\r", retval);
+                return ;
+        }
+
+	if (request_irq(INT_OS_TIMER, deferred_fiq, 0, "deferred_fiq", 0) < 0) {
+		printk(KERN_ERR "Failed to get OS_TIMER\r\n");
+		release_fiq(&fh);
+		return;
+	}
+	
+        set_fiq_handler(fiqhandler_start, fiqhandler_length);
+
+	//Initialise the buffer which is shared between FIQ mode and IRQ mode
+	fiq_buffer[FIQ_GPIO_INT_MASK]   = 0;
+	fiq_buffer[FIQ_MASK]		= 0;
+	fiq_buffer[FIQ_STATE]           = 0;
+	fiq_buffer[FIQ_CHAR]            = 0;
+	fiq_buffer[FIQ_CHAR_CNT]        = 0;
+	fiq_buffer[FIQ_CHAR_HICNT]      = 0;
+	fiq_buffer[FIQ_FRNT_OFFSET]     = 0;
+	fiq_buffer[FIQ_BACK_OFFSET]     = 0;
+	fiq_buffer[FIQ_BUF_LEN]         = 256;
+	fiq_buffer[FIQ_MISSED_CHARS]    = 0;
+	fiq_buffer[FIQ_BUFFER_START]            = (unsigned int) &fiq_buffer[FIQ_CIRC_BUFF] ;
+
+	for(i = FIQ_CNT_INT_00; i <= FIQ_CNT_INT_15; i++){
+		fiq_buffer[i]   = 0;
+		fiq_buffer_irq[i]       = 0;
+	}
+
+
+	//FIQ mode r9 always points to the fiq_buffer, becauses the FIQ isr will
+	//run in an unpredictable context.
+	//The fiq_buffer is the FIQ isr's only means of communication with the IRQ level
+	//and other kernel context code.
+	FIQ_regs.ARM_r9 = (unsigned int)fiq_buffer;
+	FIQ_regs.ARM_r10 = 0;
+	FIQ_regs.ARM_sp = 0;
+
+	set_fiq_regs(&FIQ_regs);
+
+	printk(KERN_INFO "request_fiq(): fiq_buffer = %p\n", fiq_buffer);
+
+	// set FIQ, priority 0, tigger rising on the GPIO INT
+	// It would be nice to use omap_irq_set_cfg() here, but it's static
+	val = 1 | ((IRQT_RISING & 0x1) << 1);
+	offset = IRQ_ILR0_REG_OFFSET + INT_GPIO_BANK1 * 0x4;
+	omap_writel(val, OMAP_IH1_BASE + offset);
+}
+
+EXPORT_SYMBOL(fiq_buffer);
Index: linux-2.6/arch/arm/mach-omap1/Makefile
===================================================================
--- linux-2.6/arch/arm/mach-omap1/Makefile	(revision 418)
+++ linux-2.6/arch/arm/mach-omap1/Makefile	(revision 419)
@@ -23,6 +23,7 @@
 obj-$(CONFIG_MACH_OMAP_PALMTE)		+= board-palmte.o
 obj-$(CONFIG_MACH_NOKIA770)		+= board-nokia770.o
 obj-$(CONFIG_MACH_AMS_DELTA)		+= board-ams-delta.o
+obj-$(CONFIG_AMS_DELTA_FIQ)		+= ams-delta-fiq.o ams-delta-fiq-handler.o
 
 ifeq ($(CONFIG_ARCH_OMAP15XX),y)
 # Innovator-1510 FPGA
Index: linux-2.6/arch/arm/mach-omap1/board-ams-delta.c
===================================================================
--- linux-2.6/arch/arm/mach-omap1/board-ams-delta.c	(revision 418)
+++ linux-2.6/arch/arm/mach-omap1/board-ams-delta.c	(revision 419)
@@ -33,6 +33,10 @@
 #include <asm/arch/board.h>
 #include <asm/arch/common.h>
 
+#ifdef CONFIG_AMS_DELTA_FIQ
+#include <asm/ams-delta-fiq.h>
+#endif 
+
 static u8 ams_delta_latch1_reg;
 static u16 ams_delta_latch2_reg;
 
@@ -124,6 +128,7 @@
 
 static void __init ams_delta_init_irq(void)
 {
+	printk("ams_delta_init_irq\n\r");
 	omap1_init_common_hw();
 	omap_init_irq();
 	omap_gpio_init();
@@ -229,6 +234,7 @@
 
 static void __init ams_delta_init(void)
 {
+	printk("ams_delta_init\n\r");
 	iotable_init(ams_delta_io_desc, ARRAY_SIZE(ams_delta_io_desc));
 
 	omap_board_config = ams_delta_config;
@@ -241,6 +247,11 @@
 
 	platform_add_devices(ams_delta_devices, ARRAY_SIZE(ams_delta_devices));
 
+#ifdef CONFIG_AMS_DELTA_FIQ
+	// this can't be done in init_irq - the kernel fails to boot
+	ams_delta_init_fiq();
+#endif
+
 	early_serial_setup(&ams_delta_modem_port);
 }
 
Index: linux-2.6/arch/arm/mach-omap1/ams-delta-fiq-handler.S
===================================================================
--- linux-2.6/arch/arm/mach-omap1/ams-delta-fiq-handler.S	(revision 0)
+++ linux-2.6/arch/arm/mach-omap1/ams-delta-fiq-handler.S	(revision 419)
@@ -0,0 +1,328 @@
+/*
+ *  linux/arch/arm/mach-omap1/ams-delta-fiq-handler.S
+ *
+ *  Based on  linux/arch/arm/lib/floppydma.S
+ *  Renamed and modified to work with 2.6 kernel by Matt Callow
+ *  Copyright (C) 1995, 1996 Russell King
+ *  Copyright (C) 2004 Pete Trapps
+ *  Copyright (C) 2006 Matt Callow
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/linkage.h>
+#include <asm/assembler.h>
+#include <asm/hardware.h>
+
+#define OMAP1510_TIMER1_BASE 0xfffec500
+#define OMAP1510_TIMER2_BASE 0xfffec600
+#define OMAP1510_TIMER3_BASE 0xfffec700
+#define OMAP1510_WATCHDOG_BASE	0xfffec800
+
+#define MAX_INTER_BIT_GAP (6 * 200) 	/* 6 ticks per uSec  => 200uSec */
+
+/* MPU Timer Register Offsets */
+#define CNTL_TIMER 0
+#define LOAD_TIM   4
+#define READ_TIM   8
+
+#define MPU_L1_INTERRUPT_BASE		IO_ADDRESS(0xFFFECB00)
+#define MPU_L2_INTERRUPT_BASE		IO_ADDRESS(0xFFFE0000)
+
+#define ITR					0x00
+#define MIR					0x04
+#define SIR_IRQ_CODE 		0x10
+#define SIR_FIQ_CODE 		0x14
+#define CONTROL_REG			0x18
+#define ILR14				0x54
+#define ILR30				0x94
+#define ISR					0x9C
+
+#define T_BIT		0x20
+#define F_BIT		0x40
+#define I_BIT		0x80
+#define CC_V_BIT	(1 << 28)
+#define CC_C_BIT	(1 << 29)
+#define CC_Z_BIT	(1 << 30)
+#define CC_N_BIT	(1 << 31)
+#define PCMASK		0
+
+#define GPIO_BASE			IO_ADDRESS(0xFFFCE000)
+#define GPIO_DATA_INPUT 			0x00
+#define GPIO_DATA_OUTPUT		0x04
+#define GPIO_DIRECTION_CONTROL	0x08
+#define GPIO_INTERRUPT_CONTROL	0x0C
+#define GPIO_INTERRUPT_MASK 		0x10
+#define GPIO_INTERRUPT_STATUS	0x14
+#define GPIO_PIN_CONTROL		0x18
+/*
+GPIO_DATA_INPUT bit 0 is MBRD_DI_PROC
+GPIO_DATA_INPUT bit 1 is MBRD_CLK
+*/
+#define MBRD_DI_PROC_MASK		0x01
+#define MBRD_CLK_MASK			0x02
+#define MDM_MASK				0x04
+
+#define INT_GPIO0    0
+#define INT_GPIO1    1
+#define INT_GPIO2    2
+#define INT_GPIO3    3
+#define INT_GPIO4    4
+#define INT_GPIO5    5
+#define INT_GPIO6    6
+#define INT_GPIO7    7
+#define INT_GPIO8    8
+#define INT_GPIO9    9
+#define INT_GPIO10  10
+#define INT_GPIO11  11
+#define INT_GPIO12  12
+#define INT_GPIO13  13
+#define INT_GPIO14  14
+#define INT_GPIO15  15
+
+#define GPIO6_HDW_IP_SCARD_0_MASK 0x40
+
+/*Driver buffer offsets*/
+#define FIQ_MASK			0
+#define FIQ_STATE			4
+#define FIQ_CHAR_CNT		8
+#define FIQ_FRNT_OFFSET	12
+#define FIQ_BACK_OFFSET	16
+#define FIQ_BUF_LEN			20
+#define FIQ_CHAR			24
+#define FIQ_MISSED_CHARS	28
+#define FIQ_BUFFER_START	32
+#define FIQ_GPIO_INT_MASK	36
+#define FIQ_CHAR_HICNT		40
+#define FIQ_IRQ_PEND		44
+#define FIQ_SIR_CODE_L1		48
+#define IRQ_SIR_CODE_L2		52
+#define FIQ_CNT_INT_00		56
+#define FIQ_CNT_INT_CHAR	60
+#define FIQ_CNT_INT_MDM		64
+#define FIQ_CNT_INT_FIQ		68
+#define FIQ_CNT_INT_04		72
+#define FIQ_CNT_INT_05		76
+#define FIQ_CNT_INT_KBD	80
+#define FIQ_CNT_INT_07		84
+#define FIQ_CNT_INT_08		88
+#define FIQ_CNT_INT_09		92
+#define FIQ_CNT_INT_10		96
+#define FIQ_CNT_INT_11		100
+#define FIQ_CNT_INT_12		104
+#define FIQ_CNT_INT_13		108
+#define FIQ_CNT_INT_14		112
+#define FIQ_CNT_INT_15		116
+#define FIQ_CIRC_BUFF		120	/*Start of circular buffer */
+
+#define TIMER_32k_MASK	0x400000
+#define GPIO_INT_MASK   0x4000
+
+/*
+Register useage
+r8 -
+r9 - the driver buffer
+r10 -
+r11 -
+r12 -base pointers
+r13 -
+*/
+	.text
+
+	.global qwerty_fiqin_end
+
+ENTRY(qwerty_fiqin_start)
+		@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+		@setup base pointer = MPU_L1_INTERRUPT_BASE 0xFFFECB00
+		ldr r12, mpu_l1_interrupt_base
+		ldr r11, [r12, #SIR_FIQ_CODE]	@read SIR to clear FIQ
+
+		str r11, [r9, #FIQ_SIR_CODE_L1] @ save FIQ source for IRQ handlers
+
+		@@@@@@@@@@@@@@@@@@@@@@@@@@@
+		@setup base pointer = GPIO_BASE 0xFFFCE000
+		ldr r12, gpio_base
+
+		@GPIO6 HIGH for scope trace
+		@ldr r11, [r12, #GPIO_DATA_OUTPUT]
+		@orr r11, r11, #GPIO6_HDW_IP_SCARD_0_MASK
+		@str r11, [r12, #GPIO_DATA_OUTPUT] @set GPIO6
+
+		@Count interrupts
+		ldr r10, [r9, #FIQ_CNT_INT_FIQ]			@ get fiq int count for IRQ handlers
+		add r10, r10, #1				@increment it
+		str r10, [r9, #FIQ_CNT_INT_FIQ]			@ save count for IRQ handlers
+
+key:	@Is it a keyboard interrupt?
+		ldr r11, [r12,#GPIO_INTERRUPT_STATUS]	@ get GPIO interrupt status
+		and r10, r11, #MBRD_CLK_MASK			@reveal keyboard bit
+		cmp r10, #MBRD_CLK_MASK 			@is keyboard bit low?
+		bne mdm						@no - spurious - try mdm
+		mov r10, #MBRD_CLK_MASK
+		str r10, [r12,#GPIO_INTERRUPT_STATUS]	@ Set the bit to clear the keyboard interrupt
+
+		ldr r10, [r9, #FIQ_CNT_INT_KBD]			@ get kybd int count for IRQ handlers
+		add r10, r10, #1				@increment it
+		str r10, [r9, #FIQ_CNT_INT_KBD]			@ save status for IRQ handlers
+
+		@@@@@@@@@@@@@@@@@@@@@@
+		@ start processing keyboard bitstream
+state:
+		ldr r10, [r9,#FIQ_STATE]
+		cmp r10, #0				@are we in start state (expecting start bit)?
+		bne data				@no - we are in data processing state
+
+start:
+		ldr r11, [r12, #GPIO_DATA_INPUT]	@get input
+		and r11, r11, #MBRD_DI_PROC_MASK	@mask out other bits
+		cmp r11, #MBRD_DI_PROC_MASK		@is kybd data in bit set?
+		bne exit				@no - exit and wait for next interrupt
+		mov r10, #1				@a good start bit, so set state to data processing
+		str r10, [r9,#FIQ_STATE]
+		mov r10, #0x02				@set mask to 0x02
+		str r10, [r9, #FIQ_MASK]
+		mov r10, #0				@clear character byte
+		str r10, [r9, #FIQ_CHAR]
+
+		@@@@@@@@@ MASK MDM TILL KEY DONE @@@@@@@@@@@@@@
+		mov r10, #MDM_MASK
+		ldr r11, [r12, #GPIO_INTERRUPT_MASK]	@get mask reg
+		str r11, [r9, #FIQ_GPIO_INT_MASK]	@save it for later restore
+		orr r11, r11, r10			@mask modem int
+		str r11, [r12, #GPIO_INTERRUPT_MASK]	@write mask reg
+		@@@@@@@@@@ END @@@@@@@@@@@@@@@@@
+		b exit					@leave and wait for first data bit
+
+data:	ldr r11, [r12, #GPIO_DATA_INPUT]		@get input
+		and r11, r11, #MBRD_DI_PROC_MASK	@mask out other bits
+		cmp r11, #0				@is kybd data in clear?
+		bne shift
+		ldr r10, [r9, #FIQ_CHAR]
+		ldr r11, [r9, #FIQ_MASK]
+		orr r10, r11, r10			@ or mask and character byte
+		str r10, [r9, #FIQ_CHAR]
+
+shift:	ldr r10, [r9, #FIQ_MASK]
+		mov r10, r10, lsl #1			@shift mask left
+		str r10, [r9, #FIQ_MASK]
+		cmp r10, #0x800				@have we got all the bits?
+		bne exit				@not yet - get more
+		mov r10, #0				@yes set state to start
+		str r10, [r9, #FIQ_STATE]
+
+		@@@@@@@@@ KEY DONE - RESTORE INTERRUPT MASK @@@
+		ldr r11, [r9,#FIQ_GPIO_INT_MASK]
+		str r11, [r12, #GPIO_INTERRUPT_MASK]	@write mask reg
+		@@@@@@@@@@ END @@@@@@@@@@@@@@@@@
+
+		@Add char to circular buffer
+		ldr r10, [r9, #FIQ_CHAR_CNT]		@get char count
+		ldr r8, [r9, #FIQ_BUF_LEN]		@get buffer size
+		cmp r10, r8				@is buffer full?
+		bne not_full
+		ldr r10, [r9, #FIQ_MISSED_CHARS]		@get missed char count
+		add r10, r10, #1						@inc missed char count
+		str r10, [r9, #FIQ_MISSED_CHARS]		@ save missed char count
+		b int									@force IRQ
+
+not_full:ldr r10, [r9, #FIQ_FRNT_OFFSET]			@get current front offset
+		ldr r11, [r9, #FIQ_BUF_LEN]				@get buff size
+		cmp r10, 	r11							@offset == buffer size?
+		bne store								@no - so branch to front
+		mov r10, #0							@set front offset = 0
+
+store:		ldr r12, [r9, #FIQ_BUFFER_START]		@get start addr of circ buffer
+		add r12, r12, r10, LSL #2			@add front offset to circ buffer base
+		ldr r8, [r9, #FIQ_CHAR]				@get latest character
+		str r8, [r12]					@####01 store character in circ buffer
+		add r10, r10, #1				@inc front offset
+		str r10, [r9, #FIQ_FRNT_OFFSET]			@####04 store front offset
+		ldr r8, [r9, #FIQ_CHAR_CNT]			@get char count
+		add r8, r8, #1					@inc count of chars in buffer
+		str r8, [r9, #FIQ_CHAR_CNT]			@####02 store char count
+
+		ldr r10, [r9, #FIQ_CHAR_HICNT]			@get char count hi watermark
+		cmp r10, r8
+		bgt setstat					@hi count is bigger - so don't change it
+		str r8, [r9, #FIQ_CHAR_HICNT]			@store char count in hi watermark
+setstat:
+		ldr r10, [r9, #FIQ_CNT_INT_CHAR]		@ get char count for IRQ handlers
+		add r10, r10, #1				@increment it
+		str r10, [r9, #FIQ_CNT_INT_CHAR]		@ save status for IRQ handlers
+
+		@@@@@@@@@@@@@@@@@@@@@@@@
+		@ Force a INT_OS_32kHz_TIMER interrupt
+		@setup base pointer = MPU_L2_INTERRUPT_BASE	0xFFFE0000
+int:		ldr r12, mpu_l2_interrupt_base
+		mov r10, #TIMER_32k_MASK			@set 32kHz_TIMER bit
+		str r10, [r12, #ISR] 				@write ISR
+
+
+mdm:	@Is it a modem interrupt?
+		ldr r11, [r12,#GPIO_INTERRUPT_MASK] @ get GPIO interrupt mask
+		and r10, r11, #MDM_MASK 				@reveal modem bit
+		cmp r10, #MDM_MASK					@is mask bit set?
+		beq exit							@yes, exit
+
+		ldr r11, [r12,#GPIO_DATA_INPUT] 		@ get GPIO data line status
+		and r10, r11, #MDM_MASK 				@reveal modem bit
+		cmp r10, #MDM_MASK					@is modem bit set?
+		bne exit							@no, so skip mdm - exit
+
+		mov r10, #MDM_MASK					@its a mdm interrupt
+		str r10, [r12,#GPIO_INTERRUPT_STATUS]	@ Set the bit to clear the modem interrupt
+
+		ldr r10, [r9, #FIQ_CNT_INT_MDM]			@ get mdm int count for IRQ handlers
+		add r10, r10, #1						@increment it
+		str r10, [r9, #FIQ_CNT_INT_MDM]			@ save count for IRQ handlers
+
+		@@@@@@@@@@@@@@@@@@@@@@@@
+		@ Force a INT_OS_32kHz_TIMER interrupt
+		@setup base pointer = MPU_L2_INTERRUPT_BASE 0xFFFE0000
+		ldr r12, mpu_l2_interrupt_base
+		mov r10, #TIMER_32k_MASK			@set 32kHz_TIMER bit
+		str r10, [r12, #ISR]					@write ISR
+
+
+		@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+		@setup base pointer = MPU_L1_INTERRUPT_BASE 0xFFFECB00
+exit:		ldr r12, mpu_l1_interrupt_base
+		mov r10, # 0x2
+		str r10, [r12, #CONTROL_REG] @reset FIQ Agreement
+
+
+		@@@@@@@@@@@@@@@@ MOV AT YOUR PERIL! @@@@@@@@@@@@@@@
+		@setup r12 as base pointer = GPIO_BASE 0xFFFCE000
+		@mov r12, #0xFF000000
+		@orr r12, r12, #0xFC0000
+		@orr r12, r12, #0xE000
+
+		@GPIO6 LOW for scope trace
+		@ldr r11, [r12, #GPIO_DATA_OUTPUT]
+		//keep above !! 
+		@mvn r10, #GPIO6_HDW_IP_SCARD_0_MASK
+		@and r11, r11, r10
+		@str r11, [r12, #GPIO_DATA_OUTPUT] @clear GPIO6
+
+		subs	pc, lr, #4		@ return from FIQ
+
+/*
+ * Virtual addresses for IO
+ */
+mpu_l1_interrupt_base:
+		.word MPU_L1_INTERRUPT_BASE
+mpu_l2_interrupt_base:
+		.word MPU_L2_INTERRUPT_BASE
+gpio_base:
+		.word GPIO_BASE
+qwerty_fiqin_end:
+
+
+/*
+ * Check the size of the FIQ, it cannot go beyond 0xffff0200, and is copied to 0xffff001c
+ */
+.if (qwerty_fiqin_end - qwerty_fiqin_start) >  (0x200 - 0x1c)
+.err 
+.endif
Index: linux-2.6/arch/arm/configs/ams_delta_defconfig
===================================================================
--- linux-2.6/arch/arm/configs/ams_delta_defconfig	(revision 418)
+++ linux-2.6/arch/arm/configs/ams_delta_defconfig	(revision 419)
@@ -7,6 +7,7 @@
 CONFIG_MMU=y
 CONFIG_RWSEM_GENERIC_SPINLOCK=y
 CONFIG_GENERIC_CALIBRATE_DELAY=y
+CONFIG_FIQ=y
 
 #
 # Code maturity level options
@@ -142,6 +143,7 @@
 # CONFIG_MACH_VOICEBLUE is not set
 # CONFIG_MACH_OMAP_PALMTE is not set
 CONFIG_MACH_AMS_DELTA=y
+CONFIG_AMS_DELTA_FIQ=y
 # CONFIG_MACH_OMAP_GENERIC is not set
 
 #
@@ -566,6 +568,7 @@
 CONFIG_SERIO_SERPORT=y
 CONFIG_SERIO_LIBPS2=y
 # CONFIG_SERIO_RAW is not set
+CONFIG_SERIO_AMS_DELTA=y
 # CONFIG_GAMEPORT is not set
 
 #
Index: linux-2.6/drivers/input/serio/Kconfig
===================================================================
--- linux-2.6/drivers/input/serio/Kconfig	(revision 418)
+++ linux-2.6/drivers/input/serio/Kconfig	(revision 419)
@@ -180,4 +180,13 @@
 	  To compile this driver as a module, choose M here: the
 	  module will be called serio_raw.
 
+config SERIO_AMS_DELTA
+	tristate "Amstrad Delta (E3) keyboard support"
+	depends on MACH_AMS_DELTA && AMS_DELTA_FIQ
+	---help---
+	  Say Y here if has an E3 and want to use the separate keyboard 
+
+	  To compile this driver as a module, choose M here: the
+	  module will be called ams_delta_keyboard
+
 endif
Index: linux-2.6/drivers/input/serio/ams_delta_keyboard.c
===================================================================
--- linux-2.6/drivers/input/serio/ams_delta_keyboard.c	(revision 0)
+++ linux-2.6/drivers/input/serio/ams_delta_keyboard.c	(revision 419)
@@ -0,0 +1,223 @@
+/*
+ *  Amstrad E3 (delta) keyboard driver
+ *
+ *  Copyright (c) 2006 Matt Callow
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * Thanks to Cliff Lawson for his help
+ *
+ * The amstrad keyboard (or mailboard) is connected to GPIO 0 (clock) and GPIO 1 (data)
+ * It uses normal PC-AT style serial transmission, but the data and clock lines are
+ * inverted on the E3 mainboard, and the scancodes produced are non-standard
+ *
+ * Due to the strict timing requirements of the interface, the serial data stream is
+ * read using a FIQ handler, and then the resulting byte stream passed to this driver 
+ * via a circular buffer
+ */
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+#include <asm/arch/board-ams-delta.h>
+#include <asm/arch/gpio.h>
+#include <asm/fiq.h>
+#include <asm/ams-delta-fiq.h>
+
+MODULE_AUTHOR("Matt Callow");
+MODULE_DESCRIPTION("AMS Delta (E3) Keyboard driver");
+MODULE_LICENSE("GPL");
+
+#define MAX_SCANCODE 0x84
+
+extern unsigned int fiq_buffer[1024];
+
+/*
+ * This table converts the amstrad mailboard scancodes to normal PC-AT scancodes
+ * The diagram below shows the amstrad keyboard, with the raw scancodes
+ *
+ *   (70) (7A) (46) (7C) (77)   Amstrad     (72) (69) (1A) (2A) (1C) (15)
+ * [  71][1:74][2:73][3:6B][4:22][5:1B][6:1D][7:1E][8:79][9:7D][0:75][  6C]
+ *  [Q:21][W:23][E:24][R:26][T:52][Y:5D][U:0D][I:0E][O:32][P:34]  |return|
+ *   [A:31][S:33][D:35][F:36][G:29][H:5B][J:03][K:76][L:3A][@:3B]  |   2C|
+ *  [  3C][Z:3D][X:4E][C:54][V:0B][B:05][N:41][M:42][.:43][  3E][  55]
+ *  [  83][  06][  49][           4B         ][,:44][  16][  2E][  09]
+ *
+ * These scancodes are then translated to AT scancodes using the following table
+ * The amstrad keyboard does not produce any extended scancodes, but we need to
+ * translate some amstrad scancodes to a AT extended scancode, hence the 16bit
+ * value for the translated scancode
+ *
+ * Translations for the non-obvious keys are:
+ * Store      -> Tab
+ * Setup      -> Caps Lock
+ * Service    -> Backslash
+ * Games      -> Left Alt
+ * Internet   -> Right Alt
+ * Home       -> Home
+ * Office     -> Page Up
+ * Mobile     -> Page Down
+ * Mobile Msg -> Insert
+ * Email      -> Delete 
+ * Fax        -> End
+ * Stop       -> Esc
+ * Symbol     -> Left Ctrl
+ * Calc       -> Right Ctrl
+ * Address    -> Scroll Lock
+ *
+ */
+static unsigned short trans_table[MAX_SCANCODE] = {
+             0,      0,      0, 0x003B,      0, 0x0032, 0x007E,      0,  /* 00..07 */
+             0, 0xE074,      0, 0x002A,      0, 0x003C, 0x0043,      0,  /* 08..0F */
+             0,      0,      0,      0,      0, 0xE069, 0xE06B,      0,  /* 10..17*/
+             0,      0, 0xE07A, 0x002E, 0xE071, 0x0036, 0x003D,      0,  /* 18..1F */
+             0, 0x0015, 0x0025, 0x001D, 0x0024,      0, 0x002D,      0,  /* 20..27 */
+             0, 0x0034, 0xE070,      0, 0x005A,      0, 0xE072,      0,  /* 28..2F */
+             0, 0x001C, 0x0044, 0x001B, 0x004D, 0x0023, 0x002B,      0,  /* 30..37 */
+             0,      0, 0x004B, 0x0052, 0x0012, 0x001A, 0xE075,      0,  /* 38..3F */
+             0, 0x0031, 0x003A, 0x0049, 0x0041,      0, 0x0061,      0,  /* 40..47 */
+             0, 0x0014,      0, 0x0029,      0,      0, 0x0022,      0,  /* 48..4F */
+             0,      0, 0x002C,      0, 0x0021, 0x0059,      0,      0,  /* 50..57 */
+             0,      0,      0, 0x0033,      0, 0x0035,      0,      0,  /* 58..5F */
+             0,      0,      0,      0,      0,      0,      0,      0,  /* 60..67 */
+             0, 0xE07D,      0, 0x0026, 0x0066,      0,      0,      0,  /* 68..6F */
+        0x000D, 0x0076, 0xE06C, 0x001E, 0x0016, 0x0045, 0x0042, 0xE011,  /* 70..77 */
+             0, 0x003E, 0x0058,      0, 0x0011, 0x0046,      0,      0,  /* 78..7F */
+             0,      0,      0, 0xE014                                   /* 80..83 */
+};
+
+static struct serio *ams_delta_kbd_port;
+
+static int check_data(int data) {
+	int i;
+	int parity=0;
+
+	// check valid stop bit
+	if (!(data & 0x400)) {
+		printk(KERN_WARNING "Invalid stop bit in AMS keyboard data=0x%X\r\n", data);
+		return 0;
+	}
+	// calculate the parity
+	for (i=1;i<10;i++) {
+		if (data & (1<<i)) parity++;
+	}
+	// it should be odd
+	if (!(parity & 0x01)) {
+		printk(KERN_WARNING "Paritiy check failed in AMS keyboard data=0x%X parity 0x%X\r\n", data, parity);
+	}
+	return 1;
+}
+static irqreturn_t ams_delta_kbd_interrupt(int irq, void *dev_id,
+				     struct pt_regs *regs)
+{
+	int *circ_buff	= &fiq_buffer[FIQ_CIRC_BUFF];
+	/*
+	 * Read data from the CIRC buffer, check it, translate the scancode
+	 * and then pass it on the serio
+	 */
+	fiq_buffer[FIQ_IRQ_PEND] = 0;
+
+	while( fiq_buffer[FIQ_CHAR_CNT] > 0) {
+		int data;
+		u8 original_scancode;
+		unsigned short translated_scancode;
+
+		data = circ_buff[fiq_buffer[FIQ_BACK_OFFSET]] ;
+		fiq_buffer[FIQ_BACK_OFFSET]++;
+		fiq_buffer[FIQ_CHAR_CNT]--;
+		if (fiq_buffer[FIQ_BACK_OFFSET] == fiq_buffer[FIQ_BUF_LEN]) {
+			fiq_buffer[FIQ_BACK_OFFSET] = 0;
+		}
+
+		if (check_data(data)) {
+			//kbd_rx_count++;
+			original_scancode = (u8) (data >> 1) & 0xFF;
+			//printk(KERN_DEBUG "old scancode = %X\n", original_scancode);
+			if (original_scancode < MAX_SCANCODE) {
+				translated_scancode = trans_table[original_scancode];
+				//printk(KERN_DEBUG "translated_scancode = %X\n", translated_scancode);
+				if ((translated_scancode & 0xff00) != 0) {
+					serio_interrupt(ams_delta_kbd_port, translated_scancode >> 8, 0, regs);
+				}
+				if ((translated_scancode & 0xff) != 0) {
+					serio_interrupt(ams_delta_kbd_port, translated_scancode & 0xff, 0, regs);
+				}
+			} else {
+				serio_interrupt(ams_delta_kbd_port, original_scancode, 0, regs);
+			}
+		}
+	}
+	return IRQ_HANDLED;
+}
+
+
+static struct serio * __init ams_delta_kbd_allocate_serio(void)
+{
+	struct serio *serio;
+
+	serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
+	if (serio) {
+		memset(serio, 0, sizeof(struct serio));
+		serio->id.type = SERIO_8042;
+		strlcpy(serio->name, "AMS DELTA keyboard adapter", sizeof(serio->name));
+		snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", "GPIO");
+	}
+
+	return serio;
+}
+
+
+static int __init ams_delta_kbd_init(void)
+{
+	ams_delta_kbd_port = ams_delta_kbd_allocate_serio();
+	if (!ams_delta_kbd_port) {
+		return -ENOMEM;
+	}
+	
+	if (omap_request_gpio(AMS_DELTA_KEYBRD_DATA_GPIO_PIN)) {
+		printk(KERN_ERR "Couldn't request gpio pin for keyboard data");
+		kfree(ams_delta_kbd_port);
+		return -EINVAL;
+	}
+	omap_set_gpio_direction(AMS_DELTA_KEYBRD_DATA_GPIO_PIN,1);
+
+	if (omap_request_gpio(AMS_DELTA_KEYBRD_CLK_GPIO_PIN)) {
+		printk(KERN_ERR "Couldn't request gpio pin for keyboard clock");
+		kfree(ams_delta_kbd_port);
+		omap_free_gpio(AMS_DELTA_KEYBRD_DATA_GPIO_PIN);
+		return -EINVAL;
+	}
+	omap_set_gpio_direction(AMS_DELTA_KEYBRD_CLK_GPIO_PIN,1);
+
+	if (request_irq(OMAP_GPIO_IRQ(AMS_DELTA_KEYBRD_CLK_GPIO_PIN), ams_delta_kbd_interrupt, 0, "ams-delta-keyboard", 0) < 0) {
+		printk(KERN_ERR "Couldn't request gpio interrupt %d", OMAP_GPIO_IRQ(AMS_DELTA_KEYBRD_CLK_GPIO_PIN));
+		kfree(ams_delta_kbd_port);
+		omap_free_gpio(AMS_DELTA_KEYBRD_DATA_GPIO_PIN);
+		omap_free_gpio(AMS_DELTA_KEYBRD_CLK_GPIO_PIN);
+		return -EINVAL;
+	}
+	set_irq_type(OMAP_GPIO_IRQ(AMS_DELTA_KEYBRD_CLK_GPIO_PIN),IRQT_RISING);
+
+	// enable keyboard
+	ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR, AMD_DELTA_LATCH2_KEYBRD_PWR);
+
+	serio_register_port(ams_delta_kbd_port);
+
+	printk(KERN_INFO "serio: AMS DELTA keyboard adapter\n");
+
+	return 0;
+}
+
+static void __exit ams_delta_kbd_exit(void)
+{
+	// disable keyboard
+	ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR, 0);
+	free_irq(OMAP_GPIO_IRQ(AMS_DELTA_KEYBRD_CLK_GPIO_PIN), 0);
+	omap_free_gpio(AMS_DELTA_KEYBRD_DATA_GPIO_PIN);
+	omap_free_gpio(AMS_DELTA_KEYBRD_CLK_GPIO_PIN);
+	serio_unregister_port(ams_delta_kbd_port);
+}
+
+module_init(ams_delta_kbd_init);
+module_exit(ams_delta_kbd_exit);
Index: linux-2.6/drivers/input/serio/Makefile
===================================================================
--- linux-2.6/drivers/input/serio/Makefile	(revision 418)
+++ linux-2.6/drivers/input/serio/Makefile	(revision 419)
@@ -20,3 +20,4 @@
 obj-$(CONFIG_SERIO_MACEPS2)	+= maceps2.o
 obj-$(CONFIG_SERIO_LIBPS2)	+= libps2.o
 obj-$(CONFIG_SERIO_RAW)		+= serio_raw.o
+obj-$(CONFIG_SERIO_AMS_DELTA)	+= ams_delta_keyboard.o
