mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
From: Navid Ghahremani <ghahramani.navid@gmail.com>
To: linux-arm-kernel@lists.infradead.org
Cc: arnd@arndb.de, stefandoesinger@gmail.com, krzk@kernel.org,
	Navid Ghahremani <ghahramani.navid@gmail.com>,
	Russell King <linux@armlinux.org.uk>,
	linux-kernel@vger.kernel.org
Subject: [PATCH v2 03/24] ARM: zte: Add ZX279128S platform and CPU hotplug support
Date: Wed, 14 Oct 2026 08:20:43 +0100	[thread overview]
Message-ID: <20261014072109.1759502-4-ghahramani.navid@gmail.com> (raw)
In-Reply-To: <20261014072109.1759502-1-ghahramani.navid@gmail.com>

Support the dual Cortex-A9 platform, second-CPU startup through
bootloader SRAM and CPU hotplug. Keep existing ZX297520V3 support
intact.

Signed-off-by: Navid Ghahremani <ghahramani.navid@gmail.com>
---
 arch/arm/mach-zte/Kconfig             |  16 +++
 arch/arm/mach-zte/Makefile            |   4 +
 arch/arm/mach-zte/platsmp-zx279128s.c | 173 ++++++++++++++++++++++++++
 arch/arm/mach-zte/zx279128s.c         |  19 +++
 4 files changed, 212 insertions(+)
 create mode 100644 arch/arm/mach-zte/platsmp-zx279128s.c
 create mode 100644 arch/arm/mach-zte/zx279128s.c

diff --git a/arch/arm/mach-zte/Kconfig b/arch/arm/mach-zte/Kconfig
index d3b404ca48..032079c958 100644
--- a/arch/arm/mach-zte/Kconfig
+++ b/arch/arm/mach-zte/Kconfig
@@ -26,4 +26,20 @@ config SOC_ZX297520V3
 	  Please read Documentation/arch/arm/zte/zx297520v3.rst on how to boot
 	  the kernel.
 
+config SOC_ZX279128S
+	bool "zx279128s SoC"
+	default y
+	select ARM_AMBA
+	select ARM_GIC
+	select ARM_GLOBAL_TIMER
+	select CACHE_L2X0
+	select CLKSRC_ARM_GLOBAL_TIMER_SCHED_CLOCK
+	select HAVE_ARM_SCU if SMP
+	select HAVE_ARM_TWD if SMP
+	help
+	  Support for the ZTE (Sanechips) zx279128s SoC, a dual-core
+	  Cortex-A9 with an L2C-310 cache controller, used in home gateways
+	  such as the ZTE ZXHN H3600. The second core is started through
+	  the on-chip SRAM, where the boot loader keeps it waiting.
+
 endif
diff --git a/arch/arm/mach-zte/Makefile b/arch/arm/mach-zte/Makefile
index 1bfe4fddd6..72f518632e 100644
--- a/arch/arm/mach-zte/Makefile
+++ b/arch/arm/mach-zte/Makefile
@@ -1,2 +1,6 @@
 # SPDX-License-Identifier: GPL-2.0-only
 obj-$(CONFIG_SOC_ZX297520V3) += zx297520v3.o
