OpenSBI Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v2 0/5] platform: generic: spacemit: add K3 platform support
@ 2026-08-18  1:14 Troy Mitchell
  2026-08-18  1:14 ` [PATCH v2 1/5] lib: sbi: select expected trap handler per hart Troy Mitchell
                   ` (5 more replies)
  0 siblings, 6 replies; 11+ messages in thread
From: Troy Mitchell @ 2026-08-18  1:14 UTC (permalink / raw)
  To: opensbi; +Cc: Xianbin Zhu, Anup Patel, Bo Gan, Troy Mitchell

Add initial OpenSBI platform support for the SpacemiT K3 SoC. K3 has
16 harts split across four clusters: eight X100 harts in C0/C1 and
eight A100 harts in C2/C3.

The main upstream integration constraint is that K3 is heterogeneous at
the ISA level. The CPU device tree used for testing exposes the same
non-H standard extensions for X100 and A100. X100 additionally exposes H
and the related Sha, Shcounterenw, Shgatpa, Shtvala, Shvsatpa,
Shvstvala, and Shvstvecd extensions. X100 has a 256-bit VLEN, while
A100 has a 1024-bit VLEN. A100 also implements the vendor-specific
SpacemiT IME and FP8 AI extensions, which OpenSBI does not inspect or
use.

At the base of this series, OpenSBI selects the expected-trap handler
once from the cold-boot hart and reuses it globally. An X100 cold-boot
hart would therefore make an A100 hart use the H-aware handler, which
accesses mtval2 and mtinst even though A100 does not implement H. Select
the handler from the current hart instead. An audit of the remaining
extension-dependent paths found that ISA extensions and detected CSR
features are stored per-hart, the generic FDT parser visits every hart,
and vector code reads the current hart's VLENB. The K3 CPU device tree
also exposes the same counter and timer extensions on both core types.
No other cold-boot-hart-derived ISA selection was found.

K1 and K3 share vendor cache-control CSRs, PMU idle fields, cluster
sizing, and the CCI-550 programming sequence, but their CCI topology,
boot flow, and HSM support differ. Factor only the common definitions and
CCI helper so K3 does not depend on K1 and the K1 encoded values and
behavior remain unchanged. Run per-hart register setup from
nascent_init() rather than the policy-only cold_boot_allowed() hook.

On K3, the cold-boot hart programs the four cluster warm-boot vectors,
enables CCI snoop and DVM requests, prevents WFI from powering down cores
or clusters, and wakes the secondary harts. A bounded wait prevents a
non-responsive secondary hart from stalling boot indefinitely. Each hart
programs its own PMA, cache, snooping, and prefetch state, with X100 harts
also enabling H. The warm-boot entry establishes cache coherency before
entering common code so secondary harts can observe state published by
the cold-boot hart.

Runtime validation used the SpacemiT K3 SDK. The UART at 115200 baud
showed OpenSBI and U-Boot starting successfully, followed by Linux
bringing all 16 harts online. The series has also been build-tested with:

  - the generic default configuration
  - a K1 configuration with K3 disabled and the SpacemiT HSM driver
  - a K3 configuration with K1 and the SpacemiT HSM driver disabled

To reproduce the runtime test, build this OpenSBI tree at the address
where the SDK loads it, then copy the resulting firmware into the SDK
output directory:

  K3_SDK_DIR=/path/to/k3-sdk
  make O=build-k3 CROSS_COMPILE=riscv64-linux-gnu- PLATFORM=generic \
       FW_TEXT_START=0x100000000
  cp build-k3/platform/generic/firmware/fw_dynamic.bin \
     "$K3_SDK_DIR/output/fw_dynamic.bin"

Before rebuilding U-Boot, update the UART0 node in the K3 board DT
selected by SPL and passed to OpenSBI. The SDK describes it only as
"ns16550", so upstream OpenSBI initializes it without UART_CAP_UUE and
writes 0x00 to UART_IER. The K3 UART requires UART_IER_UUE (bit 6).
Change the node in uboot-2022.10/arch/riscv/dts/k3.dtsi to:

  uart0: uart@d4017000 {
          ...
          compatible = "spacemit,k1-uart", "intel,xscale-uart",
                       "ns16550";
          ...
  };

The compatible order is significant. Upstream OpenSBI skips the vendor
string, then matches "intel,xscale-uart", which selects UART_CAP_UUE and
writes 0x40 to UART_IER. The final "ns16550" remains a generic fallback.
This is the DT used for the successful boot test.

With output/fw_dynamic.bin already present, rebuild U-Boot. The SDK
preserves that OpenSBI binary and packages it with U-Boot:

  make -C "$K3_SDK_DIR" uboot

This produces output/FSBL.bin and output/u-boot-opensbi.itb. Using the
SDK-built output/Image.itb, enter BROM fastboot mode with FEL+RESET and
stage all three images into RAM:

  fastboot stage "$K3_SDK_DIR/output/FSBL.bin"
  fastboot continue
  sleep 8
  fastboot stage "$K3_SDK_DIR/output/u-boot-opensbi.itb"
  fastboot continue
  sleep 3
  fastboot stage "$K3_SDK_DIR/output/Image.itb"
  fastboot continue

Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
---
Changes in v2:
  - correct the K3 PMACFG0 CSR number from 0xbc0 to 0x7de
  - select the expected-trap handler per hart for heterogeneous H support
  - move K1 and K3 per-hart setup out of cold_boot_allowed()
  - split the generic and K1 fixes into standalone patches, growing the
    series from three patches to five
  - document X100/A100 ISA and VLEN differences and the homogeneous-core
    audit
  - document OpenSBI/U-Boot packaging, the UART compatible adjustment,
    and the USB fastboot test procedure
  - Link to v1: https://lore.kernel.org/r/20260724-spacemit-k3-v1-0-f0e49329feb8@linux.spacemit.com

---
Troy Mitchell (2):
      lib: sbi: select expected trap handler per hart
      platform: generic: spacemit: k1: move hart init to nascent hook

Xianbin Zhu (3):
      platform: generic: spacemit: k1: rename cache flush operation
      platform: generic: spacemit: k1: refactor platform support
      platform: generic: spacemit: k3: add platform support

 include/sbi/sbi_csr_detect.h                 |   4 +-
 include/sbi/sbi_hart.h                       |   2 +-
 lib/sbi/sbi_hart.c                           |   9 +-
 lib/sbi/sbi_illegal_atomic.c                 |   4 +-
 lib/sbi/sbi_unpriv.c                         |   6 +-
 lib/utils/hsm/fdt_hsm_spacemit.c             |   7 +-
 platform/generic/Kconfig                     |   9 +
 platform/generic/configs/defconfig           |   1 +
 platform/generic/include/spacemit/common.h   |  88 +++++++++
 platform/generic/include/spacemit/k1.h       |  96 ++--------
 platform/generic/include/spacemit/k3.h       | 144 +++++++++++++++
 platform/generic/include/spacemit/k3_asm.h   |  16 ++
 platform/generic/include/spacemit/spacemit.h |  14 ++
 platform/generic/spacemit/k1.c               |  45 ++---
 platform/generic/spacemit/k3.c               | 266 +++++++++++++++++++++++++++
 platform/generic/spacemit/k3_asm.S           |  33 ++++
 platform/generic/spacemit/objects.mk         |   3 +
 platform/generic/spacemit/spacemit.c         |  36 ++++
 18 files changed, 661 insertions(+), 122 deletions(-)
---
base-commit: c0f87f10d1bfb9e72a84ddfafb5604ee1bfe9d04
change-id: 20260723-spacemit-k3-84720a7be53d

Best regards,
--  
Troy Mitchell <troy.mitchell@linux.spacemit.com>


-- 
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi

^ permalink raw reply	[flat|nested] 11+ messages in thread

* [PATCH v2 1/5] lib: sbi: select expected trap handler per hart
  2026-08-18  1:14 [PATCH v2 0/5] platform: generic: spacemit: add K3 platform support Troy Mitchell
