Re: [PATCH v2 5/5] platform: generic: spacemit: k3: add platform support
Heinrich Schuchardt <[email protected]>
| Newsgroups | org.infradead.lists.opensbi |
|---|---|
| Message-ID | <[email protected]> |
On 8/18/26 03:14, Troy Mitchell wrote: > From: Xianbin Zhu <[email protected]> > > Add initial OpenSBI platform support for the SpacemiT K3 SoC, which has > 16 harts: eight X100 harts in clusters C0/C1 and eight A100 harts in > C2/C3. > > During cold boot, the boot hart sets warm-boot entry vectors for all > four clusters, enables CCI snooping, clears PMU power-down vote bits for > all harts so WFI does not trigger power-down, and wakes all non-boot > harts (1-15). Wait up to 100 ms for each secondary hart to enter WFI, > then warn and continue so one failed hart cannot stall boot indefinitely. > > Run per-hart initialization from the nascent hook on both cold- and > warm-boot paths. Program PMA attributes, enable the D-cache and I-cache > through MSETUP, set smpen[x] in ML2SETUP to enable L1 D-cache snooping > and prefetch, and enable the H extension in MISA for X100 harts. MISA.H > is writable on K3 X100 cores, and M-mode must set it explicitly to > activate hypervisor support. > > Start secondary harts through an assembly stub that enables caches and > sets smpen[x] before entering common warm-boot code. This lets them > observe shared-data writes made by the boot hart during cold-boot > initialization. Enable global CCI-550 snoop and DVM requests on the boot > hart before waking the secondary harts. > > Signed-off-by: Xianbin Zhu <[email protected]> > Co-developed-by: Troy Mitchell <[email protected]> > Signed-off-by: Troy Mitchell <[email protected]> > --- > platform/generic/Kconfig | 5 + > platform/generic/configs/defconfig | 1 + > platform/generic/include/spacemit/common.h | 4 +- > platform/generic/include/spacemit/k3.h | 144 ++++++++++++++++ > platform/generic/include/spacemit/k3_asm.h | 16 ++ > platform/generic/spacemit/k3.c | 266 +++++++++++++++++++++++++++++ > platform/generic/spacemit/k3_asm.S | 33 ++++ > platform/generic/spacemit/objects.mk | 2 + > 8 files changed, 470 insertions(+), 1 deletion(-) > > diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig > index 958610d3..0302eda6 100644 > --- a/platform/generic/Kconfig > +++ b/platform/generic/Kconfig > @@ -109,6 +109,11 @@ config PLATFORM_SPACEMIT_K1 > select PLATFORM_SPACEMIT > default n > > +config PLATFORM_SPACEMIT_K3 > + bool "Spacemit K3 support" > + select PLATFORM_SPACEMIT > + default n > + > config CPU_MIPS_P8700 > bool > default n > diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig > index 341f6b25..58d40898 100644 > --- a/platform/generic/configs/defconfig > +++ b/platform/generic/configs/defconfig > @@ -15,6 +15,7 @@ CONFIG_PLATFORM_THEAD=y > CONFIG_PLATFORM_MIPS_P8700_EYEQ7H=y > CONFIG_PLATFORM_MIPS_P8700_BOSTON=y > CONFIG_PLATFORM_SPACEMIT_K1=y > +CONFIG_PLATFORM_SPACEMIT_K3=y > CONFIG_FDT_CACHE=y > CONFIG_FDT_CACHE_ANDES_LLCACHE=y > CONFIG_FDT_CACHE_SIFIVE_CCACHE=y > diff --git a/platform/generic/include/spacemit/common.h b/platform/generic/include/spacemit/common.h > index 317bb3e9..7b50cc4f 100644 > --- a/platform/generic/include/spacemit/common.h > +++ b/platform/generic/include/spacemit/common.h > @@ -9,7 +9,9 @@ > #ifndef __RISCV_SPACEMIT_COMMON_H__ > #define __RISCV_SPACEMIT_COMMON_H__ > > +#ifndef __ASSEMBLY__ > #include <sbi/sbi_bitops.h> > +#endif > > #define CSR_MSETUP 0x7c0 > #define CSR_ML2SETUP 0x7f0 > @@ -74,7 +76,7 @@ > #define PMU_AP_IDLE_CORE_IDLE BIT(0) > /* bit 1: CORE_PWRDWN - power off core on WFI (requires CORE_IDLE set) */ > #define PMU_AP_IDLE_CORE_PWRDWN BIT(1) > -/* bit 2: Core L1 SRAM Power Down - reserved/not used in K1 and K3 */ > +/* bit 2: Core L1 SRAM Power Down - reserved in K1, used in K3 */ > #define PMU_AP_IDLE_CORE_L1_SRAM_PWRDWN BIT(2) > /* bit 3: MASK_GIC_NIRQ_TO_CORE - mask nIRQ from GIC, auto-cleared by HW on power-down entry */ > #define PMU_AP_IDLE_MASK_GIC_NIRQ BIT(3) > diff --git a/platform/generic/include/spacemit/k3.h b/platform/generic/include/spacemit/k3.h > new file mode 100644 > index 00000000..e484d67b > --- /dev/null > +++ b/platform/generic/include/spacemit/k3.h > @@ -0,0 +1,144 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > +/* > + * Copyright (c) 2026 SpacemiT > + * Authors: > + * Xianbin Zhu <[email protected]> > + * Troy Mitchell <[email protected]> > + */ > + > +#ifndef __RISCV_SPACEMIT_K3_H__ > +#define __RISCV_SPACEMIT_K3_H__ > + > +#include <spacemit/common.h> > + > +/* PMA configuration CSR */ > +#define CSR_PMACFG0 0x7de > + > +/* > + * PMACFG0 audio buffer region field: bits [55:48]. > + * Mask and attribute are applied after a 48-bit shift because > + * csrc/csrs only accept a 12-bit immediate or a register. > + */ > +#define PMACFG0_AUDIO_BUF_FIELD_MASK 0xff > +#define PMACFG0_AUDIO_BUF_FIELD_SHIFT 48 > +#define PMACFG0_AUDIO_BUF_ATTR_CACHEABLE 0x20 > + > +/* > + * PMACFG0 XIP region field: bits [23:16]. > + * Lower 12 bits are zero so GAS emits a single lui instruction. > + */ > +#define PMACFG0_XIP_FIELD_CLEAR 0x00ff0000UL > +#define PMACFG0_XIP_ATTR_IO 0x00220000UL > + > +#define CSR_ML2HINT 0x7f7 > + > +/* Enable instruction prefetch in L2 cache on miss */ > +#define ML2SETUP_IPRF BIT(16) > +/* Enable TLB prefetch in L2 cache */ > +#define ML2SETUP_TPRF BIT(18) > +/* Enable top-of-clk of trace */ > +#define ML2HINT_TRACE_TOP_ICGEN BIT(26) > + > +/* > + * Combined MSETUP flags enabled at boot time: DE|IE|BPE|PFE|MME|ECCE. > + * Expressed as a plain hex constant so it can be used in assembly (BIT() > + * expands to 1UL<<n which GAS cannot evaluate). > + */ > +#define MSETUP_BOOT_FLAGS 0x10073 > + > +/* A100 cores: hart 8-11 belong to C2 cluster, hart 12-15 to C3 cluster */ > +#define PMU_CAP_CORE8_WAKEUP (PMU_AP_BASE + 0x360) > +#define PMU_CAP_CORE9_WAKEUP (PMU_AP_BASE + 0x364) > +#define PMU_CAP_CORE10_WAKEUP (PMU_AP_BASE + 0x368) > +#define PMU_CAP_CORE11_WAKEUP (PMU_AP_BASE + 0x36c) > +#define PMU_CAP_CORE12_WAKEUP (PMU_AP_BASE + 0x22c) > +#define PMU_CAP_CORE13_WAKEUP (PMU_AP_BASE + 0x230) > +#define PMU_CAP_CORE14_WAKEUP (PMU_AP_BASE + 0x234) > +#define PMU_CAP_CORE15_WAKEUP (PMU_AP_BASE + 0x238) > + > +/* A100 per-core idle config registers (harts 8-15) */ > +#define PMU_CAP_CORE8_IDLE_CFG (PMU_AP_BASE + 0x340) > +#define PMU_CAP_CORE9_IDLE_CFG (PMU_AP_BASE + 0x344) > +#define PMU_CAP_CORE10_IDLE_CFG (PMU_AP_BASE + 0x348) > +#define PMU_CAP_CORE11_IDLE_CFG (PMU_AP_BASE + 0x34c) > +#define PMU_CAP_CORE12_IDLE_CFG (PMU_AP_BASE + 0x20c) > +#define PMU_CAP_CORE13_IDLE_CFG (PMU_AP_BASE + 0x210) > +#define PMU_CAP_CORE14_IDLE_CFG (PMU_AP_BASE + 0x214) > +#define PMU_CAP_CORE15_IDLE_CFG (PMU_AP_BASE + 0x218) > + > +/* CX CAPMP cluster idle config registers */ > +#define PMU_CX_CAPMP_IDLE_CFG0 (PMU_AP_BASE + 0x120) > +#define PMU_CX_CAPMP_IDLE_CFG1 (PMU_AP_BASE + 0x0e4) > +#define PMU_CX_CAPMP_IDLE_CFG2 (PMU_AP_BASE + 0x150) > +#define PMU_CX_CAPMP_IDLE_CFG3 (PMU_AP_BASE + 0x154) > +#define PMU_CX_CAPMP_IDLE_CFG4 (PMU_AP_BASE + 0x314) > +#define PMU_CX_CAPMP_IDLE_CFG5 (PMU_AP_BASE + 0x318) > +#define PMU_CX_CAPMP_IDLE_CFG6 (PMU_AP_BASE + 0x31c) > +#define PMU_CX_CAPMP_IDLE_CFG7 (PMU_AP_BASE + 0x320) > +#define PMU_CX_CAPMP_IDLE_CFG8 (PMU_AP_BASE + 0x350) > +#define PMU_CX_CAPMP_IDLE_CFG9 (PMU_AP_BASE + 0x354) > +#define PMU_CX_CAPMP_IDLE_CFG10 (PMU_AP_BASE + 0x358) > +#define PMU_CX_CAPMP_IDLE_CFG11 (PMU_AP_BASE + 0x35c) > +#define PMU_CX_CAPMP_IDLE_CFG12 (PMU_AP_BASE + 0x21c) > +#define PMU_CX_CAPMP_IDLE_CFG13 (PMU_AP_BASE + 0x220) > +#define PMU_CX_CAPMP_IDLE_CFG14 (PMU_AP_BASE + 0x224) > +#define PMU_CX_CAPMP_IDLE_CFG15 (PMU_AP_BASE + 0x228) > + > +#define PMU_AP_IDLE_PWRDOWN_K3_MASK (PMU_AP_IDLE_CORE_IDLE | \ > + PMU_AP_IDLE_CORE_PWRDWN | \ > + PMU_AP_IDLE_CORE_L1_SRAM_PWRDWN | \ > + PMU_AP_IDLE_MASK_GIC_NIRQ | \ > + PMU_AP_IDLE_MASK_GIC_NFIQ) > + > +/* > + * PMU CX per-cluster idle control bits (PMU_CX_CAPMPn_IDLE_CFG). > + */ > +/* bit 0: MP Idle - gate cluster clocks externally on WFI */ > +#define PMU_CX_IDLE_MP_IDLE BIT(0) > +/* bit 1: MP Power Down - power off cluster logic when idle */ > +#define PMU_CX_IDLE_MP_PWRDWN BIT(1) > +/* bit 2: L2 Cache SRAM Power Down */ > +#define PMU_CX_IDLE_L2_SRAM_PWRDWN BIT(2) > +/* bit 3: SCU SRAM Power Down - not used, SCU SRAM does not support retention */ > +#define PMU_CX_IDLE_MP_SCU_SRAM_PWRDWN BIT(3) > +/* bit 7: ACNACTM Hardware Control - HW controls ACNACTM port on MP low power entry */ > +#define PMU_CX_IDLE_ACNACTM_HW_CTRL BIT(7) > + > +#define CLUSTER_PWR_DOWN_VALUE (PMU_CX_IDLE_MP_IDLE | \ > + PMU_CX_IDLE_MP_PWRDWN | \ > + PMU_CX_IDLE_L2_SRAM_PWRDWN | \ > + PMU_CX_IDLE_MP_SCU_SRAM_PWRDWN | \ > + PMU_CX_IDLE_ACNACTM_HW_CTRL) > + > +/* boot entry for A100 clusters */ > +#define C2_RVBADDR_LO_ADDR (0xd4282c00 + 0x3e8) > +#define C2_RVBADDR_HI_ADDR (0xd4282c00 + 0x3ec) > +#define C3_RVBADDR_LO_ADDR (0xd4282c00 + 0x260) > +#define C3_RVBADDR_HI_ADDR (0xd4282c00 + 0x264) > + > +/* > + * PMU AP core WFI status registers. Each bit indicates the corresponding > + * core has entered WFI idle state. > + * > + * X100 harts (0-7): PMU_AP_X100_IDLE_STATUS > + * cluster 0 (harts 0-3): bits 4, 7, 10, 13 > + * cluster 1 (harts 4-7): bits 20, 23, 26, 29 > + * A100 harts (8-15): PMU_AP_A100_IDLE_STATUS > + * cluster 2 (harts 8-11): bits 4, 7, 10, 13 > + * cluster 3 (harts 12-15): bits 20, 23, 26, 29 > + */ > +#define PMU_AP_X100_IDLE_STATUS 0xd4282890 > +#define PMU_AP_A100_IDLE_STATUS 0xd4282880 > + > +#define PLATFORM_A100_FIRST_ID 8 > +#define PLATFORM_A100_LAST_ID 15 > + > +#define PLAT_CCI_K3_AIDMA_IFACE_IX 0 > +#define PLAT_CCI_K3_CLUSTER0_IFACE_IX 1 > +#define PLAT_CCI_K3_CLUSTER1_IFACE_IX 2 > +#define PLAT_CCI_K3_CLUSTER2_0_IFACE_IX 3 > +#define PLAT_CCI_K3_CLUSTER2_1_IFACE_IX 4 > +#define PLAT_CCI_K3_CLUSTER3_0_IFACE_IX 5 > +#define PLAT_CCI_K3_CLUSTER3_1_IFACE_IX 6 > + > +#endif /* __RISCV_SPACEMIT_K3_H__ */ > diff --git a/platform/generic/include/spacemit/k3_asm.h b/platform/generic/include/spacemit/k3_asm.h > new file mode 100644 > index 00000000..70c00478 > --- /dev/null > +++ b/platform/generic/include/spacemit/k3_asm.h > @@ -0,0 +1,16 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > +/* > + * Copyright (c) 2026 SpacemiT > + * Authors: > + * Xianbin Zhu <[email protected]> > + * Troy Mitchell <[email protected]> > + */ > + > +#ifndef __RISCV_SPACEMIT_K3_ASM_H__ > +#define __RISCV_SPACEMIT_K3_ASM_H__ > + > +#ifndef __ASSEMBLY__ > +void _spacemit_k3_warm_start(void); > +#endif > + > +#endif /* __RISCV_SPACEMIT_K3_ASM_H__ */ > diff --git a/platform/generic/spacemit/k3.c b/platform/generic/spacemit/k3.c > new file mode 100644 > index 00000000..1e8d8c97 > --- /dev/null > +++ b/platform/generic/spacemit/k3.c > @@ -0,0 +1,266 @@ > +/* > + * SPDX-License-Identifier: BSD-2-Clause > + * > + * Copyright (c) 2026 SpacemiT > + * Authors: > + * Xianbin Zhu <[email protected]> > + * Troy Mitchell <[email protected]> > + */ > + > +#include <platform_override.h> > +#include <sbi/riscv_asm.h> > +#include <sbi/riscv_io.h> > +#include <sbi/sbi_console.h> > +#include <sbi/sbi_timer.h> > +#include <spacemit/k3.h> > +#include <spacemit/k3_asm.h> > +#include <spacemit/spacemit.h> > + > +#define PLATFORM_MAX_CPUS 16 > + > +/* > + * Maximum time to wait for each secondary hart to enter WFI after it is > + * woken during cold boot. Use the platform timer so the timeout does not > + * depend on CPU frequency or MMIO latency. > + */ > +#define HART_WFI_TIMEOUT_MS 100 > + > +static const int cci_map[] = { > + PLAT_CCI_K3_AIDMA_IFACE_IX, > + PLAT_CCI_K3_CLUSTER0_IFACE_IX, > + PLAT_CCI_K3_CLUSTER1_IFACE_IX, > + PLAT_CCI_K3_CLUSTER2_0_IFACE_IX, > + PLAT_CCI_K3_CLUSTER2_1_IFACE_IX, > + PLAT_CCI_K3_CLUSTER3_0_IFACE_IX, > + PLAT_CCI_K3_CLUSTER3_1_IFACE_IX, > +}; > + > +static const unsigned long cx_capmp_idle_cfg[] = { > + PMU_CX_CAPMP_IDLE_CFG0, PMU_CX_CAPMP_IDLE_CFG1, > + PMU_CX_CAPMP_IDLE_CFG2, PMU_CX_CAPMP_IDLE_CFG3, > + PMU_CX_CAPMP_IDLE_CFG4, PMU_CX_CAPMP_IDLE_CFG5, > + PMU_CX_CAPMP_IDLE_CFG6, PMU_CX_CAPMP_IDLE_CFG7, > + PMU_CX_CAPMP_IDLE_CFG8, PMU_CX_CAPMP_IDLE_CFG9, > + PMU_CX_CAPMP_IDLE_CFG10, PMU_CX_CAPMP_IDLE_CFG11, > + PMU_CX_CAPMP_IDLE_CFG12, PMU_CX_CAPMP_IDLE_CFG13, > + PMU_CX_CAPMP_IDLE_CFG14, PMU_CX_CAPMP_IDLE_CFG15, > +}; > + > +static const unsigned long core_pwrdown_reg[] = { > + PMU_AP_CORE0_IDLE_CFG, PMU_AP_CORE1_IDLE_CFG, > + PMU_AP_CORE2_IDLE_CFG, PMU_AP_CORE3_IDLE_CFG, > + PMU_AP_CORE4_IDLE_CFG, PMU_AP_CORE5_IDLE_CFG, > + PMU_AP_CORE6_IDLE_CFG, PMU_AP_CORE7_IDLE_CFG, > + PMU_CAP_CORE8_IDLE_CFG, PMU_CAP_CORE9_IDLE_CFG, > + PMU_CAP_CORE10_IDLE_CFG, PMU_CAP_CORE11_IDLE_CFG, > + PMU_CAP_CORE12_IDLE_CFG, PMU_CAP_CORE13_IDLE_CFG, > + PMU_CAP_CORE14_IDLE_CFG, PMU_CAP_CORE15_IDLE_CFG, > +}; > + > +/* Wakeup registers indexed by hart ID (0-15). */ > +static const unsigned long core_wakeup_reg[] = { > + PMU_AP_CORE0_WAKEUP, PMU_AP_CORE1_WAKEUP, > + PMU_AP_CORE2_WAKEUP, PMU_AP_CORE3_WAKEUP, > + PMU_AP_CORE4_WAKEUP, PMU_AP_CORE5_WAKEUP, > + PMU_AP_CORE6_WAKEUP, PMU_AP_CORE7_WAKEUP, > + PMU_CAP_CORE8_WAKEUP, PMU_CAP_CORE9_WAKEUP, > + PMU_CAP_CORE10_WAKEUP, PMU_CAP_CORE11_WAKEUP, > + PMU_CAP_CORE12_WAKEUP, PMU_CAP_CORE13_WAKEUP, > + PMU_CAP_CORE14_WAKEUP, PMU_CAP_CORE15_WAKEUP, > +}; > + > +/* > + * WFI idle status registers and per-hart mask bits. Each hart has a > + * dedicated bit in one of two status registers (X100 harts 0-7 in > + * PMU_AP_X100_IDLE_STATUS, A100 harts 8-15 in PMU_AP_A100_IDLE_STATUS). > + * Index 0 is unused — the boot hart never waits on itself. > + */ > +static const unsigned long wfi_status_reg[] = { > + 0, > + PMU_AP_X100_IDLE_STATUS, PMU_AP_X100_IDLE_STATUS, > + PMU_AP_X100_IDLE_STATUS, PMU_AP_X100_IDLE_STATUS, > + PMU_AP_X100_IDLE_STATUS, PMU_AP_X100_IDLE_STATUS, > + PMU_AP_X100_IDLE_STATUS, > + PMU_AP_A100_IDLE_STATUS, PMU_AP_A100_IDLE_STATUS, > + PMU_AP_A100_IDLE_STATUS, PMU_AP_A100_IDLE_STATUS, > + PMU_AP_A100_IDLE_STATUS, PMU_AP_A100_IDLE_STATUS, > + PMU_AP_A100_IDLE_STATUS, PMU_AP_A100_IDLE_STATUS, > +}; > + > +static const unsigned long wfi_status_mask[] = { > + 0, > + BIT(7), BIT(10), BIT(13), BIT(20), > + BIT(23), BIT(26), BIT(29), > + BIT(4), BIT(7), BIT(10), BIT(13), > + BIT(20), BIT(23), BIT(26), BIT(29), > +}; > + > +/* > + * Clear the power-down bits for both the core and its cluster so that > + * the hart does not trigger a power-down sequence on WFI. Called during > + * cold boot to ensure cores and clusters stay powered while SBI initializes. > + */ > +static void spacemit_disable_pwrdown_core(u32 hartid) > +{ > + unsigned int value; > + > + if (hartid >= array_size(cx_capmp_idle_cfg)) > + return; > + > + value = readl((void *)(unsigned long)cx_capmp_idle_cfg[hartid]); > + value &= ~CLUSTER_PWR_DOWN_VALUE; > + writel(value, (void *)(unsigned long)cx_capmp_idle_cfg[hartid]); > + > + value = readl((void *)(unsigned long)core_pwrdown_reg[hartid]); > + value &= ~PMU_AP_IDLE_PWRDOWN_K3_MASK; > + writel(value, (void *)(unsigned long)core_pwrdown_reg[hartid]); > +} > + > +/* > + * Per-hart hardware initialization, mirroring what warm-boot harts previously > + * did in assembly before entering C code. Called on every hart on both cold > + * and warm boot paths. > + * > + * 1. PMA: set audio buffer (bits [55:48]) cacheable and XIP > + * region (bits [23:16]) as IO before enabling dcache. > + * 2. Enable dcache/icache and other CPU features (MSETUP). > + * 3. Set smpen[x] in ML2SETUP to enable L1 D-cache snooping and prefetch. > + * 4. Enable H extension in MISA (X100 only; not implemented on A100). > + */ > +static void spacemit_k3_hart_init(u32 hartid) > +{ > + csr_clear(CSR_PMACFG0, > + (unsigned long)PMACFG0_AUDIO_BUF_FIELD_MASK > + << PMACFG0_AUDIO_BUF_FIELD_SHIFT); > + csr_set(CSR_PMACFG0, > + (unsigned long)PMACFG0_AUDIO_BUF_ATTR_CACHEABLE > + << PMACFG0_AUDIO_BUF_FIELD_SHIFT); > + csr_clear(CSR_PMACFG0, PMACFG0_XIP_FIELD_CLEAR); > + csr_set(CSR_PMACFG0, PMACFG0_XIP_ATTR_IO); > + asm volatile("sfence.vma" : : : "memory"); > + > + csr_set(CSR_MSETUP, MSETUP_DE | MSETUP_IE | MSETUP_BPE | > + MSETUP_PFE | MSETUP_MME | MSETUP_ECCE); > + csr_set(CSR_ML2SETUP, 1 << (hartid % PLATFORM_MAX_CPUS_PER_CLUSTER)); > + csr_set(CSR_ML2SETUP, ML2SETUP_IPRF | ML2SETUP_TPRF); > + csr_set(CSR_ML2HINT, ML2HINT_TRACE_TOP_ICGEN); > + > + if (hartid < PLATFORM_A100_FIRST_ID) { > + unsigned long misa = csr_read(CSR_MISA); > + > + /* > + * MISA.H is writable on K3 X100 cores: the H extension is > + * implemented but disabled out of reset, and M-mode must > + * explicitly set this bit to enable hypervisor support. > + * A100 cores do not implement H, so this write is X100-only. > + */ > + misa |= 1UL << ('H' - 'A'); > + csr_write(CSR_MISA, misa); > + } > +} > + > +static int spacemit_k3_nascent_init(void) > +{ > + spacemit_k3_hart_init(current_hartid()); > + > + return generic_nascent_init(); > +} > + > +/* > + * One-time platform setup run by the boot hart during cold boot: > + * set warm-boot entry for all four clusters, enable CCI snooping, > + * and wake all non-boot harts (1-15) so they enter WFI and wait > + * for the kernel to bring them online. > + */ > +static void spacemit_k3_pre_init(void) > +{ > + unsigned long entry = (unsigned long)&_spacemit_k3_warm_start; > + int i; > + > + writel((u32)entry, (void *)(unsigned long)C0_RVBADDR_LO_ADDR); > + writel((u32)(entry >> 32), (void *)(unsigned long)C0_RVBADDR_HI_ADDR); > + > + writel((u32)entry, (void *)(unsigned long)C1_RVBADDR_LO_ADDR); > + writel((u32)(entry >> 32), (void *)(unsigned long)C1_RVBADDR_HI_ADDR); > + > + writel((u32)entry, (void *)(unsigned long)C2_RVBADDR_LO_ADDR); > + writel((u32)(entry >> 32), (void *)(unsigned long)C2_RVBADDR_HI_ADDR); > + > + writel((u32)entry, (void *)(unsigned long)C3_RVBADDR_LO_ADDR); > + writel((u32)(entry >> 32), (void *)(unsigned long)C3_RVBADDR_HI_ADDR); > + > + for (i = 0; i < array_size(cci_map); i++) > + cci_enable_snoop_dvm_reqs(cci_map, i); > + > + /* Clear core and cluster power-down votes so WFI does not trigger power-down. */ > + for (i = 0; i < PLATFORM_MAX_CPUS; i++) > + spacemit_disable_pwrdown_core(i); > + > + /* > + * Wake up all non-boot harts (1-15). Each hart N has a dedicated PMU > + * wakeup register (PMU_AP/CAP_COREn_WAKEUP). Writing BIT(N) to hart N's > + * register triggers the wakeup pulse for that hart. This is confirmed by > + * hardware: the register address selects the target hart, and the value > + * written must have bit N set (not a fixed trigger bit). > + * See K3 User Manual, "16 Peripherals / Power Management / Wakeup Core". > + */ > + for (i = 1; i < array_size(core_wakeup_reg); i++) > + writel(1 << i, (void *)(unsigned long)core_wakeup_reg[i]); > + > + /* > + * Wait for each non-boot hart to enter WFI. Status bit positions are > + * non-uniform (see k3.h), so use the file-scope lookup tables. > + */ > + for (i = 1; i < PLATFORM_MAX_CPUS; i++) { > + u64 deadline = sbi_timer_value_after_msecs(HART_WFI_TIMEOUT_MS); > + > + while (!(readl((void *)(unsigned long)wfi_status_reg[i]) & wfi_status_mask[i])) { > + if (sbi_timer_value() >= deadline) { > + sbi_printf("warn: hart %d did not enter WFI\n", i); > + break; > + } > + } > + } > +} > + > +/* > + * Platform early initialization. > + */ > +static int spacemit_k3_early_init(bool cold_boot) > +{ > + int rc; > + > + if (cold_boot) > + spacemit_k3_pre_init(); > + > + rc = generic_early_init(cold_boot); > + if (rc) > + return rc; > + > + return 0; > +} > + > +static bool spacemit_cold_boot_allowed(u32 hartid) > +{ > + return !hartid; > +} > + > +static int spacemit_k3_platform_init(const void *fdt, int nodeoff, > + const struct fdt_match *match) > +{ > + generic_platform_ops.nascent_init = spacemit_k3_nascent_init; > + generic_platform_ops.early_init = spacemit_k3_early_init; > + generic_platform_ops.cold_boot_allowed = spacemit_cold_boot_allowed; > + > + return 0; > +} > + > +static const struct fdt_match spacemit_k3_match[] = { > + { .compatible = "spacemit,k3" }, > + { /* sentinel */ } > +}; > + > +const struct fdt_driver spacemit_k3 = { > + .match_table = spacemit_k3_match, > + .init = spacemit_k3_platform_init, > +}; > diff --git a/platform/generic/spacemit/k3_asm.S b/platform/generic/spacemit/k3_asm.S > new file mode 100644 > index 00000000..fe45cca6 > --- /dev/null > +++ b/platform/generic/spacemit/k3_asm.S > @@ -0,0 +1,33 @@ > +/* SPDX-License-Identifier: BSD-2-Clause */ > +/* > + * Copyright (c) 2026 SpacemiT > + * Authors: > + * Xianbin Zhu <[email protected]> > + * Troy Mitchell <[email protected]> > + */ > + > +#define __ASSEMBLY__ > +#include <spacemit/common.h> > +#include <spacemit/k3.h> > + > + .section .entry, "ax", %progbits > + .align 3 > + .globl _spacemit_k3_warm_start > +_spacemit_k3_warm_start: > + /* Enable dcache, icache, and CPU features */ > + li t0, MSETUP_BOOT_FLAGS > + csrs CSR_MSETUP, t0 > + > + /* > + * Set smpen[x] in ML2SETUP to enable L1 D-cache snooping for this > + * core, so the hardware maintains coherency between this core's L1 > + * D-cache and all other caches in the system. > + * The bit position is 1 << (hartid % PLATFORM_MAX_CPUS_PER_CLUSTER). > + */ > + csrr t0, mhartid > + andi t0, t0, (PLATFORM_MAX_CPUS_PER_CLUSTER - 1) > + li t1, 1 > + sll t1, t1, t0 > + csrs CSR_ML2SETUP, t1 > + > + j _start_warm > diff --git a/platform/generic/spacemit/objects.mk b/platform/generic/spacemit/objects.mk > index 8309ad1f..282543b5 100644 > --- a/platform/generic/spacemit/objects.mk > +++ b/platform/generic/spacemit/objects.mk > @@ -5,3 +5,5 @@ With this patch applied CROSS_COMPILE=riscv64-linux-gnu- \ make PLATFORM_RISCV_XLEN=32 PLATFORM=generic fails with errors like platform/generic/spacemit/k3.c:134:19: error: left shift count >= width of type [-Werror=shift-count-overflow] 134 | << PMACFG0_AUDIO_BUF_FIELD_SHIFT); Please, ensure that the SpacemiT K3 and K1 platforms are not built in the 32bit case. Unfortunately PLATFORM_RISCV_XLEN is not a configuration variable. Other platforms use ifeq ($(PLATFORM_RISCV_XLEN), 64) at the start of the *.mk file. Best regards Heinrich > platform-objs-$(CONFIG_PLATFORM_SPACEMIT) += spacemit/spacemit.o > carray-platform_override_modules-$(CONFIG_PLATFORM_SPACEMIT_K1) += spacemit_k1 > platform-objs-$(CONFIG_PLATFORM_SPACEMIT_K1) += spacemit/k1.o > +carray-platform_override_modules-$(CONFIG_PLATFORM_SPACEMIT_K3) += spacemit_k3 > +platform-objs-$(CONFIG_PLATFORM_SPACEMIT_K3) += spacemit/k3.o spacemit/k3_asm.o > -- opensbi mailing list [email protected] http://lists.infradead.org/mailman/listinfo/opensbi