+obj-$(CONFIG_SOC_ZX279128S) += zx279128s.o
+ifdef CONFIG_SMP
+obj-$(CONFIG_SOC_ZX279128S) += platsmp-zx279128s.o
+endif
diff --git a/arch/arm/mach-zte/platsmp-zx279128s.c b/arch/arm/mach-zte/platsmp-zx279128s.c
new file mode 100644
index 0000000000..8285b8fb10
--- /dev/null
+++ b/arch/arm/mach-zte/platsmp-zx279128s.c
@@ -0,0 +1,173 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Navid Ghahremani <ghahramani.navid@gmail.com>
+ *
+ * The boot loader runs from on-chip SRAM.  Its reset code sends every
+ * core but CPU0 into a loop: wait for an event (WFE), then jump to the
+ * start of the SRAM, where the boot loader's reset vector leads back to
+ * the WFE.  To start the second core, replace that vector with a jump to
+ * secondary_startup and send an event.
+ *
+ * To take the second core offline again (CPU hotplug, and kexec, which needs
+ * it), the core puts the boot loader's vector back and returns to the boot
+ * loader's loop with the MMU and caches off. That loop is outside the
+ * kernel's memory, so the core survives the kernel being replaced, and the
+ * next start works the same way as the first.
+ */
+
+#include <linux/cacheflush.h>
+#include <linux/io.h>
+#include <linux/iopoll.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/smp.h>
+
+#include <asm/cp15.h>
+#include <asm/idmap.h>
+#include <asm/proc-fns.h>
+#include <asm/smp_scu.h>
+#include <asm/tlbflush.h>
+
+/* ldr pc, [pc, #-4]: jump to the address stored in the next word */
+#define ZX279128S_JUMP_INSN	0xe51ff004
+
+static void __iomem *zx279128s_smp_sram;
+static phys_addr_t zx279128s_smp_sram_phys;
+/* the boot loader's vector, as found at boot */
+static u32 zx279128s_boot_vector[2];
+
+static int zx279128s_boot_secondary(unsigned int cpu, struct task_struct *idle)
+{
+	if (!zx279128s_smp_sram)
+		return -ENODEV;
+
+	/*
+	 * Store the address before the instruction: a core that wakes up
+	 * in between still runs the boot loader's vector and goes back to
+	 * sleep instead of jumping to a stale address.
+	 */
+	writel(__pa_symbol(secondary_startup), zx279128s_smp_sram + 4);
+	writel(ZX279128S_JUMP_INSN, zx279128s_smp_sram);
+
+	/* Complete both writes, then wake the core */
+	dsb_sev();
+
+	return 0;
+}
+
+static void __init zx279128s_smp_prepare_cpus(unsigned int max_cpus)
+{
+	struct device_node *np;
+	struct resource res;
+	void __iomem *scu;
+
+	np = of_find_compatible_node(NULL, NULL, "arm,cortex-a9-scu");
+	if (!np) {
+		pr_err("zx279128s: no SCU node\n");
+		return;
+	}
+	scu = of_iomap(np, 0);
+	of_node_put(np);
+	if (!scu) {
+		pr_err("zx279128s: cannot map the SCU\n");
+		return;
+	}
+	scu_enable(scu);
+	iounmap(scu);
+
+	np = of_find_compatible_node(NULL, NULL, "zte,zx279128s-smp-sram");
+	if (!np) {
+		pr_err("zx279128s: no SMP SRAM node\n");
+		return;
+	}
+	if (of_address_to_resource(np, 0, &res)) {
+		of_node_put(np);
+		pr_err("zx279128s: no SMP SRAM address\n");
+		return;
+	}
+	zx279128s_smp_sram = of_iomap(np, 0);
+	of_node_put(np);
+	if (!zx279128s_smp_sram) {
+		pr_err("zx279128s: cannot map the SMP SRAM\n");
+		return;
+	}
+	zx279128s_smp_sram_phys = res.start;
+	zx279128s_boot_vector[0] = readl(zx279128s_smp_sram);
+	zx279128s_boot_vector[1] = readl(zx279128s_smp_sram + 4);
+
+	/* zx279128s_cpu_die() reads these with its data cache off */
+	sync_cache_w(&zx279128s_smp_sram);
+	sync_cache_w(&zx279128s_smp_sram_phys);
+	sync_cache_w(&zx279128s_boot_vector);
+}
+
+#ifdef CONFIG_HOTPLUG_CPU
+typedef void (*phys_reset_t)(unsigned long addr, bool hvc);
+
+/*
+ * If the vector already holds a jump, an earlier kernel started the core
+ * and did not put the boot loader's vector back: the original is unknown,
+ * so the core cannot be parked safely.
+ */
+static bool zx279128s_cpu_can_disable(unsigned int cpu)
+{
+	return zx279128s_smp_sram &&
+	       zx279128s_boot_vector[0] != ZX279128S_JUMP_INSN;
+}
+
+static void zx279128s_cpu_die(unsigned int cpu)
+{
+	phys_reset_t phys_reset;
+
+	/* Take out a flat mapping, which keeps the kernel mappings too */
+	setup_mm_for_reboot();
+
+	/*
+	 * The boot loader's loop executes the vector, which
+	 * zx279128s_boot_secondary() changes with data writes. Stop caching
+	 * and predicting instructions, as at power-on, so that the parked
+	 * core sees the change.
+	 */
+	set_cr(get_cr() & ~(CR_I | CR_Z));
+	__flush_icache_all();
+	local_flush_bp_all();
+
+	/* Clean the caches and leave coherency with the other core */
+	v7_exit_coherency_flush(louis);
+
+	/*
+	 * Put the boot loader's vector back, instruction word last: the core
+	 * then wakes up into the boot loader's loop, and zx279128s_cpu_kill()
+	 * sees that it is about to leave the kernel.
+	 */
+	writel_relaxed(zx279128s_boot_vector[1], zx279128s_smp_sram + 4);
+	writel_relaxed(zx279128s_boot_vector[0], zx279128s_smp_sram);
+	dsb();
+
+	/* Turn the MMU off and continue at the boot loader's vector */
+	phys_reset = (phys_reset_t)virt_to_idmap(cpu_reset);
+	phys_reset(zx279128s_smp_sram_phys, false);
+}
+
+static int zx279128s_cpu_kill(unsigned int cpu)
+{
+	u32 val;
+
+	/* Wait until the dying core has put the vector back */
+	return !readl_poll_timeout(zx279128s_smp_sram, val,
+				   val == zx279128s_boot_vector[0], 1000,
+				   100 * USEC_PER_MSEC);
+}
+#endif
+
+static const struct smp_operations zx279128s_smp_ops __initconst = {
+	.smp_prepare_cpus	= zx279128s_smp_prepare_cpus,
+	.smp_boot_secondary	= zx279128s_boot_secondary,
+#ifdef CONFIG_HOTPLUG_CPU
+	.cpu_can_disable	= zx279128s_cpu_can_disable,
+	.cpu_die		= zx279128s_cpu_die,
+	.cpu_kill		= zx279128s_cpu_kill,
+#endif
+};
+
+CPU_METHOD_OF_DECLARE(zx279128s_smp, "zte,zx279128s-smp", &zx279128s_smp_ops);
diff --git a/arch/arm/mach-zte/zx279128s.c b/arch/arm/mach-zte/zx279128s.c
new file mode 100644
index 0000000000..064c76f8ad
--- /dev/null
+++ b/arch/arm/mach-zte/zx279128s.c
@@ -0,0 +1,19 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (C) 2026 Navid Ghahremani <ghahramani.navid@gmail.com>
+ */
+
+#include <asm/mach/arch.h>
+#include <linux/init.h>
+
+static const char *const zx279128s_dt_compat[] __initconst = {
+	"zte,zx279128s",
+	NULL,
+};
+
+DT_MACHINE_START(ZX279128S, "ZTE zx279128s (Device Tree)")
+	.dt_compat	= zx279128s_dt_compat,
+	/* Keep the L2 cache setup of the boot loader and the device tree */
+	.l2c_aux_val	= 0,
+	.l2c_aux_mask	= ~0,
+MACHINE_END
-- 
2.55.0


  parent reply	other threads:[~2026-10-14  7:22 UTC|newest]

