armbian-build/patch/atf/atf-spacemit/001-v1.0alpha2.patch
Patrick Yavitz 3440691a51 SpacemiT: Add ATF patches (OpenSBI)
Tag: 1.3
Source: https://gitee.com/bianbu-linux/opensbi
Signed-off-by: Patrick Yavitz <pyavitz@armbian.com>
2024-07-01 19:15:00 +02:00

6921 lines
237 KiB
Diff

From 59ac4b4bb09bfb031369a2b40156f4a1c94b93cd Mon Sep 17 00:00:00 2001
From: James Deng <james.deng@spacemit.com>
Date: Fri, 1 Mar 2024 19:54:35 +0800
Subject: [PATCH] Update for v1.0alpha2
---
Makefile | 17 +-
firmware/fw_base.ldS | 5 +
include/sbi/riscv_encoding.h | 2 +
include/sbi/sbi_ecall_interface.h | 4 +
include/sbi/sbi_hsm.h | 8 +-
include/sbi_utils/cache/cacheflush.h | 192 ++++
include/sbi_utils/cci/cci.h | 27 +
include/sbi_utils/irqchip/fdt_irqchip_plic.h | 2 +
.../psci/drivers/arm/css/css_mhu_doorbell.h | 14 +
.../sbi_utils/psci/drivers/arm/css/css_scp.h | 10 +
include/sbi_utils/psci/drivers/arm/css/scmi.h | 141 +++
.../psci/drivers/arm/css/scmi_private.h | 146 +++
.../arm/board/spacemit/include/platform_def.h | 10 +
.../sbi_utils/psci/plat/arm/common/arm_def.h | 19 +
.../sbi_utils/psci/plat/arm/common/plat_arm.h | 21 +
.../psci/plat/arm/css/common/css_pm.h | 36 +
include/sbi_utils/psci/plat/common/platform.h | 13 +
include/sbi_utils/psci/psci.h | 223 +++++
include/sbi_utils/psci/psci_lib.h | 8 +
lib/sbi/sbi_ecall_base.c | 10 +
lib/sbi/sbi_ecall_hsm.c | 5 +
lib/sbi/sbi_hart.c | 2 +
lib/sbi/sbi_hsm.c | 80 +-
lib/sbi/sbi_init.c | 10 +-
lib/sbi/sbi_pmu.c | 30 +-
lib/sbi/sbi_scratch.c | 8 +-
lib/utils/Kconfig | 2 +
.../arm_scmi/board/spacemit/spacemit_pm.c | 41 +
lib/utils/arm_scmi/common/arm_pm.c | 68 ++
lib/utils/arm_scmi/css/common/css_pm.c | 298 ++++++
lib/utils/arm_scmi/css/mhu/css_mhu_doorbell.c | 27 +
lib/utils/arm_scmi/css/mhu/mhu.h | 130 +++
lib/utils/arm_scmi/css/scmi/scmi_common.c | 228 +++++
.../arm_scmi/css/scmi/scmi_pwr_dmn_proto.c | 102 ++
.../arm_scmi/css/scmi/scmi_sys_pwr_proto.c | 90 ++
lib/utils/arm_scmi/css/scp/css_pm_scmi.c | 418 +++++++++
lib/utils/arm_scmi/objects.mk | 24 +
lib/utils/cci/bus-cci.c | 168 ++++
lib/utils/cci/objects.mk | 7 +
lib/utils/ipi/aclint_mswi.c | 4 +-
lib/utils/irqchip/fdt_irqchip_plic.c | 5 +
lib/utils/psci/Kconfig | 21 +
lib/utils/psci/objects.mk | 30 +
lib/utils/psci/psci_common.c | 872 ++++++++++++++++++
lib/utils/psci/psci_main.c | 188 ++++
lib/utils/psci/psci_off.c | 173 ++++
lib/utils/psci/psci_on.c | 246 +++++
lib/utils/psci/psci_private.h | 198 ++++
lib/utils/psci/psci_setup.c | 242 +++++
lib/utils/psci/psci_suspend.c | 298 ++++++
.../spacemit/plat/k1x/underly_implement.c | 345 +++++++
lib/utils/psci/spacemit/plat/plat_pm.c | 258 ++++++
.../psci/spacemit/plat/underly_implement.h | 14 +
lib/utils/psci/spacemit/spacemit_topology.c | 26 +
lib/utils/serial/uart8250.c | 5 +
lib/utils/timer/aclint_mtimer.c | 4 +-
platform/generic/Kconfig | 36 +
platform/generic/configs/defconfig | 3 +-
.../generic/configs/k1-x_fpga_1x4_defconfig | 16 +
.../generic/configs/k1-x_fpga_2x2_defconfig | 16 +
platform/generic/configs/k1-x_fpga_defconfig | 16 +
platform/generic/configs/k1_defconfig | 16 +
.../include/spacemit/k1x/core_common.h | 13 +
.../generic/include/spacemit/k1x/k1x_evb.h | 72 ++
.../generic/include/spacemit/k1x/k1x_fpga.h | 73 ++
.../include/spacemit/spacemit_config.h | 30 +
platform/generic/objects.mk | 2 +-
platform/generic/spacemit/fw_dynamic.its | 31 +
platform/generic/spacemit/objects.mk | 7 +
platform/generic/spacemit/spacemit_k1.c | 194 ++++
71 files changed, 6067 insertions(+), 33 deletions(-)
create mode 100644 include/sbi_utils/cache/cacheflush.h
create mode 100644 include/sbi_utils/cci/cci.h
create mode 100644 include/sbi_utils/psci/drivers/arm/css/css_mhu_doorbell.h
create mode 100644 include/sbi_utils/psci/drivers/arm/css/css_scp.h
create mode 100644 include/sbi_utils/psci/drivers/arm/css/scmi.h
create mode 100644 include/sbi_utils/psci/drivers/arm/css/scmi_private.h
create mode 100644 include/sbi_utils/psci/plat/arm/board/spacemit/include/platform_def.h
create mode 100644 include/sbi_utils/psci/plat/arm/common/arm_def.h
create mode 100644 include/sbi_utils/psci/plat/arm/common/plat_arm.h
create mode 100644 include/sbi_utils/psci/plat/arm/css/common/css_pm.h
create mode 100644 include/sbi_utils/psci/plat/common/platform.h
create mode 100644 include/sbi_utils/psci/psci.h
create mode 100644 include/sbi_utils/psci/psci_lib.h
create mode 100644 lib/utils/arm_scmi/board/spacemit/spacemit_pm.c
create mode 100644 lib/utils/arm_scmi/common/arm_pm.c
create mode 100644 lib/utils/arm_scmi/css/common/css_pm.c
create mode 100644 lib/utils/arm_scmi/css/mhu/css_mhu_doorbell.c
create mode 100644 lib/utils/arm_scmi/css/mhu/mhu.h
create mode 100644 lib/utils/arm_scmi/css/scmi/scmi_common.c
create mode 100644 lib/utils/arm_scmi/css/scmi/scmi_pwr_dmn_proto.c
create mode 100644 lib/utils/arm_scmi/css/scmi/scmi_sys_pwr_proto.c
create mode 100644 lib/utils/arm_scmi/css/scp/css_pm_scmi.c
create mode 100644 lib/utils/arm_scmi/objects.mk
create mode 100644 lib/utils/cci/bus-cci.c
create mode 100644 lib/utils/cci/objects.mk
create mode 100644 lib/utils/psci/Kconfig
create mode 100644 lib/utils/psci/objects.mk
create mode 100644 lib/utils/psci/psci_common.c
create mode 100644 lib/utils/psci/psci_main.c
create mode 100644 lib/utils/psci/psci_off.c
create mode 100644 lib/utils/psci/psci_on.c
create mode 100644 lib/utils/psci/psci_private.h
create mode 100644 lib/utils/psci/psci_setup.c
create mode 100644 lib/utils/psci/psci_suspend.c
create mode 100644 lib/utils/psci/spacemit/plat/k1x/underly_implement.c
create mode 100644 lib/utils/psci/spacemit/plat/plat_pm.c
create mode 100644 lib/utils/psci/spacemit/plat/underly_implement.h
create mode 100644 lib/utils/psci/spacemit/spacemit_topology.c
create mode 100644 platform/generic/configs/k1-x_fpga_1x4_defconfig
create mode 100644 platform/generic/configs/k1-x_fpga_2x2_defconfig
create mode 100644 platform/generic/configs/k1-x_fpga_defconfig
create mode 100644 platform/generic/configs/k1_defconfig
create mode 100644 platform/generic/include/spacemit/k1x/core_common.h
create mode 100644 platform/generic/include/spacemit/k1x/k1x_evb.h
create mode 100644 platform/generic/include/spacemit/k1x/k1x_fpga.h
create mode 100644 platform/generic/include/spacemit/spacemit_config.h
create mode 100755 platform/generic/spacemit/fw_dynamic.its
create mode 100644 platform/generic/spacemit/objects.mk
create mode 100644 platform/generic/spacemit/spacemit_k1.c
diff --git a/Makefile b/Makefile
index 730dbd9..468f8a3 100644
--- a/Makefile
+++ b/Makefile
@@ -114,6 +114,7 @@ endif
CPP = $(CC) -E
AS = $(CC)
DTC = dtc
+MKIMAGE = mkimage
ifneq ($(shell $(CC) --version 2>&1 | head -n 1 | grep clang),)
CC_IS_CLANG = y
@@ -245,6 +246,9 @@ ifdef PLATFORM
libsbiutils-objs-path-y=$(foreach obj,$(libsbiutils-objs-y),$(platform_build_dir)/lib/utils/$(obj))
platform-objs-path-y=$(foreach obj,$(platform-objs-y),$(platform_build_dir)/$(obj))
firmware-bins-path-y=$(foreach bin,$(firmware-bins-y),$(platform_build_dir)/firmware/$(bin))
+firmware-itb-path-y=$(foreach its,$(firmware-its-y),$(platform_build_dir)/firmware/$(basename $(notdir $(its))).itb)
+platform_build_itb_dir=$(patsubst %/,%,$(dir $(firstword $(firmware-itb-path-y))))
+platform_src_its_dir=$(patsubst %/,%,$(platform_src_dir)/$(dir $(firstword $(firmware-its-y))))
endif
firmware-elfs-path-y=$(firmware-bins-path-y:.bin=.elf)
firmware-objs-path-y=$(firmware-bins-path-y:.bin=.o)
@@ -342,7 +346,7 @@ CFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls -mstrict-align
ifeq ($(CC_SUPPORT_SAVE_RESTORE),y)
CFLAGS += -mno-save-restore
endif
-CFLAGS += -mabi=$(PLATFORM_RISCV_ABI) -march=$(PLATFORM_RISCV_ISA)
+CFLAGS += -mabi=$(PLATFORM_RISCV_ABI) -march=$(PLATFORM_RISCV_ISA)_zicbom
CFLAGS += -mcmodel=$(PLATFORM_RISCV_CODE_MODEL)
CFLAGS += $(RELAX_FLAG)
CFLAGS += $(GENFLAGS)
@@ -360,7 +364,7 @@ ASFLAGS += -fno-omit-frame-pointer -fno-optimize-sibling-calls -mstrict-align
ifeq ($(CC_SUPPORT_SAVE_RESTORE),y)
ASFLAGS += -mno-save-restore
endif
-ASFLAGS += -mabi=$(PLATFORM_RISCV_ABI) -march=$(PLATFORM_RISCV_ISA)
+ASFLAGS += -mabi=$(PLATFORM_RISCV_ABI) -march=$(PLATFORM_RISCV_ISA)_zicbom
ASFLAGS += -mcmodel=$(PLATFORM_RISCV_CODE_MODEL)
ASFLAGS += $(RELAX_FLAG)
ifneq ($(CC_IS_CLANG),y)
@@ -468,12 +472,16 @@ compile_carray = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
compile_gen_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
echo " GEN-DEP $(subst $(build_dir)/,,$(1))"; \
echo "$(1:.dep=$(2)): $(3)" >> $(1)
+compile_itb = \
+ $(CMD_PREFIX)echo " ITB $(subst $(build_dir)/,,$(1))"; \
+ $(MKIMAGE) -f $(2) -r $(1)
targets-y = $(build_dir)/lib/libsbi.a
ifdef PLATFORM
targets-y += $(platform_build_dir)/lib/libplatsbi.a
endif
targets-y += $(firmware-bins-path-y)
+targets-y += $(firmware-itb-path-y)
# The default "make all" rule
.PHONY: all
@@ -579,6 +587,11 @@ $(platform_build_dir)/%.dep: $(src_dir)/%.S $(KCONFIG_CONFIG)
$(platform_build_dir)/%.o: $(src_dir)/%.S
$(call compile_as,$@,$<)
+# Rules for fit image sources
+$(platform_build_itb_dir)/%.itb: $(platform_src_its_dir)/%.its $(firmware-bins-path-y)
+ $(call copy_file,$(dir $@)/$(notdir $<),$<)
+ $(call compile_itb,$@,$(basename $@).its)
+
# Rule for "make docs"
$(build_dir)/docs/latex/refman.pdf: $(build_dir)/docs/latex/refman.tex
$(CMD_PREFIX)mkdir -p $(build_dir)/docs
diff --git a/firmware/fw_base.ldS b/firmware/fw_base.ldS
index 3d68484..e214f9d 100644
--- a/firmware/fw_base.ldS
+++ b/firmware/fw_base.ldS
@@ -96,6 +96,11 @@
PROVIDE(_bss_end = .);
}
+ /DISCARD/ : {
+ *(.eh_frame*)
+ *(.debug*)
+ }
+
/* End of the read-write data sections */
. = ALIGN(0x1000); /* Need this to create proper sections */
diff --git a/include/sbi/riscv_encoding.h b/include/sbi/riscv_encoding.h
index 4ebed97..54e09d4 100644
--- a/include/sbi/riscv_encoding.h
+++ b/include/sbi/riscv_encoding.h
@@ -708,6 +708,8 @@
#define CSR_MVIPH 0x319
#define CSR_MIPH 0x354
+#define CSR_TCMCFG 0x5DB
+
/* ===== Trap/Exception Causes ===== */
#define CAUSE_MISALIGNED_FETCH 0x0
diff --git a/include/sbi/sbi_ecall_interface.h b/include/sbi/sbi_ecall_interface.h
index 1fe469e..f29c22a 100644
--- a/include/sbi/sbi_ecall_interface.h
+++ b/include/sbi/sbi_ecall_interface.h
@@ -42,6 +42,10 @@
#define SBI_EXT_BASE_GET_MARCHID 0x5
#define SBI_EXT_BASE_GET_MIMPID 0x6
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1PRO) || defined(CONFIG_PLATFORM_SPACEMIT_K1X)
+#define SBI_EXT_BASE_FLUSH_CACHE_ALL 0x7
+#endif
+
/* SBI function IDs for TIME extension*/
#define SBI_EXT_TIME_SET_TIMER 0x0
diff --git a/include/sbi/sbi_hsm.h b/include/sbi/sbi_hsm.h
index 4b5601b..066456c 100644
--- a/include/sbi/sbi_hsm.h
+++ b/include/sbi/sbi_hsm.h
@@ -74,10 +74,16 @@ bool sbi_hsm_hart_change_state(struct sbi_scratch *scratch, long oldstate,
long newstate);
int __sbi_hsm_hart_get_state(u32 hartid);
int sbi_hsm_hart_get_state(const struct sbi_domain *dom, u32 hartid);
+
+#ifdef CONFIG_ARM_PSCI_SUPPORT
+int __sbi_hsm_hart_get_psci_state(u32 hartid);
+int sbi_hsm_hart_get_psci_state(const struct sbi_domain *dom, u32 hartid);
+#endif
+
int sbi_hsm_hart_interruptible_mask(const struct sbi_domain *dom,
ulong hbase, ulong *out_hmask);
void __sbi_hsm_suspend_non_ret_save(struct sbi_scratch *scratch);
void __noreturn sbi_hsm_hart_start_finish(struct sbi_scratch *scratch,
- u32 hartid);
+ u32 hartid, bool cool_boot);
#endif
diff --git a/include/sbi_utils/cache/cacheflush.h b/include/sbi_utils/cache/cacheflush.h
new file mode 100644
index 0000000..c3e3532
--- /dev/null
+++ b/include/sbi_utils/cache/cacheflush.h
@@ -0,0 +1,192 @@
+#ifndef __CACHE_FLUSH__H__
+#define __CACHE_FLUSH__H__
+
+#include <sbi/sbi_types.h>
+#include <sbi/riscv_io.h>
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi_utils/psci/psci.h>
+#include <spacemit/spacemit_config.h>
+
+#define __ALWAYS_STATIC_INLINE __attribute__((always_inline)) static inline
+
+/**
+ \brief Clear Dcache by addr
+ \details Clear Dcache by addr.
+ \param [in] addr operate addr
+ */
+__ALWAYS_STATIC_INLINE void __DCACHE_CPA(uintptr_t addr)
+{
+ uintptr_t __v = addr;
+ asm volatile ("cbo.clean" " 0(%0)" : : "rK"(__v) : "memory");
+}
+
+/**
+ \brief Invalid Dcache by addr
+ \details Invalid Dcache by addr.
+ \param [in] addr operate addr
+ */
+__ALWAYS_STATIC_INLINE void __DCACHE_IPA(uintptr_t addr)
+{
+ uintptr_t __v = addr;
+ asm volatile ("cbo.inval" " 0(%0)" : : "rK"(__v) : "memory");
+}
+
+/**
+ \brief Clear & Invalid Dcache by addr
+ \details Clear & Invalid Dcache by addr.
+ \param [in] addr operate addr
+ */
+__ALWAYS_STATIC_INLINE void __DCACHE_CIPA(uintptr_t addr)
+{
+ uintptr_t __v = addr;
+ asm volatile ("cbo.flush" " 0(%0)" : : "rK"(__v) : "memory");
+}
+
+/**
+ \brief Get MSTATUS
+ \details Returns the content of the MSTATUS Register.
+ \return MSTATUS Register value
+ */
+__ALWAYS_STATIC_INLINE uintptr_t __get_CurrentSP(void)
+{
+ uintptr_t result;
+
+ asm volatile("move %0, sp" : "=r"(result));
+
+ return (result);
+}
+
+__ALWAYS_STATIC_INLINE uintptr_t __get_Supervisor_isr(void)
+{
+ uintptr_t result;
+
+ asm volatile("csrr %0, mip" : "=r"(result));
+
+ return (result & 0x222);
+}
+/**
+ \brief D-Cache Clean by address
+ \details Cleans D-Cache for the given address
+ \param[in] addr address (aligned to 32-byte boundary)
+ \param[in] dsize size of memory block (in number of bytes)
+*/
+static inline void csi_dcache_clean_range (uintptr_t addr, unsigned int dsize)
+{
+ int op_size = dsize + addr % CACHE_LINE_SIZE;
+ uintptr_t op_addr = addr & CACHE_INV_ADDR_Msk;
+
+ asm volatile("fence rw, rw");
+
+ while (op_size > 0) {
+ __DCACHE_CPA(op_addr);
+ op_addr += CACHE_LINE_SIZE;
+ op_size -= CACHE_LINE_SIZE;
+ }
+
+ asm volatile("fence rw, rw");
+ asm volatile("fence.i");
+}
+
+/**
+ \brief D-Cache Clean and Invalidate by address
+ \details Cleans and invalidates D_Cache for the given address
+ \param[in] addr address (aligned to 32-byte boundary)
+ \param[in] dsize size of memory block (aligned to 16-byte boundary)
+*/
+static inline void csi_dcache_clean_invalid_range (uintptr_t addr, unsigned int dsize)
+{
+ int op_size = dsize + addr % CACHE_LINE_SIZE;
+ uintptr_t op_addr = addr & CACHE_INV_ADDR_Msk;
+
+ asm volatile("fence rw, rw");
+
+ while (op_size > 0) {
+ __DCACHE_CIPA(op_addr);
+ op_addr += CACHE_LINE_SIZE;
+ op_size -= CACHE_LINE_SIZE;
+ }
+
+ asm volatile("fence rw, rw");
+ asm volatile("fence.i");
+}
+
+/**
+ \brief D-Cache Invalidate by address
+ \details Invalidates D-Cache for the given address
+ \param[in] addr address (aligned to 32-byte boundary)
+ \param[in] dsize size of memory block (in number of bytes)
+*/
+static inline void csi_dcache_invalid_range (uintptr_t addr, unsigned int dsize)
+{
+ int op_size = dsize + addr % CACHE_LINE_SIZE;
+ uintptr_t op_addr = addr & CACHE_INV_ADDR_Msk;
+
+ asm volatile("fence rw, rw");
+
+ while (op_size > 0) {
+ __DCACHE_IPA(op_addr);
+ op_addr += CACHE_LINE_SIZE;
+ op_size -= CACHE_LINE_SIZE;
+ }
+
+ asm volatile("fence rw, rw");
+ asm volatile("fence.i");
+}
+
+static inline void csi_enable_dcache(void)
+{
+ csr_set(CSR_MSETUP, 0x10073);
+}
+
+static inline void csi_disable_data_preftch(void)
+{
+ csr_clear(CSR_MSETUP, 32);
+}
+
+static inline void csi_disable_dcache(void)
+{
+ csr_clear(CSR_MSETUP, 1);
+}
+
+static inline void csi_flush_dcache_all(void)
+{
+ asm volatile ("csrwi 0x7c2, 0x3");
+}
+
+static inline void csi_invalidate_dcache_all(void)
+{
+ asm volatile ("csrwi 0x7c2, 0x2");
+}
+
+static inline void __mdelay(void)
+{
+ unsigned long long i;
+
+ for (i = 0; i < 0xffffffff; ++i)
+ cpu_relax();
+}
+
+static inline void csi_flush_l2_cache(void)
+{
+ unsigned int hartid = current_hartid();
+
+ uintptr_t *cr =(MPIDR_AFFLVL1_VAL(hartid) == 0) ? (uintptr_t *)CLUSTER0_L2_CACHE_FLUSH_REG_BASE :
+ (uintptr_t *)CLUSTER1_L2_CACHE_FLUSH_REG_BASE;
+
+ /* flush l2 cache */
+ writel(readl(cr) | (1 << L2_CACHE_FLUSH_REQUEST_BIT_OFFSET), cr);
+ /* k1pro */
+ if (L2_CACHE_FLUSH_REQUEST_BIT_OFFSET == L2_CACHE_FLUSH_DONE_BIT_OFFSET)
+ while (readl(cr) & (1 << L2_CACHE_FLUSH_DONE_BIT_OFFSET));
+ else /* k1x */ {
+ /* clear the request */
+ while (1) {
+ if ((readl(cr) & (1 << L2_CACHE_FLUSH_DONE_BIT_OFFSET)) == 0)
+ break;
+ __mdelay();
+ }
+ writel(readl(cr) & ~(1 << L2_CACHE_FLUSH_REQUEST_BIT_OFFSET), cr);
+ }
+}
+#endif
diff --git a/include/sbi_utils/cci/cci.h b/include/sbi_utils/cci/cci.h
new file mode 100644
index 0000000..c5b8b57
--- /dev/null
+++ b/include/sbi_utils/cci/cci.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2015-2018, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef __CCI_H__
+#define __CCI_H__
+
+/* Function declarations */
+
+/*
+ * The ARM CCI driver needs the following:
+ * 1. Base address of the CCI product
+ * 2. An array of map between AMBA 4 master ids and ACE/ACE lite slave
+ * interfaces.
+ * 3. Size of the array.
+ *
+ * SLAVE_IF_UNUSED should be used in the map to represent no AMBA 4 master exists
+ * for that interface.
+ */
+void cci_init(uintptr_t base, const int *map, unsigned int num_cci_masters);
+
+void cci_enable_snoop_dvm_reqs(unsigned int master_id);
+void cci_disable_snoop_dvm_reqs(unsigned int master_id);
+
+#endif /* CCI_H */
diff --git a/include/sbi_utils/irqchip/fdt_irqchip_plic.h b/include/sbi_utils/irqchip/fdt_irqchip_plic.h
index df645dd..b892b0b 100644
--- a/include/sbi_utils/irqchip/fdt_irqchip_plic.h
+++ b/include/sbi_utils/irqchip/fdt_irqchip_plic.h
@@ -28,6 +28,8 @@ void fdt_plic_context_save(bool smode, u32 *enable, u32 *threshold, u32 num);
void fdt_plic_context_restore(bool smode, const u32 *enable, u32 threshold,
u32 num);
+void fdt_plic_context_exit(void);
+
void thead_plic_restore(void);
#endif
diff --git a/include/sbi_utils/psci/drivers/arm/css/css_mhu_doorbell.h b/include/sbi_utils/psci/drivers/arm/css/css_mhu_doorbell.h
new file mode 100644
index 0000000..e49c7e2
--- /dev/null
+++ b/include/sbi_utils/psci/drivers/arm/css/css_mhu_doorbell.h
@@ -0,0 +1,14 @@
+/*
+ * Copyright (c) 2014-2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef CSS_MHU_DOORBELL_H
+#define CSS_MHU_DOORBELL_H
+
+#include <sbi_utils/psci/drivers/arm/css/scmi.h>
+
+void mhu_ring_doorbell(struct scmi_channel_plat_info *plat_info);
+
+#endif /* CSS_MHU_DOORBELL_H */
diff --git a/include/sbi_utils/psci/drivers/arm/css/css_scp.h b/include/sbi_utils/psci/drivers/arm/css/css_scp.h
new file mode 100644
index 0000000..f75eae6
--- /dev/null
+++ b/include/sbi_utils/psci/drivers/arm/css/css_scp.h
@@ -0,0 +1,10 @@
+#ifndef __CSS_SCP_H__
+#define __CSS_SCP_H__
+
+#include <sbi_utils/psci/psci.h>
+
+void css_scp_off(const struct psci_power_state *target_state);
+void css_scp_on(u_register_t mpidr);
+void css_scp_suspend(const struct psci_power_state *target_state);
+
+#endif
diff --git a/include/sbi_utils/psci/drivers/arm/css/scmi.h b/include/sbi_utils/psci/drivers/arm/css/scmi.h
new file mode 100644
index 0000000..1e8c370
--- /dev/null
+++ b/include/sbi_utils/psci/drivers/arm/css/scmi.h
@@ -0,0 +1,141 @@
+#ifndef __DRIVER_SCMI_H__
+#define __DRIVER_SCMI_H__
+
+#include <sbi_utils/psci/psci.h>
+#include <sbi/riscv_locks.h>
+#include <sbi/sbi_types.h>
+
+#define GET_SCMI_MAJOR_VER(ver) (((ver) >> 16) & 0xffff)
+#define GET_SCMI_MINOR_VER(ver) ((ver) & 0xffff)
+
+#define MAKE_SCMI_VERSION(maj, min) \
+ ((((maj) & 0xffff) << 16) | ((min) & 0xffff))
+
+/* Supported SCMI Protocol Versions */
+#define SCMI_AP_CORE_PROTO_VER MAKE_SCMI_VERSION(1, 0)
+#define SCMI_PWR_DMN_PROTO_VER MAKE_SCMI_VERSION(2, 0)
+#define SCMI_SYS_PWR_PROTO_VER MAKE_SCMI_VERSION(1, 0)
+
+/*
+ * Check that the driver's version is same or higher than the reported SCMI
+ * version. We accept lower major version numbers, as all affected protocols
+ * so far stay backwards compatible. This might need to be revisited in the
+ * future.
+ */
+#define is_scmi_version_compatible(drv, scmi) \
+ ((GET_SCMI_MAJOR_VER(drv) > GET_SCMI_MAJOR_VER(scmi)) || \
+ ((GET_SCMI_MAJOR_VER(drv) == GET_SCMI_MAJOR_VER(scmi)) && \
+ (GET_SCMI_MINOR_VER(drv) <= GET_SCMI_MINOR_VER(scmi))))
+
+/* Mandatory messages IDs for all SCMI protocols */
+#define SCMI_PROTO_VERSION_MSG 0x0
+#define SCMI_PROTO_ATTR_MSG 0x1
+#define SCMI_PROTO_MSG_ATTR_MSG 0x2
+
+/* SCMI power domain management protocol message IDs */
+#define SCMI_PWR_STATE_SET_MSG 0x4
+#define SCMI_PWR_STATE_GET_MSG 0x5
+
+/* SCMI system power management protocol message IDs */
+#define SCMI_SYS_PWR_STATE_SET_MSG 0x3
+#define SCMI_SYS_PWR_STATE_GET_MSG 0x4
+
+/* SCMI Protocol identifiers */
+#define SCMI_PWR_DMN_PROTO_ID 0x11
+#define SCMI_SYS_PWR_PROTO_ID 0x12
+
+/*
+ * Macros to describe the bit-fields of the `attribute` of system power domain
+ * protocol PROTOCOL_MSG_ATTRIBUTE message.
+ */
+#define SYS_PWR_ATTR_WARM_RESET_SHIFT 31
+#define SCMI_SYS_PWR_WARM_RESET_SUPPORTED (1U << SYS_PWR_ATTR_WARM_RESET_SHIFT)
+
+#define SYS_PWR_ATTR_SUSPEND_SHIFT 30
+#define SCMI_SYS_PWR_SUSPEND_SUPPORTED (1 << SYS_PWR_ATTR_SUSPEND_SHIFT)
+
+/*
+ * Macros to describe the bit-fields of the `flags` parameter of system power
+ * domain protocol SYSTEM_POWER_STATE_SET message.
+ */
+#define SYS_PWR_SET_GRACEFUL_REQ_SHIFT 0
+#define SCMI_SYS_PWR_GRACEFUL_REQ (1 << SYS_PWR_SET_GRACEFUL_REQ_SHIFT)
+#define SCMI_SYS_PWR_FORCEFUL_REQ (0 << SYS_PWR_SET_GRACEFUL_REQ_SHIFT)
+
+/*
+ * Macros to describe the `system_state` parameter of system power
+ * domain protocol SYSTEM_POWER_STATE_SET message.
+ */
+#define SCMI_SYS_PWR_SHUTDOWN 0x0
+#define SCMI_SYS_PWR_COLD_RESET 0x1
+#define SCMI_SYS_PWR_WARM_RESET 0x2
+#define SCMI_SYS_PWR_POWER_UP 0x3
+#define SCMI_SYS_PWR_SUSPEND 0x4
+
+/* SCMI Error code definitions */
+#define SCMI_E_QUEUED 1
+#define SCMI_E_SUCCESS 0
+#define SCMI_E_NOT_SUPPORTED -1
+#define SCMI_E_INVALID_PARAM -2
+#define SCMI_E_DENIED -3
+#define SCMI_E_NOT_FOUND -4
+#define SCMI_E_OUT_OF_RANGE -5
+#define SCMI_E_BUSY -6
+
+/*
+ * SCMI driver platform information. The details of the doorbell mechanism
+ * can be found in the SCMI specification.
+ */
+typedef struct scmi_channel_plat_info {
+ /* SCMI mailbox memory */
+ uintptr_t scmi_mbx_mem;
+ /* The door bell register address */
+ uintptr_t db_reg_addr;
+ /* The bit mask that need to be preserved when ringing doorbell */
+ uint32_t db_preserve_mask;
+ /* The bit mask that need to be set to ring doorbell */
+ uint32_t db_modify_mask;
+ /* The handler for ringing doorbell */
+ void (*ring_doorbell)(struct scmi_channel_plat_info *plat_info);
+ /* cookie is unused now. But added for future enhancements. */
+ void *cookie;
+} scmi_channel_plat_info_t;
+
+typedef spinlock_t scmi_lock_t;
+
+/*
+ * Structure to represent an SCMI channel.
+ */
+typedef struct scmi_channel {
+ scmi_channel_plat_info_t *info;
+ /* The lock for channel access */
+ scmi_lock_t *lock;
+ /* Indicate whether the channel is initialized */
+ int is_initialized;
+} scmi_channel_t;
+
+/* External Common API */
+void *scmi_init(scmi_channel_t *ch);
+/* API to override default PSCI callbacks for platforms that support SCMI. */
+const plat_psci_ops_t *css_scmi_override_pm_ops(plat_psci_ops_t *ops);
+
+/*
+ * Power domain protocol commands. Refer to the SCMI specification for more
+ * details on these commands.
+ */
+int scmi_pwr_state_set(void *p, uint32_t domain_id, uint32_t scmi_pwr_state);
+int scmi_pwr_state_get(void *p, uint32_t domain_id, uint32_t *scmi_pwr_state);
+
+int scmi_proto_version(void *p, uint32_t proto_id, uint32_t *version);
+int scmi_proto_msg_attr(void *p, uint32_t proto_id, uint32_t command_id,
+ uint32_t *attr);
+scmi_channel_plat_info_t *plat_css_get_scmi_info(unsigned int channel_id);
+
+/*
+ * System power management protocol commands. Refer SCMI specification for more
+ * details on these commands.
+ */
+int scmi_sys_pwr_state_set(void *p, uint32_t flags, uint32_t system_state);
+int scmi_sys_pwr_state_get(void *p, uint32_t *system_state);
+
+#endif
diff --git a/include/sbi_utils/psci/drivers/arm/css/scmi_private.h b/include/sbi_utils/psci/drivers/arm/css/scmi_private.h
new file mode 100644
index 0000000..7b246e5
--- /dev/null
+++ b/include/sbi_utils/psci/drivers/arm/css/scmi_private.h
@@ -0,0 +1,146 @@
+#ifndef __SCMI_PRIVATE_H__
+#define __SCMI_PRIVATE_H__
+
+#include <sbi/sbi_types.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_hart.h>
+#include <sbi_utils/psci/drivers/arm/css/scmi.h>
+
+/*
+ * SCMI power domain management protocol message and response lengths. It is
+ * calculated as sum of length in bytes of the message header (4) and payload
+ * area (the number of bytes of parameters or return values in the payload).
+ */
+#define SCMI_PROTO_VERSION_MSG_LEN 4
+#define SCMI_PROTO_VERSION_RESP_LEN 12
+
+#define SCMI_PROTO_MSG_ATTR_MSG_LEN 8
+#define SCMI_PROTO_MSG_ATTR_RESP_LEN 12
+
+#define SCMI_PWR_STATE_GET_MSG_LEN 8
+#define SCMI_PWR_STATE_GET_RESP_LEN 12
+
+/* SCMI power domain protocol `POWER_STATE_SET` message flags */
+#define SCMI_PWR_STATE_SET_FLAG_SYNC 0
+#define SCMI_PWR_STATE_SET_FLAG_ASYNC 1
+
+/* SCMI message header format bit field */
+#define SCMI_MSG_ID_SHIFT 0
+#define SCMI_MSG_ID_WIDTH 8
+#define SCMI_MSG_ID_MASK ((1 << SCMI_MSG_ID_WIDTH) - 1)
+
+#define SCMI_MSG_PROTO_ID_SHIFT 10
+#define SCMI_MSG_PROTO_ID_WIDTH 8
+#define SCMI_MSG_PROTO_ID_MASK ((1 << SCMI_MSG_PROTO_ID_WIDTH) - 1)
+
+#define SCMI_MSG_TOKEN_SHIFT 18
+#define SCMI_MSG_TOKEN_WIDTH 10
+#define SCMI_MSG_TOKEN_MASK ((1 << SCMI_MSG_TOKEN_WIDTH) - 1)
+
+#define SCMI_PWR_STATE_SET_MSG_LEN 16
+#define SCMI_PWR_STATE_SET_RESP_LEN 8
+
+#define SCMI_SYS_PWR_STATE_SET_MSG_LEN 12
+#define SCMI_SYS_PWR_STATE_SET_RESP_LEN 8
+
+#define SCMI_SYS_PWR_STATE_GET_MSG_LEN 4
+#define SCMI_SYS_PWR_STATE_GET_RESP_LEN 12
+
+/* SCMI mailbox flags */
+#define SCMI_FLAG_RESP_POLL 0
+#define SCMI_FLAG_RESP_INT 1
+
+/* Helper macros to copy arguments to the mailbox payload */
+#define SCMI_PAYLOAD_ARG1(payld_arr, arg1) \
+ *((uint32_t *)&payld_arr[0]) = arg1
+
+#define SCMI_PAYLOAD_ARG2(payld_arr, arg1, arg2) do { \
+ SCMI_PAYLOAD_ARG1(payld_arr, arg1); \
+ *((uint32_t *)&payld_arr[1]) = arg2; \
+ } while (0)
+
+#define SCMI_PAYLOAD_ARG3(payld_arr, arg1, arg2, arg3) do { \
+ SCMI_PAYLOAD_ARG2(payld_arr, arg1, arg2); \
+ *((uint32_t *)&payld_arr[2]) = arg3; \
+ } while (0)
+
+/* Helper macros to read return values from the mailbox payload */
+#define SCMI_PAYLOAD_RET_VAL1(payld_arr, val1) \
+ (val1) = *((uint32_t *)&payld_arr[0])
+
+#define SCMI_PAYLOAD_RET_VAL2(payld_arr, val1, val2) do { \
+ SCMI_PAYLOAD_RET_VAL1(payld_arr, val1); \
+ (val2) = *((uint32_t *)&payld_arr[1]); \
+ } while (0)
+
+#define SCMI_PAYLOAD_RET_VAL3(payld_arr, val1, val2, val3) do { \
+ SCMI_PAYLOAD_RET_VAL2(payld_arr, val1, val2); \
+ (val3) = *((uint32_t *)&payld_arr[2]); \
+ } while (0)
+
+#define SCMI_PAYLOAD_RET_VAL4(payld_arr, val1, val2, val3, val4) do { \
+ SCMI_PAYLOAD_RET_VAL3(payld_arr, val1, val2, val3); \
+ (val4) = *((uint32_t *)&payld_arr[3]); \
+ } while (0)
+
+/* Helper macro to get the token from a SCMI message header */
+#define SCMI_MSG_GET_TOKEN(_msg) \
+ (((_msg) >> SCMI_MSG_TOKEN_SHIFT) & SCMI_MSG_TOKEN_MASK)
+
+/* SCMI Channel Status bit fields */
+#define SCMI_CH_STATUS_RES0_MASK 0xFFFFFFFE
+#define SCMI_CH_STATUS_FREE_SHIFT 0
+#define SCMI_CH_STATUS_FREE_WIDTH 1
+#define SCMI_CH_STATUS_FREE_MASK ((1 << SCMI_CH_STATUS_FREE_WIDTH) - 1)
+
+/* Helper macros to check and write the channel status */
+#define SCMI_IS_CHANNEL_FREE(status) \
+ (!!(((status) >> SCMI_CH_STATUS_FREE_SHIFT) & SCMI_CH_STATUS_FREE_MASK))
+
+#define SCMI_MARK_CHANNEL_BUSY(status) do { \
+ if (!SCMI_IS_CHANNEL_FREE(status)) \
+ sbi_hart_hang(); \
+ (status) &= ~(SCMI_CH_STATUS_FREE_MASK << \
+ SCMI_CH_STATUS_FREE_SHIFT); \
+ } while (0)
+
+/*
+ * Helper macro to create an SCMI message header given protocol, message id
+ * and token.
+ */
+#define SCMI_MSG_CREATE(_protocol, _msg_id, _token) \
+ ((((_protocol) & SCMI_MSG_PROTO_ID_MASK) << SCMI_MSG_PROTO_ID_SHIFT) | \
+ (((_msg_id) & SCMI_MSG_ID_MASK) << SCMI_MSG_ID_SHIFT) | \
+ (((_token) & SCMI_MSG_TOKEN_MASK) << SCMI_MSG_TOKEN_SHIFT))
+
+#define MAILBOX_MEM_PAYLOAD_SIZE (0x80)
+#define MAILBOX_SECURE_PSCI_CHANNEL (0x1)
+
+/*
+ * Private data structure for representing the mailbox memory layout. Refer
+ * the SCMI specification for more details.
+ */
+typedef struct mailbox_mem {
+ uint32_t res_a; /* Reserved */
+ volatile uint32_t status;
+ uint64_t res_b; /* Reserved */
+ uint32_t flags;
+ volatile uint32_t len;
+ volatile uint32_t msg_header;
+ uint32_t payload[];
+} mailbox_mem_t;
+
+static inline void validate_scmi_channel(scmi_channel_t *ch)
+{
+ if (!ch || !ch->is_initialized)
+ sbi_hart_hang();
+
+ if (!ch->info || !ch->info->scmi_mbx_mem)
+ sbi_hart_hang();
+}
+
+void scmi_send_sync_command(scmi_channel_t *ch);
+void scmi_get_channel(scmi_channel_t *ch);
+void scmi_put_channel(scmi_channel_t *ch);
+
+#endif
diff --git a/include/sbi_utils/psci/plat/arm/board/spacemit/include/platform_def.h b/include/sbi_utils/psci/plat/arm/board/spacemit/include/platform_def.h
new file mode 100644
index 0000000..6287c82
--- /dev/null
+++ b/include/sbi_utils/psci/plat/arm/board/spacemit/include/platform_def.h
@@ -0,0 +1,10 @@
+#ifndef __PLATFORM_DEFINE_H__
+#define __PLATFORM_DEFINE_H__
+
+/* System power domain level */
+#define CSS_SYSTEM_PWR_DMN_LVL ARM_PWR_LVL2
+
+/* Number of SCMI channels on the platform */
+#define PLAT_ARM_SCMI_CHANNEL_COUNT 1U
+
+#endif
diff --git a/include/sbi_utils/psci/plat/arm/common/arm_def.h b/include/sbi_utils/psci/plat/arm/common/arm_def.h
new file mode 100644
index 0000000..3cedcff
--- /dev/null
+++ b/include/sbi_utils/psci/plat/arm/common/arm_def.h
@@ -0,0 +1,19 @@
+#ifndef __ARM_DEF_H__
+#define __ARM_DEF_H__
+
+#define MPIDR_AFFLVL0 0ULL
+#define MPIDR_AFFLVL1 1ULL
+#define MPIDR_AFFLVL2 2ULL
+#define MPIDR_AFFLVL3 3ULL
+
+/*
+ * Macros mapping the MPIDR Affinity levels to ARM Platform Power levels. The
+ * power levels have a 1:1 mapping with the MPIDR affinity levels.
+ */
+#define ARM_PWR_LVL0 MPIDR_AFFLVL0
+#define ARM_PWR_LVL1 MPIDR_AFFLVL1
+#define ARM_PWR_LVL2 MPIDR_AFFLVL2
+#define ARM_PWR_LVL3 MPIDR_AFFLVL3
+
+
+#endif
diff --git a/include/sbi_utils/psci/plat/arm/common/plat_arm.h b/include/sbi_utils/psci/plat/arm/common/plat_arm.h
new file mode 100644
index 0000000..fb7bf13
--- /dev/null
+++ b/include/sbi_utils/psci/plat/arm/common/plat_arm.h
@@ -0,0 +1,21 @@
+#ifndef __PLAT_ARM_H__
+#define __PLAT_ARM_H__
+
+#include <sbi_utils/psci/psci.h>
+#include <sbi/riscv_locks.h>
+#include <sbi_utils/psci/plat/arm/common/plat_arm.h>
+
+#define ARM_SCMI_INSTANTIATE_LOCK spinlock_t arm_scmi_lock
+
+#define ARM_SCMI_LOCK_GET_INSTANCE (&arm_scmi_lock)
+
+extern plat_psci_ops_t plat_arm_psci_pm_ops;
+
+const plat_psci_ops_t *plat_arm_psci_override_pm_ops(plat_psci_ops_t *ops);
+
+void plat_arm_pwrc_setup(void);
+
+int arm_validate_power_state(unsigned int power_state,
+ psci_power_state_t *req_state);
+
+#endif
diff --git a/include/sbi_utils/psci/plat/arm/css/common/css_pm.h b/include/sbi_utils/psci/plat/arm/css/common/css_pm.h
new file mode 100644
index 0000000..78d7f37
--- /dev/null
+++ b/include/sbi_utils/psci/plat/arm/css/common/css_pm.h
@@ -0,0 +1,36 @@
+#ifndef __CSS_ARM_H__
+#define __CSS_ARM_H__
+
+#include <sbi/sbi_types.h>
+#include <sbi_utils/psci/psci.h>
+#include <sbi_utils/psci/plat/arm/common/arm_def.h>
+#include <sbi_utils/psci/plat/arm/board/spacemit/include/platform_def.h>
+#include <spacemit/spacemit_config.h>
+
+#define SCMI_DOMAIN_ID_MASK 0xFFFFU
+#define SCMI_CHANNEL_ID_MASK 0xFFFFU
+#define SCMI_CHANNEL_ID_SHIFT 16U
+
+#define SET_SCMI_CHANNEL_ID(n) (((n) & SCMI_CHANNEL_ID_MASK) << \
+ SCMI_CHANNEL_ID_SHIFT)
+#define SET_SCMI_DOMAIN_ID(n) ((n) & SCMI_DOMAIN_ID_MASK)
+#define GET_SCMI_CHANNEL_ID(n) (((n) >> SCMI_CHANNEL_ID_SHIFT) & \
+ SCMI_CHANNEL_ID_MASK)
+#define GET_SCMI_DOMAIN_ID(n) ((n) & SCMI_DOMAIN_ID_MASK)
+
+/* Macros to read the CSS power domain state */
+#define CSS_CORE_PWR_STATE(state) (state)->pwr_domain_state[ARM_PWR_LVL0]
+#define CSS_CLUSTER_PWR_STATE(state) (state)->pwr_domain_state[ARM_PWR_LVL1]
+
+static inline unsigned int css_system_pwr_state(const psci_power_state_t *state)
+{
+#if (PLAT_MAX_PWR_LVL == CSS_SYSTEM_PWR_DMN_LVL)
+ return state->pwr_domain_state[CSS_SYSTEM_PWR_DMN_LVL];
+#else
+ return 0;
+#endif
+}
+
+extern uint32_t plat_css_core_pos_to_scmi_dmn_id_map[PLATFORM_CLUSTER_COUNT][PLATFORM_CORE_COUNT];
+
+#endif
diff --git a/include/sbi_utils/psci/plat/common/platform.h b/include/sbi_utils/psci/plat/common/platform.h
new file mode 100644
index 0000000..7c53612
--- /dev/null
+++ b/include/sbi_utils/psci/plat/common/platform.h
@@ -0,0 +1,13 @@
+#ifndef __PSCI_PLAT_COMMON_H__
+#define __PSCI_PLAT_COMMON_H__
+
+#include <sbi/sbi_types.h>
+#include <sbi_utils/psci/psci.h>
+
+unsigned char *plat_get_power_domain_tree_desc(void);
+
+int plat_setup_psci_ops(uintptr_t sec_entrypoint,
+ const struct plat_psci_ops **psci_ops);
+int plat_core_pos_by_mpidr(u_register_t mpidr);
+
+#endif
diff --git a/include/sbi_utils/psci/psci.h b/include/sbi_utils/psci/psci.h
new file mode 100644
index 0000000..c76fd25
--- /dev/null
+++ b/include/sbi_utils/psci/psci.h
@@ -0,0 +1,223 @@
+#ifndef __PSCI_H__
+#define __PSCI_H__
+
+#include <sbi/sbi_types.h>
+#include <spacemit/spacemit_config.h>
+
+#define MPIDR_AFFLVL0_VAL(mpidr) \
+ (((mpidr) >> MPIDR_AFF0_SHIFT) & MPIDR_AFFINITY0_MASK)
+#define MPIDR_AFFLVL1_VAL(mpidr) \
+ (((mpidr) >> MPIDR_AFF1_SHIFT) & MPIDR_AFFINITY1_MASK)
+/*
+ * Macros for local power states in ARM platforms encoded by State-ID field
+ * within the power-state parameter.
+ */
+/* Local power state for power domains in Run state. */
+#define ARM_LOCAL_STATE_RUN 0U
+/* Local power state for retention. Valid only for CPU power domains */
+#define ARM_LOCAL_STATE_RET 1U
+/* Local power state for OFF/power-down. Valid for CPU and cluster power
+ domains */
+#define ARM_LOCAL_STATE_OFF 2U
+
+/*
+ * This macro defines the deepest retention state possible. A higher state
+ * id will represent an invalid or a power down state.
+ */
+#define PLAT_MAX_RET_STATE ARM_LOCAL_STATE_RET
+
+/*
+ * This macro defines the deepest power down states possible. Any state ID
+ * higher than this is invalid.
+ */
+#define PLAT_MAX_OFF_STATE ARM_LOCAL_STATE_OFF
+
+/*
+ * Type for representing the local power state at a particular level.
+ */
+typedef unsigned char plat_local_state_t;
+
+/* The local state macro used to represent RUN state. */
+#define PSCI_LOCAL_STATE_RUN 0U
+
+typedef unsigned long u_register_t;
+
+/*******************************************************************************
+ * PSCI error codes
+ ******************************************************************************/
+#define PSCI_E_SUCCESS 0
+#define PSCI_E_NOT_SUPPORTED -1
+#define PSCI_E_INVALID_PARAMS -2
+#define PSCI_E_DENIED -3
+#define PSCI_E_ALREADY_ON -4
+#define PSCI_E_ON_PENDING -5
+#define PSCI_E_INTERN_FAIL -6
+#define PSCI_E_NOT_PRESENT -7
+#define PSCI_E_DISABLED -8
+#define PSCI_E_INVALID_ADDRESS -9
+
+#define PSCI_INVALID_MPIDR ~((u_register_t)0)
+
+
+/*
+ * These are the states reported by the PSCI_AFFINITY_INFO API for the specified
+ * CPU. The definitions of these states can be found in Section 5.7.1 in the
+ * PSCI specification (ARM DEN 0022C).
+ */
+typedef enum {
+ AFF_STATE_ON = 0U,
+ AFF_STATE_OFF = 1U,
+ AFF_STATE_ON_PENDING = 2U
+} aff_info_state_t;
+
+/*******************************************************************************
+ * Structure used to store per-cpu information relevant to the PSCI service.
+ * It is populated in the per-cpu data array. In return we get a guarantee that
+ * this information will not reside on a cache line shared with another cpu.
+ ******************************************************************************/
+typedef struct psci_cpu_data {
+ /* State as seen by PSCI Affinity Info API */
+ aff_info_state_t aff_info_state;
+
+ /*
+ * Highest power level which takes part in a power management
+ * operation.
+ */
+ unsigned int target_pwrlvl;
+
+ /* The local power state of this CPU */
+ plat_local_state_t local_state;
+} psci_cpu_data_t;
+
+/*
+ * Macro to represent invalid affinity level within PSCI.
+ */
+#define PSCI_INVALID_PWR_LVL (PLAT_MAX_PWR_LVL + 1U)
+
+/*
+ * These are the power states reported by PSCI_NODE_HW_STATE API for the
+ * specified CPU. The definitions of these states can be found in Section 5.15.3
+ * of PSCI specification (ARM DEN 0022C).
+ */
+#define HW_ON 0
+#define HW_OFF 1
+#define HW_STANDBY 2
+
+#define PSTATE_ID_SHIFT (0U)
+#define PSTATE_VALID_MASK (0xFCFE0000U)
+#define PSTATE_TYPE_SHIFT (16U)
+#define PSTATE_PWR_LVL_SHIFT (24U)
+#define PSTATE_ID_MASK (0xffffU)
+#define PSTATE_PWR_LVL_MASK (0x3U)
+
+#define psci_get_pstate_pwrlvl(pstate) (((pstate) >> PSTATE_PWR_LVL_SHIFT) & \
+ PSTATE_PWR_LVL_MASK)
+#define psci_make_powerstate(state_id, type, pwrlvl) \
+ (((state_id) & PSTATE_ID_MASK) << PSTATE_ID_SHIFT) |\
+ (((type) & PSTATE_TYPE_MASK) << PSTATE_TYPE_SHIFT) |\
+ (((pwrlvl) & PSTATE_PWR_LVL_MASK) << PSTATE_PWR_LVL_SHIFT)
+
+#define PSTATE_TYPE_STANDBY (0x0U)
+#define PSTATE_TYPE_POWERDOWN (0x1U)
+#define PSTATE_TYPE_MASK (0x1U)
+
+/* RISCV suspend power state */
+#define RSTATE_TYPE_SHIFT (31U)
+#define RSTATE_PWR_LVL_SHIFT (24U)
+#define RSTATE_COMMON_SHIFT (28U)
+
+/*****************************************************************************
+ * This data structure defines the representation of the power state parameter
+ * for its exchange between the generic PSCI code and the platform port. For
+ * example, it is used by the platform port to specify the requested power
+ * states during a power management operation. It is used by the generic code to
+ * inform the platform about the target power states that each level should
+ * enter.
+ ****************************************************************************/
+typedef struct psci_power_state {
+ /*
+ * The pwr_domain_state[] stores the local power state at each level
+ * for the CPU.
+ */
+ plat_local_state_t pwr_domain_state[PLAT_MAX_PWR_LVL + 1U ];
+} psci_power_state_t;
+
+/*
+ * Function to test whether the plat_local_state is RUN state
+ */
+static inline int is_local_state_run(unsigned int plat_local_state)
+{
+ return (plat_local_state == PSCI_LOCAL_STATE_RUN) ? 1 : 0;
+}
+
+/*
+ * Function to test whether the plat_local_state is OFF state
+ */
+static inline int is_local_state_off(unsigned int plat_local_state)
+{
+ return ((plat_local_state > PLAT_MAX_RET_STATE) &&
+ (plat_local_state <= PLAT_MAX_OFF_STATE)) ? 1 : 0;
+}
+
+/* Power state helper functions */
+
+static inline unsigned int psci_check_power_state(unsigned int power_state)
+{
+ return ((power_state) & PSTATE_VALID_MASK);
+}
+
+static inline unsigned int psci_get_pstate_id(unsigned int power_state)
+{
+ return ((power_state) >> PSTATE_ID_SHIFT) & PSTATE_ID_MASK;
+}
+
+static inline unsigned int psci_get_pstate_type(unsigned int power_state)
+{
+ return ((power_state) >> PSTATE_TYPE_SHIFT) & PSTATE_TYPE_MASK;
+}
+
+/*******************************************************************************
+ * Structure populated by platform specific code to export routines which
+ * perform common low level power management functions
+ ******************************************************************************/
+typedef struct plat_psci_ops {
+ void (*cpu_standby)(plat_local_state_t cpu_state);
+ int (*pwr_domain_on)(u_register_t mpidr);
+ void (*pwr_domain_off)(const psci_power_state_t *target_state);
+ int (*pwr_domain_off_early)(const psci_power_state_t *target_state);
+ void (*pwr_domain_suspend_pwrdown_early)(
+ const psci_power_state_t *target_state);
+ void (*pwr_domain_suspend)(const psci_power_state_t *target_state);
+ void (*pwr_domain_on_finish)(const psci_power_state_t *target_state);
+ void (*pwr_domain_on_finish_late)(
+ const psci_power_state_t *target_state);
+ void (*pwr_domain_suspend_finish)(
+ const psci_power_state_t *target_state);
+ void (*pwr_domain_pwr_down_wfi)(
+ const psci_power_state_t *target_state);
+ void (*system_off)(void);
+ void (*system_reset)(void);
+ int (*validate_power_state)(unsigned int power_state,
+ psci_power_state_t *req_state);
+ int (*validate_ns_entrypoint)(uintptr_t ns_entrypoint);
+ void (*get_sys_suspend_power_state)(
+ psci_power_state_t *req_state);
+ int (*get_pwr_lvl_state_idx)(plat_local_state_t pwr_domain_state,
+ int pwrlvl);
+ int (*translate_power_state_by_mpidr)(u_register_t mpidr,
+ unsigned int power_state,
+ psci_power_state_t *output_state);
+ int (*get_node_hw_state)(u_register_t mpidr, unsigned int power_level);
+ int (*mem_protect_chk)(uintptr_t base, u_register_t length);
+ int (*read_mem_protect)(int *val);
+ int (*write_mem_protect)(int val);
+ int (*system_reset2)(int is_vendor,
+ int reset_type, u_register_t cookie);
+} plat_psci_ops_t;
+
+int psci_cpu_on(u_register_t target_cpu, uintptr_t entrypoint);
+int psci_cpu_off(void);
+int psci_affinity_info(u_register_t target_affinity, unsigned int lowest_affinity_level);
+int psci_cpu_suspend(unsigned int power_state, uintptr_t entrypoint, u_register_t context_id);
+
+#endif
diff --git a/include/sbi_utils/psci/psci_lib.h b/include/sbi_utils/psci/psci_lib.h
new file mode 100644
index 0000000..15576b7
--- /dev/null
+++ b/include/sbi_utils/psci/psci_lib.h
@@ -0,0 +1,8 @@
+#ifndef __PSCI_LIB_H__
+#define __PSCI_LIB_H__
+
+int psci_setup(void);
+void psci_print_power_domain_map(void);
+void psci_warmboot_entrypoint(void);
+
+#endif
diff --git a/lib/sbi/sbi_ecall_base.c b/lib/sbi/sbi_ecall_base.c
index 74f05eb..b02bcc1 100644
--- a/lib/sbi/sbi_ecall_base.c
+++ b/lib/sbi/sbi_ecall_base.c
@@ -14,6 +14,9 @@
#include <sbi/sbi_trap.h>
#include <sbi/sbi_version.h>
#include <sbi/riscv_asm.h>
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1PRO) || defined(CONFIG_PLATFORM_SPACEMIT_K1X)
+#include <sbi_utils/cache/cacheflush.h>
+#endif
static int sbi_ecall_base_probe(unsigned long extid, unsigned long *out_val)
{
@@ -62,6 +65,13 @@ static int sbi_ecall_base_handler(unsigned long extid, unsigned long funcid,
case SBI_EXT_BASE_GET_MIMPID:
*out_val = csr_read(CSR_MIMPID);
break;
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1PRO) || defined(CONFIG_PLATFORM_SPACEMIT_K1X)
+ case SBI_EXT_BASE_FLUSH_CACHE_ALL:
+ csi_flush_dcache_all();
+ /* there has no need to flush l2 cache here */
+ /* csi_flush_l2_cache(); */
+ break;
+#endif
case SBI_EXT_BASE_PROBE_EXT:
ret = sbi_ecall_base_probe(regs->a0, out_val);
break;
diff --git a/lib/sbi/sbi_ecall_hsm.c b/lib/sbi/sbi_ecall_hsm.c
index 20705c3..ed8c940 100644
--- a/lib/sbi/sbi_ecall_hsm.c
+++ b/lib/sbi/sbi_ecall_hsm.c
@@ -35,8 +35,13 @@ static int sbi_ecall_hsm_handler(unsigned long extid, unsigned long funcid,
ret = sbi_hsm_hart_stop(scratch, true);
break;
case SBI_EXT_HSM_HART_GET_STATUS:
+#ifndef CONFIG_ARM_PSCI_SUPPORT
ret = sbi_hsm_hart_get_state(sbi_domain_thishart_ptr(),
regs->a0);
+#else
+ ret = sbi_hsm_hart_get_psci_state(sbi_domain_thishart_ptr(),
+ regs->a0);
+#endif
break;
case SBI_EXT_HSM_HART_SUSPEND:
ret = sbi_hsm_hart_suspend(scratch, regs->a0, regs->a1,
diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c
index 6e52cbd..3a3265d 100644
--- a/lib/sbi/sbi_hart.c
+++ b/lib/sbi/sbi_hart.c
@@ -818,6 +818,8 @@ sbi_hart_switch_mode(unsigned long arg0, unsigned long arg1,
}
}
+ csr_write(CSR_TCMCFG, 1);
+
register unsigned long a0 asm("a0") = arg0;
register unsigned long a1 asm("a1") = arg1;
__asm__ __volatile__("mret" : : "r"(a0), "r"(a1));
diff --git a/lib/sbi/sbi_hsm.c b/lib/sbi/sbi_hsm.c
index f870ca7..acd3c9e 100644
--- a/lib/sbi/sbi_hsm.c
+++ b/lib/sbi/sbi_hsm.c
@@ -25,6 +25,8 @@
#include <sbi/sbi_system.h>
#include <sbi/sbi_timer.h>
#include <sbi/sbi_console.h>
+#include <sbi_utils/cache/cacheflush.h>
+#include <sbi_utils/psci/psci.h>
#define __sbi_hsm_hart_change_state(hdata, oldstate, newstate) \
({ \
@@ -76,6 +78,21 @@ int sbi_hsm_hart_get_state(const struct sbi_domain *dom, u32 hartid)
return __sbi_hsm_hart_get_state(hartid);
}
+#ifdef CONFIG_ARM_PSCI_SUPPORT
+int __sbi_hsm_hart_get_psci_state(u32 hartid)
+{
+ return psci_affinity_info(hartid, 0);
+}
+
+int sbi_hsm_hart_get_psci_state(const struct sbi_domain *dom, u32 hartid)
+{
+ if (!sbi_domain_is_assigned_hart(dom, hartid))
+ return SBI_EINVAL;
+
+ return __sbi_hsm_hart_get_psci_state(hartid);
+}
+#endif
+
/*
* Try to acquire the ticket for the given target hart to make sure only
* one hart prepares the start of the target hart.
@@ -137,8 +154,13 @@ int sbi_hsm_hart_interruptible_mask(const struct sbi_domain *dom,
return 0;
}
+extern unsigned char _data_start[];
+extern unsigned char _data_end[];
+extern unsigned char _bss_start[];
+extern unsigned char _bss_end[];
+
void __noreturn sbi_hsm_hart_start_finish(struct sbi_scratch *scratch,
- u32 hartid)
+ u32 hartid, bool cool_boot)
{
unsigned long next_arg1;
unsigned long next_addr;
@@ -155,34 +177,54 @@ void __noreturn sbi_hsm_hart_start_finish(struct sbi_scratch *scratch,
next_mode = scratch->next_mode;
hsm_start_ticket_release(hdata);
+ /**
+ * clean the cache : .data/bss section & local scratch & local sp
+ * let the second hart can view the data
+ * */
+ if (cool_boot) {
+ csi_flush_dcache_all();
+ csi_flush_l2_cache();
+ }
+
sbi_hart_switch_mode(hartid, next_arg1, next_addr, next_mode, false);
}
+#ifdef CONFIG_ARM_PSCI_SUPPORT
static void sbi_hsm_hart_wait(struct sbi_scratch *scratch, u32 hartid)
{
- unsigned long saved_mie;
struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
hart_data_offset);
- /* Save MIE CSR */
- saved_mie = csr_read(CSR_MIE);
-
- /* Set MSIE and MEIE bits to receive IPI */
- csr_set(CSR_MIE, MIP_MSIP | MIP_MEIP);
-
- /* Wait for state transition requested by sbi_hsm_hart_start() */
- while (atomic_read(&hdata->state) != SBI_HSM_STATE_START_PENDING) {
- wfi();
- }
-
- /* Restore MIE CSR */
- csr_write(CSR_MIE, saved_mie);
- /*
- * No need to clear IPI here because the sbi_ipi_init() will
- * clear it for current HART via sbi_platform_ipi_init().
- */
+ while (atomic_read(&hdata->state) != SBI_HSM_STATE_START_PENDING);
+}
+#else
+static void sbi_hsm_hart_wait(struct sbi_scratch *scratch, u32 hartid)
+{
+ unsigned long saved_mie;
+ struct sbi_hsm_data *hdata = sbi_scratch_offset_ptr(scratch,
+ hart_data_offset);
+ /* Save MIE CSR */
+ saved_mie = csr_read(CSR_MIE);
+
+ /* Set MSIE and MEIE bits to receive IPI */
+ csr_set(CSR_MIE, MIP_MSIP | MIP_MEIP);
+
+ /* Wait for state transition requested by sbi_hsm_hart_start() */
+ while (atomic_read(&hdata->state) != SBI_HSM_STATE_START_PENDING) {
+ wfi();
+ }
+
+ /* Restore MIE CSR */
+ csr_write(CSR_MIE, saved_mie);
+
+ /*
+ * No need to clear IPI here because the sbi_ipi_init() will
+ * clear it for current HART via sbi_platform_ipi_init().
+ */
}
+#endif
+
const struct sbi_hsm_device *sbi_hsm_get_device(void)
{
return hsm_dev;
diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c
index 423e6d8..d36d5a0 100644
--- a/lib/sbi/sbi_init.c
+++ b/lib/sbi/sbi_init.c
@@ -185,6 +185,7 @@ static void sbi_boot_print_hart(struct sbi_scratch *scratch, u32 hartid)
sbi_hart_delegation_dump(scratch, "Boot HART ", " ");
}
+#ifndef CONFIG_ARM_PSCI_SUPPORT
static spinlock_t coldboot_lock = SPIN_LOCK_INITIALIZER;
static struct sbi_hartmask coldboot_wait_hmask = { 0 };
@@ -257,6 +258,7 @@ static void wake_coldboot_harts(struct sbi_scratch *scratch, u32 hartid)
/* Release coldboot lock */
spin_unlock(&coldboot_lock);
}
+#endif
static unsigned long entry_count_offset;
static unsigned long init_count_offset;
@@ -392,12 +394,14 @@ static void __noreturn init_coldboot(struct sbi_scratch *scratch, u32 hartid)
sbi_boot_print_hart(scratch, hartid);
+#ifndef CONFIG_ARM_PSCI_SUPPORT
wake_coldboot_harts(scratch, hartid);
+#endif
count = sbi_scratch_offset_ptr(scratch, init_count_offset);
(*count)++;
- sbi_hsm_hart_start_finish(scratch, hartid);
+ sbi_hsm_hart_start_finish(scratch, hartid, true);
}
static void __noreturn init_warm_startup(struct sbi_scratch *scratch,
@@ -456,7 +460,7 @@ static void __noreturn init_warm_startup(struct sbi_scratch *scratch,
count = sbi_scratch_offset_ptr(scratch, init_count_offset);
(*count)++;
- sbi_hsm_hart_start_finish(scratch, hartid);
+ sbi_hsm_hart_start_finish(scratch, hartid, false);
}
static void __noreturn init_warm_resume(struct sbi_scratch *scratch,
@@ -481,7 +485,9 @@ static void __noreturn init_warmboot(struct sbi_scratch *scratch, u32 hartid)
{
int hstate;
+#ifndef CONFIG_ARM_PSCI_SUPPORT
wait_for_coldboot(scratch, hartid);
+#endif
hstate = sbi_hsm_hart_get_state(sbi_domain_thishart_ptr(), hartid);
if (hstate < 0)
diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
index c73e6ef..80367b2 100644
--- a/lib/sbi/sbi_pmu.c
+++ b/lib/sbi/sbi_pmu.c
@@ -557,8 +557,25 @@ int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
return ret;
}
+#ifdef CONFIG_PLATFORM_SPACEMIT_K1X
+static inline int spacemit_mhpmevent_inhibit_flags_are_invalid(uint64_t mhpmevent_val)
+{
+ uint64_t event_hw_idx = mhpmevent_val & ~MHPMEVENT_SSCOF_MASK;
+
+ /* Inhibit flags in mhpmevents of L2 cache events are invalid. */
+ if (event_hw_idx >= 184 && event_hw_idx <= 189)
+ return 1;
+
+ return 0;
+}
+#endif /* CONFIG_PLATFORM_SPACEMIT_K1X */
+
static void pmu_update_inhibit_flags(unsigned long flags, uint64_t *mhpmevent_val)
{
+#ifdef CONFIG_PLATFORM_SPACEMIT_K1X
+ if (spacemit_mhpmevent_inhibit_flags_are_invalid(*mhpmevent_val))
+ return;
+#endif
if (flags & SBI_PMU_CFG_FLAG_SET_VUINH)
*mhpmevent_val |= MHPMEVENT_VUINH;
if (flags & SBI_PMU_CFG_FLAG_SET_VSINH)
@@ -587,9 +604,16 @@ static int pmu_update_hw_mhpmevent(struct sbi_pmu_hw_event *hw_evt, int ctr_idx,
* Always set the OVF bit(disable interrupts) and inhibit counting of
* events in M-mode. The OVF bit should be enabled during the start call.
*/
- if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF))
- mhpmevent_val = (mhpmevent_val & ~MHPMEVENT_SSCOF_MASK) |
- MHPMEVENT_MINH | MHPMEVENT_OF;
+ if (sbi_hart_has_extension(scratch, SBI_HART_EXT_SSCOFPMF)) {
+#ifdef CONFIG_PLATFORM_SPACEMIT_K1X
+ if (spacemit_mhpmevent_inhibit_flags_are_invalid(mhpmevent_val))
+ mhpmevent_val = (mhpmevent_val & ~MHPMEVENT_SSCOF_MASK) |
+ MHPMEVENT_OF;
+ else
+#endif
+ mhpmevent_val = (mhpmevent_val & ~MHPMEVENT_SSCOF_MASK) |
+ MHPMEVENT_MINH | MHPMEVENT_OF;
+ }
if (pmu_dev && pmu_dev->hw_counter_disable_irq)
pmu_dev->hw_counter_disable_irq(ctr_idx);
diff --git a/lib/sbi/sbi_scratch.c b/lib/sbi/sbi_scratch.c
index 87ef84c..44917eb 100644
--- a/lib/sbi/sbi_scratch.c
+++ b/lib/sbi/sbi_scratch.c
@@ -13,6 +13,7 @@
#include <sbi/sbi_platform.h>
#include <sbi/sbi_scratch.h>
#include <sbi/sbi_string.h>
+#include <spacemit/spacemit_config.h>
u32 last_hartid_having_scratch = SBI_HARTMASK_MAX_BITS - 1;
struct sbi_scratch *hartid_to_scratch_table[SBI_HARTMASK_MAX_BITS] = { 0 };
@@ -59,11 +60,14 @@ unsigned long sbi_scratch_alloc_offset(unsigned long size)
if (!size)
return 0;
- size += __SIZEOF_POINTER__ - 1;
- size &= ~((unsigned long)__SIZEOF_POINTER__ - 1);
+ size += CACHE_LINE_SIZE - 1;
+ size &= ~((unsigned long)CACHE_LINE_SIZE - 1);
spin_lock(&extra_lock);
+ extra_offset += CACHE_LINE_SIZE - 1;
+ extra_offset &= ~((unsigned long)CACHE_LINE_SIZE - 1);
+
if (SBI_SCRATCH_SIZE < (extra_offset + size))
goto done;
diff --git a/lib/utils/Kconfig b/lib/utils/Kconfig
index 5a71e75..3ac04ab 100644
--- a/lib/utils/Kconfig
+++ b/lib/utils/Kconfig
@@ -22,4 +22,6 @@ source "$(OPENSBI_SRC_DIR)/lib/utils/sys/Kconfig"
source "$(OPENSBI_SRC_DIR)/lib/utils/timer/Kconfig"
+source "$(OPENSBI_SRC_DIR)/lib/utils/psci/Kconfig"
+
endmenu
diff --git a/lib/utils/arm_scmi/board/spacemit/spacemit_pm.c b/lib/utils/arm_scmi/board/spacemit/spacemit_pm.c
new file mode 100644
index 0000000..96fc7bb
--- /dev/null
+++ b/lib/utils/arm_scmi/board/spacemit/spacemit_pm.c
@@ -0,0 +1,41 @@
+/*
+ * Copyright (c) 2018-2019, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <spacemit/spacemit_config.h>
+#include <sbi_utils/psci/drivers/arm/css/scmi.h>
+#include <sbi_utils/psci/psci.h>
+#include <sbi_utils/psci/plat/arm/common/plat_arm.h>
+#include <sbi_utils/psci/drivers/arm/css/scmi.h>
+#include <sbi_utils/psci/drivers/arm/css/css_mhu_doorbell.h>
+
+const plat_psci_ops_t *plat_arm_psci_override_pm_ops(plat_psci_ops_t *ops)
+{
+ return css_scmi_override_pm_ops(ops);
+}
+
+static scmi_channel_plat_info_t spacemit_scmi_plat_info = {
+ .scmi_mbx_mem = SCMI_MAILBOX_SHARE_MEM,
+ .db_reg_addr = PLAT_MAILBOX_REG_BASE,
+ /* no used */
+ .db_preserve_mask = 0xfffffffe,
+ /* no used */
+ .db_modify_mask = 0x1,
+ .ring_doorbell = &mhu_ring_doorbell,
+};
+
+scmi_channel_plat_info_t *plat_css_get_scmi_info(unsigned int channel_id)
+{
+ return &spacemit_scmi_plat_info;
+}
+
+/*
+ * The array mapping platform core position (implemented by plat_my_core_pos())
+ * to the SCMI power domain ID implemented by SCP.
+ */
+uint32_t plat_css_core_pos_to_scmi_dmn_id_map[PLATFORM_CLUSTER_COUNT][PLATFORM_CORE_COUNT] = {
+ PLAT_SCMI_SINGLE_CLUSTER_DOMAIN_MAP,
+ PLAT_SCMI_DOUBLE_CLUSTER_DOMAIN_MAP
+};
diff --git a/lib/utils/arm_scmi/common/arm_pm.c b/lib/utils/arm_scmi/common/arm_pm.c
new file mode 100644
index 0000000..7fffece
--- /dev/null
+++ b/lib/utils/arm_scmi/common/arm_pm.c
@@ -0,0 +1,68 @@
+/*
+ * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sbi_utils/psci/psci.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_hart.h>
+#include <sbi_utils/psci/plat/arm/common/plat_arm.h>
+#include <sbi_utils/psci/plat/arm/common/arm_def.h>
+
+/*******************************************************************************
+ * ARM standard platform handler called to check the validity of the power state
+ * parameter.
+ ******************************************************************************/
+int arm_validate_power_state(unsigned int power_state,
+ psci_power_state_t *req_state)
+{
+ unsigned int pstate = psci_get_pstate_type(power_state);
+ unsigned int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
+ unsigned int i;
+
+ if (req_state == NULL) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ if (pwr_lvl > PLAT_MAX_PWR_LVL)
+ return PSCI_E_INVALID_PARAMS;
+
+ /* Sanity check the requested state */
+ if (pstate == PSTATE_TYPE_STANDBY) {
+ /*
+ * It's possible to enter standby only on power level 0
+ * Ignore any other power level.
+ */
+ if (pwr_lvl != ARM_PWR_LVL0)
+ return PSCI_E_INVALID_PARAMS;
+
+ req_state->pwr_domain_state[ARM_PWR_LVL0] =
+ ARM_LOCAL_STATE_RET;
+ } else {
+ for (i = ARM_PWR_LVL0; i <= pwr_lvl; i++)
+ req_state->pwr_domain_state[i] =
+ ARM_LOCAL_STATE_OFF;
+ }
+
+ /*
+ * We expect the 'state id' to be zero.
+ */
+ if (psci_get_pstate_id(power_state) != 0U)
+ return PSCI_E_INVALID_PARAMS;
+
+ return PSCI_E_SUCCESS;
+}
+
+/*******************************************************************************
+ * The ARM Standard platform definition of platform porting API
+ * `plat_setup_psci_ops`.
+ ******************************************************************************/
+int plat_setup_psci_ops(uintptr_t sec_entrypoint,
+ const plat_psci_ops_t **psci_ops)
+{
+ *psci_ops = plat_arm_psci_override_pm_ops(&plat_arm_psci_pm_ops);
+
+ return 0;
+}
diff --git a/lib/utils/arm_scmi/css/common/css_pm.c b/lib/utils/arm_scmi/css/common/css_pm.c
new file mode 100644
index 0000000..8d17b6b
--- /dev/null
+++ b/lib/utils/arm_scmi/css/common/css_pm.c
@@ -0,0 +1,298 @@
+/*
+ * Copyright (c) 2015-2022, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/riscv_asm.h>
+#include <sbi_utils/psci/psci.h>
+#include <sbi_utils/cci/cci.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi_utils/irqchip/fdt_irqchip_plic.h>
+#include <sbi_utils/psci/plat/arm/common/arm_def.h>
+#include <sbi_utils/psci/plat/arm/css/common/css_pm.h>
+#include <sbi_utils/psci/drivers/arm/css/css_scp.h>
+#include <sbi_utils/psci/plat/arm/common/plat_arm.h>
+
+/* Allow CSS platforms to override `plat_arm_psci_pm_ops` */
+#pragma weak plat_arm_psci_pm_ops
+
+/*******************************************************************************
+ * Handler called when a power domain is about to be turned on. The
+ * level and mpidr determine the affinity instance.
+ ******************************************************************************/
+int css_pwr_domain_on(u_register_t mpidr)
+{
+ css_scp_on(mpidr);
+
+ return PSCI_E_SUCCESS;
+}
+
+static void css_pwr_domain_on_finisher_common(
+ const psci_power_state_t *target_state)
+{
+ unsigned int clusterid;
+ unsigned int hartid = current_hartid();
+
+ if (CSS_CORE_PWR_STATE(target_state) != ARM_LOCAL_STATE_OFF) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ /*
+ * Perform the common cluster specific operations i.e enable coherency
+ * if this cluster was off.
+ */
+ if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) {
+ clusterid = MPIDR_AFFLVL1_VAL(hartid);
+ cci_enable_snoop_dvm_reqs(clusterid);
+ }
+}
+
+/*******************************************************************************
+ * Handler called when a power level has just been powered on after
+ * being turned off earlier. The target_state encodes the low power state that
+ * each level has woken up from. This handler would never be invoked with
+ * the system power domain uninitialized as either the primary would have taken
+ * care of it as part of cold boot or the first core awakened from system
+ * suspend would have already initialized it.
+ ******************************************************************************/
+void css_pwr_domain_on_finish(const psci_power_state_t *target_state)
+{
+ /* Assert that the system power domain need not be initialized */
+ if (css_system_pwr_state(target_state) != ARM_LOCAL_STATE_RUN) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ css_pwr_domain_on_finisher_common(target_state);
+}
+
+/*******************************************************************************
+ * Handler called when a power domain has just been powered on and the cpu
+ * and its cluster are fully participating in coherent transaction on the
+ * interconnect. Data cache must be enabled for CPU at this point.
+ ******************************************************************************/
+void css_pwr_domain_on_finish_late(const psci_power_state_t *target_state)
+{
+#if 0
+ /* Program the gic per-cpu distributor or re-distributor interface */
+ plat_arm_gic_pcpu_init();
+
+ /* Enable the gic cpu interface */
+ plat_arm_gic_cpuif_enable();
+
+ /* Setup the CPU power down request interrupt for secondary core(s) */
+ css_setup_cpu_pwr_down_intr();
+#endif
+}
+
+/*******************************************************************************
+ * Common function called while turning a cpu off or suspending it. It is called
+ * from css_off() or css_suspend() when these functions in turn are called for
+ * power domain at the highest power level which will be powered down. It
+ * performs the actions common to the OFF and SUSPEND calls.
+ ******************************************************************************/
+static void css_power_down_common(const psci_power_state_t *target_state)
+{
+ unsigned int clusterid;
+ unsigned int hartid = current_hartid();
+#if 0
+ /* Prevent interrupts from spuriously waking up this cpu */
+ plat_arm_gic_cpuif_disable();
+#endif
+ /* Cluster is to be turned off, so disable coherency */
+ if (CSS_CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) {
+ clusterid = MPIDR_AFFLVL1_VAL(hartid);
+ cci_disable_snoop_dvm_reqs(clusterid);
+ }
+}
+
+static int css_pwr_domain_off_early(const psci_power_state_t *target_state)
+{
+ /* the ipi's pending is cleared before */
+ /* disable the plic irq */
+ fdt_plic_context_exit();
+ /* clear the external irq pending */
+ csr_clear(CSR_MIP, MIP_MEIP);
+ csr_clear(CSR_MIP, MIP_SEIP);
+
+ /* here we clear the sstimer pending if this core have */
+ if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), SBI_HART_EXT_SSTC)) {
+ csr_write(CSR_STIMECMP, 0xffffffffffffffff);
+ }
+
+ return 0;
+}
+
+/*******************************************************************************
+ * Handler called when a power domain is about to be turned off. The
+ * target_state encodes the power state that each level should transition to.
+ ******************************************************************************/
+void css_pwr_domain_off(const psci_power_state_t *target_state)
+{
+ if (CSS_CORE_PWR_STATE(target_state) != ARM_LOCAL_STATE_OFF) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ css_power_down_common(target_state);
+ css_scp_off(target_state);
+}
+
+void css_pwr_down_wfi(const psci_power_state_t *target_state)
+{
+ while (1)
+ wfi();
+}
+
+/*
+ * The system power domain suspend is only supported only via
+ * PSCI SYSTEM_SUSPEND API. PSCI CPU_SUSPEND request to system power domain
+ * will be downgraded to the lower level.
+ */
+static int css_validate_power_state(unsigned int power_state,
+ psci_power_state_t *req_state)
+{
+ int rc;
+
+ rc = arm_validate_power_state(power_state, req_state);
+
+ /*
+ * Ensure that we don't overrun the pwr_domain_state array in the case
+ * where the platform supported max power level is less than the system
+ * power level
+ */
+
+#if (PLAT_MAX_PWR_LVL == CSS_SYSTEM_PWR_DMN_LVL)
+
+ /*
+ * Ensure that the system power domain level is never suspended
+ * via PSCI CPU SUSPEND API. Currently system suspend is only
+ * supported via PSCI SYSTEM SUSPEND API.
+ */
+
+ req_state->pwr_domain_state[CSS_SYSTEM_PWR_DMN_LVL] =
+ ARM_LOCAL_STATE_RUN;
+#endif
+
+ return rc;
+}
+
+/*******************************************************************************
+ * Handler called when the CPU power domain is about to enter standby.
+ ******************************************************************************/
+void css_cpu_standby(plat_local_state_t cpu_state)
+{
+ /* unsigned int scr; */
+
+ if (cpu_state != ARM_LOCAL_STATE_RET) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ wfi();
+#if 0
+ scr = read_scr_el3();
+ /*
+ * Enable the Non secure interrupt to wake the CPU.
+ * In GICv3 affinity routing mode, the non secure group1 interrupts use
+ * the PhysicalFIQ at EL3 whereas in GICv2, it uses the PhysicalIRQ.
+ * Enabling both the bits works for both GICv2 mode and GICv3 affinity
+ * routing mode.
+ */
+ write_scr_el3(scr | SCR_IRQ_BIT | SCR_FIQ_BIT);
+ isb();
+ dsb();
+ wfi();
+
+ /*
+ * Restore SCR to the original value, synchronisation of scr_el3 is
+ * done by eret while el3_exit to save some execution cycles.
+ */
+ write_scr_el3(scr);
+#endif
+}
+
+/*******************************************************************************
+ * Handler called when a power domain is about to be suspended. The
+ * target_state encodes the power state that each level should transition to.
+ ******************************************************************************/
+void css_pwr_domain_suspend(const psci_power_state_t *target_state)
+{
+ /*
+ * CSS currently supports retention only at cpu level. Just return
+ * as nothing is to be done for retention.
+ */
+ if (CSS_CORE_PWR_STATE(target_state) == ARM_LOCAL_STATE_RET)
+ return;
+
+
+ if (CSS_CORE_PWR_STATE(target_state) != ARM_LOCAL_STATE_OFF) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ css_power_down_common(target_state);
+
+ csr_clear(CSR_MIE, MIP_SSIP | MIP_MSIP | MIP_STIP | MIP_MTIP | MIP_SEIP | MIP_MEIP);
+
+ /* Perform system domain state saving if issuing system suspend */
+ if (css_system_pwr_state(target_state) == ARM_LOCAL_STATE_OFF) {
+ /* arm_system_pwr_domain_save(); */
+
+ /* Power off the Redistributor after having saved its context */
+ /* plat_arm_gic_redistif_off(); */
+ }
+
+ css_scp_suspend(target_state);
+}
+
+/*******************************************************************************
+ * Handler called when a power domain has just been powered on after
+ * having been suspended earlier. The target_state encodes the low power state
+ * that each level has woken up from.
+ * TODO: At the moment we reuse the on finisher and reinitialize the secure
+ * context. Need to implement a separate suspend finisher.
+ ******************************************************************************/
+void css_pwr_domain_suspend_finish(
+ const psci_power_state_t *target_state)
+{
+ /* Return as nothing is to be done on waking up from retention. */
+ if (CSS_CORE_PWR_STATE(target_state) == ARM_LOCAL_STATE_RET)
+ return;
+
+ /* Perform system domain restore if woken up from system suspend */
+ if (css_system_pwr_state(target_state) == ARM_LOCAL_STATE_OFF)
+ /*
+ * At this point, the Distributor must be powered on to be ready
+ * to have its state restored. The Redistributor will be powered
+ * on as part of gicv3_rdistif_init_restore.
+ */
+ /* arm_system_pwr_domain_resume() */;
+
+ css_pwr_domain_on_finisher_common(target_state);
+
+ /* Enable the gic cpu interface */
+ /* plat_arm_gic_cpuif_enable() */;
+}
+
+/*******************************************************************************
+ * Export the platform handlers via plat_arm_psci_pm_ops. The ARM Standard
+ * platform will take care of registering the handlers with PSCI.
+ ******************************************************************************/
+plat_psci_ops_t plat_arm_psci_pm_ops = {
+ .pwr_domain_on = css_pwr_domain_on,
+ .pwr_domain_on_finish = css_pwr_domain_on_finish,
+ .pwr_domain_on_finish_late = css_pwr_domain_on_finish_late,
+ .pwr_domain_off = css_pwr_domain_off,
+ .pwr_domain_off_early = css_pwr_domain_off_early,
+ .pwr_domain_pwr_down_wfi = css_pwr_down_wfi,
+ .validate_power_state = css_validate_power_state,
+ .cpu_standby = css_cpu_standby,
+ .pwr_domain_suspend = css_pwr_domain_suspend,
+ .pwr_domain_suspend_finish = css_pwr_domain_suspend_finish,
+};
diff --git a/lib/utils/arm_scmi/css/mhu/css_mhu_doorbell.c b/lib/utils/arm_scmi/css/mhu/css_mhu_doorbell.c
new file mode 100644
index 0000000..887d031
--- /dev/null
+++ b/lib/utils/arm_scmi/css/mhu/css_mhu_doorbell.c
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2014-2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <sbi_utils/psci/drivers/arm/css/scmi.h>
+#include <sbi_utils/psci/drivers/arm/css/scmi_private.h>
+#include "mhu.h"
+
+void mhu_ring_doorbell(struct scmi_channel_plat_info *plat_info)
+{
+ unsigned int msg;
+ mbox_reg_desc_t *regs = (mbox_reg_desc_t *)plat_info->db_reg_addr;
+
+ /* clear the fifo */
+ while (regs->msg_status[MAILBOX_SECURE_PSCI_CHANNEL + 2].bits.num_msg) {
+ msg = regs->mbox_msg[MAILBOX_SECURE_PSCI_CHANNEL + 2].val;
+ }
+
+ /* clear pending */
+ msg = regs->mbox_irq[0].irq_status_clr.val;
+ msg |= (1 << ((MAILBOX_SECURE_PSCI_CHANNEL + 2) * 2));
+ regs->mbox_irq[0].irq_status_clr.val = msg;
+
+ /* door bell the esos */
+ regs->mbox_msg[MAILBOX_SECURE_PSCI_CHANNEL].val = 'c';
+}
diff --git a/lib/utils/arm_scmi/css/mhu/mhu.h b/lib/utils/arm_scmi/css/mhu/mhu.h
new file mode 100644
index 0000000..fb3e757
--- /dev/null
+++ b/lib/utils/arm_scmi/css/mhu/mhu.h
@@ -0,0 +1,130 @@
+/*
+ * Arm SCP/MCP Software
+ * Copyright (c) 2015-2021, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#ifndef INTERNAL_MHU_H
+#define INTERNAL_MHU_H
+
+/* mailbox register description */
+/* mailbox sysconfig */
+typedef union mbox_sysconfig {
+ unsigned int val;
+ struct {
+ unsigned int resetn:1;
+ unsigned int reserved:31;
+ } bits;
+} mbox_sysconfig_t;
+
+
+typedef union mbox_msg {
+ unsigned int val;
+ struct {
+ unsigned int msg:32;
+ } bits;
+} mbox_msg_t;
+
+typedef union mbox_fifo_status {
+ unsigned int val;
+ struct {
+ unsigned int is_full:1;
+ unsigned int reserved:31;
+ } bits;
+} mbox_fifo_status_t;
+
+
+typedef union mbox_msg_status {
+ unsigned int val;
+ struct {
+ unsigned int num_msg:4;
+ unsigned int reserved:28;
+ } bits;
+} mbox_msg_status_t;
+
+typedef union mbox_irq_status {
+ unsigned int val;
+ struct {
+ unsigned int new_msg0_status:1;
+ unsigned int not_msg0_full:1;
+ unsigned int new_msg1_status:1;
+ unsigned int not_msg1_full:1;
+ unsigned int new_msg2_status:1;
+ unsigned int not_msg2_full:1;
+ unsigned int new_msg3_status:1;
+ unsigned int not_msg3_full:1;
+ unsigned int reserved:24;
+ } bits;
+} mbox_irq_status_t;
+
+typedef union mbox_irq_status_clr {
+ unsigned int val;
+ struct {
+ unsigned int new_msg0_clr:1;
+ unsigned int not_msg0_full_clr:1;
+ unsigned int new_msg1_clr:1;
+ unsigned int not_msg1_full_clr:1;
+ unsigned int new_msg2_clr:1;
+ unsigned int not_msg2_full_clr:1;
+ unsigned int new_msg3_clr:1;
+ unsigned int not_msg3_full_clr:1;
+ unsigned int reserved:24;
+ } bits;
+} mbox_irq_status_clr_t;
+
+typedef union mbox_irq_enable_set {
+ unsigned int val;
+ struct {
+ unsigned int new_msg0_irq_en:1;
+ unsigned int not_msg0_full_irq_en:1;
+ unsigned int new_msg1_irq_en:1;
+ unsigned int not_msg1_full_irq_en:1;
+ unsigned int new_msg2_irq_en:1;
+ unsigned int not_msg2_full_irq_en:1;
+ unsigned int new_msg3_irq_en:1;
+ unsigned int not_msg3_full_irq_en:1;
+ unsigned int reserved:24;
+ } bits;
+} mbox_irq_enable_set_t;
+
+typedef union mbox_irq_enable_clr {
+ unsigned int val;
+ struct {
+ unsigned int new_msg0_irq_clr:1;
+ unsigned int not_msg0_full_irq_clr:1;
+ unsigned int new_msg1_irq_clr:1;
+ unsigned int not_msg1_full_irq_clr:1;
+ unsigned int new_msg2_irq_clr:1;
+ unsigned int not_msg2_full_irq_clr:1;
+ unsigned int new_msg3_irq_clr:1;
+ unsigned int not_msg3_full_irq_clr:1;
+ unsigned int reserved:24;
+ } bits;
+} mbox_irq_enable_clr_t;
+
+typedef struct mbox_irq {
+ mbox_irq_status_t irq_status;
+ mbox_irq_status_clr_t irq_status_clr;
+ mbox_irq_enable_set_t irq_en_set;
+ mbox_irq_enable_clr_t irq_en_clr;
+} mbox_irq_t;
+
+/*!
+ * \brief MHU Register Definitions
+ */
+typedef struct mhu_reg {
+ unsigned int mbox_version; /* 0x00 */
+ unsigned int reserved0[3]; /* 0x4 0x8 0xc */
+ mbox_sysconfig_t mbox_sysconfig; /* 0x10 */
+ unsigned int reserved1[11]; /* 0x14, 0x18, 0x1c, 0x20, 0x24, 0x28, 0x2c, 0x30, 0x34, 0x38, 0x3c */
+ mbox_msg_t mbox_msg[4]; /* 0x40, 0x44, 0x48, 0x4c */
+ unsigned int reserved2[12];
+ mbox_fifo_status_t fifo_status[4]; /* 0x80, 0x84, 0x88, 0x8c */
+ unsigned int reserved3[12];
+ mbox_msg_status_t msg_status[4]; /* 0xc0 */
+ unsigned int reserved4[12];
+ mbox_irq_t mbox_irq[2]; /* 0x100 */
+} mbox_reg_desc_t;
+
+#endif /* INTERNAL_MHU_H */
diff --git a/lib/utils/arm_scmi/css/scmi/scmi_common.c b/lib/utils/arm_scmi/css/scmi/scmi_common.c
new file mode 100644
index 0000000..1c56da2
--- /dev/null
+++ b/lib/utils/arm_scmi/css/scmi/scmi_common.c
@@ -0,0 +1,228 @@
+/*
+ * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi_utils/cache/cacheflush.h>
+#include <sbi_utils/psci/drivers/arm/css/scmi.h>
+#include <sbi_utils/psci/drivers/arm/css/scmi_private.h>
+
+#define scmi_lock_init(lock)
+#define scmi_lock_get(lock) spin_lock(lock)
+#define scmi_lock_release(lock) spin_unlock(lock)
+
+
+/*
+ * Private helper function to get exclusive access to SCMI channel.
+ */
+void scmi_get_channel(scmi_channel_t *ch)
+{
+ if (!ch->lock)
+ sbi_hart_hang();
+
+ scmi_lock_get(ch->lock);
+
+ /* Make sure any previous command has finished */
+ if (!SCMI_IS_CHANNEL_FREE(
+ ((mailbox_mem_t *)(ch->info->scmi_mbx_mem))->status))
+ sbi_hart_hang();
+}
+
+/*
+ * Private helper function to transfer ownership of channel from AP to SCP.
+ */
+void scmi_send_sync_command(scmi_channel_t *ch)
+{
+ mailbox_mem_t *mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+
+ SCMI_MARK_CHANNEL_BUSY(mbx_mem->status);
+
+ /*
+ * Ensure that any write to the SCMI payload area is seen by SCP before
+ * we write to the doorbell register. If these 2 writes were reordered
+ * by the CPU then SCP would read stale payload data
+ */
+ /* dmbst(); */
+ asm volatile ("fence iorw, iorw");
+
+ ch->info->ring_doorbell(ch->info);
+ /*
+ * Ensure that the write to the doorbell register is ordered prior to
+ * checking whether the channel is free.
+ */
+ /* dmbsy(); */
+ asm volatile ("fence iorw, iorw");
+
+ /* Wait for channel to be free */
+ while (!SCMI_IS_CHANNEL_FREE(mbx_mem->status))
+ ;
+
+ /*
+ * Ensure that any read to the SCMI payload area is done after reading
+ * mailbox status. If these 2 reads were reordered then the CPU would
+ * read invalid payload data
+ */
+ /* dmbld(); */
+ asm volatile ("fence iorw, iorw");
+}
+
+/*
+ * Private helper function to release exclusive access to SCMI channel.
+ */
+void scmi_put_channel(scmi_channel_t *ch)
+{
+ /* Make sure any previous command has finished */
+ if (!SCMI_IS_CHANNEL_FREE(
+ ((mailbox_mem_t *)(ch->info->scmi_mbx_mem))->status))
+ sbi_hart_hang();
+
+ if (!ch->lock)
+ sbi_hart_hang();
+
+ scmi_lock_release(ch->lock);
+}
+
+/*
+ * API to query the SCMI protocol version.
+ */
+int scmi_proto_version(void *p, uint32_t proto_id, uint32_t *version)
+{
+ mailbox_mem_t *mbx_mem;
+ unsigned int token = 0;
+ int ret;
+ scmi_channel_t *ch = (scmi_channel_t *)p;
+
+ validate_scmi_channel(ch);
+
+ scmi_get_channel(ch);
+
+ mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+ mbx_mem->msg_header = SCMI_MSG_CREATE(proto_id, SCMI_PROTO_VERSION_MSG,
+ token);
+ mbx_mem->len = SCMI_PROTO_VERSION_MSG_LEN;
+ mbx_mem->flags = SCMI_FLAG_RESP_POLL;
+
+ csi_dcache_clean_invalid_range((uintptr_t)ch->info->scmi_mbx_mem, 0x80);
+
+ scmi_send_sync_command(ch);
+
+ /* Get the return values */
+ SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *version);
+ if (mbx_mem->len != SCMI_PROTO_VERSION_RESP_LEN) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ if (token != SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ scmi_put_channel(ch);
+
+ return ret;
+}
+
+/*
+ * API to query the protocol message attributes for a SCMI protocol.
+ */
+int scmi_proto_msg_attr(void *p, uint32_t proto_id,
+ uint32_t command_id, uint32_t *attr)
+{
+ mailbox_mem_t *mbx_mem;
+ unsigned int token = 0;
+ int ret;
+ scmi_channel_t *ch = (scmi_channel_t *)p;
+
+ validate_scmi_channel(ch);
+
+ scmi_get_channel(ch);
+
+ mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+ mbx_mem->msg_header = SCMI_MSG_CREATE(proto_id,
+ SCMI_PROTO_MSG_ATTR_MSG, token);
+ mbx_mem->len = SCMI_PROTO_MSG_ATTR_MSG_LEN;
+ mbx_mem->flags = SCMI_FLAG_RESP_POLL;
+ SCMI_PAYLOAD_ARG1(mbx_mem->payload, command_id);
+
+ csi_dcache_clean_invalid_range((uintptr_t)ch->info->scmi_mbx_mem, 0x80);
+
+ scmi_send_sync_command(ch);
+
+ /* Get the return values */
+ SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *attr);
+ if (mbx_mem->len != SCMI_PROTO_MSG_ATTR_RESP_LEN) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ if (token != SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ scmi_put_channel(ch);
+
+ return ret;
+}
+
+/*
+ * SCMI Driver initialization API. Returns initialized channel on success
+ * or NULL on error. The return type is an opaque void pointer.
+ */
+void *scmi_init(scmi_channel_t *ch)
+{
+ uint32_t version;
+ int ret;
+
+ if (!ch || !ch->info || !ch->info->db_reg_addr || !ch->info->db_modify_mask ||
+ !ch->info->db_preserve_mask || !ch->info->ring_doorbell ||
+ !ch->lock)
+ sbi_hart_hang();
+
+ scmi_lock_init(ch->lock);
+
+ ch->is_initialized = 1;
+
+ ret = scmi_proto_version(ch, SCMI_PWR_DMN_PROTO_ID, &version);
+ if (ret != SCMI_E_SUCCESS) {
+ sbi_printf("SCMI power domain protocol version message failed\n");
+ goto error;
+ }
+
+ if (!is_scmi_version_compatible(SCMI_PWR_DMN_PROTO_VER, version)) {
+ sbi_printf("SCMI power domain protocol version 0x%x incompatible with driver version 0x%x\n",
+ version, SCMI_PWR_DMN_PROTO_VER);
+ goto error;
+ }
+
+ sbi_printf("SCMI power domain protocol version 0x%x detected\n", version);
+
+ ret = scmi_proto_version(ch, SCMI_SYS_PWR_PROTO_ID, &version);
+ if ((ret != SCMI_E_SUCCESS)) {
+ sbi_printf("SCMI system power protocol version message failed\n");
+ goto error;
+ }
+
+ if (!is_scmi_version_compatible(SCMI_SYS_PWR_PROTO_VER, version)) {
+ sbi_printf("SCMI system power management protocol version 0x%x incompatible with driver version 0x%x\n",
+ version, SCMI_SYS_PWR_PROTO_VER);
+ goto error;
+ }
+
+ sbi_printf("SCMI system power management protocol version 0x%x detected\n",
+ version);
+
+ sbi_printf("SCMI driver initialized\n");
+
+ return (void *)ch;
+
+error:
+ ch->is_initialized = 0;
+ return NULL;
+}
diff --git a/lib/utils/arm_scmi/css/scmi/scmi_pwr_dmn_proto.c b/lib/utils/arm_scmi/css/scmi/scmi_pwr_dmn_proto.c
new file mode 100644
index 0000000..5a4f734
--- /dev/null
+++ b/lib/utils/arm_scmi/css/scmi/scmi_pwr_dmn_proto.c
@@ -0,0 +1,102 @@
+/*
+ * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_hart.h>
+#include <sbi_utils/cache/cacheflush.h>
+#include <sbi_utils/psci/drivers/arm/css/scmi.h>
+#include <sbi_utils/psci/drivers/arm/css/scmi_private.h>
+
+/*
+ * API to set the SCMI power domain power state.
+ */
+int scmi_pwr_state_set(void *p, uint32_t domain_id,
+ uint32_t scmi_pwr_state)
+{
+ mailbox_mem_t *mbx_mem;
+ unsigned int token = 0;
+ int ret;
+
+ /*
+ * Only asynchronous mode of `set power state` command is allowed on
+ * application processors.
+ */
+ uint32_t pwr_state_set_msg_flag = SCMI_PWR_STATE_SET_FLAG_ASYNC;
+ scmi_channel_t *ch = (scmi_channel_t *)p;
+
+ validate_scmi_channel(ch);
+
+ scmi_get_channel(ch);
+
+ mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+ mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_PWR_DMN_PROTO_ID,
+ SCMI_PWR_STATE_SET_MSG, token);
+ mbx_mem->len = SCMI_PWR_STATE_SET_MSG_LEN;
+ mbx_mem->flags = SCMI_FLAG_RESP_POLL;
+ SCMI_PAYLOAD_ARG3(mbx_mem->payload, pwr_state_set_msg_flag,
+ domain_id, scmi_pwr_state);
+
+ csi_dcache_clean_invalid_range((uintptr_t)ch->info->scmi_mbx_mem, 0x80);
+ scmi_send_sync_command(ch);
+
+ /* Get the return values */
+ SCMI_PAYLOAD_RET_VAL1(mbx_mem->payload, ret);
+ if (mbx_mem->len != SCMI_PWR_STATE_SET_RESP_LEN) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ if (token != SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ scmi_put_channel(ch);
+
+ return ret;
+}
+
+/*
+ * API to get the SCMI power domain power state.
+ */
+int scmi_pwr_state_get(void *p, uint32_t domain_id,
+ uint32_t *scmi_pwr_state)
+{
+ mailbox_mem_t *mbx_mem;
+ unsigned int token = 0;
+ int ret;
+ scmi_channel_t *ch = (scmi_channel_t *)p;
+
+ validate_scmi_channel(ch);
+
+ scmi_get_channel(ch);
+
+ mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+ mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_PWR_DMN_PROTO_ID,
+ SCMI_PWR_STATE_GET_MSG, token);
+ mbx_mem->len = SCMI_PWR_STATE_GET_MSG_LEN;
+ mbx_mem->flags = SCMI_FLAG_RESP_POLL;
+ SCMI_PAYLOAD_ARG1(mbx_mem->payload, domain_id);
+
+ csi_dcache_clean_invalid_range((uintptr_t)ch->info->scmi_mbx_mem, 0x80);
+ scmi_send_sync_command(ch);
+
+ /* Get the return values */
+ SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *scmi_pwr_state);
+ if (mbx_mem->len != SCMI_PWR_STATE_GET_RESP_LEN) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ if (token != SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ scmi_put_channel(ch);
+
+ return ret;
+}
diff --git a/lib/utils/arm_scmi/css/scmi/scmi_sys_pwr_proto.c b/lib/utils/arm_scmi/css/scmi/scmi_sys_pwr_proto.c
new file mode 100644
index 0000000..fc7c30e
--- /dev/null
+++ b/lib/utils/arm_scmi/css/scmi/scmi_sys_pwr_proto.c
@@ -0,0 +1,90 @@
+/*
+ * Copyright (c) 2017-2019, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_hart.h>
+#include <sbi_utils/cache/cacheflush.h>
+#include <sbi_utils/psci/drivers/arm/css/scmi.h>
+#include <sbi_utils/psci/drivers/arm/css/scmi_private.h>
+
+/*
+ * API to set the SCMI system power state
+ */
+int scmi_sys_pwr_state_set(void *p, uint32_t flags, uint32_t system_state)
+{
+ mailbox_mem_t *mbx_mem;
+ unsigned int token = 0;
+ int ret;
+ scmi_channel_t *ch = (scmi_channel_t *)p;
+
+ validate_scmi_channel(ch);
+
+ scmi_get_channel(ch);
+
+ mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+ mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_SYS_PWR_PROTO_ID,
+ SCMI_SYS_PWR_STATE_SET_MSG, token);
+ mbx_mem->len = SCMI_SYS_PWR_STATE_SET_MSG_LEN;
+ mbx_mem->flags = SCMI_FLAG_RESP_POLL;
+ SCMI_PAYLOAD_ARG2(mbx_mem->payload, flags, system_state);
+
+ scmi_send_sync_command(ch);
+
+ /* Get the return values */
+ SCMI_PAYLOAD_RET_VAL1(mbx_mem->payload, ret);
+ if (mbx_mem->len != SCMI_SYS_PWR_STATE_SET_RESP_LEN) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ if (token != SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ scmi_put_channel(ch);
+
+ return ret;
+}
+
+/*
+ * API to get the SCMI system power state
+ */
+int scmi_sys_pwr_state_get(void *p, uint32_t *system_state)
+{
+ mailbox_mem_t *mbx_mem;
+ unsigned int token = 0;
+ int ret;
+ scmi_channel_t *ch = (scmi_channel_t *)p;
+
+ validate_scmi_channel(ch);
+
+ scmi_get_channel(ch);
+
+ mbx_mem = (mailbox_mem_t *)(ch->info->scmi_mbx_mem);
+ mbx_mem->msg_header = SCMI_MSG_CREATE(SCMI_SYS_PWR_PROTO_ID,
+ SCMI_SYS_PWR_STATE_GET_MSG, token);
+ mbx_mem->len = SCMI_SYS_PWR_STATE_GET_MSG_LEN;
+ mbx_mem->flags = SCMI_FLAG_RESP_POLL;
+
+ scmi_send_sync_command(ch);
+
+ /* Get the return values */
+ SCMI_PAYLOAD_RET_VAL2(mbx_mem->payload, ret, *system_state);
+ if (mbx_mem->len != SCMI_SYS_PWR_STATE_GET_RESP_LEN) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ if (token != SCMI_MSG_GET_TOKEN(mbx_mem->msg_header)) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ scmi_put_channel(ch);
+
+ return ret;
+}
diff --git a/lib/utils/arm_scmi/css/scp/css_pm_scmi.c b/lib/utils/arm_scmi/css/scp/css_pm_scmi.c
new file mode 100644
index 0000000..a88eb9f
--- /dev/null
+++ b/lib/utils/arm_scmi/css/scp/css_pm_scmi.c
@@ -0,0 +1,418 @@
+/*
+ * Copyright (c) 2017-2022, Arm Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sbi/sbi_console.h>
+#include <sbi/riscv_asm.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_platform.h>
+#include <sbi_utils/psci/psci.h>
+#include <sbi_utils/psci/plat/arm/common/plat_arm.h>
+#include <sbi_utils/psci/drivers/arm/css/scmi.h>
+#include <sbi_utils/psci/plat/arm/css/common/css_pm.h>
+#include <sbi_utils/psci/plat/arm/common/arm_def.h>
+#include <sbi_utils/psci/plat/arm/board/spacemit/include/platform_def.h>
+#include <sbi_utils/psci/plat/common/platform.h>
+#include <../../../psci/psci_private.h>
+
+/*
+ * This file implements the SCP helper functions using SCMI protocol.
+ */
+
+/*
+ * SCMI power state parameter bit field encoding for ARM CSS platforms.
+ *
+ * 31 20 19 16 15 12 11 8 7 4 3 0
+ * +-------------------------------------------------------------+
+ * | SBZ | Max level | Level 3 | Level 2 | Level 1 | Level 0 |
+ * | | | state | state | state | state |
+ * +-------------------------------------------------------------+
+ *
+ * `Max level` encodes the highest level that has a valid power state
+ * encoded in the power state.
+ */
+#define SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT 16
+#define SCMI_PWR_STATE_MAX_PWR_LVL_WIDTH 4
+#define SCMI_PWR_STATE_MAX_PWR_LVL_MASK \
+ ((1 << SCMI_PWR_STATE_MAX_PWR_LVL_WIDTH) - 1)
+#define SCMI_SET_PWR_STATE_MAX_PWR_LVL(_power_state, _max_level) \
+ (_power_state) |= ((_max_level) & SCMI_PWR_STATE_MAX_PWR_LVL_MASK)\
+ << SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT
+#define SCMI_GET_PWR_STATE_MAX_PWR_LVL(_power_state) \
+ (((_power_state) >> SCMI_PWR_STATE_MAX_PWR_LVL_SHIFT) \
+ & SCMI_PWR_STATE_MAX_PWR_LVL_MASK)
+
+#define SCMI_PWR_STATE_LVL_WIDTH 4
+#define SCMI_PWR_STATE_LVL_MASK \
+ ((1 << SCMI_PWR_STATE_LVL_WIDTH) - 1)
+#define SCMI_SET_PWR_STATE_LVL(_power_state, _level, _level_state) \
+ (_power_state) |= ((_level_state) & SCMI_PWR_STATE_LVL_MASK) \
+ << (SCMI_PWR_STATE_LVL_WIDTH * (_level))
+#define SCMI_GET_PWR_STATE_LVL(_power_state, _level) \
+ (((_power_state) >> (SCMI_PWR_STATE_LVL_WIDTH * (_level))) & \
+ SCMI_PWR_STATE_LVL_MASK)
+
+/*
+ * The SCMI power state enumeration for a power domain level
+ */
+typedef enum {
+ scmi_power_state_off = 0,
+ scmi_power_state_on = 1,
+ scmi_power_state_sleep = 2,
+} scmi_power_state_t;
+
+/*
+ * The global handles for invoking the SCMI driver APIs after the driver
+ * has been initialized.
+ */
+static void *scmi_handles[PLAT_ARM_SCMI_CHANNEL_COUNT];
+
+/* The global SCMI channels array */
+static scmi_channel_t scmi_channels[PLAT_ARM_SCMI_CHANNEL_COUNT];
+
+/*
+ * Channel ID for the default SCMI channel.
+ * The default channel is used to issue SYSTEM level SCMI requests and is
+ * initialized to the channel which has the boot cpu as its resource.
+ */
+static uint32_t default_scmi_channel_id;
+
+/*
+ * TODO: Allow use of channel specific lock instead of using a single lock for
+ * all the channels.
+ */
+ARM_SCMI_INSTANTIATE_LOCK;
+
+/*
+ * Function to obtain the SCMI Domain ID and SCMI Channel number from the linear
+ * core position. The SCMI Channel number is encoded in the upper 16 bits and
+ * the Domain ID is encoded in the lower 16 bits in each entry of the mapping
+ * array exported by the platform.
+ */
+static void css_scp_core_pos_to_scmi_channel(unsigned int core_pos,
+ unsigned int *scmi_domain_id, unsigned int *scmi_channel_id)
+{
+ unsigned int composite_id;
+ unsigned int *map_id = plat_get_power_domain_tree_desc()[CLUSTER_INDEX_IN_CPU_TOPOLOGY] > 1 ?
+ plat_css_core_pos_to_scmi_dmn_id_map[1] :
+ plat_css_core_pos_to_scmi_dmn_id_map[0];
+
+ composite_id = map_id[core_pos];
+
+ *scmi_channel_id = GET_SCMI_CHANNEL_ID(composite_id);
+ *scmi_domain_id = GET_SCMI_DOMAIN_ID(composite_id);
+}
+
+/*
+ * Helper function to turn off a CPU power domain and its parent power domains
+ * if applicable.
+ */
+void css_scp_off(const struct psci_power_state *target_state)
+{
+ unsigned int lvl = 0, channel_id, domain_id;
+ int ret;
+ uint32_t scmi_pwr_state = 0, cpu_idx;
+ unsigned int hartid = current_hartid();
+
+ cpu_idx = plat_core_pos_by_mpidr(hartid);
+
+ /* At-least the CPU level should be specified to be OFF */
+ if (target_state->pwr_domain_state[ARM_PWR_LVL0] != ARM_LOCAL_STATE_OFF) {
+ sbi_printf("%s:%d, wrong power domain state\n",
+ __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ /* PSCI CPU OFF cannot be used to turn OFF system power domain */
+ if (css_system_pwr_state(target_state) != ARM_LOCAL_STATE_RUN) {
+ sbi_printf("%s:%d, wrong power domain state\n",
+ __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ for (; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
+ if (target_state->pwr_domain_state[lvl] == ARM_LOCAL_STATE_RUN)
+ break;
+
+ if (target_state->pwr_domain_state[lvl] != ARM_LOCAL_STATE_OFF) {
+ sbi_printf("%s:%d, wrong power domain state\n",
+ __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl,
+ scmi_power_state_off);
+ }
+
+ SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1);
+
+ css_scp_core_pos_to_scmi_channel(cpu_idx, &domain_id, &channel_id);
+ ret = scmi_pwr_state_set(scmi_handles[channel_id],
+ domain_id, scmi_pwr_state);
+ if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) {
+ sbi_printf("SCMI set power state command return 0x%x unexpected\n",
+ ret);
+ sbi_hart_hang();
+ }
+}
+
+/*
+ * Helper function to turn ON a CPU power domain and its parent power domains
+ * if applicable.
+ */
+void css_scp_on(u_register_t mpidr)
+{
+ unsigned int lvl = 0, channel_id, core_pos, domain_id;
+ int ret;
+ uint32_t scmi_pwr_state = 0;
+
+ core_pos = plat_core_pos_by_mpidr(mpidr);
+ if (core_pos >= PLATFORM_CORE_COUNT) {
+ sbi_printf("%s:%d, node_idx beyond the boundary\n",
+ __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ for (; lvl <= PLAT_MAX_PWR_LVL; lvl++)
+ SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl,
+ scmi_power_state_on);
+
+ SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1);
+
+ css_scp_core_pos_to_scmi_channel(core_pos, &domain_id,
+ &channel_id);
+ ret = scmi_pwr_state_set(scmi_handles[channel_id],
+ domain_id, scmi_pwr_state);
+ if (ret != SCMI_E_QUEUED && ret != SCMI_E_SUCCESS) {
+ sbi_printf("SCMI set power state command return 0x%x unexpected\n",
+ ret);
+ sbi_hart_hang();
+ }
+}
+
+/*
+ * Helper function to get the power state of a power domain node as reported
+ * by the SCP.
+ */
+int css_scp_get_power_state(u_register_t mpidr, unsigned int power_level)
+{
+ int ret;
+ uint32_t scmi_pwr_state = 0, lvl_state;
+ unsigned int channel_id, cpu_idx, domain_id;
+
+ cpu_idx = plat_core_pos_by_mpidr(mpidr);
+
+ if (cpu_idx >= PLATFORM_CORE_COUNT) {
+ sbi_printf("%s:%d, node_idx beyond the boundary\n",
+ __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ /* We don't support get power state at the system power domain level */
+ if ((power_level > PLAT_MAX_PWR_LVL) ||
+ (power_level == CSS_SYSTEM_PWR_DMN_LVL)) {
+ sbi_printf("Invalid power level %u specified for SCMI get power state\n",
+ power_level);
+ return PSCI_E_INVALID_PARAMS;
+ }
+
+ css_scp_core_pos_to_scmi_channel(cpu_idx, &domain_id, &channel_id);
+ ret = scmi_pwr_state_get(scmi_handles[channel_id],
+ domain_id, &scmi_pwr_state);
+
+ if (ret != SCMI_E_SUCCESS) {
+ sbi_printf("SCMI get power state command return 0x%x unexpected\n",
+ ret);
+ return PSCI_E_INVALID_PARAMS;
+ }
+
+ /*
+ * Find the maximum power level described in the get power state
+ * command. If it is less than the requested power level, then assume
+ * the requested power level is ON.
+ */
+ if (SCMI_GET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state) < power_level)
+ return HW_ON;
+
+ lvl_state = SCMI_GET_PWR_STATE_LVL(scmi_pwr_state, power_level);
+ if (lvl_state == scmi_power_state_on)
+ return HW_ON;
+
+ if ((lvl_state != scmi_power_state_off) &&
+ (lvl_state != scmi_power_state_sleep)) {
+ sbi_printf("wrong power state, :%d\n", ret);
+ sbi_hart_hang();
+
+ }
+
+ return HW_OFF;
+}
+
+void plat_arm_pwrc_setup(void)
+{
+ unsigned int composite_id, idx, cpu_idx;
+ unsigned int hartid = current_hartid();
+
+ cpu_idx = plat_core_pos_by_mpidr(hartid);
+
+ for (idx = 0; idx < PLAT_ARM_SCMI_CHANNEL_COUNT; idx++) {
+ sbi_printf("Initializing SCMI driver on channel %d\n", idx);
+
+ scmi_channels[idx].info = plat_css_get_scmi_info(idx);
+ scmi_channels[idx].lock = ARM_SCMI_LOCK_GET_INSTANCE;
+ scmi_handles[idx] = scmi_init(&scmi_channels[idx]);
+
+ if (scmi_handles[idx] == NULL) {
+ sbi_printf("SCMI Initialization failed on channel %d\n", idx);
+ sbi_hart_hang();
+ }
+ }
+
+ unsigned int *map_id = plat_get_power_domain_tree_desc()[CLUSTER_INDEX_IN_CPU_TOPOLOGY] > 1 ?
+ plat_css_core_pos_to_scmi_dmn_id_map[1] :
+ plat_css_core_pos_to_scmi_dmn_id_map[0];
+
+ composite_id = map_id[cpu_idx];
+ default_scmi_channel_id = GET_SCMI_CHANNEL_ID(composite_id);
+}
+
+/******************************************************************************
+ * This function overrides the default definition for ARM platforms. Initialize
+ * the SCMI driver, query capability via SCMI and modify the PSCI capability
+ * based on that.
+ *****************************************************************************/
+const plat_psci_ops_t *css_scmi_override_pm_ops(plat_psci_ops_t *ops)
+{
+ uint32_t msg_attr;
+ int ret;
+ void *scmi_handle = scmi_handles[default_scmi_channel_id];
+
+ /* Check that power domain POWER_STATE_SET message is supported */
+ ret = scmi_proto_msg_attr(scmi_handle, SCMI_PWR_DMN_PROTO_ID,
+ SCMI_PWR_STATE_SET_MSG, &msg_attr);
+ if (ret != SCMI_E_SUCCESS) {
+ sbi_printf("Set power state command is not supported by SCMI\n");
+ sbi_hart_hang();
+ }
+
+ /*
+ * Don't support PSCI NODE_HW_STATE call if SCMI doesn't support
+ * POWER_STATE_GET message.
+ */
+ ret = scmi_proto_msg_attr(scmi_handle, SCMI_PWR_DMN_PROTO_ID,
+ SCMI_PWR_STATE_GET_MSG, &msg_attr);
+ if (ret != SCMI_E_SUCCESS)
+ ops->get_node_hw_state = NULL;
+
+ /* Check if the SCMI SYSTEM_POWER_STATE_SET message is supported */
+ ret = scmi_proto_msg_attr(scmi_handle, SCMI_SYS_PWR_PROTO_ID,
+ SCMI_SYS_PWR_STATE_SET_MSG, &msg_attr);
+ if (ret != SCMI_E_SUCCESS) {
+ /* System power management operations are not supported */
+ ops->system_off = NULL;
+ ops->system_reset = NULL;
+ ops->get_sys_suspend_power_state = NULL;
+ } else {
+ if (!(msg_attr & SCMI_SYS_PWR_SUSPEND_SUPPORTED)) {
+ /*
+ * System power management protocol is available, but
+ * it does not support SYSTEM SUSPEND.
+ */
+ ops->get_sys_suspend_power_state = NULL;
+ }
+ if (!(msg_attr & SCMI_SYS_PWR_WARM_RESET_SUPPORTED)) {
+ /*
+ * WARM reset is not available.
+ */
+ ops->system_reset2 = NULL;
+ }
+ }
+
+ return ops;
+}
+
+/*
+ * Helper function to suspend a CPU power domain and its parent power domains
+ * if applicable.
+ */
+void css_scp_suspend(const struct psci_power_state *target_state)
+{
+ int ret;
+ unsigned int curr_hart = current_hartid();
+
+ unsigned int core_pos = plat_core_pos_by_mpidr(curr_hart);
+ if (core_pos >= PLATFORM_CORE_COUNT) {
+ sbi_printf("%s:%d, node_idx beyond the boundary\n",
+ __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+
+ /* At least power domain level 0 should be specified to be suspended */
+ if (target_state->pwr_domain_state[ARM_PWR_LVL0] !=
+ ARM_LOCAL_STATE_OFF) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ /* Check if power down at system power domain level is requested */
+ if (css_system_pwr_state(target_state) == ARM_LOCAL_STATE_OFF) {
+ /* Issue SCMI command for SYSTEM_SUSPEND on all SCMI channels */
+ ret = scmi_sys_pwr_state_set(
+ scmi_handles[default_scmi_channel_id],
+ SCMI_SYS_PWR_FORCEFUL_REQ, SCMI_SYS_PWR_SUSPEND);
+ if (ret != SCMI_E_SUCCESS) {
+ sbi_printf("SCMI system power domain suspend return 0x%x unexpected\n",
+ ret);
+ sbi_hart_hang();
+ }
+ return;
+ }
+
+ unsigned int lvl, channel_id, domain_id;
+ uint32_t scmi_pwr_state = 0;
+ /*
+ * If we reach here, then assert that power down at system power domain
+ * level is running.
+ */
+ if (css_system_pwr_state(target_state) != ARM_LOCAL_STATE_RUN) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ /* For level 0, specify `scmi_power_state_sleep` as the power state */
+ SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, ARM_PWR_LVL0,
+ scmi_power_state_sleep);
+
+ for (lvl = ARM_PWR_LVL1; lvl <= PLAT_MAX_PWR_LVL; lvl++) {
+ if (target_state->pwr_domain_state[lvl] == ARM_LOCAL_STATE_RUN)
+ break;
+
+ if (target_state->pwr_domain_state[lvl] !=
+ ARM_LOCAL_STATE_OFF) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+ /*
+ * Specify `scmi_power_state_off` as power state for higher
+ * levels.
+ */
+ SCMI_SET_PWR_STATE_LVL(scmi_pwr_state, lvl,
+ scmi_power_state_off);
+ }
+
+ SCMI_SET_PWR_STATE_MAX_PWR_LVL(scmi_pwr_state, lvl - 1);
+
+ css_scp_core_pos_to_scmi_channel(core_pos,
+ &domain_id, &channel_id);
+ ret = scmi_pwr_state_set(scmi_handles[channel_id],
+ domain_id, scmi_pwr_state);
+
+ if (ret != SCMI_E_SUCCESS) {
+ sbi_printf("SCMI set power state command return 0x%x unexpected\n",
+ ret);
+ sbi_hart_hang();
+ }
+}
diff --git a/lib/utils/arm_scmi/objects.mk b/lib/utils/arm_scmi/objects.mk
new file mode 100644
index 0000000..532e070
--- /dev/null
+++ b/lib/utils/arm_scmi/objects.mk
@@ -0,0 +1,24 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Western Digital Corporation or its affiliates.
+#
+# Authors:
+# Anup Patel <anup.patel@wdc.com>
+#
+
+libsbiutils-objs-$(CONFIG_ARM_SCMI_PROTOCOL_SUPPORT) += arm_scmi/common/arm_pm.o
+
+libsbiutils-objs-$(CONFIG_ARM_SCMI_PROTOCOL_SUPPORT) += arm_scmi/css/common/css_pm.o
+
+libsbiutils-objs-$(CONFIG_ARM_SCMI_PROTOCOL_SUPPORT) += arm_scmi/css/scmi/scmi_common.o
+
+libsbiutils-objs-$(CONFIG_ARM_SCMI_PROTOCOL_SUPPORT) += arm_scmi/css/scmi/scmi_pwr_dmn_proto.o
+
+libsbiutils-objs-$(CONFIG_ARM_SCMI_PROTOCOL_SUPPORT) += arm_scmi/css/scmi/scmi_sys_pwr_proto.o
+
+libsbiutils-objs-$(CONFIG_ARM_SCMI_PROTOCOL_SUPPORT) += arm_scmi/css/scp/css_pm_scmi.o
+
+libsbiutils-objs-$(CONFIG_ARM_SCMI_PROTOCOL_SUPPORT) += arm_scmi/css/mhu/css_mhu_doorbell.o
+
+libsbiutils-objs-$(CONFIG_ARM_SCMI_PROTOCOL_SUPPORT) += arm_scmi/board/spacemit/spacemit_pm.o
diff --git a/lib/utils/cci/bus-cci.c b/lib/utils/cci/bus-cci.c
new file mode 100644
index 0000000..7fe11d0
--- /dev/null
+++ b/lib/utils/cci/bus-cci.c
@@ -0,0 +1,168 @@
+/*
+ * Copyright (c) 2015-2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_console.h>
+
+
+/* Slave interface offsets from PERIPHBASE */
+#define SLAVE_IFACE6_OFFSET (0x7000UL)
+#define SLAVE_IFACE5_OFFSET (0x6000UL)
+#define SLAVE_IFACE4_OFFSET (0x5000UL)
+#define SLAVE_IFACE3_OFFSET (0x4000UL)
+#define SLAVE_IFACE2_OFFSET (0x3000UL)
+#define SLAVE_IFACE1_OFFSET (0x2000UL)
+#define SLAVE_IFACE0_OFFSET (0x1000UL)
+#define SLAVE_IFACE_OFFSET(index) (SLAVE_IFACE0_OFFSET + \
+ ((0x1000UL) * (index)))
+
+/* Slave interface event and count register offsets from PERIPHBASE */
+#define EVENT_SELECT7_OFFSET (0x80000UL)
+#define EVENT_SELECT6_OFFSET (0x70000UL)
+#define EVENT_SELECT5_OFFSET (0x60000UL)
+#define EVENT_SELECT4_OFFSET (0x50000UL)
+#define EVENT_SELECT3_OFFSET (0x40000UL)
+#define EVENT_SELECT2_OFFSET (0x30000UL)
+#define EVENT_SELECT1_OFFSET (0x20000UL)
+#define EVENT_SELECT0_OFFSET (0x10000UL)
+#define EVENT_OFFSET(index) (EVENT_SELECT0_OFFSET + \
+ ((0x10000UL) * (index)))
+
+/* Control and ID register offsets */
+#define CTRL_OVERRIDE_REG (0x0U)
+#define SECURE_ACCESS_REG (0x8U)
+#define STATUS_REG (0xcU)
+#define IMPRECISE_ERR_REG (0x10U)
+#define PERFMON_CTRL_REG (0x100U)
+#define IFACE_MON_CTRL_REG (0x104U)
+
+/* Component and peripheral ID registers */
+#define PERIPHERAL_ID0 (0xFE0U)
+#define PERIPHERAL_ID1 (0xFE4U)
+#define PERIPHERAL_ID2 (0xFE8U)
+#define PERIPHERAL_ID3 (0xFECU)
+#define PERIPHERAL_ID4 (0xFD0U)
+#define PERIPHERAL_ID5 (0xFD4U)
+#define PERIPHERAL_ID6 (0xFD8U)
+#define PERIPHERAL_ID7 (0xFDCU)
+
+#define COMPONENT_ID0 (0xFF0U)
+#define COMPONENT_ID1 (0xFF4U)
+#define COMPONENT_ID2 (0xFF8U)
+#define COMPONENT_ID3 (0xFFCU)
+#define COMPONENT_ID4 (0x1000U)
+#define COMPONENT_ID5 (0x1004U)
+#define COMPONENT_ID6 (0x1008U)
+#define COMPONENT_ID7 (0x100CU)
+
+/* Slave interface register offsets */
+#define SNOOP_CTRL_REG (0x0U)
+#define SH_OVERRIDE_REG (0x4U)
+#define READ_CHNL_QOS_VAL_OVERRIDE_REG (0x100U)
+#define WRITE_CHNL_QOS_VAL_OVERRIDE_REG (0x104U)
+#define MAX_OT_REG (0x110U)
+
+/* Snoop Control register bit definitions */
+#define DVM_EN_BIT (1U<<1)
+#define SNOOP_EN_BIT (1U<<0)
+#define SUPPORT_SNOOPS (1U<<30)
+#define SUPPORT_DVM (1U<<31)
+
+/* Status register bit definitions */
+#define CHANGE_PENDING_BIT (1U<<0)
+
+/* Event and count register offsets */
+#define EVENT_SELECT_REG (0x0U)
+#define EVENT_COUNT_REG (0x4U)
+#define COUNT_CNTRL_REG (0x8U)
+#define COUNT_OVERFLOW_REG (0xCU)
+
+/* Slave interface monitor registers */
+#define INT_MON_REG_SI0 (0x90000U)
+#define INT_MON_REG_SI1 (0x90004U)
+#define INT_MON_REG_SI2 (0x90008U)
+#define INT_MON_REG_SI3 (0x9000CU)
+#define INT_MON_REG_SI4 (0x90010U)
+#define INT_MON_REG_SI5 (0x90014U)
+#define INT_MON_REG_SI6 (0x90018U)
+
+/* Master interface monitor registers */
+#define INT_MON_REG_MI0 (0x90100U)
+#define INT_MON_REG_MI1 (0x90104U)
+#define INT_MON_REG_MI2 (0x90108U)
+#define INT_MON_REG_MI3 (0x9010cU)
+#define INT_MON_REG_MI4 (0x90110U)
+#define INT_MON_REG_MI5 (0x90114U)
+
+#define SLAVE_IF_UNUSED (-1)
+
+#define MAKE_CCI_PART_NUMBER(hi, lo) (((hi) << 8) | (lo))
+#define CCI_PART_LO_MASK (0xffU)
+#define CCI_PART_HI_MASK (0xfU)
+
+/* CCI part number codes read from Peripheral ID registers 0 and 1 */
+#define CCI400_PART_NUM (0x420)
+#define CCI500_PART_NUM (0x422)
+#define CCI550_PART_NUM (0x423)
+
+#define CCI400_SLAVE_PORTS (5)
+#define CCI500_SLAVE_PORTS (7)
+#define CCI550_SLAVE_PORTS (7)
+
+static void *cci_base;
+static const int *cci_slave_if_map;
+
+
+void cci_init(u32 base, const int *map, unsigned int num_cci_masters)
+{
+ cci_base = (void *)(u64)base;
+ cci_slave_if_map = map;
+}
+
+void cci_enable_snoop_dvm_reqs(unsigned int master_id)
+{
+ int slave_if_id = cci_slave_if_map[master_id];
+
+ /*
+ * Enable Snoops and DVM messages, no need for Read/Modify/Write as
+ * rest of bits are write ignore
+ */
+ writel(DVM_EN_BIT | SNOOP_EN_BIT, cci_base +
+ SLAVE_IFACE_OFFSET(slave_if_id) + SNOOP_CTRL_REG);
+
+ /*
+ * 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(cci_base + STATUS_REG) & CHANGE_PENDING_BIT) != 0U)
+ ;
+}
+
+void cci_disable_snoop_dvm_reqs(unsigned int master_id)
+{
+ int slave_if_id = cci_slave_if_map[master_id];
+
+ /*
+ * Disable Snoops and DVM messages, no need for Read/Modify/Write as
+ * rest of bits are write ignore.
+ */
+ writel(~(DVM_EN_BIT | SNOOP_EN_BIT), cci_base +
+ SLAVE_IFACE_OFFSET(slave_if_id) + SNOOP_CTRL_REG);
+
+ /*
+ * 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(cci_base + STATUS_REG) & CHANGE_PENDING_BIT) != 0U)
+ ;
+}
+
diff --git a/lib/utils/cci/objects.mk b/lib/utils/cci/objects.mk
new file mode 100644
index 0000000..08aac67
--- /dev/null
+++ b/lib/utils/cci/objects.mk
@@ -0,0 +1,7 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (C) 2020 Bin Meng <bmeng.cn@gmail.com>
+#
+
+libsbiutils-objs-y += cci/bus-cci.o
diff --git a/lib/utils/ipi/aclint_mswi.c b/lib/utils/ipi/aclint_mswi.c
index f47b3bc..6342030 100644
--- a/lib/utils/ipi/aclint_mswi.c
+++ b/lib/utils/ipi/aclint_mswi.c
@@ -15,6 +15,7 @@
#include <sbi/sbi_ipi.h>
#include <sbi/sbi_scratch.h>
#include <sbi/sbi_timer.h>
+#include <sbi/sbi_platform.h>
#include <sbi_utils/ipi/aclint_mswi.h>
static unsigned long mswi_ptr_offset;
@@ -84,6 +85,7 @@ int aclint_mswi_cold_init(struct aclint_mswi_data *mswi)
struct sbi_scratch *scratch;
unsigned long pos, region_size;
struct sbi_domain_memregion reg;
+ const struct sbi_platform *sbi = sbi_platform_thishart_ptr();
/* Sanity checks */
if (!mswi || (mswi->addr & (ACLINT_MSWI_ALIGN - 1)) ||
@@ -100,7 +102,7 @@ int aclint_mswi_cold_init(struct aclint_mswi_data *mswi)
/* Update MSWI pointer in scratch space */
for (i = 0; i < mswi->hart_count; i++) {
- scratch = sbi_hartid_to_scratch(mswi->first_hartid + i);
+ scratch = sbi_hartid_to_scratch(sbi->hart_index2id[i]);
if (!scratch)
return SBI_ENOENT;
mswi_set_hart_data_ptr(scratch, mswi);
diff --git a/lib/utils/irqchip/fdt_irqchip_plic.c b/lib/utils/irqchip/fdt_irqchip_plic.c
index 829c5ee..0a2d61b 100644
--- a/lib/utils/irqchip/fdt_irqchip_plic.c
+++ b/lib/utils/irqchip/fdt_irqchip_plic.c
@@ -85,6 +85,11 @@ static int irqchip_plic_warm_init(void)
plic_get_hart_scontext(scratch));
}
+void fdt_plic_context_exit(void)
+{
+ irqchip_plic_warm_init();
+}
+
static int irqchip_plic_update_hartid_table(void *fdt, int nodeoff,
struct plic_data *pd)
{
diff --git a/lib/utils/psci/Kconfig b/lib/utils/psci/Kconfig
new file mode 100644
index 0000000..a009597
--- /dev/null
+++ b/lib/utils/psci/Kconfig
@@ -0,0 +1,21 @@
+# SPDX-License-Identifier: BSD-2-Clause
+
+menu "ARM's power management framework Support"
+
+config ARM_PSCI_SUPPORT
+ bool "Support psci protocol"
+ default n
+
+if ARM_PSCI_SUPPORT
+
+config ARM_SCMI_PROTOCOL_SUPPORT
+ bool "Using r-core and arm's scmi protocol to dealing with the pwr management"
+ default n
+
+config ARM_NON_SCMI_SUPPORT
+ bool "dealing with the pwr management in Machine mode-opensbi"
+ default n
+
+endif
+
+endmenu
diff --git a/lib/utils/psci/objects.mk b/lib/utils/psci/objects.mk
new file mode 100644
index 0000000..6170680
--- /dev/null
+++ b/lib/utils/psci/objects.mk
@@ -0,0 +1,30 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (c) 2020 Western Digital Corporation or its affiliates.
+#
+# Authors:
+# Anup Patel <anup.patel@wdc.com>
+#
+
+libsbiutils-objs-$(CONFIG_ARM_PSCI_SUPPORT) += psci/psci_common.o
+
+libsbiutils-objs-$(CONFIG_ARM_PSCI_SUPPORT) += psci/psci_setup.o
+
+libsbiutils-objs-$(CONFIG_ARM_PSCI_SUPPORT) += psci/psci_main.o
+
+libsbiutils-objs-$(CONFIG_ARM_PSCI_SUPPORT) += psci/psci_on.o
+
+libsbiutils-objs-$(CONFIG_ARM_PSCI_SUPPORT) += psci/psci_off.o
+
+libsbiutils-objs-${CONFIG_ARM_PSCI_SUPPORT} += psci/psci_suspend.o
+
+libsbiutils-objs-$(CONFIG_ARM_PSCI_SUPPORT) += psci/spacemit/spacemit_topology.o
+
+ifeq ($(CONFIG_ARM_NON_SCMI_SUPPORT), y)
+# common
+libsbiutils-objs-$(CONFIG_ARM_NON_SCMI_SUPPORT) += psci/spacemit/plat/plat_pm.o
+
+# platform
+libsbiutils-objs-$(CONFIG_PLATFORM_SPACEMIT_K1X) += psci/spacemit/plat/k1x/underly_implement.o
+endif
diff --git a/lib/utils/psci/psci_common.c b/lib/utils/psci/psci_common.c
new file mode 100644
index 0000000..f4b4bee
--- /dev/null
+++ b/lib/utils/psci/psci_common.c
@@ -0,0 +1,872 @@
+/*
+ * Copyright (c) 2013-2022, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+#include <sbi_utils/psci/psci.h>
+#include <sbi_utils/cache/cacheflush.h>
+#include <sbi_utils/psci/plat/common/platform.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_console.h>
+#include <spacemit/spacemit_config.h>
+#include "psci_private.h"
+
+/*
+ * PSCI requested local power state map. This array is used to store the local
+ * power states requested by a CPU for power levels from level 1 to
+ * PLAT_MAX_PWR_LVL. It does not store the requested local power state for power
+ * level 0 (PSCI_CPU_PWR_LVL) as the requested and the target power state for a
+ * CPU are the same.
+ *
+ * During state coordination, the platform is passed an array containing the
+ * local states requested for a particular non cpu power domain by each cpu
+ * within the domain.
+ *
+ * TODO: Dense packing of the requested states will cause cache thrashing
+ * when multiple power domains write to it. If we allocate the requested
+ * states at each power level in a cache-line aligned per-domain memory,
+ * the cache thrashing can be avoided.
+ */
+static plat_local_state_t
+ /* psci_req_local_pwr_states[PLAT_MAX_PWR_LVL][PLATFORM_CORE_COUNT] */
+ psci_req_local_pwr_states[PLAT_MAX_PWR_LVL][CACHE_LINE_SIZE] __attribute__((aligned(CACHE_LINE_SIZE)));
+
+unsigned int psci_plat_core_count;
+
+unsigned long psci_delta_off;
+
+/*******************************************************************************
+ * Arrays that hold the platform's power domain tree information for state
+ * management of power domains.
+ * Each node in the array 'psci_non_cpu_pd_nodes' corresponds to a power domain
+ * which is an ancestor of a CPU power domain.
+ * Each node in the array 'psci_cpu_pd_nodes' corresponds to a cpu power domain
+ ******************************************************************************/
+non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS];
+
+/* Lock for PSCI state coordination */
+DEFINE_PSCI_LOCK(psci_locks[PSCI_NUM_NON_CPU_PWR_DOMAINS]);
+
+cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT];
+
+/*******************************************************************************
+ * Pointer to functions exported by the platform to complete power mgmt. ops
+ ******************************************************************************/
+const plat_psci_ops_t *psci_plat_pm_ops;
+
+/*
+ * The plat_local_state used by the platform is one of these types: RUN,
+ * RETENTION and OFF. The platform can define further sub-states for each type
+ * apart from RUN. This categorization is done to verify the sanity of the
+ * psci_power_state passed by the platform and to print debug information. The
+ * categorization is done on the basis of the following conditions:
+ *
+ * 1. If (plat_local_state == 0) then the category is STATE_TYPE_RUN.
+ *
+ * 2. If (0 < plat_local_state <= PLAT_MAX_RET_STATE), then the category is
+ * STATE_TYPE_RETN.
+ *
+ * 3. If (plat_local_state > PLAT_MAX_RET_STATE), then the category is
+ * STATE_TYPE_OFF.
+ */
+typedef enum plat_local_state_type {
+ STATE_TYPE_RUN = 0,
+ STATE_TYPE_RETN,
+ STATE_TYPE_OFF
+} plat_local_state_type_t;
+
+/* Function used to categorize plat_local_state. */
+plat_local_state_type_t find_local_state_type(plat_local_state_t state)
+{
+ if (state != 0U) {
+ if (state > PLAT_MAX_RET_STATE) {
+ return STATE_TYPE_OFF;
+ } else {
+ return STATE_TYPE_RETN;
+ }
+ } else {
+ return STATE_TYPE_RUN;
+ }
+}
+
+/*******************************************************************************
+ * PSCI helper function to get the parent nodes corresponding to a cpu_index.
+ ******************************************************************************/
+void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx,
+ unsigned int end_lvl,
+ unsigned int *node_index)
+{
+ unsigned int parent_node = psci_cpu_pd_nodes[cpu_idx].parent_node;
+ unsigned int i;
+ unsigned int *node = node_index;
+
+ for (i = PSCI_CPU_PWR_LVL + 1U; i <= end_lvl; i++) {
+ *node = parent_node;
+ node++;
+ parent_node = psci_non_cpu_pd_nodes[parent_node].parent_node;
+ }
+}
+
+/******************************************************************************
+ * This function initializes the psci_req_local_pwr_states.
+ *****************************************************************************/
+void psci_init_req_local_pwr_states(void)
+{
+ /* Initialize the requested state of all non CPU power domains as OFF */
+ unsigned int pwrlvl;
+ unsigned int core;
+
+ for (pwrlvl = 0U; pwrlvl < PLAT_MAX_PWR_LVL; pwrlvl++) {
+ for (core = 0; core < psci_plat_core_count; core++) {
+ psci_req_local_pwr_states[pwrlvl][core] =
+ PLAT_MAX_OFF_STATE;
+ }
+ csi_dcache_clean_invalid_range(
+ (uintptr_t) psci_req_local_pwr_states[pwrlvl],
+ CACHE_LINE_SIZE);
+ }
+}
+
+void set_non_cpu_pd_node_local_state(unsigned int parent_idx,
+ plat_local_state_t state)
+{
+ psci_non_cpu_pd_nodes[parent_idx].local_state = state;
+ csi_dcache_clean_invalid_range(
+ (uintptr_t) &psci_non_cpu_pd_nodes[parent_idx],
+ sizeof(psci_non_cpu_pd_nodes[parent_idx]));
+}
+
+/******************************************************************************
+ * Helper function to update the requested local power state array. This array
+ * does not store the requested state for the CPU power level. Hence an
+ * assertion is added to prevent us from accessing the CPU power level.
+ *****************************************************************************/
+void psci_set_req_local_pwr_state(unsigned int pwrlvl,
+ unsigned int cpu_idx,
+ plat_local_state_t req_pwr_state)
+{
+ if ((pwrlvl > PSCI_CPU_PWR_LVL) && (pwrlvl <= PLAT_MAX_PWR_LVL) &&
+ (cpu_idx < psci_plat_core_count)) {
+ psci_req_local_pwr_states[pwrlvl - 1U][cpu_idx] = req_pwr_state;
+ csi_dcache_clean_invalid_range(
+ (uintptr_t) psci_req_local_pwr_states[pwrlvl - 1U],
+ CACHE_LINE_SIZE);
+ }
+}
+
+/******************************************************************************
+ * Helper function to set the target local power state that each power domain
+ * from the current cpu power domain to its ancestor at the 'end_pwrlvl' will
+ * enter. This function will be called after coordination of requested power
+ * states has been done for each power level.
+ *****************************************************************************/
+static void psci_set_target_local_pwr_states(unsigned int end_pwrlvl,
+ const psci_power_state_t *target_state)
+{
+ unsigned int parent_idx, lvl;
+ psci_cpu_data_t *svc_cpu_data;
+ const plat_local_state_t *pd_state = target_state->pwr_domain_state;
+ unsigned int hartid = current_hartid();
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ psci_set_cpu_local_state(pd_state[PSCI_CPU_PWR_LVL]);
+
+ /*
+ * Need to flush as local_state might be accessed with Data Cache
+ * disabled during power on
+ */
+ csi_dcache_clean_invalid_range((uintptr_t)&svc_cpu_data->local_state, sizeof(plat_local_state_t));
+
+ parent_idx = psci_cpu_pd_nodes[plat_core_pos_by_mpidr(hartid)].parent_node;
+
+ /* Copy the local_state from state_info */
+ for (lvl = 1U; lvl <= end_pwrlvl; lvl++) {
+ set_non_cpu_pd_node_local_state(parent_idx, pd_state[lvl]);
+ parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node;
+ }
+}
+
+/******************************************************************************
+ * Helper function to return a reference to an array containing the local power
+ * states requested by each cpu for a power domain at 'pwrlvl'. The size of the
+ * array will be the number of cpu power domains of which this power domain is
+ * an ancestor. These requested states will be used to determine a suitable
+ * target state for this power domain during psci state coordination. An
+ * assertion is added to prevent us from accessing the CPU power level.
+ *****************************************************************************/
+static plat_local_state_t *psci_get_req_local_pwr_states(unsigned int pwrlvl,
+ unsigned int cpu_idx)
+{
+ if (pwrlvl <= PSCI_CPU_PWR_LVL) {
+ sbi_printf("%s:%d, err\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ if ((pwrlvl > PSCI_CPU_PWR_LVL) && (pwrlvl <= PLAT_MAX_PWR_LVL) &&
+ (cpu_idx < psci_plat_core_count)) {
+ return &psci_req_local_pwr_states[pwrlvl - 1U][cpu_idx];
+ } else
+ return NULL;
+}
+
+/*
+ * Helper functions to get/set the fields of PSCI per-cpu data.
+ */
+void psci_set_aff_info_state(aff_info_state_t aff_state)
+{
+ psci_cpu_data_t *svc_cpu_data;
+ unsigned int hartid = current_hartid();
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ svc_cpu_data->aff_info_state = aff_state;
+}
+
+aff_info_state_t psci_get_aff_info_state(void)
+{
+ psci_cpu_data_t *svc_cpu_data;
+ unsigned int hartid = current_hartid();
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ return svc_cpu_data->aff_info_state;
+}
+
+aff_info_state_t psci_get_aff_info_state_by_idx(unsigned int idx)
+{
+ psci_cpu_data_t *svc_cpu_data;
+ const struct sbi_platform *sbi = sbi_platform_thishart_ptr();
+ unsigned int hartid = sbi->hart_index2id[idx];
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ return svc_cpu_data->aff_info_state;
+}
+
+void psci_set_aff_info_state_by_idx(unsigned int idx,
+ aff_info_state_t aff_state)
+{
+ psci_cpu_data_t *svc_cpu_data;
+ const struct sbi_platform *sbi = sbi_platform_thishart_ptr();
+ unsigned int hartid = sbi->hart_index2id[idx];
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ svc_cpu_data->aff_info_state = aff_state;
+}
+
+void psci_set_cpu_local_state(plat_local_state_t state)
+{
+ psci_cpu_data_t *svc_cpu_data;
+ unsigned int hartid = current_hartid();
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ svc_cpu_data->local_state = state;
+}
+
+void psci_set_suspend_pwrlvl(unsigned int target_lvl)
+{
+ psci_cpu_data_t *svc_cpu_data;
+ unsigned int hartid = current_hartid();
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ svc_cpu_data->target_pwrlvl = target_lvl;
+}
+
+static inline plat_local_state_t psci_get_cpu_local_state_by_idx(
+ unsigned int idx)
+{
+ psci_cpu_data_t *svc_cpu_data;
+ const struct sbi_platform *sbi = sbi_platform_thishart_ptr();
+ unsigned int hartid = sbi->hart_index2id[idx];
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ return svc_cpu_data->local_state;
+}
+
+static inline plat_local_state_t psci_get_cpu_local_state(void)
+{
+ psci_cpu_data_t *svc_cpu_data;
+ unsigned int hartid = current_hartid();
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ return svc_cpu_data->local_state;
+}
+
+/******************************************************************************
+ * This function is invoked post CPU power up and initialization. It sets the
+ * affinity info state, target power state and requested power state for the
+ * current CPU and all its ancestor power domains to RUN.
+ *****************************************************************************/
+void psci_set_pwr_domains_to_run(unsigned int end_pwrlvl)
+{
+ unsigned int parent_idx, lvl;
+ unsigned int cpu_idx;
+ psci_cpu_data_t *svc_cpu_data;
+ unsigned int hartid = current_hartid();
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ cpu_idx = plat_core_pos_by_mpidr(hartid);
+
+ parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node;
+
+ /* Reset the local_state to RUN for the non cpu power domains. */
+ for (lvl = PSCI_CPU_PWR_LVL + 1U; lvl <= end_pwrlvl; lvl++) {
+ set_non_cpu_pd_node_local_state(parent_idx,
+ PSCI_LOCAL_STATE_RUN);
+ psci_set_req_local_pwr_state(lvl,
+ cpu_idx,
+ PSCI_LOCAL_STATE_RUN);
+ parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node;
+ }
+
+ /* Set the affinity info state to ON */
+ psci_set_aff_info_state(AFF_STATE_ON);
+
+ psci_set_cpu_local_state(PSCI_LOCAL_STATE_RUN);
+
+ csi_dcache_clean_invalid_range((uintptr_t)svc_cpu_data, sizeof(psci_cpu_data_t));
+}
+
+/*******************************************************************************
+ * This function prints the state of all power domains present in the
+ * system
+ ******************************************************************************/
+void psci_print_power_domain_map(void)
+{
+ unsigned int idx;
+ plat_local_state_t state;
+ plat_local_state_type_t state_type;
+
+ /* This array maps to the PSCI_STATE_X definitions in psci.h */
+ static const char * const psci_state_type_str[] = {
+ "ON",
+ "RETENTION",
+ "OFF",
+ };
+
+ sbi_printf("PSCI Power Domain Map:\n");
+ for (idx = 0; idx < (PSCI_NUM_PWR_DOMAINS - psci_plat_core_count);
+ idx++) {
+ state_type = find_local_state_type(
+ psci_non_cpu_pd_nodes[idx].local_state);
+ sbi_printf(" Domain Node : Level %u, parent_node %u,"
+ " State %s (0x%x)\n",
+ psci_non_cpu_pd_nodes[idx].level,
+ psci_non_cpu_pd_nodes[idx].parent_node,
+ psci_state_type_str[state_type],
+ psci_non_cpu_pd_nodes[idx].local_state);
+ }
+
+ for (idx = 0; idx < psci_plat_core_count; idx++) {
+ state = psci_get_cpu_local_state_by_idx(idx);
+ state_type = find_local_state_type(state);
+ sbi_printf(" CPU Node : MPID 0x%llx, parent_node %u,"
+ " State %s (0x%x)\n",
+ (unsigned long long)psci_cpu_pd_nodes[idx].mpidr,
+ psci_cpu_pd_nodes[idx].parent_node,
+ psci_state_type_str[state_type],
+ psci_get_cpu_local_state_by_idx(idx));
+ }
+}
+
+/*******************************************************************************
+ * Simple routine to determine whether a mpidr is valid or not.
+ ******************************************************************************/
+int psci_validate_mpidr(u_register_t mpidr)
+{
+ if (plat_core_pos_by_mpidr(mpidr) < 0)
+ return PSCI_E_INVALID_PARAMS;
+
+ return PSCI_E_SUCCESS;
+}
+
+static unsigned int psci_get_suspend_pwrlvl(void)
+{
+ psci_cpu_data_t *svc_cpu_data;
+ unsigned int hartid = current_hartid();
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ return svc_cpu_data->target_pwrlvl;
+}
+
+/*******************************************************************************
+ * Routine to return the maximum power level to traverse to after a cpu has
+ * been physically powered up. It is expected to be called immediately after
+ * reset from assembler code.
+ ******************************************************************************/
+static unsigned int get_power_on_target_pwrlvl(void)
+{
+ unsigned int pwrlvl;
+
+ /*
+ * Assume that this cpu was suspended and retrieve its target power
+ * level. If it is invalid then it could only have been turned off
+ * earlier. PLAT_MAX_PWR_LVL will be the highest power level a
+ * cpu can be turned off to.
+ */
+ pwrlvl = psci_get_suspend_pwrlvl();
+ if (pwrlvl == PSCI_INVALID_PWR_LVL)
+ pwrlvl = PLAT_MAX_PWR_LVL;
+ if (pwrlvl >= PSCI_INVALID_PWR_LVL) {
+ sbi_printf("%s:%d,\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ return pwrlvl;
+}
+
+/*******************************************************************************
+ * This function is passed the highest level in the topology tree that the
+ * operation should be applied to and a list of node indexes. It picks up locks
+ * from the node index list in order of increasing power domain level in the
+ * range specified.
+ ******************************************************************************/
+void psci_acquire_pwr_domain_locks(unsigned int end_pwrlvl,
+ const unsigned int *parent_nodes)
+{
+ unsigned int parent_idx;
+ unsigned int level;
+
+ /* No locking required for level 0. Hence start locking from level 1 */
+ for (level = PSCI_CPU_PWR_LVL + 1U; level <= end_pwrlvl; level++) {
+ parent_idx = parent_nodes[level - 1U];
+ psci_lock_get(&psci_non_cpu_pd_nodes[parent_idx]);
+ }
+}
+
+/*******************************************************************************
+ * This function is passed the highest level in the topology tree that the
+ * operation should be applied to and a list of node indexes. It releases the
+ * locks in order of decreasing power domain level in the range specified.
+ ******************************************************************************/
+void psci_release_pwr_domain_locks(unsigned int end_pwrlvl,
+ const unsigned int *parent_nodes)
+{
+ unsigned int parent_idx;
+ unsigned int level;
+
+ /* Unlock top down. No unlocking required for level 0. */
+ for (level = end_pwrlvl; level >= (PSCI_CPU_PWR_LVL + 1U); level--) {
+ parent_idx = parent_nodes[level - 1U];
+ psci_lock_release(&psci_non_cpu_pd_nodes[parent_idx]);
+ }
+}
+
+/******************************************************************************
+ * This function finds the highest power level which will be powered down
+ * amongst all the power levels specified in the 'state_info' structure
+ *****************************************************************************/
+unsigned int psci_find_max_off_lvl(const psci_power_state_t *state_info)
+{
+ int i;
+
+ for (i = (int) PLAT_MAX_PWR_LVL; i >= (int) PSCI_CPU_PWR_LVL; i--) {
+ if (is_local_state_off(state_info->pwr_domain_state[i]) != 0)
+ return (unsigned int) i;
+ }
+
+ return PSCI_INVALID_PWR_LVL;
+}
+
+/*
+ * The PSCI generic code uses this API to let the platform participate in state
+ * coordination during a power management operation. It compares the platform
+ * specific local power states requested by each cpu for a given power domain
+ * and returns the coordinated target power state that the domain should
+ * enter. A platform assigns a number to a local power state. This default
+ * implementation assumes that the platform assigns these numbers in order of
+ * increasing depth of the power state i.e. for two power states X & Y, if X < Y
+ * then X represents a shallower power state than Y. As a result, the
+ * coordinated target local power state for a power domain will be the minimum
+ * of the requested local power states.
+ */
+plat_local_state_t plat_get_target_pwr_state(unsigned int lvl,
+ const plat_local_state_t *states,
+ unsigned int ncpu)
+{
+ plat_local_state_t target = PLAT_MAX_OFF_STATE, temp;
+ const plat_local_state_t *st = states;
+ unsigned int n = ncpu;
+
+ if (ncpu <= 0U) {
+ sbi_printf("%s:%d, err\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ do {
+ temp = *st;
+ st++;
+ if (temp < target)
+ target = temp;
+ n--;
+ } while (n > 0U);
+
+ return target;
+}
+
+/*
+ * psci_non_cpu_pd_nodes can be placed either in normal memory or coherent
+ * memory.
+ *
+ * With !USE_COHERENT_MEM, psci_non_cpu_pd_nodes is placed in normal memory,
+ * it's accessed by both cached and non-cached participants. To serve the common
+ * minimum, perform a cache flush before read and after write so that non-cached
+ * participants operate on latest data in main memory.
+ *
+ * When USE_COHERENT_MEM is used, psci_non_cpu_pd_nodes is placed in coherent
+ * memory. With HW_ASSISTED_COHERENCY, all PSCI participants are cache-coherent.
+ * In both cases, no cache operations are required.
+ */
+
+/*
+ * Retrieve local state of non-CPU power domain node from a non-cached CPU,
+ * after any required cache maintenance operation.
+ */
+static plat_local_state_t get_non_cpu_pd_node_local_state(
+ unsigned int parent_idx)
+{
+ return psci_non_cpu_pd_nodes[parent_idx].local_state;
+}
+
+/******************************************************************************
+ * Helper function to return the current local power state of each power domain
+ * from the current cpu power domain to its ancestor at the 'end_pwrlvl'. This
+ * function will be called after a cpu is powered on to find the local state
+ * each power domain has emerged from.
+ *****************************************************************************/
+void psci_get_target_local_pwr_states(unsigned int end_pwrlvl,
+ psci_power_state_t *target_state)
+{
+ unsigned int parent_idx, lvl, cpu_idx;
+ plat_local_state_t *pd_state = target_state->pwr_domain_state;
+ unsigned int hartid = current_hartid();
+
+ cpu_idx = plat_core_pos_by_mpidr(hartid);
+ pd_state[PSCI_CPU_PWR_LVL] = psci_get_cpu_local_state();
+ parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node;
+
+ /* Copy the local power state from node to state_info */
+ for (lvl = PSCI_CPU_PWR_LVL + 1U; lvl <= end_pwrlvl; lvl++) {
+ pd_state[lvl] = get_non_cpu_pd_node_local_state(parent_idx);
+ parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node;
+ }
+
+ /* Set the the higher levels to RUN */
+ for (; lvl <= PLAT_MAX_PWR_LVL; lvl++)
+ target_state->pwr_domain_state[lvl] = PSCI_LOCAL_STATE_RUN;
+}
+
+/*******************************************************************************
+ * Generic handler which is called when a cpu is physically powered on. It
+ * traverses the node information and finds the highest power level powered
+ * off and performs generic, architectural, platform setup and state management
+ * to power on that power level and power levels below it.
+ * e.g. For a cpu that's been powered on, it will call the platform specific
+ * code to enable the gic cpu interface and for a cluster it will enable
+ * coherency at the interconnect level in addition to gic cpu interface.
+ ******************************************************************************/
+void psci_warmboot_entrypoint(void)
+{
+ unsigned int end_pwrlvl;
+ unsigned int cpu_idx;
+ unsigned int parent_nodes[PLAT_MAX_PWR_LVL] = {0};
+ psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} };
+ unsigned int hartid = current_hartid();
+
+ cpu_idx = plat_core_pos_by_mpidr(hartid);
+
+ /* if we resumed directly from CPU-non-ret because of the wakeup source in suspending process */
+ if (psci_get_cpu_local_state() == PSCI_LOCAL_STATE_RUN) {
+ /* sbi_printf("%s:%d\n", __func__, __LINE__); */
+ return;
+ }
+
+ /*
+ * Verify that we have been explicitly turned ON or resumed from
+ * suspend.
+ */
+ if (psci_get_aff_info_state() == AFF_STATE_OFF) {
+ sbi_printf("Unexpected affinity info state.\n");
+ sbi_hart_hang();
+ }
+
+ /*
+ * Get the maximum power domain level to traverse to after this cpu
+ * has been physically powered up.
+ */
+ end_pwrlvl = get_power_on_target_pwrlvl();
+
+ /* Get the parent nodes */
+ psci_get_parent_pwr_domain_nodes(cpu_idx, end_pwrlvl, parent_nodes);
+
+ /*
+ * This function acquires the lock corresponding to each power level so
+ * that by the time all locks are taken, the system topology is snapshot
+ * and state management can be done safely.
+ */
+ psci_acquire_pwr_domain_locks(end_pwrlvl, parent_nodes);
+
+ psci_get_target_local_pwr_states(end_pwrlvl, &state_info);
+
+#if ENABLE_PSCI_STAT
+ plat_psci_stat_accounting_stop(&state_info);
+#endif
+
+ /*
+ * This CPU could be resuming from suspend or it could have just been
+ * turned on. To distinguish between these 2 cases, we examine the
+ * affinity state of the CPU:
+ * - If the affinity state is ON_PENDING then it has just been
+ * turned on.
+ * - Else it is resuming from suspend.
+ *
+ * Depending on the type of warm reset identified, choose the right set
+ * of power management handler and perform the generic, architecture
+ * and platform specific handling.
+ */
+ if (psci_get_aff_info_state() == AFF_STATE_ON_PENDING)
+ psci_cpu_on_finish(cpu_idx, &state_info);
+ else
+ psci_cpu_suspend_finish(cpu_idx, &state_info);
+
+ /*
+ * Set the requested and target state of this CPU and all the higher
+ * power domains which are ancestors of this CPU to run.
+ */
+ psci_set_pwr_domains_to_run(end_pwrlvl);
+
+#if ENABLE_PSCI_STAT
+ /*
+ * Update PSCI stats.
+ * Caches are off when writing stats data on the power down path.
+ * Since caches are now enabled, it's necessary to do cache
+ * maintenance before reading that same data.
+ */
+ psci_stats_update_pwr_up(end_pwrlvl, &state_info);
+#endif
+
+ /*
+ * This loop releases the lock corresponding to each power level
+ * in the reverse order to which they were acquired.
+ */
+ psci_release_pwr_domain_locks(end_pwrlvl, parent_nodes);
+}
+
+/******************************************************************************
+ * This function is used in platform-coordinated mode.
+ *
+ * This function is passed the local power states requested for each power
+ * domain (state_info) between the current CPU domain and its ancestors until
+ * the target power level (end_pwrlvl). It updates the array of requested power
+ * states with this information.
+ *
+ * Then, for each level (apart from the CPU level) until the 'end_pwrlvl', it
+ * retrieves the states requested by all the cpus of which the power domain at
+ * that level is an ancestor. It passes this information to the platform to
+ * coordinate and return the target power state. If the target state for a level
+ * is RUN then subsequent levels are not considered. At the CPU level, state
+ * coordination is not required. Hence, the requested and the target states are
+ * the same.
+ *
+ * The 'state_info' is updated with the target state for each level between the
+ * CPU and the 'end_pwrlvl' and returned to the caller.
+ *
+ * This function will only be invoked with data cache enabled and while
+ * powering down a core.
+ *****************************************************************************/
+void psci_do_state_coordination(unsigned int end_pwrlvl,
+ psci_power_state_t *state_info)
+{
+ unsigned int lvl, parent_idx;
+ unsigned int start_idx;
+ unsigned int ncpus;
+ plat_local_state_t target_state, *req_states;
+ unsigned int hartid = current_hartid();
+ unsigned int cpu_idx = plat_core_pos_by_mpidr(hartid);;
+
+ if (end_pwrlvl > PLAT_MAX_PWR_LVL) {
+ sbi_printf("%s:%d, err\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ parent_idx = psci_cpu_pd_nodes[cpu_idx].parent_node;
+
+ /* For level 0, the requested state will be equivalent
+ to target state */
+ for (lvl = PSCI_CPU_PWR_LVL + 1U; lvl <= end_pwrlvl; lvl++) {
+
+ /* First update the requested power state */
+ psci_set_req_local_pwr_state(lvl, cpu_idx,
+ state_info->pwr_domain_state[lvl]);
+
+ /* Get the requested power states for this power level */
+ start_idx = psci_non_cpu_pd_nodes[parent_idx].cpu_start_idx;
+ req_states = psci_get_req_local_pwr_states(lvl, start_idx);
+
+ /*
+ * Let the platform coordinate amongst the requested states at
+ * this power level and return the target local power state.
+ */
+ ncpus = psci_non_cpu_pd_nodes[parent_idx].ncpus;
+ target_state = plat_get_target_pwr_state(lvl,
+ req_states,
+ ncpus);
+
+ state_info->pwr_domain_state[lvl] = target_state;
+
+ /* Break early if the negotiated target power state is RUN */
+ if (is_local_state_run(state_info->pwr_domain_state[lvl]) != 0)
+ break;
+
+ parent_idx = psci_non_cpu_pd_nodes[parent_idx].parent_node;
+ }
+
+ /*
+ * This is for cases when we break out of the above loop early because
+ * the target power state is RUN at a power level < end_pwlvl.
+ * We update the requested power state from state_info and then
+ * set the target state as RUN.
+ */
+ for (lvl = lvl + 1U; lvl <= end_pwrlvl; lvl++) {
+ psci_set_req_local_pwr_state(lvl, cpu_idx,
+ state_info->pwr_domain_state[lvl]);
+ state_info->pwr_domain_state[lvl] = PSCI_LOCAL_STATE_RUN;
+
+ }
+
+ /* Update the target state in the power domain nodes */
+ psci_set_target_local_pwr_states(end_pwrlvl, state_info);
+}
+
+/******************************************************************************
+ * This function ensures that the power state parameter in a CPU_SUSPEND request
+ * is valid. If so, it returns the requested states for each power level.
+ *****************************************************************************/
+int psci_validate_power_state(unsigned int power_state,
+ psci_power_state_t *state_info)
+{
+ /* Check SBZ bits in power state are zero */
+ if (psci_check_power_state(power_state) != 0U)
+ return PSCI_E_INVALID_PARAMS;
+
+ if (psci_plat_pm_ops->validate_power_state == NULL) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ /* Validate the power_state using platform pm_ops */
+ return psci_plat_pm_ops->validate_power_state(power_state, state_info);
+}
+
+/******************************************************************************
+ * This functions finds the level of the highest power domain which will be
+ * placed in a low power state during a suspend operation.
+ *****************************************************************************/
+unsigned int psci_find_target_suspend_lvl(const psci_power_state_t *state_info)
+{
+ int i;
+
+ for (i = (int) PLAT_MAX_PWR_LVL; i >= (int) PSCI_CPU_PWR_LVL; i--) {
+ if (is_local_state_run(state_info->pwr_domain_state[i]) == 0)
+ return (unsigned int) i;
+ }
+
+ return PSCI_INVALID_PWR_LVL;
+}
+
+/******************************************************************************
+ * This function validates a suspend request by making sure that if a standby
+ * state is requested then no power level is turned off and the highest power
+ * level is placed in a standby/retention state.
+ *
+ * It also ensures that the state level X will enter is not shallower than the
+ * state level X + 1 will enter.
+ *
+ * This validation will be enabled only for DEBUG builds as the platform is
+ * expected to perform these validations as well.
+ *****************************************************************************/
+int psci_validate_suspend_req(const psci_power_state_t *state_info,
+ unsigned int is_power_down_state)
+{
+ unsigned int max_off_lvl, target_lvl, max_retn_lvl;
+ plat_local_state_t state;
+ plat_local_state_type_t req_state_type, deepest_state_type;
+ int i;
+
+ /* Find the target suspend power level */
+ target_lvl = psci_find_target_suspend_lvl(state_info);
+ if (target_lvl == PSCI_INVALID_PWR_LVL)
+ return PSCI_E_INVALID_PARAMS;
+
+ /* All power domain levels are in a RUN state to begin with */
+ deepest_state_type = STATE_TYPE_RUN;
+
+ for (i = (int) target_lvl; i >= (int) PSCI_CPU_PWR_LVL; i--) {
+ state = state_info->pwr_domain_state[i];
+ req_state_type = find_local_state_type(state);
+
+ /*
+ * While traversing from the highest power level to the lowest,
+ * the state requested for lower levels has to be the same or
+ * deeper i.e. equal to or greater than the state at the higher
+ * levels. If this condition is true, then the requested state
+ * becomes the deepest state encountered so far.
+ */
+ if (req_state_type < deepest_state_type)
+ return PSCI_E_INVALID_PARAMS;
+ deepest_state_type = req_state_type;
+ }
+
+ /* Find the highest off power level */
+ max_off_lvl = psci_find_max_off_lvl(state_info);
+
+ /* The target_lvl is either equal to the max_off_lvl or max_retn_lvl */
+ max_retn_lvl = PSCI_INVALID_PWR_LVL;
+ if (target_lvl != max_off_lvl)
+ max_retn_lvl = target_lvl;
+
+ /*
+ * If this is not a request for a power down state then max off level
+ * has to be invalid and max retention level has to be a valid power
+ * level.
+ */
+ if ((is_power_down_state == 0U) &&
+ ((max_off_lvl != PSCI_INVALID_PWR_LVL) ||
+ (max_retn_lvl == PSCI_INVALID_PWR_LVL)))
+ return PSCI_E_INVALID_PARAMS;
+
+ return PSCI_E_SUCCESS;
+}
+
+void riscv_pwr_state_to_psci(unsigned int rstate, unsigned int *pstate)
+{
+ *pstate = 0;
+
+ /* suspend ? */
+ if (rstate & (1 << RSTATE_TYPE_SHIFT))
+ *pstate |= (1 << PSTATE_TYPE_SHIFT);
+
+ /* cluster ? */
+ if (rstate & (PSTATE_PWR_LVL_MASK << RSTATE_PWR_LVL_SHIFT))
+ *pstate |= (rstate & (PSTATE_PWR_LVL_MASK << RSTATE_PWR_LVL_SHIFT));
+}
diff --git a/lib/utils/psci/psci_main.c b/lib/utils/psci/psci_main.c
new file mode 100644
index 0000000..f2441f5
--- /dev/null
+++ b/lib/utils/psci/psci_main.c
@@ -0,0 +1,188 @@
+#include <sbi_utils/psci/psci.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_types.h>
+#include <sbi/sbi_scratch.h>
+#include "psci_private.h"
+
+/*******************************************************************************
+ * PSCI frontend api for servicing SMCs. Described in the PSCI spec.
+ ******************************************************************************/
+int psci_cpu_on(u_register_t target_cpu,
+ uintptr_t entrypoint)
+
+{
+ int rc;
+
+ /* Determine if the cpu exists of not */
+ rc = psci_validate_mpidr(target_cpu);
+ if (rc != PSCI_E_SUCCESS)
+ return PSCI_E_INVALID_PARAMS;
+
+ /*
+ * To turn this cpu on, specify which power
+ * levels need to be turned on
+ */
+ return psci_cpu_on_start(target_cpu, entrypoint);
+}
+
+int psci_affinity_info(u_register_t target_affinity,
+ unsigned int lowest_affinity_level)
+{
+ int ret;
+ unsigned int target_idx;
+ psci_cpu_data_t *svc_cpu_data;
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(target_affinity);
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ /* We dont support level higher than PSCI_CPU_PWR_LVL */
+ if (lowest_affinity_level > PSCI_CPU_PWR_LVL)
+ return PSCI_E_INVALID_PARAMS;
+
+ /* Calculate the cpu index of the target */
+ ret = plat_core_pos_by_mpidr(target_affinity);
+ if (ret == -1) {
+ return PSCI_E_INVALID_PARAMS;
+ }
+ target_idx = (unsigned int)ret;
+
+ /*
+ * Generic management:
+ * Perform cache maintanence ahead of reading the target CPU state to
+ * ensure that the data is not stale.
+ * There is a theoretical edge case where the cache may contain stale
+ * data for the target CPU data - this can occur under the following
+ * conditions:
+ * - the target CPU is in another cluster from the current
+ * - the target CPU was the last CPU to shutdown on its cluster
+ * - the cluster was removed from coherency as part of the CPU shutdown
+ *
+ * In this case the cache maintenace that was performed as part of the
+ * target CPUs shutdown was not seen by the current CPU's cluster. And
+ * so the cache may contain stale data for the target CPU.
+ */
+ csi_dcache_clean_invalid_range((uintptr_t)&svc_cpu_data->aff_info_state, sizeof(aff_info_state_t));
+
+ return psci_get_aff_info_state_by_idx(target_idx);
+}
+
+int psci_cpu_off(void)
+{
+ int rc;
+ unsigned int target_pwrlvl = PLAT_MAX_PWR_LVL;
+
+ /*
+ * Do what is needed to power off this CPU and possible higher power
+ * levels if it able to do so. Upon success, enter the final wfi
+ * which will power down this CPU.
+ */
+ rc = psci_do_cpu_off(target_pwrlvl);
+
+ /*
+ * The only error cpu_off can return is E_DENIED. So check if that's
+ * indeed the case.
+ */
+ if (rc != PSCI_E_DENIED) {
+ sbi_printf("%s:%d, err\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ return rc;
+}
+
+int psci_cpu_suspend(unsigned int power_state,
+ uintptr_t entrypoint,
+ u_register_t context_id)
+{
+ int rc;
+ unsigned int target_pwrlvl, is_power_down_state, pwr_state;
+ /* entry_point_info_t ep; */
+ psci_power_state_t state_info = { {PSCI_LOCAL_STATE_RUN} };
+ plat_local_state_t cpu_pd_state;
+
+ riscv_pwr_state_to_psci(power_state, &pwr_state);
+
+ /* Validate the power_state parameter */
+ rc = psci_validate_power_state(pwr_state, &state_info);
+ if (rc != PSCI_E_SUCCESS) {
+ if (rc != PSCI_E_INVALID_PARAMS) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+ return rc;
+ }
+
+ /*
+ * Get the value of the state type bit from the power state parameter.
+ */
+ is_power_down_state = psci_get_pstate_type(pwr_state);
+
+ /* Sanity check the requested suspend levels */
+ if (psci_validate_suspend_req(&state_info, is_power_down_state)
+ != PSCI_E_SUCCESS) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ target_pwrlvl = psci_find_target_suspend_lvl(&state_info);
+ if (target_pwrlvl == PSCI_INVALID_PWR_LVL) {
+ sbi_printf("Invalid target power level for suspend operation\n");
+ sbi_hart_hang();
+ }
+
+ /* Fast path for CPU standby.*/
+ if (is_cpu_standby_req(is_power_down_state, target_pwrlvl)) {
+ if (psci_plat_pm_ops->cpu_standby == NULL)
+ return PSCI_E_INVALID_PARAMS;
+
+ /*
+ * Set the state of the CPU power domain to the platform
+ * specific retention state and enter the standby state.
+ */
+ cpu_pd_state = state_info.pwr_domain_state[PSCI_CPU_PWR_LVL];
+ psci_set_cpu_local_state(cpu_pd_state);
+
+#if ENABLE_PSCI_STAT
+ plat_psci_stat_accounting_start(&state_info);
+#endif
+
+ psci_plat_pm_ops->cpu_standby(cpu_pd_state);
+
+ /* Upon exit from standby, set the state back to RUN. */
+ psci_set_cpu_local_state(PSCI_LOCAL_STATE_RUN);
+
+#if ENABLE_PSCI_STAT
+ plat_psci_stat_accounting_stop(&state_info);
+
+ /* Update PSCI stats */
+ psci_stats_update_pwr_up(PSCI_CPU_PWR_LVL, &state_info);
+#endif
+
+ return PSCI_E_SUCCESS;
+ }
+
+ /*
+ * If a power down state has been requested, we need to verify entry
+ * point and program entry information.
+ */
+ if (is_power_down_state != 0U) {
+ /* rc = psci_validate_entry_point(&ep, entrypoint, context_id);
+ if (rc != PSCI_E_SUCCESS)
+ return rc; */;
+ }
+
+ /*
+ * Do what is needed to enter the power down state. Upon success,
+ * enter the final wfi which will power down this CPU. This function
+ * might return if the power down was abandoned for any reason, e.g.
+ * arrival of an interrupt
+ */
+ rc = psci_cpu_suspend_start(/* &ep */entrypoint,
+ target_pwrlvl,
+ &state_info,
+ is_power_down_state);
+
+ return rc;
+}
+
diff --git a/lib/utils/psci/psci_off.c b/lib/utils/psci/psci_off.c
new file mode 100644
index 0000000..e8b5be1
--- /dev/null
+++ b/lib/utils/psci/psci_off.c
@@ -0,0 +1,173 @@
+#include <sbi_utils/psci/psci.h>
+#include <sbi_utils/cache/cacheflush.h>
+#include <sbi_utils/psci/plat/common/platform.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_hart.h>
+#include "psci_private.h"
+
+/******************************************************************************
+ * Construct the psci_power_state to request power OFF at all power levels.
+ ******************************************************************************/
+static void psci_set_power_off_state(psci_power_state_t *state_info)
+{
+ unsigned int lvl;
+
+ for (lvl = PSCI_CPU_PWR_LVL; lvl <= PLAT_MAX_PWR_LVL; lvl++)
+ state_info->pwr_domain_state[lvl] = PLAT_MAX_OFF_STATE;
+}
+
+/******************************************************************************
+ * Top level handler which is called when a cpu wants to power itself down.
+ * It's assumed that along with turning the cpu power domain off, power
+ * domains at higher levels will be turned off as far as possible. It finds
+ * the highest level where a domain has to be powered off by traversing the
+ * node information and then performs generic, architectural, platform setup
+ * and state management required to turn OFF that power domain and domains
+ * below it. e.g. For a cpu that's to be powered OFF, it could mean programming
+ * the power controller whereas for a cluster that's to be powered off, it will
+ * call the platform specific code which will disable coherency at the
+ * interconnect level if the cpu is the last in the cluster and also the
+ * program the power controller.
+ ******************************************************************************/
+int psci_do_cpu_off(unsigned int end_pwrlvl)
+{
+ int rc = PSCI_E_SUCCESS;
+ unsigned int hartid = current_hartid();
+ psci_cpu_data_t *svc_cpu_data;
+ unsigned int idx = plat_core_pos_by_mpidr(hartid);;
+ psci_power_state_t state_info;
+ unsigned int parent_nodes[PLAT_MAX_PWR_LVL] = {0};
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ /*
+ * This function must only be called on platforms where the
+ * CPU_OFF platform hooks have been implemented.
+ */
+ if (psci_plat_pm_ops->pwr_domain_off == NULL) {
+ sbi_printf("%s:%d, err\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ /* Construct the psci_power_state for CPU_OFF */
+ psci_set_power_off_state(&state_info);
+
+ /*
+ * Call the platform provided early CPU_OFF handler to allow
+ * platforms to perform any housekeeping activities before
+ * actually powering the CPU off. PSCI_E_DENIED indicates that
+ * the CPU off sequence should be aborted at this time.
+ */
+ if (psci_plat_pm_ops->pwr_domain_off_early) {
+ rc = psci_plat_pm_ops->pwr_domain_off_early(&state_info);
+ if (rc == PSCI_E_DENIED) {
+ return rc;
+ }
+ }
+
+ /*
+ * Get the parent nodes here, this is important to do before we
+ * initiate the power down sequence as after that point the core may
+ * have exited coherency and its cache may be disabled, any access to
+ * shared memory after that (such as the parent node lookup in
+ * psci_cpu_pd_nodes) can cause coherency issues on some platforms.
+ */
+ psci_get_parent_pwr_domain_nodes(idx, end_pwrlvl, parent_nodes);
+
+ /*
+ * This function acquires the lock corresponding to each power
+ * level so that by the time all locks are taken, the system topology
+ * is snapshot and state management can be done safely.
+ */
+ psci_acquire_pwr_domain_locks(end_pwrlvl, parent_nodes);
+
+#if 0
+ /*
+ * Call the cpu off handler registered by the Secure Payload Dispatcher
+ * to let it do any bookkeeping. Assume that the SPD always reports an
+ * E_DENIED error if SP refuse to power down
+ */
+ if ((psci_spd_pm != NULL) && (psci_spd_pm->svc_off != NULL)) {
+ rc = psci_spd_pm->svc_off(0);
+ if (rc != 0)
+ goto exit;
+ }
+#endif
+
+ /*
+ * This function is passed the requested state info and
+ * it returns the negotiated state info for each power level upto
+ * the end level specified.
+ */
+ psci_do_state_coordination(end_pwrlvl, &state_info);
+
+#if ENABLE_PSCI_STAT
+ /* Update the last cpu for each level till end_pwrlvl */
+ psci_stats_update_pwr_down(end_pwrlvl, &state_info);
+#endif
+
+ /*
+ * Without hardware-assisted coherency, the CPU drivers disable data
+ * caches, then perform cache-maintenance operations in software.
+ *
+ * This also calls prepare_cpu_pwr_dwn() to initiate power down
+ * sequence, but that function will return with data caches disabled.
+ * We must ensure that the stack memory is flushed out to memory before
+ * we start popping from it again.
+ */
+ psci_do_pwrdown_cache_maintenance(hartid, (uintptr_t)scratch, psci_find_max_off_lvl(&state_info));
+
+ /*
+ * Plat. management: Perform platform specific actions to turn this
+ * cpu off e.g. exit cpu coherency, program the power controller etc.
+ */
+ psci_plat_pm_ops->pwr_domain_off(&state_info);
+
+#if ENABLE_PSCI_STAT
+ plat_psci_stat_accounting_start(&state_info);
+#endif
+
+#if 0
+exit:
+#endif
+ /*
+ * Release the locks corresponding to each power level in the
+ * reverse order to which they were acquired.
+ */
+ psci_release_pwr_domain_locks(end_pwrlvl, parent_nodes);
+
+ /*
+ * Check if all actions needed to safely power down this cpu have
+ * successfully completed.
+ */
+ if (rc == PSCI_E_SUCCESS) {
+ /*
+ * Set the affinity info state to OFF. When caches are disabled,
+ * this writes directly to main memory, so cache maintenance is
+ * required to ensure that later cached reads of aff_info_state
+ * return AFF_STATE_OFF. A dsbish() ensures ordering of the
+ * update to the affinity info state prior to cache line
+ * invalidation.
+ */
+ csi_dcache_clean_invalid_range((uintptr_t)&svc_cpu_data->aff_info_state, sizeof(aff_info_state_t));
+ psci_set_aff_info_state(AFF_STATE_OFF);
+ /* psci_dsbish(); */
+ asm volatile ("fence rw, rw");
+ csi_dcache_invalid_range((uintptr_t)&svc_cpu_data->aff_info_state, sizeof(aff_info_state_t));
+
+ if (psci_plat_pm_ops->pwr_domain_pwr_down_wfi != NULL) {
+ /* This function must not return */
+ psci_plat_pm_ops->pwr_domain_pwr_down_wfi(&state_info);
+ } else {
+ /*
+ * Enter a wfi loop which will allow the power
+ * controller to physically power down this cpu.
+ */
+ //psci_power_down_wfi();
+ }
+ }
+
+ return rc;
+}
diff --git a/lib/utils/psci/psci_on.c b/lib/utils/psci/psci_on.c
new file mode 100644
index 0000000..2dd4ff0
--- /dev/null
+++ b/lib/utils/psci/psci_on.c
@@ -0,0 +1,246 @@
+#include <sbi_utils/psci/psci.h>
+#include <sbi_utils/cache/cacheflush.h>
+#include <sbi_utils/psci/plat/common/platform.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_hart.h>
+#include "psci_private.h"
+
+/*
+ * Helper functions for the CPU level spinlocks
+ */
+static inline void psci_spin_lock_cpu(unsigned int idx)
+{
+ spin_lock(&psci_cpu_pd_nodes[idx].cpu_lock);
+}
+
+static inline void psci_spin_unlock_cpu(unsigned int idx)
+{
+ spin_unlock(&psci_cpu_pd_nodes[idx].cpu_lock);
+}
+
+/*******************************************************************************
+ * This function checks whether a cpu which has been requested to be turned on
+ * is OFF to begin with.
+ ******************************************************************************/
+static int cpu_on_validate_state(aff_info_state_t aff_state)
+{
+ if (aff_state == AFF_STATE_ON)
+ return PSCI_E_ALREADY_ON;
+
+ if (aff_state == AFF_STATE_ON_PENDING)
+ return PSCI_E_ON_PENDING;
+
+ if (aff_state != AFF_STATE_OFF) {
+ sbi_printf("wrong aff state.\n");
+ sbi_hart_hang();
+ }
+
+ return PSCI_E_SUCCESS;
+}
+
+/*******************************************************************************
+ * Generic handler which is called to physically power on a cpu identified by
+ * its mpidr. It performs the generic, architectural, platform setup and state
+ * management to power on the target cpu e.g. it will ensure that
+ * enough information is stashed for it to resume execution in the non-secure
+ * security state.
+ *
+ * The state of all the relevant power domains are changed after calling the
+ * platform handler as it can return error.
+ ******************************************************************************/
+int psci_cpu_on_start(u_register_t target, uintptr_t entrypoint)
+{
+ int rc;
+ aff_info_state_t target_aff_state;
+ int ret = 0;
+ unsigned int target_idx;
+ psci_cpu_data_t *svc_cpu_data;
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(target);
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ ret = plat_core_pos_by_mpidr(target);
+
+ if ((ret < 0) || (ret >= (int)PLATFORM_CORE_COUNT)) {
+ sbi_printf("Unexpected core index.\n");
+ sbi_hart_hang();
+ }
+
+ target_idx = (unsigned int)ret;
+
+ /*
+ * This function must only be called on platforms where the
+ * CPU_ON platform hooks have been implemented.
+ */
+ if (psci_plat_pm_ops->pwr_domain_on == NULL ||
+ psci_plat_pm_ops->pwr_domain_on_finish == NULL) {
+ sbi_printf("%s:%d, invalid psci ops\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ /* Protect against multiple CPUs trying to turn ON the same target CPU */
+ psci_spin_lock_cpu(target_idx);
+
+ /*
+ * Generic management: Ensure that the cpu is off to be
+ * turned on.
+ * Perform cache maintanence ahead of reading the target CPU state to
+ * ensure that the data is not stale.
+ * There is a theoretical edge case where the cache may contain stale
+ * data for the target CPU data - this can occur under the following
+ * conditions:
+ * - the target CPU is in another cluster from the current
+ * - the target CPU was the last CPU to shutdown on its cluster
+ * - the cluster was removed from coherency as part of the CPU shutdown
+ *
+ * In this case the cache maintenace that was performed as part of the
+ * target CPUs shutdown was not seen by the current CPU's cluster. And
+ * so the cache may contain stale data for the target CPU.
+ */
+ csi_dcache_clean_invalid_range((uintptr_t)&svc_cpu_data->aff_info_state, sizeof(aff_info_state_t));
+
+ rc = cpu_on_validate_state(psci_get_aff_info_state_by_idx(target_idx));
+ if (rc != PSCI_E_SUCCESS) {
+ goto exit;
+ }
+#if 0
+ /*
+ * Call the cpu on handler registered by the Secure Payload Dispatcher
+ * to let it do any bookeeping. If the handler encounters an error, it's
+ * expected to assert within
+ */
+ if ((psci_spd_pm != NULL) && (psci_spd_pm->svc_on != NULL))
+ psci_spd_pm->svc_on(target_cpu);
+#endif
+ /*
+ * Set the Affinity info state of the target cpu to ON_PENDING.
+ * Flush aff_info_state as it will be accessed with caches
+ * turned OFF.
+ */
+ psci_set_aff_info_state_by_idx((uintptr_t)target_idx, AFF_STATE_ON_PENDING);
+
+ csi_dcache_clean_invalid_range((uintptr_t)&svc_cpu_data->aff_info_state, sizeof(aff_info_state_t));
+
+ /*
+ * The cache line invalidation by the target CPU after setting the
+ * state to OFF (see psci_do_cpu_off()), could cause the update to
+ * aff_info_state to be invalidated. Retry the update if the target
+ * CPU aff_info_state is not ON_PENDING.
+ */
+ target_aff_state = psci_get_aff_info_state_by_idx(target_idx);
+ if (target_aff_state != AFF_STATE_ON_PENDING) {
+ if (target_aff_state != AFF_STATE_OFF) {
+ sbi_printf("%s:%d, invalid psci state\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+ psci_set_aff_info_state_by_idx(target_idx, AFF_STATE_ON_PENDING);
+
+ csi_dcache_clean_invalid_range((uintptr_t)&svc_cpu_data->aff_info_state, sizeof(aff_info_state_t));
+
+ if (psci_get_aff_info_state_by_idx(target_idx) !=
+ AFF_STATE_ON_PENDING) {
+ sbi_printf("%s:%d, invalid psci state\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+ }
+
+ /*
+ * Perform generic, architecture and platform specific handling.
+ */
+ /*
+ * Plat. management: Give the platform the current state
+ * of the target cpu to allow it to perform the necessary
+ * steps to power on.
+ */
+ rc = psci_plat_pm_ops->pwr_domain_on(target);
+ if ((rc != PSCI_E_SUCCESS) && (rc != PSCI_E_INTERN_FAIL)) {
+ sbi_printf("%s:%d, power-on domain err\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ if (rc == PSCI_E_SUCCESS) {
+ /* Store the re-entry information for the non-secure world. */
+ /**/;
+ } else {
+ /* Restore the state on error. */
+ psci_set_aff_info_state_by_idx(target_idx, AFF_STATE_OFF);
+ csi_dcache_clean_invalid_range((uintptr_t)&svc_cpu_data->aff_info_state, sizeof(aff_info_state_t));
+ }
+
+exit:
+ psci_spin_unlock_cpu(target_idx);
+ return rc;
+}
+
+/*******************************************************************************
+ * The following function finish an earlier power on request. They
+ * are called by the common finisher routine in psci_common.c. The `state_info`
+ * is the psci_power_state from which this CPU has woken up from.
+ ******************************************************************************/
+void psci_cpu_on_finish(unsigned int cpu_idx, const psci_power_state_t *state_info)
+{
+ const struct sbi_platform *sbi = sbi_platform_thishart_ptr();
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(sbi->hart_index2id[cpu_idx]);
+
+ /*
+ * Plat. management: Perform the platform specific actions
+ * for this cpu e.g. enabling the gic or zeroing the mailbox
+ * register. The actual state of this cpu has already been
+ * changed.
+ */
+ psci_plat_pm_ops->pwr_domain_on_finish(state_info);
+
+ /*
+ * Arch. management: Enable data cache and manage stack memory
+ */
+ psci_do_pwrup_cache_maintenance((uintptr_t)scratch);
+
+ /*
+ * Plat. management: Perform any platform specific actions which
+ * can only be done with the cpu and the cluster guaranteed to
+ * be coherent.
+ */
+ if (psci_plat_pm_ops->pwr_domain_on_finish_late != NULL)
+ psci_plat_pm_ops->pwr_domain_on_finish_late(state_info);
+
+#if 0
+ /*
+ * All the platform specific actions for turning this cpu
+ * on have completed. Perform enough arch.initialization
+ * to run in the non-secure address space.
+ */
+ psci_arch_setup();
+#endif
+
+ /*
+ * Lock the CPU spin lock to make sure that the context initialization
+ * is done. Since the lock is only used in this function to create
+ * a synchronization point with cpu_on_start(), it can be released
+ * immediately.
+ */
+ psci_spin_lock_cpu(cpu_idx);
+ psci_spin_unlock_cpu(cpu_idx);
+
+ /* Ensure we have been explicitly woken up by another cpu */
+ if (psci_get_aff_info_state() != AFF_STATE_ON_PENDING) {
+ sbi_printf("%s:%d, err\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+#if 0
+ /*
+ * Call the cpu on finish handler registered by the Secure Payload
+ * Dispatcher to let it do any bookeeping. If the handler encounters an
+ * error, it's expected to assert within
+ */
+ if ((psci_spd_pm != NULL) && (psci_spd_pm->svc_on_finish != NULL))
+ psci_spd_pm->svc_on_finish(0);
+
+ PUBLISH_EVENT(psci_cpu_on_finish);
+#endif
+
+ /* Populate the mpidr field within the cpu node array */
+ /* This needs to be done only once */
+ psci_cpu_pd_nodes[cpu_idx].mpidr = current_hartid();
+}
diff --git a/lib/utils/psci/psci_private.h b/lib/utils/psci/psci_private.h
new file mode 100644
index 0000000..d1cd2ba
--- /dev/null
+++ b/lib/utils/psci/psci_private.h
@@ -0,0 +1,198 @@
+#ifndef __PSCI_PRIVATE_H__
+#define __PSCI_PRIVATE_H__
+
+#include <sbi/riscv_locks.h>
+#include <sbi/sbi_platform.h>
+#include <sbi_utils/psci/psci.h>
+#include <sbi_utils/cache/cacheflush.h>
+
+/*******************************************************************************
+ * The following two data structures implement the power domain tree. The tree
+ * is used to track the state of all the nodes i.e. power domain instances
+ * described by the platform. The tree consists of nodes that describe CPU power
+ * domains i.e. leaf nodes and all other power domains which are parents of a
+ * CPU power domain i.e. non-leaf nodes.
+ ******************************************************************************/
+typedef struct non_cpu_pwr_domain_node {
+ /*
+ * Index of the first CPU power domain node level 0 which has this node
+ * as its parent.
+ */
+ unsigned int cpu_start_idx;
+
+ /*
+ * Number of CPU power domains which are siblings of the domain indexed
+ * by 'cpu_start_idx' i.e. all the domains in the range 'cpu_start_idx
+ * -> cpu_start_idx + ncpus' have this node as their parent.
+ */
+ unsigned int ncpus;
+
+ /*
+ * Index of the parent power domain node.
+ * TODO: Figure out whether to whether using pointer is more efficient.
+ */
+ unsigned int parent_node;
+
+ plat_local_state_t local_state;
+
+ unsigned char level;
+
+ /* For indexing the psci_lock array*/
+ unsigned short lock_index;
+} __aligned(CACHE_LINE_SIZE) non_cpu_pd_node_t;
+
+typedef struct cpu_pwr_domain_node {
+ u_register_t mpidr;
+
+ /*
+ * Index of the parent power domain node.
+ * TODO: Figure out whether to whether using pointer is more efficient.
+ */
+ unsigned int parent_node;
+
+ /*
+ * A CPU power domain does not require state coordination like its
+ * parent power domains. Hence this node does not include a bakery
+ * lock. A spinlock is required by the CPU_ON handler to prevent a race
+ * when multiple CPUs try to turn ON the same target CPU.
+ */
+ spinlock_t cpu_lock;
+} cpu_pd_node_t;
+
+/*
+ * On systems where participant CPUs are cache-coherent, we can use spinlocks
+ * instead of bakery locks.
+ */
+typedef struct psci_spinlock_t {
+ spinlock_t lock;
+} __aligned(CACHE_LINE_SIZE) _psci_spinlock_t;
+
+#define DEFINE_PSCI_LOCK(_name) _psci_spinlock_t _name
+#define DECLARE_PSCI_LOCK(_name) extern DEFINE_PSCI_LOCK(_name)
+
+/* One lock is required per non-CPU power domain node */
+DECLARE_PSCI_LOCK(psci_locks[PSCI_NUM_NON_CPU_PWR_DOMAINS]);
+
+static inline void psci_lock_init(non_cpu_pd_node_t *non_cpu_pd_node, unsigned short idx)
+{
+ non_cpu_pd_node[idx].lock_index = idx;
+}
+
+static inline void psci_lock_get(non_cpu_pd_node_t *non_cpu_pd_node)
+{
+ spin_lock(&psci_locks[non_cpu_pd_node->lock_index].lock);
+}
+
+static inline void psci_lock_release(non_cpu_pd_node_t *non_cpu_pd_node)
+{
+ spin_unlock(&psci_locks[non_cpu_pd_node->lock_index].lock);
+}
+
+/* common */
+extern non_cpu_pd_node_t psci_non_cpu_pd_nodes[PSCI_NUM_NON_CPU_PWR_DOMAINS];
+extern cpu_pd_node_t psci_cpu_pd_nodes[PLATFORM_CORE_COUNT];
+extern unsigned int psci_plat_core_count;
+extern unsigned long psci_delta_off;
+extern const plat_psci_ops_t *psci_plat_pm_ops;
+
+void psci_acquire_pwr_domain_locks(unsigned int end_pwrlvl,
+ const unsigned int *parent_nodes);
+void psci_release_pwr_domain_locks(unsigned int end_pwrlvl,
+ const unsigned int *parent_nodes);
+unsigned int psci_find_max_off_lvl(const psci_power_state_t *state_info);
+
+int psci_validate_mpidr(u_register_t mpidr);
+void psci_get_parent_pwr_domain_nodes(unsigned int cpu_idx,
+ unsigned int end_lvl,
+ unsigned int *node_index);
+
+void psci_init_req_local_pwr_states(void);
+void set_non_cpu_pd_node_local_state(unsigned int parent_idx,
+ plat_local_state_t state);
+void psci_set_req_local_pwr_state(unsigned int pwrlvl,
+ unsigned int cpu_idx,
+ plat_local_state_t req_pwr_state);
+void psci_set_aff_info_state(aff_info_state_t aff_state);
+aff_info_state_t psci_get_aff_info_state(void);
+aff_info_state_t psci_get_aff_info_state_by_idx(unsigned int idx);
+void psci_set_aff_info_state_by_idx(unsigned int idx, aff_info_state_t aff_state);
+void psci_set_cpu_local_state(plat_local_state_t state);
+void psci_set_pwr_domains_to_run(unsigned int end_pwrlvl);
+
+void psci_get_target_local_pwr_states(unsigned int end_pwrlvl,
+ psci_power_state_t *target_state);
+
+void psci_do_state_coordination(unsigned int end_pwrlvl,
+ psci_power_state_t *state_info);
+
+int plat_core_pos_by_mpidr(u_register_t mpidr);
+int psci_validate_power_state(unsigned int power_state,
+ psci_power_state_t *state_info);
+int psci_validate_suspend_req(const psci_power_state_t *state_info,
+ unsigned int is_power_down_state);
+unsigned int psci_find_max_off_lvl(const psci_power_state_t *state_info);
+unsigned int psci_find_target_suspend_lvl(const psci_power_state_t *state_info);
+
+void psci_set_suspend_pwrlvl(unsigned int target_lvl);
+/* Private exported functions from psci_suspend.c */
+int psci_cpu_suspend_start(/* const entry_point_info_t *ep */ uintptr_t entrypoint,
+ unsigned int end_pwrlvl,
+ psci_power_state_t *state_info,
+ unsigned int is_power_down_state);
+void psci_cpu_suspend_finish(unsigned int cpu_idx, const psci_power_state_t *state_info);
+void riscv_pwr_state_to_psci(unsigned int rstate, unsigned int *pstate);
+
+/* Helper function to identify a CPU standby request in PSCI Suspend call */
+static inline bool is_cpu_standby_req(unsigned int is_power_down_state,
+ unsigned int retn_lvl)
+{
+ return (is_power_down_state == 0U) && (retn_lvl == 0U);
+}
+
+static inline void psci_do_pwrup_cache_maintenance(uintptr_t scratch)
+{
+ /* invalidate local cache */
+ csi_invalidate_dcache_all();
+
+ /* enable dcache */
+ csi_enable_dcache();
+}
+
+static inline void psci_disable_core_snoop(void)
+{
+ unsigned int hartid = current_hartid();
+
+ csr_clear(CSR_ML2SETUP, 1 << (hartid % PLATFORM_MAX_CPUS_PER_CLUSTER));
+}
+
+static inline void psci_do_pwrdown_cache_maintenance(int hartid, uintptr_t scratch, int power_level)
+{
+ /* disable the data preftch */
+ csi_disable_data_preftch();
+
+ /* flush dacache all */
+ csi_flush_dcache_all();
+
+ if (power_level >= PSCI_CPU_PWR_LVL + 1) {
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1X)
+ /* disable the tcm */
+ csr_write(CSR_TCMCFG, 0);
+#endif
+ csi_flush_l2_cache();
+ }
+
+ /* disable dcache */
+ csi_disable_dcache();
+
+ /* disable core snoop */
+ psci_disable_core_snoop();
+
+ asm volatile ("fence iorw, iorw");
+}
+
+/* psci cpu */
+int psci_cpu_on_start(u_register_t target, uintptr_t entrypoint);
+void psci_cpu_on_finish(unsigned int cpu_idx, const psci_power_state_t *state_info);
+int psci_do_cpu_off(unsigned int end_pwrlvl);
+
+#endif
diff --git a/lib/utils/psci/psci_setup.c b/lib/utils/psci/psci_setup.c
new file mode 100644
index 0000000..ba52c20
--- /dev/null
+++ b/lib/utils/psci/psci_setup.c
@@ -0,0 +1,242 @@
+/*
+ * Copyright (c) 2013-2020, ARM Limited and Contributors. All rights reserved.
+ *
+ * SPDX-License-Identifier: BSD-3-Clause
+ */
+
+#include <sbi/sbi_console.h>
+#include <sbi_utils/cache/cacheflush.h>
+#include <sbi_utils/psci/plat/common/platform.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi_utils/psci/psci_lib.h>
+#include "psci_private.h"
+
+/*******************************************************************************
+ * Function which initializes the 'psci_non_cpu_pd_nodes' or the
+ * 'psci_cpu_pd_nodes' corresponding to the power level.
+ ******************************************************************************/
+static void psci_init_pwr_domain_node(uint16_t node_idx,
+ unsigned int parent_idx,
+ unsigned char level)
+{
+ if (level > PSCI_CPU_PWR_LVL) {
+ if (node_idx >= PSCI_NUM_NON_CPU_PWR_DOMAINS) {
+ sbi_printf("%s:%d, node_idx beyond the boundary\n",
+ __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+
+ psci_non_cpu_pd_nodes[node_idx].level = level;
+ psci_lock_init(psci_non_cpu_pd_nodes, node_idx);
+ psci_non_cpu_pd_nodes[node_idx].parent_node = parent_idx;
+ psci_non_cpu_pd_nodes[node_idx].local_state =
+ PLAT_MAX_OFF_STATE;
+ } else {
+ psci_cpu_data_t *svc_cpu_data;
+ const struct sbi_platform *sbi = sbi_platform_thishart_ptr();
+
+ if (node_idx >= PLATFORM_CORE_COUNT) {
+ sbi_printf("%s:%d, node_idx beyond the boundary\n",
+ __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ unsigned int hartid = sbi->hart_index2id[node_idx];
+
+ psci_cpu_pd_nodes[node_idx].parent_node = parent_idx;
+
+ /* Initialize with an invalid mpidr */
+ psci_cpu_pd_nodes[node_idx].mpidr = PSCI_INVALID_MPIDR;
+
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ /* Set the Affinity Info for the cores as OFF */
+ svc_cpu_data->aff_info_state = AFF_STATE_OFF;
+
+ /* Invalidate the suspend level for the cpu */
+ svc_cpu_data->target_pwrlvl = PSCI_INVALID_PWR_LVL;
+
+ /* Set the power state to OFF state */
+ svc_cpu_data->local_state = PLAT_MAX_OFF_STATE;
+
+ csi_dcache_clean_invalid_range((uintptr_t)svc_cpu_data, sizeof(psci_cpu_data_t));
+ }
+}
+
+/*******************************************************************************
+ * This functions updates cpu_start_idx and ncpus field for each of the node in
+ * psci_non_cpu_pd_nodes[]. It does so by comparing the parent nodes of each of
+ * the CPUs and check whether they match with the parent of the previous
+ * CPU. The basic assumption for this work is that children of the same parent
+ * are allocated adjacent indices. The platform should ensure this though proper
+ * mapping of the CPUs to indices via plat_core_pos_by_mpidr() and
+ * plat_my_core_pos() APIs.
+ *******************************************************************************/
+static void psci_update_pwrlvl_limits(void)
+{
+ unsigned int cpu_idx;
+ int j;
+ unsigned int nodes_idx[PLAT_MAX_PWR_LVL] = {0};
+ unsigned int temp_index[PLAT_MAX_PWR_LVL];
+
+ for (cpu_idx = 0; cpu_idx < psci_plat_core_count; cpu_idx++) {
+ psci_get_parent_pwr_domain_nodes(cpu_idx,
+ PLAT_MAX_PWR_LVL,
+ temp_index);
+ for (j = (int)PLAT_MAX_PWR_LVL - 1; j >= 0; j--) {
+ if (temp_index[j] != nodes_idx[j]) {
+ nodes_idx[j] = temp_index[j];
+ psci_non_cpu_pd_nodes[nodes_idx[j]].cpu_start_idx
+ = cpu_idx;
+ }
+ psci_non_cpu_pd_nodes[nodes_idx[j]].ncpus++;
+ }
+ }
+}
+
+/*******************************************************************************
+ * Core routine to populate the power domain tree. The tree descriptor passed by
+ * the platform is populated breadth-first and the first entry in the map
+ * informs the number of root power domains. The parent nodes of the root nodes
+ * will point to an invalid entry(-1).
+ ******************************************************************************/
+static unsigned int populate_power_domain_tree(const unsigned char
+ *topology)
+{
+ unsigned int i, j = 0U, num_nodes_at_lvl = 1U, num_nodes_at_next_lvl;
+ unsigned int node_index = 0U, num_children;
+ unsigned int parent_node_index = 0U;
+ int level = (int)PLAT_MAX_PWR_LVL;
+
+ /*
+ * For each level the inputs are:
+ * - number of nodes at this level in plat_array i.e. num_nodes_at_level
+ * This is the sum of values of nodes at the parent level.
+ * - Index of first entry at this level in the plat_array i.e.
+ * parent_node_index.
+ * - Index of first free entry in psci_non_cpu_pd_nodes[] or
+ * psci_cpu_pd_nodes[] i.e. node_index depending upon the level.
+ */
+ while (level >= (int) PSCI_CPU_PWR_LVL) {
+ num_nodes_at_next_lvl = 0U;
+ /*
+ * For each entry (parent node) at this level in the plat_array:
+ * - Find the number of children
+ * - Allocate a node in a power domain array for each child
+ * - Set the parent of the child to the parent_node_index - 1
+ * - Increment parent_node_index to point to the next parent
+ * - Accumulate the number of children at next level.
+ */
+ for (i = 0U; i < num_nodes_at_lvl; i++) {
+ if (parent_node_index > PSCI_NUM_NON_CPU_PWR_DOMAINS) {
+ sbi_printf("%s:%d, node_idx beyond the boundary\n",
+ __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ num_children = topology[parent_node_index];
+
+ for (j = node_index;
+ j < (node_index + num_children); j++)
+ psci_init_pwr_domain_node((uint16_t)j,
+ parent_node_index - 1U,
+ (unsigned char)level);
+
+ node_index = j;
+ num_nodes_at_next_lvl += num_children;
+ parent_node_index++;
+ }
+
+ num_nodes_at_lvl = num_nodes_at_next_lvl;
+ level--;
+
+ /* Reset the index for the cpu power domain array */
+ if (level == (int) PSCI_CPU_PWR_LVL)
+ node_index = 0;
+ }
+
+ /* Validate the sanity of array exported by the platform */
+ if (j > PLATFORM_CORE_COUNT) {
+ sbi_printf("%s:%d, invalidate core count\n",
+ __func__, __LINE__);
+ sbi_hart_hang();
+ }
+ return j;
+}
+
+/*******************************************************************************
+ * This function does the architectural setup and takes the warm boot
+ * entry-point `mailbox_ep` as an argument. The function also initializes the
+ * power domain topology tree by querying the platform. The power domain nodes
+ * higher than the CPU are populated in the array psci_non_cpu_pd_nodes[] and
+ * the CPU power domains are populated in psci_cpu_pd_nodes[]. The platform
+ * exports its static topology map through the
+ * populate_power_domain_topology_tree() API. The algorithm populates the
+ * psci_non_cpu_pd_nodes and psci_cpu_pd_nodes iteratively by using this
+ * topology map. On a platform that implements two clusters of 2 cpus each,
+ * and supporting 3 domain levels, the populated psci_non_cpu_pd_nodes would
+ * look like this:
+ *
+ * ---------------------------------------------------
+ * | system node | cluster 0 node | cluster 1 node |
+ * ---------------------------------------------------
+ *
+ * And populated psci_cpu_pd_nodes would look like this :
+ * <- cpus cluster0 -><- cpus cluster1 ->
+ * ------------------------------------------------
+ * | CPU 0 | CPU 1 | CPU 2 | CPU 3 |
+ * ------------------------------------------------
+ ******************************************************************************/
+int psci_setup(void)
+{
+ unsigned int cpu_idx;
+ const unsigned char *topology_tree;
+ unsigned int hartid = current_hartid();
+
+ cpu_idx = plat_core_pos_by_mpidr(hartid);
+
+ psci_delta_off = sbi_scratch_alloc_offset(sizeof(psci_cpu_data_t));
+ if (!psci_delta_off)
+ return SBI_ENOMEM;
+
+ /* Query the topology map from the platform */
+ topology_tree = plat_get_power_domain_tree_desc();
+
+ /* Populate the power domain arrays using the platform topology map */
+ psci_plat_core_count = populate_power_domain_tree(topology_tree);
+
+ /* Update the CPU limits for each node in psci_non_cpu_pd_nodes */
+ psci_update_pwrlvl_limits();
+
+ /* Populate the mpidr field of cpu node for this CPU */
+ psci_cpu_pd_nodes[cpu_idx].mpidr = hartid;
+
+ psci_init_req_local_pwr_states();
+
+ /*
+ * Set the requested and target state of this CPU and all the higher
+ * power domain levels for this CPU to run.
+ */
+ psci_set_pwr_domains_to_run(PLAT_MAX_PWR_LVL);
+
+ psci_print_power_domain_map();
+
+ (void) plat_setup_psci_ops(0, &psci_plat_pm_ops);
+ if (psci_plat_pm_ops == NULL) {
+ sbi_printf("%s:%d, invalid psci ops\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ /*
+ * Flush `psci_plat_pm_ops` as it will be accessed by secondary CPUs
+ * during warm boot, possibly before data cache is enabled.
+ */
+ csi_dcache_clean_invalid_range((uintptr_t)&psci_plat_pm_ops, sizeof(*psci_plat_pm_ops));
+
+ return 0;
+}
diff --git a/lib/utils/psci/psci_suspend.c b/lib/utils/psci/psci_suspend.c
new file mode 100644
index 0000000..1466acf
--- /dev/null
+++ b/lib/utils/psci/psci_suspend.c
@@ -0,0 +1,298 @@
+#include <sbi_utils/psci/psci.h>
+#include <sbi_utils/cache/cacheflush.h>
+#include <sbi_utils/psci/plat/common/platform.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_console.h>
+#include <sbi/sbi_hart.h>
+#include "psci_private.h"
+
+/*******************************************************************************
+ * This function does generic and platform specific operations after a wake-up
+ * from standby/retention states at multiple power levels.
+ ******************************************************************************/
+static void psci_suspend_to_standby_finisher(unsigned int cpu_idx,
+ unsigned int end_pwrlvl)
+{
+ unsigned int parent_nodes[PLAT_MAX_PWR_LVL] = {0};
+ psci_power_state_t state_info;
+
+ /* Get the parent nodes */
+ psci_get_parent_pwr_domain_nodes(cpu_idx, end_pwrlvl, parent_nodes);
+
+ psci_acquire_pwr_domain_locks(end_pwrlvl, parent_nodes);
+
+ /*
+ * Find out which retention states this CPU has exited from until the
+ * 'end_pwrlvl'. The exit retention state could be deeper than the entry
+ * state as a result of state coordination amongst other CPUs post wfi.
+ */
+ psci_get_target_local_pwr_states(end_pwrlvl, &state_info);
+
+#if ENABLE_PSCI_STAT
+ plat_psci_stat_accounting_stop(&state_info);
+ psci_stats_update_pwr_up(end_pwrlvl, &state_info);
+#endif
+
+ /*
+ * Plat. management: Allow the platform to do operations
+ * on waking up from retention.
+ */
+ psci_plat_pm_ops->pwr_domain_suspend_finish(&state_info);
+
+ /*
+ * Set the requested and target state of this CPU and all the higher
+ * power domain levels for this CPU to run.
+ */
+ psci_set_pwr_domains_to_run(end_pwrlvl);
+
+ psci_release_pwr_domain_locks(end_pwrlvl, parent_nodes);
+}
+
+/*******************************************************************************
+ * This function does generic and platform specific suspend to power down
+ * operations.
+ ******************************************************************************/
+static void psci_suspend_to_pwrdown_start(unsigned int end_pwrlvl,
+ /* const entry_point_info_t *ep */ uintptr_t ep,
+ const psci_power_state_t *state_info)
+{
+ unsigned int hartid = current_hartid();
+ psci_cpu_data_t *svc_cpu_data;
+ /* unsigned int max_off_lvl = psci_find_max_off_lvl(state_info); */
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ /* save something ???? */
+ /* PUBLISH_EVENT(psci_suspend_pwrdown_start); */
+
+ /* Save PSCI target power level for the suspend finisher handler */
+ psci_set_suspend_pwrlvl(end_pwrlvl);
+
+ /*
+ * Flush the target power level as it might be accessed on power up with
+ * Data cache disabled.
+ */
+ csi_dcache_clean_invalid_range((uintptr_t)&svc_cpu_data->target_pwrlvl, sizeof(unsigned int));
+
+#if 0
+ /*
+ * Call the cpu suspend handler registered by the Secure Payload
+ * Dispatcher to let it do any book-keeping. If the handler encounters an
+ * error, it's expected to assert within
+ */
+ if ((psci_spd_pm != NULL) && (psci_spd_pm->svc_suspend != NULL))
+ psci_spd_pm->svc_suspend(max_off_lvl);
+#endif
+
+ /*
+ * Plat. management: Allow the platform to perform any early
+ * actions required to power down the CPU. This might be useful for
+ * HW_ASSISTED_COHERENCY = 0 platforms that can safely perform these
+ * actions with data caches enabled.
+ */
+ if (psci_plat_pm_ops->pwr_domain_suspend_pwrdown_early != NULL)
+ psci_plat_pm_ops->pwr_domain_suspend_pwrdown_early(state_info);
+
+ /*
+ * Store the re-entry information for the non-secure world.
+ */
+ /* cm_init_my_context(ep); */
+
+ /*
+ * Arch. management. Initiate power down sequence.
+ * TODO : Introduce a mechanism to query the cache level to flush
+ * and the cpu-ops power down to perform from the platform.
+ */
+ /* psci_pwrdown_cpu(max_off_lvl); */
+ psci_do_pwrdown_cache_maintenance(hartid, (uintptr_t)scratch, psci_find_max_off_lvl(state_info));
+}
+
+/*******************************************************************************
+ * Top level handler which is called when a cpu wants to suspend its execution.
+ * It is assumed that along with suspending the cpu power domain, power domains
+ * at higher levels until the target power level will be suspended as well. It
+ * coordinates with the platform to negotiate the target state for each of
+ * the power domain level till the target power domain level. It then performs
+ * generic, architectural, platform setup and state management required to
+ * suspend that power domain level and power domain levels below it.
+ * e.g. For a cpu that's to be suspended, it could mean programming the
+ * power controller whereas for a cluster that's to be suspended, it will call
+ * the platform specific code which will disable coherency at the interconnect
+ * level if the cpu is the last in the cluster and also the program the power
+ * controller.
+ *
+ * All the required parameter checks are performed at the beginning and after
+ * the state transition has been done, no further error is expected and it is
+ * not possible to undo any of the actions taken beyond that point.
+ ******************************************************************************/
+int psci_cpu_suspend_start(/* const entry_point_info_t *ep */uintptr_t ep,
+ unsigned int end_pwrlvl,
+ psci_power_state_t *state_info,
+ unsigned int is_power_down_state)
+{
+ int rc = PSCI_E_SUCCESS;
+ bool skip_wfi = false;
+ unsigned int hartid = current_hartid();
+ unsigned int idx = plat_core_pos_by_mpidr(hartid);
+ unsigned int parent_nodes[PLAT_MAX_PWR_LVL] = {0};
+
+ /*
+ * This function must only be called on platforms where the
+ * CPU_SUSPEND platform hooks have been implemented.
+ */
+ if ((psci_plat_pm_ops->pwr_domain_suspend == NULL) ||
+ (psci_plat_pm_ops->pwr_domain_suspend_finish == NULL)) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ /* Get the parent nodes */
+ psci_get_parent_pwr_domain_nodes(idx, end_pwrlvl, parent_nodes);
+
+ /*
+ * This function acquires the lock corresponding to each power
+ * level so that by the time all locks are taken, the system topology
+ * is snapshot and state management can be done safely.
+ */
+ psci_acquire_pwr_domain_locks(end_pwrlvl, parent_nodes);
+
+ /*
+ * We check if there are any pending interrupts after the delay
+ * introduced by lock contention to increase the chances of early
+ * detection that a wake-up interrupt has fired.
+ */
+ if (__get_Supervisor_isr() != 0U) {
+ skip_wfi = true;
+ goto exit;
+ }
+
+ /*
+ * This function is passed the requested state info and
+ * it returns the negotiated state info for each power level upto
+ * the end level specified.
+ */
+ psci_do_state_coordination(end_pwrlvl, state_info);
+
+#if ENABLE_PSCI_STAT
+ /* Update the last cpu for each level till end_pwrlvl */
+ psci_stats_update_pwr_down(end_pwrlvl, state_info);
+#endif
+
+ if (is_power_down_state != 0U)
+ psci_suspend_to_pwrdown_start(end_pwrlvl, ep, state_info);
+
+ /*
+ * Plat. management: Allow the platform to perform the
+ * necessary actions to turn off this cpu e.g. set the
+ * platform defined mailbox with the psci entrypoint,
+ * program the power controller etc.
+ */
+
+ psci_plat_pm_ops->pwr_domain_suspend(state_info);
+
+#if ENABLE_PSCI_STAT
+ plat_psci_stat_accounting_start(state_info);
+#endif
+
+exit:
+ /*
+ * Release the locks corresponding to each power level in the
+ * reverse order to which they were acquired.
+ */
+ psci_release_pwr_domain_locks(end_pwrlvl, parent_nodes);
+
+ if (skip_wfi) {
+ return rc;
+ }
+
+ if (is_power_down_state != 0U) {
+ /* The function calls below must not return */
+ if (psci_plat_pm_ops->pwr_domain_pwr_down_wfi != NULL)
+ psci_plat_pm_ops->pwr_domain_pwr_down_wfi(state_info);
+ else
+ /* psci_power_down_wfi() */;
+ }
+
+ /*
+ * We will reach here if only retention/standby states have been
+ * requested at multiple power levels. This means that the cpu
+ * context will be preserved.
+ */
+ /* wfi(); */
+ asm volatile ("wfi");
+
+ /*
+ * After we wake up from context retaining suspend, call the
+ * context retaining suspend finisher.
+ */
+ psci_suspend_to_standby_finisher(idx, end_pwrlvl);
+
+ return rc;
+}
+
+/*******************************************************************************
+ * The following functions finish an earlier suspend request. They
+ * are called by the common finisher routine in psci_common.c. The `state_info`
+ * is the psci_power_state from which this CPU has woken up from.
+ ******************************************************************************/
+void psci_cpu_suspend_finish(unsigned int cpu_idx, const psci_power_state_t *state_info)
+{
+ /* unsigned int counter_freq; */
+ /* unsigned int max_off_lvl; */
+ unsigned int hartid = current_hartid();
+ psci_cpu_data_t *svc_cpu_data;
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(hartid);
+ svc_cpu_data = sbi_scratch_offset_ptr(scratch, psci_delta_off);
+
+ /* Ensure we have been woken up from a suspended state */
+ if ((psci_get_aff_info_state() != AFF_STATE_ON) ||
+ (is_local_state_off(
+ state_info->pwr_domain_state[PSCI_CPU_PWR_LVL]) == 0)) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ /*
+ * Plat. management: Perform the platform specific actions
+ * before we change the state of the cpu e.g. enabling the
+ * gic or zeroing the mailbox register. If anything goes
+ * wrong then assert as there is no way to recover from this
+ * situation.
+ */
+ psci_plat_pm_ops->pwr_domain_suspend_finish(state_info);
+
+ /* Arch. management: Enable the data cache, stack memory maintenance. */
+ psci_do_pwrup_cache_maintenance((uintptr_t)scratch);
+
+#if 0
+ /* Re-init the cntfrq_el0 register */
+ counter_freq = plat_get_syscnt_freq2();
+ write_cntfrq_el0(counter_freq);
+#endif
+ /*
+ * Call the cpu suspend finish handler registered by the Secure Payload
+ * Dispatcher to let it do any bookeeping. If the handler encounters an
+ * error, it's expected to assert within
+ */
+#if 0
+ if ((psci_spd_pm != NULL) && (psci_spd_pm->svc_suspend_finish != NULL)) {
+ max_off_lvl = psci_find_max_off_lvl(state_info);
+ assert(max_off_lvl != PSCI_INVALID_PWR_LVL);
+ psci_spd_pm->svc_suspend_finish(max_off_lvl);
+ }
+#endif
+
+ /* Invalidate the suspend level for the cpu */
+ psci_set_suspend_pwrlvl(PSCI_INVALID_PWR_LVL);
+ csi_dcache_clean_invalid_range((uintptr_t)&svc_cpu_data->target_pwrlvl, sizeof(unsigned int));
+
+ /* PUBLISH_EVENT(psci_suspend_pwrdown_finish); */
+
+ /*
+ * Generic management: Now we just need to retrieve the
+ * information that we had stashed away during the suspend
+ * call to set this cpu on its way.
+ */
+ /* cm_prepare_el3_exit_ns(); */
+}
diff --git a/lib/utils/psci/spacemit/plat/k1x/underly_implement.c b/lib/utils/psci/spacemit/plat/k1x/underly_implement.c
new file mode 100644
index 0000000..9976b57
--- /dev/null
+++ b/lib/utils/psci/spacemit/plat/k1x/underly_implement.c
@@ -0,0 +1,345 @@
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_types.h>
+#include <sbi/riscv_asm.h>
+#include <sbi_utils/psci/psci.h>
+#include <sbi/sbi_console.h>
+#include <spacemit/spacemit_config.h>
+
+#define C1_CPU_RESET_BASE_ADDR (0xD4282B24)
+
+#define PMU_CAP_CORE0_IDLE_CFG (0xd4282924)
+#define PMU_CAP_CORE1_IDLE_CFG (0xd4282928)
+#define PMU_CAP_CORE2_IDLE_CFG (0xd4282960)
+#define PMU_CAP_CORE3_IDLE_CFG (0xd4282964)
+#define PMU_CAP_CORE4_IDLE_CFG (0xd4282b04)
+#define PMU_CAP_CORE5_IDLE_CFG (0xd4282b08)
+#define PMU_CAP_CORE6_IDLE_CFG (0xd4282b0c)
+#define PMU_CAP_CORE7_IDLE_CFG (0xd4282b10)
+
+#define PMU_C0_CAPMP_IDLE_CFG0 (0xd4282920)
+#define PMU_C0_CAPMP_IDLE_CFG1 (0xd42828e4)
+#define PMU_C0_CAPMP_IDLE_CFG2 (0xd4282950)
+#define PMU_C0_CAPMP_IDLE_CFG3 (0xd4282954)
+#define PMU_C1_CAPMP_IDLE_CFG0 (0xd4282b14)
+#define PMU_C1_CAPMP_IDLE_CFG1 (0xd4282b18)
+#define PMU_C1_CAPMP_IDLE_CFG2 (0xd4282b1c)
+#define PMU_C1_CAPMP_IDLE_CFG3 (0xd4282b20)
+
+#define PMU_ACPR_CLUSTER0_REG (0xd4051090)
+#define PMU_ACPR_CLUSTER1_REG (0xd4051094)
+
+#define CPU_PWR_DOWN_VALUE (0x3)
+#define CLUSTER_PWR_DOWN_VALUE (0x3)
+#define CLUSTER_AXISDO_OFFSET (31)
+
+struct pmu_cap_wakeup {
+ unsigned int pmu_cap_core0_wakeup;
+ unsigned int pmu_cap_core1_wakeup;
+ unsigned int pmu_cap_core2_wakeup;
+ unsigned int pmu_cap_core3_wakeup;
+};
+
+/* D1P */
+void spacemit_top_on(u_register_t mpidr)
+{
+ unsigned int *cluster0_acpr = NULL;
+ unsigned int *cluster1_acpr = NULL;
+
+ cluster0_acpr = (unsigned int *)PMU_ACPR_CLUSTER0_REG;
+ cluster1_acpr = (unsigned int *)PMU_ACPR_CLUSTER1_REG;
+
+ unsigned int value = readl(cluster0_acpr);
+ value &= ~(1 << CLUSTER_AXISDO_OFFSET);
+ writel(value, cluster0_acpr);
+
+ value = readl(cluster1_acpr);
+ value &= ~(1 << CLUSTER_AXISDO_OFFSET);
+ writel(value, cluster1_acpr);
+}
+
+/* D1P */
+void spacemit_top_off(u_register_t mpidr)
+{
+ unsigned int *cluster0_acpr = NULL;
+ unsigned int *cluster1_acpr = NULL;
+
+ cluster0_acpr = (unsigned int *)PMU_ACPR_CLUSTER0_REG;
+ cluster1_acpr = (unsigned int *)PMU_ACPR_CLUSTER1_REG;
+
+ unsigned int value = readl(cluster0_acpr);
+ value |= (1 << CLUSTER_AXISDO_OFFSET);
+ writel(value, cluster0_acpr);
+
+ value = readl(cluster1_acpr);
+ value |= (1 << CLUSTER_AXISDO_OFFSET);
+ writel(value, cluster1_acpr);
+}
+
+/* M2 */
+void spacemit_cluster_on(u_register_t mpidr)
+{
+ unsigned int target_cpu_idx, value;
+ unsigned int *cluster_assert_base0 = NULL;
+ unsigned int *cluster_assert_base1 = NULL;
+ unsigned int *cluster_assert_base2 = NULL;
+ unsigned int *cluster_assert_base3 = NULL;
+ unsigned int *cluster_assert_base4 = NULL;
+ unsigned int *cluster_assert_base5 = NULL;
+ unsigned int *cluster_assert_base6 = NULL;
+ unsigned int *cluster_assert_base7 = NULL;
+
+ target_cpu_idx = MPIDR_AFFLVL1_VAL(mpidr) * PLATFORM_MAX_CPUS_PER_CLUSTER
+ + MPIDR_AFFLVL0_VAL(mpidr);
+
+ switch (target_cpu_idx) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ cluster_assert_base0 = (unsigned int *)PMU_C0_CAPMP_IDLE_CFG0;
+ cluster_assert_base1 = (unsigned int *)PMU_C0_CAPMP_IDLE_CFG1;
+ cluster_assert_base2 = (unsigned int *)PMU_C0_CAPMP_IDLE_CFG2;
+ cluster_assert_base3 = (unsigned int *)PMU_C0_CAPMP_IDLE_CFG3;
+
+ /* cluster vote */
+ /* M2 */
+ value = readl(cluster_assert_base0);
+ value &= ~CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base0);
+
+ value = readl(cluster_assert_base1);
+ value &= ~CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base1);
+
+ value = readl(cluster_assert_base2);
+ value &= ~CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base2);
+
+ value = readl(cluster_assert_base3);
+ value &= ~CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base3);
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ cluster_assert_base4 = (unsigned int *)PMU_C1_CAPMP_IDLE_CFG0;
+ cluster_assert_base5 = (unsigned int *)PMU_C1_CAPMP_IDLE_CFG1;
+ cluster_assert_base6 = (unsigned int *)PMU_C1_CAPMP_IDLE_CFG2;
+ cluster_assert_base7 = (unsigned int *)PMU_C1_CAPMP_IDLE_CFG3;
+
+ /* cluster vote */
+ /* M2 */
+ value = readl(cluster_assert_base4);
+ value &= ~CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base4);
+
+ value = readl(cluster_assert_base5);
+ value &= ~CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base5);
+
+ value = readl(cluster_assert_base6);
+ value &= ~CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base6);
+
+ value = readl(cluster_assert_base7);
+ value &= ~CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base7);
+ break;
+ }
+}
+
+/* M2 */
+void spacemit_cluster_off(u_register_t mpidr)
+{
+ unsigned int target_cpu_idx, value;
+ unsigned int *cluster_assert_base0 = NULL;
+ unsigned int *cluster_assert_base1 = NULL;
+ unsigned int *cluster_assert_base2 = NULL;
+ unsigned int *cluster_assert_base3 = NULL;
+ unsigned int *cluster_assert_base4 = NULL;
+ unsigned int *cluster_assert_base5 = NULL;
+ unsigned int *cluster_assert_base6 = NULL;
+ unsigned int *cluster_assert_base7 = NULL;
+
+ target_cpu_idx = MPIDR_AFFLVL1_VAL(mpidr) * PLATFORM_MAX_CPUS_PER_CLUSTER
+ + MPIDR_AFFLVL0_VAL(mpidr);
+
+ switch (target_cpu_idx) {
+ case 0:
+ case 1:
+ case 2:
+ case 3:
+ cluster_assert_base0 = (unsigned int *)PMU_C0_CAPMP_IDLE_CFG0;
+ cluster_assert_base1 = (unsigned int *)PMU_C0_CAPMP_IDLE_CFG1;
+ cluster_assert_base2 = (unsigned int *)PMU_C0_CAPMP_IDLE_CFG2;
+ cluster_assert_base3 = (unsigned int *)PMU_C0_CAPMP_IDLE_CFG3;
+
+ /* cluster vote */
+ /* M2 */
+ value = readl(cluster_assert_base0);
+ value |= CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base0);
+
+ value = readl(cluster_assert_base1);
+ value |= CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base1);
+
+ value = readl(cluster_assert_base2);
+ value |= CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base2);
+
+ value = readl(cluster_assert_base3);
+ value |= CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base3);
+ break;
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ cluster_assert_base4 = (unsigned int *)PMU_C1_CAPMP_IDLE_CFG0;
+ cluster_assert_base5 = (unsigned int *)PMU_C1_CAPMP_IDLE_CFG1;
+ cluster_assert_base6 = (unsigned int *)PMU_C1_CAPMP_IDLE_CFG2;
+ cluster_assert_base7 = (unsigned int *)PMU_C1_CAPMP_IDLE_CFG3;
+
+ /* cluster vote */
+ /* M2 */
+ value = readl(cluster_assert_base4);
+ value |= CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base4);
+
+ value = readl(cluster_assert_base5);
+ value |= CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base5);
+
+ value = readl(cluster_assert_base6);
+ value |= CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base6);
+
+ value = readl(cluster_assert_base7);
+ value |= CLUSTER_PWR_DOWN_VALUE;
+ writel(value, cluster_assert_base7);
+ break;
+ }
+}
+
+void spacemit_wakeup_cpu(u_register_t mpidr)
+{
+ unsigned int *cpu_reset_base;
+ struct pmu_cap_wakeup *pmu_cap_wakeup;
+ unsigned int cur_cluster, cur_cpu;
+ unsigned int target_cpu_idx;
+ unsigned int cur_hartid = current_hartid();
+
+ cur_cluster = MPIDR_AFFLVL1_VAL(cur_hartid);
+ cur_cpu = MPIDR_AFFLVL0_VAL(cur_hartid);
+
+ pmu_cap_wakeup = (struct pmu_cap_wakeup *)((cur_cluster == 0) ? (unsigned int *)CPU_RESET_BASE_ADDR :
+ (unsigned int *)C1_CPU_RESET_BASE_ADDR);
+
+ switch (cur_cpu) {
+ case 0:
+ cpu_reset_base = &pmu_cap_wakeup->pmu_cap_core0_wakeup;
+ break;
+ case 1:
+ cpu_reset_base = &pmu_cap_wakeup->pmu_cap_core1_wakeup;
+ break;
+ case 2:
+ cpu_reset_base = &pmu_cap_wakeup->pmu_cap_core2_wakeup;
+ break;
+ case 3:
+ cpu_reset_base = &pmu_cap_wakeup->pmu_cap_core3_wakeup;
+ break;
+ }
+
+ target_cpu_idx = MPIDR_AFFLVL1_VAL(mpidr) * PLATFORM_MAX_CPUS_PER_CLUSTER
+ + MPIDR_AFFLVL0_VAL(mpidr);
+
+ writel(1 << target_cpu_idx, cpu_reset_base);
+}
+
+void spacemit_assert_cpu(u_register_t mpidr)
+{
+ unsigned int target_cpu_idx;
+ unsigned int *cpu_assert_base = NULL;
+
+ target_cpu_idx = MPIDR_AFFLVL1_VAL(mpidr) * PLATFORM_MAX_CPUS_PER_CLUSTER
+ + MPIDR_AFFLVL0_VAL(mpidr);
+
+ switch (target_cpu_idx) {
+ case 0:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE0_IDLE_CFG;
+ break;
+ case 1:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE1_IDLE_CFG;
+ break;
+ case 2:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE2_IDLE_CFG;
+ break;
+ case 3:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE3_IDLE_CFG;
+ break;
+ case 4:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE4_IDLE_CFG;
+ break;
+ case 5:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE5_IDLE_CFG;
+ break;
+ case 6:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE6_IDLE_CFG;
+ break;
+ case 7:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE7_IDLE_CFG;
+ break;
+ }
+
+ /* cpu vote */
+ /* C2 */
+ unsigned int value = readl(cpu_assert_base);
+ value |= CPU_PWR_DOWN_VALUE;
+ writel(value, cpu_assert_base);
+}
+
+void spacemit_deassert_cpu(void)
+{
+ unsigned int mpidr = current_hartid();
+
+ /* clear the idle bit */
+ unsigned int target_cpu_idx;
+ unsigned int *cpu_assert_base = NULL;
+
+ target_cpu_idx = MPIDR_AFFLVL1_VAL(mpidr) * PLATFORM_MAX_CPUS_PER_CLUSTER
+ + MPIDR_AFFLVL0_VAL(mpidr);
+
+ switch (target_cpu_idx) {
+ case 0:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE0_IDLE_CFG;
+ break;
+ case 1:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE1_IDLE_CFG;
+ break;
+ case 2:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE2_IDLE_CFG;
+ break;
+ case 3:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE3_IDLE_CFG;
+ break;
+ case 4:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE4_IDLE_CFG;
+ break;
+ case 5:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE5_IDLE_CFG;
+ break;
+ case 6:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE6_IDLE_CFG;
+ break;
+ case 7:
+ cpu_assert_base = (unsigned int *)PMU_CAP_CORE7_IDLE_CFG;
+ break;
+ }
+
+ /* de-vote cpu */
+ unsigned int value = readl(cpu_assert_base);
+ value &= ~CPU_PWR_DOWN_VALUE;
+ writel(value, cpu_assert_base);
+}
diff --git a/lib/utils/psci/spacemit/plat/plat_pm.c b/lib/utils/psci/spacemit/plat/plat_pm.c
new file mode 100644
index 0000000..464a56a
--- /dev/null
+++ b/lib/utils/psci/spacemit/plat/plat_pm.c
@@ -0,0 +1,258 @@
+#include <sbi/sbi_types.h>
+#include <sbi/riscv_asm.h>
+#include <sbi_utils/cci/cci.h>
+#include <sbi_utils/psci/psci.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_console.h>
+#include <sbi_utils/psci/plat/arm/common/arm_def.h>
+#include <sbi_utils/irqchip/fdt_irqchip_plic.h>
+#include "underly_implement.h"
+
+#define CORE_PWR_STATE(state) \
+ ((state)->pwr_domain_state[MPIDR_AFFLVL0])
+#define CLUSTER_PWR_STATE(state) \
+ ((state)->pwr_domain_state[MPIDR_AFFLVL1])
+#define SYSTEM_PWR_STATE(state) \
+ ((state)->pwr_domain_state[PLAT_MAX_PWR_LVL])
+
+static int spacemit_pwr_domain_on(u_register_t mpidr)
+{
+ /* wakeup the cpu */
+ spacemit_wakeup_cpu(mpidr);
+
+ return 0;
+}
+
+static void spacemit_pwr_domain_on_finish(const psci_power_state_t *target_state)
+{
+ unsigned int hartid = current_hartid();
+
+ if (SYSTEM_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) {
+ /* D1P */
+ spacemit_top_on(hartid);
+ }
+
+ /*
+ * Enable CCI coherency for this cluster.
+ * No need for locks as no other cpu is active at the moment.
+ */
+ if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
+ spacemit_cluster_on(hartid);
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1X)
+ /* disable the tcm */
+ csr_write(CSR_TCMCFG, 0);
+#endif
+ cci_enable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(hartid));
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1X)
+ /* enable the tcm */
+ csr_write(CSR_TCMCFG, 1);
+#endif
+ }
+}
+
+static int spacemit_pwr_domain_off_early(const psci_power_state_t *target_state)
+{
+ /* the ipi's pending is cleared before */
+ /* disable the plic irq */
+ fdt_plic_context_exit();
+ /* clear the external irq pending */
+ csr_clear(CSR_MIP, MIP_MEIP);
+ csr_clear(CSR_MIP, MIP_SEIP);
+
+ /* here we clear the sstimer pending if this core have */
+ if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), SBI_HART_EXT_SSTC)) {
+ csr_write(CSR_STIMECMP, 0xffffffffffffffff);
+ }
+
+ return 0;
+}
+
+static void spacemit_pwr_domain_off(const psci_power_state_t *target_state)
+{
+ unsigned int hartid = current_hartid();
+
+ if (CLUSTER_PWR_STATE(target_state) == PLAT_MAX_OFF_STATE) {
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1X)
+ /* disable the tcm */
+ csr_write(CSR_TCMCFG, 0);
+#endif
+ cci_disable_snoop_dvm_reqs(MPIDR_AFFLVL1_VAL(hartid));
+ spacemit_cluster_off(hartid);
+ }
+
+ if (SYSTEM_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) {
+ spacemit_top_off(hartid);
+ }
+
+ spacemit_assert_cpu(hartid);
+
+}
+
+static void spacemit_pwr_domain_pwr_down_wfi(const psci_power_state_t *target_state)
+{
+ while (1) {
+ asm volatile ("wfi");
+ }
+}
+
+static void spacemit_pwr_domain_on_finish_late(const psci_power_state_t *target_state)
+{
+ spacemit_deassert_cpu();
+}
+
+static int _spacemit_validate_power_state(unsigned int power_state,
+ psci_power_state_t *req_state)
+{
+ unsigned int pstate = psci_get_pstate_type(power_state);
+ unsigned int pwr_lvl = psci_get_pstate_pwrlvl(power_state);
+ unsigned int i;
+
+ if (req_state == NULL) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ if (pwr_lvl > PLAT_MAX_PWR_LVL)
+ return PSCI_E_INVALID_PARAMS;
+
+ /* Sanity check the requested state */
+ if (pstate == PSTATE_TYPE_STANDBY) {
+ /*
+ * It's possible to enter standby only on power level 0
+ * Ignore any other power level.
+ */
+ if (pwr_lvl != ARM_PWR_LVL0)
+ return PSCI_E_INVALID_PARAMS;
+
+ req_state->pwr_domain_state[ARM_PWR_LVL0] =
+ ARM_LOCAL_STATE_RET;
+ } else {
+ for (i = ARM_PWR_LVL0; i <= pwr_lvl; i++)
+ req_state->pwr_domain_state[i] =
+ ARM_LOCAL_STATE_OFF;
+ }
+
+ /*
+ * We expect the 'state id' to be zero.
+ */
+ if (psci_get_pstate_id(power_state) != 0U)
+ return PSCI_E_INVALID_PARAMS;
+
+ return PSCI_E_SUCCESS;
+}
+
+static int spacemit_validate_power_state(unsigned int power_state,
+ psci_power_state_t *req_state)
+{
+ int rc;
+
+ rc = _spacemit_validate_power_state(power_state, req_state);
+
+ return rc;
+}
+
+static void spacemit_pwr_domain_suspend(const psci_power_state_t *target_state)
+{
+ unsigned int clusterid;
+ unsigned int hartid = current_hartid();
+
+ /*
+ * CSS currently supports retention only at cpu level. Just return
+ * as nothing is to be done for retention.
+ */
+ if (CORE_PWR_STATE(target_state) == ARM_LOCAL_STATE_RET)
+ return;
+
+
+ if (CORE_PWR_STATE(target_state) != ARM_LOCAL_STATE_OFF) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ /* Cluster is to be turned off, so disable coherency */
+ if (CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) {
+ clusterid = MPIDR_AFFLVL1_VAL(hartid);
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1X)
+ /* disable the tcm */
+ csr_write(CSR_TCMCFG, 0);
+#endif
+ cci_disable_snoop_dvm_reqs(clusterid);
+
+ spacemit_cluster_off(hartid);
+ }
+
+ if (SYSTEM_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) {
+ /* D1P */
+ spacemit_top_off(hartid);
+ }
+
+ spacemit_assert_cpu(hartid);
+}
+
+static void spacemit_pwr_domain_suspend_finish(const psci_power_state_t *target_state)
+{
+ unsigned int clusterid;
+ unsigned int hartid = current_hartid();
+
+ /* Return as nothing is to be done on waking up from retention. */
+ if (CORE_PWR_STATE(target_state) == ARM_LOCAL_STATE_RET)
+ return;
+
+ if (CORE_PWR_STATE(target_state) != ARM_LOCAL_STATE_OFF) {
+ sbi_printf("%s:%d\n", __func__, __LINE__);
+ sbi_hart_hang();
+ }
+
+ /*
+ * Perform the common cluster specific operations i.e enable coherency
+ * if this cluster was off.
+ */
+ if (CLUSTER_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) {
+ clusterid = MPIDR_AFFLVL1_VAL(hartid);
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1X)
+ /* disable the tcm */
+ csr_write(CSR_TCMCFG, 0);
+#endif
+ cci_enable_snoop_dvm_reqs(clusterid);
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1X)
+ /* enable the tcm */
+ csr_write(CSR_TCMCFG, 1);
+#endif
+ spacemit_cluster_on(hartid);
+ }
+
+ if (SYSTEM_PWR_STATE(target_state) == ARM_LOCAL_STATE_OFF) {
+ /* D1P */
+ spacemit_top_on(hartid);
+ }
+
+ /* Do something */
+ spacemit_deassert_cpu();
+}
+
+static void spacemit_pwr_domain_suspend_pwrdown_early(const psci_power_state_t *target_state)
+{
+ csr_clear(CSR_MIE, MIP_SSIP | MIP_MSIP | MIP_STIP | MIP_MTIP | MIP_SEIP | MIP_MEIP);
+}
+
+static const plat_psci_ops_t spacemit_psci_ops = {
+ .cpu_standby = NULL,
+ .pwr_domain_on = spacemit_pwr_domain_on,
+ .pwr_domain_on_finish = spacemit_pwr_domain_on_finish,
+ .pwr_domain_off_early = spacemit_pwr_domain_off_early,
+ .pwr_domain_off = spacemit_pwr_domain_off,
+ .pwr_domain_pwr_down_wfi = spacemit_pwr_domain_pwr_down_wfi,
+ .pwr_domain_on_finish_late = spacemit_pwr_domain_on_finish_late,
+ .validate_power_state = spacemit_validate_power_state,
+ .pwr_domain_suspend = spacemit_pwr_domain_suspend,
+ .pwr_domain_suspend_pwrdown_early = spacemit_pwr_domain_suspend_pwrdown_early,
+ .pwr_domain_suspend_finish = spacemit_pwr_domain_suspend_finish,
+};
+
+int plat_setup_psci_ops(uintptr_t sec_entrypoint, const plat_psci_ops_t **psci_ops)
+{
+ *psci_ops = &spacemit_psci_ops;
+
+ return 0;
+}
diff --git a/lib/utils/psci/spacemit/plat/underly_implement.h b/lib/utils/psci/spacemit/plat/underly_implement.h
new file mode 100644
index 0000000..dd6c972
--- /dev/null
+++ b/lib/utils/psci/spacemit/plat/underly_implement.h
@@ -0,0 +1,14 @@
+#ifndef __UNDERLY_IMPLEMENT__H__
+#define __UNDERLY_IMPLEMENT__H__
+
+#include <sbi/sbi_types.h>
+
+void spacemit_top_on(u_register_t mpidr);
+void spacemit_top_off(u_register_t mpidr);
+void spacemit_cluster_on(u_register_t mpidr);
+void spacemit_cluster_off(u_register_t mpidr);
+void spacemit_wakeup_cpu(u_register_t mpidr);
+void spacemit_assert_cpu(u_register_t mpidr);
+void spacemit_deassert_cpu(void);
+
+#endif
diff --git a/lib/utils/psci/spacemit/spacemit_topology.c b/lib/utils/psci/spacemit/spacemit_topology.c
new file mode 100644
index 0000000..de327d8
--- /dev/null
+++ b/lib/utils/psci/spacemit/spacemit_topology.c
@@ -0,0 +1,26 @@
+#include <sbi_utils/psci/psci.h>
+
+static unsigned char plat_power_domain_tree_desc[] = {
+ /* No of root nodes */
+ 1,
+ /* Num of children for the root node */
+ 0,
+ /* Num of children for the first cluster node */
+ 0,
+ /* Num of children for the second cluster node */
+ 0,
+};
+
+int plat_core_pos_by_mpidr(u_register_t mpidr)
+{
+ unsigned int cluster = MPIDR_AFFLVL1_VAL(mpidr);
+ unsigned int core = MPIDR_AFFLVL0_VAL(mpidr);
+
+ return (cluster == 0) ? core :
+ (plat_power_domain_tree_desc[2] + core);
+}
+
+unsigned char *plat_get_power_domain_tree_desc(void)
+{
+ return plat_power_domain_tree_desc;
+}
diff --git a/lib/utils/serial/uart8250.c b/lib/utils/serial/uart8250.c
index 99bf1bf..87a6bc6 100644
--- a/lib/utils/serial/uart8250.c
+++ b/lib/utils/serial/uart8250.c
@@ -131,6 +131,11 @@ int uart8250_init(unsigned long base, u32 in_freq, u32 baudrate, u32 reg_shift,
/* Set scratchpad */
set_reg(UART_SCR_OFFSET, 0x00);
+#ifdef CONFIG_PLATFORM_SPACEMIT_K1X
+ /* enable uart. */
+ set_reg(UART_IER_OFFSET, 0x40);
+#endif
+
sbi_console_set_device(&uart8250_console);
return 0;
diff --git a/lib/utils/timer/aclint_mtimer.c b/lib/utils/timer/aclint_mtimer.c
index 13af5d8..a9dbea2 100644
--- a/lib/utils/timer/aclint_mtimer.c
+++ b/lib/utils/timer/aclint_mtimer.c
@@ -15,6 +15,7 @@
#include <sbi/sbi_error.h>
#include <sbi/sbi_scratch.h>
#include <sbi/sbi_timer.h>
+#include <sbi/sbi_platform.h>
#include <sbi_utils/timer/aclint_mtimer.h>
static unsigned long mtimer_ptr_offset;
@@ -183,6 +184,7 @@ int aclint_mtimer_cold_init(struct aclint_mtimer_data *mt,
u32 i;
int rc;
struct sbi_scratch *scratch;
+ const struct sbi_platform *sbi = sbi_platform_thishart_ptr();
/* Sanity checks */
if (!mt ||
@@ -218,7 +220,7 @@ int aclint_mtimer_cold_init(struct aclint_mtimer_data *mt,
/* Update MTIMER pointer in scratch space */
for (i = 0; i < mt->hart_count; i++) {
- scratch = sbi_hartid_to_scratch(mt->first_hartid + i);
+ scratch = sbi_hartid_to_scratch(sbi->hart_index2id[i]);
if (!scratch)
return SBI_ENOENT;
mtimer_set_hart_data_ptr(scratch, mt);
diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
index 72768ed..350e41e 100644
--- a/platform/generic/Kconfig
+++ b/platform/generic/Kconfig
@@ -52,6 +52,42 @@ config PLATFORM_STARFIVE_JH7110
bool "StarFive JH7110 support"
default n
+config PLATFORM_SPACEMIT_K1PRO
+ bool "Spacemit K1pro support"
+ default n
+
+if PLATFORM_SPACEMIT_K1PRO
+ config PLATFORM_SPACEMIT_K1PRO_FPGA
+ bool "Spacemit K1pro board fpga"
+ default n
+
+ config PLATFORM_SPACEMIT_K1PRO_QEMU
+ bool "Spacemit K1pro board qemu"
+ default n
+
+ config PLATFORM_SPACEMIT_K1PRO_SIM
+ bool "Spacemit K1pro board sim"
+ default n
+
+ config PLATFORM_SPACEMIT_K1PRO_VERIFY
+ bool "Spacemit K1pro board verify"
+ default n
+endif
+
+config PLATFORM_SPACEMIT_K1X
+ bool "Spacemit K1x support"
+ default n
+
+if PLATFORM_SPACEMIT_K1X
+ config PLATFORM_SPACEMIT_K1X_FPGA
+ bool "Spacemit K1x board fpag"
+ default n
+
+ config PLATFORM_SPACEMIT_K1X_EVB
+ bool "Spacemit K1x board evb"
+ default n
+endif
+
source "$(OPENSBI_SRC_DIR)/platform/generic/andes/Kconfig"
endif
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index ee0df38..6366769 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -4,6 +4,7 @@ CONFIG_PLATFORM_RENESAS_RZFIVE=y
CONFIG_PLATFORM_SIFIVE_FU540=y
CONFIG_PLATFORM_SIFIVE_FU740=y
CONFIG_PLATFORM_STARFIVE_JH7110=y
+CONFIG_PLATFORM_SPACEMIT_K1PRO=y
CONFIG_FDT_GPIO=y
CONFIG_FDT_GPIO_SIFIVE=y
CONFIG_FDT_GPIO_STARFIVE=y
@@ -37,4 +38,4 @@ CONFIG_FDT_SERIAL_XILINX_UARTLITE=y
CONFIG_FDT_TIMER=y
CONFIG_FDT_TIMER_MTIMER=y
CONFIG_FDT_TIMER_PLMT=y
-CONFIG_SERIAL_SEMIHOSTING=y
+CONFIG_SERIAL_SEMIHOSTING=n
diff --git a/platform/generic/configs/k1-x_fpga_1x4_defconfig b/platform/generic/configs/k1-x_fpga_1x4_defconfig
new file mode 100644
index 0000000..c9419d6
--- /dev/null
+++ b/platform/generic/configs/k1-x_fpga_1x4_defconfig
@@ -0,0 +1,16 @@
+CONFIG_PLATFORM_SPACEMIT_K1X=y
+CONFIG_PLATFORM_SPACEMIT_K1X_FPGA=y
+# CONFIG_SBI_ECALL_TIME is not set
+CONFIG_FDT_IPI=y
+CONFIG_FDT_IPI_MSWI=y
+CONFIG_FDT_IRQCHIP=y
+CONFIG_FDT_IRQCHIP_PLIC=y
+CONFIG_FDT_RESET=y
+CONFIG_FDT_RESET_HTIF=y
+CONFIG_FDT_RESET_SIFIVE_TEST=y
+CONFIG_FDT_RESET_SUNXI_WDT=y
+CONFIG_FDT_RESET_THEAD=y
+CONFIG_FDT_SERIAL=y
+CONFIG_FDT_SERIAL_UART8250=y
+CONFIG_ARM_PSCI_SUPPORT=y
+CONFIG_ARM_NON_SCMI_SUPPORT=y
diff --git a/platform/generic/configs/k1-x_fpga_2x2_defconfig b/platform/generic/configs/k1-x_fpga_2x2_defconfig
new file mode 100644
index 0000000..c9419d6
--- /dev/null
+++ b/platform/generic/configs/k1-x_fpga_2x2_defconfig
@@ -0,0 +1,16 @@
+CONFIG_PLATFORM_SPACEMIT_K1X=y
+CONFIG_PLATFORM_SPACEMIT_K1X_FPGA=y
+# CONFIG_SBI_ECALL_TIME is not set
+CONFIG_FDT_IPI=y
+CONFIG_FDT_IPI_MSWI=y
+CONFIG_FDT_IRQCHIP=y
+CONFIG_FDT_IRQCHIP_PLIC=y
+CONFIG_FDT_RESET=y
+CONFIG_FDT_RESET_HTIF=y
+CONFIG_FDT_RESET_SIFIVE_TEST=y
+CONFIG_FDT_RESET_SUNXI_WDT=y
+CONFIG_FDT_RESET_THEAD=y
+CONFIG_FDT_SERIAL=y
+CONFIG_FDT_SERIAL_UART8250=y
+CONFIG_ARM_PSCI_SUPPORT=y
+CONFIG_ARM_NON_SCMI_SUPPORT=y
diff --git a/platform/generic/configs/k1-x_fpga_defconfig b/platform/generic/configs/k1-x_fpga_defconfig
new file mode 100644
index 0000000..c9419d6
--- /dev/null
+++ b/platform/generic/configs/k1-x_fpga_defconfig
@@ -0,0 +1,16 @@
+CONFIG_PLATFORM_SPACEMIT_K1X=y
+CONFIG_PLATFORM_SPACEMIT_K1X_FPGA=y
+# CONFIG_SBI_ECALL_TIME is not set
+CONFIG_FDT_IPI=y
+CONFIG_FDT_IPI_MSWI=y
+CONFIG_FDT_IRQCHIP=y
+CONFIG_FDT_IRQCHIP_PLIC=y
+CONFIG_FDT_RESET=y
+CONFIG_FDT_RESET_HTIF=y
+CONFIG_FDT_RESET_SIFIVE_TEST=y
+CONFIG_FDT_RESET_SUNXI_WDT=y
+CONFIG_FDT_RESET_THEAD=y
+CONFIG_FDT_SERIAL=y
+CONFIG_FDT_SERIAL_UART8250=y
+CONFIG_ARM_PSCI_SUPPORT=y
+CONFIG_ARM_NON_SCMI_SUPPORT=y
diff --git a/platform/generic/configs/k1_defconfig b/platform/generic/configs/k1_defconfig
new file mode 100644
index 0000000..08f1703
--- /dev/null
+++ b/platform/generic/configs/k1_defconfig
@@ -0,0 +1,16 @@
+CONFIG_PLATFORM_SPACEMIT_K1X=y
+CONFIG_PLATFORM_SPACEMIT_K1X_EVB=y
+# CONFIG_SBI_ECALL_TIME is not set
+CONFIG_FDT_IPI=y
+CONFIG_FDT_IPI_MSWI=y
+CONFIG_FDT_IRQCHIP=y
+CONFIG_FDT_IRQCHIP_PLIC=y
+CONFIG_FDT_RESET=y
+CONFIG_FDT_RESET_HTIF=y
+CONFIG_FDT_RESET_SIFIVE_TEST=y
+CONFIG_FDT_RESET_SUNXI_WDT=y
+CONFIG_FDT_RESET_THEAD=y
+CONFIG_FDT_SERIAL=y
+CONFIG_FDT_SERIAL_UART8250=y
+CONFIG_ARM_PSCI_SUPPORT=y
+CONFIG_ARM_NON_SCMI_SUPPORT=y
diff --git a/platform/generic/include/spacemit/k1x/core_common.h b/platform/generic/include/spacemit/k1x/core_common.h
new file mode 100644
index 0000000..4bee5e1
--- /dev/null
+++ b/platform/generic/include/spacemit/k1x/core_common.h
@@ -0,0 +1,13 @@
+#ifndef __K1X_CORE_COMMON_H__
+#define __K1X_CORE_COMMON_H__
+
+
+#define CSR_MHCR 0x7c1
+#define CSR_MSETUP 0x7c0
+#define CSR_MHINT 0x7c5
+#define CSR_ML2SETUP 0x7F0
+
+#define CACHE_LINE_SIZE (64)
+#define CACHE_INV_ADDR_Msk (0xffffffffffffffff << 6)
+
+#endif /* __K1X_CORE_COMMON_H__ */
diff --git a/platform/generic/include/spacemit/k1x/k1x_evb.h b/platform/generic/include/spacemit/k1x/k1x_evb.h
new file mode 100644
index 0000000..b951105
--- /dev/null
+++ b/platform/generic/include/spacemit/k1x/k1x_evb.h
@@ -0,0 +1,72 @@
+#ifndef __K1X_EVB_CONFIG_H__
+#define __K1X_EVB_CONFIG_H__
+
+/***************************cci******************************/
+#define PLATFORM_CCI_ADDR (0xD8500000)
+
+#define PLAT_CCI_CLUSTER0_IFACE_IX 0
+#define PLAT_CCI_CLUSTER1_IFACE_IX 1
+#define PLAT_CCI_CLUSTER2_IFACE_IX 2
+#define PLAT_CCI_CLUSTER3_IFACE_IX 3
+
+#define PLAT_CCI_MAP static const int cci_map[] = { \
+ PLAT_CCI_CLUSTER0_IFACE_IX, \
+ PLAT_CCI_CLUSTER1_IFACE_IX, \
+ PLAT_CCI_CLUSTER2_IFACE_IX, \
+ PLAT_CCI_CLUSTER3_IFACE_IX, \
+};
+
+/***************************cpu******************************/
+#define CPU_RESET_BASE_ADDR (0xD428292C)
+#define C0_RVBADDR_LO_ADDR (0xD4282DB0)
+#define C0_RVBADDR_HI_ADDR (0xD4282DB4)
+
+#define C1_RVBADDR_LO_ADDR (0xD4282C00 + 0x2B0)
+#define C1_RVBADDR_HI_ADDR (0xD4282C00 + 0X2B4)
+
+/***************************mailbox***************************/
+#define SCMI_MAILBOX_SHARE_MEM (0x2f902080)
+#define PLAT_MAILBOX_REG_BASE (0x2f824000)
+
+/****************************scmi*****************************/
+#define PLAT_SCMI_DOMAIN_MAP {0, 1, 2, 3}
+
+/*************************cpu topology************************/
+#define ARM_SYSTEM_COUNT (1U)
+/* this is the max cluster count of this platform */
+#define PLATFORM_CLUSTER_COUNT (2U)
+/* this is the max core count of this platform */
+#define PLATFORM_CORE_COUNT (8U)
+/* this is the max NUN CPU power domains */
+#define PSCI_NUM_NON_CPU_PWR_DOMAINS (3U)
+/* this is the max cpu cores per cluster*/
+#define PLATFORM_MAX_CPUS_PER_CLUSTER (4U)
+
+#define CLUSTER_INDEX_IN_CPU_TOPOLOGY (1U)
+#define CLUSTER0_INDEX_IN_CPU_TOPOLOGY (2U)
+#define CLUSTER1_INDEX_IN_CPU_TOPOLOGY (3U)
+
+#define PSCI_NUM_PWR_DOMAINS \
+ (ARM_SYSTEM_COUNT + plat_get_power_domain_tree_desc()[CLUSTER_INDEX_IN_CPU_TOPOLOGY] \
+ + plat_get_power_domain_tree_desc()[CLUSTER0_INDEX_IN_CPU_TOPOLOGY] + \
+ plat_get_power_domain_tree_desc()[CLUSTER1_INDEX_IN_CPU_TOPOLOGY])
+
+/***************************psci pwr level********************/
+/* This is the power level corresponding to a CPU */
+#define PSCI_CPU_PWR_LVL 0U
+#define PLAT_MAX_PWR_LVL 2U
+
+/***************************cpu affin*************************/
+#define MPIDR_AFFINITY0_MASK 0x3U
+#define MPIDR_AFFINITY1_MASK 0xfU
+#define MPIDR_AFF0_SHIFT 0U
+#define MPIDR_AFF1_SHIFT 2U
+
+/**************************cluster power domain***************/
+#define CLUSTER0_L2_CACHE_FLUSH_REG_BASE (0xD84401B0)
+#define CLUSTER1_L2_CACHE_FLUSH_REG_BASE (0xD84401B4)
+
+#define L2_CACHE_FLUSH_REQUEST_BIT_OFFSET (0x1)
+#define L2_CACHE_FLUSH_DONE_BIT_OFFSET (0x3)
+
+#endif /* __K1X_EVB_CONFIG_H__ */
diff --git a/platform/generic/include/spacemit/k1x/k1x_fpga.h b/platform/generic/include/spacemit/k1x/k1x_fpga.h
new file mode 100644
index 0000000..4748c86
--- /dev/null
+++ b/platform/generic/include/spacemit/k1x/k1x_fpga.h
@@ -0,0 +1,73 @@
+#ifndef __K1X_FPGA_CONFIG_H__
+#define __K1X_FPGA_CONFIG_H__
+
+/***************************cci******************************/
+#define PLATFORM_CCI_ADDR (0xD8500000)
+
+#define PLAT_CCI_CLUSTER0_IFACE_IX 0
+#define PLAT_CCI_CLUSTER1_IFACE_IX 1
+#define PLAT_CCI_CLUSTER2_IFACE_IX 2
+#define PLAT_CCI_CLUSTER3_IFACE_IX 3
+
+#define PLAT_CCI_MAP static const int cci_map[] = { \
+ PLAT_CCI_CLUSTER0_IFACE_IX, \
+ PLAT_CCI_CLUSTER1_IFACE_IX, \
+ PLAT_CCI_CLUSTER2_IFACE_IX, \
+ PLAT_CCI_CLUSTER3_IFACE_IX, \
+};
+
+/***************************cpu******************************/
+#define CPU_RESET_BASE_ADDR (0xD428292C)
+#define C0_RVBADDR_LO_ADDR (0xD4282DB0)
+#define C0_RVBADDR_HI_ADDR (0xD4282DB4)
+
+#define C1_RVBADDR_LO_ADDR (0xD4282C00 + 0x2B0)
+#define C1_RVBADDR_HI_ADDR (0xD4282C00 + 0X2B4)
+
+/***************************mailbox***************************/
+#define SCMI_MAILBOX_SHARE_MEM (0x2f902080)
+#define PLAT_MAILBOX_REG_BASE (0x2f824000)
+
+/****************************scmi*****************************/
+#define PLAT_SCMI_SINGLE_CLUSTER_DOMAIN_MAP {0, 1, 2, 3}
+#define PLAT_SCMI_DOUBLE_CLUSTER_DOMAIN_MAP {0, 1, 4, 5}
+
+/*************************cpu topology************************/
+#define ARM_SYSTEM_COUNT (1U)
+/* this is the max cluster count of this platform */
+#define PLATFORM_CLUSTER_COUNT (2U)
+/* this is the max core count of this platform */
+#define PLATFORM_CORE_COUNT (8U)
+/* this is the max NUN CPU power domains */
+#define PSCI_NUM_NON_CPU_PWR_DOMAINS (3U)
+/* this is the max cpu cores per cluster*/
+#define PLATFORM_MAX_CPUS_PER_CLUSTER (4U)
+
+#define CLUSTER_INDEX_IN_CPU_TOPOLOGY (1U)
+#define CLUSTER0_INDEX_IN_CPU_TOPOLOGY (2U)
+#define CLUSTER1_INDEX_IN_CPU_TOPOLOGY (3U)
+
+#define PSCI_NUM_PWR_DOMAINS \
+ (ARM_SYSTEM_COUNT + plat_get_power_domain_tree_desc()[CLUSTER_INDEX_IN_CPU_TOPOLOGY] \
+ + plat_get_power_domain_tree_desc()[CLUSTER0_INDEX_IN_CPU_TOPOLOGY] + \
+ plat_get_power_domain_tree_desc()[CLUSTER1_INDEX_IN_CPU_TOPOLOGY])
+
+/***************************psci pwr level********************/
+/* This is the power level corresponding to a CPU */
+#define PSCI_CPU_PWR_LVL 0U
+#define PLAT_MAX_PWR_LVL 2U
+
+/***************************cpu affin*************************/
+#define MPIDR_AFFINITY0_MASK 0x3U
+#define MPIDR_AFFINITY1_MASK 0xfU
+#define MPIDR_AFF0_SHIFT 0U
+#define MPIDR_AFF1_SHIFT 2U
+
+/**************************cluster power domain***************/
+#define CLUSTER0_L2_CACHE_FLUSH_REG_BASE (0xD84401B0)
+#define CLUSTER1_L2_CACHE_FLUSH_REG_BASE (0xD84401B4)
+
+#define L2_CACHE_FLUSH_REQUEST_BIT_OFFSET (0x1)
+#define L2_CACHE_FLUSH_DONE_BIT_OFFSET (0x3)
+
+#endif /* __K1X_FPGA_CONFIG_H__ */
diff --git a/platform/generic/include/spacemit/spacemit_config.h b/platform/generic/include/spacemit/spacemit_config.h
new file mode 100644
index 0000000..d48869c
--- /dev/null
+++ b/platform/generic/include/spacemit/spacemit_config.h
@@ -0,0 +1,30 @@
+#ifndef __SPACEMIT_CONFIG_H__
+#define __SPACEMIT_CONFIG_H__
+
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1PRO)
+#include "./k1pro/core_common.h"
+
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1PRO_FPGA)
+#include "./k1pro/k1pro_fpga.h"
+#elif defined(CONFIG_PLATFORM_SPACEMIT_K1PRO_QEMU)
+#include "./k1pro/k1pro_qemu.h"
+#elif defined(CONFIG_PLATFORM_SPACEMIT_K1PRO_SIM)
+#include "./k1pro/k1pro_sim.h"
+#elif defined(CONFIG_PLATFORM_SPACEMIT_K1PRO_VERIFY)
+#include "./k1pro/k1pro_verify.h"
+#endif
+
+#endif
+
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1X)
+#include "./k1x/core_common.h"
+
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1X_FPGA)
+#include "./k1x/k1x_fpga.h"
+#elif defined(CONFIG_PLATFORM_SPACEMIT_K1X_EVB)
+#include "./k1x/k1x_evb.h"
+#endif
+
+#endif
+
+#endif /* __SPACEMIT_CONFIG_H__ */
diff --git a/platform/generic/objects.mk b/platform/generic/objects.mk
index 136853e..f3418ef 100644
--- a/platform/generic/objects.mk
+++ b/platform/generic/objects.mk
@@ -22,7 +22,7 @@ platform-objs-y += platform.o
platform-objs-y += platform_override_modules.o
# Blobs to build
-FW_TEXT_START=0x80000000
+FW_TEXT_START?=0x80000000
FW_DYNAMIC=y
FW_JUMP=y
ifeq ($(PLATFORM_RISCV_XLEN), 32)
diff --git a/platform/generic/spacemit/fw_dynamic.its b/platform/generic/spacemit/fw_dynamic.its
new file mode 100755
index 0000000..f1159d4
--- /dev/null
+++ b/platform/generic/spacemit/fw_dynamic.its
@@ -0,0 +1,31 @@
+/dts-v1/;
+
+/ {
+ description = "Configuration to load OpenSBI before U-Boot";
+ #address-cells = <2>;
+ fit,fdt-list = "of-list";
+
+ images {
+ opensbi {
+ description = "OpenSBI fw_dynamic Firmware";
+ type = "firmware";
+ os = "opensbi";
+ arch = "riscv";
+ compression = "none";
+ load = <0x0 0x0>;
+ entry = <0x0 0x0>;
+ data = /incbin/("./fw_dynamic.bin");
+ hash-1 {
+ algo = "crc32";
+ };
+ };
+ };
+ configurations {
+ default = "config_1";
+
+ config_1 {
+ description = "opensbi FIT config";
+ firmware = "opensbi";
+ };
+ };
+};
diff --git a/platform/generic/spacemit/objects.mk b/platform/generic/spacemit/objects.mk
new file mode 100644
index 0000000..92ef7eb
--- /dev/null
+++ b/platform/generic/spacemit/objects.mk
@@ -0,0 +1,7 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+
+carray-platform_override_modules-$(CONFIG_PLATFORM_SPACEMIT_K1PRO)$(CONFIG_PLATFORM_SPACEMIT_K1X) += spacemit_k1
+platform-objs-$(CONFIG_PLATFORM_SPACEMIT_K1PRO)$(CONFIG_PLATFORM_SPACEMIT_K1X) += spacemit/spacemit_k1.o
+firmware-its-$(CONFIG_PLATFORM_SPACEMIT_K1PRO)$(CONFIG_PLATFORM_SPACEMIT_K1X) += spacemit/fw_dynamic.its
diff --git a/platform/generic/spacemit/spacemit_k1.c b/platform/generic/spacemit/spacemit_k1.c
new file mode 100644
index 0000000..8664e05
--- /dev/null
+++ b/platform/generic/spacemit/spacemit_k1.c
@@ -0,0 +1,194 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2022 Spacemit.
+ */
+
+#include <libfdt.h>
+#include <platform_override.h>
+#include <sbi/riscv_asm.h>
+#include <sbi/riscv_encoding.h>
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_const.h>
+#include <sbi/sbi_hart.h>
+#include <sbi/sbi_hartmask.h>
+#include <sbi/riscv_atomic.h>
+#include <sbi/sbi_platform.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/psci/psci_lib.h>
+#include <sbi_utils/cci/cci.h>
+#include <sbi/sbi_hsm.h>
+#include <sbi/sbi_ecall_interface.h>
+#include <sbi_utils/psci/psci.h>
+#include <sbi/sbi_scratch.h>
+#include <sbi_utils/cache/cacheflush.h>
+#include <../../../lib/utils/psci/psci_private.h>
+#include <sbi_utils/psci/plat/arm/common/plat_arm.h>
+#include <sbi_utils/psci/plat/common/platform.h>
+#include <spacemit/spacemit_config.h>
+
+extern struct sbi_platform platform;
+
+PLAT_CCI_MAP
+
+static void wakeup_other_core(void)
+{
+ int i;
+ u32 hartid, clusterid, cluster_enabled = 0;
+ unsigned int cur_hartid = current_hartid();
+ struct sbi_scratch *scratch = sbi_hartid_to_scratch(cur_hartid);
+
+#if defined(CONFIG_PLATFORM_SPACEMIT_K1X)
+ /* set other cpu's boot-entry */
+ writel(scratch->warmboot_addr & 0xffffffff, (u32 *)C0_RVBADDR_LO_ADDR);
+ writel((scratch->warmboot_addr >> 32) & 0xffffffff, (u32 *)C0_RVBADDR_HI_ADDR);
+
+ writel(scratch->warmboot_addr & 0xffffffff, (u32 *)C1_RVBADDR_LO_ADDR);
+ writel((scratch->warmboot_addr >> 32) & 0xffffffff, (u32 *)C1_RVBADDR_HI_ADDR);
+#elif defined(CONFIG_PLATFORM_SPACEMIT_K1PRO)
+ for (i = 0; i < platform.hart_count; i++) {
+ hartid = platform.hart_index2id[i];
+
+ unsigned long core_index = MPIDR_AFFLVL1_VAL(hartid) * PLATFORM_MAX_CPUS_PER_CLUSTER
+ + MPIDR_AFFLVL0_VAL(hartid);
+
+ writel(scratch->warmboot_addr & 0xffffffff, (u32 *)(CORE0_RVBADDR_LO_ADDR + core_index * CORE_RVBADDR_STEP));
+ writel((scratch->warmboot_addr >> 32) & 0xffffffff, (u32 *)(CORE0_RVBADDR_HI_ADDR + core_index * CORE_RVBADDR_STEP));
+ }
+#endif
+
+#ifdef CONFIG_ARM_PSCI_SUPPORT
+ unsigned char *cpu_topology = plat_get_power_domain_tree_desc();
+#endif
+
+ // hart0 is already boot up
+ for (i = 0; i < platform.hart_count; i++) {
+ hartid = platform.hart_index2id[i];
+
+ clusterid = MPIDR_AFFLVL1_VAL(hartid);
+
+ /* we only enable snoop of cluster0 */
+ if (0 == (cluster_enabled & (1 << clusterid))) {
+ cluster_enabled |= 1 << clusterid;
+ if (0 == clusterid) {
+ cci_enable_snoop_dvm_reqs(clusterid);
+ }
+#ifdef CONFIG_ARM_PSCI_SUPPORT
+ cpu_topology[CLUSTER_INDEX_IN_CPU_TOPOLOGY]++;
+#endif
+ }
+
+#ifdef CONFIG_ARM_PSCI_SUPPORT
+ /* we only support 2 cluster by now */
+ if (clusterid == PLATFORM_CLUSTER_COUNT - 1)
+ cpu_topology[CLUSTER1_INDEX_IN_CPU_TOPOLOGY]++;
+ else
+ cpu_topology[CLUSTER0_INDEX_IN_CPU_TOPOLOGY]++;
+#endif
+ }
+}
+
+/*
+ * Platform early initialization.
+ */
+static int spacemit_k1_early_init(bool cold_boot, const struct fdt_match *match)
+{
+ if (cold_boot) {
+ /* initiate cci */
+ cci_init(PLATFORM_CCI_ADDR, cci_map, array_size(cci_map));
+ /* enable dcache */
+ csi_enable_dcache();
+ /* wakeup other core ? */
+ wakeup_other_core();
+ /* initialize */
+#ifdef CONFIG_ARM_SCMI_PROTOCOL_SUPPORT
+ plat_arm_pwrc_setup();
+#endif
+ } else {
+#ifdef CONFIG_ARM_PSCI_SUPPORT
+ psci_warmboot_entrypoint();
+#endif
+ }
+
+ return 0;
+}
+
+#ifdef CONFIG_ARM_PSCI_SUPPORT
+/** Start (or power-up) the given hart */
+static int spacemit_hart_start(unsigned int hartid, unsigned long saddr)
+{
+ return psci_cpu_on_start(hartid, saddr);
+}
+
+/**
+ * Stop (or power-down) the current hart from running. This call
+ * doesn't expect to return if success.
+ */
+static int spacemit_hart_stop(void)
+{
+ psci_cpu_off();
+
+ return 0;
+}
+
+static int spacemit_hart_suspend(unsigned int suspend_type)
+{
+ psci_cpu_suspend(suspend_type, 0, 0);
+
+ return 0;
+}
+
+static void spacemit_hart_resume(void)
+{
+ psci_warmboot_entrypoint();
+}
+
+static const struct sbi_hsm_device spacemit_hsm_ops = {
+ .name = "spacemit-hsm",
+ .hart_start = spacemit_hart_start,
+ .hart_stop = spacemit_hart_stop,
+ .hart_suspend = spacemit_hart_suspend,
+ .hart_resume = spacemit_hart_resume,
+};
+#endif
+
+/*
+ * Platform final initialization.
+ */
+static int spacemit_k1_final_init(bool cold_boot, const struct fdt_match *match)
+{
+#ifdef CONFIG_ARM_PSCI_SUPPORT
+ /* for clod boot, we build the cpu topology structure */
+ if (cold_boot) {
+ sbi_hsm_set_device(&spacemit_hsm_ops);
+ return psci_setup();
+ }
+#endif
+
+ return 0;
+}
+
+static bool spacemit_cold_boot_allowed(u32 hartid, const struct fdt_match *match)
+{
+ /* enable core snoop */
+ csr_set(CSR_ML2SETUP, 1 << (hartid % PLATFORM_MAX_CPUS_PER_CLUSTER));
+
+ /* dealing with resuming process */
+ if ((__sbi_hsm_hart_get_state(hartid) == SBI_HSM_STATE_SUSPENDED) && (hartid == 0))
+ return false;
+
+ return ((hartid == 0) ? true : false);
+}
+
+static const struct fdt_match spacemit_k1_match[] = {
+ { .compatible = "spacemit,k1-pro" },
+ { .compatible = "spacemit,k1x" },
+ { },
+};
+
+const struct platform_override spacemit_k1 = {
+ .match_table = spacemit_k1_match,
+ .early_init = spacemit_k1_early_init,
+ .final_init = spacemit_k1_final_init,
+ .cold_boot_allowed = spacemit_cold_boot_allowed,
+};