@ 2026-08-18  1:14 ` Troy Mitchell
  2026-08-20  8:40   ` Bo Gan
  2026-08-18  1:14 ` [PATCH v2 2/5] platform: generic: spacemit: k1: rename cache flush operation Troy Mitchell
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 11+ messages in thread
From: Troy Mitchell @ 2026-08-18  1:14 UTC (permalink / raw)
  To: opensbi; +Cc: Xianbin Zhu, Anup Patel, Bo Gan, Troy Mitchell

The expected trap handler is selected once by the cold boot hart. This
breaks heterogeneous systems where the cold boot hart implements H but
another hart does not, because the H-aware handler accesses mtval2 and
mtinst.

Select the handler from the current hart's MISA at each use so every
hart uses only the CSRs it implements.

Fixes: 1de66d170e71 ("lib: Optimize unpriv load/store implementation")
Reported-by: Bo Gan <ganboing@gmail.com>
Link: https://lore.kernel.org/r/e702f291-dde8-4b99-a65e-182d2b847720@gmail.com
Suggested-by: Bo Gan <ganboing@gmail.com>
Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
---
 include/sbi/sbi_csr_detect.h | 4 ++--
 include/sbi/sbi_hart.h       | 2 +-
 lib/sbi/sbi_hart.c           | 9 +++++----
 lib/sbi/sbi_illegal_atomic.c | 4 ++--
 lib/sbi/sbi_unpriv.c         | 6 +++---
 5 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/include/sbi/sbi_csr_detect.h b/include/sbi/sbi_csr_detect.h
index 097c31c8..31e50db9 100644
--- a/include/sbi/sbi_csr_detect.h
+++ b/include/sbi/sbi_csr_detect.h
@@ -16,9 +16,9 @@
 
 #define csr_read_allowed(csr_num, trap)					\
 	({								\
+	register ulong mtvec = sbi_hart_expected_trap_addr();		\
 	register ulong tinfo asm("a3") = (ulong)trap;			\
 	register ulong ttmp asm("a4");					\
-	register ulong mtvec = (ulong)sbi_hart_expected_trap;		\
 	register ulong ret = 0;						\
 	((struct sbi_trap_info *)(trap))->cause = 0;			\
 	asm volatile(							\
@@ -35,9 +35,9 @@
 
 #define csr_write_allowed(csr_num, trap, value)				\
 	({								\
+	register ulong mtvec = sbi_hart_expected_trap_addr();		\
 	register ulong tinfo asm("a3") = (ulong)trap;			\
 	register ulong ttmp asm("a4");					\
-	register ulong mtvec = (ulong)sbi_hart_expected_trap;		\
 	((struct sbi_trap_info *)(trap))->cause = 0;			\
 	asm volatile(							\
 		"add %[ttmp], %[tinfo], zero\n"				\
diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h
index 543393bb..6f4ee31e 100644
--- a/include/sbi/sbi_hart.h
+++ b/include/sbi/sbi_hart.h
@@ -135,7 +135,7 @@ struct sbi_scratch;
 int sbi_hart_reinit(struct sbi_scratch *scratch);
 int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot);
 
-extern void (*sbi_hart_expected_trap)(void);
+ulong sbi_hart_expected_trap_addr(void);
 
 unsigned int sbi_hart_mhpm_mask(struct sbi_scratch *scratch);
 void sbi_hart_delegation_dump(struct sbi_scratch *scratch,
diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
index bee88557..14e44955 100644
--- a/lib/sbi/sbi_hart.c
+++ b/lib/sbi/sbi_hart.c
@@ -25,7 +25,11 @@
 extern void __sbi_expected_trap(void);
 extern void __sbi_expected_trap_hext(void);
 
-void (*sbi_hart_expected_trap)(void) = &__sbi_expected_trap;
+ulong sbi_hart_expected_trap_addr(void)
+{
+	return misa_extension('H') ? (ulong)&__sbi_expected_trap_hext :
+				     (ulong)&__sbi_expected_trap;
+}
 
 unsigned long hart_features_offset;
 
@@ -712,9 +716,6 @@ int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot)
 	csr_write(CSR_MIP, 0);
 
 	if (cold_boot) {
-		if (misa_extension('H'))
-			sbi_hart_expected_trap = &__sbi_expected_trap_hext;
-
 		hart_features_offset = sbi_scratch_alloc_offset(
 					sizeof(struct sbi_hart_features));
 		if (!hart_features_offset)
diff --git a/lib/sbi/sbi_illegal_atomic.c b/lib/sbi/sbi_illegal_atomic.c
index 977a9ad0..30f5118e 100644
--- a/lib/sbi/sbi_illegal_atomic.c
+++ b/lib/sbi/sbi_illegal_atomic.c
@@ -30,7 +30,7 @@ int sbi_illegal_atomic(ulong insn, struct sbi_trap_regs *regs)
 	{									\
 		register ulong tinfo asm("a3");					\
 		register ulong mstatus = 0;					\
-		register ulong mtvec = (ulong)sbi_hart_expected_trap;		\
+		register ulong mtvec = sbi_hart_expected_trap_addr();		\
 		type ret = 0;							\
 		trap->cause = 0;						\
 		asm volatile(							\
@@ -57,7 +57,7 @@ int sbi_illegal_atomic(ulong insn, struct sbi_trap_regs *regs)
 	{									\
 		register ulong tinfo asm("a3");					\
 		register ulong mstatus = 0;					\
-		register ulong mtvec = (ulong)sbi_hart_expected_trap;		\
+		register ulong mtvec = sbi_hart_expected_trap_addr();		\
 		type ret = 0;							\
 		trap->cause = 0;						\
 		asm volatile(							\
diff --git a/lib/sbi/sbi_unpriv.c b/lib/sbi/sbi_unpriv.c
index 60becedc..1550d111 100644
--- a/lib/sbi/sbi_unpriv.c
+++ b/lib/sbi/sbi_unpriv.c
@@ -33,9 +33,9 @@ union sbi_unpriv_data {
 	type sbi_load_##type(const type *addr,                                \
 			     struct sbi_trap_info *trap)                      \
 	{                                                                     \
+		register ulong mtvec = sbi_hart_expected_trap_addr();         \
 		register ulong tinfo asm("a3") = (ulong)trap;                 \
 		register ulong mstatus = 0;                                   \
-		register ulong mtvec = (ulong)sbi_hart_expected_trap;         \
 		type ret = 0;                                                 \
 		trap->cause = 0;                                              \
 		asm volatile(                                                 \
@@ -58,9 +58,9 @@ union sbi_unpriv_data {
 	void sbi_store_##type(type *addr, type val,                           \
 			      struct sbi_trap_info *trap)                     \
 	{                                                                     \
+		register ulong mtvec = sbi_hart_expected_trap_addr();         \
 		register ulong tinfo asm("a3") = (ulong)trap;                 \
 		register ulong mstatus = 0;                                   \
-		register ulong mtvec = (ulong)sbi_hart_expected_trap;         \
 		trap->cause = 0;                                              \
 		asm volatile(                                                 \
 			"csrrw %[mtvec], " STR(CSR_MTVEC) ", %[mtvec]\n"      \
@@ -207,7 +207,7 @@ ulong sbi_get_insn(ulong mepc, struct sbi_trap_info *trap)
 	register ulong tinfo asm("a3");
 	register ulong ttmp asm("a4");
 	register ulong mstatus = 0;
-	register ulong mtvec = (ulong)sbi_hart_expected_trap;
+	register ulong mtvec = sbi_hart_expected_trap_addr();
 	ulong insn = 0;
 
 	trap->cause = 0;

-- 
2.55.0


-- 
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 2/5] platform: generic: spacemit: k1: rename cache flush operation
  2026-08-18  1:14 [PATCH v2 0/5] platform: generic: spacemit: add K3 platform support Troy Mitchell
  2026-08-18  1:14 ` [PATCH v2 1/5] lib: sbi: select expected trap handler per hart Troy Mitchell
@ 2026-08-18  1:14 ` Troy Mitchell
  2026-08-18  1:14 ` [PATCH v2 3/5] platform: generic: spacemit: k1: move hart init to nascent hook Troy Mitchell
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 11+ messages in thread
From: Troy Mitchell @ 2026-08-18  1:14 UTC (permalink / raw)
  To: opensbi; +Cc: Xianbin Zhu, Anup Patel, Bo Gan, Troy Mitchell

From: Xianbin Zhu <xianbin.zhu@linux.spacemit.com>

MRAOP_ICACHE_INVALID is used to flush the local D-cache before a hart
stops, so the name incorrectly describes I-cache invalidation. Rename
it to MRAOP_CACHE_FLUSH without changing its encoded value.

Signed-off-by: Xianbin Zhu <xianbin.zhu@linux.spacemit.com>
Co-developed-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
---
 lib/utils/hsm/fdt_hsm_spacemit.c       | 2 +-
 platform/generic/include/spacemit/k1.h | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/lib/utils/hsm/fdt_hsm_spacemit.c b/lib/utils/hsm/fdt_hsm_spacemit.c
index 868e49bf..99c52335 100644
--- a/lib/utils/hsm/fdt_hsm_spacemit.c
+++ b/lib/utils/hsm/fdt_hsm_spacemit.c
@@ -92,7 +92,7 @@ static int spacemit_hart_stop(void)
 	asm volatile ("fence iorw, iorw");
 
 	/* flush local dcache */
