Improve s3c_irqext_unmask to handle level irqs better

Balaji Rao balajirrao at openmoko.org
Wed Dec 10 08:30:53 CET 2008


Hi,

In pcf50633_irq, since we can't do i2c transactions there, we disable the
irq and defer the work to a workqueue. When we read the  pcf50633 int
registers, it irq is deasserted by the chip. But EINTPEND still says
that the irq is pending. Now, when the irq is unmasked, at the end of
the workqueue function, the interrupt handler is called unnecessarily.

To fix this, in s3c_irqext_unmask, we ack the interrupt if it's pending
and if it's level triggered.

Ben, does this sound right ?

Signed-off-by: Balaji Rao <balajirrao at openmoko.org>

diff --git a/arch/arm/plat-s3c24xx/irq.c b/arch/arm/plat-s3c24xx/irq.c
index d696bbd..d853c93 100644
--- a/arch/arm/plat-s3c24xx/irq.c
+++ b/arch/arm/plat-s3c24xx/irq.c
@@ -166,16 +166,74 @@ s3c_irqext_ack(unsigned int irqno)
 	}
 }
 
+static int
+s3c_get_irqext_type(unsigned int irq)
+{
+	void __iomem *extint_reg;
+	unsigned long value, extint_offset;
+	unsigned int type = IRQ_TYPE_NONE;
+
+	if ((irq >= IRQ_EINT0) && (irq <= IRQ_EINT3)) {
+		extint_reg = S3C24XX_EXTINT0;
+		extint_offset = (irq - IRQ_EINT0) * 4;
+	} else if ((irq >= IRQ_EINT4) && (irq <= IRQ_EINT7)) {
+		extint_reg = S3C24XX_EXTINT0;
+		extint_offset = (irq - (EXTINT_OFF)) * 4;
+	} else if ((irq >= IRQ_EINT8) && (irq <= IRQ_EINT15)) {
+		extint_reg = S3C24XX_EXTINT1;
+		extint_offset = (irq - IRQ_EINT8) * 4;
+	} else if ((irq >= IRQ_EINT16) && (irq <= IRQ_EINT23))	{
+		extint_reg = S3C24XX_EXTINT2;
+		extint_offset = (irq - IRQ_EINT16) * 4;
+	} else
+		return -1;
+
+	value = __raw_readl(extint_reg);
+	value = ((value >> extint_offset) & 7);
+
+	switch (value) {
+	case S3C2410_EXTINT_RISEEDGE:
+		type = IRQ_TYPE_EDGE_RISING;
+		break;
+	case S3C2410_EXTINT_FALLEDGE:
+		type = IRQ_TYPE_EDGE_FALLING;
+		break;
+	case S3C2410_EXTINT_BOTHEDGE:
+		type = IRQ_TYPE_EDGE_BOTH;
+		break;
+	case S3C2410_EXTINT_LOWLEV:
+		type = IRQ_TYPE_LEVEL_LOW;
+		break;
+	case S3C2410_EXTINT_HILEV:
+		type = IRQ_TYPE_LEVEL_HIGH;
+		break;
+	}
+	return type;
+}
+
+
 static void
-s3c_irqext_unmask(unsigned int irqno)
+s3c_irqext_unmask(unsigned int irq)
 {
-	unsigned long mask;
+	unsigned long mask, pending;
+	unsigned int irqno;
+	int type;
 
-	irqno -= EXTINT_OFF;
+	irqno = irq - EXTINT_OFF;
 
 	mask = __raw_readl(S3C24XX_EINTMASK);
 	mask &= ~( 1UL << irqno);
 	__raw_writel(mask, S3C24XX_EINTMASK);
+
+	/*
+	 * ACK this irq if it is pending and is level triggered, as it
+	 * could be deasserted by now. No harm done if it's not.
+	 */
+	pending = __raw_readl(S3C24XX_EINTPEND);
+	type = s3c_get_irqext_type(irq);
+	if (pending & (1UL << irqno) &&	(type == IRQ_TYPE_LEVEL_HIGH
+						|| type == IRQ_TYPE_LEVEL_LOW))
+		s3c_irqext_ack(irq);
 }
 
 int
 



More information about the openmoko-kernel mailing list