OMAP1: Amstrad Delta: add FIQ handler for serial keyboardport interrupt processing
authorJanusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Wed, 28 Apr 2010 01:01:29 +0000 (01:01 +0000)
committerTony Lindgren <tony@atomide.com>
Wed, 5 May 2010 18:11:10 +0000 (11:11 -0700)
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 words, pushing them into a buffer, then requests a higher level interrupt
after one or more words 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.

IRQ12 line, not used by OMAP1510 hardware (described as reserved), has been
choosen as a higher level interrupt source.

Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Signed-off-by: Tony Lindgren <tony@atomide.com>
arch/arm/mach-omap1/Kconfig
arch/arm/mach-omap1/Makefile
arch/arm/mach-omap1/ams-delta-fiq-handler.S [new file with mode: 0644]
arch/arm/mach-omap1/include/mach/ams-delta-fiq.h [new file with mode: 0644]

index 27f4897..b18d7c2 100644 (file)
@@ -152,6 +152,16 @@ 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 allows for fast handling of interrupts generated
+         by the clock line of the E3 mailboard (or a PS/2 keyboard)
+         connected to the GPIO based external keyboard port.
+
 config MACH_OMAP_GENERIC
        bool "Generic OMAP board"
        depends on ARCH_OMAP1 && (ARCH_OMAP15XX || ARCH_OMAP16XX)
index b6a537c..db0d480 100644 (file)
@@ -37,6 +37,7 @@ obj-$(CONFIG_MACH_OMAP_PALMZ71)               += board-palmz71.o
 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 --git a/arch/arm/mach-omap1/ams-delta-fiq-handler.S b/arch/arm/mach-omap1/ams-delta-fiq-handler.S