-	csr_write(CSR_MRAOP, MRAOP_ICACHE_INVALID);
+	csr_write(CSR_MRAOP, MRAOP_CACHE_FLUSH);
 	asm volatile ("fence iorw, iorw");
 
 	/* disable dcache */
diff --git a/platform/generic/include/spacemit/k1.h b/platform/generic/include/spacemit/k1.h
index 7095bc08..ac727b04 100644
--- a/platform/generic/include/spacemit/k1.h
+++ b/platform/generic/include/spacemit/k1.h
@@ -20,8 +20,8 @@
 /* ECC enable */
 #define MSETUP_ECCE				BIT(16)
 
-/* icache invalidation */
-#define MRAOP_ICACHE_INVALID			GENMASK(1, 0)
+/* cache flush */
+#define MRAOP_CACHE_FLUSH			GENMASK(1, 0)
 
 #define PMU_AP_BASE				0xd4282800
 

-- 
2.55.0


-- 
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 3/5] platform: generic: spacemit: k1: move hart init to nascent hook
  2026-08-18  1:14 [PATCH v2 0/5] platform: generic: spacemit: add K3 platform support Troy Mitchell
  2026-08-18  1:14 ` [PATCH v2 1/5] lib: sbi: select expected trap handler per hart Troy Mitchell
  2026-08-18  1:14 ` [PATCH v2 2/5] platform: generic: spacemit: k1: rename cache flush operation Troy Mitchell
@ 2026-08-18  1:14 ` Troy Mitchell
  2026-08-20  8:42   ` Bo Gan
  2026-08-18  1:14 ` [PATCH v2 4/5] platform: generic: spacemit: k1: refactor platform support Troy Mitchell
                   ` (2 subsequent siblings)
  5 siblings, 1 reply; 11+ messages in thread
From: Troy Mitchell @ 2026-08-18  1:14 UTC (permalink / raw)
  To: opensbi; +Cc: Xianbin Zhu, Anup Patel, Bo Gan, Troy Mitchell

cold_boot_allowed() is a policy query and should not modify per-hart
state. The K1 callback currently programs ML2SETUP as a side effect.

Move the ML2SETUP programming to nascent_init(), which runs on every
hart before common initialization. Chain generic_nascent_init() to keep
the generic per-hart setup intact.

Fixes: 1f84ec2ac22e ("platform: generic: spacemit: add K1")
Reported-by: Bo Gan <ganboing@gmail.com>
Link: https://lore.kernel.org/r/56220293-88e3-451f-833e-8251656a83d9@gmail.com
Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
---
 platform/generic/spacemit/k1.c | 12 ++++++++++--
 1 file changed, 10 insertions(+), 2 deletions(-)

diff --git a/platform/generic/spacemit/k1.c b/platform/generic/spacemit/k1.c
index 7ee5f17a..19a9b27c 100644
--- a/platform/generic/spacemit/k1.c
+++ b/platform/generic/spacemit/k1.c
@@ -8,6 +8,7 @@
  */
 
 #include <platform_override.h>
+#include <sbi/riscv_asm.h>
 #include <sbi/riscv_io.h>
 #include <sbi/sbi_hsm.h>
 #include <spacemit/k1.h>
@@ -86,16 +87,23 @@ static int spacemit_k1_early_init(bool cold_boot)
 	return 0;
 }
 
-static bool spacemit_cold_boot_allowed(u32 hartid)
+static int spacemit_k1_nascent_init(void)
 {
-	csr_set(CSR_ML2SETUP, 1 << (hartid % PLATFORM_MAX_CPUS_PER_CLUSTER));
+	csr_set(CSR_ML2SETUP,
+		1 << (current_hartid() % PLATFORM_MAX_CPUS_PER_CLUSTER));
+
+	return generic_nascent_init();
+}
 
+static bool spacemit_cold_boot_allowed(u32 hartid)
+{
 	return !hartid;
 }
 
 static int spacemit_k1_platform_init(const void *fdt, int nodeoff,
 				     const struct fdt_match *match)
 {
+	generic_platform_ops.nascent_init = spacemit_k1_nascent_init;
 	generic_platform_ops.early_init = spacemit_k1_early_init;
 	generic_platform_ops.cold_boot_allowed = spacemit_cold_boot_allowed;
 

-- 
2.55.0


-- 
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 4/5] platform: generic: spacemit: k1: refactor platform support
  2026-08-18  1:14 [PATCH v2 0/5] platform: generic: spacemit: add K3 platform support Troy Mitchell
                   ` (2 preceding siblings ...)
  2026-08-18  1:14 ` [PATCH v2 3/5] platform: generic: spacemit: k1: move hart init to nascent hook Troy Mitchell