Thread overview: 45+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
     [not found] <20261014072109.1759502-1-ghahramani.navid@gmail.com>
2026-10-14  7:20 ` [PATCH v2 01/24] MAINTAINERS: Consolidate ARM/ZTE ZX SOC SUPPORT with Stefan Doesinger Navid Ghahremani
2026-10-14 20:11   ` Stefan Dösinger
2026-10-14  7:20 ` [PATCH v2 02/24] dt-bindings: arm: Add ZTE ZX279128S platform descriptions Navid Ghahremani
2026-10-14  7:20 ` Navid Ghahremani [this message]
2026-10-14  7:20 ` [PATCH v2 04/24] dt-bindings: clock: Add ZTE ZX279128S CRM clock bindings Navid Ghahremani
2026-10-14 21:48   ` Stefan Dösinger
2026-10-15  9:25   ` Krzysztof Kozlowski
2026-10-15  9:26   ` Krzysztof Kozlowski
2026-10-14  7:20 ` [PATCH v2 05/24] dt-bindings: reset: Add ZTE ZX279128S reset controller bindings Navid Ghahremani
2026-10-15  9:27   ` Krzysztof Kozlowski
2026-10-14  7:20 ` [PATCH v2 06/24] clk: zte: Add ZX279128S CRM clock and reset driver Navid Ghahremani
2026-10-14 13:21   ` Philipp Zabel
2026-10-14 19:39   ` Stefan Dösinger
2026-10-14  7:20 ` [PATCH v2 07/24] dt-bindings: gpio: Add ZTE ZX279128S GPIO controller Navid Ghahremani
2026-10-14  7:20 ` [PATCH v2 08/24] gpio: Add ZTE ZX279128S GPIO driver Navid Ghahremani
2026-10-14 21:11   ` Stefan Dösinger
2026-10-15 12:25   ` Bartosz Golaszewski
2026-10-14  7:20 ` [PATCH v2 09/24] dt-bindings: pinctrl: pinctrl-single: Add ZTE ZX279128S pin mux Navid Ghahremani
2026-10-14  7:20 ` [PATCH v2 10/24] dt-bindings: phy: Add ZTE ZX279128S PCIe PHY binding Navid Ghahremani
2026-10-15  9:29   ` Krzysztof Kozlowski
2026-10-14  7:20 ` [PATCH v2 11/24] phy: zte: Add ZTE ZX279128S PCIe PHY driver Navid Ghahremani
2026-10-14  7:20 ` [PATCH v2 12/24] dt-bindings: PCI: Add ZTE ZX279128S host controller Navid Ghahremani
2026-10-14  7:20 ` [PATCH v2 13/24] PCI: dwc: Add ZTE ZX279128S host controller driver Navid Ghahremani
2026-10-14 13:19   ` Bjorn Helgaas
2026-10-14 21:06     ` Stefan Dösinger
2026-10-14  7:20 ` [PATCH v2 14/24] dt-bindings: net: Add ZTE ZX279128S MDIO controller Navid Ghahremani
2026-10-14  7:20 ` [PATCH v2 15/24] net: mdio: " Navid Ghahremani
2026-10-14 12:13   ` Andrew Lunn
2026-10-14  7:20 ` [PATCH v2 16/24] net: phy: Add Sanechips ZX5201 PHY support Navid Ghahremani
2026-10-14 12:37   ` Andrew Lunn
2026-10-14  7:20 ` [PATCH v2 17/24] dt-bindings: net: Add ZTE ZX279128S Ethernet switch Navid Ghahremani
2026-10-14  7:20 ` [PATCH v2 18/24] net: ethernet: zte: Add ZX279128S Ethernet switch driver Navid Ghahremani
2026-10-14  7:20 ` [PATCH v2 19/24] dt-bindings: spi: Add ZTE ZX family SPI flash controller binding Navid Ghahremani
2026-10-14 20:33   ` Stefan Dösinger
2026-10-15  9:35   ` Krzysztof Kozlowski
2026-10-15  9:36   ` Krzysztof Kozlowski
2026-10-14  7:21 ` [PATCH v2 20/24] spi: zx: Add ZTE ZX family SPI flash controller driver Navid Ghahremani
2026-10-14 19:28   ` Stefan Dösinger
2026-10-15  9:34   ` Krzysztof Kozlowski
2026-10-15 14:29   ` kernel test robot
2026-10-14  7:21 ` [PATCH v2 21/24] dt-bindings: usb: Add ZTE ZX279128S DWC3 controller Navid Ghahremani
2026-10-14  7:21 ` [PATCH v2 22/24] usb: dwc3: generic-plat: Add ZTE ZX279128S Navid Ghahremani
2026-10-14  7:21 ` [PATCH v2 23/24] ARM: dts: zte: Add ZX279128S SoC description Navid Ghahremani
2026-10-14  7:21 ` [PATCH v2 24/24] ARM: dts: zte: Add ZTE ZXHN H3600 board Navid Ghahremani
2026-10-14 12:52   ` Andrew Lunn

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=20261014072109.1759502-4-ghahramani.navid@gmail.com \
    --to=ghahramani.navid@gmail.com \
    --cc=arnd@arndb.de \
    --cc=krzk@kernel.org \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux@armlinux.org.uk \
    --cc=stefandoesinger@gmail.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®