new file mode 100644 (file)
index 0000000..927d5a1
--- /dev/null
@@ -0,0 +1,278 @@
+/*
+ *  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) 2010 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 <plat/io.h>
+#include <plat/board-ams-delta.h>
+
+#include <mach/ams-delta-fiq.h>
+
+/*
+ * GPIO related definitions, copied from arch/arm/plat-omap/gpio.c.
+ * Unfortunately, those were not placed in a separate header file.
+ */
+#define OMAP1510_GPIO_BASE             0xFFFCE000
+#define OMAP1510_GPIO_DATA_INPUT       0x00
+#define OMAP1510_GPIO_DATA_OUTPUT      0x04
+#define OMAP1510_GPIO_DIR_CONTROL      0x08
+#define OMAP1510_GPIO_INT_CONTROL      0x0c
+#define OMAP1510_GPIO_INT_MASK         0x10
+#define OMAP1510_GPIO_INT_STATUS       0x14
+#define OMAP1510_GPIO_PIN_CONTROL      0x18
+
+/* GPIO register bitmasks */
+#define KEYBRD_DATA_MASK               (0x1 << AMS_DELTA_GPIO_PIN_KEYBRD_DATA)
+#define KEYBRD_CLK_MASK                        (0x1 << AMS_DELTA_GPIO_PIN_KEYBRD_CLK)
+#define MODEM_IRQ_MASK                 (0x1 << AMS_DELTA_GPIO_PIN_MODEM_IRQ)
+#define HOOK_SWITCH_MASK               (0x1 << AMS_DELTA_GPIO_PIN_HOOK_SWITCH)
+#define OTHERS_MASK                    (MODEM_IRQ_MASK | HOOK_SWITCH_MASK)
+
+/* IRQ handler register bitmasks */
+#define DEFERRED_FIQ_MASK              (0x1 << (INT_DEFERRED_FIQ % IH2_BASE))
+#define GPIO_BANK1_MASK                (0x1 << INT_GPIO_BANK1)
+
+/* Driver buffer byte offsets */
+#define BUF_MASK                       (FIQ_MASK * 4)
+#define BUF_STATE                      (FIQ_STATE * 4)
+#define BUF_KEYS_CNT                   (FIQ_KEYS_CNT * 4)
+#define BUF_TAIL_OFFSET                        (FIQ_TAIL_OFFSET * 4)
+#define BUF_HEAD_OFFSET                        (FIQ_HEAD_OFFSET * 4)
+#define BUF_BUF_LEN                    (FIQ_BUF_LEN * 4)
+#define BUF_KEY                                (FIQ_KEY * 4)
+#define BUF_MISSED_KEYS                        (FIQ_MISSED_KEYS * 4)
+#define BUF_BUFFER_START               (FIQ_BUFFER_START * 4)
+#define BUF_GPIO_INT_MASK              (FIQ_GPIO_INT_MASK * 4)
+#define BUF_KEYS_HICNT                 (FIQ_KEYS_HICNT * 4)
+#define BUF_IRQ_PEND                   (FIQ_IRQ_PEND * 4)
+#define BUF_SIR_CODE_L1                        (FIQ_SIR_CODE_L1 * 4)
+#define BUF_SIR_CODE_L2                        (IRQ_SIR_CODE_L2 * 4)
+#define BUF_CNT_INT_00                 (FIQ_CNT_INT_00 * 4)
+#define BUF_CNT_INT_KEY                        (FIQ_CNT_INT_KEY * 4)
+#define BUF_CNT_INT_MDM                        (FIQ_CNT_INT_MDM * 4)
+#define BUF_CNT_INT_03                 (FIQ_CNT_INT_03 * 4)
+#define BUF_CNT_INT_HSW                        (FIQ_CNT_INT_HSW * 4)
+#define BUF_CNT_INT_05                 (FIQ_CNT_INT_05 * 4)
+#define BUF_CNT_INT_06                 (FIQ_CNT_INT_06 * 4)
+#define BUF_CNT_INT_07                 (FIQ_CNT_INT_07 * 4)
+#define BUF_CNT_INT_08                 (FIQ_CNT_INT_08 * 4)
+#define BUF_CNT_INT_09                 (FIQ_CNT_INT_09 * 4)
+#define BUF_CNT_INT_10                 (FIQ_CNT_INT_10 * 4)
+#define BUF_CNT_INT_11                 (FIQ_CNT_INT_11 * 4)
+#define BUF_CNT_INT_12                 (FIQ_CNT_INT_12 * 4)
+#define BUF_CNT_INT_13                 (FIQ_CNT_INT_13 * 4)
+#define BUF_CNT_INT_14                 (FIQ_CNT_INT_14 * 4)
+#define BUF_CNT_INT_15                 (FIQ_CNT_INT_15 * 4)
+#define BUF_CIRC_BUFF                  (FIQ_CIRC_BUFF * 4)
+
+
+/*
+ * Register useage
+ * r8  - temporary
+ * r9  - the driver buffer
+ * r10 - temporary
+ * r11 - interrupts mask
+ * r12 - base pointers
+ * r13 - interrupts status
+ */
+
+       .text
+
+       .global qwerty_fiqin_end
+
+ENTRY(qwerty_fiqin_start)
+       @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+       @ FIQ intrrupt handler
+       ldr r12, omap_ih1_base                  @ set pointer to level1 handler
+
+       ldr r11, [r12, #IRQ_MIR_REG_OFFSET]     @ fetch interrupts mask
+
+       ldr r13, [r12, #IRQ_ITR_REG_OFFSET]     @ fetch interrupts status
+       bics r13, r13, r11                      @ clear masked - any left?
+       beq exit                                @ none - spurious FIQ? exit
+
+       ldr r10, [r12, #IRQ_SIR_FIQ_REG_OFFSET] @ get requested interrupt number
+
+       mov r8, #2                              @ reset FIQ agreement
+       str r8, [r12, #IRQ_CONTROL_REG_OFFSET]
+
+       cmp r10, #INT_GPIO_BANK1                @ is it GPIO bank interrupt?
+       beq gpio                                @ yes - process it
+
+       mov r8, #1
+       orr r8, r11, r8, lsl r10                @ mask spurious interrupt
+       str r8, [r12, #IRQ_MIR_REG_OFFSET]
+exit:
+       subs    pc, lr, #4                      @ return from FIQ
+       @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+
+       @@@@@@@@@@@@@@@@@@@@@@@@@@@
+gpio:  @ GPIO bank interrupt handler
+       ldr r12, omap1510_gpio_base             @ set base pointer to GPIO bank
+
+       ldr r11, [r12, #OMAP1510_GPIO_INT_MASK] @ fetch GPIO interrupts mask
+restart:
+       ldr r13, [r12, #OMAP1510_GPIO_INT_STATUS]       @ fetch status bits
+       bics r13, r13, r11                      @ clear masked - any left?
+       beq exit                                @ no - spurious interrupt? exit
+
+       orr r11, r11, r13                       @ mask all requested interrupts
+       str r11, [r12, #OMAP1510_GPIO_INT_MASK]
+
+       ands r10, r13, #KEYBRD_CLK_MASK         @ extract keyboard status - set?
+       beq hksw                                @ no - try next source
+
+
+       @@@@@@@@@@@@@@@@@@@@@@
+       @ Keyboard clock FIQ mode interrupt handler
+       @ r10 now contains KEYBRD_CLK_MASK, use it
+       str r10, [r12, #OMAP1510_GPIO_INT_STATUS]       @ ack the interrupt
+       bic r11, r11, r10                               @ unmask it
+       str r11, [r12, #OMAP1510_GPIO_INT_MASK]
+
+       @ Process keyboard data
+       ldr r8, [r12, #OMAP1510_GPIO_DATA_INPUT]        @ fetch GPIO input
+
+       ldr r10, [r9, #BUF_STATE]               @ fetch kbd interface state
+       cmp r10, #0                             @ are we expecting start bit?
+       bne data                                @ no - go to data processing
+
+       ands r8, r8, #KEYBRD_DATA_MASK          @ check start bit - detected?
+       beq hksw                                @ no - try next source
+
+       @ r8 contains KEYBRD_DATA_MASK, use it
+       str r8, [r9, #BUF_STATE]                @ enter data processing state
+       @ r10 already contains 0, reuse it
+       str r10, [r9, #BUF_KEY]                 @ clear keycode
+       mov r10, #2                             @ reset input bit mask
+       str r10, [r9, #BUF_MASK]
+
+       @ Mask other GPIO line interrupts till key done
+       str r11, [r9, #BUF_GPIO_INT_MASK]       @ save mask for later restore
+       mvn r11, #KEYBRD_CLK_MASK               @ prepare all except kbd mask
+       str r11, [r12, #OMAP1510_GPIO_INT_MASK] @ store into the mask register
+
+       b restart                               @ restart
+
+data:  ldr r10, [r9, #BUF_MASK]                @ fetch current input bit mask
+
+       @ r8 still contains GPIO input bits
+       ands r8, r8, #KEYBRD_DATA_MASK          @ is keyboard data line low?
+       ldreq r8, [r9, #BUF_KEY]                @ yes - fetch collected so far,
+       orreq r8, r8, r10                       @ set 1 at current mask position
+       streq r8, [r9, #BUF_KEY]                @ and save back
+
+       mov r10, r10, lsl #1                    @ shift mask left
+       bics r10, r10, #0x800                   @ have we got all the bits?
+       strne r10, [r9, #BUF_MASK]              @ not yet - store the mask
+       bne restart                             @ and restart
+
+       @ r10 already contains 0, reuse it
+       str r10, [r9, #BUF_STATE]               @ reset state to start
+
+       @ Key done - restore interrupt mask
+       ldr r10, [r9, #BUF_GPIO_INT_MASK]       @ fetch saved mask
+       and r11, r11, r10                       @ unmask all saved as unmasked
+       str r11, [r12, #OMAP1510_GPIO_INT_MASK] @ restore into the mask register
+
+       @ Try appending the keycode to the circular buffer
+       ldr r10, [r9, #BUF_KEYS_CNT]            @ get saved keystrokes count
+       ldr r8, [r9, #BUF_BUF_LEN]              @ get buffer size
+       cmp r10, r8                             @ is buffer full?
+       beq hksw                                @ yes - key lost, next source
+
+       add r10, r10, #1                        @ incremet keystrokes counter
+       str r10, [r9, #BUF_KEYS_CNT]
+
+       ldr r10, [r9, #BUF_TAIL_OFFSET]         @ get buffer tail offset
+       @ r8 already contains buffer size
+       cmp r10, r8                             @ end of buffer?
+       moveq r10, #0                           @ yes - rewind to buffer start
+
+       ldr r12, [r9, #BUF_BUFFER_START]        @ get buffer start address
+       add r12, r12, r10, LSL #2               @ calculate buffer tail address
+       ldr r8, [r9, #BUF_KEY]                  @ get last keycode
+       str r8, [r12]                           @ append it to the buffer tail
+
+       add r10, r10, #1                        @ increment buffer tail offset
+       str r10, [r9, #BUF_TAIL_OFFSET]
+
+       ldr r10, [r9, #BUF_CNT_INT_KEY]         @ increment interrupts counter
+       add r10, r10, #1
+       str r10, [r9, #BUF_CNT_INT_KEY]
+       @@@@@@@@@@@@@@@@@@@@@@@@
+
+
+hksw:  @Is hook switch interrupt requested?
+       tst r13, #HOOK_SWITCH_MASK              @ is hook switch status bit set?
+       beq mdm                                 @ no - try next source
+
+
+       @@@@@@@@@@@@@@@@@@@@@@@@
+       @ Hook switch interrupt FIQ mode simple handler
+
+       @ Don't toggle active edge, the switch always bounces
+
+       @ Increment hook switch interrupt counter
+       ldr r10, [r9, #BUF_CNT_INT_HSW]
+       add r10, r10, #1
+       str r10, [r9, #BUF_CNT_INT_HSW]
+       @@@@@@@@@@@@@@@@@@@@@@@@
+
+
+mdm:   @Is it a modem interrupt?
+       tst r13, #MODEM_IRQ_MASK                @ is modem status bit set?
+       beq irq                                 @ no - check for next interrupt
+
+
+       @@@@@@@@@@@@@@@@@@@@@@@@
+       @ Modem FIQ mode interrupt handler stub
+
+       @ Increment modem interrupt counter
+       ldr r10, [r9, #BUF_CNT_INT_MDM]
+       add r10, r10, #1
+       str r10, [r9, #BUF_CNT_INT_MDM]
+       @@@@@@@@@@@@@@@@@@@@@@@@
+
+
+irq:   @ Place deferred_fiq interrupt request
+       ldr r12, deferred_fiq_ih_base           @ set pointer to IRQ handler
+       mov r10, #DEFERRED_FIQ_MASK             @ set deferred_fiq bit
+       str r10, [r12, #IRQ_ISR_REG_OFFSET]     @ place it in the ISR register
+
+       ldr r12, omap1510_gpio_base             @ set pointer back to GPIO bank
+       b restart                               @ check for next GPIO interrupt
+       @@@@@@@@@@@@@@@@@@@@@@@@@@@
+
+
+/*
+ * Virtual addresses for IO
+ */
+omap_ih1_base:
+       .word OMAP1_IO_ADDRESS(OMAP_IH1_BASE)
+deferred_fiq_ih_base:
+       .word OMAP1_IO_ADDRESS(DEFERRED_FIQ_IH_BASE)
+omap1510_gpio_base:
+       .word OMAP1_IO_ADDRESS(OMAP1510_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 --git a/arch/arm/mach-omap1/include/mach/ams-delta-fiq.h b/arch/arm/mach-omap1/include/mach/ams-delta-fiq.h
new file mode 100644 (file)
index 0000000..8dbe75b
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * 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
+ * Copyright (c) 2010 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.
+ */
+#ifndef __AMS_DELTA_FIQ_H
+#define __AMS_DELTA_FIQ_H
+
+#include <plat/irqs.h>
+
+/*
+ * Interrupt number used for passing control from FIQ to IRQ.
+ * IRQ12, described as reserved, has been selected.
+ */
+#define INT_DEFERRED_FIQ       INT_1510_RES12
+/*
+ * Base address of an interrupt handler that the INT_DEFERRED_FIQ belongs to.
+ */
+#if (INT_DEFERRED_FIQ < IH2_BASE)
+#define DEFERRED_FIQ_IH_BASE   OMAP_IH1_BASE
+#else
+#define DEFERRED_FIQ_IH_BASE   OMAP_IH2_BASE
+#endif
+
+/*
+ * These are the offsets from the begining of the fiq_buffer. They are put here
+ * since the buffer and header need to be accessed by drivers servicing devices
+ * which generate GPIO interrupts - e.g. keyboard, modem, hook switch.
+ */
+#define FIQ_MASK                0
+#define FIQ_STATE               1
+#define FIQ_KEYS_CNT            2
+#define FIQ_TAIL_OFFSET                 3
+#define FIQ_HEAD_OFFSET                 4
+#define FIQ_BUF_LEN             5
+#define FIQ_KEY                         6
+#define FIQ_MISSED_KEYS                 7
+#define FIQ_BUFFER_START        8
+#define FIQ_GPIO_INT_MASK       9
+#define FIQ_KEYS_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_KEY                15
+#define FIQ_CNT_INT_MDM                16
+#define FIQ_CNT_INT_03         17
+#define FIQ_CNT_INT_HSW                18
+#define FIQ_CNT_INT_05         19
+#define FIQ_CNT_INT_06         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 */
+
+#endif