@ 2026-08-18  1:14 ` Troy Mitchell
  2026-08-18  1:14 ` [PATCH v2 5/5] platform: generic: spacemit: k3: add " Troy Mitchell
  2026-08-20  9:26 ` [PATCH v2 0/5] platform: generic: spacemit: add K3 " Bo Gan
  5 siblings, 0 replies; 11+ messages in thread
From: Troy Mitchell @ 2026-08-18  1:14 UTC (permalink / raw)
  To: opensbi; +Cc: Xianbin Zhu, Anup Patel, Bo Gan, Troy Mitchell

From: Xianbin Zhu <xianbin.zhu@linux.spacemit.com>

K3 shares cache-control CSRs, PMU idle fields, cluster sizing, and the
CCI-550 programming sequence with K1. Keeping these definitions and the
CCI helper in K1-specific files would either duplicate them or make K3
depend on K1 support.

Add a hidden PLATFORM_SPACEMIT option and move the shared definitions to
spacemit/common.h. Move cci_enable_snoop_dvm_reqs() to spacemit.c while
keeping each SoC's CCI topology and boot flow in its own platform driver.

Rename the PMU idle fields to match the K1 and K3 User Manuals. Keep the
K1 power-down mask at 0x1b, so K1 power-management behavior remains
unchanged. Keep the K1 HSM implementation K1-specific and switch it to
the shared definitions.

This is a non-functional refactoring that prepares the common support
used by K3.

Signed-off-by: Xianbin Zhu <xianbin.zhu@linux.spacemit.com>
Co-developed-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
---
 lib/utils/hsm/fdt_hsm_spacemit.c             |  5 +-
 platform/generic/Kconfig                     |  4 ++
 platform/generic/include/spacemit/common.h   | 86 +++++++++++++++++++++++++
 platform/generic/include/spacemit/k1.h       | 94 +++++-----------------------
 platform/generic/include/spacemit/spacemit.h | 14 +++++
 platform/generic/spacemit/k1.c               | 33 ++--------
 platform/generic/spacemit/objects.mk         |  1 +
 platform/generic/spacemit/spacemit.c         | 36 +++++++++++
 8 files changed, 167 insertions(+), 106 deletions(-)

diff --git a/lib/utils/hsm/fdt_hsm_spacemit.c b/lib/utils/hsm/fdt_hsm_spacemit.c
index 99c52335..a7045569 100644
--- a/lib/utils/hsm/fdt_hsm_spacemit.c
+++ b/lib/utils/hsm/fdt_hsm_spacemit.c
@@ -10,6 +10,7 @@
 #include <platform_override.h>
 #include <sbi/riscv_io.h>
 #include <sbi/sbi_hsm.h>
+#include <spacemit/common.h>
 #include <spacemit/k1.h>
 
 static const u64 cpu_wakeup_reg[] = {
@@ -42,9 +43,9 @@ static inline void spacemit_set_cpu_power(u32 hartid, bool enable)
 	value = readl(cpu_idle_base);
 
 	if (enable)
-		value &= ~PMU_AP_IDLE_PWRDOWN_MASK;
+		value &= ~PMU_AP_IDLE_PWRDOWN_K1_MASK;
 	else
-		value |= PMU_AP_IDLE_PWRDOWN_MASK;
+		value |= PMU_AP_IDLE_PWRDOWN_K1_MASK;
 
 	writel(value, cpu_idle_base);
 }
diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
index d594b140..958610d3 100644
--- a/platform/generic/Kconfig
+++ b/platform/generic/Kconfig
@@ -100,9 +100,13 @@ config PLATFORM_THEAD
 	select THEAD_C9XX_PMU
 	default n
 
+config PLATFORM_SPACEMIT
+	bool
+
 config PLATFORM_SPACEMIT_K1
 	bool "Spacemit K1 support"
 	select FDT_HSM_SPACEMIT
+	select PLATFORM_SPACEMIT
 	default n
 
 config CPU_MIPS_P8700
diff --git a/platform/generic/include/spacemit/common.h b/platform/generic/include/spacemit/common.h
new file mode 100644
index 00000000..317bb3e9
--- /dev/null
+++ b/platform/generic/include/spacemit/common.h
@@ -0,0 +1,86 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2025 SpacemiT
+ * Authors:
+ *   Xianbin Zhu <xianbin.zhu@linux.spacemit.com>
+ *   Troy Mitchell <troy.mitchell@linux.spacemit.com>
+ */
+
+#ifndef __RISCV_SPACEMIT_COMMON_H__
+#define __RISCV_SPACEMIT_COMMON_H__
+
+#include <sbi/sbi_bitops.h>
+
+#define CSR_MSETUP				0x7c0
+#define CSR_ML2SETUP				0x7f0
+
+/* dcache enable */
+#define MSETUP_DE				BIT(0)
+/* icache enable */
+#define MSETUP_IE				BIT(1)
+/* branch prediction enable */
+#define MSETUP_BPE				BIT(4)
+/* prefetch functionality enable */
+#define MSETUP_PFE				BIT(5)
+/* misaligned memory access enable */
+#define MSETUP_MME				BIT(6)
+/* ECC enable */
+#define MSETUP_ECCE				BIT(16)
+
+#define PMU_AP_BASE				0xd4282800
+
+#define PMU_AP_CORE0_WAKEUP			(PMU_AP_BASE + 0x12c)
+#define PMU_AP_CORE1_WAKEUP			(PMU_AP_BASE + 0x130)
+#define PMU_AP_CORE2_WAKEUP			(PMU_AP_BASE + 0x134)
+#define PMU_AP_CORE3_WAKEUP			(PMU_AP_BASE + 0x138)
+#define PMU_AP_CORE4_WAKEUP			(PMU_AP_BASE + 0x324)
+#define PMU_AP_CORE5_WAKEUP			(PMU_AP_BASE + 0x328)
+#define PMU_AP_CORE6_WAKEUP			(PMU_AP_BASE + 0x32c)
+#define PMU_AP_CORE7_WAKEUP			(PMU_AP_BASE + 0x330)
+
+#define PMU_AP_CORE0_IDLE_CFG			(PMU_AP_BASE + 0x124)
+#define PMU_AP_CORE1_IDLE_CFG			(PMU_AP_BASE + 0x128)
+#define PMU_AP_CORE2_IDLE_CFG			(PMU_AP_BASE + 0x160)
+#define PMU_AP_CORE3_IDLE_CFG			(PMU_AP_BASE + 0x164)
+#define PMU_AP_CORE4_IDLE_CFG			(PMU_AP_BASE + 0x304)
+#define PMU_AP_CORE5_IDLE_CFG			(PMU_AP_BASE + 0x308)
+#define PMU_AP_CORE6_IDLE_CFG			(PMU_AP_BASE + 0x30c)
+#define PMU_AP_CORE7_IDLE_CFG			(PMU_AP_BASE + 0x310)
+
+/* boot entry for X100 clusters (C0, C1) */
+#define C0_RVBADDR_LO_ADDR			0xd4282db0
+#define C0_RVBADDR_HI_ADDR			0xd4282db4
+#define C1_RVBADDR_LO_ADDR			0xd4282eb0
+#define C1_RVBADDR_HI_ADDR			0xd4282eb4
+
+/* CCI-550 base and slave interface layout */
+#define CCI_550_PLATFORM_CCI_ADDR		0xd8500000
+#define CCI_550_STATUS				0x000c
+#define CCI_550_STATUS_CHANGE_PENDING		BIT(0)
+#define CCI_550_SLAVE_IFACE0_OFFSET		0x1000
+#define CCI_550_SLAVE_IFACE_OFFSET(idx)		\
+		(CCI_550_SLAVE_IFACE0_OFFSET + ((0x1000) * (idx)))
+#define CCI_550_SNOOP_CTRL			0x0000
+#define CCI_550_SNOOP_CTRL_ENABLE_SNOOPS	BIT(0)
+#define CCI_550_SNOOP_CTRL_ENABLE_DVMS		BIT(1)
+
+/*
+ * PMU AP per-core idle control register bits (PMU_AP_COREn_IDLE_CFG).
+ * Bit definitions are identical in K1 and K3 User Manuals.
+ * These replace the original ad-hoc names from k1.h (which incorrectly
+ * called bit 1 SRAM_PWRDWN, bit 3 WAKE_MCE, and bit 4 MC_SW_REQ).
+ */
+/* bit 0: CORE_IDLE - gate core clock externally when core enters WFI */
+#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 */
+#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)
+/* bit 4: MASK_GIC_NFIQ_TO_CORE - mask nFIQ from GIC, auto-cleared by HW on power-down entry */
+#define PMU_AP_IDLE_MASK_GIC_NFIQ		BIT(4)
+
+#define PLATFORM_MAX_CPUS_PER_CLUSTER		4
+
+#endif /* __RISCV_SPACEMIT_COMMON_H__ */
diff --git a/platform/generic/include/spacemit/k1.h b/platform/generic/include/spacemit/k1.h
index ac727b04..662b42e5 100644
--- a/platform/generic/include/spacemit/k1.h
+++ b/platform/generic/include/spacemit/k1.h
@@ -1,88 +1,28 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2025 SpacemiT
+ * Authors:
+ *   Xianbin Zhu <xianbin.zhu@linux.spacemit.com>
+ *   Troy Mitchell <troy.mitchell@linux.spacemit.com>
+ */
+
 #ifndef __RISCV_SPACEMIT_K1_H__
 #define __RISCV_SPACEMIT_K1_H__
 
-#define CSR_MSETUP				0x7c0
-#define CSR_MHCR				0x7c1
-#define CSR_MRAOP				0x7c2
-#define CSR_MHINT				0x7c5
-#define CSR_ML2SETUP				0x7f0
+#include <spacemit/common.h>
 
-/* decache enable */
-#define MSETUP_DE				BIT(0)
-/* icache enable */
-#define MSETUP_IE				BIT(1)
-/* branch prediction enable */
-#define MSETUP_BPE				BIT(4)
-/* prefetch functionality enable */
-#define MSETUP_PFE				BIT(5)
-/* misaligned memory access enable */
-#define MSETUP_MME				BIT(6)
-/* ECC enable */
-#define MSETUP_ECCE				BIT(16)
+#define CSR_MRAOP				0x7c2
 
-/* cache flush */
 #define MRAOP_CACHE_FLUSH			GENMASK(1, 0)
 
-#define PMU_AP_BASE				0xd4282800
-
-#define PMU_AP_CORE0_WAKEUP			(PMU_AP_BASE + 0x12c)
-#define PMU_AP_CORE1_WAKEUP			(PMU_AP_BASE + 0x130)
-#define PMU_AP_CORE2_WAKEUP			(PMU_AP_BASE + 0x134)
-#define PMU_AP_CORE3_WAKEUP			(PMU_AP_BASE + 0x138)
-#define PMU_AP_CORE4_WAKEUP			(PMU_AP_BASE + 0x324)
-#define PMU_AP_CORE5_WAKEUP			(PMU_AP_BASE + 0x328)
-#define PMU_AP_CORE6_WAKEUP			(PMU_AP_BASE + 0x32c)
-#define PMU_AP_CORE7_WAKEUP			(PMU_AP_BASE + 0x330)
-
-#define PMU_AP_CORE0_IDLE_CFG			(PMU_AP_BASE + 0x124)
-#define PMU_AP_CORE1_IDLE_CFG			(PMU_AP_BASE + 0x128)
-#define PMU_AP_CORE2_IDLE_CFG			(PMU_AP_BASE + 0x160)
-#define PMU_AP_CORE3_IDLE_CFG			(PMU_AP_BASE + 0x164)
-#define PMU_AP_CORE4_IDLE_CFG			(PMU_AP_BASE + 0x304)
-#define PMU_AP_CORE5_IDLE_CFG			(PMU_AP_BASE + 0x308)
-#define PMU_AP_CORE6_IDLE_CFG			(PMU_AP_BASE + 0x30c)
-#define PMU_AP_CORE7_IDLE_CFG			(PMU_AP_BASE + 0x310)
-
-/* power down */
-#define PMU_AP_IDLE_PWRDWN			BIT(0)
-/* sram power down */
-#define PMU_AP_IDLE_SRAM_PWRDWN			BIT(1)
-/* enable wake up the memory controller */
-#define PMU_AP_IDLE_WAKE_MCE			BIT(3)
-/* disable memory controller software req */
-#define PMU_AP_IDLE_MC_SW_REQ			BIT(4)
-
-#define PMU_AP_IDLE_PWRDOWN_MASK		(PMU_AP_IDLE_PWRDWN | PMU_AP_IDLE_SRAM_PWRDWN | \
-						 PMU_AP_IDLE_WAKE_MCE | PMU_AP_IDLE_MC_SW_REQ)
-/* cci */
-#define C0_RVBADDR_LO_ADDR			0xd4282db0
-#define C0_RVBADDR_HI_ADDR			0xd4282db4
-#define C1_RVBADDR_LO_ADDR			0xd4282eb0
-#define C1_RVBADDR_HI_ADDR			0xd4282eb4
-
-#define CCI_550_PLATFORM_CCI_ADDR		0xd8500000
-
-/* relative to cci base */
-#define CCI_550_STATUS				0x000c
-/* status register bits */
-#define CCI_550_STATUS_CHANGE_PENDING		BIT(0)
-
-/* slave interface registers */
-#define CCI_550_SLAVE_IFACE0_OFFSET		0x1000
-#define CCI_550_SLAVE_IFACE_OFFSET(idx)		(CCI_550_SLAVE_IFACE0_OFFSET + ((0x1000) * (idx)))
-
-/* relative to slave interface base */
-#define CCI_550_SNOOP_CTRL			0x0000
-/* snoop control register bits */
-#define CCI_550_SNOOP_CTRL_ENABLE_SNOOPS	BIT(0)
-#define CCI_550_SNOOP_CTRL_ENABLE_DVMS		BIT(1)
+#define PMU_AP_IDLE_PWRDOWN_K1_MASK		(PMU_AP_IDLE_CORE_IDLE |	\
+						 PMU_AP_IDLE_CORE_PWRDWN |	\
+						 PMU_AP_IDLE_MASK_GIC_NIRQ |	\
+						 PMU_AP_IDLE_MASK_GIC_NFIQ)
 
-/* clusters and CPU mapping */
-#define PLATFORM_MAX_CPUS			8
-#define PLATFORM_MAX_CPUS_PER_CLUSTER		4
 #define CPU_TO_CLUSTER(cpu)			((cpu) / PLATFORM_MAX_CPUS_PER_CLUSTER)
 
-#define PLAT_CCI_CLUSTER0_IFACE_IX		0
-#define PLAT_CCI_CLUSTER1_IFACE_IX		1
+#define PLAT_CCI_K1_CLUSTER0_IFACE_IX		0
+#define PLAT_CCI_K1_CLUSTER1_IFACE_IX		1
 
-#endif
+#endif /* __RISCV_SPACEMIT_K1_H__ */
diff --git a/platform/generic/include/spacemit/spacemit.h b/platform/generic/include/spacemit/spacemit.h
new file mode 100644
index 00000000..671cfd05
--- /dev/null
+++ b/platform/generic/include/spacemit/spacemit.h
@@ -0,0 +1,14 @@
+/* SPDX-License-Identifier: BSD-2-Clause */
+/*
+ * Copyright (c) 2025 SpacemiT
+ * Authors:
+ *   Xianbin Zhu <xianbin.zhu@linux.spacemit.com>
+ *   Troy Mitchell <troy.mitchell@linux.spacemit.com>
+ */
+
+#ifndef __RISCV_SPACEMIT_H__
+#define __RISCV_SPACEMIT_H__
+
+void cci_enable_snoop_dvm_reqs(const int *cci_map, unsigned int master_id);
+
+#endif
diff --git a/platform/generic/spacemit/k1.c b/platform/generic/spacemit/k1.c
index 19a9b27c..323239e0 100644
--- a/platform/generic/spacemit/k1.c
+++ b/platform/generic/spacemit/k1.c
@@ -12,37 +12,16 @@
 #include <sbi/riscv_io.h>
 #include <sbi/sbi_hsm.h>
 #include <spacemit/k1.h>
+#include <spacemit/spacemit.h>
+
+#define PLATFORM_MAX_CPUS			8
 
 /* only use 0-1 cluster in SpacemiT K1 */
 static const int cci_map[] = {
-	PLAT_CCI_CLUSTER0_IFACE_IX,
-	PLAT_CCI_CLUSTER1_IFACE_IX,
+	PLAT_CCI_K1_CLUSTER0_IFACE_IX,
+	PLAT_CCI_K1_CLUSTER1_IFACE_IX,
 };
 
-static void cci_enable_snoop_dvm_reqs(unsigned int master_id)
-{
-	int slave_if_id = cci_map[master_id];
-
-	/*
-	 * Enable Snoops and DVM messages, no need for Read/Modify/Write as
-	 * rest of bits are write ignore
-	 */
-	writel(CCI_550_SNOOP_CTRL_ENABLE_DVMS | CCI_550_SNOOP_CTRL_ENABLE_SNOOPS,
-	       (void *)(u64)CCI_550_PLATFORM_CCI_ADDR +
-	       CCI_550_SLAVE_IFACE_OFFSET(slave_if_id) + CCI_550_SNOOP_CTRL);
-
-	/*
-	 * Wait for the completion of the write to the Snoop Control Register
-	 * before testing the change_pending bit
-	 */
-	mb();
-
-	/* Wait for the dust to settle down */
-	while ((readl((void *)(u64)CCI_550_PLATFORM_CCI_ADDR + CCI_550_STATUS) &
-	       CCI_550_STATUS_CHANGE_PENDING))
-		;
-}
-
 static void spacemit_k1_pre_init(void)
 {
 	unsigned int clusterid, cluster_enabled = 0;
@@ -62,7 +41,7 @@ static void spacemit_k1_pre_init(void)
 
 		if (!(cluster_enabled & (1 << clusterid))) {
 			cluster_enabled |= 1 << clusterid;
-			cci_enable_snoop_dvm_reqs(clusterid);
+			cci_enable_snoop_dvm_reqs(cci_map, clusterid);
 		}
 	}
 }
diff --git a/platform/generic/spacemit/objects.mk b/platform/generic/spacemit/objects.mk
index dcb37867..8309ad1f 100644
--- a/platform/generic/spacemit/objects.mk
+++ b/platform/generic/spacemit/objects.mk
@@ -2,5 +2,6 @@
 # SPDX-License-Identifier: BSD-2-Clause
 #
 
+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
diff --git a/platform/generic/spacemit/spacemit.c b/platform/generic/spacemit/spacemit.c
new file mode 100644
index 00000000..a215c1ad
--- /dev/null
+++ b/platform/generic/spacemit/spacemit.c
@@ -0,0 +1,36 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2025 SpacemiT
+ * Authors:
+ *   Xianbin Zhu <xianbin.zhu@linux.spacemit.com>
+ *   Troy Mitchell <troy.mitchell@linux.spacemit.com>
+ */
+
+#include <platform_override.h>
+#include <sbi/riscv_io.h>
+#include <spacemit/common.h>
+
+void cci_enable_snoop_dvm_reqs(const int *cci_map, unsigned int master_id)
+{
+	int slave_if_id = cci_map[master_id];
+
+	/*
+	 * Enable Snoops and DVM messages, no need for Read/Modify/Write as
+	 * rest of bits are write ignore
+	 */
+	writel(CCI_550_SNOOP_CTRL_ENABLE_DVMS | CCI_550_SNOOP_CTRL_ENABLE_SNOOPS,
+	       (void *)(unsigned long)CCI_550_PLATFORM_CCI_ADDR +
+	       CCI_550_SLAVE_IFACE_OFFSET(slave_if_id) + CCI_550_SNOOP_CTRL);
+
+	/*
+	 * Wait for the completion of the write to the Snoop Control Register
+	 * before testing the change_pending bit
+	 */
+	mb();
+
+	/* Wait for the dust to settle down */
+	while (readl((void *)(unsigned long)CCI_550_PLATFORM_CCI_ADDR + CCI_550_STATUS) &
+	       CCI_550_STATUS_CHANGE_PENDING)
+		;
+}

-- 
2.55.0


-- 
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* [PATCH v2 5/5] platform: generic: spacemit: k3: add platform support
  2026-08-18  1:14 [PATCH v2 0/5] platform: generic: spacemit: add K3 platform support Troy Mitchell
                   ` (3 preceding siblings ...)
  2026-08-18  1:14 ` [PATCH v2 4/5] platform: generic: spacemit: k1: refactor platform support Troy Mitchell
@ 2026-08-18  1:14 ` Troy Mitchell
  2026-08-20  9:18   ` Bo Gan
  2026-08-20 14:25   ` Samuel Holland
  2026-08-20  9:26 ` [PATCH v2 0/5] platform: generic: spacemit: add K3 " Bo Gan
  5 siblings, 2 replies; 11+ messages in thread
From: Troy Mitchell @ 2026-08-18  1:14 UTC (permalink / raw)
  To: opensbi; +Cc: Xianbin Zhu, Anup Patel, Bo Gan, Troy Mitchell

From: Xianbin Zhu <xianbin.zhu@linux.spacemit.com>

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 <xianbin.zhu@linux.spacemit.com>
Co-developed-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
---
 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 <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].
+ * 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 <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__ */
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 <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_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 <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:
+	/* 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 @@
 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

-- 
2.55.0


-- 
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi

^ permalink raw reply related	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 1/5] lib: sbi: select expected trap handler per hart
  2026-08-18  1:14 ` [PATCH v2 1/5] lib: sbi: select expected trap handler per hart Troy Mitchell
