diff options
author | Paul Burton <paul.burton@mips.com> | 2019-06-05 04:34:10 -0400 |
---|---|---|
committer | Marc Zyngier <marc.zyngier@arm.com> | 2019-06-05 04:35:31 -0400 |
commit | 6d4d367d0e9ffab4d64a3436256a6a052dc1195d (patch) | |
tree | 74e6a488ef7f8c544b90a8e5185679a62530dc57 | |
parent | eb737b8f446044df327b30f24416be0cae35d4aa (diff) |
irqchip/mips-gic: Use the correct local interrupt map registers
The MIPS GIC contains a block of registers used to map local interrupts
to a particular CPU interrupt pin. Since these registers are found at a
consecutive range of addresses we access them using an index, via the
(read|write)_gic_v[lo]_map accessor functions. We currently use values
from enum mips_gic_local_interrupt as those indices.
Unfortunately whilst enum mips_gic_local_interrupt provides the correct
offsets for bits in the pending & mask registers, the ordering of the
map registers is subtly different... Compared with the ordering of
pending & mask bits, the map registers move the FDC from the end of the
list to index 3 after the timer interrupt. As a result the performance
counter & software interrupts are therefore at indices 4-6 rather than
indices 3-5.
Notably this causes problems with performance counter interrupts being
incorrectly mapped on some systems, and presumably will also cause
problems for FDC interrupts.
Introduce a function to map from enum mips_gic_local_interrupt to the
index of the corresponding map register, and use it to ensure we access
the map registers for the correct interrupts.
Signed-off-by: Paul Burton <paul.burton@mips.com>
Fixes: a0dc5cb5e31b ("irqchip: mips-gic: Simplify gic_local_irq_domain_map()")
Fixes: da61fcf9d62a ("irqchip: mips-gic: Use irq_cpu_online to (un)mask all-VP(E) IRQs")
Reported-and-tested-by: Archer Yan <ayan@wavecomp.com>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: stable@vger.kernel.org # v4.14+
Signed-off-by: Marc Zyngier <marc.zyngier@arm.com>
-rw-r--r-- | arch/mips/include/asm/mips-gic.h | 30 | ||||
-rw-r--r-- | drivers/irqchip/irq-mips-gic.c | 4 |
2 files changed, 32 insertions, 2 deletions
diff --git a/arch/mips/include/asm/mips-gic.h b/arch/mips/include/asm/mips-gic.h index 558059a8f218..0277b56157af 100644 --- a/arch/mips/include/asm/mips-gic.h +++ b/arch/mips/include/asm/mips-gic.h | |||
@@ -315,6 +315,36 @@ static inline bool mips_gic_present(void) | |||
315 | } | 315 | } |
316 | 316 | ||
317 | /** | 317 | /** |
318 | * mips_gic_vx_map_reg() - Return GIC_Vx_<intr>_MAP register offset | ||
319 | * @intr: A GIC local interrupt | ||
320 | * | ||
321 | * Determine the index of the GIC_VL_<intr>_MAP or GIC_VO_<intr>_MAP register | ||
322 | * within the block of GIC map registers. This is almost the same as the order | ||
323 | * of interrupts in the pending & mask registers, as used by enum | ||
324 | * mips_gic_local_interrupt, but moves the FDC interrupt & thus offsets the | ||
325 | * interrupts after it... | ||
326 | * | ||
327 | * Return: The map register index corresponding to @intr. | ||
328 | * | ||
329 | * The return value is suitable for use with the (read|write)_gic_v[lo]_map | ||
330 | * accessor functions. | ||
331 | */ | ||
332 | static inline unsigned int | ||
333 | mips_gic_vx_map_reg(enum mips_gic_local_interrupt intr) | ||
334 | { | ||
335 | /* WD, Compare & Timer are 1:1 */ | ||
336 | if (intr <= GIC_LOCAL_INT_TIMER) | ||
337 | return intr; | ||
338 | |||
339 | /* FDC moves to after Timer... */ | ||
340 | if (intr == GIC_LOCAL_INT_FDC) | ||
341 | return GIC_LOCAL_INT_TIMER + 1; | ||
342 | |||
343 | /* As a result everything else is offset by 1 */ | ||
344 | return intr + 1; | ||
345 | } | ||
346 | |||
347 | /** | ||
318 | * gic_get_c0_compare_int() - Return cp0 count/compare interrupt virq | 348 | * gic_get_c0_compare_int() - Return cp0 count/compare interrupt virq |
319 | * | 349 | * |
320 | * Determine the virq number to use for the coprocessor 0 count/compare | 350 | * Determine the virq number to use for the coprocessor 0 count/compare |
diff --git a/drivers/irqchip/irq-mips-gic.c b/drivers/irqchip/irq-mips-gic.c index d32268cc1174..f3985469c221 100644 --- a/drivers/irqchip/irq-mips-gic.c +++ b/drivers/irqchip/irq-mips-gic.c | |||
@@ -388,7 +388,7 @@ static void gic_all_vpes_irq_cpu_online(struct irq_data *d) | |||
388 | intr = GIC_HWIRQ_TO_LOCAL(d->hwirq); | 388 | intr = GIC_HWIRQ_TO_LOCAL(d->hwirq); |
389 | cd = irq_data_get_irq_chip_data(d); | 389 | cd = irq_data_get_irq_chip_data(d); |
390 | 390 | ||
391 | write_gic_vl_map(intr, cd->map); | 391 | write_gic_vl_map(mips_gic_vx_map_reg(intr), cd->map); |
392 | if (cd->mask) | 392 | if (cd->mask) |
393 | write_gic_vl_smask(BIT(intr)); | 393 | write_gic_vl_smask(BIT(intr)); |
394 | } | 394 | } |
@@ -517,7 +517,7 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq, | |||
517 | spin_lock_irqsave(&gic_lock, flags); | 517 | spin_lock_irqsave(&gic_lock, flags); |
518 | for_each_online_cpu(cpu) { | 518 | for_each_online_cpu(cpu) { |
519 | write_gic_vl_other(mips_cm_vp_id(cpu)); | 519 | write_gic_vl_other(mips_cm_vp_id(cpu)); |
520 | write_gic_vo_map(intr, map); | 520 | write_gic_vo_map(mips_gic_vx_map_reg(intr), map); |
521 | } | 521 | } |
522 | spin_unlock_irqrestore(&gic_lock, flags); | 522 | spin_unlock_irqrestore(&gic_lock, flags); |
523 | 523 | ||