Skip to content

Commit

Permalink
irqchip/exiu: Fix acknowledgment of edge triggered interrupts
Browse files Browse the repository at this point in the history
Currently the EXIU uses the fasteoi interrupt flow that is configured by
it's parent (irq-gic-v3.c). With this flow the only chance to clear the
interrupt request happens during .irq_eoi() and (obviously) this happens
after the interrupt handler has run. EXIU requires edge triggered
interrupts to be acked prior to interrupt handling. Without this we
risk incorrect interrupt dismissal when a new interrupt is delivered
after the handler reads and acknowledges the peripheral but before the
irq_eoi() takes place.

Fix this by clearing the interrupt request from .irq_ack() if we are
configured for edge triggered interrupts. This requires adopting the
fasteoi-ack flow instead of the fasteoi to ensure the ack gets called.

These changes have been tested using the power button on a
Developerbox/SC2A11 combined with some hackery in gpio-keys so I can
play with the different trigger mode [and an mdelay(500) so I can
can check what happens on a double click in both modes].

Fixes: 706cffc ("irqchip/exiu: Add support for Socionext Synquacer EXIU controller")
Signed-off-by: Daniel Thompson <daniel.thompson@linaro.org>
Reviewed-by: Ard Biesheuvel <ardb@kernel.org>
Signed-off-by: Marc Zyngier <maz@kernel.org>
Link: https://lore.kernel.org/r/20220503134541.2566457-1-daniel.thompson@linaro.org
  • Loading branch information
daniel-thompson authored and Marc Zyngier committed May 4, 2022
1 parent b2d229d commit 4efc851
Show file tree
Hide file tree
Showing 2 changed files with 23 additions and 3 deletions.
1 change: 1 addition & 0 deletions arch/arm64/Kconfig.platforms
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,7 @@ config ARCH_INTEL_SOCFPGA

config ARCH_SYNQUACER
bool "Socionext SynQuacer SoC Family"
select IRQ_FASTEOI_HIERARCHY_HANDLERS

config ARCH_TEGRA
bool "NVIDIA Tegra SoC Family"
Expand Down
25 changes: 22 additions & 3 deletions drivers/irqchip/irq-sni-exiu.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,11 +37,26 @@ struct exiu_irq_data {
u32 spi_base;
};

static void exiu_irq_eoi(struct irq_data *d)
static void exiu_irq_ack(struct irq_data *d)
{
struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);

writel(BIT(d->hwirq), data->base + EIREQCLR);
}

static void exiu_irq_eoi(struct irq_data *d)
{
struct exiu_irq_data *data = irq_data_get_irq_chip_data(d);

/*
* Level triggered interrupts are latched and must be cleared during
* EOI or the interrupt will be jammed on. Of course if a level
* triggered interrupt is still asserted then the write will not clear
* the interrupt.
*/
if (irqd_is_level_type(d))
writel(BIT(d->hwirq), data->base + EIREQCLR);

irq_chip_eoi_parent(d);
}

Expand Down Expand Up @@ -91,10 +106,13 @@ static int exiu_irq_set_type(struct irq_data *d, unsigned int type)
writel_relaxed(val, data->base + EILVL);

val = readl_relaxed(data->base + EIEDG);
if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH)
if (type == IRQ_TYPE_LEVEL_LOW || type == IRQ_TYPE_LEVEL_HIGH) {
val &= ~BIT(d->hwirq);
else
irq_set_handler_locked(d, handle_fasteoi_irq);
} else {
val |= BIT(d->hwirq);
irq_set_handler_locked(d, handle_fasteoi_ack_irq);
}
writel_relaxed(val, data->base + EIEDG);

writel_relaxed(BIT(d->hwirq), data->base + EIREQCLR);
Expand All @@ -104,6 +122,7 @@ static int exiu_irq_set_type(struct irq_data *d, unsigned int type)

static struct irq_chip exiu_irq_chip = {
.name = "EXIU",
.irq_ack = exiu_irq_ack,
.irq_eoi = exiu_irq_eoi,
.irq_enable = exiu_irq_enable,
.irq_mask = exiu_irq_mask,
Expand Down

0 comments on commit 4efc851

Please sign in to comment.