@ 2026-08-20  8:40   ` Bo Gan
  0 siblings, 0 replies; 11+ messages in thread
From: Bo Gan @ 2026-08-20  8:40 UTC (permalink / raw)
  To: Troy Mitchell, opensbi; +Cc: Xianbin Zhu, Anup Patel, Bo Gan

On 8/17/26 18:14, Troy Mitchell wrote:
> The expected trap handler is selected once by the cold boot hart. This
> breaks heterogeneous systems where the cold boot hart implements H but
> another hart does not, because the H-aware handler accesses mtval2 and
> mtinst.
> 
> Select the handler from the current hart's MISA at each use so every
> hart uses only the CSRs it implements.
> 
> Fixes: 1de66d170e71 ("lib: Optimize unpriv load/store implementation")
> Reported-by: Bo Gan <ganboing@gmail.com>
> Link: https://lore.kernel.org/r/e702f291-dde8-4b99-a65e-182d2b847720@gmail.com
> Suggested-by: Bo Gan <ganboing@gmail.com>
> Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>

Reading PATCH 5/5, I realized H can be dynamically turned on/off for X100,
so dynamically determine expected_trap_addr makes even more sense now.

Reviewed-by: Bo Gan <ganboing@gmail.com>

Bo

> ---
>   include/sbi/sbi_csr_detect.h | 4 ++--
>   include/sbi/sbi_hart.h       | 2 +-
>   lib/sbi/sbi_hart.c           | 9 +++++----
>   lib/sbi/sbi_illegal_atomic.c | 4 ++--
>   lib/sbi/sbi_unpriv.c         | 6 +++---
>   5 files changed, 13 insertions(+), 12 deletions(-)
> 
> diff --git a/include/sbi/sbi_csr_detect.h b/include/sbi/sbi_csr_detect.h
> index 097c31c8..31e50db9 100644
> --- a/include/sbi/sbi_csr_detect.h
> +++ b/include/sbi/sbi_csr_detect.h
> @@ -16,9 +16,9 @@
>   
>   #define csr_read_allowed(csr_num, trap)					\
>   	({								\
> +	register ulong mtvec = sbi_hart_expected_trap_addr();		\
>   	register ulong tinfo asm("a3") = (ulong)trap;			\
>   	register ulong ttmp asm("a4");					\
> -	register ulong mtvec = (ulong)sbi_hart_expected_trap;		\
>   	register ulong ret = 0;						\
>   	((struct sbi_trap_info *)(trap))->cause = 0;			\
>   	asm volatile(							\
> @@ -35,9 +35,9 @@
>   
>   #define csr_write_allowed(csr_num, trap, value)				\
>   	({								\
> +	register ulong mtvec = sbi_hart_expected_trap_addr();		\
>   	register ulong tinfo asm("a3") = (ulong)trap;			\
>   	register ulong ttmp asm("a4");					\
> -	register ulong mtvec = (ulong)sbi_hart_expected_trap;		\
>   	((struct sbi_trap_info *)(trap))->cause = 0;			\
>   	asm volatile(							\
>   		"add %[ttmp], %[tinfo], zero\n"				\
> diff --git a/include/sbi/sbi_hart.h b/include/sbi/sbi_hart.h
> index 543393bb..6f4ee31e 100644
> --- a/include/sbi/sbi_hart.h
> +++ b/include/sbi/sbi_hart.h
> @@ -135,7 +135,7 @@ struct sbi_scratch;
>   int sbi_hart_reinit(struct sbi_scratch *scratch);
>   int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot);
>   
> -extern void (*sbi_hart_expected_trap)(void);
> +ulong sbi_hart_expected_trap_addr(void);
>   
>   unsigned int sbi_hart_mhpm_mask(struct sbi_scratch *scratch);
>   void sbi_hart_delegation_dump(struct sbi_scratch *scratch,
> diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
> index bee88557..14e44955 100644
> --- a/lib/sbi/sbi_hart.c
> +++ b/lib/sbi/sbi_hart.c
> @@ -25,7 +25,11 @@
>   extern void __sbi_expected_trap(void);
>   extern void __sbi_expected_trap_hext(void);
>   
> -void (*sbi_hart_expected_trap)(void) = &__sbi_expected_trap;
> +ulong sbi_hart_expected_trap_addr(void)
> +{
> +	return misa_extension('H') ? (ulong)&__sbi_expected_trap_hext :
> +				     (ulong)&__sbi_expected_trap;
> +}
>   
>   unsigned long hart_features_offset;
>   
> @@ -712,9 +716,6 @@ int sbi_hart_init(struct sbi_scratch *scratch, bool cold_boot)
>   	csr_write(CSR_MIP, 0);
>   
>   	if (cold_boot) {
> -		if (misa_extension('H'))
> -			sbi_hart_expected_trap = &__sbi_expected_trap_hext;
> -
>   		hart_features_offset = sbi_scratch_alloc_offset(
>   					sizeof(struct sbi_hart_features));
>   		if (!hart_features_offset)
> diff --git a/lib/sbi/sbi_illegal_atomic.c b/lib/sbi/sbi_illegal_atomic.c
> index 977a9ad0..30f5118e 100644
> --- a/lib/sbi/sbi_illegal_atomic.c
> +++ b/lib/sbi/sbi_illegal_atomic.c
> @@ -30,7 +30,7 @@ int sbi_illegal_atomic(ulong insn, struct sbi_trap_regs *regs)
>   	{									\
>   		register ulong tinfo asm("a3");					\
>   		register ulong mstatus = 0;					\
> -		register ulong mtvec = (ulong)sbi_hart_expected_trap;		\
> +		register ulong mtvec = sbi_hart_expected_trap_addr();		\
>   		type ret = 0;							\
>   		trap->cause = 0;						\
>   		asm volatile(							\
> @@ -57,7 +57,7 @@ int sbi_illegal_atomic(ulong insn, struct sbi_trap_regs *regs)
>   	{									\
>   		register ulong tinfo asm("a3");					\
>   		register ulong mstatus = 0;					\
> -		register ulong mtvec = (ulong)sbi_hart_expected_trap;		\
> +		register ulong mtvec = sbi_hart_expected_trap_addr();		\
>   		type ret = 0;							\
>   		trap->cause = 0;						\
>   		asm volatile(							\
> diff --git a/lib/sbi/sbi_unpriv.c b/lib/sbi/sbi_unpriv.c
> index 60becedc..1550d111 100644
> --- a/lib/sbi/sbi_unpriv.c
> +++ b/lib/sbi/sbi_unpriv.c
> @@ -33,9 +33,9 @@ union sbi_unpriv_data {
>   	type sbi_load_##type(const type *addr,                                \
>   			     struct sbi_trap_info *trap)                      \
>   	{                                                                     \
> +		register ulong mtvec = sbi_hart_expected_trap_addr();         \
>   		register ulong tinfo asm("a3") = (ulong)trap;                 \
>   		register ulong mstatus = 0;                                   \
> -		register ulong mtvec = (ulong)sbi_hart_expected_trap;         \
>   		type ret = 0;                                                 \
>   		trap->cause = 0;                                              \
>   		asm volatile(                                                 \
> @@ -58,9 +58,9 @@ union sbi_unpriv_data {
>   	void sbi_store_##type(type *addr, type val,                           \
>   			      struct sbi_trap_info *trap)                     \
>   	{                                                                     \
> +		register ulong mtvec = sbi_hart_expected_trap_addr();         \
>   		register ulong tinfo asm("a3") = (ulong)trap;                 \
>   		register ulong mstatus = 0;                                   \
> -		register ulong mtvec = (ulong)sbi_hart_expected_trap;         \
>   		trap->cause = 0;                                              \
>   		asm volatile(                                                 \
>   			"csrrw %[mtvec], " STR(CSR_MTVEC) ", %[mtvec]\n"      \
> @@ -207,7 +207,7 @@ ulong sbi_get_insn(ulong mepc, struct sbi_trap_info *trap)
>   	register ulong tinfo asm("a3");
>   	register ulong ttmp asm("a4");
>   	register ulong mstatus = 0;
> -	register ulong mtvec = (ulong)sbi_hart_expected_trap;
> +	register ulong mtvec = sbi_hart_expected_trap_addr();
>   	ulong insn = 0;
>   
>   	trap->cause = 0;
> 


-- 
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 3/5] platform: generic: spacemit: k1: move hart init to nascent hook
  2026-08-18  1:14 ` [PATCH v2 3/5] platform: generic: spacemit: k1: move hart init to nascent hook Troy Mitchell
