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:a0dc5cb5e3
("irqchip: mips-gic: Simplify gic_local_irq_domain_map()") Fixes:da61fcf9d6
("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>
This commit is contained in:
parent
eb737b8f44
commit
6d4d367d0e
|
@ -314,6 +314,36 @@ static inline bool mips_gic_present(void)
|
|||
return IS_ENABLED(CONFIG_MIPS_GIC) && mips_gic_base;
|
||||
}
|
||||
|
||||
/**
|
||||
* mips_gic_vx_map_reg() - Return GIC_Vx_<intr>_MAP register offset
|
||||
* @intr: A GIC local interrupt
|
||||
*
|
||||
* Determine the index of the GIC_VL_<intr>_MAP or GIC_VO_<intr>_MAP register
|
||||
* within the block of GIC map registers. This is almost the same as the order
|
||||
* of interrupts in the pending & mask registers, as used by enum
|
||||
* mips_gic_local_interrupt, but moves the FDC interrupt & thus offsets the
|
||||
* interrupts after it...
|
||||
*
|
||||
* Return: The map register index corresponding to @intr.
|
||||
*
|
||||
* The return value is suitable for use with the (read|write)_gic_v[lo]_map
|
||||
* accessor functions.
|
||||
*/
|
||||
static inline unsigned int
|
||||
mips_gic_vx_map_reg(enum mips_gic_local_interrupt intr)
|
||||
{
|
||||
/* WD, Compare & Timer are 1:1 */
|
||||
if (intr <= GIC_LOCAL_INT_TIMER)
|
||||
return intr;
|
||||
|
||||
/* FDC moves to after Timer... */
|
||||
if (intr == GIC_LOCAL_INT_FDC)
|
||||
return GIC_LOCAL_INT_TIMER + 1;
|
||||
|
||||
/* As a result everything else is offset by 1 */
|
||||
return intr + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* gic_get_c0_compare_int() - Return cp0 count/compare interrupt virq
|
||||
*
|
||||
|
|
|
@ -388,7 +388,7 @@ static void gic_all_vpes_irq_cpu_online(struct irq_data *d)
|
|||
intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
|
||||
cd = irq_data_get_irq_chip_data(d);
|
||||
|
||||
write_gic_vl_map(intr, cd->map);
|
||||
write_gic_vl_map(mips_gic_vx_map_reg(intr), cd->map);
|
||||
if (cd->mask)
|
||||
write_gic_vl_smask(BIT(intr));
|
||||
}
|
||||
|
@ -517,7 +517,7 @@ static int gic_irq_domain_map(struct irq_domain *d, unsigned int virq,
|
|||
spin_lock_irqsave(&gic_lock, flags);
|
||||
for_each_online_cpu(cpu) {
|
||||
write_gic_vl_other(mips_cm_vp_id(cpu));
|
||||
write_gic_vo_map(intr, map);
|
||||
write_gic_vo_map(mips_gic_vx_map_reg(intr), map);
|
||||
}
|
||||
spin_unlock_irqrestore(&gic_lock, flags);
|
||||
|
||||
|
|
Loading…
Reference in New Issue