Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22 changes: 14 additions & 8 deletions drivers/irqchip/irq-sifive-plic.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,19 +157,25 @@ static void plic_irq_disable(struct irq_data *d)
static void plic_irq_eoi(struct irq_data *d)
{
struct plic_handler *handler = this_cpu_ptr(&plic_handlers);
u32 __iomem *reg;
bool enabled;

reg = handler->enable_base + (d->hwirq / 32) * sizeof(u32);
enabled = readl(reg) & BIT(d->hwirq % 32);
unsigned long flags;
void __iomem *reg = handler->enable_base + (d->hwirq / 32) * sizeof(u32);
u32 hwirq_mask = 1 << (d->hwirq % 32);

if (unlikely(!enabled)) {
plic_toggle(handler, d->hwirq, 1);
/*
* PLIC drops a completion unless the source is enabled for this hart; a
* dropped one leaves the interrupt stuck in-progress. Serialize against
* plic_set_affinity() clearing the enable bit by holding enable_lock
* across the EOI; re-enable around it if the bit was clear.
*/
raw_spin_lock_irqsave(&handler->enable_lock, flags);
if (unlikely(!(readl(reg) & hwirq_mask))) {
__plic_toggle(handler->enable_base, d->hwirq, 1);
writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM);
plic_toggle(handler, d->hwirq, 0);
__plic_toggle(handler->enable_base, d->hwirq, 0);
} else {
writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM);
}
raw_spin_unlock_irqrestore(&handler->enable_lock, flags);
}

#ifdef CONFIG_SMP
Expand Down
Loading