@ 2026-08-20  8:42   ` Bo Gan
  0 siblings, 0 replies; 11+ messages in thread
From: Bo Gan @ 2026-08-20  8:42 UTC (permalink / raw)
  To: Troy Mitchell, opensbi; +Cc: Xianbin Zhu, Anup Patel, Bo Gan

On 8/17/26 18:14, Troy Mitchell wrote:
> cold_boot_allowed() is a policy query and should not modify per-hart
> state. The K1 callback currently programs ML2SETUP as a side effect.
> 
> Move the ML2SETUP programming to nascent_init(), which runs on every
> hart before common initialization. Chain generic_nascent_init() to keep
> the generic per-hart setup intact.
> 
> Fixes: 1f84ec2ac22e ("platform: generic: spacemit: add K1")
> Reported-by: Bo Gan <ganboing@gmail.com>
> Link: https://lore.kernel.org/r/56220293-88e3-451f-833e-8251656a83d9@gmail.com
> Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>

Reviewed-by: Bo Gan <ganboing@gmail.com>

Bo

> ---
>   platform/generic/spacemit/k1.c | 12 ++++++++++--
>   1 file changed, 10 insertions(+), 2 deletions(-)
> 
> diff --git a/platform/generic/spacemit/k1.c b/platform/generic/spacemit/k1.c
> index 7ee5f17a..19a9b27c 100644
> --- a/platform/generic/spacemit/k1.c
> +++ b/platform/generic/spacemit/k1.c
> @@ -8,6 +8,7 @@
>    */
>   
>   #include <platform_override.h>
> +#include <sbi/riscv_asm.h>
>   #include <sbi/riscv_io.h>
>   #include <sbi/sbi_hsm.h>
>   #include <spacemit/k1.h>
> @@ -86,16 +87,23 @@ static int spacemit_k1_early_init(bool cold_boot)
>   	return 0;
>   }
>   
> -static bool spacemit_cold_boot_allowed(u32 hartid)
> +static int spacemit_k1_nascent_init(void)
>   {
> -	csr_set(CSR_ML2SETUP, 1 << (hartid % PLATFORM_MAX_CPUS_PER_CLUSTER));
> +	csr_set(CSR_ML2SETUP,
> +		1 << (current_hartid() % PLATFORM_MAX_CPUS_PER_CLUSTER));
> +
> +	return generic_nascent_init();
> +}
>   
> +static bool spacemit_cold_boot_allowed(u32 hartid)
> +{
>   	return !hartid;
>   }
>   
>   static int spacemit_k1_platform_init(const void *fdt, int nodeoff,
>   				     const struct fdt_match *match)
>   {
> +	generic_platform_ops.nascent_init = spacemit_k1_nascent_init;
>   	generic_platform_ops.early_init = spacemit_k1_early_init;
>   	generic_platform_ops.cold_boot_allowed = spacemit_cold_boot_allowed;
>   
> 


-- 
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 5/5] platform: generic: spacemit: k3: add platform support
  2026-08-18  1:14 ` [PATCH v2 5/5] platform: generic: spacemit: k3: add " Troy Mitchell
