[ARM] 3645/1: S3C2412: irq support for external interrupts

Patch from Ben Dooks

Move the decoding of the IRQ_EXT4 and above out of
the entry macro, and into an chained irq handler
as the EXTINT registers move depending on the CPU
being used.

Signed-off-by: Ben Dooks <ben-linux@fluff.org>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
diff --git a/arch/arm/mach-s3c2410/irq.c b/arch/arm/mach-s3c2410/irq.c
index 4646574..6822dc7 100644
--- a/arch/arm/mach-s3c2410/irq.c
+++ b/arch/arm/mach-s3c2410/irq.c
@@ -191,13 +191,9 @@
 	.ack	   = s3c_irq_ack,
 	.mask	   = s3c_irq_mask,
 	.unmask	   = s3c_irq_unmask,
-	.set_wake	   = s3c_irq_wake
+	.set_wake  = s3c_irq_wake
 };
 
-/* S3C2410_EINTMASK
- * S3C2410_EINTPEND
- */
-
 static void
 s3c_irqext_mask(unsigned int irqno)
 {
@@ -205,9 +201,9 @@
 
 	irqno -= EXTINT_OFF;
 
-	mask = __raw_readl(S3C2410_EINTMASK);
+	mask = __raw_readl(S3C24XX_EINTMASK);
 	mask |= ( 1UL << irqno);
-	__raw_writel(mask, S3C2410_EINTMASK);
+	__raw_writel(mask, S3C24XX_EINTMASK);
 
 	if (irqno <= (IRQ_EINT7 - EXTINT_OFF)) {
 		/* check to see if all need masking */
@@ -232,11 +228,11 @@
 	bit = 1UL << (irqno - EXTINT_OFF);
 
 
-	mask = __raw_readl(S3C2410_EINTMASK);
+	mask = __raw_readl(S3C24XX_EINTMASK);
 
-	__raw_writel(bit, S3C2410_EINTPEND);
+	__raw_writel(bit, S3C24XX_EINTPEND);
 
-	req = __raw_readl(S3C2410_EINTPEND);
+	req = __raw_readl(S3C24XX_EINTPEND);
 	req &= ~mask;
 
 	/* not sure if we should be acking the parent irq... */
@@ -257,9 +253,9 @@
 
 	irqno -= EXTINT_OFF;
 
-	mask = __raw_readl(S3C2410_EINTMASK);
+	mask = __raw_readl(S3C24XX_EINTMASK);
 	mask &= ~( 1UL << irqno);
-	__raw_writel(mask, S3C2410_EINTMASK);
+	__raw_writel(mask, S3C24XX_EINTMASK);
 
 	s3c_irq_unmask((irqno <= (IRQ_EINT7 - EXTINT_OFF)) ? IRQ_EINT4t7 : IRQ_EINT8t23);
 }
@@ -572,6 +568,23 @@
 	s3c_irq_demux_uart(IRQ_S3CUART_RX2, regs);
 }
 
