]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - arch/arm/mach-omap2/irq.c
ARM: OMAP3: Mask interrupts when disabling interrupts
[linux-2.6-omap-h63xx.git] / arch / arm / mach-omap2 / irq.c
index c40fc378a251244bce6e6c0ad9f0e177c7326a75..b44dcaeef738b06140a54437dee4dc91a67a1b77 100644 (file)
@@ -23,6 +23,7 @@
 #define INTC_REVISION          0x0000
 #define INTC_SYSCONFIG         0x0010
 #define INTC_SYSSTATUS         0x0014
+#define INTC_SIR               0x0040
 #define INTC_CONTROL           0x0048
 #define INTC_MIR_CLEAR0                0x0088
 #define INTC_MIR_SET0          0x008c
@@ -60,6 +61,30 @@ static u32 intc_bank_read_reg(struct omap_irq_bank *bank, u16 reg)
        return __raw_readl(bank->base_reg + reg);
 }
 
+static int previous_irq;
+
+/*
+ * On 34xx we can get occasional spurious interrupts if the ack from
+ * an interrupt handler does not get posted before we unmask. Warn about
+ * the interrupt handlers that need to flush posted writes.
+ */
+static int omap_check_spurious(unsigned int irq)
+{
+       u32 sir, spurious;
+
+       sir = intc_bank_read_reg(&irq_banks[0], INTC_SIR);
+       spurious = sir >> 6;
+
+       if (spurious > 1) {
+               printk(KERN_WARNING "Spurious irq %i: 0x%08x, please flush "
+                                       "posted write for irq %i\n",
+                                       irq, sir, previous_irq);
+               return spurious;
+       }
+
+       return 0;
+}
+
 /* XXX: FIQ and additional INTC support (only MPU at the moment) */
 static void omap_ack_irq(unsigned int irq)
 {
@@ -70,6 +95,20 @@ static void omap_mask_irq(unsigned int irq)
 {
        int offset = irq & (~(IRQ_BITS_PER_REG - 1));
 
+       if (cpu_is_omap34xx()) {
+               int spurious = 0;
+
+               /*
+                * INT_34XX_GPT12_IRQ is also the spurious irq. Maybe because
+                * it is the highest irq number?
+                */
+               if (irq == INT_34XX_GPT12_IRQ)
+                       spurious = omap_check_spurious(irq);
+
+               if (!spurious)
+                       previous_irq = irq;
+       }
+
        irq &= (IRQ_BITS_PER_REG - 1);
 
        intc_bank_write_reg(1 << irq, &irq_banks[0], INTC_MIR_SET0 + offset);
@@ -84,6 +123,11 @@ static void omap_unmask_irq(unsigned int irq)
        intc_bank_write_reg(1 << irq, &irq_banks[0], INTC_MIR_CLEAR0 + offset);
 }
 
+static void omap_disable_irq(unsigned int irq)
+{
+       omap_mask_irq(irq);
+}
+
 static void omap_mask_ack_irq(unsigned int irq)
 {
        omap_mask_irq(irq);
@@ -95,6 +139,7 @@ static struct irq_chip omap_irq_chip = {
        .ack    = omap_mask_ack_irq,
        .mask   = omap_mask_irq,
        .unmask = omap_unmask_irq,
+       .disable = omap_disable_irq,
 };
 
 static void __init omap_irq_bank_init_one(struct omap_irq_bank *bank)
@@ -117,6 +162,26 @@ static void __init omap_irq_bank_init_one(struct omap_irq_bank *bank)
        intc_bank_write_reg(1 << 0, bank, INTC_SYSCONFIG);
 }
 
+int omap_irq_pending(void)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(irq_banks); i++) {
+               struct omap_irq_bank *bank = irq_banks + i;
+               int irq;
+
+               for (irq = 0; irq < bank->nr_irqs; irq += IRQ_BITS_PER_REG) {
+                       int offset = irq & (~(IRQ_BITS_PER_REG - 1));
+
+                       if (intc_bank_read_reg(bank, (INTC_PENDING_IRQ0 +
+                                                     offset)))
+                               return 1;
+               }
+       }
+
+       return 0;
+}
+
 void __init omap_init_irq(void)
 {
        unsigned long nr_of_irqs = 0;