@ 2026-08-20  9:18   ` Bo Gan
  2026-08-20 14:25   ` Samuel Holland
  1 sibling, 0 replies; 11+ messages in thread
From: Bo Gan @ 2026-08-20  9:18 UTC (permalink / raw)
  To: Troy Mitchell, opensbi; +Cc: Xianbin Zhu, Anup Patel, Bo Gan

Hi Troy, Xianbin,

See my comments below:

On 8/17/26 18:14, Troy Mitchell wrote:
> From: Xianbin Zhu <xianbin.zhu@linux.spacemit.com>
> 
> 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 <xianbin.zhu@linux.spacemit.com>
> Co-developed-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
> Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
> ---
>   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 <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].
> + * 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).
> + */

Use _BITUL/ULL? They should take care of omitting the "UL" if __ASSEMBLER__

> +#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 <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__ */
> 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 <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_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,

Why not just PMU_AP_X100_IDLE_STATUS? You don't need to use it, but for
the sake of faithfully describing the HW?

> +	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,

Same issue as above. Just do BIT(4)

> +	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;

This condition will never be true.

> +
> +	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.

I'm confused by this. Are we doing some duplicate work? Hasn't
_spacemit_k3_warm_start already done those? What's the audio buffer used
for? It says set audio buffer attributes before enabling dcache, but
isn't dcache already enabled by the asm code?


> + * 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");

Better use __sbi_sfence_vma_all()? Also is __sbi_hfence_gvma_all() needed
for X100?

