This series of patches adds support for an external keyboard (called mailboard) connected to the Amstrad Delta (E3) videophone.
The series is based on a patch by Matt Callow, created against linux-omap-2.6.19[1], initially submitted to the e3-hacking mailing list in April 2006[2].
Since the keyboard serial clock line is connected to a GPIO line, it generates interrupts every single bit received. In order to handle this correctly, the code makes use of a FIQ hardware feature. Since all GPIO generated interrupts would be converted to FIQ, the handler must include support for all supported GPIO connected devices: the keyboard, a modem and a hook switch.
My modifications to the original patch include: - split into several patches, - refresh against a recent linux version, - a lot of cleanups, mainly to get rid of checkpatch reported issues, - add support for handling interrupts generated by a GPIO line that the meanwhile activated hook switch hangs off. v2 changes: - remove scan code to key code mapping from the serio driver, that doesn't belong here and should be set up from userspace; thanks to Dmitry Torokhov for advising this, - no funtional changes in FIQ part (there were no comments in round 1).
There is still one minor issue that I was not able to deal with: when built with CONFIG_OMAP_RESET_CLOCKS=y, the keyboard driver stops working. I have no idea how I could find which clock I should enable to get it working regardless of boot loader used. Any suggestions?
Created and tested against linux-2.6.34-rc2.
Janusz Krzysztofik(5) omap1: Amstrad Delta: add FIQ handler for serial keyboard port interrupt processing omap1: Amstrad Delta: add a handler for processing interrupts generated by the FIQ routine omap1: Amstrad Delta: use FIQ for processing GPIO interrupts input: serio: add support for Amstrad Delta serial keyboard port omap1: Amstrad Delta: modify defconfig for external keyboard support
arch/arm/configs/ams_delta_defconfig | 2 arch/arm/mach-omap1/Kconfig | 8 arch/arm/mach-omap1/Makefile | 3 arch/arm/mach-omap1/ams-delta-fiq-handler.S | 342 +++++++++++++++++++++++ arch/arm/mach-omap1/ams-delta-fiq.c | 175 +++++++++++ arch/arm/mach-omap1/board-ams-delta.c | 6 arch/arm/mach-omap1/include/mach/ams-delta-fiq.h | 57 +++ arch/arm/plat-omap/include/plat/irqs.h | 4 drivers/input/serio/Kconfig | 9 drivers/input/serio/Makefile | 1 drivers/input/serio/ams_delta_keyboard.c | 171 +++++++++++ 11 files changed, 777 insertions(+), 1 deletion(-)
Thanks, Janusz
[1] http://the.earth.li/pub/e3/2.6.19/ams-delta-keyboard.patch [2] http://www.earth.li/pipermail/e3-hacking/2006-April/000453.html
This patch introduces a Fast Interrupt Request (FIQ) handler for Amstrad Delta (E3) videophone. The handler's purpose is to process interrupts generated by a GPIO line that a serial keyboard clock hangs off. It collects consecutive bits into bytes, pushing them into a buffer, then requests a higher level interrupt after one or more characters are ready for further processing by a keyboard port driver.
The handler also processes interrupts generated by two other GPIO lines, used by other on-board supported devices, by simply requesting a higher level interrupt, that in turn should invoke those device's specific irq handlers.
32k timer IRQ line, not used for any purpose by the on-board hardware, has been choosen as a higher level interrupt source.
Created and tested against linux-2.6.34-rc2.
Signed-off-by: Janusz Krzysztofik jkrzyszt@tis.icnet.pl --- v2 changes: - no functional changes, - refresh against linux-2.6.34-rc2.
arch/arm/mach-omap1/Kconfig | 8 arch/arm/mach-omap1/Makefile | 1 arch/arm/mach-omap1/ams-delta-fiq-handler.S | 342 ++++++++++++++++++++++++++++ arch/arm/plat-omap/include/plat/irqs.h | 4 4 files changed, 355 insertions(+)
diff -uprN git.orig/arch/arm/mach-omap1/Kconfig git/arch/arm/mach-omap1/Kconfig --- git.orig/arch/arm/mach-omap1/Kconfig 2010-03-25 15:52:17.000000000 +0100 +++ git/arch/arm/mach-omap1/Kconfig 2010-03-28 23:28:44.000000000 +0200 @@ -152,6 +152,14 @@ config MACH_AMS_DELTA 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 on 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) diff -uprN git.orig/arch/arm/mach-omap1/Makefile git/arch/arm/mach-omap1/Makefile --- git.orig/arch/arm/mach-omap1/Makefile 2010-03-25 15:52:17.000000000 +0100 +++ git/arch/arm/mach-omap1/Makefile 2010-03-28 23:28:44.000000000 +0200 @@ -37,6 +37,7 @@ obj-$(CONFIG_MACH_OMAP_PALMZ71) += boar obj-$(CONFIG_MACH_OMAP_PALMTT) += board-palmtt.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-handler.o obj-$(CONFIG_MACH_SX1) += board-sx1.o board-sx1-mmc.o obj-$(CONFIG_MACH_HERALD) += board-htcherald.o
diff -uprN git.orig/arch/arm/mach-omap1/ams-delta-fiq-handler.S git/arch/arm/mach-omap1/ams-delta-fiq-handler.S --- git.orig/arch/arm/mach-omap1/ams-delta-fiq-handler.S 1970-01-01 01:00:00.000000000 +0100 +++ git/arch/arm/mach-omap1/ams-delta-fiq-handler.S 2010-03-28 23:28:44.000000000 +0200 @@ -0,0 +1,342 @@ +/* + * 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 + * Copyright (C) 2009 Janusz Krzysztofik + * + * 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 <mach/hardware.h> +#include <plat/io.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/uSec => 200uSec */ + +/* MPU Timer Register Offsets */ +#define CNTL_TIMER 0 +#define LOAD_TIM 4 +#define READ_TIM 8 + +#define MPU_L1_INTERRUPT_BASE OMAP1_IO_ADDRESS(0xFFFECB00) +#define MPU_L2_INTERRUPT_BASE OMAP1_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 OMAP1_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 HKSW_MASK 0x10 +#define OTHERS_MASK 0x14 + +#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 + + @@@@@@@@@@@@@@@@@@@@@@@@@@@ + @setup base pointer = GPIO_BASE 0xFFFCE000 + ldr r12, gpio_base + +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 interrupt + + ldr r10, [r9, #FIQ_CNT_INT_KBD] @ get 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 expecting start bit? + bne data @ no - 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 kbd data in bit set? + bne exit @ no - exit, wait for next irq + mov r10, #1 @ good start bit, change 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 OTHERS TILL KEY DONE @@@@@@@@@@@@@@ + mov r10, #OTHERS_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 @ exit, 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 kbd 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 buff base + ldr r8, [r9, #FIQ_CHAR] @ get latest character + str r8, [r12] @ ####01 store 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 bigger - don't change + str r8, [r9, #FIQ_CHAR_HICNT] @ store char cnt in hi watermark +setstat: + ldr r10, [r9, #FIQ_CNT_INT_CHAR] @ get char cnt 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 + + + @@@@@@@@@@@@@@@@@@@@@@@@@@@ + @setup base pointer = GPIO_BASE 0xFFFCE000 + ldr r12, gpio_base + +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 hksw @ yes, next source + + 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 hksw @ no, so skip mdm - next source + + mov r10, #MDM_MASK @ its a mdm interrupt + str r10, [r12,#GPIO_INTERRUPT_STATUS] @ clear the modem interrupt + + ldr r10, [r9, #FIQ_CNT_INT_MDM] @ get modem interrupt count + 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 = GPIO_BASE 0xFFFCE000 + ldr r12, gpio_base + +hksw: @Is it a hook switch interrupt? + ldr r11, [r12,#GPIO_INTERRUPT_MASK] @ get GPIO interrupt mask + and r10, r11, #HKSW_MASK @ reveal hook switch bit + cmp r10, #HKSW_MASK @ is mask bit set? + beq exit @ yes, exit + + ldr r11, [r12,#GPIO_INTERRUPT_STATUS] @ get GPIO interrupts status + and r10, r11, #HKSW_MASK @ reveal hook switch bit + cmp r10, #HKSW_MASK @ is hook switch bit set? + bne exit @ no, so skip hksw - exit + + mov r10, #HKSW_MASK @ it's a hooksw interrupt + str r10, [r12,#GPIO_INTERRUPT_STATUS] @ clear hook switch interrupt + + ldr r10, [r9, #FIQ_CNT_INT_04] @ get hooksw inerrupt count + add r10, r10, #1 @ increment it + str r10, [r9, #FIQ_CNT_INT_04] @ 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 + + 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 diff -uprN git.orig/arch/arm/plat-omap/include/plat/irqs.h git/arch/arm/plat-omap/include/plat/irqs.h --- git.orig/arch/arm/plat-omap/include/plat/irqs.h 2010-03-25 15:52:38.000000000 +0100 +++ git/arch/arm/plat-omap/include/plat/irqs.h 2010-03-28 23:28:44.000000000 +0200 @@ -430,4 +430,8 @@ void omap3_intc_resume_idle(void);
#include <mach/hardware.h>
+#ifdef CONFIG_FIQ +#define FIQ_START 1024 +#endif + #endif
Hi Janusz,
My ARM's not used much these days so I don't know how much this matters and may be incorrect so I've cut down the CC list, but given it's a FIQ handler and speed matters...
+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
Couldn't that be an `ands' instruction if MBRD_CLK_MASK has one-bit set? Then the cmp could be deleted.
+state:
- ldr r10, [r9,#FIQ_STATE]
- cmp r10, #0 @ are we expecting start bit?
- bne data @ no - in data processing state
Similarly, an `ldrs' here would make the cmp redundant. There's other similar bits elsewhere.
Thanks for continuing with these E3 patches.
Cheers, Ralph.
Monday 29 March 2010 20:32:11 Ralph Corderoy wrote:
Hi Janusz,
My ARM's not used much these days so I don't know how much this matters and may be incorrect so I've cut down the CC list, but given it's a FIQ handler and speed matters...
Hi Ralph,
Not only speed; there is a very low size limit for FIQ handler and I already have had problems with fitting into the available space with the code, so your hints are really appreciated.
And thanks for pushing me into reading the ARM Assembly Language book, so I would be able to do more than just understanding and copy-pasting the code ;).
+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
Couldn't that be an `ands' instruction if MBRD_CLK_MASK has one-bit set? Then the cmp could be deleted.
Agree.
+state:
- ldr r10, [r9,#FIQ_STATE]
- cmp r10, #0 @ are we expecting start bit?
- bne data @ no - in data processing state
Similarly, an `ldrs' here would make the cmp redundant.
I'm not sure if the S suffix is also valid for memory transfer instructions, so I readd linux-omap and linux-arm-kernel to the CC list.
There's other similar bits elsewhere.
Yes, I'll try to recognize and optimize them all for next version.
Thanks, Janusz
Hi Janusz,
And thanks for pushing me into reading the ARM Assembly Language book, so I would be able to do more than just understanding and copy-pasting the code ;).
It's a lovely instruction set compared to many others.
+state:
- ldr r10, [r9,#FIQ_STATE]
- cmp r10, #0 @ are we expecting start bit?
- bne data @ no - in data processing state
Similarly, an `ldrs' here would make the cmp redundant.
I'm not sure if the S suffix is also valid for memory transfer instructions
No, you're correct. Memory transfers can't do the "set ALU flags" S-bit. Sorry to mislead.
Cheers, Ralph.
This patch introduces an IRQ handler used for processing interrupts generated by the FIQ handler when it decides there are data ready for processing.
The handler further invokes device specific interrupt routines based on an interrupt source as passed from the FIQ handler.
It can be registered by the board as a handler for the otherwise unused 32k timer interrupt.
Applies on top of patch 1/5 from this series: omap1: Amstrad Delta: add FIQ handler for serial keyboard port interrupt processing
Created and tested against linux-2.6.34-rc2.
Signed-off-by: Janusz Krzysztofik jkrzyszt@tis.icnet.pl --- v2 changes: - add fiq_buffer[] declaration missing from the header file, - refresh against 2.6.34-rc2.
arch/arm/mach-omap1/Makefile | 2 arch/arm/mach-omap1/ams-delta-fiq.c | 175 +++++++++++++++++++++++ arch/arm/mach-omap1/include/mach/ams-delta-fiq.h | 57 +++++++ 3 files changed, 233 insertions(+), 1 deletion(-)
diff -uprN git.orig/arch/arm/mach-omap1/Makefile git/arch/arm/mach-omap1/Makefile --- git.orig/arch/arm/mach-omap1/Makefile 2010-03-28 23:35:25.000000000 +0200 +++ git/arch/arm/mach-omap1/Makefile 2010-03-28 23:34:38.000000000 +0200 @@ -37,7 +37,7 @@ obj-$(CONFIG_MACH_OMAP_PALMZ71) += boar obj-$(CONFIG_MACH_OMAP_PALMTT) += board-palmtt.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-handler.o +obj-$(CONFIG_AMS_DELTA_FIQ) += ams-delta-fiq.o ams-delta-fiq-handler.o obj-$(CONFIG_MACH_SX1) += board-sx1.o board-sx1-mmc.o obj-$(CONFIG_MACH_HERALD) += board-htcherald.o
diff -uprN git.orig/arch/arm/mach-omap1/ams-delta-fiq.c git/arch/arm/mach-omap1/ams-delta-fiq.c --- git.orig/arch/arm/mach-omap1/ams-delta-fiq.c 1970-01-01 01:00:00.000000000 +0100 +++ git/arch/arm/mach-omap1/ams-delta-fiq.c 2010-03-28 23:34:38.000000000 +0200 @@ -0,0 +1,175 @@ +/* + * Amstrad E3 FIQ handling + * + * Copyright (C) 2009 Janusz Krzysztofik + * 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 <linux/gpio.h> +#include <asm/fiq.h> +#include <mach/ams-delta-fiq.h> + +#include <mach/hardware.h> +#include <asm/mach-types.h> +#include <asm/mach/arch.h> +#include <asm/mach/map.h> +#include <asm/mach/irq.h> + +#include <plat/board-ams-delta.h> +#include <plat/mux.h> +#include <plat/board.h> +#include <plat/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]; +EXPORT_SYMBOL(fiq_buffer); + +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, 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_04; + 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) { + irq_desc[irq_num].action->handler(irq_num, + irq_desc[irq_num].action->dev_id); + /* keep /proc/interrupts up to date */ + kstat_cpu(cpu).softirqs[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; + 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 | ((IRQ_TYPE_EDGE_RISING & 0x1) << 1); + offset = IRQ_ILR0_REG_OFFSET + INT_GPIO_BANK1 * 0x4; + omap_writel(val, OMAP_IH1_BASE + offset); +} diff -uprN git.orig/arch/arm/mach-omap1/include/mach/ams-delta-fiq.h git/arch/arm/mach-omap1/include/mach/ams-delta-fiq.h --- git.orig/arch/arm/mach-omap1/include/mach/ams-delta-fiq.h 1970-01-01 01:00:00.000000000 +0100 +++ git/arch/arm/mach-omap1/include/mach/ams-delta-fiq.h 2010-03-28 23:34:38.000000000 +0200 @@ -0,0 +1,57 @@ +/* + * arch/arm/mach-omap1/include/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 unsigned int fiq_buffer[]; +extern unsigned char qwerty_fiqin_start, qwerty_fiqin_end; + +extern void __init ams_delta_init_fiq(void);
The patch adds initialization of FIQ related handlers to the Amstrad Delta videophone board code. FIQ will be used instead of a traditional IRQ for processing all GPIO generated interrupts, including a keyboard serial clock line.
Amstrad Delta defconfig is modified to use FIQ by default.
Compiles and works on top of patch 2/5 from this series: omap1: Amstrad Delta: add a handler for processing interrupts generated by the FIQ routine
Created and tested against linux-2.6.34-rc2.
Signed-off-by: Janusz Krzysztofik jkrzyszt@tis.icnet.pl --- v2 changes: - no functional changes, - refreshed against linux-2.6.34-rc2.
arch/arm/configs/ams_delta_defconfig | 1 + arch/arm/mach-omap1/board-ams-delta.c | 6 ++++++ 2 files changed, 7 insertions(+)
diff -uprN git.orig/arch/arm/configs/ams_delta_defconfig git/arch/arm/configs/ams_delta_defconfig --- git.orig/arch/arm/configs/ams_delta_defconfig 2010-03-25 15:51:51.000000000 +0100 +++ git/arch/arm/configs/ams_delta_defconfig 2010-03-28 23:38:54.000000000 +0200 @@ -195,6 +195,7 @@ CONFIG_ARCH_OMAP15XX=y # CONFIG_MACH_OMAP_PALMTT is not set # CONFIG_MACH_SX1 is not set CONFIG_MACH_AMS_DELTA=y +CONFIG_AMS_DELTA_FIQ=y # CONFIG_MACH_OMAP_GENERIC is not set
# diff -uprN git.orig/arch/arm/mach-omap1/board-ams-delta.c git/arch/arm/mach-omap1/board-ams-delta.c --- git.orig/arch/arm/mach-omap1/board-ams-delta.c 2010-03-25 15:52:17.000000000 +0100 +++ git/arch/arm/mach-omap1/board-ams-delta.c 2010-03-28 23:38:54.000000000 +0200 @@ -33,6 +33,8 @@ #include <plat/board.h> #include <plat/common.h>
+#include <mach/ams-delta-fiq.h> + static u8 ams_delta_latch1_reg; static u16 ams_delta_latch2_reg;
@@ -236,6 +238,10 @@ static void __init ams_delta_init(void) omap_usb_init(&ams_delta_usb_config); platform_add_devices(ams_delta_devices, ARRAY_SIZE(ams_delta_devices));
+#ifdef CONFIG_AMS_DELTA_FIQ + ams_delta_init_fiq(); +#endif + omap_writew(omap_readw(ARM_RSTCT1) | 0x0004, ARM_RSTCT1); }
The patch introduces a serio driver that supports a keyboard serial port found on the Amstrad Delta videophone board.
After initializing the hardware, the driver reads its input data from a buffer filled in by the board FIQ (Fast Interrupt Request) handler.
Standard AT keyboard driver (atkbd) will be used on top of the serio layer for handling the E3 keyboard (called mailboard) connected to the port. Since the device generated scancodes differ from what the atkbd expects, a custom key code to scan code table must be loaded from userspace for the keyboard to be useable.
Compiles and works on to of patch 3/5: omap1: Amstrad Delta: use FIQ for processing GPIO interrupts
Created and tested against linux-2.6.34-rc2.
Signed-off-by: Janusz Krzysztofik jkrzyszt@tis.icnet.pl --- v2 changes: - use correct variable name for return value in ams_delta_kbd_init(), - remove scan code to key code mapping, - refreshed against linux-2.6.34-rc2.
drivers/input/serio/Kconfig | 9 + drivers/input/serio/Makefile | 1 drivers/input/serio/ams_delta_keyboard.c | 171 +++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+)
diff -uprN git.orig/drivers/input/serio/Kconfig git/drivers/input/serio/Kconfig --- git.orig/drivers/input/serio/Kconfig 2010-03-25 15:55:42.000000000 +0100 +++ git/drivers/input/serio/Kconfig 2010-03-28 23:42:09.000000000 +0200 @@ -209,4 +209,13 @@ config SERIO_ALTERA_PS2 To compile this driver as a module, choose M here: the module will be called altera_ps2.
+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 diff -uprN git.orig/drivers/input/serio/Makefile git/drivers/input/serio/Makefile --- git.orig/drivers/input/serio/Makefile 2010-03-25 15:55:42.000000000 +0100 +++ git/drivers/input/serio/Makefile 2010-03-28 23:42:09.000000000 +0200 @@ -21,5 +21,6 @@ obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o 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 obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o diff -uprN git.orig/drivers/input/serio/ams_delta_keyboard.c git/drivers/input/serio/ams_delta_keyboard.c --- git.orig/drivers/input/serio/ams_delta_keyboard.c 1970-01-01 01:00:00.000000000 +0100 +++ git/drivers/input/serio/ams_delta_keyboard.c 2010-03-28 23:42:09.000000000 +0200 @@ -0,0 +1,171 @@ +/* + * 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 Delta 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/gpio.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/serio.h> +#include <asm/fiq.h> +#include <plat/board-ams-delta.h> +#include <mach/ams-delta-fiq.h> + +MODULE_AUTHOR("Matt Callow"); +MODULE_DESCRIPTION("AMS Delta (E3) Keyboard driver"); +MODULE_LICENSE("GPL"); + +#define MAX_SCANCODE 0x84 + +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 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)) { + scancode = (u8) (data >> 1) & 0xFF; + serio_interrupt(ams_delta_kbd_port, scancode, 0); + } + } + 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) +{ + int err; + + ams_delta_kbd_port = ams_delta_kbd_allocate_serio(); + if (!ams_delta_kbd_port) { + err = -ENOMEM; + goto err; + } + + if (gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_DATA, "kbd-data")) { + printk(KERN_ERR "Couldn't request gpio pin for keyboard data"); + err = -EINVAL; + goto serio; + } + gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_DATA); + + if (gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_CLK, "kbd-clock")) { + printk(KERN_ERR "Couldn't request gpio pin for keyboard clock"); + err = -EINVAL; + goto gpio_data; + } + gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_CLK); + + if (request_irq(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), + ams_delta_kbd_interrupt, 0, "ams-delta-keyboard", 0) < 0) { + printk(KERN_ERR "Couldn't request gpio interrupt %d", + OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK)); + err = -EINVAL; + goto gpio_clk; + } + set_irq_type(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), + IRQ_TYPE_EDGE_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; +gpio_clk: + gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK); +gpio_data: + gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA); +serio: + kfree(ams_delta_kbd_port); +err: + return err; +} +module_init(ams_delta_kbd_init); + +static void __exit ams_delta_kbd_exit(void) +{ + serio_unregister_port(ams_delta_kbd_port); + /* disable keyboard */ + ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR, 0); + free_irq(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0); + gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK); + gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA); + kfree(ams_delta_kbd_port); +} +module_exit(ams_delta_kbd_exit);
On Mon, Mar 29, 2010 at 04:30:41PM +0200, Janusz Krzysztofik wrote:
The patch introduces a serio driver that supports a keyboard serial port found on the Amstrad Delta videophone board.
After initializing the hardware, the driver reads its input data from a buffer filled in by the board FIQ (Fast Interrupt Request) handler.
Standard AT keyboard driver (atkbd) will be used on top of the serio layer for handling the E3 keyboard (called mailboard) connected to the port. Since the device generated scancodes differ from what the atkbd expects, a custom key code to scan code table must be loaded from userspace for the keyboard to be useable.
Compiles and works on to of patch 3/5: omap1: Amstrad Delta: use FIQ for processing GPIO interrupts
Created and tested against linux-2.6.34-rc2.
Signed-off-by: Janusz Krzysztofik jkrzyszt@tis.icnet.pl
v2 changes:
- use correct variable name for return value in ams_delta_kbd_init(),
- remove scan code to key code mapping,
- refreshed against linux-2.6.34-rc2.
drivers/input/serio/Kconfig | 9 + drivers/input/serio/Makefile | 1 drivers/input/serio/ams_delta_keyboard.c | 171 +++++++++++++++++++++++++++++++ 3 files changed, 181 insertions(+)
diff -uprN git.orig/drivers/input/serio/Kconfig git/drivers/input/serio/Kconfig --- git.orig/drivers/input/serio/Kconfig 2010-03-25 15:55:42.000000000 +0100 +++ git/drivers/input/serio/Kconfig 2010-03-28 23:42:09.000000000 +0200 @@ -209,4 +209,13 @@ config SERIO_ALTERA_PS2 To compile this driver as a module, choose M here: the module will be called altera_ps2.
+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 diff -uprN git.orig/drivers/input/serio/Makefile git/drivers/input/serio/Makefile --- git.orig/drivers/input/serio/Makefile 2010-03-25 15:55:42.000000000 +0100 +++ git/drivers/input/serio/Makefile 2010-03-28 23:42:09.000000000 +0200 @@ -21,5 +21,6 @@ obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o 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 obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o diff -uprN git.orig/drivers/input/serio/ams_delta_keyboard.c git/drivers/input/serio/ams_delta_keyboard.c --- git.orig/drivers/input/serio/ams_delta_keyboard.c 1970-01-01 01:00:00.000000000 +0100 +++ git/drivers/input/serio/ams_delta_keyboard.c 2010-03-28 23:42:09.000000000 +0200 @@ -0,0 +1,171 @@ +/*
- 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 Delta 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/gpio.h> +#include <linux/init.h> +#include <linux/irq.h> +#include <linux/module.h> +#include <linux/serio.h> +#include <asm/fiq.h> +#include <plat/board-ams-delta.h> +#include <mach/ams-delta-fiq.h>
+MODULE_AUTHOR("Matt Callow"); +MODULE_DESCRIPTION("AMS Delta (E3) Keyboard driver"); +MODULE_LICENSE("GPL");
+#define MAX_SCANCODE 0x84
Not needed anymore?
+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);
Consider switching top dev_err(), dev_warning(), etc. Also do not split text strings, even if you run over 80 column limit. BTW, why "\r\n" in the message?
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 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)) {
scancode = (u8) (data >> 1) & 0xFF;
serio_interrupt(ams_delta_kbd_port, scancode, 0);
}
- }
- return IRQ_HANDLED;
+}
+static struct serio * __init ams_delta_kbd_allocate_serio(void) +{
- struct serio *serio;
- serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
kzalloc() please.
- 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");
strlcpy(serio->phys, "GPIO/serio0", sizeof(serio->phys)); ?
- }
- return serio;
+}
+static int __init ams_delta_kbd_init(void) +{
- int err;
- ams_delta_kbd_port = ams_delta_kbd_allocate_serio();
- if (!ams_delta_kbd_port) {
err = -ENOMEM;
goto err;
- }
- if (gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_DATA, "kbd-data")) {
printk(KERN_ERR "Couldn't request gpio pin for keyboard data");
err = -EINVAL;
Why do you mangle return value of gpio_request()? Just return what it reported. Same goes for request_irq() below.
goto serio;
- }
- gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_DATA);
- if (gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_CLK, "kbd-clock")) {
printk(KERN_ERR "Couldn't request gpio pin for keyboard clock");
err = -EINVAL;
goto gpio_data;
- }
- gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_CLK);
- if (request_irq(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK),
ams_delta_kbd_interrupt, 0, "ams-delta-keyboard", 0) < 0) {
printk(KERN_ERR "Couldn't request gpio interrupt %d",
OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK));
err = -EINVAL;
goto gpio_clk;
- }
- set_irq_type(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK),
IRQ_TYPE_EDGE_RISING);
- /* enable keyboard */
- ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR,
AMD_DELTA_LATCH2_KEYBRD_PWR);
This shoukd probably go into open() method of the serio port.
- serio_register_port(ams_delta_kbd_port);
- printk(KERN_INFO "serio: AMS DELTA keyboard adapter\n");
- return 0;
+gpio_clk:
- gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK);
+gpio_data:
- gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA);
+serio:
- kfree(ams_delta_kbd_port);
+err:
- return err;
+} +module_init(ams_delta_kbd_init);
+static void __exit ams_delta_kbd_exit(void) +{
- serio_unregister_port(ams_delta_kbd_port);
- /* disable keyboard */
- ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR, 0);
And this into close().
- free_irq(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0);
- gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK);
- gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA);
- kfree(ams_delta_kbd_port);
+} +module_exit(ams_delta_kbd_exit);
Thanks.
Tuesday 30 March 2010 08:56:19 Dmitry Torokhov wrote:
On Mon, Mar 29, 2010 at 04:30:41PM +0200, Janusz Krzysztofik wrote:
The patch introduces a serio driver that supports a keyboard serial port found on the Amstrad Delta videophone board.
[snip]
+#define MAX_SCANCODE 0x84
Not needed anymore?
[snip]
printk(KERN_WARNING
"Invalid stop bit in AMS keyboard"
" data=0x%X\r\n", data);
Consider switching top dev_err(), dev_warning(), etc. Also do not split text strings, even if you run over 80 column limit. BTW, why "\r\n" in the message?
[snip]
- serio = kmalloc(sizeof(struct serio), GFP_KERNEL);
kzalloc() please.
[snip]
snprintf(serio->phys, sizeof(serio->phys), "%s/serio0", "GPIO");
strlcpy(serio->phys, "GPIO/serio0", sizeof(serio->phys)); ?
[snip]
- if (gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_DATA, "kbd-data")) {
printk(KERN_ERR "Couldn't request gpio pin for keyboard data");
err = -EINVAL;
Why do you mangle return value of gpio_request()? Just return what it reported. Same goes for request_irq() below.
[snip]
- /* enable keyboard */
- ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR,
AMD_DELTA_LATCH2_KEYBRD_PWR);
This shoukd probably go into open() method of the serio port.
[snip]
- /* disable keyboard */
- ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR, 0);
And this into close().
Hi Dmitry, Thanks for your review time. I agree with all your comments and will address them in next version.
Regards, Janusz
The patch adds a CONFIG directive required for supporting the Amstrad Delta connected external keyboard by default.
Works on top of patch 4/5: input: serio: add support for Amstrad Delta serial keyboard port
Created and tested against linux-2.6.34-rc2.
Signed-off-by: Janusz Krzysztofik jkrzyszt@tis.icnet.pl --- v2 changes: - no functional changes, - refreshed against linux-2.6.34-rc2.
arch/arm/configs/ams_delta_defconfig | 1 + 1 file changed, 1 insertion(+)
diff -uprN git.orig/arch/arm/configs/ams_delta_defconfig git/arch/arm/configs/ams_delta_defconfig --- git.orig/arch/arm/configs/ams_delta_defconfig 2010-03-28 23:43:07.000000000 +0200 +++ git/arch/arm/configs/ams_delta_defconfig 2010-03-28 23:46:29.000000000 +0200 @@ -700,6 +700,7 @@ CONFIG_SERIO=y CONFIG_SERIO_SERPORT=y CONFIG_SERIO_LIBPS2=y # CONFIG_SERIO_RAW is not set +CONFIG_SERIO_AMS_DELTA=y # CONFIG_GAMEPORT is not set
#
Monday 29 March 2010 16:19:23 Janusz Krzysztofik napisał(a):
This series of patches adds support for an external keyboard (called mailboard) connected to the Amstrad Delta (E3) videophone.
The series is based on a patch by Matt Callow, created against linux-omap-2.6.19[1], initially submitted to the e3-hacking mailing list in April 2006[2].
Since the keyboard serial clock line is connected to a GPIO line, it generates interrupts every single bit received. In order to handle this correctly, the code makes use of a FIQ hardware feature. Since all GPIO generated interrupts would be converted to FIQ, the handler must include support for all supported GPIO connected devices: the keyboard, a modem and a hook switch.
My modifications to the original patch include:
- split into several patches,
- refresh against a recent linux version,
- a lot of cleanups, mainly to get rid of checkpatch reported issues,
- add support for handling interrupts generated by a GPIO line that the meanwhile activated hook switch hangs off.
v2 changes:
- remove scan code to key code mapping from the serio driver, that doesn't belong here and should be set up from userspace;
Hi,
For those of you who would like to do some testing:
You'll need a utility for installing a key table. Given no single hint from linux-input team, I ended up using input-kbd, that is part of input-utils package, at least on Gentoo and Debian. Other possibilities I know of: - V4L guys provided linux/Documentation/DocBook/v4l/keytable.c.xml example, - http://0pointer.de/lennart/projects/keyfuzz/
My input-kbd compatible /etc/mailboard.map for my Delta looks like this: ------------------------- 112 = KEY_ESC 122 = KEY_F1 70 = KEY_F2 124 = KEY_F3 119 = KEY_F4 114 = KEY_F5 105 = KEY_F6 26 = KEY_F7 42 = KEY_F8 28 = KEY_F9 21 = KEY_F10 113 = KEY_TAB 116 = KEY_1 115 = KEY_2 107 = KEY_3 34 = KEY_4 27 = KEY_5 29 = KEY_6 30 = KEY_7 121 = KEY_8 125 = KEY_9 117 = KEY_0 108 = KEY_BACKSPACE 33 = KEY_Q 35 = KEY_W 36 = KEY_E 38 = KEY_R 82 = KEY_T 93 = KEY_Y 13 = KEY_U 14 = KEY_I 50 = KEY_O 52 = KEY_P 44 = KEY_ENTER 49 = KEY_A 51 = KEY_S 53 = KEY_D 54 = KEY_F 41 = KEY_G 91 = KEY_H 3 = KEY_J 118 = KEY_K 58 = KEY_L 59 = KEY_BACKSLASH # there is no KEY_AT 60 = KEY_LEFTSHIFT 61 = KEY_Z 78 = KEY_X 84 = KEY_C 11 = KEY_V 5 = KEY_B 65 = KEY_N 66 = KEY_M 67 = KEY_DOT 62 = KEY_UP 85 = KEY_RIGHTSHIFT 259 = KEY_LEFTCTRL 6 = KEY_LEFTALT 73 = KEY_RIGHTMETA 75 = KEY_SPACE 68 = KEY_COMMA 22 = KEY_LEFT 46 = KEY_DOWN 9 = KEY_RIGHT ------------------------- You may want to adopt it to better suit your taste.
My udev installs it up automatically for me with this simple rule: ------------------------- ACTION=="add", KERNEL=="event[0-9]*", \ ATTRS{phys}=="GPIO/serio[0-9]*/input[0-9]*", \ RUN+="/usr/bin/input-kbd -f /etc/mailboard.map %n" -------------------------
If you are using Angstrom (or other OpenEmbedded generated distribution), you may want to give my simple recipes/input-utils/input-utils.bb recipe a try: ------------------------- PV = "20081014-${SRCREV}" SRCREV = "101501" SRC_URI = "http://dl.bytesex.org/cvs-snapshots/input-$%7BPV%7D.tar.gz \ file://fix-makefiles.patch;patch=1" LICENSE = "GPL" inherit autotools S = "${WORKDIR}/input" PACKAGES_prepend = "input-kbd input-remote " FILES_input-kbd = "/usr/bin/input-kbd" FILES_input-remote = "/usr/bin/input-send /usr/bin/input-recv" --------------------------
and recipes/input-utils/files/fix-makefiles.patch: -------------------------- --- input/mk/Variables.mk.orig 2008-10-14 12:27:01.000000000 +0200 +++ input/mk/Variables.mk 2010-03-29 17:11:58.000000000 +0200 @@ -30,7 +30,7 @@ MOC ?= $(if $(QTDIR),$(QTDIR
STRIP ?= -s INSTALL ?= install -INSTALL_BINARY := $(INSTALL) $(STRIP) +INSTALL_BINARY := $(INSTALL) INSTALL_SCRIPT := $(INSTALL) INSTALL_DATA := $(INSTALL) -m 644 INSTALL_DIR := $(INSTALL) -d --------------------------
After putting the above two files in your local OE overlay, building the package(s) with "bitbake input-utils" and refreshing your repository Packeges list, all you need is running "opkg install input-kbd" on your Delta.
Have fun, Janusz
thanks to Dmitry Torokhov for advising this,
- no funtional changes in FIQ part (there were no comments in round 1).
There is still one minor issue that I was not able to deal with: when built with CONFIG_OMAP_RESET_CLOCKS=y, the keyboard driver stops working. I have no idea how I could find which clock I should enable to get it working regardless of boot loader used. Any suggestions?
Created and tested against linux-2.6.34-rc2.
Janusz Krzysztofik(5) omap1: Amstrad Delta: add FIQ handler for serial keyboard port interrupt processing omap1: Amstrad Delta: add a handler for processing interrupts generated by the FIQ routine omap1: Amstrad Delta: use FIQ for processing GPIO interrupts input: serio: add support for Amstrad Delta serial keyboard port omap1: Amstrad Delta: modify defconfig for external keyboard support
arch/arm/configs/ams_delta_defconfig | 2 arch/arm/mach-omap1/Kconfig | 8 arch/arm/mach-omap1/Makefile | 3 arch/arm/mach-omap1/ams-delta-fiq-handler.S | 342 +++++++++++++++++++++++ arch/arm/mach-omap1/ams-delta-fiq.c | 175 +++++++++++ arch/arm/mach-omap1/board-ams-delta.c | 6 arch/arm/mach-omap1/include/mach/ams-delta-fiq.h | 57 +++ arch/arm/plat-omap/include/plat/irqs.h | 4 drivers/input/serio/Kconfig | 9 drivers/input/serio/Makefile | 1 drivers/input/serio/ams_delta_keyboard.c | 171 +++++++++++ 11 files changed, 777 insertions(+), 1 deletion(-)
Thanks, Janusz
[1] http://the.earth.li/pub/e3/2.6.19/ams-delta-keyboard.patch [2] http://www.earth.li/pipermail/e3-hacking/2006-April/000453.html
On Mon, Mar 29, 2010 at 09:58:02PM +0200, Janusz Krzysztofik wrote:
Monday 29 March 2010 16:19:23 Janusz Krzysztofik napisał(a):
This series of patches adds support for an external keyboard (called mailboard) connected to the Amstrad Delta (E3) videophone.
The series is based on a patch by Matt Callow, created against linux-omap-2.6.19[1], initially submitted to the e3-hacking mailing list in April 2006[2].
Since the keyboard serial clock line is connected to a GPIO line, it generates interrupts every single bit received. In order to handle this correctly, the code makes use of a FIQ hardware feature. Since all GPIO generated interrupts would be converted to FIQ, the handler must include support for all supported GPIO connected devices: the keyboard, a modem and a hook switch.
My modifications to the original patch include:
- split into several patches,
- refresh against a recent linux version,
- a lot of cleanups, mainly to get rid of checkpatch reported issues,
- add support for handling interrupts generated by a GPIO line that the meanwhile activated hook switch hangs off.
v2 changes:
- remove scan code to key code mapping from the serio driver, that doesn't belong here and should be set up from userspace;
Hi,
For those of you who would like to do some testing:
You'll need a utility for installing a key table. Given no single hint from linux-input team,
Sorry about that.
I ended up using input-kbd, that is part of input-utils package, at least on Gentoo and Debian. Other possibilities I know of:
- V4L guys provided linux/Documentation/DocBook/v4l/keytable.c.xml example,
- http://0pointer.de/lennart/projects/keyfuzz/
HAL used to be responsible for loading custom keymaps on modern distros but I believe now that task is moved into udev (see extras/keymap).