genirq: Add a new IRQCHIP_EOI_THREADED flag
authorThomas Gleixner <tglx@linutronix.de>
Thu, 13 Mar 2014 18:03:51 +0000 (19:03 +0100)
committerThomas Gleixner <tglx@linutronix.de>
Fri, 14 Mar 2014 12:43:33 +0000 (13:43 +0100)
The flag is necessary for interrupt chips which require an ACK/EOI
after the handler has run. In case of threaded handlers this needs to
happen after the threaded handler has completed before the unmask of
the interrupt.

The flag is only unseful in combination with the handle_fasteoi_irq
flow control handler.

It can be combined with the flag IRQCHIP_EOI_IF_HANDLED, so the EOI is
not issued when the interrupt is disabled or in progress.

Tested-by: Hans de Goede <hdegoede@redhat.com>
Reviewed-by: Hans de Goede <hdegoede@redhat.com>
Cc: linux-arm-kernel@lists.infradead.org
Cc: linux-sunxi@googlegroups.com
Cc: Maxime Ripard <maxime.ripard@free-electrons.com>
Link: http://lkml.kernel.org/r/1394733834-26839-2-git-send-email-hdegoede@redhat.com
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
include/linux/irq.h
kernel/irq/chip.c
kernel/irq/internals.h
kernel/irq/manage.c

index 67ace7aa7947e2656c3175de4a22bca2f28df721..d278838908cbc3f1cdac08ae3f3714934f01f188 100644 (file)
@@ -356,6 +356,7 @@ struct irq_chip {
  *                             when irq enabled
  * IRQCHIP_SKIP_SET_WAKE:      Skip chip.irq_set_wake(), for this irq chip
  * IRQCHIP_ONESHOT_SAFE:       One shot does not require mask/unmask
+ * IRQCHIP_EOI_THREADED:       Chip requires eoi() on unmask in threaded mode
  */
 enum {
        IRQCHIP_SET_TYPE_MASKED         = (1 <<  0),
@@ -364,6 +365,7 @@ enum {
        IRQCHIP_ONOFFLINE_ENABLED       = (1 <<  3),
        IRQCHIP_SKIP_SET_WAKE           = (1 <<  4),
        IRQCHIP_ONESHOT_SAFE            = (1 <<  5),
+       IRQCHIP_EOI_THREADED            = (1 <<  6),
 };
 
 /* This include will go away once we isolated irq_desc usage to core code */
index dc04c166c54d7bc8e86ab87bec4075a3cc492e12..6397df2d6945b09ae853c1a75e4d18aa6da7096f 100644 (file)
@@ -281,6 +281,19 @@ void unmask_irq(struct irq_desc *desc)
        }
 }
 
+void unmask_threaded_irq(struct irq_desc *desc)
+{
+       struct irq_chip *chip = desc->irq_data.chip;
+
+       if (chip->flags & IRQCHIP_EOI_THREADED)
+               chip->irq_eoi(&desc->irq_data);
+
+       if (chip->irq_unmask) {
+               chip->irq_unmask(&desc->irq_data);
+               irq_state_clr_masked(desc);
+       }
+}
+
 /*
  *     handle_nested_irq - Handle a nested irq from a irq thread
  *     @irq:   the interrupt number
@@ -435,6 +448,27 @@ static inline void preflow_handler(struct irq_desc *desc)
 static inline void preflow_handler(struct irq_desc *desc) { }
 #endif
 
+static void cond_unmask_eoi_irq(struct irq_desc *desc, struct irq_chip *chip)
+{
+       if (!(desc->istate & IRQS_ONESHOT)) {
+               chip->irq_eoi(&desc->irq_data);
+               return;
+       }
+       /*
+        * We need to unmask in the following cases:
+        * - Oneshot irq which did not wake the thread (caused by a
+        *   spurious interrupt or a primary handler handling it
+        *   completely).
+        */
+       if (!irqd_irq_disabled(&desc->irq_data) &&
+           irqd_irq_masked(&desc->irq_data) && !desc->threads_oneshot) {
+               chip->irq_eoi(&desc->irq_data);
+               unmask_irq(desc);
+       } else if (!(chip->flags & IRQCHIP_EOI_THREADED)) {
+               chip->irq_eoi(&desc->irq_data);
+       }
+}
+
 /**
  *     handle_fasteoi_irq - irq handler for transparent controllers
  *     @irq:   the interrupt number
@@ -448,6 +482,8 @@ static inline void preflow_handler(struct irq_desc *desc) { }
 void
 handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
 {
+       struct irq_chip *chip = desc->irq_data.chip;
+
        raw_spin_lock(&desc->lock);
 
        if (unlikely(irqd_irq_inprogress(&desc->irq_data)))
@@ -473,18 +509,14 @@ handle_fasteoi_irq(unsigned int irq, struct irq_desc *desc)
        preflow_handler(desc);
        handle_irq_event(desc);
 
-       if (desc->istate & IRQS_ONESHOT)
-               cond_unmask_irq(desc);
+       cond_unmask_eoi_irq(desc, chip);
 
-out_eoi:
-       desc->irq_data.chip->irq_eoi(&desc->irq_data);
-out_unlock:
        raw_spin_unlock(&desc->lock);
        return;
 out:
-       if (!(desc->irq_data.chip->flags & IRQCHIP_EOI_IF_HANDLED))
-               goto out_eoi;
-       goto out_unlock;
+       if (!(chip->flags & IRQCHIP_EOI_IF_HANDLED))
+               chip->irq_eoi(&desc->irq_data);
+       raw_spin_unlock(&desc->lock);
 }
 
 /**
index 17b671713d5fe885408b05076620e133bea192f6..ddf1ffeb79f1e3ac5a5408465d4f31391525de13 100644 (file)
@@ -74,6 +74,7 @@ extern void irq_percpu_enable(struct irq_desc *desc, unsigned int cpu);
 extern void irq_percpu_disable(struct irq_desc *desc, unsigned int cpu);
 extern void mask_irq(struct irq_desc *desc);
 extern void unmask_irq(struct irq_desc *desc);
+extern void unmask_threaded_irq(struct irq_desc *desc);
 
 extern void init_kstat_irqs(struct irq_desc *desc, int node, int nr);
 
index de1a8ed29b400ad8ad9eef13188b30da846b3808..2486a4c1a710ba057c7f884faae19bff1fc6d31c 100644 (file)
@@ -748,7 +748,7 @@ again:
 
        if (!desc->threads_oneshot && !irqd_irq_disabled(&desc->irq_data) &&
            irqd_irq_masked(&desc->irq_data))
-               unmask_irq(desc);
+               unmask_threaded_irq(desc);
 
 out_unlock:
        raw_spin_unlock_irq(&desc->lock);