Linux clock framework development
 help / color / mirror / Atom feed
* [PATCH 0/3] MIPS: CI20: Enable SMP with two CPUs
@ 2026-11-05 20:32 Florian Fainelli
  2026-11-05 20:32 ` [PATCH 1/3] clk: ingenic: Poll JZ4780 CPU1 power status atomically Florian Fainelli
                   ` (2 more replies)
  0 siblings, 3 replies; 6+ messages in thread
From: Florian Fainelli @ 2026-11-05 20:32 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, H. Nikolaus Schaller, Paul Boddie

This patch series resurrects SMP support for the CI20 board. This was
submitted back in 2020 by Zhou Yanjie.

Florian Fainelli (1):
  clk: ingenic: Poll JZ4780 CPU1 power status atomically

周琰杰 (Zhou Yanjie) (2):
  MIPS: Ingenic: Add JZ4780 SMP support to the generic platform
  MIPS: CI20: Enable SMP with two CPUs

 MAINTAINERS                           |   1 +
 arch/mips/configs/ci20_defconfig      |   2 +
 arch/mips/generic/Makefile            |   1 +
 arch/mips/generic/ingenic-smp-entry.S |  54 ++++++
 arch/mips/generic/ingenic-smp.c       | 240 ++++++++++++++++++++++++++
 arch/mips/generic/ingenic-smp.h       |  20 +++
 arch/mips/generic/init.c              |   4 +
 arch/mips/ingenic/Kconfig             |   6 +
 arch/mips/kernel/idle.c               |  25 ++-
 drivers/clk/ingenic/jz4780-cgu.c      |   2 +-
 10 files changed, 353 insertions(+), 2 deletions(-)
 create mode 100644 arch/mips/generic/ingenic-smp-entry.S
 create mode 100644 arch/mips/generic/ingenic-smp.c
 create mode 100644 arch/mips/generic/ingenic-smp.h

-- 
2.53.0


