OMAP1: Amstrad Delta: add a handler for processing interruptsgenerated by the FIQ...
authorJanusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Wed, 28 Apr 2010 01:03:59 +0000 (01:03 +0000)
committerTony Lindgren <tony@atomide.com>
Wed, 5 May 2010 18:11:10 +0000 (11:11 -0700)
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
interrupt source counters passed from the FIQ handler.

The handler setup function is intended to be called from the board
provided init_machine() callback.

Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
[tony@atomide.com: Updated to include linux/io.h instead of plat/io.h
Signed-off-by: Tony Lindgren <tony@atomide.com>
arch/arm/mach-omap1/Makefile
arch/arm/mach-omap1/ams-delta-fiq.c [new file with mode: 0644]
arch/arm/mach-omap1/include/mach/ams-delta-fiq.h

index db0d480013443581d482c131b8dd80b0590272c6..ea231c7a550a7d3ff891cd0f38729906967c2786 100644 (file)
@@ -37,7 +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_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 --git a/arch/arm/mach-omap1/ams-delta-fiq.c b/arch/arm/mach-omap1/ams-delta-fiq.c
new file mode 100644 (file)
index 0000000..6c994e2
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ *  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/gpio.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/module.h>
+#include <linux/io.h>
+
+#include <plat/board-ams-delta.h>
+
+#include <asm/fiq.h>
+#include <mach/ams-delta-fiq.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
+ * keystrokes 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 irq_counter[16];
+
+static irqreturn_t deferred_fiq(int irq, void *dev_id)
+{
+       struct irq_desc *irq_desc;
+       struct irq_chip *irq_chip = NULL;
+       int gpio, irq_num, fiq_count;
+
+       irq_desc = irq_to_desc(IH_GPIO_BASE);
+       if (irq_desc)
+               irq_chip = irq_desc->chip;
+
+       /*
+        * For each handled GPIO interrupt, keep calling its interrupt handler
+        * until the IRQ counter catches the FIQ incremented interrupt counter.
+        */
+       for (gpio = AMS_DELTA_GPIO_PIN_KEYBRD_CLK;
+                       gpio <= AMS_DELTA_GPIO_PIN_HOOK_SWITCH; gpio++) {
+               irq_num = gpio_to_irq(gpio);
+               fiq_count = fiq_buffer[FIQ_CNT_INT_00 + gpio];
+
+               while (irq_counter[gpio] < fiq_count) {
+                       if (gpio != AMS_DELTA_GPIO_PIN_KEYBRD_CLK) {
+                               /*
+                                * It looks like handle_edge_irq() that
+                                * OMAP GPIO edge interrupts default to,
+                                * expects interrupt already unmasked.
+                                */
+                               if (irq_chip && irq_chip->unmask)
+                                       irq_chip->unmask(irq_num);
+                       }
+                       generic_handle_irq(irq_num);
+
+                       irq_counter[gpio]++;
+               }
+       }
+       return IRQ_HANDLED;
+}
+
+void __init ams_delta_init_fiq(void)
+{
+       void *fiqhandler_start;
+       unsigned int fiqhandler_length;
+       struct pt_regs FIQ_regs;
+       unsigned long val, offset;
+       int i, retval;
+
+       fiqhandler_start = &qwerty_fiqin_start;
+       fiqhandler_length = &qwerty_fiqin_end - &qwerty_fiqin_start;
+       pr_info("Installing fiq handler from %p, length 0x%x\n",
+                       fiqhandler_start, fiqhandler_length);
+
+       retval = claim_fiq(&fh);
+       if (retval) {
+               pr_err("ams_delta_init_fiq(): couldn't claim FIQ, ret=%d\n",
+                               retval);
+               return;
+       }
+
+       retval = request_irq(INT_DEFERRED_FIQ, deferred_fiq,
+                       IRQ_TYPE_EDGE_RISING, "deferred_fiq", 0);
+       if (retval < 0) {
+               pr_err("Failed to get deferred_fiq IRQ, ret=%d\n", retval);
+               release_fiq(&fh);
+               return;
+       }
+       /*
+        * Since no set_type() method is provided by OMAP irq chip,
+        * switch to edge triggered interrupt type manually.
+        */
+       offset = IRQ_ILR0_REG_OFFSET + INT_DEFERRED_FIQ * 0x4;
+       val = omap_readl(DEFERRED_FIQ_IH_BASE + offset) & ~(1 << 1);
+       omap_writel(val, DEFERRED_FIQ_IH_BASE + offset);
+
+       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_KEY]             = 0;
+       fiq_buffer[FIQ_KEYS_CNT]        = 0;
+       fiq_buffer[FIQ_KEYS_HICNT]      = 0;
+       fiq_buffer[FIQ_TAIL_OFFSET]     = 0;
+       fiq_buffer[FIQ_HEAD_OFFSET]     = 0;
+       fiq_buffer[FIQ_BUF_LEN]         = 256;
+       fiq_buffer[FIQ_MISSED_KEYS]     = 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 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;
+       set_fiq_regs(&FIQ_regs);
+
+       pr_info("request_fiq(): fiq_buffer = %p\n", fiq_buffer);
+
+       /*
+        * Redirect GPIO interrupts to FIQ
+        */
+       offset = IRQ_ILR0_REG_OFFSET + INT_GPIO_BANK1 * 0x4;
+       val = omap_readl(OMAP_IH1_BASE + offset) | 1;
+       omap_writel(val, OMAP_IH1_BASE + offset);
+}
index 8dbe75b7b89f001ccdf9de75c238ca6149113db1..7a2df29400cac5105acebc22657f036a6f3de828 100644 (file)
 
 #define FIQ_CIRC_BUFF          30      /*Start of circular buffer */
 
+#ifndef __ASSEMBLER__
+extern unsigned int fiq_buffer[];
+extern unsigned char qwerty_fiqin_start, qwerty_fiqin_end;
+
+extern void __init ams_delta_init_fiq(void);
+#endif
+
 #endif