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:
Родитель
eb737b8f44
Коммит
6d4d367d0e
|
@ -314,6 +314,36 @@ static inline bool mips_gic_present(void)
|
||||||
return IS_ENABLED(CONFIG_MIPS_GIC) && mips_gic_base;
|
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
|
* 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);
|
intr = GIC_HWIRQ_TO_LOCAL(d->hwirq);
|
||||||
cd = irq_data_get_irq_chip_data(d);
|
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)
|
if (cd->mask)
|
||||||
write_gic_vl_smask(BIT(intr));
|
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);
|
spin_lock_irqsave(&gic_lock, flags);
|
||||||
for_each_online_cpu(cpu) {
|
for_each_online_cpu(cpu) {
|
||||||
write_gic_vl_other(mips_cm_vp_id(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);
|
spin_unlock_irqrestore(&gic_lock, flags);
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче