* [PATCH v4 0/3] MIPS: CI20: Enable SMP with two CPUs
@ 2026-11-12 18:44 Florian Fainelli
2026-11-12 18:44 ` [PATCH v4 1/3] clk: ingenic: Poll JZ4780 CPU1 power status atomically Florian Fainelli
` (2 more replies)
0 siblings, 3 replies; 7+ messages in thread
From: Florian Fainelli @ 2026-11-12 18:44 UTC (permalink / raw)
To: linux-mips
Cc: Florian Fainelli, Thomas Bogendoerfer, Paul Burton,
Paul Cercueil, Stephen Boyd, Brian Masney, Jerome Brunet,
周琰杰 (Zhou Yanjie),
Jiaxun Yang, open list, open list:COMMON CLK FRAMEWORK, hns,
paul
This patch series resurrects SMP support for the CI20 board. This was
submitted back in 2020 by Zhou Yanjie.
Changes in v4:
• Enable the secondary CPU clock outside the raw spinlock
• Mark enumerated CPUs present and assign hardware core IDs
• Skip generic IPI allocation when the platform supplies both IPI callbacks
• Fix generic 64-bit builds using CKSEG aliases and pointer-sized assembly macros
• Reject unsupported CPU offlining with -EOPNOTSUPP
• Restrict the XBurst SMP idle handler to multicore systems and recheck
pending interrupts after flushing the cache
Changes in v3:
• Align the second line of the readl_poll_timeout_atomic() per Brian's
feedback
• Collected Paul's R-b and T-b tags
Changes in v2:
• Moved SMP sources to arch/mips/ingenic/ and the header to arch/mips/include/asm/
• Removed CONFIG_MIPS_INGENIC_SMP, using CONFIG_MACH_JZ4780 and CONFIG_SMP directly
• Renamed JZ4780-specific SMP symbols to XBurst names
• Changed CPU enumeration to assign consecutive logical IDs and removed the explicit two-core limit
• Changed mailbox IRQ allocation failure from panic to logging and returning
• Shortened commit descriptions, removed Codex attribution, and adjusted source credits and sign-offs
• Kept the clock polling patch and CI20 defconfig changes unchanged,
added Paul's R-b for patch #1
• Updated the kernel baseline to include additional upstream fixes
Florian Fainelli (1):
clk: ingenic: Poll JZ4780 CPU1 power status atomically
周琰杰 (Zhou Yanjie) (2):
MIPS: Ingenic: Add XBurst SMP support
MIPS: CI20: Enable SMP with two CPUs
MAINTAINERS | 3 +-
arch/mips/Kbuild | 1 +
arch/mips/configs/ci20_defconfig | 2 +
arch/mips/generic/init.c | 3 +
arch/mips/include/asm/ingenic-smp.h | 20 ++
arch/mips/ingenic/Kconfig | 2 +
arch/mips/ingenic/Makefile | 5 +
arch/mips/ingenic/ingenic-smp-entry.S | 54 ++++++
arch/mips/ingenic/ingenic-smp.c | 262 ++++++++++++++++++++++++++
arch/mips/kernel/idle.c | 30 ++-
arch/mips/kernel/smp.c | 5 +-
drivers/clk/ingenic/jz4780-cgu.c | 4 +-
12 files changed, 386 insertions(+), 5 deletions(-)
create mode 100644 arch/mips/include/asm/ingenic-smp.h
create mode 100644 arch/mips/ingenic/Makefile
create mode 100644 arch/mips/ingenic/ingenic-smp-entry.S
create mode 100644 arch/mips/ingenic/ingenic-smp.c
--
2.53.0
^ permalink raw reply [flat|nested] 7+ messages in thread* [PATCH v4 1/3] clk: ingenic: Poll JZ4780 CPU1 power status atomically 2026-11-12 18:44 [PATCH v4 0/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli @ 2026-11-12 18:44 ` Florian Fainelli 2026-11-12 18:44 ` [PATCH v4 2/3] MIPS: Ingenic: Add XBurst SMP support Florian Fainelli 2026-11-12 18:44 ` [PATCH v4 3/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli 2 siblings, 0 replies; 7+ messages in thread From: Florian Fainelli @ 2026-11-12 18:44 UTC (permalink / raw) To: linux-mips Cc: Florian Fainelli, stable, Paul Cercueil, Thomas Bogendoerfer, Paul Burton, Stephen Boyd, Brian Masney, Jerome Brunet, 周琰杰 (Zhou Yanjie), Jiaxun Yang, open list, open list:COMMON CLK FRAMEWORK, hns, paul The CPU1 clock enable callback runs under the clock framework's spinlock with interrupts disabled. Its readl_poll_timeout() can sleep between reads, which is invalid in this context when SMP powers up CPU1. Use readl_poll_timeout_atomic() with the existing interval and timeout. This is a prerequisite for the JZ4780 SMP bring-up path. The original CPU1 clock implementation by Zhou Yanjie is already upstream. Cross-built for CI20; the subsequent SMP-enabled image brought up both CPU cores. Development and validation used Codex assistance. CC: stable@vger.kernel.org Fixes: 6673db4f3f26 ("clk: JZ4780: Add function for enable the second core.") Link: https://lkml.kernel.org/r/1582215889-113034-3-git-send-email-zhouyanjie@wanyeetech.com Reviewed-by: Paul Cercueil <paul@crapouillou.net> Tested-by: Paul Cercueil <paul@crapouillou.net> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> --- drivers/clk/ingenic/jz4780-cgu.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/clk/ingenic/jz4780-cgu.c b/drivers/clk/ingenic/jz4780-cgu.c index 1e88aef7ac0f..1809f8859c7e 100644 --- a/drivers/clk/ingenic/jz4780-cgu.c +++ b/drivers/clk/ingenic/jz4780-cgu.c @@ -242,8 +242,8 @@ static int jz4780_core1_enable(struct clk_hw *hw) spin_unlock_irqrestore(&cgu->lock, flags); /* wait for the CPU to be powered up */ - retval = readl_poll_timeout(cgu->base + CGU_REG_LCR, lcr, - !(lcr & LCR_SCPUS), 10, timeout); + retval = readl_poll_timeout_atomic(cgu->base + CGU_REG_LCR, lcr, + !(lcr & LCR_SCPUS), 10, timeout); if (retval == -ETIMEDOUT) { pr_err("%s: Wait for power up core1 timeout\n", __func__); return retval; -- 2.53.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 2/3] MIPS: Ingenic: Add XBurst SMP support 2026-11-12 18:44 [PATCH v4 0/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli 2026-11-12 18:44 ` [PATCH v4 1/3] clk: ingenic: Poll JZ4780 CPU1 power status atomically Florian Fainelli @ 2026-11-12 18:44 ` Florian Fainelli 2026-11-13 12:54 ` Paul Cercueil 2026-11-12 18:44 ` [PATCH v4 3/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli 2 siblings, 1 reply; 7+ messages in thread From: Florian Fainelli @ 2026-11-12 18:44 UTC (permalink / raw) To: linux-mips Cc: 周琰杰 (Zhou Yanjie), Paul Cercueil, Florian Fainelli, Thomas Bogendoerfer, Paul Burton, Stephen Boyd, Brian Masney, Jerome Brunet, Jiaxun Yang, open list, open list:COMMON CLK FRAMEWORK, hns, paul From: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> Add support for the second XBurst core on the JZ4780, based on Zhou Yanjie's 2020 series and Paul Burton's original CI20 SMP implementation. Use the CP0 mailboxes for inter-processor interrupts. XBurst stops its private cache clock when executing WAIT. Write back the data cache before entering idle so that the other core can access data belonging to the sleeping core. Link: https://lkml.iu.edu/hypermail/linux/kernel/2005.2/03488.html Link: https://lists.openwall.net/linux-kernel/2020/05/13/1482 Link: https://lists.openwall.net/linux-kernel/2020/05/19/1099 Reviewed-by: Paul Cercueil <paul@crapouillou.net> Tested-by: Paul Cercueil <paul@crapouillou.net> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> [florian: forward ported to latest version] Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> --- MAINTAINERS | 3 +- arch/mips/Kbuild | 1 + arch/mips/generic/init.c | 3 + arch/mips/include/asm/ingenic-smp.h | 20 ++ arch/mips/ingenic/Kconfig | 2 + arch/mips/ingenic/Makefile | 5 + arch/mips/ingenic/ingenic-smp-entry.S | 54 ++++++ arch/mips/ingenic/ingenic-smp.c | 262 ++++++++++++++++++++++++++ arch/mips/kernel/idle.c | 30 ++- arch/mips/kernel/smp.c | 5 +- 10 files changed, 382 insertions(+), 3 deletions(-) create mode 100644 arch/mips/include/asm/ingenic-smp.h create mode 100644 arch/mips/ingenic/Makefile create mode 100644 arch/mips/ingenic/ingenic-smp-entry.S create mode 100644 arch/mips/ingenic/ingenic-smp.c diff --git a/MAINTAINERS b/MAINTAINERS index 222b7fc2c3aa..7128df320bab 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -13150,8 +13150,9 @@ L: linux-mips@vger.kernel.org S: Maintained F: arch/mips/boot/dts/ingenic/ F: arch/mips/generic/board-ingenic.c +F: arch/mips/include/asm/ingenic-smp.h F: arch/mips/include/asm/mach-ingenic/ -F: arch/mips/ingenic/Kconfig +F: arch/mips/ingenic/ F: drivers/clk/ingenic/ F: drivers/dma/dma-jz4780.c F: drivers/gpu/drm/ingenic/ diff --git a/arch/mips/Kbuild b/arch/mips/Kbuild index e901bf554483..4912ab80d143 100644 --- a/arch/mips/Kbuild +++ b/arch/mips/Kbuild @@ -12,6 +12,7 @@ obj- := $(platform-y) # The object files are linked as core-y files would be linked obj-y += generic/ +obj-$(CONFIG_MACH_INGENIC) += ingenic/ obj-y += kernel/ obj-y += mm/ obj-y += net/ diff --git a/arch/mips/generic/init.c b/arch/mips/generic/init.c index 1d712eac1617..38f294887c17 100644 --- a/arch/mips/generic/init.c +++ b/arch/mips/generic/init.c @@ -13,6 +13,7 @@ #include <asm/bootinfo.h> #include <asm/fw/fw.h> +#include <asm/ingenic-smp.h> #include <asm/irq_cpu.h> #include <asm/machine.h> #include <asm/mips-cps.h> @@ -113,6 +114,8 @@ void __init device_tree_init(void) unflatten_and_copy_device_tree(); mips_cpc_probe(); + if (!register_ingenic_smp_ops()) + return; if (!register_cps_smp_ops()) return; if (!register_vsmp_smp_ops()) diff --git a/arch/mips/include/asm/ingenic-smp.h b/arch/mips/include/asm/ingenic-smp.h new file mode 100644 index 000000000000..ea81c333820d --- /dev/null +++ b/arch/mips/include/asm/ingenic-smp.h @@ -0,0 +1,20 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* Copyright (C) 2013 Paul Burton <paulburton@kernel.org> */ +#ifndef __ASM_INGENIC_SMP_H__ +#define __ASM_INGENIC_SMP_H__ + +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/kconfig.h> + +#if defined(CONFIG_MACH_JZ4780) && defined(CONFIG_SMP) +int __init register_ingenic_smp_ops(void); +void xburst_secondary_cpu_entry(void); +#else +static inline int register_ingenic_smp_ops(void) +{ + return -ENODEV; +} +#endif + +#endif /* __ASM_INGENIC_SMP_H__ */ diff --git a/arch/mips/ingenic/Kconfig b/arch/mips/ingenic/Kconfig index f595b339a4b8..583f8d3651df 100644 --- a/arch/mips/ingenic/Kconfig +++ b/arch/mips/ingenic/Kconfig @@ -64,6 +64,8 @@ config MACH_JZ4780 select MIPS_CPU_SCACHE select SYS_HAS_CPU_MIPS32_R2 select SYS_SUPPORTS_HIGHMEM + select SYS_SUPPORTS_SMP + select SMP_UP if SMP config MACH_X1000 bool diff --git a/arch/mips/ingenic/Makefile b/arch/mips/ingenic/Makefile new file mode 100644 index 000000000000..fc3b1bf1f25a --- /dev/null +++ b/arch/mips/ingenic/Makefile @@ -0,0 +1,5 @@ +# SPDX-License-Identifier: GPL-2.0 + +ifdef CONFIG_SMP +obj-$(CONFIG_MACH_JZ4780) += ingenic-smp.o ingenic-smp-entry.o +endif diff --git a/arch/mips/ingenic/ingenic-smp-entry.S b/arch/mips/ingenic/ingenic-smp-entry.S new file mode 100644 index 000000000000..290e23c65707 --- /dev/null +++ b/arch/mips/ingenic/ingenic-smp-entry.S @@ -0,0 +1,54 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Copyright (C) 2013 Paul Burton <paulburton@kernel.org> + * XBurst secondary CPU reset entry + */ + +#include <asm/addrspace.h> +#include <asm/asm.h> +#include <asm/asmmacro.h> +#include <asm/cacheops.h> +#include <asm/mipsregs.h> + +#define CACHE_SIZE (32 * 1024) +#define CACHE_LINESIZE 32 + + /* The CP0 REIM register only stores the upper 16 address bits. */ + .section .text.smp-entry, "ax" + .balign 0x10000 + .set noreorder +LEAF(xburst_secondary_cpu_entry) + mtc0 zero, CP0_CAUSE + li t0, ST0_CU0 + mtc0 t0, CP0_STATUS + ehb + + /* Invalidate both private caches before entering cached code. */ + PTR_LI t0, CKSEG0 + ori t1, t0, CACHE_SIZE + mtc0 zero, CP0_TAGLO + ehb +1: cache Index_Store_Tag_I, 0(t0) + cache Index_Store_Tag_D, 0(t0) + addiu t0, t0, CACHE_LINESIZE + bne t0, t1, 1b + nop + + mfc0 t0, CP0_CONFIG + li t1, ~CONF_CM_CMASK + and t0, t0, t1 + ori t0, t0, CONF_CM_CACHABLE_NONCOHERENT + mtc0 t0, CP0_CONFIG + mtc0 zero, CP0_PAGEMASK + ehb + + /* CPU0 publishes these in RAM before releasing CPU1 from reset. */ + PTR_LA t0, xburst_cpu_entry_sp + LONG_L sp, 0(t0) + PTR_LA t0, xburst_cpu_entry_gp + LONG_L gp, 0(t0) + + PTR_LA t0, smp_bootstrap + jr t0 + nop +END(xburst_secondary_cpu_entry) diff --git a/arch/mips/ingenic/ingenic-smp.c b/arch/mips/ingenic/ingenic-smp.c new file mode 100644 index 000000000000..b020f4eb1d65 --- /dev/null +++ b/arch/mips/ingenic/ingenic-smp.c @@ -0,0 +1,262 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * XBurst SMP support, based on Paul Burton's CI20 implementation and + * Zhou Yanjie's 2020 JZ4780 SMP series. + * Copyright (C) 2013 Paul Burton <paulburton@kernel.org> + */ + +#include <linux/clk.h> +#include <linux/cpu.h> +#include <linux/interrupt.h> +#include <linux/of.h> +#include <linux/of_clk.h> +#include <linux/sched/task_stack.h> +#include <linux/smp.h> + +#include <asm/addrspace.h> +#include <asm/irq_cpu.h> +#include <asm/mipsregs.h> +#include <asm/r4kcache.h> +#include <asm/smp-ops.h> + +#include <asm/ingenic-smp.h> + +#define read_c0_corectrl() __read_32bit_c0_register($12, 2) +#define write_c0_corectrl(val) __write_32bit_c0_register($12, 2, val) +#define read_c0_corestatus() __read_32bit_c0_register($12, 3) +#define write_c0_corestatus(val) __write_32bit_c0_register($12, 3, val) +#define read_c0_reim() __read_32bit_c0_register($12, 4) +#define write_c0_reim(val) __write_32bit_c0_register($12, 4, val) +#define read_c0_mailbox0() __read_32bit_c0_register($20, 0) +#define write_c0_mailbox0(val) __write_32bit_c0_register($20, 0, val) +#define read_c0_mailbox1() __read_32bit_c0_register($20, 1) +#define write_c0_mailbox1(val) __write_32bit_c0_register($20, 1, val) + +#define XBURST_MBOX_IRQ (MIPS_CPU_IRQ_BASE + 3) +#define CORECTRL_SWRST(cpu) BIT(cpu) +#define CORECTRL_RPC(cpu) BIT(8 + (cpu)) +#define CORESTATUS_MIRQ(cpu) BIT(cpu) +#define REIM_ENTRY GENMASK(31, 16) +#define REIM_MBOXIRQ(cpu) BIT(cpu) + +static struct clk *cpu1_clock; +static DEFINE_RAW_SPINLOCK(smp_lock); + +/* Referenced by the uncached secondary CPU reset entry. */ +unsigned long xburst_cpu_entry_sp; +unsigned long xburst_cpu_entry_gp; + +static irqreturn_t xburst_mbox_interrupt(int irq, void *dev_id) +{ + unsigned int cpu = cpu_logical_map(smp_processor_id()); + u32 action, status; + + raw_spin_lock(&smp_lock); + if (cpu == 0) { + action = read_c0_mailbox0(); + write_c0_mailbox0(0); + } else { + action = read_c0_mailbox1(); + write_c0_mailbox1(0); + } + + status = read_c0_corestatus(); + write_c0_corestatus(status & ~CORESTATUS_MIRQ(cpu)); + irq_disable_hazard(); + raw_spin_unlock(&smp_lock); + + if (action & SMP_RESCHEDULE_YOURSELF) + scheduler_ipi(); + if (action & SMP_CALL_FUNCTION) + generic_smp_call_function_interrupt(); + + return IRQ_HANDLED; +} + +static void __init xburst_smp_setup(void) +{ + struct device_node *np; + u32 entry, reim; + unsigned int cpu = 0; + u64 hwid; + + for_each_of_cpu_node(np) { + hwid = of_get_cpu_hwid(np, 0); + if (!of_device_is_available(np) || + hwid >= ARRAY_SIZE(__cpu_number_map)) + continue; + if (cpu >= nr_cpu_ids) { + of_node_put(np); + break; + } + __cpu_number_map[hwid] = cpu; + __cpu_logical_map[cpu] = hwid; + set_cpu_possible(cpu, true); + set_cpu_present(cpu, true); + cpu_set_core(&cpu_data[cpu], hwid); + cpu++; + } + + /* Program the shared reset vector with mailbox delivery masked. */ + reim = read_c0_reim(); + reim &= ~(REIM_MBOXIRQ(0) | REIM_MBOXIRQ(1)); + write_c0_reim(reim); + write_c0_mailbox0(0); + write_c0_mailbox1(0); + write_c0_corestatus(0); + + entry = CKSEG1ADDR((unsigned long)xburst_secondary_cpu_entry); + write_c0_reim((reim & ~REIM_ENTRY) | (entry & REIM_ENTRY)); +} + +static void __init xburst_prepare_cpus(unsigned int max_cpus) +{ + struct device_node *np; + u32 reim; + int ret; + + ret = request_irq(XBURST_MBOX_IRQ, xburst_mbox_interrupt, + IRQF_PERCPU | IRQF_NO_THREAD, "core mailbox", NULL); + if (ret) { + pr_err("XBurst: cannot request mailbox IRQ: %d\n", ret); + return; + } + + reim = read_c0_reim(); + write_c0_reim(reim | REIM_MBOXIRQ(0)); + set_c0_status(STATUSF_IP3); + irq_enable_hazard(); + + if (!cpu_possible(1)) + return; + + np = of_get_cpu_node(1, NULL); + cpu1_clock = of_clk_get(np, 0); + of_node_put(np); + if (IS_ERR(cpu1_clock)) { + pr_err("XBurst: cannot get CPU1 clock: %ld\n", + PTR_ERR(cpu1_clock)); + return; + } + + ret = clk_prepare(cpu1_clock); + if (ret) { + clk_put(cpu1_clock); + cpu1_clock = ERR_PTR(ret); + pr_err("XBurst: cannot prepare CPU1 clock: %d\n", ret); + } +} + +static int xburst_boot_secondary(int cpu, struct task_struct *idle) +{ + unsigned long flags; + u32 ctrl; + int ret; + + if (cpu_logical_map(cpu) != 1 || IS_ERR_OR_NULL(cpu1_clock)) + return -ENODEV; + + raw_spin_lock_irqsave(&smp_lock, flags); + ctrl = read_c0_corectrl() | CORECTRL_SWRST(1) | CORECTRL_RPC(1); + write_c0_corectrl(ctrl); + back_to_back_c0_hazard(); + raw_spin_unlock_irqrestore(&smp_lock, flags); + + ret = clk_enable(cpu1_clock); + if (ret) + return ret; + + xburst_cpu_entry_sp = __KSTK_TOS(idle); + xburst_cpu_entry_gp = (unsigned long)task_thread_info(idle); + /* Publish bootstrap state before CPU1 starts using its private cache. */ + r4k_blast_dcache(); + /* Complete cache writeback before releasing reset. */ + mb(); + raw_spin_lock_irqsave(&smp_lock, flags); + ctrl = read_c0_corectrl(); + write_c0_corectrl(ctrl & ~CORECTRL_SWRST(1)); + back_to_back_c0_hazard(); + raw_spin_unlock_irqrestore(&smp_lock, flags); + return 0; +} + +static void xburst_init_secondary(void) +{ + clear_c0_status(ST0_IM); + irq_disable_hazard(); +} + +static void xburst_smp_finish(void) +{ + unsigned int cpu = cpu_logical_map(smp_processor_id()); + u32 reim; + + raw_spin_lock(&smp_lock); + reim = read_c0_reim(); + write_c0_reim(reim | REIM_MBOXIRQ(cpu)); + raw_spin_unlock(&smp_lock); + + change_c0_status(ST0_IM, STATUSF_IP3 | STATUSF_IP2 | + STATUSF_IP1 | STATUSF_IP0); + irq_enable_hazard(); + local_irq_enable(); +} + +static void xburst_send_ipi_locked(int cpu, unsigned int action) +{ + if (cpu_logical_map(cpu) == 0) + write_c0_mailbox0(read_c0_mailbox0() | action); + else + write_c0_mailbox1(read_c0_mailbox1() | action); + back_to_back_c0_hazard(); +} + +static void xburst_send_ipi_single(int cpu, unsigned int action) +{ + unsigned long flags; + + raw_spin_lock_irqsave(&smp_lock, flags); + xburst_send_ipi_locked(cpu, action); + raw_spin_unlock_irqrestore(&smp_lock, flags); +} + +static void xburst_send_ipi_mask(const struct cpumask *mask, unsigned int action) +{ + unsigned long flags; + int cpu; + + raw_spin_lock_irqsave(&smp_lock, flags); + for_each_cpu(cpu, mask) + xburst_send_ipi_locked(cpu, action); + raw_spin_unlock_irqrestore(&smp_lock, flags); +} + +#ifdef CONFIG_HOTPLUG_CPU +static int xburst_cpu_disable(void) +{ + /* CPU teardown and restart are not supported yet. */ + return -EOPNOTSUPP; +} +#endif + +static const struct plat_smp_ops xburst_smp_ops = { + .send_ipi_single = xburst_send_ipi_single, + .send_ipi_mask = xburst_send_ipi_mask, + .init_secondary = xburst_init_secondary, + .smp_finish = xburst_smp_finish, + .boot_secondary = xburst_boot_secondary, + .smp_setup = xburst_smp_setup, + .prepare_cpus = xburst_prepare_cpus, +#ifdef CONFIG_HOTPLUG_CPU + .cpu_disable = xburst_cpu_disable, +#endif +}; + +int __init register_ingenic_smp_ops(void) +{ + if (!of_machine_is_compatible("ingenic,jz4780")) + return -ENODEV; + + register_smp_ops(&xburst_smp_ops); + return 0; +} diff --git a/arch/mips/kernel/idle.c b/arch/mips/kernel/idle.c index 80e8a04a642e..0b042a328ad7 100644 --- a/arch/mips/kernel/idle.c +++ b/arch/mips/kernel/idle.c @@ -18,6 +18,7 @@ #include <asm/cpu-type.h> #include <asm/idle.h> #include <asm/mipsregs.h> +#include <asm/r4kcache.h> /* * Not all of the MIPS CPUs have the "wait" instruction available. Moreover, @@ -71,6 +72,27 @@ static void __cpuidle rm7k_wait_irqoff(void) " .set pop \n"); } +/* XBurst gates its private cache clock in WAIT; write back dirty data first. */ +static void __cpuidle xburst_wait_irqoff(void) +{ + unsigned long pending; + + if (need_resched()) + return; + + r4k_blast_dcache(); + pending = read_c0_cause() & read_c0_status() & CAUSEF_IP; + if (!need_resched() && !pending) { + __asm__ __volatile__( + ".set push\n" + ".set arch=r4000\n" + "sync\n" + "wait\n" + ".set pop\n" + : : : "memory"); + } +} + /* * Au1 'wait' is only useful when the 32kHz counter is used as timer, * since coreclock (and the cp0 counter) stops upon executing it. Only an @@ -156,10 +178,16 @@ void __init check_wait(void) case CPU_CAVIUM_OCTEON_PLUS: case CPU_CAVIUM_OCTEON2: case CPU_CAVIUM_OCTEON3: - case CPU_XBURST: case CPU_LOONGSON32: cpu_wait = r4k_wait; break; + case CPU_XBURST: + if (IS_ENABLED(CONFIG_MACH_JZ4780) && + IS_ENABLED(CONFIG_SMP) && num_possible_cpus() > 1) + cpu_wait = xburst_wait_irqoff; + else + cpu_wait = r4k_wait; + break; case CPU_LOONGSON64: if ((c->processor_id & (PRID_IMP_MASK | PRID_REV_MASK)) >= (PRID_IMP_LOONGSON_64C | PRID_REV_LOONGSON3A_R2_0) || diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c index 0f28b4a62e72..46c70d866723 100644 --- a/arch/mips/kernel/smp.c +++ b/arch/mips/kernel/smp.c @@ -338,7 +338,10 @@ int mips_smp_ipi_free(const struct cpumask *mask) static int __init mips_smp_ipi_init(void) { - if (num_possible_cpus() == 1) + /* Platforms with their own IPI callbacks do not need an IRQ domain. */ + if ((mp_ops->send_ipi_single != mips_smp_send_ipi_single && + mp_ops->send_ipi_mask != mips_smp_send_ipi_mask) || + num_possible_cpus() == 1) return 0; mips_smp_ipi_allocate(cpu_possible_mask); -- 2.53.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4 2/3] MIPS: Ingenic: Add XBurst SMP support 2026-11-12 18:44 ` [PATCH v4 2/3] MIPS: Ingenic: Add XBurst SMP support Florian Fainelli @ 2026-11-13 12:54 ` Paul Cercueil 2026-11-13 18:07 ` Florian Fainelli 0 siblings, 1 reply; 7+ messages in thread From: Paul Cercueil @ 2026-11-13 12:54 UTC (permalink / raw) To: Florian Fainelli, linux-mips Cc: 周琰杰 (Zhou Yanjie), Thomas Bogendoerfer, Paul Burton, Stephen Boyd, Brian Masney, Jerome Brunet, Jiaxun Yang, open list, open list:COMMON CLK FRAMEWORK, hns, paul Hi Florian, Le jeudi 12 novembre 2026 à 10:44 -0800, Florian Fainelli a écrit : > From: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> > > Add support for the second XBurst core on the JZ4780, based on Zhou > Yanjie's 2020 series and Paul Burton's original CI20 SMP implementation. > Use the CP0 mailboxes for inter-processor interrupts. > > XBurst stops its private cache clock when executing WAIT. Write back > the data cache before entering idle so that the other core can access > data belonging to the sleeping core. > > Link: https://lkml.iu.edu/hypermail/linux/kernel/2005.2/03488.html > Link: https://lists.openwall.net/linux-kernel/2020/05/13/1482 > Link: https://lists.openwall.net/linux-kernel/2020/05/19/1099 > Reviewed-by: Paul Cercueil <paul@crapouillou.net> > Tested-by: Paul Cercueil <paul@crapouillou.net> You updated the patch after my Reviewed-by, so that voids it... > Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> > [florian: forward ported to latest version] > Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> > --- Please add a changelog per patch too. > MAINTAINERS | 3 +- > arch/mips/Kbuild | 1 + > arch/mips/generic/init.c | 3 + > arch/mips/include/asm/ingenic-smp.h | 20 ++ > arch/mips/ingenic/Kconfig | 2 + > arch/mips/ingenic/Makefile | 5 + > arch/mips/ingenic/ingenic-smp-entry.S | 54 ++++++ > arch/mips/ingenic/ingenic-smp.c | 262 ++++++++++++++++++++++++++ > arch/mips/kernel/idle.c | 30 ++- > arch/mips/kernel/smp.c | 5 +- > 10 files changed, 382 insertions(+), 3 deletions(-) > create mode 100644 arch/mips/include/asm/ingenic-smp.h > create mode 100644 arch/mips/ingenic/Makefile > create mode 100644 arch/mips/ingenic/ingenic-smp-entry.S > create mode 100644 arch/mips/ingenic/ingenic-smp.c > > diff --git a/MAINTAINERS b/MAINTAINERS > index 222b7fc2c3aa..7128df320bab 100644 > --- a/MAINTAINERS > +++ b/MAINTAINERS > @@ -13150,8 +13150,9 @@ L: linux-mips@vger.kernel.org > S: Maintained > F: arch/mips/boot/dts/ingenic/ > F: arch/mips/generic/board-ingenic.c > +F: arch/mips/include/asm/ingenic-smp.h > F: arch/mips/include/asm/mach-ingenic/ > -F: arch/mips/ingenic/Kconfig > +F: arch/mips/ingenic/ > F: drivers/clk/ingenic/ > F: drivers/dma/dma-jz4780.c > F: drivers/gpu/drm/ingenic/ > diff --git a/arch/mips/Kbuild b/arch/mips/Kbuild > index e901bf554483..4912ab80d143 100644 > --- a/arch/mips/Kbuild > +++ b/arch/mips/Kbuild > @@ -12,6 +12,7 @@ obj- := $(platform-y) > # The object files are linked as core-y files would be linked > > obj-y += generic/ > +obj-$(CONFIG_MACH_INGENIC) += ingenic/ > obj-y += kernel/ > obj-y += mm/ > obj-y += net/ > diff --git a/arch/mips/generic/init.c b/arch/mips/generic/init.c > index 1d712eac1617..38f294887c17 100644 > --- a/arch/mips/generic/init.c > +++ b/arch/mips/generic/init.c > @@ -13,6 +13,7 @@ > > #include <asm/bootinfo.h> > #include <asm/fw/fw.h> > +#include <asm/ingenic-smp.h> > #include <asm/irq_cpu.h> > #include <asm/machine.h> > #include <asm/mips-cps.h> > @@ -113,6 +114,8 @@ void __init device_tree_init(void) > unflatten_and_copy_device_tree(); > mips_cpc_probe(); > > + if (!register_ingenic_smp_ops()) > + return; > if (!register_cps_smp_ops()) > return; > if (!register_vsmp_smp_ops()) > diff --git a/arch/mips/include/asm/ingenic-smp.h > b/arch/mips/include/asm/ingenic-smp.h > new file mode 100644 > index 000000000000..ea81c333820d > --- /dev/null > +++ b/arch/mips/include/asm/ingenic-smp.h > @@ -0,0 +1,20 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* Copyright (C) 2013 Paul Burton <paulburton@kernel.org> */ > +#ifndef __ASM_INGENIC_SMP_H__ > +#define __ASM_INGENIC_SMP_H__ > + > +#include <linux/errno.h> > +#include <linux/init.h> > +#include <linux/kconfig.h> > + > +#if defined(CONFIG_MACH_JZ4780) && defined(CONFIG_SMP) > +int __init register_ingenic_smp_ops(void); > +void xburst_secondary_cpu_entry(void); > +#else > +static inline int register_ingenic_smp_ops(void) > +{ > + return -ENODEV; > +} > +#endif > + > +#endif /* __ASM_INGENIC_SMP_H__ */ > diff --git a/arch/mips/ingenic/Kconfig b/arch/mips/ingenic/Kconfig > index f595b339a4b8..583f8d3651df 100644 > --- a/arch/mips/ingenic/Kconfig > +++ b/arch/mips/ingenic/Kconfig > @@ -64,6 +64,8 @@ config MACH_JZ4780 > select MIPS_CPU_SCACHE > select SYS_HAS_CPU_MIPS32_R2 > select SYS_SUPPORTS_HIGHMEM > + select SYS_SUPPORTS_SMP > + select SMP_UP if SMP > > config MACH_X1000 > bool > diff --git a/arch/mips/ingenic/Makefile b/arch/mips/ingenic/Makefile > new file mode 100644 > index 000000000000..fc3b1bf1f25a > --- /dev/null > +++ b/arch/mips/ingenic/Makefile > @@ -0,0 +1,5 @@ > +# SPDX-License-Identifier: GPL-2.0 > + > +ifdef CONFIG_SMP > +obj-$(CONFIG_MACH_JZ4780) += ingenic-smp.o ingenic-smp-entry.o > +endif > diff --git a/arch/mips/ingenic/ingenic-smp-entry.S > b/arch/mips/ingenic/ingenic-smp-entry.S > new file mode 100644 > index 000000000000..290e23c65707 > --- /dev/null > +++ b/arch/mips/ingenic/ingenic-smp-entry.S > @@ -0,0 +1,54 @@ > +/* SPDX-License-Identifier: GPL-2.0-or-later */ > +/* > + * Copyright (C) 2013 Paul Burton <paulburton@kernel.org> > + * XBurst secondary CPU reset entry > + */ > + > +#include <asm/addrspace.h> > +#include <asm/asm.h> > +#include <asm/asmmacro.h> > +#include <asm/cacheops.h> > +#include <asm/mipsregs.h> > + > +#define CACHE_SIZE (32 * 1024) > +#define CACHE_LINESIZE 32 > + > + /* The CP0 REIM register only stores the upper 16 address bits. */ > + .section .text.smp-entry, "ax" > + .balign 0x10000 > + .set noreorder > +LEAF(xburst_secondary_cpu_entry) > + mtc0 zero, CP0_CAUSE > + li t0, ST0_CU0 > + mtc0 t0, CP0_STATUS > + ehb > + > + /* Invalidate both private caches before entering cached code. */ > + PTR_LI t0, CKSEG0 > + ori t1, t0, CACHE_SIZE > + mtc0 zero, CP0_TAGLO > + ehb > +1: cache Index_Store_Tag_I, 0(t0) > + cache Index_Store_Tag_D, 0(t0) > + addiu t0, t0, CACHE_LINESIZE > + bne t0, t1, 1b > + nop > + > + mfc0 t0, CP0_CONFIG > + li t1, ~CONF_CM_CMASK > + and t0, t0, t1 > + ori t0, t0, CONF_CM_CACHABLE_NONCOHERENT > + mtc0 t0, CP0_CONFIG > + mtc0 zero, CP0_PAGEMASK > + ehb > + > + /* CPU0 publishes these in RAM before releasing CPU1 from reset. */ > + PTR_LA t0, xburst_cpu_entry_sp > + LONG_L sp, 0(t0) > + PTR_LA t0, xburst_cpu_entry_gp > + LONG_L gp, 0(t0) > + > + PTR_LA t0, smp_bootstrap > + jr t0 > + nop > +END(xburst_secondary_cpu_entry) > diff --git a/arch/mips/ingenic/ingenic-smp.c b/arch/mips/ingenic/ingenic-smp.c > new file mode 100644 > index 000000000000..b020f4eb1d65 > --- /dev/null > +++ b/arch/mips/ingenic/ingenic-smp.c > @@ -0,0 +1,262 @@ > +// SPDX-License-Identifier: GPL-2.0-or-later > +/* > + * XBurst SMP support, based on Paul Burton's CI20 implementation and > + * Zhou Yanjie's 2020 JZ4780 SMP series. > + * Copyright (C) 2013 Paul Burton <paulburton@kernel.org> > + */ > + > +#include <linux/clk.h> > +#include <linux/cpu.h> > +#include <linux/interrupt.h> > +#include <linux/of.h> > +#include <linux/of_clk.h> > +#include <linux/sched/task_stack.h> > +#include <linux/smp.h> > + > +#include <asm/addrspace.h> > +#include <asm/irq_cpu.h> > +#include <asm/mipsregs.h> > +#include <asm/r4kcache.h> > +#include <asm/smp-ops.h> > + > +#include <asm/ingenic-smp.h> > + > +#define read_c0_corectrl() __read_32bit_c0_register($12, 2) > +#define write_c0_corectrl(val) __write_32bit_c0_register($12, 2, val) > +#define read_c0_corestatus() __read_32bit_c0_register($12, 3) > +#define write_c0_corestatus(val) __write_32bit_c0_register($12, 3, > val) > +#define read_c0_reim() __read_32bit_c0_register($12, 4) > +#define write_c0_reim(val) __write_32bit_c0_register($12, 4, val) > +#define read_c0_mailbox0() __read_32bit_c0_register($20, 0) > +#define write_c0_mailbox0(val) __write_32bit_c0_register($20, 0, val) > +#define read_c0_mailbox1() __read_32bit_c0_register($20, 1) > +#define write_c0_mailbox1(val) __write_32bit_c0_register($20, 1, val) > + > +#define XBURST_MBOX_IRQ (MIPS_CPU_IRQ_BASE + 3) > +#define CORECTRL_SWRST(cpu) BIT(cpu) > +#define CORECTRL_RPC(cpu) BIT(8 + (cpu)) > +#define CORESTATUS_MIRQ(cpu) BIT(cpu) > +#define REIM_ENTRY GENMASK(31, 16) > +#define REIM_MBOXIRQ(cpu) BIT(cpu) > + > +static struct clk *cpu1_clock; > +static DEFINE_RAW_SPINLOCK(smp_lock); > + > +/* Referenced by the uncached secondary CPU reset entry. */ > +unsigned long xburst_cpu_entry_sp; > +unsigned long xburst_cpu_entry_gp; > + > +static irqreturn_t xburst_mbox_interrupt(int irq, void *dev_id) > +{ > + unsigned int cpu = cpu_logical_map(smp_processor_id()); > + u32 action, status; > + > + raw_spin_lock(&smp_lock); > + if (cpu == 0) { > + action = read_c0_mailbox0(); > + write_c0_mailbox0(0); > + } else { > + action = read_c0_mailbox1(); > + write_c0_mailbox1(0); > + } > + > + status = read_c0_corestatus(); > + write_c0_corestatus(status & ~CORESTATUS_MIRQ(cpu)); > + irq_disable_hazard(); > + raw_spin_unlock(&smp_lock); > + > + if (action & SMP_RESCHEDULE_YOURSELF) > + scheduler_ipi(); > + if (action & SMP_CALL_FUNCTION) > + generic_smp_call_function_interrupt(); > + > + return IRQ_HANDLED; > +} > + > +static void __init xburst_smp_setup(void) > +{ > + struct device_node *np; > + u32 entry, reim; > + unsigned int cpu = 0; > + u64 hwid; > + > + for_each_of_cpu_node(np) { > + hwid = of_get_cpu_hwid(np, 0); > + if (!of_device_is_available(np) || > + hwid >= ARRAY_SIZE(__cpu_number_map)) > + continue; > + if (cpu >= nr_cpu_ids) { > + of_node_put(np); > + break; > + } > + __cpu_number_map[hwid] = cpu; > + __cpu_logical_map[cpu] = hwid; > + set_cpu_possible(cpu, true); > + set_cpu_present(cpu, true); > + cpu_set_core(&cpu_data[cpu], hwid); > + cpu++; > + } > + > + /* Program the shared reset vector with mailbox delivery masked. */ > + reim = read_c0_reim(); > + reim &= ~(REIM_MBOXIRQ(0) | REIM_MBOXIRQ(1)); > + write_c0_reim(reim); > + write_c0_mailbox0(0); > + write_c0_mailbox1(0); > + write_c0_corestatus(0); > + > + entry = CKSEG1ADDR((unsigned long)xburst_secondary_cpu_entry); > + write_c0_reim((reim & ~REIM_ENTRY) | (entry & REIM_ENTRY)); > +} > + > +static void __init xburst_prepare_cpus(unsigned int max_cpus) > +{ > + struct device_node *np; > + u32 reim; > + int ret; > + > + ret = request_irq(XBURST_MBOX_IRQ, xburst_mbox_interrupt, > + IRQF_PERCPU | IRQF_NO_THREAD, "core mailbox", > NULL); > + if (ret) { > + pr_err("XBurst: cannot request mailbox IRQ: %d\n", ret); > + return; > + } > + > + reim = read_c0_reim(); > + write_c0_reim(reim | REIM_MBOXIRQ(0)); > + set_c0_status(STATUSF_IP3); > + irq_enable_hazard(); > + > + if (!cpu_possible(1)) > + return; > + > + np = of_get_cpu_node(1, NULL); > + cpu1_clock = of_clk_get(np, 0); > + of_node_put(np); > + if (IS_ERR(cpu1_clock)) { > + pr_err("XBurst: cannot get CPU1 clock: %ld\n", > + PTR_ERR(cpu1_clock)); > + return; > + } > + > + ret = clk_prepare(cpu1_clock); > + if (ret) { > + clk_put(cpu1_clock); > + cpu1_clock = ERR_PTR(ret); > + pr_err("XBurst: cannot prepare CPU1 clock: %d\n", ret); > + } > +} > + > +static int xburst_boot_secondary(int cpu, struct task_struct *idle) > +{ > + unsigned long flags; > + u32 ctrl; > + int ret; > + > + if (cpu_logical_map(cpu) != 1 || IS_ERR_OR_NULL(cpu1_clock)) > + return -ENODEV; > + > + raw_spin_lock_irqsave(&smp_lock, flags); > + ctrl = read_c0_corectrl() | CORECTRL_SWRST(1) | CORECTRL_RPC(1); > + write_c0_corectrl(ctrl); > + back_to_back_c0_hazard(); > + raw_spin_unlock_irqrestore(&smp_lock, flags); > + > + ret = clk_enable(cpu1_clock); > + if (ret) > + return ret; > + > + xburst_cpu_entry_sp = __KSTK_TOS(idle); > + xburst_cpu_entry_gp = (unsigned long)task_thread_info(idle); > + /* Publish bootstrap state before CPU1 starts using its private > cache. */ > + r4k_blast_dcache(); > + /* Complete cache writeback before releasing reset. */ > + mb(); > + raw_spin_lock_irqsave(&smp_lock, flags); > + ctrl = read_c0_corectrl(); > + write_c0_corectrl(ctrl & ~CORECTRL_SWRST(1)); > + back_to_back_c0_hazard(); > + raw_spin_unlock_irqrestore(&smp_lock, flags); > + return 0; > +} > + > +static void xburst_init_secondary(void) > +{ > + clear_c0_status(ST0_IM); > + irq_disable_hazard(); > +} > + > +static void xburst_smp_finish(void) > +{ > + unsigned int cpu = cpu_logical_map(smp_processor_id()); > + u32 reim; > + > + raw_spin_lock(&smp_lock); > + reim = read_c0_reim(); > + write_c0_reim(reim | REIM_MBOXIRQ(cpu)); > + raw_spin_unlock(&smp_lock); > + > + change_c0_status(ST0_IM, STATUSF_IP3 | STATUSF_IP2 | > + STATUSF_IP1 | STATUSF_IP0); > + irq_enable_hazard(); > + local_irq_enable(); > +} > + > +static void xburst_send_ipi_locked(int cpu, unsigned int action) > +{ > + if (cpu_logical_map(cpu) == 0) > + write_c0_mailbox0(read_c0_mailbox0() | action); > + else > + write_c0_mailbox1(read_c0_mailbox1() | action); > + back_to_back_c0_hazard(); > +} > + > +static void xburst_send_ipi_single(int cpu, unsigned int action) > +{ > + unsigned long flags; > + > + raw_spin_lock_irqsave(&smp_lock, flags); > + xburst_send_ipi_locked(cpu, action); > + raw_spin_unlock_irqrestore(&smp_lock, flags); > +} > + > +static void xburst_send_ipi_mask(const struct cpumask *mask, unsigned int > action) > +{ > + unsigned long flags; > + int cpu; > + > + raw_spin_lock_irqsave(&smp_lock, flags); > + for_each_cpu(cpu, mask) > + xburst_send_ipi_locked(cpu, action); > + raw_spin_unlock_irqrestore(&smp_lock, flags); > +} > + > +#ifdef CONFIG_HOTPLUG_CPU > +static int xburst_cpu_disable(void) > +{ > + /* CPU teardown and restart are not supported yet. */ > + return -EOPNOTSUPP; > +} > +#endif > + > +static const struct plat_smp_ops xburst_smp_ops = { > + .send_ipi_single = xburst_send_ipi_single, > + .send_ipi_mask = xburst_send_ipi_mask, > + .init_secondary = xburst_init_secondary, > + .smp_finish = xburst_smp_finish, > + .boot_secondary = xburst_boot_secondary, > + .smp_setup = xburst_smp_setup, > + .prepare_cpus = xburst_prepare_cpus, > +#ifdef CONFIG_HOTPLUG_CPU > + .cpu_disable = xburst_cpu_disable, > +#endif Drop the macro guards: .cpu_disable = IF_PTR(IS_ENABLED(CONFIG_HOTPLUG_CPU), xburst_cpu_disable), > +}; > + > +int __init register_ingenic_smp_ops(void) > +{ > + if (!of_machine_is_compatible("ingenic,jz4780")) > + return -ENODEV; > + > + register_smp_ops(&xburst_smp_ops); > + return 0; > +} > diff --git a/arch/mips/kernel/idle.c b/arch/mips/kernel/idle.c > index 80e8a04a642e..0b042a328ad7 100644 > --- a/arch/mips/kernel/idle.c > +++ b/arch/mips/kernel/idle.c > @@ -18,6 +18,7 @@ > #include <asm/cpu-type.h> > #include <asm/idle.h> > #include <asm/mipsregs.h> > +#include <asm/r4kcache.h> > > /* > * Not all of the MIPS CPUs have the "wait" instruction available. Moreover, > @@ -71,6 +72,27 @@ static void __cpuidle rm7k_wait_irqoff(void) > " .set pop \n"); > } > > +/* XBurst gates its private cache clock in WAIT; write back dirty data first. > */ > +static void __cpuidle xburst_wait_irqoff(void) > +{ > + unsigned long pending; > + > + if (need_resched()) > + return; > + > + r4k_blast_dcache(); > + pending = read_c0_cause() & read_c0_status() & CAUSEF_IP; > + if (!need_resched() && !pending) { You now call need_resched() twice, that's on purpose? > + __asm__ __volatile__( > + ".set push\n" > + ".set arch=r4000\n" > + "sync\n" > + "wait\n" > + ".set pop\n" > + : : : "memory"); > + } > +} > + > /* > * Au1 'wait' is only useful when the 32kHz counter is used as timer, > * since coreclock (and the cp0 counter) stops upon executing it. Only an > @@ -156,10 +178,16 @@ void __init check_wait(void) > case CPU_CAVIUM_OCTEON_PLUS: > case CPU_CAVIUM_OCTEON2: > case CPU_CAVIUM_OCTEON3: > - case CPU_XBURST: > case CPU_LOONGSON32: > cpu_wait = r4k_wait; > break; > + case CPU_XBURST: > + if (IS_ENABLED(CONFIG_MACH_JZ4780) && > + IS_ENABLED(CONFIG_SMP) && num_possible_cpus() > 1) > + cpu_wait = xburst_wait_irqoff; > + else > + cpu_wait = r4k_wait; > + break; > case CPU_LOONGSON64: > if ((c->processor_id & (PRID_IMP_MASK | PRID_REV_MASK)) >= > (PRID_IMP_LOONGSON_64C | > PRID_REV_LOONGSON3A_R2_0) || > diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c > index 0f28b4a62e72..46c70d866723 100644 > --- a/arch/mips/kernel/smp.c > +++ b/arch/mips/kernel/smp.c > @@ -338,7 +338,10 @@ int mips_smp_ipi_free(const struct cpumask *mask) > > static int __init mips_smp_ipi_init(void) > { > - if (num_possible_cpus() == 1) > + /* Platforms with their own IPI callbacks do not need an IRQ domain. > */ > + if ((mp_ops->send_ipi_single != mips_smp_send_ipi_single && > + mp_ops->send_ipi_mask != mips_smp_send_ipi_mask) || > + num_possible_cpus() == 1) Needs braces now because of the multi-line if(). This change wasn't in your v3, and I also feel like this would belong in a separate patch. Cheers, -Paul > return 0; > > mips_smp_ipi_allocate(cpu_possible_mask); ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4 2/3] MIPS: Ingenic: Add XBurst SMP support 2026-11-13 12:54 ` Paul Cercueil @ 2026-11-13 18:07 ` Florian Fainelli 2026-11-13 18:15 ` Florian Fainelli 0 siblings, 1 reply; 7+ messages in thread From: Florian Fainelli @ 2026-11-13 18:07 UTC (permalink / raw) To: Paul Cercueil, linux-mips Cc: 周琰杰 (Zhou Yanjie), Thomas Bogendoerfer, Paul Burton, Stephen Boyd, Brian Masney, Jerome Brunet, Jiaxun Yang, open list, open list:COMMON CLK FRAMEWORK, hns, paul On 11/13/26 04:54, Paul Cercueil wrote: > Hi Florian, > > Le jeudi 12 novembre 2026 à 10:44 -0800, Florian Fainelli a écrit : >> From: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> >> >> Add support for the second XBurst core on the JZ4780, based on Zhou >> Yanjie's 2020 series and Paul Burton's original CI20 SMP implementation. >> Use the CP0 mailboxes for inter-processor interrupts. >> >> XBurst stops its private cache clock when executing WAIT. Write back >> the data cache before entering idle so that the other core can access >> data belonging to the sleeping core. >> >> Link: https://lkml.iu.edu/hypermail/linux/kernel/2005.2/03488.html >> Link: https://lists.openwall.net/linux-kernel/2020/05/13/1482 >> Link: https://lists.openwall.net/linux-kernel/2020/05/19/1099 >> Reviewed-by: Paul Cercueil <paul@crapouillou.net> >> Tested-by: Paul Cercueil <paul@crapouillou.net> > > You updated the patch after my Reviewed-by, so that voids it... Indeed, sorry about that. > >> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> >> [florian: forward ported to latest version] >> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> >> --- > > Please add a changelog per patch too. I would rather do this only in the cover letter, but fine, I will oblige here. [snip] >> +static const struct plat_smp_ops xburst_smp_ops = { >> + .send_ipi_single = xburst_send_ipi_single, >> + .send_ipi_mask = xburst_send_ipi_mask, >> + .init_secondary = xburst_init_secondary, >> + .smp_finish = xburst_smp_finish, >> + .boot_secondary = xburst_boot_secondary, >> + .smp_setup = xburst_smp_setup, >> + .prepare_cpus = xburst_prepare_cpus, >> +#ifdef CONFIG_HOTPLUG_CPU >> + .cpu_disable = xburst_cpu_disable, >> +#endif > > Drop the macro guards: > .cpu_disable = IF_PTR(IS_ENABLED(CONFIG_HOTPLUG_CPU), xburst_cpu_disable), OK. > >> +}; >> + >> +int __init register_ingenic_smp_ops(void) >> +{ >> + if (!of_machine_is_compatible("ingenic,jz4780")) >> + return -ENODEV; >> + >> + register_smp_ops(&xburst_smp_ops); >> + return 0; >> +} >> diff --git a/arch/mips/kernel/idle.c b/arch/mips/kernel/idle.c >> index 80e8a04a642e..0b042a328ad7 100644 >> --- a/arch/mips/kernel/idle.c >> +++ b/arch/mips/kernel/idle.c >> @@ -18,6 +18,7 @@ >> #include <asm/cpu-type.h> >> #include <asm/idle.h> >> #include <asm/mipsregs.h> >> +#include <asm/r4kcache.h> >> >> /* >> * Not all of the MIPS CPUs have the "wait" instruction available. Moreover, >> @@ -71,6 +72,27 @@ static void __cpuidle rm7k_wait_irqoff(void) >> " .set pop \n"); >> } >> >> +/* XBurst gates its private cache clock in WAIT; write back dirty data first. >> */ >> +static void __cpuidle xburst_wait_irqoff(void) >> +{ >> + unsigned long pending; >> + >> + if (need_resched()) >> + return; >> + >> + r4k_blast_dcache(); >> + pending = read_c0_cause() & read_c0_status() & CAUSEF_IP; >> + if (!need_resched() && !pending) { > > You now call need_resched() twice, that's on purpose? The two need_resched() checks are intentional: the first avoids an unnecessary cache flush; the second checks for work arriving during it. > >> + __asm__ __volatile__( >> + ".set push\n" >> + ".set arch=r4000\n" >> + "sync\n" >> + "wait\n" >> + ".set pop\n" >> + : : : "memory"); >> + } >> +} >> + >> /* >> * Au1 'wait' is only useful when the 32kHz counter is used as timer, >> * since coreclock (and the cp0 counter) stops upon executing it. Only an >> @@ -156,10 +178,16 @@ void __init check_wait(void) >> case CPU_CAVIUM_OCTEON_PLUS: >> case CPU_CAVIUM_OCTEON2: >> case CPU_CAVIUM_OCTEON3: >> - case CPU_XBURST: >> case CPU_LOONGSON32: >> cpu_wait = r4k_wait; >> break; >> + case CPU_XBURST: >> + if (IS_ENABLED(CONFIG_MACH_JZ4780) && >> + IS_ENABLED(CONFIG_SMP) && num_possible_cpus() > 1) >> + cpu_wait = xburst_wait_irqoff; >> + else >> + cpu_wait = r4k_wait; >> + break; >> case CPU_LOONGSON64: >> if ((c->processor_id & (PRID_IMP_MASK | PRID_REV_MASK)) >= >> (PRID_IMP_LOONGSON_64C | >> PRID_REV_LOONGSON3A_R2_0) || >> diff --git a/arch/mips/kernel/smp.c b/arch/mips/kernel/smp.c >> index 0f28b4a62e72..46c70d866723 100644 >> --- a/arch/mips/kernel/smp.c >> +++ b/arch/mips/kernel/smp.c >> @@ -338,7 +338,10 @@ int mips_smp_ipi_free(const struct cpumask *mask) >> >> static int __init mips_smp_ipi_init(void) >> { >> - if (num_possible_cpus() == 1) >> + /* Platforms with their own IPI callbacks do not need an IRQ domain. >> */ >> + if ((mp_ops->send_ipi_single != mips_smp_send_ipi_single && >> + mp_ops->send_ipi_mask != mips_smp_send_ipi_mask) || >> + num_possible_cpus() == 1) > > Needs braces now because of the multi-line if(). > > This change wasn't in your v3, and I also feel like this would belong in a > separate patch. Yes, that's fair enough. -- Florian ^ permalink raw reply [flat|nested] 7+ messages in thread
* Re: [PATCH v4 2/3] MIPS: Ingenic: Add XBurst SMP support 2026-11-13 18:07 ` Florian Fainelli @ 2026-11-13 18:15 ` Florian Fainelli 0 siblings, 0 replies; 7+ messages in thread From: Florian Fainelli @ 2026-11-13 18:15 UTC (permalink / raw) To: Paul Cercueil, linux-mips Cc: 周琰杰 (Zhou Yanjie), Thomas Bogendoerfer, Paul Burton, Stephen Boyd, Brian Masney, Jerome Brunet, Jiaxun Yang, open list, open list:COMMON CLK FRAMEWORK, hns, paul On 11/13/26 10:07, Florian Fainelli wrote: >> Drop the macro guards: >> .cpu_disable = IF_PTR(IS_ENABLED(CONFIG_HOTPLUG_CPU), >> xburst_cpu_disable), > > OK. On that suggestion specifically, the member cpu_disable does not exist when CONFIG_HOTPLUG_CPU is not defined, so although PTR_IF() evaluates to NULL, we are still trying to assign to a non-existent member, which won't work. I did make the callback always defined and used __maybe_unused instead. -- Florian ^ permalink raw reply [flat|nested] 7+ messages in thread
* [PATCH v4 3/3] MIPS: CI20: Enable SMP with two CPUs 2026-11-12 18:44 [PATCH v4 0/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli 2026-11-12 18:44 ` [PATCH v4 1/3] clk: ingenic: Poll JZ4780 CPU1 power status atomically Florian Fainelli 2026-11-12 18:44 ` [PATCH v4 2/3] MIPS: Ingenic: Add XBurst SMP support Florian Fainelli @ 2026-11-12 18:44 ` Florian Fainelli 2 siblings, 0 replies; 7+ messages in thread From: Florian Fainelli @ 2026-11-12 18:44 UTC (permalink / raw) To: linux-mips Cc: 周琰杰 (Zhou Yanjie), Paul Cercueil, Florian Fainelli, Thomas Bogendoerfer, Paul Burton, Stephen Boyd, Brian Masney, Jerome Brunet, Jiaxun Yang, open list, open list:COMMON CLK FRAMEWORK, hns, paul From: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> Enable both JZ4780 cores now that the generic Ingenic platform provides secondary CPU reset and mailbox support. The existing device tree already reserves TCU channels for both per-CPU clock events. Link: https://lists.openwall.net/linux-kernel/2020/05/19/739 Reviewed-by: Paul Cercueil <paul@crapouillou.net> Tested-by: Paul Cercueil <paul@crapouillou.net> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com> --- arch/mips/configs/ci20_defconfig | 2 ++ 1 file changed, 2 insertions(+) diff --git a/arch/mips/configs/ci20_defconfig b/arch/mips/configs/ci20_defconfig index 90536cab417e..09d65094b1bd 100644 --- a/arch/mips/configs/ci20_defconfig +++ b/arch/mips/configs/ci20_defconfig @@ -22,6 +22,8 @@ CONFIG_EXPERT=y CONFIG_MACH_INGENIC_SOC=y CONFIG_JZ4780_CI20=y CONFIG_HIGHMEM=y +CONFIG_SMP=y +CONFIG_NR_CPUS=2 CONFIG_HZ_100=y # CONFIG_SUSPEND is not set # CONFIG_SECCOMP is not set -- 2.53.0 ^ permalink raw reply [flat|nested] 7+ messages in thread
end of thread, other threads:[~2026-11-13 18:15 UTC | newest] Thread overview: 7+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2026-11-12 18:44 [PATCH v4 0/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli 2026-11-12 18:44 ` [PATCH v4 1/3] clk: ingenic: Poll JZ4780 CPU1 power status atomically Florian Fainelli 2026-11-12 18:44 ` [PATCH v4 2/3] MIPS: Ingenic: Add XBurst SMP support Florian Fainelli 2026-11-13 12:54 ` Paul Cercueil 2026-11-13 18:07 ` Florian Fainelli 2026-11-13 18:15 ` Florian Fainelli 2026-11-12 18:44 ` [PATCH v4 3/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox
all inboxes | Powered by JetHome®