^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/3] clk: ingenic: Poll JZ4780 CPU1 power status atomically
  2026-11-05 20:32 [PATCH 0/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli
@ 2026-11-05 20:32 ` Florian Fainelli
  2026-11-05 20:32 ` [PATCH 2/3] MIPS: Ingenic: Add JZ4780 SMP support to the generic platform Florian Fainelli
  2026-11-05 20:32 ` [PATCH 3/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli
  2 siblings, 0 replies; 6+ messages in thread
From: Florian Fainelli @ 2026-11-05 20:32 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, H. Nikolaus Schaller, Paul Boddie

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.

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
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
 drivers/clk/ingenic/jz4780-cgu.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/clk/ingenic/jz4780-cgu.c b/drivers/clk/ingenic/jz4780-cgu.c
index 1e88aef7ac0f..f5983033583a 100644
--- a/drivers/clk/ingenic/jz4780-cgu.c
+++ b/drivers/clk/ingenic/jz4780-cgu.c
@@ -242,7 +242,7 @@ 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,
+	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__);
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/3] MIPS: Ingenic: Add JZ4780 SMP support to the generic platform
  2026-11-05 20:32 [PATCH 0/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli
  2026-11-05 20:32 ` [PATCH 1/3] clk: ingenic: Poll JZ4780 CPU1 power status atomically Florian Fainelli
@ 2026-11-05 20:32 ` Florian Fainelli
  2026-11-05 21:05   ` sashiko-bot
  2026-11-05 20:32 ` [PATCH 3/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli
  2 siblings, 1 reply; 6+ messages in thread
From: Florian Fainelli @ 2026-11-05 20:32 UTC (permalink / raw)
  To: linux-mips
  Cc: 周琰杰 (Zhou Yanjie), Florian Fainelli,
	Thomas Bogendoerfer, Paul Burton, Paul Cercueil, Stephen Boyd,
	Brian Masney, Jerome Brunet, Jiaxun Yang, open list,
	open list:COMMON CLK FRAMEWORK, H. Nikolaus Schaller, Paul Boddie

From: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>

Port the JZ4780 reset-entry and CP0 mailbox implementation from Zhou
Yanjie's 2020 v8 series, based on Paul Burton's original CI20 SMP code.
The old arch/mips/jz4740 platform has moved into the generic MIPS code,
so register the Ingenic SMP operations after unflattening the device
tree and before selecting the generic fallback implementations.

Reuse the upstream CPU clock gates, CPU device-tree nodes and per-CPU
TCU timers. Enumerate only available CPU nodes within the two-core
hardware limit. Prepare the CPU1 clock before boot, propagate clock
failures, serialize mailbox accesses with a raw spinlock and publish
bootstrap state before releasing the secondary core from reset. Align
the secondary reset entry to the REIM register's 64 KiB granularity.
Retain the UP fallback for other SoCs in a shared Ingenic kernel.

Write back the private data cache before WAIT, since XBurst gates its
clock during idle and another core must not access dirty data in that
sleeping cache. Return with interrupts disabled as required by the
current idle API. Existing per-CPU TCU events make the old forced
broadcast-timer workaround unnecessary.

This adaptation incorporates the CPU-enumeration review feedback and
removes the old unused CPU mask. Preserve the original source credits;
historical Tested-by and Reviewed-by tags are not carried to this
substantially changed port. Adaptation and validation used Codex
assistance.

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
Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
[florian: forward ported to latest version]
Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
---
 MAINTAINERS                           |   1 +
 arch/mips/generic/Makefile            |   1 +
 arch/mips/generic/ingenic-smp-entry.S |  54 ++++++
 arch/mips/generic/ingenic-smp.c       | 240 ++++++++++++++++++++++++++
 arch/mips/generic/ingenic-smp.h       |  20 +++
 arch/mips/generic/init.c              |   4 +
 arch/mips/ingenic/Kconfig             |   6 +
 arch/mips/kernel/idle.c               |  25 ++-
 8 files changed, 350 insertions(+), 1 deletion(-)
 create mode 100644 arch/mips/generic/ingenic-smp-entry.S
 create mode 100644 arch/mips/generic/ingenic-smp.c
 create mode 100644 arch/mips/generic/ingenic-smp.h

diff --git a/MAINTAINERS b/MAINTAINERS
index 09713dc4c6a7..101027579ccd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -13117,6 +13117,7 @@ L:	linux-mips@vger.kernel.org
 S:	Maintained
 F:	arch/mips/boot/dts/ingenic/
 F:	arch/mips/generic/board-ingenic.c
+F:	arch/mips/generic/ingenic-smp*
 F:	arch/mips/include/asm/mach-ingenic/
 F:	arch/mips/ingenic/Kconfig
 F:	drivers/clk/ingenic/
diff --git a/arch/mips/generic/Makefile b/arch/mips/generic/Makefile
index ea0e4ad5e600..df92650c0f15 100644
--- a/arch/mips/generic/Makefile
+++ b/arch/mips/generic/Makefile
@@ -12,5 +12,6 @@ obj-$(CONFIG_YAMON_DT_SHIM)		+= yamon-dt.o
 obj-$(CONFIG_LEGACY_BOARD_SEAD3)	+= board-sead3.o
 obj-$(CONFIG_LEGACY_BOARD_OCELOT)	+= board-ocelot.o
 obj-$(CONFIG_MACH_INGENIC)			+= board-ingenic.o
+obj-$(CONFIG_MIPS_INGENIC_SMP)	+= ingenic-smp.o ingenic-smp-entry.o
 obj-$(CONFIG_VIRT_BOARD_RANCHU)		+= board-ranchu.o
 obj-$(CONFIG_MACH_REALTEK_RTL)		+= board-realtek.o
diff --git a/arch/mips/generic/ingenic-smp-entry.S b/arch/mips/generic/ingenic-smp-entry.S
new file mode 100644
index 000000000000..828df4687e84
--- /dev/null
+++ b/arch/mips/generic/ingenic-smp-entry.S
@@ -0,0 +1,54 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (C) 2013 Paul Burton <paul.burton@imgtec.com>
+ * JZ4780 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(jz4780_secondary_cpu_entry)
+	mtc0	zero, CP0_CAUSE
+	li	t0, ST0_CU0
+	mtc0	t0, CP0_STATUS
+	ehb
+
+	/* Invalidate both private caches before entering cached code. */
+	li	t0, KSEG0
+	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. */
+	la	t0, jz4780_cpu_entry_sp
+	lw	sp, 0(t0)
+	la	t0, jz4780_cpu_entry_gp
+	lw	gp, 0(t0)
+
+	la	t0, smp_bootstrap
+	jr	t0
+	 nop
+END(jz4780_secondary_cpu_entry)
diff --git a/arch/mips/generic/ingenic-smp.c b/arch/mips/generic/ingenic-smp.c
new file mode 100644
index 000000000000..98c1fd7483db
--- /dev/null
+++ b/arch/mips/generic/ingenic-smp.c
@@ -0,0 +1,240 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * JZ4780 SMP support, based on Paul Burton's CI20 implementation and
+ * Zhou Yanjie's 2020 JZ4780 SMP series.
+ * Copyright (C) 2013 Paul Burton <paul.burton@imgtec.com>
+ */
+
+#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 "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 JZ4780_NR_CPUS		2
+#define JZ4780_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 jz4780_cpu_entry_sp;
+unsigned long jz4780_cpu_entry_gp;
+
+static irqreturn_t jz4780_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 jz4780_smp_setup(void)
+{
+	struct device_node *np;
+	u32 entry, reim;
+	u64 hwid;
+
+	for_each_of_cpu_node(np) {
+		hwid = of_get_cpu_hwid(np, 0);
+		if (hwid >= nr_cpu_ids || hwid >= JZ4780_NR_CPUS ||
+		    !of_device_is_available(np))
+			continue;
+		__cpu_number_map[hwid] = hwid;
+		__cpu_logical_map[hwid] = hwid;
+		set_cpu_possible(hwid, true);
+	}
+
+	/* 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 = KSEG1ADDR((unsigned long)jz4780_secondary_cpu_entry);
+	write_c0_reim((reim & ~REIM_ENTRY) | (entry & REIM_ENTRY));
+}
+
+static void __init jz4780_prepare_cpus(unsigned int max_cpus)
+{
+	struct device_node *np;
+	u32 reim;
+	int ret;
+
+	ret = request_irq(JZ4780_MBOX_IRQ, jz4780_mbox_interrupt,
+			  IRQF_PERCPU | IRQF_NO_THREAD, "core mailbox", NULL);
+	if (ret)
+		panic("JZ4780: cannot request mailbox IRQ: %d", ret);
+
+	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("JZ4780: 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("JZ4780: cannot prepare CPU1 clock: %d\n", ret);
+	}
+}
+
+static int jz4780_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();
+
+	ret = clk_enable(cpu1_clock);
+	if (ret)
+		goto unlock;
+
+	jz4780_cpu_entry_sp = __KSTK_TOS(idle);
+	jz4780_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();
+	write_c0_corectrl(ctrl & ~CORECTRL_SWRST(1));
+	back_to_back_c0_hazard();
+unlock:
+	raw_spin_unlock_irqrestore(&smp_lock, flags);
+	return ret;
+}
+
+static void jz4780_init_secondary(void)
+{
+	clear_c0_status(ST0_IM);
+	irq_disable_hazard();
+}
+
+static void jz4780_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 jz4780_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 jz4780_send_ipi_single(int cpu, unsigned int action)
+{
+	unsigned long flags;
+
+	raw_spin_lock_irqsave(&smp_lock, flags);
+	jz4780_send_ipi_locked(cpu, action);
+	raw_spin_unlock_irqrestore(&smp_lock, flags);
+}
+
+static void jz4780_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)
+		jz4780_send_ipi_locked(cpu, action);
+	raw_spin_unlock_irqrestore(&smp_lock, flags);
+}
+
+static const struct plat_smp_ops jz4780_smp_ops = {
+	.send_ipi_single = jz4780_send_ipi_single,
+	.send_ipi_mask = jz4780_send_ipi_mask,
+	.init_secondary = jz4780_init_secondary,
+	.smp_finish = jz4780_smp_finish,
+	.boot_secondary = jz4780_boot_secondary,
+	.smp_setup = jz4780_smp_setup,
+	.prepare_cpus = jz4780_prepare_cpus,
+};
+
+int __init register_ingenic_smp_ops(void)
+{
+	if (!of_machine_is_compatible("ingenic,jz4780"))
+		return -ENODEV;
+
+	register_smp_ops(&jz4780_smp_ops);
+	return 0;
+}
diff --git a/arch/mips/generic/ingenic-smp.h b/arch/mips/generic/ingenic-smp.h
new file mode 100644
index 000000000000..54191be8731a
--- /dev/null
+++ b/arch/mips/generic/ingenic-smp.h
@@ -0,0 +1,20 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/* Copyright (C) 2013 Paul Burton <paul.burton@imgtec.com> */
+#ifndef __MIPS_GENERIC_INGENIC_SMP_H__
+#define __MIPS_GENERIC_INGENIC_SMP_H__
+
+#include <linux/errno.h>
+#include <linux/init.h>
+#include <linux/kconfig.h>
+
+#ifdef CONFIG_MIPS_INGENIC_SMP
+int __init register_ingenic_smp_ops(void);
+void jz4780_secondary_cpu_entry(void);
+#else
+static inline int register_ingenic_smp_ops(void)
+{
+	return -ENODEV;
+}
+#endif
+
+#endif /* __MIPS_GENERIC_INGENIC_SMP_H__ */
diff --git a/arch/mips/generic/init.c b/arch/mips/generic/init.c
index 1d712eac1617..00149bd1e9a9 100644
--- a/arch/mips/generic/init.c
+++ b/arch/mips/generic/init.c
@@ -20,6 +20,8 @@
 #include <asm/smp-ops.h>
 #include <asm/time.h>
 
+#include "ingenic-smp.h"
+
 static __initconst const void *fdt;
 static __initconst const struct mips_machine *mach;
 static __initconst const void *mach_match_data;
@@ -113,6 +115,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/ingenic/Kconfig b/arch/mips/ingenic/Kconfig
index f595b339a4b8..1dfd0bfd0193 100644
--- a/arch/mips/ingenic/Kconfig
+++ b/arch/mips/ingenic/Kconfig
@@ -64,6 +64,12 @@ config MACH_JZ4780
 	select MIPS_CPU_SCACHE
 	select SYS_HAS_CPU_MIPS32_R2
 	select SYS_SUPPORTS_HIGHMEM
+	select SYS_SUPPORTS_SMP
+
+config MIPS_INGENIC_SMP
+	def_bool y
+	depends on MACH_JZ4780 && SMP
+	select SMP_UP
 
 config MACH_X1000
 	bool
diff --git a/arch/mips/kernel/idle.c b/arch/mips/kernel/idle.c
index 80e8a04a642e..6cabdd4825a7 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,23 @@ 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 ingenic_smp_wait_irqoff(void)
+{
+	unsigned long pending = read_c0_cause() & read_c0_status() & CAUSEF_IP;
+
+	if (!need_resched() && !pending) {
+		r4k_blast_dcache();
+		__asm__ __volatile__(
+			".set push\n"
+			".set mips3\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 +174,15 @@ 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_MIPS_INGENIC_SMP))
+			cpu_wait = ingenic_smp_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) ||
-- 
2.53.0


^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/3] MIPS: CI20: Enable SMP with two CPUs
  2026-11-05 20:32 [PATCH 0/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli
  2026-11-05 20:32 ` [PATCH 1/3] clk: ingenic: Poll JZ4780 CPU1 power status atomically Florian Fainelli
  2026-11-05 20:32 ` [PATCH 2/3] MIPS: Ingenic: Add JZ4780 SMP support to the generic platform Florian Fainelli
@ 2026-11-05 20:32 ` Florian Fainelli
  2026-11-05 20:58   ` sashiko-bot
  2 siblings, 1 reply; 6+ messages in thread
From: Florian Fainelli @ 2026-11-05 20:32 UTC (permalink / raw)
  To: linux-mips
  Cc: 周琰杰 (Zhou Yanjie), Florian Fainelli,
	Thomas Bogendoerfer, Paul Burton, Paul Cercueil, Stephen Boyd,
	Brian Masney, Jerome Brunet, Jiaxun Yang, open list,
	open list:COMMON CLK FRAMEWORK, H. Nikolaus Schaller, Paul Boddie

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.

Rebase Zhou Yanjie's original defconfig update onto the current CI20
configuration. The resulting image brought up both cores on the board.
The rebase and validation used Codex assistance.

Link: https://lists.openwall.net/linux-kernel/2020/05/19/739
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 related	[flat|nested] 6+ messages in thread

* Re: [PATCH 3/3] MIPS: CI20: Enable SMP with two CPUs
  2026-11-05 20:32 ` [PATCH 3/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli
@ 2026-11-05 20:58   ` sashiko-bot
  0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-11-05 20:58 UTC (permalink / raw)
  To: Florian Fainelli; +Cc: open list:COMMON CLK FRAMEWORK

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] mips: ingenic: invalid lock nesting in jz4780_boot_secondary()
- [High] mips: ingenic: boot BUG_ON in mips_smp_ipi_allocate() and missing present mask
- [High] mips: ingenic: missing cpu_set_core() breaks cache IPIs under CONFIG_MIPS_CPS
- [Low] mips: ingenic: 64-bit build failure when CONFIG_MIPS_INGENIC_SMP=y
--

commit 481937342699e3f6c1c384229fba2460caf28933
Author: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>

MIPS: Ingenic: Add JZ4780 SMP support to the generic platform

This commit ports JZ4780 secondary core reset entry and mailbox support to the
generic MIPS platform, registering Ingenic multi-processor operations during
device tree initialization.

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

> diff --git a/arch/mips/generic/ingenic-smp.c b/arch/mips/generic/ingenic-smp.c
> new file mode 100644
> index 0000000000000..98c1fd7483db5
> --- /dev/null
> +++ b/arch/mips/generic/ingenic-smp.c
> @@ -0,0 +1,240 @@

[ ... ]

> +static struct clk *cpu1_clock;
> +static DEFINE_RAW_SPINLOCK(smp_lock);

[ ... ]

> +static void __init jz4780_smp_setup(void)
> +{
> +	struct device_node *np;
> +	u32 entry, reim;
> +	u64 hwid;
> +
> +	for_each_of_cpu_node(np) {
> +		hwid = of_get_cpu_hwid(np, 0);
> +		if (hwid >= nr_cpu_ids || hwid >= JZ4780_NR_CPUS ||
> +		    !of_device_is_available(np))
> +			continue;
> +		__cpu_number_map[hwid] = hwid;
> +		__cpu_logical_map[hwid] = hwid;
> +		set_cpu_possible(hwid, true);
> +	}

[Severity: High]
How does this behave when booting a generic kernel image with
CONFIG_MIPS_GENERIC_KERNEL=y and CONFIG_BOARD_INGENIC=y?

In jz4780_smp_setup(), discovered cores are marked possible with
set_cpu_possible(hwid, true), while neither jz4780_smp_setup() nor
jz4780_prepare_cpus() calls set_cpu_present(hwid, true). In
arch/mips/kernel/smp.c, smp_prepare_cpus() only populates cpu_present_mask
when CONFIG_HOTPLUG_CPU is disabled:

    void __init smp_prepare_cpus(unsigned int max_cpus)
    {
        ...
    #ifndef CONFIG_HOTPLUG_CPU
        init_cpu_present(cpu_possible_mask);
    #endif
        cpumask_copy(&cpu_coherent_mask, cpu_possible_mask);
    }

When CONFIG_HOTPLUG_CPU=y (such as in generic_defconfig), the secondary core
is never marked present and smp_init() will not bring it up.

On the other hand, when the secondary core is marked present,
CONFIG_MIPS_GENERIC_KERNEL selects CONFIG_MIPS_GIC and
CONFIG_SYS_SUPPORTS_MULTITHREADING, which select CONFIG_GENERIC_IRQ_IPI when
CONFIG_SMP=y. During do_pre_smp_initcalls(), mips_smp_ipi_init() calls
mips_smp_ipi_allocate(cpu_possible_mask):

    if (!ipidomain) {
        BUG_ON(num_present_cpus() > 1);
        return 0;
    }

Because JZ4780 uses its own mailbox handlers in jz4780_smp_ops rather than
registering a DOMAIN_BUS_IPI irq_domain, ipidomain is NULL. Won't this trigger
the BUG_ON(num_present_cpus() > 1) check at boot when running a generic kernel
with CONFIG_GENERIC_IRQ_IPI=y?

[Severity: High]
Also in jz4780_smp_setup() (and jz4780_init_secondary()), should
cpu_set_core(&cpu_data[hwid], hwid) be called so each core gets a distinct
core identifier?

In arch/mips/kernel/cpu-probe.c, decode_configs() only populates the core
number when CONFIG_MIPS_CPS is disabled:

    #ifndef CONFIG_MIPS_CPS
        if (cpu_has_mips_r2_r6) {
            unsigned int core;

            core = get_ebase_cpunum();
            if (cpu_has_mipsmt)
                core >>= fls(core_nvpes()) - 1;
            cpu_set_core(c, core);
        }
    #endif

When CONFIG_MIPS_CPS=y is enabled alongside CONFIG_MIPS_INGENIC_SMP=y (for
example in a generic kernel config), neither decode_configs() nor the Ingenic
platform code calls cpu_set_core(). Both cores then keep core number 0 in
cpu_data[cpu].globalnumber, causing cpus_are_siblings(0, 1) to return true
and calculate_cpu_foreign_map() to leave cpu_foreign_map[] empty.

In arch/mips/mm/c-r4k.c, r4k_op_needs_ipi() checks:

    #ifdef CONFIG_SMP
        return !cpumask_empty(&cpu_foreign_map[0]);
    #else
        return false;
    #endif

With cpu_foreign_map[0] empty, won't r4k_on_each_cpu() skip cross-core
instruction and data cache flush calls between the two cores even though they
have separate non-coherent primary caches?

[ ... ]

> +static int jz4780_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();
> +
> +	ret = clk_enable(cpu1_clock);
> +	if (ret)
> +		goto unlock;

[Severity: High]
Is it valid to call clk_enable(cpu1_clock) while holding the raw spinlock
smp_lock?

In jz4780_boot_secondary(), smp_lock is a raw_spinlock_t acquired via
raw_spin_lock_irqsave(&smp_lock, flags). Calling clk_enable(cpu1_clock) under
smp_lock enters:

    jz4780_boot_secondary()
      raw_spin_lock_irqsave(&smp_lock, flags)
      clk_enable(cpu1_clock)
        clk_core_enable_lock()
          clk_enable_lock()
            spin_lock_irqsave(&enable_lock, flags)
          clk_core_enable()
            jz4780_core1_enable()
              spin_lock_irqsave(&cgu->lock, flags)

Both enable_lock in drivers/clk/clk.c and cgu->lock in
drivers/clk/ingenic/jz4780-cgu.c are non-raw spinlock_t locks, and
jz4780_core1_enable() also busy-polls CGU_REG_LCR via
readl_poll_timeout_atomic() for up to 5000 us. Doesn't nesting non-raw
spinlock_t acquisitions inside a raw_spinlock_t critical section violate
lockdep wait-context rules (triggering an invalid wait context warning when
CONFIG_PROVE_RAW_LOCK_NESTING is enabled and sleeping while atomic on
PREEMPT_RT)?

[ ... ]

> diff --git a/arch/mips/ingenic/Kconfig b/arch/mips/ingenic/Kconfig
> index f595b339a4b84..1dfd0bfd01937 100644
> --- a/arch/mips/ingenic/Kconfig
> +++ b/arch/mips/ingenic/Kconfig
> @@ -64,6 +64,12 @@ config MACH_JZ4780
>  	select MIPS_CPU_SCACHE
>  	select SYS_HAS_CPU_MIPS32_R2
>  	select SYS_SUPPORTS_HIGHMEM
> +	select SYS_SUPPORTS_SMP
> +
> +config MIPS_INGENIC_SMP
> +	def_bool y
> +	depends on MACH_JZ4780 && SMP
> +	select SMP_UP

[Severity: Low]
Can CONFIG_MIPS_INGENIC_SMP be enabled on a 64-bit generic kernel build?

In arch/mips/generic/Kconfig, CONFIG_BOARD_INGENIC can be enabled under
CONFIG_MIPS_GENERIC_KERNEL with CONFIG_64BIT=y, which selects
CONFIG_MACH_INGENIC_GENERIC and CONFIG_MACH_JZ4780. When CONFIG_SMP=y is also
enabled, CONFIG_MIPS_INGENIC_SMP is automatically selected.

When CONFIG_64BIT=y, arch/mips/include/asm/addrspace.h does not define
KSEG1ADDR or KSEG0, so building arch/mips/generic/ingenic-smp.c fails in
jz4780_smp_setup():

    entry = KSEG1ADDR((unsigned long)jz4780_secondary_cpu_entry);

and assembling arch/mips/generic/ingenic-smp-entry.S fails in
jz4780_secondary_cpu_entry at KSEG0 and at the 32-bit la and lw instructions
used on jz4780_cpu_entry_sp, jz4780_cpu_entry_gp, and smp_bootstrap:

    li	t0, KSEG0
    ...
    la	t0, jz4780_cpu_entry_sp
    lw	sp, 0(t0)
    la	t0, jz4780_cpu_entry_gp
    lw	gp, 0(t0)

    la	t0, smp_bootstrap

Should MIPS_INGENIC_SMP (or BOARD_INGENIC) depend on 32BIT, or use
pointer-sized macros and CKSEG0 / CKSEG1ADDR?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261105203243.2292647-1-f.fainelli@gmail.com?part=3

^ permalink raw reply	[flat|nested] 6+ messages in thread

* Re: [PATCH 2/3] MIPS: Ingenic: Add JZ4780 SMP support to the generic platform
  2026-11-05 20:32 ` [PATCH 2/3] MIPS: Ingenic: Add JZ4780 SMP support to the generic platform Florian Fainelli
@ 2026-11-05 21:05   ` sashiko-bot
  0 siblings, 0 replies; 6+ messages in thread
From: sashiko-bot @ 2026-11-05 21:05 UTC (permalink / raw)
  To: Florian Fainelli; +Cc: open list:COMMON CLK FRAMEWORK

Thank you for your contribution! Sashiko AI review found 6 potential issue(s) to consider:
- [High] MIPS: Ingenic: boot BUG_ON in mips_smp_ipi_allocate() on generic kernel
- [High] MIPS: idle: check_wait() selects SMP wait on UP XBurst SoCs in shared kernels
- [High] MIPS: Ingenic: missing cpu_set_core() breaks cache flush IPIs under MIPS_CPS
- [High] MIPS: Ingenic: invalid lock nesting in jz4780_boot_secondary()
- [High] MIPS: Ingenic: missing cpu_present, hotplug ops, and IPI guard on JZ4780
- [Low] MIPS: Ingenic: 64-bit generic kernel build failure in ingenic-smp
--

commit 481937342699e3f6c1c384229fba2460caf28933
Author: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>

MIPS: Ingenic: Add JZ4780 SMP support to the generic platform

This commit ports JZ4780 SMP support to the generic MIPS platform, adding the
secondary CPU reset entry, CP0 mailbox IPI handling, CPU clock bringup, and
SMP operations registration. It also adds an XBurst SMP idle wait routine that
writes back the private data cache before executing the wait instruction.

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

> diff --git a/arch/mips/generic/ingenic-smp-entry.S b/arch/mips/generic/ingenic-smp-entry.S
> new file mode 100644
> index 0000000000000..828df4687e844
> --- /dev/null
> +++ b/arch/mips/generic/ingenic-smp-entry.S
> @@ -0,0 +1,54 @@

[ ... ]

> +LEAF(jz4780_secondary_cpu_entry)
> +	mtc0	zero, CP0_CAUSE
> +	li	t0, ST0_CU0
> +	mtc0	t0, CP0_STATUS
> +	ehb
> +
> +	/* Invalidate both private caches before entering cached code. */
> +	li	t0, KSEG0
> +	ori	t1, t0, CACHE_SIZE

[Severity: Low]
Can this break the build on a 64-bit generic MIPS kernel configuration?

In arch/mips/Kconfig, CONFIG_MIPS_GENERIC_KERNEL selects
SYS_SUPPORTS_64BIT_KERNEL and SYS_SUPPORTS_MIPS_CPS. In
arch/mips/generic/Kconfig, CONFIG_BOARD_INGENIC depends only on
MIPS_GENERIC_KERNEL without a 32BIT dependency and selects
MACH_INGENIC_GENERIC, which in turn selects MACH_JZ4780. Because
CONFIG_MIPS_INGENIC_SMP in arch/mips/ingenic/Kconfig depends only on
MACH_JZ4780 && SMP, enabling CONFIG_BOARD_INGENIC=y and CONFIG_SMP=y in a
CONFIG_64BIT=y kernel builds arch/mips/generic/ingenic-smp-entry.S and
arch/mips/generic/ingenic-smp.c.

In arch/mips/include/asm/addrspace.h, KSEG0 and KSEG1ADDR are only defined
for !CONFIG_64BIT (whereas CKSEG0 and CKSEG1ADDR are defined for both 32-bit
and 64-bit kernels). Under CONFIG_64BIT=y, jz4780_secondary_cpu_entry fails
to assemble due to undeclared KSEG0 (and uses 32-bit la/lw instructions to
load the unsigned long globals jz4780_cpu_entry_sp and jz4780_cpu_entry_gp),
and jz4780_smp_setup() in arch/mips/generic/ingenic-smp.c fails to compile
due to undeclared KSEG1ADDR:

    entry = KSEG1ADDR((unsigned long)jz4780_secondary_cpu_entry);

Should CONFIG_MIPS_INGENIC_SMP (or CONFIG_BOARD_INGENIC) depend on 32BIT, or
use CKSEG0/CKSEG1ADDR and PTR_LA/LONG_L?

> diff --git a/arch/mips/generic/ingenic-smp.c b/arch/mips/generic/ingenic-smp.c
> new file mode 100644
> index 0000000000000..98c1fd7483db5
> --- /dev/null
> +++ b/arch/mips/generic/ingenic-smp.c
> @@ -0,0 +1,240 @@

[ ... ]

> +static void __init jz4780_smp_setup(void)
> +{
> +	struct device_node *np;
> +	u32 entry, reim;
> +	u64 hwid;
> +
> +	for_each_of_cpu_node(np) {
> +		hwid = of_get_cpu_hwid(np, 0);
> +		if (hwid >= nr_cpu_ids || hwid >= JZ4780_NR_CPUS ||
> +		    !of_device_is_available(np))
> +			continue;
> +		__cpu_number_map[hwid] = hwid;
> +		__cpu_logical_map[hwid] = hwid;
> +		set_cpu_possible(hwid, true);
> +	}

[Severity: High]
Does missing cpu_set_core() here (or in jz4780_init_secondary()) suppress
cross-CPU cache flush IPIs when CONFIG_MIPS_CPS=y is enabled in a generic
kernel?

Generic MIPS kernels (CONFIG_MIPS_GENERIC_KERNEL=y) typically enable
CONFIG_MIPS_CPS=y. On JZ4780, device_tree_init() registers jz4780_smp_ops
via register_ingenic_smp_ops() and returns before register_cps_smp_ops().

During CPU probing on both CPU0 and CPU1, decode_configs() in
arch/mips/kernel/cpu-probe.c skips setting the core ID from EBase.CPUNum
when CONFIG_MIPS_CPS is enabled:

    #ifndef CONFIG_MIPS_CPS
        if (cpu_has_mips_r2_r6) {
            unsigned int core;

            core = get_ebase_cpunum();
            if (cpu_has_mipsmt)
                core >>= fls(core_nvpes()) - 1;
            cpu_set_core(c, core);
        }
    #endif

Because neither jz4780_smp_setup() nor jz4780_init_secondary() calls
cpu_set_core(&cpu_data[hwid], hwid), both cpu_data[0] and cpu_data[1] retain
core ID 0 (globalnumber == 0) and package == 0.

When start_secondary() calls calculate_cpu_foreign_map() in
arch/mips/kernel/smp.c, cpus_are_siblings(1, 0) sees matching package and
core numbers and returns true, leaving cpu_foreign_map[0] empty. In
arch/mips/mm/c-r4k.c, r4k_op_needs_ipi() checks:

    return !cpumask_empty(&cpu_foreign_map[0]);

which returns false on all CPUs, so r4k_on_each_cpu() never sends IPIs to
flush the remote core's private 32 KiB L1 I-cache and D-cache during cache
maintenance operations, and populate_cache_leaves() and show_cpuinfo()
report both CPUs as core 0 sharing L1 caches.

[ ... ]

> +static int jz4780_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();
> +
> +	ret = clk_enable(cpu1_clock);
> +	if (ret)
> +		goto unlock;

[Severity: High]
Does calling clk_enable(cpu1_clock) while holding the raw spinlock smp_lock
violate raw_spinlock_t -> spinlock_t lock nesting rules?

When __cpu_up() calls jz4780_boot_secondary(), smp_lock is acquired via
raw_spin_lock_irqsave(&smp_lock, flags) (wait type LD_WAIT_SPIN). While
holding smp_lock, clk_enable(cpu1_clock) runs:

    jz4780_boot_secondary()
      raw_spin_lock_irqsave(&smp_lock, flags)
      clk_enable(cpu1_clock)
        clk_core_enable_lock()
          clk_enable_lock()
            spin_lock_irqsave(&enable_lock, flags)
          clk_core_enable()
            jz4780_core1_enable()
              spin_lock_irqsave(&cgu->lock, flags)

Both enable_lock in drivers/clk/clk.c and cgu->lock in
drivers/clk/ingenic/jz4780-cgu.c are non-raw spinlock_t locks (wait type
LD_WAIT_CONFIG), which triggers a lockdep invalid wait context splat under
CONFIG_PROVE_RAW_LOCK_NESTING and holds smp_lock with interrupts disabled
across the CGU_REG_LCR polling loop of up to 5000 us in
jz4780_core1_enable().

[ ... ]

> +static const struct plat_smp_ops jz4780_smp_ops = {
> +	.send_ipi_single = jz4780_send_ipi_single,
> +	.send_ipi_mask = jz4780_send_ipi_mask,
> +	.init_secondary = jz4780_init_secondary,
> +	.smp_finish = jz4780_smp_finish,
> +	.boot_secondary = jz4780_boot_secondary,
> +	.smp_setup = jz4780_smp_setup,
> +	.prepare_cpus = jz4780_prepare_cpus,
> +};

[Severity: High]
How does jz4780_smp_ops behave when CONFIG_HOTPLUG_CPU=y is enabled (for
example via CONFIG_MIPS_GENERIC_KERNEL=y with CONFIG_MIPS_CPS=y and
CONFIG_SUSPEND=y)?

First, in arch/mips/kernel/smp.c, smp_prepare_cpus() only copies
cpu_possible_mask to cpu_present_mask when CONFIG_HOTPLUG_CPU is disabled:

    #ifndef CONFIG_HOTPLUG_CPU
        init_cpu_present(cpu_possible_mask);
    #endif

Because neither jz4780_smp_setup() nor jz4780_prepare_cpus() calls
set_cpu_present(), CPU1 is never marked present when CONFIG_HOTPLUG_CPU=y,
preventing bringup_nonboot_cpus() and _cpu_up() from booting CPU1.

Second, under CONFIG_HOTPLUG_CPU=y, __cpu_disable() and __cpu_die() in
arch/mips/include/asm/smp.h unconditionally dereference mp_ops->cpu_disable
and mp_ops->cpu_die without NULL checks, whereas jz4780_smp_ops leaves both
callbacks NULL.

> diff --git a/arch/mips/generic/init.c b/arch/mips/generic/init.c
> index 1d712eac16171..00149bd1e9a97 100644
> --- a/arch/mips/generic/init.c
> +++ b/arch/mips/generic/init.c
> @@ -113,6 +115,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())

[Severity: High]
When booting a CONFIG_MIPS_GENERIC_KERNEL=y image with CONFIG_BOARD_INGENIC=y
and CONFIG_SMP=y on a dual-core JZ4780, will mips_smp_ipi_init() trigger a
boot-time BUG_ON() in mips_smp_ipi_allocate()?

In arch/mips/Kconfig, CONFIG_MIPS_GENERIC_KERNEL selects MIPS_GIC as well as
IRQ_MIPS_CPU and SYS_SUPPORTS_MULTITHREADING, which select
CONFIG_GENERIC_IRQ_IPI=y when CONFIG_SMP=y.

During boot on JZ4780, device_tree_init() registers jz4780_smp_ops and
jz4780_smp_setup() marks 2 CPUs as possible. When kernel_init_freeable()
runs do_pre_smp_initcalls(), the early_initcall mips_smp_ipi_init() in
arch/mips/kernel/smp.c sees num_possible_cpus() > 1 and calls
mips_smp_ipi_allocate(cpu_possible_mask):

    static int __init mips_smp_ipi_init(void)
    {
        if (num_possible_cpus() == 1)
            return 0;

        mips_smp_ipi_allocate(cpu_possible_mask);

Because JZ4780 uses its own CP0 mailbox IPI callbacks
(jz4780_send_ipi_single and jz4780_send_ipi_mask) rather than a
DOMAIN_BUS_IPI irq_domain, ipidomain is NULL in mips_smp_ipi_allocate():

    if (!ipidomain) {
        BUG_ON(num_present_cpus() > 1);
        return 0;
    }

Whenever num_present_cpus() > 1, this hits BUG_ON(num_present_cpus() > 1).

> diff --git a/arch/mips/kernel/idle.c b/arch/mips/kernel/idle.c
> index 80e8a04a642e0..6cabdd4825a7b 100644
> --- a/arch/mips/kernel/idle.c
> +++ b/arch/mips/kernel/idle.c
> @@ -156,10 +174,15 @@ 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_MIPS_INGENIC_SMP))
> +			cpu_wait = ingenic_smp_wait_irqoff;
> +		else
> +			cpu_wait = r4k_wait;
> +		break;

[Severity: High]
Does checking IS_ENABLED(CONFIG_MIPS_INGENIC_SMP) at compile time in
check_wait() cause single-core XBurst SoCs in a shared kernel to use
ingenic_smp_wait_irqoff() instead of r4k_wait()?

In arch/mips/ingenic/Kconfig, shared Ingenic configurations
(CONFIG_INGENIC_GENERIC_BOARD or CONFIG_BOARD_INGENIC) select
CONFIG_MACH_INGENIC_GENERIC, which selects MACH_JZ4740, MACH_JZ4725B,
MACH_JZ4770, MACH_JZ4780, MACH_X1000, and MACH_X1830. When CONFIG_SMP=y is
enabled in that shared kernel, CONFIG_MIPS_INGENIC_SMP is unconditionally
enabled at build time.

All of those SoCs set c->cputype = CPU_XBURST in cpu_probe_ingenic(). When a
shared kernel boots on a uniprocessor XBurst SoC (or on JZ4780 with a single
active CPU), register_ingenic_smp_ops() falls back to register_up_smp_ops(),
leaving num_possible_cpus() == 1. However, check_wait() still assigns
cpu_wait = ingenic_smp_wait_irqoff because IS_ENABLED(CONFIG_MIPS_INGENIC_SMP)
is true.

As a result, every idle transition on single-core XBurst SoCs flushes and
invalidates the entire L1 data cache via r4k_blast_dcache() and executes the
wait instruction with CP0_Status.IE cleared (which also prevents interrupts
from waking MIPS32r1 XBurst cores such as JZ4740 and JZ4725B from wait).
Should check_wait() also check at runtime whether multiple CPUs are present
or possible (for example, num_possible_cpus() > 1)?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20261105203243.2292647-1-f.fainelli@gmail.com?part=2

^ permalink raw reply	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-11-05 21:05 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-11-05 20:32 [PATCH 0/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli
2026-11-05 20:32 ` [PATCH 1/3] clk: ingenic: Poll JZ4780 CPU1 power status atomically Florian Fainelli
2026-11-05 20:32 ` [PATCH 2/3] MIPS: Ingenic: Add JZ4780 SMP support to the generic platform Florian Fainelli
2026-11-05 21:05   ` sashiko-bot
2026-11-05 20:32 ` [PATCH 3/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli
2026-11-05 20:58   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox