aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorPaul Burton <paul.burton@mips.com>2019-06-05 04:34:10 -0400
committerMarc Zyngier <marc.zyngier@arm.com>2019-06-05 04:35:31 -0400
commit6d4d367d0e9ffab4d64a3436256a6a052dc1195d (patch)
tree74e6a488ef7f8c544b90a8e5185679a62530dc57
parenteb737b8f446044df327b30f24416be0cae35d4aa (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.h30
-rw-r--r--drivers/irqchip/irq-mips-gic.c4
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 */
332static inline unsigned int
333mips_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