aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/mach-s3c24xx/irq.c
diff options
context:
space:
mode:
authorHeiko Stuebner <heiko@sntech.de>2013-03-07 12:38:25 +0900
committerKukjin Kim <kgene.kim@samsung.com>2013-03-07 12:38:25 +0900
commit17453dd2e7df20612770ebbf1ab5d506a432210c (patch)
tree818a77eccd79e7e265a5d9ca5f6ef579f67697b2 /arch/arm/mach-s3c24xx/irq.c
parent3d3eb5a476a55e9ef1aeaa6a3c2b00587ed32521 (diff)
ARM: S3C24XX: add handle_irq function
This removes the dependency on static irq mappings for basic irq handling and makes the s3c24xx entry-macro.S obsolete. Also the interrupts of the second full interrupt controller on the s3c2416 are really handled now, which was forgotten when adding them. The handling itself does the same as the previous assembler-code in that it tries to get the interrupt offset from the offset register first and if that produces wrong results manually searches for the interrupt bit in the pending register value. It also saves the historic comment which explains the reason behind this. Signed-off-by: Heiko Stuebner <heiko@sntech.de> Signed-off-by: Kukjin Kim <kgene.kim@samsung.com>
Diffstat (limited to 'arch/arm/mach-s3c24xx/irq.c')
-rw-r--r--arch/arm/mach-s3c24xx/irq.c58
1 files changed, 58 insertions, 0 deletions
diff --git a/arch/arm/mach-s3c24xx/irq.c b/arch/arm/mach-s3c24xx/irq.c
index 8bc29313c34..5c9f8b7a1fd 100644
--- a/arch/arm/mach-s3c24xx/irq.c
+++ b/arch/arm/mach-s3c24xx/irq.c
@@ -26,6 +26,7 @@
#include <linux/device.h>
#include <linux/irqdomain.h>
+#include <asm/exception.h>
#include <asm/mach/irq.h>
#include <mach/regs-irq.h>
@@ -282,6 +283,56 @@ static void s3c_irq_demux(unsigned int irq, struct irq_desc *desc)
chained_irq_exit(chip, desc);
}
+static struct s3c_irq_intc *main_intc;
+static struct s3c_irq_intc *main_intc2;
+
+static inline int s3c24xx_handle_intc(struct s3c_irq_intc *intc,
+ struct pt_regs *regs)
+{
+ int pnd;
+ int offset;
+ int irq;
+
+ pnd = __raw_readl(intc->reg_intpnd);
+ if (!pnd)
+ return false;
+
+ /* We have a problem that the INTOFFSET register does not always
+ * show one interrupt. Occasionally we get two interrupts through
+ * the prioritiser, and this causes the INTOFFSET register to show
+ * what looks like the logical-or of the two interrupt numbers.
+ *
+ * Thanks to Klaus, Shannon, et al for helping to debug this problem
+ */
+ offset = __raw_readl(intc->reg_intpnd + 4);
+
+ /* Find the bit manually, when the offset is wrong.
+ * The pending register only ever contains the one bit of the next
+ * interrupt to handle.
+ */
+ if (!(pnd & (1 << offset)))
+ offset = __ffs(pnd);
+
+ irq = irq_find_mapping(intc->domain, offset);
+ handle_IRQ(irq, regs);
+ return true;
+}
+
+asmlinkage void __exception_irq_entry s3c24xx_handle_irq(struct pt_regs *regs)
+{
+ do {
+ if (likely(main_intc))
+ if (s3c24xx_handle_intc(main_intc, regs))
+ continue;
+
+ if (main_intc2)
+ if (s3c24xx_handle_intc(main_intc2, regs))
+ continue;
+
+ break;
+ } while (1);
+}
+
#ifdef CONFIG_FIQ
/**
* s3c24xx_set_fiq - set the FIQ routing
@@ -502,6 +553,13 @@ static struct s3c_irq_intc *s3c24xx_init_intc(struct device_node *np,
goto err;
}
+ if (address == 0x4a000000)
+ main_intc = intc;
+ else if (address == 0x4a000040)
+ main_intc2 = intc;
+
+ set_handle_irq(s3c24xx_handle_irq);
+
return intc;
err: