Subject: [E3-hacking] mailboard patch for 2.6.16

From: Matt Callow <[email-masked]>
Date: Mon, 10 Apr 2006 22:25:50 +0100

Hi all,

attached is my patch to enable the external keyboard (mailboard)
It should be applied to the 2.6.16 kernel tree after all the other
patches on noodles site.

Matt
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

---

From: Jonathan McDowell <[email-masked]>
Date: Tue, 11 Apr 2006 13:34:42 +0100

On Mon, Apr 10, 2006 at 10:25:50PM +0100, Matt Callow wrote:
> attached is my patch to enable the external keyboard (mailboard) It
> should be applied to the 2.6.16 kernel tree after all the other
> patches on noodles site.

Nice work. I've put the patch up in the same place as the others and
added it to my prebuilt kernel. Oh, and I confirmed the modem still
seems to work ok (seeing as it's on a GPIO IRQ as well).

http://the.earth.li/pub/e3/2.6.16/

has the current set of patches.

J.

-- 
noodles is a slightly smaller version of cup noodles

---

From: David Given <[email-masked]>
Date: Wed, 12 Apr 2006 00:44:46 +0100

Jonathan McDowell wrote:
[...]
> seems to work ok (seeing as it's on a GPIO IRQ as well).

Trying this, I find that the events don't seem to be processed through the
console. Do I need to do anything to enable this? I have SERIO_AMS_DELTA
switched on, but there doesn't seem to be an entry in Input Devices->Keyboards.

-- 
+- David Given --McQ-+ "If you're up against someone more intelligent
|  [email-masked]    | than you are, do something insane and let him think
| ([email-masked]) | himself to death." --- Pyanfar Chanur
+- www.cowlark.com --+

---

From: Matt Callow <[email-masked]>
Date: Wed, 12 Apr 2006 01:00:56 +0100

>switched on, but there doesn't seem to be an entry in Input Devices->Keyboards.
It used the standard AT keyboard driver.
You will need CONFIG_INPUT_KEYBOARD, CONFIG_KEYBOARD_ATKBD, CONFIG_FIQ
and CONFIG_SERIO_AMS_DELTA in your .config (there are all in 
ams_delta_defconfig)

Matt

---

From: David Given <[email-masked]>
Date: Wed, 12 Apr 2006 12:27:00 +0100

Matt Callow wrote:
[...]
> and CONFIG_SERIO_AMS_DELTA in your .config (there are all in 

Huh --- I did look at the defconfig file, but it never occurred to me to
look at that bit (after all, the mailboard is most decidedly *not* a PC
AT keyboard)...

It might be useful to stick in a couple of sentences in the
CONFIG_SERIO_AMS_DELTA help text saying that you also need AT keyboard
support, to avoid this kind of confusion later.

Anyway, I'm looking forward to trying it --- the 6x11 console font is
startlingly clear and easy to read on that little screen, and I suspect
that with the keyboard it'll make a rather nice network terminal.

- --
+- David Given --McQ-+
|  [email-masked]    | "Those that repeat truisms, are also forced to
| ([email-masked]) | repeat them." --- Anonymous from Slashdot
+- www.cowlark.com --+

---

From: Jasmine Strong <[email-masked]>
Date: Wed, 12 Apr 2006 13:31:01 +0200

> look at that bit (after all, the mailboard is most decidedly *not*  

Actually, it is-  it's a PS/2 device.  It just happens to be  
connected to
some IO lines, rather than a FIFO, like it ought to be.

-J.