> +
> +	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 <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:
> +	/* 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 @@
>   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
> 

Perhaps in objects.mk, you should skip the build for rv32. I bet you'll get
warnings or even errors targeting rv32. No point of doing that anyway.

Bo


-- 
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 0/5] platform: generic: spacemit: add K3 platform support
  2026-08-18  1:14 [PATCH v2 0/5] platform: generic: spacemit: add K3 platform support Troy Mitchell
                   ` (4 preceding siblings ...)
  2026-08-18  1:14 ` [PATCH v2 5/5] platform: generic: spacemit: k3: add " Troy Mitchell
@ 2026-08-20  9:26 ` Bo Gan
  5 siblings, 0 replies; 11+ messages in thread
From: Bo Gan @ 2026-08-20  9:26 UTC (permalink / raw)
  To: Troy Mitchell, opensbi; +Cc: Xianbin Zhu, Anup Patel, Bo Gan

Hi Troy, Xianbin,

I got my com260 delivered just days ago, and I'm able to review and test
this series. I just sent out some review feedbacks. One more high-level
question is regarding rpmi. I see the vendor opensbi utilize the services
provided by esos running on the small cores. What's the plan here? Is it
(this patch series) suppose to work with the esos currently shipped?

Also you mentioned "K3 SDK" many times, but I couldn't find any pointers.
What is that exactly?

Bo

On 8/17/26 18:14, Troy Mitchell wrote:
> Add initial OpenSBI platform support for the SpacemiT K3 SoC. K3 has
> 16 harts split across four clusters: eight X100 harts in C0/C1 and
> eight A100 harts in C2/C3.
> 
> The main upstream integration constraint is that K3 is heterogeneous at
> the ISA level. The CPU device tree used for testing exposes the same
> non-H standard extensions for X100 and A100. X100 additionally exposes H
> and the related Sha, Shcounterenw, Shgatpa, Shtvala, Shvsatpa,
> Shvstvala, and Shvstvecd extensions. X100 has a 256-bit VLEN, while
> A100 has a 1024-bit VLEN. A100 also implements the vendor-specific
> SpacemiT IME and FP8 AI extensions, which OpenSBI does not inspect or
> use.
> 
> At the base of this series, OpenSBI selects the expected-trap handler
> once from the cold-boot hart and reuses it globally. An X100 cold-boot
> hart would therefore make an A100 hart use the H-aware handler, which
> accesses mtval2 and mtinst even though A100 does not implement H. Select
> the handler from the current hart instead. An audit of the remaining
> extension-dependent paths found that ISA extensions and detected CSR
> features are stored per-hart, the generic FDT parser visits every hart,
> and vector code reads the current hart's VLENB. The K3 CPU device tree
> also exposes the same counter and timer extensions on both core types.
> No other cold-boot-hart-derived ISA selection was found.
> 
> K1 and K3 share vendor cache-control CSRs, PMU idle fields, cluster
> sizing, and the CCI-550 programming sequence, but their CCI topology,
> boot flow, and HSM support differ. Factor only the common definitions and
> CCI helper so K3 does not depend on K1 and the K1 encoded values and
> behavior remain unchanged. Run per-hart register setup from
> nascent_init() rather than the policy-only cold_boot_allowed() hook.
> 
> On K3, the cold-boot hart programs the four cluster warm-boot vectors,
> enables CCI snoop and DVM requests, prevents WFI from powering down cores
> or clusters, and wakes the secondary harts. A bounded wait prevents a
> non-responsive secondary hart from stalling boot indefinitely. Each hart
> programs its own PMA, cache, snooping, and prefetch state, with X100 harts
> also enabling H. The warm-boot entry establishes cache coherency before
> entering common code so secondary harts can observe state published by
> the cold-boot hart.
> 
> Runtime validation used the SpacemiT K3 SDK. The UART at 115200 baud
> showed OpenSBI and U-Boot starting successfully, followed by Linux
> bringing all 16 harts online. The series has also been build-tested with:
> 
>    - the generic default configuration
>    - a K1 configuration with K3 disabled and the SpacemiT HSM driver
>    - a K3 configuration with K1 and the SpacemiT HSM driver disabled
> 
> To reproduce the runtime test, build this OpenSBI tree at the address
> where the SDK loads it, then copy the resulting firmware into the SDK
> output directory:
> 
>    K3_SDK_DIR=/path/to/k3-sdk
>    make O=build-k3 CROSS_COMPILE=riscv64-linux-gnu- PLATFORM=generic \
>         FW_TEXT_START=0x100000000
>    cp build-k3/platform/generic/firmware/fw_dynamic.bin \
>       "$K3_SDK_DIR/output/fw_dynamic.bin"
> 
> Before rebuilding U-Boot, update the UART0 node in the K3 board DT
> selected by SPL and passed to OpenSBI. The SDK describes it only as
> "ns16550", so upstream OpenSBI initializes it without UART_CAP_UUE and
> writes 0x00 to UART_IER. The K3 UART requires UART_IER_UUE (bit 6).
> Change the node in uboot-2022.10/arch/riscv/dts/k3.dtsi to:
> 
>    uart0: uart@d4017000 {
>            ...
>            compatible = "spacemit,k1-uart", "intel,xscale-uart",
>                         "ns16550";
>            ...
>    };
> 
> The compatible order is significant. Upstream OpenSBI skips the vendor
> string, then matches "intel,xscale-uart", which selects UART_CAP_UUE and
> writes 0x40 to UART_IER. The final "ns16550" remains a generic fallback.
> This is the DT used for the successful boot test.
> 
> With output/fw_dynamic.bin already present, rebuild U-Boot. The SDK
> preserves that OpenSBI binary and packages it with U-Boot:
> 
>    make -C "$K3_SDK_DIR" uboot
> 
> This produces output/FSBL.bin and output/u-boot-opensbi.itb. Using the
> SDK-built output/Image.itb, enter BROM fastboot mode with FEL+RESET and
> stage all three images into RAM:
> 
>    fastboot stage "$K3_SDK_DIR/output/FSBL.bin"
>    fastboot continue
>    sleep 8
>    fastboot stage "$K3_SDK_DIR/output/u-boot-opensbi.itb"
>    fastboot continue
>    sleep 3
>    fastboot stage "$K3_SDK_DIR/output/Image.itb"
>    fastboot continue
> 
> Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
> ---
> Changes in v2:
>    - correct the K3 PMACFG0 CSR number from 0xbc0 to 0x7de
>    - select the expected-trap handler per hart for heterogeneous H support
>    - move K1 and K3 per-hart setup out of cold_boot_allowed()
>    - split the generic and K1 fixes into standalone patches, growing the
>      series from three patches to five
>    - document X100/A100 ISA and VLEN differences and the homogeneous-core
>      audit
>    - document OpenSBI/U-Boot packaging, the UART compatible adjustment,
>      and the USB fastboot test procedure
>    - Link to v1: https://lore.kernel.org/r/20260724-spacemit-k3-v1-0-f0e49329feb8@linux.spacemit.com
> 
> ---
> Troy Mitchell (2):
>        lib: sbi: select expected trap handler per hart
>        platform: generic: spacemit: k1: move hart init to nascent hook
> 
> Xianbin Zhu (3):
>        platform: generic: spacemit: k1: rename cache flush operation
>        platform: generic: spacemit: k1: refactor platform support
>        platform: generic: spacemit: k3: add platform support
> 
>   include/sbi/sbi_csr_detect.h                 |   4 +-
>   include/sbi/sbi_hart.h                       |   2 +-
>   lib/sbi/sbi_hart.c                           |   9 +-
>   lib/sbi/sbi_illegal_atomic.c                 |   4 +-
>   lib/sbi/sbi_unpriv.c                         |   6 +-
>   lib/utils/hsm/fdt_hsm_spacemit.c             |   7 +-
>   platform/generic/Kconfig                     |   9 +
>   platform/generic/configs/defconfig           |   1 +
>   platform/generic/include/spacemit/common.h   |  88 +++++++++
>   platform/generic/include/spacemit/k1.h       |  96 ++--------
>   platform/generic/include/spacemit/k3.h       | 144 +++++++++++++++
>   platform/generic/include/spacemit/k3_asm.h   |  16 ++
>   platform/generic/include/spacemit/spacemit.h |  14 ++
>   platform/generic/spacemit/k1.c               |  45 ++---
>   platform/generic/spacemit/k3.c               | 266 +++++++++++++++++++++++++++
>   platform/generic/spacemit/k3_asm.S           |  33 ++++
>   platform/generic/spacemit/objects.mk         |   3 +
>   platform/generic/spacemit/spacemit.c         |  36 ++++
>   18 files changed, 661 insertions(+), 122 deletions(-)
> ---
> base-commit: c0f87f10d1bfb9e72a84ddfafb5604ee1bfe9d04
> change-id: 20260723-spacemit-k3-84720a7be53d
> 
> Best regards,
> --
> Troy Mitchell <troy.mitchell@linux.spacemit.com>
> 


-- 
opensbi mailing list
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi

^ permalink raw reply	[flat|nested] 11+ messages in thread

* Re: [PATCH v2 5/5] platform: generic: spacemit: k3: add platform support
  2026-08-18  1:14 ` [PATCH v2 5/5] platform: generic: spacemit: k3: add " Troy Mitchell
  2026-08-20  9:18   ` Bo Gan
@ 2026-08-20 14:25   ` Samuel Holland
  1 sibling, 0 replies; 11+ messages in thread
From: Samuel Holland @ 2026-08-20 14:25 UTC (permalink / raw)
  To: Troy Mitchell, opensbi; +Cc: Xianbin Zhu, Anup Patel, Bo Gan

Hi Troy,

On 2026-08-17 8:14 PM, Troy Mitchell wrote:
> From: Xianbin Zhu <xianbin.zhu@linux.spacemit.com>
> 
> 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 <xianbin.zhu@linux.spacemit.com>
> Co-developed-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
> Signed-off-by: Troy Mitchell <troy.mitchell@linux.spacemit.com>
> ---
>  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 <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].
> + * 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 <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__ */
> 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 <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_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;
> +			}
> +		}
> +	}

The operations in these loops should be in a .hart_start function of an HSM
driver, not run in a loop at cold boot. You will need to do this anyway to
support power management, and it makes cold boot faster.

> +}
> +
> +/*
> + * 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;
> +}

If harts 1-15 are not powered on until spacemit_k3_pre_init(), this callback
won't do anything, because hart 0 is the only hart running at this point.

Regards,
Samuel

> +
> +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 <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:
> +	/* 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 @@
>  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
opensbi@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/opensbi

^ permalink raw reply	[flat|nested] 11+ messages in thread

end of thread, other threads:[~2026-08-20 14:25 UTC | newest]

Thread overview: 11+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-08-18  1:14 [PATCH v2 0/5] platform: generic: spacemit: add K3 platform support Troy Mitchell
2026-08-18  1:14 ` [PATCH v2 1/5] lib: sbi: select expected trap handler per hart Troy Mitchell
2026-08-20  8:40   ` Bo Gan
2026-08-18  1:14 ` [PATCH v2 2/5] platform: generic: spacemit: k1: rename cache flush operation Troy Mitchell
2026-08-18  1:14 ` [PATCH v2 3/5] platform: generic: spacemit: k1: move hart init to nascent hook Troy Mitchell
2026-08-20  8:42   ` Bo Gan
2026-08-18  1:14 ` [PATCH v2 4/5] platform: generic: spacemit: k1: refactor platform support Troy Mitchell
2026-08-18  1:14 ` [PATCH v2 5/5] platform: generic: spacemit: k3: add " Troy Mitchell
2026-08-20  9:18   ` Bo Gan
2026-08-20 14:25   ` Samuel Holland
2026-08-20  9:26 ` [PATCH v2 0/5] platform: generic: spacemit: add K3 " Bo Gan

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox