[PATCH v5 10/10] MIPS: smp-mt: Rework IPI functions

From: Jiaxun Yang
Date: Sun Sep 08 2024 - 06:22:47 EST


Move smp IRQ code from irq-mips-cpu to smp-mt as IPI is
not really relavant to CPU intc. In VEIC mode we can have
irq-mips-cpu not registered and SW interrupts comes from
EIC controllers.

Implement IPI with ipi-mux to allow easy extension to number
of IPIs.

Tested-by: Serge Semin <fancer.lancer@xxxxxxxxx>
Signed-off-by: Jiaxun Yang <jiaxun.yang@xxxxxxxxxxx>
---
arch/mips/Kconfig | 2 +
arch/mips/kernel/smp-mt.c | 70 +++++++++++++++++++++++
drivers/irqchip/Kconfig | 1 -
drivers/irqchip/irq-mips-cpu.c | 124 +----------------------------------------
4 files changed, 74 insertions(+), 123 deletions(-)

diff --git a/arch/mips/Kconfig b/arch/mips/Kconfig
index 43da6d596e2b..08ef79093916 100644
--- a/arch/mips/Kconfig
+++ b/arch/mips/Kconfig
@@ -2189,6 +2189,8 @@ config MIPS_MT_SMP
depends on SYS_SUPPORTS_MULTITHREADING && !CPU_MICROMIPS
select CPU_MIPSR2_IRQ_VI
select CPU_MIPSR2_IRQ_EI
+ select GENERIC_IRQ_IPI
+ select GENERIC_IRQ_IPI_MUX
select SYNC_R4K
select MIPS_MT
select SMP
diff --git a/arch/mips/kernel/smp-mt.c b/arch/mips/kernel/smp-mt.c
index 7729cc733421..2f00077dbf07 100644
--- a/arch/mips/kernel/smp-mt.c
+++ b/arch/mips/kernel/smp-mt.c
@@ -10,6 +10,10 @@
#include <linux/sched.h>
#include <linux/cpumask.h>
#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqchip/chained_irq.h>
+#include <linux/irqdomain.h>
#include <linux/compiler.h>
#include <linux/sched/task_stack.h>
#include <linux/smp.h>
@@ -19,6 +23,7 @@
#include <asm/cpu.h>
#include <asm/processor.h>
#include <asm/hardirq.h>
+#include <asm/ipi.h>
#include <asm/mmu_context.h>
#include <asm/time.h>
#include <asm/mipsregs.h>
@@ -26,6 +31,65 @@
#include <asm/mips_mt.h>
#include <asm/mips-cps.h>

