From: Paul Cercueil <paul@crapouillou.net>
To: Florian Fainelli <f.fainelli@gmail.com>, linux-mips@vger.kernel.org
Cc: "周琰杰 (Zhou Yanjie)" <zhouyanjie@wanyeetech.com>,
"Thomas Bogendoerfer" <tsbogend@alpha.franken.de>,
"Paul Burton" <paulburton@kernel.org>,
"Stephen Boyd" <sboyd@kernel.org>,
"Brian Masney" <bmasney+clk@redhat.com>,
"Jerome Brunet" <jbrunet+clk@baylibre.com>,
"Jiaxun Yang" <jiaxun.yang@flygoat.com>,
"open list" <linux-kernel@vger.kernel.org>,
"open list:COMMON CLK FRAMEWORK" <linux-clk@vger.kernel.org>,
hns@goldelico.com, paul@boddie.org.uk
Subject: Re: [PATCH v2 2/3] MIPS: Ingenic: Add XBurst SMP support
Date: Tue, 10 Nov 2026 16:30:46 +0100 [thread overview]
Message-ID: <1a29bb3bb70e761393b36520f9bb2a16ed466f14.camel@crapouillou.net> (raw)
In-Reply-To: <20261110043131.1985993-3-f.fainelli@gmail.com>
Hi Florian,
Le lundi 09 novembre 2026 à 20:31 -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
> Signed-off-by: 周琰杰 (Zhou Yanjie) <zhouyanjie@wanyeetech.com>
> [florian: forward ported to latest version]
> Signed-off-by: Florian Fainelli <f.fainelli@gmail.com>
Reviewed-by: Paul Cercueil <paul@crapouillou.net>
Cheers,
-Paul
> ---
> 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 | 247 ++++++++++++++++++++++++++
> arch/mips/kernel/idle.c | 26 ++-
> 9 files changed, 359 insertions(+), 2 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..250a0c89c955
> --- /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. */
> + 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, xburst_cpu_entry_sp
> + lw sp, 0(t0)
> + la t0, xburst_cpu_entry_gp
> + lw gp, 0(t0)
> +
> + 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..3cdb953c7a87
> --- /dev/null
> +++ b/arch/mips/ingenic/ingenic-smp.c
> @@ -0,0 +1,247 @@
> +// 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);
> + 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 = KSEG1ADDR((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();
> +
> + ret = clk_enable(cpu1_clock);
> + if (ret)
> + goto unlock;
> +
> + 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();
> + write_c0_corectrl(ctrl & ~CORECTRL_SWRST(1));
> + back_to_back_c0_hazard();
> +unlock:
> + raw_spin_unlock_irqrestore(&smp_lock, flags);
> + return ret;
> +}
> +
> +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);
> +}
> +
> +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,
> +};
> +
> +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..46526997a4c6 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 xburst_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,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))
> + 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) ||
next prev parent reply other threads:[~2026-11-10 15:30 UTC|newest]
Thread overview: 8+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-11-10 4:31 [PATCH v2 0/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli
2026-11-10 4:31 ` [PATCH v2 1/3] clk: ingenic: Poll JZ4780 CPU1 power status atomically Florian Fainelli
2026-11-10 4:31 ` [PATCH v2 2/3] MIPS: Ingenic: Add XBurst SMP support Florian Fainelli
2026-11-10 15:30 ` Paul Cercueil [this message]
2026-11-10 4:31 ` [PATCH v2 3/3] MIPS: CI20: Enable SMP with two CPUs Florian Fainelli
2026-11-10 15:31 ` Paul Cercueil
2026-11-10 15:44 ` [PATCH v2 0/3] " Paul Cercueil
2026-11-10 17:14 ` Florian Fainelli
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1a29bb3bb70e761393b36520f9bb2a16ed466f14.camel@crapouillou.net \
--to=paul@crapouillou.net \
--cc=bmasney+clk@redhat.com \
--cc=f.fainelli@gmail.com \
--cc=hns@goldelico.com \
--cc=jbrunet+clk@baylibre.com \
--cc=jiaxun.yang@flygoat.com \
--cc=linux-clk@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mips@vger.kernel.org \
--cc=paul@boddie.org.uk \
--cc=paulburton@kernel.org \
--cc=sboyd@kernel.org \
--cc=tsbogend@alpha.franken.de \
--cc=zhouyanjie@wanyeetech.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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®