Commit 1b6af71a authored by James Hogan's avatar James Hogan Committed by Ralf Baechle

IRQCHIP: mips-gic: Avoid rerouting timer IRQs for smp-cmp

Commit e9de688d ("irqchip: mips-gic: Support local interrupts")
changed the GIC irqchip driver so that all local interrupts were routed
to the same CPU pin used for external interrupts. Unfortunately this
causes a regression when smp-cmp is used. The CPUs are started by the
bootloader and put in a timer based waiting poll loop, but when their
timer interrupts are rerouted to a different IRQ pin which is not
unmasked they never wake up.

Since smp-cmp support is deprecated and everybody who was using it
should be switching to smp-cps which brings up the secondary CPUs
without bootloader assistance, I've gone for the simple fix which can be
easily removed once smp-cmp is removed, rather than a fully generic fix.

In __gic_init() the local GIC_VPE_TIMER_MAP register is read to find the
boot-time routing of the local timer interrupt, and a chained handler is
added to that CPU pin as well as the normal one.
Signed-off-by: default avatarJames Hogan <james.hogan@imgtec.com>
Fixes: e9de688d ("irqchip: mips-gic: Support local interrupts")
Cc: Andrew Bresticker <abrestic@chromium.org>
Cc: Qais Yousef <qais.yousef@imgtec.com>
Cc: Paul Burton <paul.burton@imgtec.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: linux-mips@linux-mips.org
Reviewed-by: default avatarAndrew Bresticker <abrestic@chromium.org>
Patchwork: https://patchwork.linux-mips.org/patch/9081/Signed-off-by: default avatarRalf Baechle <ralf@linux-mips.org>
parent c2d9f177
...@@ -37,6 +37,7 @@ static struct irq_domain *gic_irq_domain; ...@@ -37,6 +37,7 @@ static struct irq_domain *gic_irq_domain;
static int gic_shared_intrs; static int gic_shared_intrs;
static int gic_vpes; static int gic_vpes;
static unsigned int gic_cpu_pin; static unsigned int gic_cpu_pin;
static unsigned int timer_cpu_pin;
static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller; static struct irq_chip gic_level_irq_controller, gic_edge_irq_controller;
static void __gic_irq_dispatch(void); static void __gic_irq_dispatch(void);
...@@ -616,6 +617,8 @@ static int gic_local_irq_domain_map(struct irq_domain *d, unsigned int virq, ...@@ -616,6 +617,8 @@ static int gic_local_irq_domain_map(struct irq_domain *d, unsigned int virq,
gic_write(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_MAP), val); gic_write(GIC_REG(VPE_OTHER, GIC_VPE_COMPARE_MAP), val);
break; break;
case GIC_LOCAL_INT_TIMER: case GIC_LOCAL_INT_TIMER:
/* CONFIG_MIPS_CMP workaround (see __gic_init) */
val = GIC_MAP_TO_PIN_MSK | timer_cpu_pin;
gic_write(GIC_REG(VPE_OTHER, GIC_VPE_TIMER_MAP), val); gic_write(GIC_REG(VPE_OTHER, GIC_VPE_TIMER_MAP), val);
break; break;
case GIC_LOCAL_INT_PERFCTR: case GIC_LOCAL_INT_PERFCTR:
...@@ -713,12 +716,36 @@ static void __init __gic_init(unsigned long gic_base_addr, ...@@ -713,12 +716,36 @@ static void __init __gic_init(unsigned long gic_base_addr,
if (cpu_has_veic) { if (cpu_has_veic) {
/* Always use vector 1 in EIC mode */ /* Always use vector 1 in EIC mode */
gic_cpu_pin = 0; gic_cpu_pin = 0;
timer_cpu_pin = gic_cpu_pin;
set_vi_handler(gic_cpu_pin + GIC_PIN_TO_VEC_OFFSET, set_vi_handler(gic_cpu_pin + GIC_PIN_TO_VEC_OFFSET,
__gic_irq_dispatch); __gic_irq_dispatch);
} else { } else {
gic_cpu_pin = cpu_vec - GIC_CPU_PIN_OFFSET; gic_cpu_pin = cpu_vec - GIC_CPU_PIN_OFFSET;
irq_set_chained_handler(MIPS_CPU_IRQ_BASE + cpu_vec, irq_set_chained_handler(MIPS_CPU_IRQ_BASE + cpu_vec,
gic_irq_dispatch); gic_irq_dispatch);
/*
* With the CMP implementation of SMP (deprecated), other CPUs
* are started by the bootloader and put into a timer based
* waiting poll loop. We must not re-route those CPU's local
* timer interrupts as the wait instruction will never finish,
* so just handle whatever CPU interrupt it is routed to by
* default.
*
* This workaround should be removed when CMP support is
* dropped.
*/
if (IS_ENABLED(CONFIG_MIPS_CMP) &&
gic_local_irq_is_routable(GIC_LOCAL_INT_TIMER)) {
timer_cpu_pin = gic_read(GIC_REG(VPE_LOCAL,
GIC_VPE_TIMER_MAP)) &
GIC_MAP_MSK;
irq_set_chained_handler(MIPS_CPU_IRQ_BASE +
GIC_CPU_PIN_OFFSET +
timer_cpu_pin,
gic_irq_dispatch);
} else {
timer_cpu_pin = gic_cpu_pin;
}
} }
gic_irq_domain = irq_domain_add_simple(node, GIC_NUM_LOCAL_INTRS + gic_irq_domain = irq_domain_add_simple(node, GIC_NUM_LOCAL_INTRS +
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment