@@ -9,11 +9,13 @@
#include <platform_override.h>
#include <sbi/riscv_io.h>
+#include <sbi/sbi_error.h>
#include <sbi/sbi_hsm.h>
#include <spacemit/common.h>
#include <spacemit/k1.h>
+#include <spacemit/k3.h>
-static const u64 cpu_wakeup_reg[] = {
+static const u64 k1_cpu_wakeup_reg[] = {
PMU_AP_CORE0_WAKEUP,
PMU_AP_CORE1_WAKEUP,
PMU_AP_CORE2_WAKEUP,
@@ -24,7 +26,7 @@ static const u64 cpu_wakeup_reg[] = {
PMU_AP_CORE7_WAKEUP,
};
-static const u64 cpu_idle_reg[] = {
+static const u64 k1_cpu_idle_reg[] = {
PMU_AP_CORE0_IDLE_CFG,
PMU_AP_CORE1_IDLE_CFG,
PMU_AP_CORE2_IDLE_CFG,
@@ -35,10 +37,11 @@ static const u64 cpu_idle_reg[] = {
PMU_AP_CORE7_IDLE_CFG,
};
-static inline void spacemit_set_cpu_power(u32 hartid, bool enable)
+static inline void spacemit_k1_set_cpu_power(u32 hartid, bool enable)
{
unsigned int value;
- unsigned int *cpu_idle_base = (unsigned int *)(unsigned long)cpu_idle_reg[hartid];
+ unsigned int *cpu_idle_base =
+ (unsigned int *)(unsigned long)k1_cpu_idle_reg[hartid];
value = readl(cpu_idle_base);
@@ -50,31 +53,32 @@ static inline void spacemit_set_cpu_power(u32 hartid, bool enable)
writel(value, cpu_idle_base);
}
-static void spacemit_wakeup_cpu(u32 mpidr)
+static void spacemit_k1_wakeup_cpu(u32 mpidr)
{
unsigned int *cpu_reset_base;
unsigned int cur_hartid = current_hartid();
- cpu_reset_base = (unsigned int *)(unsigned long)cpu_wakeup_reg[cur_hartid];
+ cpu_reset_base =
+ (unsigned int *)(unsigned long)k1_cpu_wakeup_reg[cur_hartid];
writel(1 << mpidr, cpu_reset_base);
}
-static void spacemit_assert_cpu(void)
+static void spacemit_k1_assert_cpu(void)
{
- spacemit_set_cpu_power(current_hartid(), false);
+ spacemit_k1_set_cpu_power(current_hartid(), false);
}
-static void spacemit_deassert_cpu(unsigned int hartid)
+static void spacemit_k1_deassert_cpu(unsigned int hartid)
{
- spacemit_set_cpu_power(hartid, true);
+ spacemit_k1_set_cpu_power(hartid, true);
}
/* Start (or power-up) the given hart */
-static int spacemit_hart_start(unsigned int hartid, unsigned long saddr)
+static int spacemit_k1_hart_start(unsigned int hartid, unsigned long saddr)
{
- spacemit_deassert_cpu(hartid);
- spacemit_wakeup_cpu(hartid);
+ spacemit_k1_deassert_cpu(hartid);
+ spacemit_k1_wakeup_cpu(hartid);
return 0;
}
@@ -83,10 +87,11 @@ static int spacemit_hart_start(unsigned int hartid, unsigned long saddr)
* Stop (or power-down) the current hart from running. This call
* doesn't expect to return if success.
*/
-static int spacemit_hart_stop(void)
+static int spacemit_k1_hart_stop(void)
{
csr_write(CSR_STIMECMP, GENMASK_ULL(63, 0));
- csr_clear(CSR_MIE, MIP_SSIP | MIP_MSIP | MIP_STIP | MIP_MTIP | MIP_SEIP | MIP_MEIP);
+ csr_clear(CSR_MIE, MIP_SSIP | MIP_MSIP | MIP_STIP | MIP_MTIP |
+ MIP_SEIP | MIP_MEIP);
/* disable data prefetch */
csr_clear(CSR_MSETUP, MSETUP_PFE);
@@ -106,31 +111,93 @@ static int spacemit_hart_stop(void)
* Therefore, use modulo with PLATFORM_MAX_CPUS_PER_CLUSTER
* to select the proper bit.
*/
- csr_clear(CSR_ML2SETUP, 1 << (current_hartid() % PLATFORM_MAX_CPUS_PER_CLUSTER));
+ csr_clear(CSR_ML2SETUP,
+ 1 << (current_hartid() % PLATFORM_MAX_CPUS_PER_CLUSTER));
asm volatile ("fence iorw, iorw");
- spacemit_assert_cpu();
+ spacemit_k1_assert_cpu();
wfi();
return SBI_ENOTSUPP;
}
-static const struct sbi_hsm_device spacemit_hsm_ops = {
+static const struct sbi_hsm_device spacemit_k1_hsm_ops = {
.name = "spacemit-hsm",
- .hart_start = spacemit_hart_start,
- .hart_stop = spacemit_hart_stop,
+ .hart_start = spacemit_k1_hart_start,
+ .hart_stop = spacemit_k1_hart_stop,
};
-static int spacemit_hsm_probe(const void *fdt, int nodeoff, const struct fdt_match *match)
+static const unsigned long k3_cluster_idle_reg[] = {
+ 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 k3_core_idle_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,
+};
+
+static const unsigned long k3_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,
+};
+
+static int spacemit_k3_hart_start(u32 hartid, ulong saddr)
+{
+ u32 value;
+
+ if (hartid >= array_size(k3_core_wakeup_reg))
+ return SBI_EINVAL;
+
+ value = readl((void *)k3_cluster_idle_reg[hartid]);
+ value &= ~CLUSTER_PWR_DOWN_VALUE;
+ writel(value, (void *)k3_cluster_idle_reg[hartid]);
+
+ value = readl((void *)k3_core_idle_reg[hartid]);
+ value &= ~PMU_AP_IDLE_PWRDOWN_K3_MASK;
+ writel(value, (void *)k3_core_idle_reg[hartid]);
+
+ writel(BIT(hartid), (void *)k3_core_wakeup_reg[hartid]);
+
+ return 0;
+}
+
+/* K3 uses this only to power on a hart which has not entered OpenSBI yet. */
+static const struct sbi_hsm_device spacemit_k3_hsm_ops = {
+ .name = "spacemit-k3-hsm",
+ .hart_start = spacemit_k3_hart_start,
+};
+
+static int spacemit_hsm_probe(const void *fdt, int nodeoff,
+ const struct fdt_match *match)
{
- sbi_hsm_set_device(&spacemit_hsm_ops);
+ sbi_hsm_set_device(match->data);
return 0;
}
static const struct fdt_match spacemit_hsm_match[] = {
- { .compatible = "spacemit,k1" },
+ { .compatible = "spacemit,k1", .data = &spacemit_k1_hsm_ops },
+ { .compatible = "spacemit,k3", .data = &spacemit_k3_hsm_ops },
{ },
};
@@ -109,6 +109,13 @@ config PLATFORM_SPACEMIT_K1
select PLATFORM_SPACEMIT
default n
+config PLATFORM_SPACEMIT_K3
+ bool "Spacemit K3 support"
+ select FDT_HSM
+ select FDT_HSM_SPACEMIT
+ select PLATFORM_SPACEMIT
+ default n
+
config CPU_MIPS_P8700
bool
default n
@@ -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
@@ -9,23 +9,27 @@
#ifndef __RISCV_SPACEMIT_COMMON_H__
#define __RISCV_SPACEMIT_COMMON_H__
+#include <sbi/sbi_const.h>
+
+#ifndef __ASSEMBLY__
#include <sbi/sbi_bitops.h>
+#endif
#define CSR_MSETUP 0x7c0
#define CSR_ML2SETUP 0x7f0
/* dcache enable */
-#define MSETUP_DE BIT(0)
+#define MSETUP_DE _BITUL(0)
/* icache enable */
-#define MSETUP_IE BIT(1)
+#define MSETUP_IE _BITUL(1)
/* branch prediction enable */
-#define MSETUP_BPE BIT(4)
+#define MSETUP_BPE _BITUL(4)
/* prefetch functionality enable */
-#define MSETUP_PFE BIT(5)
+#define MSETUP_PFE _BITUL(5)
/* misaligned memory access enable */
-#define MSETUP_MME BIT(6)
+#define MSETUP_MME _BITUL(6)
/* ECC enable */
-#define MSETUP_ECCE BIT(16)
+#define MSETUP_ECCE _BITUL(16)
#define PMU_AP_BASE 0xd4282800
@@ -74,7 +78,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)
new file mode 100644
@@ -0,0 +1,127 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2026 SpacemiT
+ * Authors:
+ * Xianbin Zhu <xianbin.zhu@linux.spacemit.com>
+ * Troy Mitchell <troy.mitchell@linux.spacemit.com>
+ */
+
+#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].
+ * The Boot ROM maps this region as I/O. Restore it to a cacheable
+ * attribute so later firmware can use it for data or instructions.
+ */
+#define PMACFG0_AUDIO_BUF_FIELD_MASK _UL(0xff)
+#define PMACFG0_AUDIO_BUF_FIELD_SHIFT 48
+#define PMACFG0_AUDIO_BUF_ATTR_CACHEABLE _UL(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 _UL(0x00ff0000)
+#define PMACFG0_XIP_ATTR_IO _UL(0x00220000)
+
+#define CSR_ML2HINT 0x7f7
+#define MISA_EXT_H _BITUL(7)
+
+/* Enable instruction prefetch in L2 cache on miss */
+#define ML2SETUP_IPRF _BITUL(16)
+/* Enable TLB prefetch in L2 cache */
+#define ML2SETUP_TPRF _BITUL(18)
+/* Enable top-of-clk of trace */
+#define ML2HINT_TRACE_TOP_ICGEN _BITUL(26)
+
+#define MSETUP_BOOT_FLAGS (MSETUP_DE | MSETUP_IE | \
+ MSETUP_BPE | MSETUP_PFE | \
+ MSETUP_MME | MSETUP_ECCE)
+
+/* 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)
+
+#define PLATFORM_A100_FIRST_ID 8
+
+#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__ */
new file mode 100644
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2026 SpacemiT
+ * Authors:
+ * Xianbin Zhu <xianbin.zhu@linux.spacemit.com>
+ * Troy Mitchell <troy.mitchell@linux.spacemit.com>
+ */
+
+#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__ */
new file mode 100644
@@ -0,0 +1,122 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 SpacemiT
+ * Authors:
+ * Xianbin Zhu <xianbin.zhu@linux.spacemit.com>
+ * Troy Mitchell <troy.mitchell@linux.spacemit.com>
+ */
+
+#include <platform_override.h>
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_hfence.h>
+#include <sbi/sbi_tlb.h>
+#include <spacemit/k3.h>
+#include <spacemit/k3_asm.h>
+#include <spacemit/spacemit.h>
+
+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,
+};
+
+/*
+ * The boot hart reaches this function before caches are enabled. Secondary
+ * harts perform the same setup in _spacemit_k3_warm_start before entering
+ * the common warm-boot path.
+ */
+static void spacemit_k3_hart_init(u32 hartid)
+{
+ csr_clear(CSR_PMACFG0,
+ PMACFG0_AUDIO_BUF_FIELD_MASK <<
+ PMACFG0_AUDIO_BUF_FIELD_SHIFT);
+ csr_set(CSR_PMACFG0,
+ 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);
+ __sbi_sfence_vma_all();
+
+ csr_set(CSR_ML2SETUP,
+ _BITUL(hartid % PLATFORM_MAX_CPUS_PER_CLUSTER) |
+ ML2SETUP_IPRF | ML2SETUP_TPRF);
+ csr_set(CSR_ML2HINT, ML2HINT_TRACE_TOP_ICGEN);
+ csr_set(CSR_MSETUP, MSETUP_BOOT_FLAGS);
+
+ if (hartid < PLATFORM_A100_FIRST_ID) {
+ /* MISA.H is writable on X100 and not implemented on A100. */
+ csr_set(CSR_MISA, MISA_EXT_H);
+ __sbi_hfence_gvma_all();
+ }
+}
+
+/* The boot hart is already running, so it is never started by the HSM. */
+static void spacemit_k3_keep_boot_hart_powered(void)
+{
+ u32 value;
+
+ value = readl((void *)(unsigned long)PMU_CX_CAPMP_IDLE_CFG0);
+ value &= ~CLUSTER_PWR_DOWN_VALUE;
+ writel(value, (void *)(unsigned long)PMU_CX_CAPMP_IDLE_CFG0);
+
+ value = readl((void *)(unsigned long)PMU_AP_CORE0_IDLE_CFG);
+ value &= ~PMU_AP_IDLE_PWRDOWN_K3_MASK;
+ writel(value, (void *)(unsigned long)PMU_AP_CORE0_IDLE_CFG);
+}
+
+/* Program global state before the HSM starts any secondary hart. */
+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);
+
+ spacemit_k3_keep_boot_hart_powered();
+}
+
+static int spacemit_k3_early_init(bool cold_boot)
+{
+ if (cold_boot)
+ spacemit_k3_pre_init();
+
+ return generic_early_init(cold_boot);
+}
+
+static int spacemit_k3_platform_init(const void *fdt, int nodeoff,
+ const struct fdt_match *match)
+{
+ spacemit_k3_hart_init(current_hartid());
+ generic_platform_ops.early_init = spacemit_k3_early_init;
+
+ 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,
+};
new file mode 100644
@@ -0,0 +1,52 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2026 SpacemiT
+ * Authors:
+ * Xianbin Zhu <xianbin.zhu@linux.spacemit.com>
+ * Troy Mitchell <troy.mitchell@linux.spacemit.com>
+ */
+
+#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:
+ /* Program PMA attributes before enabling the caches. */
+ li t0, (PMACFG0_AUDIO_BUF_FIELD_MASK << \
+ PMACFG0_AUDIO_BUF_FIELD_SHIFT)
+ csrc CSR_PMACFG0, t0
+ li t0, (PMACFG0_AUDIO_BUF_ATTR_CACHEABLE << \
+ PMACFG0_AUDIO_BUF_FIELD_SHIFT)
+ csrs CSR_PMACFG0, t0
+ li t0, PMACFG0_XIP_FIELD_CLEAR
+ csrc CSR_PMACFG0, t0
+ li t0, PMACFG0_XIP_ATTR_IO
+ csrs CSR_PMACFG0, t0
+ sfence.vma
+
+ /* Enable L1 snooping and L2 prefetch for this hart. */
+ csrr t0, mhartid
+ andi t0, t0, (PLATFORM_MAX_CPUS_PER_CLUSTER - 1)
+ li t1, 1
+ sll t1, t1, t0
+ li t2, (ML2SETUP_IPRF | ML2SETUP_TPRF)
+ or t1, t1, t2
+ csrs CSR_ML2SETUP, t1
+ li t0, ML2HINT_TRACE_TOP_ICGEN
+ csrs CSR_ML2HINT, t0
+
+ li t0, MSETUP_BOOT_FLAGS
+ csrs CSR_MSETUP, t0
+
+ /* Enable H before common code selects the per-hart trap entry. */
+ csrr t0, mhartid
+ li t1, PLATFORM_A100_FIRST_ID
+ bgeu t0, t1, 1f
+ li t0, MISA_EXT_H
+ csrs misa, t0
+ .word 0x62000073 /* hfence.gvma */
+1:
+ j _start_warm
@@ -2,6 +2,10 @@
# SPDX-License-Identifier: BSD-2-Clause
#
+ifeq ($(PLATFORM_RISCV_XLEN), 64)
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
+endif