Skip to content

Commit a1706a1

Browse files
smaeulMarc Zyngier
authored andcommitted
irqchip/sifive-plic: Separate the enable and mask operations
The PLIC has two per-IRQ checks before sending an IRQ to a hart context. First, it checks that the IRQ's priority is nonzero. Then, it checks that the enable bit is set for that combination of IRQ and context. Currently, the PLIC driver sets both the priority value and the enable bit in its (un)mask operations. However, modifying the enable bit is problematic for two reasons: 1) The enable bits are packed, so changes are not atomic and require taking a spinlock. 2) The following requirement from the PLIC spec, which explains the racy (un)mask operations in plic_irq_eoi(): If the completion ID does not match an interrupt source that is currently enabled for the target, the completion is silently ignored. Both of these problems are solved by using the priority value to mask IRQs. Each IRQ has a separate priority register, so writing the priority value is atomic. And since the enable bit remains set while an IRQ is masked, the EOI operation works normally. The enable bits are still used to control the IRQ's affinity. Signed-off-by: Samuel Holland <samuel@sholland.org> Signed-off-by: Marc Zyngier <maz@kernel.org> Link: https://lore.kernel.org/r/20220701202440.59059-3-samuel@sholland.org
1 parent de07894 commit a1706a1

File tree

1 file changed

+34
-21
lines changed

1 file changed

+34
-21
lines changed

drivers/irqchip/irq-sifive-plic.c

Lines changed: 34 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -108,26 +108,45 @@ static inline void plic_irq_toggle(const struct cpumask *mask,
108108
struct irq_data *d, int enable)
109109
{
110110
int cpu;
111-
struct plic_priv *priv = irq_data_get_irq_chip_data(d);
112111

113-
writel(enable, priv->regs + PRIORITY_BASE + d->hwirq * PRIORITY_PER_ID);
114112
for_each_cpu(cpu, mask) {
115113
struct plic_handler *handler = per_cpu_ptr(&plic_handlers, cpu);
116114

117115
plic_toggle(handler, d->hwirq, enable);
118116
}
119117
}
120118

121-
static void plic_irq_unmask(struct irq_data *d)
119+
static void plic_irq_enable(struct irq_data *d)
122120
{
123121
plic_irq_toggle(irq_data_get_effective_affinity_mask(d), d, 1);
124122
}
125123

126-
static void plic_irq_mask(struct irq_data *d)
124+
static void plic_irq_disable(struct irq_data *d)
127125
{
128126
plic_irq_toggle(irq_data_get_effective_affinity_mask(d), d, 0);
129127
}
130128

129+
static void plic_irq_unmask(struct irq_data *d)
130+
{
131+
struct plic_priv *priv = irq_data_get_irq_chip_data(d);
132+
133+
writel(1, priv->regs + PRIORITY_BASE + d->hwirq * PRIORITY_PER_ID);
134+
}
135+
136+
static void plic_irq_mask(struct irq_data *d)
137+
{
138+
struct plic_priv *priv = irq_data_get_irq_chip_data(d);
139+
140+
writel(0, priv->regs + PRIORITY_BASE + d->hwirq * PRIORITY_PER_ID);
141+
}
142+
143+
static void plic_irq_eoi(struct irq_data *d)
144+
{
145+
struct plic_handler *handler = this_cpu_ptr(&plic_handlers);
146+
147+
writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM);
148+
}
149+
131150
#ifdef CONFIG_SMP
132151
static int plic_set_affinity(struct irq_data *d,
133152
const struct cpumask *mask_val, bool force)
@@ -146,32 +165,21 @@ static int plic_set_affinity(struct irq_data *d,
146165
if (cpu >= nr_cpu_ids)
147166
return -EINVAL;
148167

149-
plic_irq_mask(d);
168+
plic_irq_disable(d);
150169

151170
irq_data_update_effective_affinity(d, cpumask_of(cpu));
152171

153-
if (!irqd_irq_masked(d))
154-
plic_irq_unmask(d);
172+
if (!irqd_irq_disabled(d))
173+
plic_irq_enable(d);
155174

156175
return IRQ_SET_MASK_OK_DONE;
157176
}
158177
#endif
159178

160-
static void plic_irq_eoi(struct irq_data *d)
161-
{
162-
struct plic_handler *handler = this_cpu_ptr(&plic_handlers);
163-
164-
if (irqd_irq_masked(d)) {
165-
plic_irq_unmask(d);
166-
writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM);
167-
plic_irq_mask(d);
168-
} else {
169-
writel(d->hwirq, handler->hart_base + CONTEXT_CLAIM);
170-
}
171-
}
172-
173179
static struct irq_chip plic_edge_chip = {
174180
.name = "SiFive PLIC",
181+
.irq_enable = plic_irq_enable,
182+
.irq_disable = plic_irq_disable,
175183
.irq_ack = plic_irq_eoi,
176184
.irq_mask = plic_irq_mask,
177185
.irq_unmask = plic_irq_unmask,
@@ -184,6 +192,8 @@ static struct irq_chip plic_edge_chip = {
184192

185193
static struct irq_chip plic_chip = {
186194
.name = "SiFive PLIC",
195+
.irq_enable = plic_irq_enable,
196+
.irq_disable = plic_irq_disable,
187197
.irq_mask = plic_irq_mask,
188198
.irq_unmask = plic_irq_unmask,
189199
.irq_eoi = plic_irq_eoi,
@@ -429,8 +439,11 @@ static int __init __plic_init(struct device_node *node,
429439
i * CONTEXT_ENABLE_SIZE;
430440
handler->priv = priv;
431441
done:
432-
for (hwirq = 1; hwirq <= nr_irqs; hwirq++)
442+
for (hwirq = 1; hwirq <= nr_irqs; hwirq++) {
433443
plic_toggle(handler, hwirq, 0);
444+
writel(1, priv->regs + PRIORITY_BASE +
445+
hwirq * PRIORITY_PER_ID);
446+
}
434447
nr_handlers++;
435448
}
436449

0 commit comments

Comments
 (0)