+static int vsmp_sw0_virq __ro_after_init;
+
+static void smvp_handle_ipi_irq(struct irq_desc *desc)
+{
+ struct irq_chip *chip = irq_desc_get_chip(desc);
+
+ chained_irq_enter(chip, desc);
+
+ /* irq-mips-cpu would ack for us, but EIC drivers won't */
+ if (cpu_has_veic) {
+ unsigned int vpflags = dvpe();
+
+ clear_c0_cause(C_SW0);
+ evpe(vpflags);
+ }
+ ipi_mux_process();
+
+ chained_irq_exit(chip, desc);
+}
+
+static void smvp_ipi_send(unsigned int cpu)
+{
+ unsigned long flags;
+ int vpflags;
+
+ local_irq_save(flags);
+
+ /* We can only send IPIs to VPEs within the local core */
+ WARN_ON(!cpus_are_siblings(smp_processor_id(), cpu));
+ vpflags = dvpe();
+ settc(cpu_vpe_id(&cpu_data[cpu]));
+ write_vpe_c0_status(read_vpe_c0_status() | C_SW0);
+ write_vpe_c0_cause(read_vpe_c0_cause() | C_SW0);
+ evpe(vpflags);
+
+ local_irq_restore(flags);
+}
+
+static int __init vsmp_ipi_init(void)
+{
+ int sw0_virq, mux_virq;
+
+ /* SW0 Interrupt for IPI */
+ sw0_virq = get_mips_sw_int(0);
+ if (!sw0_virq)
+ return -EINVAL;
+
+ mux_virq = ipi_mux_create(IPI_MAX, smvp_ipi_send);
+ if (!mux_virq)
+ return -EINVAL;
+
+ irq_set_percpu_devid(sw0_virq);
+ irq_set_chained_handler(sw0_virq, smvp_handle_ipi_irq);
+ mips_smp_ipi_set_virq_range(mux_virq, IPI_MAX);
+ vsmp_sw0_virq = sw0_virq;
+
+ return 0;
+}
+
static void __init smvp_copy_vpe_config(void)
{
write_vpe_c0_status(
@@ -123,6 +187,8 @@ static void vsmp_smp_finish(void)
/* CDFIXME: remove this? */
write_c0_compare(read_c0_count() + (8* mips_hpt_frequency/HZ));

+ enable_percpu_irq(vsmp_sw0_virq, IRQ_TYPE_NONE);
+
#ifdef CONFIG_MIPS_MT_FPAFF
/* If we have an FPU, enroll ourselves in the FPU-full mask */
if (cpu_has_fpu)
@@ -226,7 +292,11 @@ static void __init vsmp_smp_setup(void)

static void __init vsmp_prepare_cpus(unsigned int max_cpus)
{
+ int rc;
+
mips_mt_set_cpuoptions();
+ rc = vsmp_ipi_init();
+ WARN_ON(rc);
}

const struct plat_smp_ops vsmp_smp_ops = {
diff --git a/drivers/irqchip/Kconfig b/drivers/irqchip/Kconfig
index 763070be0088..786ed8a6b719 100644
--- a/drivers/irqchip/Kconfig
+++ b/drivers/irqchip/Kconfig
@@ -192,7 +192,6 @@ config MADERA_IRQ
config IRQ_MIPS_CPU
bool
select GENERIC_IRQ_CHIP
- select GENERIC_IRQ_IPI if SMP && SYS_SUPPORTS_MULTITHREADING
select IRQ_DOMAIN
select GENERIC_IRQ_EFFECTIVE_AFF_MASK if SMP

diff --git a/drivers/irqchip/irq-mips-cpu.c b/drivers/irqchip/irq-mips-cpu.c
index b2318e915d88..ec6999b5e73f 100644
--- a/drivers/irqchip/irq-mips-cpu.c
+++ b/drivers/irqchip/irq-mips-cpu.c
@@ -34,8 +34,7 @@
#include <asm/mipsmtregs.h>
#include <asm/setup.h>

-static struct irq_domain *irq_domain;
-static struct irq_domain *ipi_domain;
+static struct irq_domain *irq_domain __read_mostly;

static inline void unmask_mips_irq(struct irq_data *d)
{
@@ -108,37 +107,12 @@ static void mips_mt_sw_irq_ack(struct irq_data *d)
evpe(vpflags);
}

-#ifdef CONFIG_GENERIC_IRQ_IPI
-
-static void mips_mt_send_ipi(struct irq_data *d, unsigned int cpu)
-{
- irq_hw_number_t hwirq = irqd_to_hwirq(d);
- unsigned long flags;
- int vpflags;
-
- local_irq_save(flags);
-
- /* We can only send IPIs to VPEs within the local core */
- WARN_ON(!cpus_are_siblings(smp_processor_id(), cpu));
-
- vpflags = dvpe();
- settc(cpu_vpe_id(&cpu_data[cpu]));
- write_vpe_c0_cause(read_vpe_c0_cause() | (C_SW0 << hwirq));
- evpe(vpflags);
-
- local_irq_restore(flags);
-}
-
-#endif /* CONFIG_GENERIC_IRQ_IPI */
static const struct irq_chip mips_mt_cpu_sw_irq_controller = {
.name = "MIPS",
.irq_startup = mips_mt_sw_irq_startup,
.irq_ack = mips_mt_sw_irq_ack,
.irq_mask = mask_mips_irq,
.irq_unmask = unmask_mips_irq,
-#ifdef CONFIG_GENERIC_IRQ_IPI
- .ipi_send_single = mips_mt_send_ipi,
-#endif
};
#endif

@@ -154,15 +128,8 @@ asmlinkage void __weak plat_irq_dispatch(void)

pending >>= CAUSEB_IP;
while (pending) {
- struct irq_domain *d;
-
irq = fls(pending) - 1;
- if (IS_ENABLED(CONFIG_GENERIC_IRQ_IPI) && irq < 2)
- d = ipi_domain;
- else
- d = irq_domain;
-
- do_domain_IRQ(d, irq);
+ do_domain_IRQ(irq_domain, irq);
pending &= ~BIT(irq);
}
}
@@ -202,86 +169,6 @@ static const struct irq_domain_ops mips_cpu_intc_irq_domain_ops = {
.xlate = irq_domain_xlate_onecell,
};

-#ifdef CONFIG_GENERIC_IRQ_IPI
-
-struct cpu_ipi_domain_state {
- DECLARE_BITMAP(allocated, 2);
-};
-
-static int mips_cpu_ipi_alloc(struct irq_domain *domain, unsigned int virq,
- unsigned int nr_irqs, void *arg)
-{
- struct cpu_ipi_domain_state *state = domain->host_data;
- unsigned int i, hwirq;
- int ret;
-
- for (i = 0; i < nr_irqs; i++) {
- hwirq = find_first_zero_bit(state->allocated, 2);
- if (hwirq == 2)
- return -EBUSY;
- bitmap_set(state->allocated, hwirq, 1);
-
- ret = irq_domain_set_hwirq_and_chip(domain, virq + i, hwirq,
- &mips_mt_cpu_irq_controller,
- NULL);
- if (ret)
- return ret;
-
- ret = irq_domain_set_hwirq_and_chip(domain->parent, virq + i, hwirq,
- &mips_mt_cpu_irq_controller,
- NULL);
-
- if (ret)
- return ret;
-
- ret = irq_set_irq_type(virq + i, IRQ_TYPE_LEVEL_HIGH);
- if (ret)
- return ret;
- }
-
- return 0;
-}
-
-static int mips_cpu_ipi_match(struct irq_domain *d, struct device_node *node,
- enum irq_domain_bus_token bus_token)
-{
- bool is_ipi;
-
- switch (bus_token) {
- case DOMAIN_BUS_IPI:
- is_ipi = d->bus_token == bus_token;
- return (!node || (to_of_node(d->fwnode) == node)) && is_ipi;
- default:
- return 0;
- }
-}
-
-static const struct irq_domain_ops mips_cpu_ipi_chip_ops = {
- .alloc = mips_cpu_ipi_alloc,
- .match = mips_cpu_ipi_match,
-};
-
-static void mips_cpu_register_ipi_domain(struct device_node *of_node)
-{
- struct cpu_ipi_domain_state *ipi_domain_state;
-
- ipi_domain_state = kzalloc(sizeof(*ipi_domain_state), GFP_KERNEL);
- ipi_domain = irq_domain_add_hierarchy(irq_domain,
- IRQ_DOMAIN_FLAG_IPI_SINGLE,
- 2, of_node,
- &mips_cpu_ipi_chip_ops,
- ipi_domain_state);
- if (!ipi_domain)
- panic("Failed to add MIPS CPU IPI domain");
- irq_domain_update_bus_token(ipi_domain, DOMAIN_BUS_IPI);
-}
-
-#else /* !CONFIG_GENERIC_IRQ_IPI */
-
-static inline void mips_cpu_register_ipi_domain(struct device_node *of_node) {}
-
-#endif /* !CONFIG_GENERIC_IRQ_IPI */
-
int mips_cpu_get_sw_int(int hwint)
{
/* Only 0 and 1 for SW INT */
@@ -304,13 +191,6 @@ static void __init __mips_cpu_irq_init(struct device_node *of_node)
NULL);
if (!irq_domain)
panic("Failed to add irqdomain for MIPS CPU");
-
- /*
- * Only proceed to register the software interrupt IPI implementation
- * for CPUs which implement the MIPS MT (multi-threading) ASE.
- */
- if (cpu_has_mipsmt)
- mips_cpu_register_ipi_domain(of_node);
}

void __init mips_cpu_irq_init(void)

--
2.46.0