+static void
+s3c_irq_demux_extint(unsigned int irq,
+		     struct irqdesc *desc,
+		     struct pt_regs *regs)
+{
+	unsigned long eintpnd = __raw_readl(S3C24XX_EINTPEND);
+	unsigned long eintmsk = __raw_readl(S3C24XX_EINTMASK);
+
+	eintpnd &= ~eintmsk;
+
+	if (eintpnd) {
+		irq = fls(eintpnd);
+		irq += (IRQ_EINT4 - (4 + 1));
+
+		desc_handle_irq(irq, irq_desc + irq, regs);
+	}
+}
 
 /* s3c24xx_init_irq
  *
@@ -591,12 +604,12 @@
 
 	last = 0;
 	for (i = 0; i < 4; i++) {
-		pend = __raw_readl(S3C2410_EINTPEND);
+		pend = __raw_readl(S3C24XX_EINTPEND);
 
 		if (pend == 0 || pend == last)
 			break;
 
-		__raw_writel(pend, S3C2410_EINTPEND);
+		__raw_writel(pend, S3C24XX_EINTPEND);
 		printk("irq: clearing pending ext status %08x\n", (int)pend);
 		last = pend;
 	}
@@ -630,12 +643,14 @@
 
 	irqdbf("s3c2410_init_irq: registering s3c2410 interrupt handlers\n");
 
-	for (irqno = IRQ_BATT_FLT; irqno <= IRQ_ADCPARENT; irqno++) {
+	for (irqno = IRQ_EINT4t7; irqno <= IRQ_ADCPARENT; irqno++) {
 		/* set all the s3c2410 internal irqs */
 
 		switch (irqno) {
 			/* deal with the special IRQs (cascaded) */
 
+		case IRQ_EINT4t7:
+		case IRQ_EINT8t23:
 		case IRQ_UART0:
 		case IRQ_UART1:
 		case IRQ_UART2:
@@ -659,12 +674,14 @@
 
 	/* setup the cascade irq handlers */
 
+	set_irq_chained_handler(IRQ_EINT4t7, s3c_irq_demux_extint);
+	set_irq_chained_handler(IRQ_EINT8t23, s3c_irq_demux_extint);
+
 	set_irq_chained_handler(IRQ_UART0, s3c_irq_demux_uart0);
 	set_irq_chained_handler(IRQ_UART1, s3c_irq_demux_uart1);
 	set_irq_chained_handler(IRQ_UART2, s3c_irq_demux_uart2);
 	set_irq_chained_handler(IRQ_ADCPARENT, s3c_irq_demux_adc);
 
-
 	/* external interrupts */
 
 	for (irqno = IRQ_EINT0; irqno <= IRQ_EINT3; irqno++) {
diff --git a/include/asm-arm/arch-s3c2410/entry-macro.S b/include/asm-arm/arch-s3c2410/entry-macro.S
index 894c35c..e09a6b8 100644
--- a/include/asm-arm/arch-s3c2410/entry-macro.S
+++ b/include/asm-arm/arch-s3c2410/entry-macro.S
@@ -18,8 +18,6 @@
 
 #define INTPND		(0x10)
 #define INTOFFSET	(0x14)
-#define EXTINTPEND	(0xa8)
-#define EXTINTMASK	(0xa4)
 
 #include <asm/hardware.h>
 #include <asm/arch/irqs.h>
@@ -28,37 +26,23 @@
 
 		mov	\base, #S3C24XX_VA_IRQ
 
-		ldr	\irqstat, [ \base, #INTPND]
-		bics	\irqnr, \irqstat, #3<<4		@@ only an GPIO IRQ
-		beq	2000f
-
 		@@ try the interrupt offset register, since it is there
 
+		ldr	\irqstat, [ \base, #INTPND ]
+		teq	\irqstat, #0
+		beq	1002f
 		ldr	\irqnr, [ \base, #INTOFFSET ]
 		mov	\tmp, #1
 		tst	\irqstat, \tmp, lsl \irqnr
-		addne	\irqnr, \irqnr, #IRQ_EINT0
 		bne	1001f
 
 		@@ the number specified is not a valid irq, so try
 		@@ and work it out for ourselves
 
-		mov	\irqnr, #IRQ_EINT0		@@ start here
-		b	3000f
-
-2000:
-		@@ load the GPIO interrupt register, and check it
-
-		add	\tmp, \base, #S3C24XX_VA_GPIO - S3C24XX_VA_IRQ
-		ldr	\irqstat, [ \tmp, # EXTINTPEND ]
-		ldr	\irqnr,   [ \tmp, # EXTINTMASK ]
-		bics	\irqstat, \irqstat, \irqnr
-		beq	1001f
-
-		mov	\irqnr, #(IRQ_EINT4 - 4)
+		mov	\irqnr, #0		@@ start here
 
 		@@ work out which irq (if any) we got
-3000:
+
 		movs	\tmp, \irqstat, lsl#16
 		addeq	\irqnr, \irqnr, #16
 		moveq	\irqstat, \irqstat, lsr#16
@@ -75,9 +59,9 @@
 		addeq	\irqnr, \irqnr, #1
 
 		@@ we have the value
-		movs	\irqnr, \irqnr
-
 1001:
+		adds	\irqnr, \irqnr, #IRQ_EINT0
+1002:
 		@@ exit here, Z flag unset if IRQ
 
 	.endm
diff --git a/include/asm-arm/arch-s3c2410/regs-irq.h b/include/asm-arm/arch-s3c2410/regs-irq.h
index 24b7292..572fca5 100644
--- a/include/asm-arm/arch-s3c2410/regs-irq.h
+++ b/include/asm-arm/arch-s3c2410/regs-irq.h
@@ -23,6 +23,7 @@
 
 #define S3C2410_IRQREG(x)   ((x) + S3C24XX_VA_IRQ)
 #define S3C2410_EINTREG(x)  ((x) + S3C24XX_VA_GPIO)
+#define S3C24XX_EINTREG(x)  ((x) + S3C24XX_VA_GPIO2)
 
 #define S3C2410_SRCPND	       S3C2410_IRQREG(0x000)
 #define S3C2410_INTMOD	       S3C2410_IRQREG(0x004)
@@ -40,5 +41,10 @@
 
 #define S3C2410_EINTMASK       S3C2410_EINTREG(0x0A4)
 #define S3C2410_EINTPEND       S3C2410_EINTREG(0X0A8)
+#define S3C2412_EINTMASK       S3C2410_EINTREG(0x0B4)
+#define S3C2412_EINTPEND       S3C2410_EINTREG(0X0B8)
+
+#define S3C24XX_EINTMASK       S3C24XX_EINTREG(0x0A4)
+#define S3C24XX_EINTPEND       S3C24XX_EINTREG(0X0A8)
 
 #endif /* ___ASM_ARCH_REGS_IRQ_H */