@@ -240,8 +240,6 @@ default_enable(irq)
default_disable(irq)
{
- if (!delay_disable(irq))
- desc->chip->mask(irq);
}
default_ack(irq)
@@ -264,7 +262,11 @@ noop(irq)
}
</programlisting>
- </para>
+ Note: default_disable() is empty so that an edge-triggered
+ interrupt happening between a disable_irq() and an enable_irq()
+ is caught by the kernel for later replay. See
+ <xref linkend="Delayed_interrupt_disable"/> for details.
+ </para>
</sect3>
</sect2>
<sect2 id="Default_flow_handler_implementations">
@@ -278,9 +280,14 @@ noop(irq)
<para>
The following control flow is implemented (simplified excerpt):
<programlisting>
-desc->chip->start();
+desc->chip->mask_ack();
+if (desc->status & inprogress)
+ return;
+desc->status |= inprogress;
handle_IRQ_event(desc->action);
-desc->chip->end();
+desc->status &= inprogress;
+if (!(desc->status & disabled))
+ desc->chip->unmask_ack();
</programlisting>
</para>
</sect3>
@@ -293,21 +300,23 @@ desc->chip->end();
<para>
The following control flow is implemented (simplified excerpt):
<programlisting>
-if (desc->status & running) {
- desc->chip->hold();
+if (desc->status & (inprogress | disabled)) {
desc->status |= pending | masked;
+ desc->chip->mask_ack();
return;
}
-desc->chip->start();
-desc->status |= running;
+desc->chip->ack();
+desc->status |= inprogress;
do {
- if (desc->status & masked)
- desc->chip->enable();
+ if ((desc->status & (pending | masked | disabled))
+ == (pending | masked)) {
+ desc->chip->unmask();
+ desc->status &= ~masked;
+ }
desc->status &= ~pending;
handle_IRQ_event(desc->action);
-} while (status & pending);
-desc->status &= ~running;
-desc->chip->end();
+} while ((status & (pending | disabled)) == pending);
+desc->status &= inprogress;
</programlisting>
</para>
</sect3>
@@ -342,9 +351,9 @@ handle_IRQ_event(desc->action);
<para>
The following control flow is implemented (simplified excerpt):
<programlisting>
-desc->chip->start();
+desc->chip->ack();
handle_IRQ_event(desc->action);
-desc->chip->end();
+desc->chip->eoi();
</programlisting>
</para>
</sect3>
@@ -361,22 +370,20 @@ desc->chip->end();
<sect2 id="Delayed_interrupt_disable">
<title>Delayed interrupt disable</title>
<para>
- This per interrupt selectable feature, which was introduced by Russell
- King in the ARM interrupt implementation, does not mask an interrupt
- at the hardware level when disable_irq() is called. The interrupt is
- kept enabled and is masked in the flow handler when an interrupt event
- happens. This prevents losing edge interrupts on hardware which does
- not store an edge interrupt event while the interrupt is disabled at
- the hardware level. When an interrupt arrives while the IRQ_DISABLED
- flag is set, then the interrupt is masked at the hardware level and
- the IRQ_PENDING bit is set. When the interrupt is re-enabled by
- enable_irq() the pending bit is checked and if it is set, the
- interrupt is resent either via hardware or by a software resend
- mechanism. (It's necessary to enable CONFIG_HARDIRQS_SW_RESEND when
- you want to use the delayed interrupt disable feature and your
- hardware is not capable of retriggering an interrupt.)
- The delayed interrupt disable can be runtime enabled, per interrupt,
- by setting the IRQ_DELAYED_DISABLE flag in the irq_desc status field.
+ At least when using default_disable as the ->disable() callback of a
+ chip handler, an interrupt is not masked at the hardware level when
+ disable_irq() is called. The interrupt is kept enabled and is masked
+ in the flow handler when an interrupt event happens. This prevents
+ losing edge interrupts on hardware which does not store an edge
+ interrupt event while the interrupt is disabled at the hardware level.
+ This makes use of the replay mechanism that is mandatory anyway to
+ prevent potential race conditions between an interrupt event and a call
+ to disable_irq() at the same time. When an interrupt arrives in
+ handle_edge_irq() while the IRQ_DISABLED flag is set, then the
+ interrupt is masked at the hardware level and the IRQ_PENDING bit is
+ set. When the interrupt is re-enabled by enable_irq() the pending bit
+ is checked and if it is set, the interrupt is resent either via
+ hardware or by a software resend mechanism.
</para>
</sect2>
</sect1>
@@ -387,6 +394,8 @@ desc->chip->end();
contains all the direct chip relevant functions, which
can be utilized by the irq flow implementations.
<itemizedlist>
+ <listitem><para>disable() - Optional</para></listitem>
+ <listitem><para>enable() - Optional</para></listitem>
<listitem><para>ack()</para></listitem>
<listitem><para>mask_ack() - Optional, recommended for performance</para></listitem>
<listitem><para>mask()</para></listitem>
Following "genirq: always build resend_irqs" and previous changes, this commit updates the genirq DocBook. Signed-off-by: Guillaume Knispel <gknispel@proformatique.com> CC: linux-kernel@vger.kernel.org CC: Linuxppc-dev@lists.ozlabs.org CC: Bartlomiej Zolnierkiewicz <bzolnier@gmail.com> CC: Benjamin Herrenschmidt <benh@kernel.crashing.org> CC: Haavard Skinnemoen <hskinnemoen@atmel.com> CC: Ingo Molnar <mingo@elte.hu> CC: Lars-Peter Clausen <lars@metafoo.de> CC: Linus Torvalds <torvalds@linux-foundation.org> CC: Randy Dunlap <randy.dunlap@oracle.com> CC: Peter Zijlstra <peterz@infradead.org> CC: Russell King <linux@arm.linux.org.uk> CC: Thomas Gleixner <tglx@linutronix.de> --- Documentation/DocBook/genericirq.tmpl | 73 ++++++++++++++++++-------------- 1 files changed, 41 insertions(+), 32 deletions(-)