diff --git a/Documentation/arch/arm/index.rst b/Documentation/arch/arm/index.rst index afe17db294c4..b15621093f7a 100644 --- a/Documentation/arch/arm/index.rst +++ b/Documentation/arch/arm/index.rst @@ -75,3 +75,5 @@ SoC-specific documents sti/overview vfp/release-notes + + zte/index diff --git a/Documentation/arch/arm/zte/index.rst b/Documentation/arch/arm/zte/index.rst new file mode 100644 index 000000000000..0ed80b60b746 --- /dev/null +++ b/Documentation/arch/arm/zte/index.rst @@ -0,0 +1,10 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +======= +ZTE SoC +======= + +.. toctree:: + :maxdepth: 1 + + zx297520v3 diff --git a/Documentation/arch/arm/zte/zx297520v3.rst b/Documentation/arch/arm/zte/zx297520v3.rst new file mode 100644 index 000000000000..2122887e391a --- /dev/null +++ b/Documentation/arch/arm/zte/zx297520v3.rst @@ -0,0 +1,167 @@ +.. SPDX-License-Identifier: GPL-2.0-only + +==================================== +Booting Linux on ZTE zx297520v3 SoCs +==================================== + +............................................................................... + +Author: Stefan Dösinger + +Date : 27 Jan 2026 + +1. Hardware description +--------------------------- +Zx297520v3 SoCs use a 64 bit capable Cortex-A53 CPU and GICv3, although they +run in arm32 mode only. The CPU has support EL3, but no hypervisor (EL2) and +it seems to lack VFP and NEON. + +The SoC is used in a number of cheap LTE to WiFi routers, both battery powered +MiFis and stationary CPEs. In addition to the CPU these devices usually have +64 MB Ram (although some is shared with the LTE chip), 128 MB NAND flash, an +SDIO connected RTL8192-type Wifi chip limited to 2.4 ghz operation, USB 2, +and buttons. Devices with as low as 32 MB or as high as 128 MB ram exist, as +do devices with 8 or 16 MB of NOR flash. + +Some devices, especially the stationary ones, have 100 mbit Ethernet and an +Ethernet switch. + +Usually the devices have LEDs for status indication, although some have SPI or +I2C connected displays + +Some have an SD card slot. If it exists, it is a better choice for the root +file system because it easily outperforms the built-in NAND. + +The LTE interface runs on a separate DSP called ZSP880. It is probably derived +from LSI ZSPs and has an undocumented instruction set. The ZSP communicates +with the main CPU via SRAM and DRAM and a mailbox hardware that can generate +IRQs on either ends. + +There is also a Cortex M0 CPU, which is responsible for early HW initialization +and starting the Cortex A53 CPU. It does not have any essential purpose once +U-Boot is started. A SRAM-Based handover protocol exists to run custom code on +this CPU. + +2. Booting via USB +--------------------------- + +The Boot ROM has support for booting custom code via USB. This mode can be +entered by connecting a Boot PIN to GND or by modifying the third byte on NAND +(set it to anything other than 0x5A aka 'Z'). A free software tool to start +custom U-Boot and kernels can be found here: + +https://github.com/zx297520v3-mainline/zx297520v3-loader + +If USB download mode is entered but no boot commands are sent through USB, the +device will proceed to boot normally after a few seconds. It is therefore +possible to enable USB boot permanently and still leave the default boot files +in place. + +https://github.com/zx297520v3-mainline/u-boot-mainline + +Contains an U-Boot version that can be used with the USB loader and sets up the +CPU and interrupt controller to comply with Linux's booting requirements. + +3. Building for built-in U-Boot +--------------------------- +The devices come with an ancient U-Boot that loads legacy uImages from NAND and +boots them without a chance for the user to interrupt. The images are stored in +files ap_cpuap.bin and ap_recovery.bin on a jffs2 partition named imagefs, +usually mtd4. A file named "fotaflag" switches between the two modes. + +In addition to the uImage header, those files have a 384 byte signature header, +which is used for authenticating the images on some devices. Most devices have +this authentication disabled and it is enough to pad the uImage files with 384 +zero bytes. + +Builtin U-Boot also poorly sets up the CPU. Read the next section for details +on this. It has no support for loading DTBs, so CONFIG_ARM_APPENDED_DTB is +needed. + +So to build an image that boots from NAND the following steps are necessary: + +1) Patch the assembly code from section 3 into arch/arm/kernel/head.S. +2) make zx29_defconfig +3) make [-j x] +4) cat arch/arm/boot/zImage arch/arm/boot/dts/zte/[device].dtb > kernel+dtb +5) mkimage -A arm -O linux -T kernel -C none -a 0x20008000 -d kernel+dtb uimg +6) dd if=/dev/zero bs=1 count=384 of=ap_recovery.bin +7) cat uimg >> ap_recovery.bin +8) Place this file onto imagefs on the device. Delete ap_cpuap.bin if the +free space is not enough. +9) Create the file fotaflag: echo -n FOTA-RECOVERY > fotaflag + +For development, booting ap_recovery.bin is recommended because the normal boot +mode arms the watchdog before starting the kernel. + +4. CPU and GIC Setup +--------------------------- + +Generally CPU and GICv3 need to be set up according to the requirements spelled +out in Documentation/arch/arm64/booting.rst. For zx297520v3 this means: + +1. GICD_CTLR.DS=1 to disable GIC security +2. Enable access to ICC_SRE +3. Disable trapping IRQs into monitor mode +4. Configure EL2 and below to run in insecure mode. +5. Configure timer PPIs to active-low. + +The kernel sources provided by ZTE do not boot either (interrupts do not work +at all). They are incomplete in other aspects too, so it is assumed that there +is some workaround similar to the one described in this document somewhere in +the binary blobs. + +The assembly code below is given as an example of how to achieve this: + +``` +#include +#include +#include + +@ Detect sane bootloaders and skip the hack +ldr r3, =0xf2000000 +ldr r3, [r3] +ldr r4, =(GICD_CTLR_ARE_NS | GICD_CTLR_DS) +cmp r3, r4 +beq skip_zx_hack +@ This allows EL1 to handle ints hat are normally handled by EL2/3. +ldr r3, =0xf2000000 +str r4, [r3] + +cps #MON_MODE + +@ Work in non-secure physical address space: SCR_EL3.NS = 1. At least the UART +@ seems to respond only to non-secure addresses. I have taken insipiration from +@ Raspberry pi's armstub7.S here. +mov r3, #0x131 @ non-secure, Make F, A bits in CPSR writeable + @ Allow hypervisor call. +mcr p15, 0, r3, c1, c1, 0 + +@ AP_PPI_MODE_REG: Configure timer PPIs (10, 11, 13, 14) to active-low. +ldr r3, =0xF22020a8 +ldr r4, =0x50 +str r4, [r3] +ldr r3, =0xF22020ac +ldr r4, =0x14 +str r4, [r3] + +@ Enable EL2 access to ICC_SRE (bit 3, ICC_SRE_EL3.Enable). Enable system reg +@ access to GICv3 registers (bit 0, ICC_SRE_EL3.SRE) for EL1 and EL3. +mrc p15, 6, r3, c12, c12, 5 @ ICC_SRE_EL3 +orr r3, #0x9 @ FIXME: No defines for SRE_EL3 values? +mcr p15, 6, r3, c12, c12, 5 +mrc p15, 0, r3, c12, c12, 5 @ ICC_SRE_EL1 +orr r3, #(ICC_SRE_EL1_SRE) +mcr p15, 0, r3, c12, c12, 5 + +@ Like ICC_SRE_EL3, enable EL1 access to ICC_SRE and system register access +@ for EL2. +mrc p15, 4, r3, c12, c9, 5 @ ICC_SRE_EL2 aka ICC_HSRE +orr r3, r3, #(ICC_SRE_EL2_ENABLE | ICC_SRE_EL2_SRE) +mcr p15, 4, r3, c12, c9, 5 +isb + +@ Back to SVC mode +cps #SVC_MODE +skip_zx_hack: +``` diff --git a/MAINTAINERS b/MAINTAINERS index 2fb1c75afd16..22368f96be9f 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3782,6 +3782,15 @@ F: drivers/video/fbdev/vt8500lcdfb.* F: drivers/video/fbdev/wm8505fb* F: drivers/video/fbdev/wmt_ge_rops.* +ARM/ZTE ZX29 SOC SUPPORT +M: Stefan Dösinger +L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) +S: Odd fixes +F: Documentation/arch/arm/zte/ +F: Documentation/devicetree/bindings/arm/zte.yaml +F: arch/arm/boot/dts/zte/ +F: arch/arm/mach-zte/ + ARM/ZYNQ ARCHITECTURE M: Michal Simek L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers) diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 71fc5dd4123f..c1045154412a 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -465,6 +465,8 @@ source "arch/arm/mach-versatile/Kconfig" source "arch/arm/mach-vt8500/Kconfig" +source "arch/arm/mach-zte/Kconfig" + source "arch/arm/mach-zynq/Kconfig" # ARMv7-M architecture diff --git a/arch/arm/Makefile b/arch/arm/Makefile index b7de4b6b284c..573813ef5e77 100644 --- a/arch/arm/Makefile +++ b/arch/arm/Makefile @@ -223,6 +223,7 @@ machine-$(CONFIG_ARCH_SUNXI) += sunxi machine-$(CONFIG_ARCH_TEGRA) += tegra machine-$(CONFIG_ARCH_U8500) += ux500 machine-$(CONFIG_ARCH_VT8500) += vt8500 +machine-$(CONFIG_ARCH_ZTE) += zte machine-$(CONFIG_ARCH_ZYNQ) += zynq machine-$(CONFIG_PLAT_VERSATILE) += versatile machine-$(CONFIG_PLAT_SPEAR) += spear diff --git a/arch/arm/mach-zte/Kconfig b/arch/arm/mach-zte/Kconfig new file mode 100644 index 000000000000..d3b404ca488d --- /dev/null +++ b/arch/arm/mach-zte/Kconfig @@ -0,0 +1,29 @@ +# SPDX-License-Identifier: GPL-2.0-only +menuconfig ARCH_ZTE + bool "ZTE zx family" + depends on ARCH_MULTI_V7 + help + Support for ZTE zx-based family of processors. + +if ARCH_ZTE + +config SOC_ZX297520V3 + bool "zx297520v3 SoC" + default y + select ARM_GIC_V3 + # This board does not have PSCI firmware, but ARM_GIC_V3 depends on + # ARM_PSCI_FW being enabled. + select ARM_PSCI_FW + select ARM_AMBA + select HAVE_ARM_ARCH_TIMER + select PM_GENERIC_DOMAINS if PM + help + Support for ZTE zx297520v3 SoC. It is a single core SoC used in cheap + LTE to WiFi routers. These devices can be identified by the occurrence + of the string "zx297520v3" in the boot output and /proc/cpuinfo of + their stock firmware. + + Please read Documentation/arch/arm/zte/zx297520v3.rst on how to boot + the kernel. + +endif diff --git a/arch/arm/mach-zte/Makefile b/arch/arm/mach-zte/Makefile new file mode 100644 index 000000000000..1bfe4fddd6af --- /dev/null +++ b/arch/arm/mach-zte/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0-only +obj-$(CONFIG_SOC_ZX297520V3) += zx297520v3.o diff --git a/arch/arm/mach-zte/zx297520v3.c b/arch/arm/mach-zte/zx297520v3.c new file mode 100644 index 000000000000..06f71348459e --- /dev/null +++ b/arch/arm/mach-zte/zx297520v3.c @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2026 Stefan Dösinger + */ + +#include +#include + +static const char *const zx297520v3_dt_compat[] __initconst = { + "zte,zx297520v3", + NULL, +}; + +DT_MACHINE_START(ZX, "ZTE zx297520v3 (Device Tree)") + .dt_compat = zx297520v3_dt_compat, +MACHINE_END