Merge branch 'liblockdep-fixes' of git://git.kernel.org/pub/scm/linux/kernel/git...
[firefly-linux-kernel-4.4.55.git] / drivers / irqchip / irq-sun4i.c
index 15999551ff7ffb74641d35aab7d8f3cd30676172..6fcef4a95a18af9462431a1d1ef39cd0f100aaa2 100644 (file)
@@ -41,13 +41,11 @@ static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs);
 static void sun4i_irq_ack(struct irq_data *irqd)
 {
        unsigned int irq = irqd_to_hwirq(irqd);
-       unsigned int irq_off = irq % 32;
-       int reg = irq / 32;
-       u32 val;
 
-       val = readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg));
-       writel(val | (1 << irq_off),
-              sun4i_irq_base + SUN4I_IRQ_PENDING_REG(reg));
+       if (irq != 0)
+               return; /* Only IRQ 0 / the ENMI needs to be acked */
+
+       writel(BIT(0), sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0));
 }
 
 static void sun4i_irq_mask(struct irq_data *irqd)
@@ -76,16 +74,16 @@ static void sun4i_irq_unmask(struct irq_data *irqd)
 
 static struct irq_chip sun4i_irq_chip = {
        .name           = "sun4i_irq",
-       .irq_ack        = sun4i_irq_ack,
+       .irq_eoi        = sun4i_irq_ack,
        .irq_mask       = sun4i_irq_mask,
        .irq_unmask     = sun4i_irq_unmask,
+       .flags          = IRQCHIP_EOI_THREADED | IRQCHIP_EOI_IF_HANDLED,
 };
 
 static int sun4i_irq_map(struct irq_domain *d, unsigned int virq,
                         irq_hw_number_t hw)
 {
-       irq_set_chip_and_handler(virq, &sun4i_irq_chip,
-                                handle_level_irq);
+       irq_set_chip_and_handler(virq, &sun4i_irq_chip, handle_fasteoi_irq);
        set_irq_flags(virq, IRQF_VALID | IRQF_PROBE);
 
        return 0;
@@ -109,7 +107,7 @@ static int __init sun4i_of_init(struct device_node *node,
        writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(1));
        writel(0, sun4i_irq_base + SUN4I_IRQ_ENABLE_REG(2));
 
-       /* Mask all the interrupts */
+       /* Unmask all the interrupts, ENABLE_REG(x) is used for masking */
        writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(0));
        writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(1));
        writel(0, sun4i_irq_base + SUN4I_IRQ_MASK_REG(2));
@@ -140,10 +138,24 @@ static void __exception_irq_entry sun4i_handle_irq(struct pt_regs *regs)
 {
        u32 irq, hwirq;
 
+       /*
+        * hwirq == 0 can mean one of 3 things:
+        * 1) no more irqs pending
+        * 2) irq 0 pending
+        * 3) spurious irq
+        * So if we immediately get a reading of 0, check the irq-pending reg
+        * to differentiate between 2 and 3. We only do this once to avoid
+        * the extra check in the common case of 1 hapening after having
+        * read the vector-reg once.
+        */
        hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2;
-       while (hwirq != 0) {
+       if (hwirq == 0 &&
+                 !(readl(sun4i_irq_base + SUN4I_IRQ_PENDING_REG(0)) & BIT(0)))
+               return;
+
+       do {
                irq = irq_find_mapping(sun4i_irq_domain, hwirq);
                handle_IRQ(irq, regs);
                hwirq = readl(sun4i_irq_base + SUN4I_IRQ_VECTOR_REG) >> 2;
-       }
+       } while (hwirq != 0);
 }