summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile47
-rw-r--r--docs/platform/generic.md2
-rw-r--r--docs/platform/metanoia-mt2824.md147
-rw-r--r--include/sbi/sbi_dbtr.h10
-rw-r--r--include/sbi/sbi_ecall_interface.h14
-rw-r--r--include/sbi/sbi_hart_protection.h7
-rw-r--r--include/sbi/sbi_hartmask.h4
-rw-r--r--include/sbi/sbi_timer.h3
-rw-r--r--include/sbi/sbi_tlb.h31
-rw-r--r--include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h8
-rw-r--r--lib/sbi/Kconfig9
-rw-r--r--lib/sbi/sbi_dbtr.c166
-rw-r--r--lib/sbi/sbi_ecall.c2
-rw-r--r--lib/sbi/sbi_emulate_csr.c3
-rw-r--r--lib/sbi/sbi_fp.c2
-rw-r--r--lib/sbi/sbi_hart_protection.c34
-rw-r--r--lib/sbi/sbi_init.c6
-rw-r--r--lib/sbi/sbi_irqchip.c3
-rw-r--r--lib/sbi/sbi_pmu.c27
-rw-r--r--lib/sbi/sbi_sse.c13
-rw-r--r--lib/sbi/sbi_timer.c12
-rw-r--r--lib/sbi/sbi_tlb.c46
-rw-r--r--lib/sbi/tests/sbi_ecall_test.c59
-rw-r--r--lib/utils/cache/fdt_andes_llcache.c23
-rw-r--r--lib/utils/cppc/fdt_cppc_rpmi.c44
-rw-r--r--lib/utils/hsm/fdt_hsm_andes_atcsmu.c64
-rw-r--r--lib/utils/mpxy/fdt_mpxy_rpmi_logging.c2
-rw-r--r--lib/utils/mpxy/fdt_mpxy_rpmi_mm.c9
-rw-r--r--lib/utils/reset/Kconfig4
-rw-r--r--lib/utils/reset/fdt_reset_metanoia.c72
-rw-r--r--lib/utils/reset/objects.mk3
-rw-r--r--lib/utils/suspend/fdt_suspend_andes_atcsmu.c70
-rw-r--r--platform/generic/Kconfig7
-rw-r--r--platform/generic/andes/ae350.c26
-rw-r--r--platform/generic/andes/andes_pma.c4
-rw-r--r--platform/generic/configs/defconfig2
-rw-r--r--platform/generic/include/andes/andes.h4
-rw-r--r--platform/generic/include/thead/c9xx_errata.h1
-rw-r--r--platform/generic/metanoia/mt2824/mt2824.c31
-rw-r--r--platform/generic/metanoia/mt2824/objects.mk10
-rw-r--r--platform/generic/sophgo/sg2042.c32
-rw-r--r--platform/generic/thead/thead-generic.c31
42 files changed, 934 insertions, 160 deletions
diff --git a/Makefile b/Makefile
index 37793aaf..aebaa98c 100644
--- a/Makefile
+++ b/Makefile
@@ -481,17 +481,26 @@ endif
define dynamic_flags
-I$(shell dirname $(2)) -D__OBJNAME__=$(subst -,_,$(shell basename $(1) .o))
endef
+
+# If the user requested silent operation (make -s), hide the
+# pretty command output by turning the echo into ":" (nop).
+ifneq ($(findstring s,$(firstword $(MAKEFLAGS))),)
+pretty_echo := :
+else
+pretty_echo := echo
+endif
+
merge_objs = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " MERGE $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " MERGE $(subst $(build_dir)/,,$(1))"; \
$(LD) $(MERGEFLAGS) $(2) -o $(1)
merge_deps = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " MERGE-DEP $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " MERGE-DEP $(subst $(build_dir)/,,$(1))"; \
cat $(2) > $(1)
copy_file = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " COPY $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " COPY $(subst $(build_dir)/,,$(1))"; \
cp -L -f $(2) $(1)
inst_file = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " INSTALL $(subst $(install_root_dir)/,,$(1))"; \
+ $(pretty_echo) " INSTALL $(subst $(install_root_dir)/,,$(1))"; \
cp -L -f $(2) $(1)
inst_file_list = $(CMD_PREFIX)if [ ! -z "$(4)" ]; then \
mkdir -p $(1)/$(3); \
@@ -499,62 +508,62 @@ inst_file_list = $(CMD_PREFIX)if [ ! -z "$(4)" ]; then \
rel_file=`echo $$file | sed -e 's@$(2)/$(subst $(install_firmware_path),platform,$(3))@@'`; \
dest_file=$(1)"/"$(3)"/"`echo $$rel_file`; \
dest_dir=`dirname $$dest_file`; \
- echo " INSTALL "$(3)"/"`echo $$rel_file`; \
+ $(pretty_echo) " INSTALL "$(3)"/"`echo $$rel_file`; \
mkdir -p $$dest_dir; \
cp -L -f $$file $$dest_file; \
done \
fi
inst_header_dir = $(CMD_PREFIX)mkdir -p $(1); \
- echo " INSTALL $(subst $(install_root_dir)/,,$(1))"; \
+ $(pretty_echo) " INSTALL $(subst $(install_root_dir)/,,$(1))"; \
cp -L -rf $(2) $(1)
compile_cpp_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " CPP-DEP $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " CPP-DEP $(subst $(build_dir)/,,$(1))"; \
printf %s `dirname $(1)`/ > $(1) && \
$(CC) $(CPPFLAGS) -x c -MM $(3) \
-MT `basename $(1:.dep=$(2))` >> $(1) || rm -f $(1)
compile_cpp = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " CPP $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " CPP $(subst $(build_dir)/,,$(1))"; \
$(CPP) $(CPPFLAGS) -x c $(2) | grep -v "\#" > $(1)
compile_cc_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " CC-DEP $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " CC-DEP $(subst $(build_dir)/,,$(1))"; \
printf %s `dirname $(1)`/ > $(1) && \
$(CC) $(CFLAGS) $(call dynamic_flags,$(1),$(2)) \
-MM $(2) >> $(1) || rm -f $(1)
compile_cc = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " CC $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " CC $(subst $(build_dir)/,,$(1))"; \
$(CC) $(CFLAGS) $(call dynamic_flags,$(1),$(2)) -c $(2) -o $(1)
compile_as_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " AS-DEP $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " AS-DEP $(subst $(build_dir)/,,$(1))"; \
printf %s `dirname $(1)`/ > $(1) && \
$(AS) $(ASFLAGS) $(call dynamic_flags,$(1),$(2)) \
-MM $(2) >> $(1) || rm -f $(1)
compile_as = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " AS $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " AS $(subst $(build_dir)/,,$(1))"; \
$(AS) $(ASFLAGS) $(call dynamic_flags,$(1),$(2)) -c $(2) -o $(1)
compile_elf = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " ELF $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " ELF $(subst $(build_dir)/,,$(1))"; \
$(CC) $(CFLAGS) $(3) $(ELFFLAGS) -Wl,-T$(2) -o $(1)
compile_ar = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " AR $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " AR $(subst $(build_dir)/,,$(1))"; \
$(AR) $(ARFLAGS) $(1) $(2)
compile_objcopy = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " OBJCOPY $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " OBJCOPY $(subst $(build_dir)/,,$(1))"; \
$(OBJCOPY) -S -O binary $(2) $(1)
compile_dts = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " DTC $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " DTC $(subst $(build_dir)/,,$(1))"; \
$(CPP) $(DTSCPPFLAGS) $(2) | $(DTC) -O dtb -i `dirname $(2)` -o $(1)
compile_d2c = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " D2C $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " D2C $(subst $(build_dir)/,,$(1))"; \
$(if $($(2)-varalign-$(3)),$(eval D2C_ALIGN_BYTES := $($(2)-varalign-$(3))),$(eval D2C_ALIGN_BYTES := $(4))) \
$(if $($(2)-varprefix-$(3)),$(eval D2C_NAME_PREFIX := $($(2)-varprefix-$(3))),$(eval D2C_NAME_PREFIX := $(5))) \
$(if $($(2)-padding-$(3)),$(eval D2C_PADDING_BYTES := $($(2)-padding-$(3))),$(eval D2C_PADDING_BYTES := 0)) \
$(src_dir)/scripts/d2c.sh -i $(6) -a $(D2C_ALIGN_BYTES) -p $(D2C_NAME_PREFIX) -t $(D2C_PADDING_BYTES) > $(1)
compile_carray = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " CARRAY $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " CARRAY $(subst $(build_dir)/,,$(1))"; \
$(eval CARRAY_VAR_LIST := $(carray-$(subst .carray.c,,$(shell basename $(1)))-y)) \
$(src_dir)/scripts/carray.sh -i $(2) -l "$(CARRAY_VAR_LIST)" > $(1) || rm $(1)
compile_gen_dep = $(CMD_PREFIX)mkdir -p `dirname $(1)`; \
- echo " GEN-DEP $(subst $(build_dir)/,,$(1))"; \
+ $(pretty_echo) " GEN-DEP $(subst $(build_dir)/,,$(1))"; \
echo "$(1:.dep=$(2)): $(3)" >> $(1)
targets-y = $(build_dir)/lib/libsbi.a
diff --git a/docs/platform/generic.md b/docs/platform/generic.md
index 0b896ede..760152a0 100644
--- a/docs/platform/generic.md
+++ b/docs/platform/generic.md
@@ -50,6 +50,7 @@ RISC-V Platforms Using Generic Platform
* **Tenstorrent Atlantis Platform** (*[tt-atlantis.md]*)
* **OpenPiton FPGA SoC** (*[fpga-openpiton.md]*)
* **Ariane FPGA SoC** (*[fpga-ariane.md]*)
+* **Metanoia MT2824 SoC** (*[metanoia-mt2824.md]*)
[andes-ae350.md]: andes-ae350.md
[qemu_virt.md]: qemu_virt.md
@@ -61,3 +62,4 @@ RISC-V Platforms Using Generic Platform
[tt-atlantis.md]: tt-atlantis.md
[fpga-openpiton.md]: fpga-openpiton.md
[fpga-ariane.md]: fpga-ariane.md
+[metanoia-mt2824.md]: metanoia-mt2824.md
diff --git a/docs/platform/metanoia-mt2824.md b/docs/platform/metanoia-mt2824.md
new file mode 100644
index 00000000..83017df5
--- /dev/null
+++ b/docs/platform/metanoia-mt2824.md
@@ -0,0 +1,147 @@
+Metanoia MT2824 SoC Platform
+============================
+The Metanoia MT2824 is a highly integrated 5G System-on-Chip designed
+for O-RAN radio units and small cells. It features two Andes AX45MP
+RISC-V cores. More details about the SoC and its reference platform
+named MT5824 can be found at the following [link][0].
+
+To build platform specific library and firmwares, provide the
+*PLATFORM=generic* parameter to the top level make command.
+
+[0]: https://metanoia-comm.com/products/5g/mt5824/
+
+Platform Options
+----------------
+
+The Metanoia MT2824 platform does not have any platform-specific options.
+
+Building Metanoia MT2824 Platform
+---------------------------------
+
+```
+make PLATFORM=generic
+```
+
+DTS Example: (Metanoia MT2824)
+------------------------------
+
+```
+ compatible = "metanoia,mt2824";
+
+ cpus: cpus {
+ #address-cells = <0x01>;
+ #size-cells = <0x00>;
+ timebase-frequency = <20000000>;
+
+ cpu0: cpu@0 {
+ device_type = "cpu";
+ reg = <0x00>;
+ status = "okay";
+ compatible = "andestech,ax45mp", "riscv";
+ riscv,isa-base = "rv64i";
+ riscv,isa-extensions = "i", "m", "a", "f", "d", "c",
+ "zicntr", "zicsr", "zifencei",
+ "zihpm", "xandespmu";
+ mmu-type = "riscv,sv39";
+ clock-frequency = <900000000>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <0x100>;
+ i-cache-line-size = <0x40>;
+ i-cache-block-size = <0x40>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <0x80>;
+ d-cache-line-size = <0x40>;
+ d-cache-block-size = <0x40>;
+ next-level-cache = <0x01>;
+
+ cpu0intc: interrupt-controller {
+ #interrupt-cells = <0x01>;
+ interrupt-controller;
+ compatible = "andestech,cpu-intc", "riscv,cpu-intc";
+ };
+ };
+
+ cpu1: cpu@1 {
+ device_type = "cpu";
+ reg = <0x01>;
+ status = "okay";
+ compatible = "andestech,ax45mp", "riscv";
+ riscv,isa-base = "rv64i";
+ riscv,isa-extensions = "i", "m", "a", "f", "d", "c",
+ "zicntr", "zicsr", "zifencei",
+ "zihpm", "xandespmu";
+ mmu-type = "riscv,sv39";
+ clock-frequency = <900000000>;
+ i-cache-size = <0x8000>;
+ i-cache-sets = <0x100>;
+ i-cache-line-size = <0x40>;
+ i-cache-block-size = <0x40>;
+ d-cache-size = <0x8000>;
+ d-cache-sets = <0x80>;
+ d-cache-line-size = <0x40>;
+ d-cache-block-size = <0x40>;
+ next-level-cache = <0x01>;
+
+ cpu1intc: interrupt-controller {
+ #interrupt-cells = <0x01>;
+ interrupt-controller;
+ compatible = "andestech,cpu-intc", "riscv,cpu-intc";
+ };
+ };
+ };
+
+ l2c: l2-cache@a000000 {
+ compatible = "andestech,ax45mp-cache", "cache";
+ reg = <0x00 0xa000000 0x00 0x40000>;
+ cache-line-size = <64>;
+ cache-level = <2>;
+ cache-size = <0x40000>;
+ cache-sets = <1024>;
+ cache-unified;
+ interrupts = <1 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-parent = <&plic>;
+ };
+
+ soc: soc {
+ #address-cells = <0x02>;
+ #size-cells = <0x02>;
+ compatible = "simple-bus";
+ interrupt-parent = <&plic>;
+ ranges;
+
+ plic: interrupt-controller@c000000 {
+ compatible = "metanoia,mt2824-plic", "andestech,nceplic100";
+ reg = <0x0 0x0c000000 0x0 0x400000>;
+ interrupts-extended =
+ <&cpu0intc 0x0b>,
+ <&cpu0intc 0x09>,
+ <&cpu1intc 0x0b>,
+ <&cpu1intc 0x09>;
+ interrupt-controller;
+ #address-cells = <0x00>;
+ #interrupt-cells = <0x02>;
+ riscv,ndev = <0x47>;
+ };
+
+ plicsw: interrupt-controller@c800000 {
+ compatible = "metanoia,mt2824-plicsw", "andestech,plicsw";
+ reg = <0x00 0x0c800000 0x00 0x400000>;
+ interrupts-extended =
+ <&cpu0intc IRQ_TYPE_EDGE_BOTH>,
+ <&cpu1intc IRQ_TYPE_EDGE_BOTH>;
+ };
+
+ plmt0@c400000 {
+ compatible = "metanoia,mt2824-plmt", "andestech,plmt0";
+ reg = <0x0 0x0c400000 0x0 0x00400000>;
+ interrupts-extended =
+ <&cpu0intc 0x07>,
+ <&cpu1intc 0x07>;
+ };
+
+ reboot: reboot@10000004 {
+ compatible = "metanoia,mt2824-reboot";
+ reg = <0x0 0x10000004 0x0 0x08>;
+ };
+ };
+```
diff --git a/include/sbi/sbi_dbtr.h b/include/sbi/sbi_dbtr.h
index 5e0bf84e..90871f0d 100644
--- a/include/sbi/sbi_dbtr.h
+++ b/include/sbi/sbi_dbtr.h
@@ -77,6 +77,16 @@ struct sbi_dbtr_hart_triggers_state {
u32 probed;
};
+/** Platform specific debug trigger operations */
+struct sbi_dbtr_device {
+ char name[32];
+ bool (*trigger_supported)(unsigned long idx, unsigned long tdata1,
+ unsigned long tdata2, unsigned long tdata3);
+};
+
+const struct sbi_dbtr_device *sbi_dbtr_get_device(void);
+void sbi_dbtr_set_device(const struct sbi_dbtr_device *dev);
+
#define TDATA1_GET_TYPE(_t1) \
EXTRACT_FIELD(_t1, RV_DBTR_BIT_MASK(TDATA1, TYPE))
diff --git a/include/sbi/sbi_ecall_interface.h b/include/sbi/sbi_ecall_interface.h
index acb91a01..fd4e77ca 100644
--- a/include/sbi/sbi_ecall_interface.h
+++ b/include/sbi/sbi_ecall_interface.h
@@ -271,6 +271,8 @@ struct sbi_pmu_event_info {
#define SBI_PMU_EVENT_IDX_TYPE_OFFSET 16
#define SBI_PMU_EVENT_IDX_TYPE_MASK (0xF << SBI_PMU_EVENT_IDX_TYPE_OFFSET)
#define SBI_PMU_EVENT_IDX_CODE_MASK 0xFFFF
+#define SBI_PMU_EVENT_IDX_MBZ_OFFSET 20
+#define SBI_PMU_EVENT_IDX_MBZ_MASK (0xFFF << SBI_PMU_EVENT_IDX_MBZ_OFFSET)
#define SBI_PMU_EVENT_RAW_IDX 0x20000
#define SBI_PMU_EVENT_RAW_V2_IDX 0x30000
@@ -304,10 +306,22 @@ struct sbi_pmu_event_info {
/* Flags defined for counter start function */
#define SBI_PMU_START_FLAG_SET_INIT_VALUE (1 << 0)
#define SBI_PMU_START_FLAG_INIT_FROM_SNAPSHOT (1 << 1)
+/* Start flags valid mask */
+#define SBI_PMU_START_FLAGS_MASK \
+ ( \
+ SBI_PMU_START_FLAG_SET_INIT_VALUE | \
+ SBI_PMU_START_FLAG_INIT_FROM_SNAPSHOT \
+ )
/* Flags defined for counter stop function */
#define SBI_PMU_STOP_FLAG_RESET (1 << 0)
#define SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT (1 << 1)
+/* Stop flags valid mask */
+#define SBI_PMU_STOP_FLAGS_MASK \
+ ( \
+ SBI_PMU_STOP_FLAG_RESET | \
+ SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT \
+ )
/* SBI function IDs for DBCN extension */
#define SBI_EXT_DBCN_CONSOLE_WRITE 0x0
diff --git a/include/sbi/sbi_hart_protection.h b/include/sbi/sbi_hart_protection.h
index de5f2f89..cafe6ee7 100644
--- a/include/sbi/sbi_hart_protection.h
+++ b/include/sbi/sbi_hart_protection.h
@@ -50,11 +50,14 @@ struct sbi_hart_protection {
};
/**
- * Get the best hart memory protection mechanism
+ * Get a string containing names of protection mechanisms
+ *
+ * @param out_str pointer to output string
+ * @param out_str_size maximum size of output string
*
* @return pointer to best hart memory protection mechanism
*/
-struct sbi_hart_protection *sbi_hart_memory_protection_best(void);
+void sbi_hart_protection_get_str(char *out_str, int out_str_size);
/**
* Register a hart protection mechanism
diff --git a/include/sbi/sbi_hartmask.h b/include/sbi/sbi_hartmask.h
index 200ab6e5..da6821ee 100644
--- a/include/sbi/sbi_hartmask.h
+++ b/include/sbi/sbi_hartmask.h
@@ -20,7 +20,11 @@
* also represents the maximum number of HART ids generic OpenSBI
* can handle.
*/
+#ifdef CONFIG_HARTMASK_MAX_BITS
+#define SBI_HARTMASK_MAX_BITS CONFIG_HARTMASK_MAX_BITS
+#else
#define SBI_HARTMASK_MAX_BITS 128
+#endif
/** Representation of hartmask */
struct sbi_hartmask {
diff --git a/include/sbi/sbi_timer.h b/include/sbi/sbi_timer.h
index 2a040927..17d6c1dc 100644
--- a/include/sbi/sbi_timer.h
+++ b/include/sbi/sbi_timer.h
@@ -140,6 +140,9 @@ bool sbi_timer_waitms_until(bool (*predicate)(void *), void *arg,
/** Get timer value for current HART */
u64 sbi_timer_value(void);
+/** Get timer frequency for current HART */
+unsigned long sbi_timer_frequency(void);
+
/** Compute timer value after specified milliseconds */
static inline u64 sbi_timer_value_after_msecs(ulong msecs)
{
diff --git a/include/sbi/sbi_tlb.h b/include/sbi/sbi_tlb.h
index 3936e857..6db8744d 100644
--- a/include/sbi/sbi_tlb.h
+++ b/include/sbi/sbi_tlb.h
@@ -54,6 +54,37 @@ do { \
#define SBI_TLB_INFO_SIZE sizeof(struct sbi_tlb_info)
+/** Collection of Local TLB operations */
+struct sbi_tlb_local_operations {
+ /** Local fence.i */
+ void (*local_fence_i)(struct sbi_tlb_info *tinfo);
+
+ /** Local supervisor-mode TLB flush */
+ void (*local_sfence_vma)(struct sbi_tlb_info *tinfo);
+
+ /** Local supervisor-mode TLB flush with ASID */
+ void (*local_sfence_vma_asid)(struct sbi_tlb_info *tinfo);
+
+ /** Local G-stage TLB flush for a specific VMID */
+ void (*local_hfence_gvma_vmid)(struct sbi_tlb_info *tinfo);
+
+ /** Local G-stage TLB flush */
+ void (*local_hfence_gvma)(struct sbi_tlb_info *tinfo);
+
+ /** Local VS-stage TLB flush with ASID */
+ void (*local_hfence_vvma_asid)(struct sbi_tlb_info *tinfo);
+
+ /** Local VS-stage TLB flush */
+ void (*local_hfence_vvma)(struct sbi_tlb_info *tinfo);
+};
+
+/** Get the local TLB operations */
+const struct sbi_tlb_local_operations *sbi_tlb_get_local_operations(void);
+
+/** Set the local TLB operations */
+void sbi_tlb_set_local_operations(
+ const struct sbi_tlb_local_operations *ops);
+
void __sbi_sfence_vma_all();
int sbi_tlb_request(ulong hmask, ulong hbase, struct sbi_tlb_info *tinfo);
diff --git a/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h b/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
index 5e71fab7..bb9a2bc2 100644
--- a/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
+++ b/include/sbi_utils/hsm/fdt_hsm_andes_atcsmu.h
@@ -9,6 +9,11 @@
#include <sbi/sbi_types.h>
+struct atcsmu_sleep_arg {
+ u32 hartid;
+ bool deep_sleep;
+};
+
/* clang-format off */
#define SCRATCH_PAD_OFFSET 0x40
@@ -36,6 +41,7 @@
#define PCS_WAKEUP_RTC_ALARM_MASK BIT(2)
#define PCS_WAKEUP_UART2_MASK BIT(9)
#define PCS_WAKEUP_MSIP_MASK BIT(29)
+#define PCS_WAKEUP_MEIP_MASK BIT(31)
#define PCS0_CTL_OFFSET 0x94
#define PCSm_CTL_OFFSET(i) ((i + 3) * 0x20 + PCS0_CTL_OFFSET)
@@ -60,6 +66,6 @@ int atcsmu_set_reset_vector(u64 wakeup_addr, u32 hartid);
u32 atcsmu_get_sleep_type(u32 hartid);
void atcsmu_write_scratch(u32 value);
u32 atcsmu_read_scratch(void);
-bool atcsmu_pcs_is_sleep(u32 hartid, bool deep_sleep);
+bool atcsmu_hart_is_sleep(void *opaque);
#endif
diff --git a/lib/sbi/Kconfig b/lib/sbi/Kconfig
index e76aecca..24132eaa 100644
--- a/lib/sbi/Kconfig
+++ b/lib/sbi/Kconfig
@@ -2,6 +2,15 @@
menu "Generic SBI Support"
+config HARTMASK_MAX_BITS
+ int "Maximum number of hart mask bits (32-512)"
+ range 32 512
+ default 256
+ help
+ The hartmask is indexed using physical hart IDs so this setting
+ also represents the maximum number of hart IDs generic OpenSBI
+ can handle.
+
config DEFAULT_HART_STACK_SIZE
int "Default per-HART stack size (bytes)"
range 8192 1048576
diff --git a/lib/sbi/sbi_dbtr.c b/lib/sbi/sbi_dbtr.c
index 01047969..4fd7fb5e 100644
--- a/lib/sbi/sbi_dbtr.c
+++ b/lib/sbi/sbi_dbtr.c
@@ -24,6 +24,22 @@
/** Offset of pointer to HART's debug triggers info in scratch space */
static unsigned long hart_state_ptr_offset;
+/** Device specific debug trigger operations */
+static const struct sbi_dbtr_device *dbtr_dev = NULL;
+
+const struct sbi_dbtr_device *sbi_dbtr_get_device(void)
+{
+ return dbtr_dev;
+}
+
+void sbi_dbtr_set_device(const struct sbi_dbtr_device *dev)
+{
+ if (!dev || dbtr_dev)
+ return;
+
+ dbtr_dev = dev;
+}
+
#define dbtr_get_hart_state_ptr(__scratch) \
sbi_scratch_read_type((__scratch), void *, hart_state_ptr_offset)
@@ -105,10 +121,75 @@ static void sbi_trigger_init(struct sbi_dbtr_trigger *trig,
trig->index = idx;
}
-static inline struct sbi_dbtr_trigger *sbi_alloc_trigger(void)
+static bool dbtr_trigger_hw_supported(unsigned long idx, unsigned long tdata1,
+ unsigned long tdata2,
+ unsigned long tdata3)
+{
+ if (dbtr_dev && dbtr_dev->trigger_supported)
+ return dbtr_dev->trigger_supported(idx, tdata1, tdata2,
+ tdata3);
+
+ return true;
+}
+
+static bool dbtr_trigger_any_hw_supported(
+ struct sbi_dbtr_hart_triggers_state *hs,
+ unsigned long tdata1, unsigned long tdata2,
+ unsigned long tdata3)
+{
+ unsigned long type = TDATA1_GET_TYPE(tdata1);
+ struct sbi_dbtr_trigger *trig;
+ int i;
+
+ for (i = 0; i < hs->total_trigs; i++) {
+ trig = INDEX_TO_TRIGGER(i);
+ if (__test_bit(type, &trig->type_mask) &&
+ dbtr_trigger_hw_supported(trig->index, tdata1, tdata2,
+ tdata3))
+ return true;
+ }
+
+ return false;
+}
+
+/*
+ * Find the first free hardware trigger slot supporting the configuration.
+ * Slots set in claimed_mask are treated as taken, which allows the caller
+ * to track slot availability. A 32-bit mask covers RV_MAX_TRIGGERS (32);
+ * for a larger number of triggers, this function needs to be updated.
+ */
+static int dbtr_find_free_slot(struct sbi_dbtr_hart_triggers_state *hs,
+ u32 claimed_mask,
+ unsigned long tdata1, unsigned long tdata2,
+ unsigned long tdata3)
+{
+ unsigned long type = TDATA1_GET_TYPE(tdata1);
+ struct sbi_dbtr_trigger *trig;
+ int i;
+
+ for (i = 0; i < hs->total_trigs; i++) {
+ trig = INDEX_TO_TRIGGER(i);
+ if (trig->state & RV_DBTR_BIT_MASK(TS, MAPPED))
+ continue;
+ if (claimed_mask & BIT(i))
+ continue;
+ if (!__test_bit(type, &trig->type_mask))
+ continue;
+ if (!dbtr_trigger_hw_supported(trig->index, tdata1,
+ tdata2, tdata3))
+ continue;
+ return i;
+ }
+
+ return SBI_ENOENT;
+}
+
+static inline struct sbi_dbtr_trigger *sbi_alloc_trigger(unsigned long tdata1,
+ unsigned long tdata2,
+ unsigned long tdata3)
{
int i;
- struct sbi_dbtr_trigger *f_trig = NULL;
+ struct sbi_dbtr_trigger *f_trig;
struct sbi_dbtr_hart_triggers_state *hart_state;
hart_state = dbtr_thishart_state_ptr();
@@ -118,17 +199,12 @@ static inline struct sbi_dbtr_trigger *sbi_alloc_trigger(void)
if (hart_state->available_trigs <= 0)
return NULL;
- for (i = 0; i < hart_state->total_trigs; i++) {
- f_trig = INDEX_TO_TRIGGER(i);
- if (f_trig->state & RV_DBTR_BIT_MASK(TS, MAPPED))
- continue;
- hart_state->available_trigs--;
- break;
- }
-
- if (i == hart_state->total_trigs)
+ i = dbtr_find_free_slot(hart_state, 0, tdata1, tdata2, tdata3);
+ if (i < 0)
return NULL;
+ f_trig = INDEX_TO_TRIGGER(i);
+ hart_state->available_trigs--;
__set_bit(RV_DBTR_BIT(TS, MAPPED), &f_trig->state);
return f_trig;
@@ -547,7 +623,8 @@ int sbi_dbtr_num_trig(unsigned long data, unsigned long *out)
for (i = 0; i < hs->total_trigs; i++) {
trig = INDEX_TO_TRIGGER(i);
- if (__test_bit(type, &trig->type_mask))
+ if (__test_bit(type, &trig->type_mask) &&
+ dbtr_trigger_hw_supported(trig->index, data, 0, 0))
total++;
}
@@ -573,7 +650,7 @@ int sbi_dbtr_read_trig(unsigned long smode,
if (trig_idx_base >= hs->total_trigs ||
trig_idx_base + trig_count >= hs->total_trigs)
- return SBI_ERR_INVALID_PARAM;
+ return SBI_ERR_BAD_RANGE;
if (sbi_dbtr_shmem_disabled(hs))
return SBI_ERR_NO_SHMEM;
@@ -608,6 +685,8 @@ int sbi_dbtr_install_trig(unsigned long smode,
struct sbi_dbtr_data_msg *recv;
struct sbi_dbtr_id_msg *xmit;
unsigned long ctrl;
+ u32 claimed = 0;
+ int slot;
struct sbi_dbtr_trigger *trig;
struct sbi_dbtr_hart_triggers_state *hs = NULL;
bool tdata2_impl, tdata3_impl;
@@ -626,8 +705,10 @@ int sbi_dbtr_install_trig(unsigned long smode,
/*
* SBI v3.0 sec 19.4 requires SBI_ERR_NOT_SUPPORTED when a trigger
* programs a non-zero value into an unimplemented optional CSR. Only
- * the "whole CSR unimplemented" case is caught; WARL bits tied off
- * inside an otherwise-implemented CSR are not.
+ * the "whole CSR unimplemented" case is caught here; WARL bits tied
+ * off inside an otherwise-implemented CSR are delegated to the
+ * device-specific trigger_supported() callback via
+ * dbtr_trigger_any_hw_supported().
*/
tdata2_impl = tdata_implemented(CSR_TDATA2);
tdata3_impl = tdata_implemented(CSR_TDATA3);
@@ -648,7 +729,7 @@ int sbi_dbtr_install_trig(unsigned long smode,
*out = _idx;
sbi_hart_protection_unmap_range((unsigned long)shmem_base,
trig_count * sizeof(*entry));
- return SBI_ERR_FAILED;
+ return SBI_ERR_INVALID_PARAM;
}
if ((recv->tdata2 && !tdata2_impl) ||
@@ -658,6 +739,16 @@ int sbi_dbtr_install_trig(unsigned long smode,
trig_count * sizeof(*entry));
return SBI_ERR_NOT_SUPPORTED;
}
+
+ if (!dbtr_trigger_any_hw_supported(hs,
+ lle_to_cpu(recv->tdata1),
+ lle_to_cpu(recv->tdata2),
+ lle_to_cpu(recv->tdata3))) {
+ *out = _idx;
+ sbi_hart_protection_unmap_range((unsigned long)shmem_base,
+ trig_count * sizeof(*entry));
+ return SBI_ERR_NOT_SUPPORTED;
+ }
}
if (hs->available_trigs < trig_count) {
@@ -667,17 +758,40 @@ int sbi_dbtr_install_trig(unsigned long smode,
return SBI_ERR_FAILED;
}
- /* Install triggers */
+ /*
+ * Dry-run the allocation of the whole batch so that no trigger
+ * is installed if any of the requested configurations cannot be
+ * matched to a free hardware trigger slot.
+ */
for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) {
- /*
- * Since we have already checked if enough triggers are
- * available, trigger allocation must succeed.
- */
- trig = sbi_alloc_trigger();
+ recv = (struct sbi_dbtr_data_msg *)(&entry->data);
+ slot = dbtr_find_free_slot(hs, claimed,
+ lle_to_cpu(recv->tdata1),
+ lle_to_cpu(recv->tdata2),
+ lle_to_cpu(recv->tdata3));
+ if (slot < 0) {
+ *out = _idx;
+ sbi_hart_protection_unmap_range((unsigned long)shmem_base,
+ trig_count * sizeof(*entry));
+ return SBI_ERR_FAILED;
+ }
+ claimed |= BIT(slot);
+ }
+ /* Install triggers */
+ for_each_trig_entry(shmem_base, trig_count, typeof(*entry), entry) {
recv = (struct sbi_dbtr_data_msg *)(&entry->data);
xmit = (struct sbi_dbtr_id_msg *)(&entry->id);
+ /*
+ * The dry-run above matched every requested configuration
+ * to a free hardware trigger slot, so allocation must
+ * succeed.
+ */
+ trig = sbi_alloc_trigger(lle_to_cpu(recv->tdata1),
+ lle_to_cpu(recv->tdata2),
+ lle_to_cpu(recv->tdata3));
+
dbtr_trigger_setup(trig, recv);
dbtr_trigger_enable(trig);
xmit->idx = cpu_to_lle(trig->index);
@@ -789,6 +903,14 @@ int sbi_dbtr_update_trig(unsigned long smode,
return SBI_ERR_NOT_SUPPORTED;
}
+ if (!dbtr_trigger_hw_supported(trig->index,
+ lle_to_cpu(entry->data.tdata1),
+ lle_to_cpu(entry->data.tdata2),
+ lle_to_cpu(entry->data.tdata3))) {
+ sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry));
+ return SBI_ERR_NOT_SUPPORTED;
+ }
+
dbtr_trigger_setup(trig, &entry->data);
sbi_hart_protection_unmap_range((unsigned long)entry, sizeof(*entry));
dbtr_trigger_enable(trig);
diff --git a/lib/sbi/sbi_ecall.c b/lib/sbi/sbi_ecall.c
index 745fa313..65c5a55a 100644
--- a/lib/sbi/sbi_ecall.c
+++ b/lib/sbi/sbi_ecall.c
@@ -66,6 +66,8 @@ void sbi_ecall_get_extensions_str(char *exts_str, int exts_str_size, bool experi
sbi_list_for_each_entry(t, &ecall_exts_list, head) {
if (experimental != t->experimental)
continue;
+ if (offset + sbi_strlen(t->name) + 1 > exts_str_size)
+ break;
sbi_snprintf(exts_str + offset, exts_str_size - offset,
"%s,", t->name);
offset = offset + sbi_strlen(t->name) + 1;
diff --git a/lib/sbi/sbi_emulate_csr.c b/lib/sbi/sbi_emulate_csr.c
index c2253c82..4558abff 100644
--- a/lib/sbi/sbi_emulate_csr.c
+++ b/lib/sbi/sbi_emulate_csr.c
@@ -90,7 +90,8 @@ int sbi_emulate_csr_read(int csr_num, struct sbi_trap_regs *regs,
*csr_val = csr_read(CSR_MCYCLEH);
break;
case CSR_TIMEH:
- /* Refer comments on TIME CSR above. */
+ if (!hpm_allowed(csr_num - CSR_CYCLEH, prev_mode, virt))
+ return SBI_ENOTSUPP;
*csr_val = (virt) ? sbi_timer_virt_value() >> 32:
sbi_timer_value() >> 32;
break;
diff --git a/lib/sbi/sbi_fp.c b/lib/sbi/sbi_fp.c
index c8941252..38fb7a01 100644
--- a/lib/sbi/sbi_fp.c
+++ b/lib/sbi/sbi_fp.c
@@ -20,7 +20,7 @@ void sbi_fp_save(struct sbi_fp_context *dst)
if (!dst)
return;
- mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_VS);
+ mstatus_orig = csr_read_set(CSR_MSTATUS, MSTATUS_FS);
asm volatile(
#if defined(__riscv_d)
diff --git a/lib/sbi/sbi_hart_protection.c b/lib/sbi/sbi_hart_protection.c
index d607ed74..c4c149c8 100644
--- a/lib/sbi/sbi_hart_protection.c
+++ b/lib/sbi/sbi_hart_protection.c
@@ -4,13 +4,15 @@
* Copyright (c) 2025 Ventana Micro Systems Inc.
*/
+#include <sbi/sbi_console.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_hart_protection.h>
#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_string.h>
static SBI_LIST_HEAD(hart_protection_list);
-struct sbi_hart_protection *sbi_hart_memory_protection_best(void)
+static struct sbi_hart_protection *__hart_memory_protection_best(void)
{
struct sbi_hart_protection *pos;
@@ -22,6 +24,32 @@ struct sbi_hart_protection *sbi_hart_memory_protection_best(void)
return NULL;
}
+void sbi_hart_protection_get_str(char *out_str, int out_str_size)
+{
+ bool memory_protect_done = false;
+ struct sbi_hart_protection *pos;
+ int offset = 0;
+
+ if (!out_str || out_str_size <= 0)
+ return;
+ sbi_memset(out_str, 0, out_str_size);
+
+ sbi_list_for_each_entry(pos, &hart_protection_list, head) {
+ if (pos->type == SBI_HART_PROTECTION_TYPE_MEMORY) {
+ if (memory_protect_done)
+ continue;
+ memory_protect_done = true;
+ }
+ sbi_snprintf(out_str + offset, out_str_size - offset, "%s,", pos->name);
+ offset = offset + sbi_strlen(pos->name) + 1;
+ }
+
+ if (offset)
+ out_str[offset - 1] = '\0';
+ else
+ sbi_strncpy(out_str, "none", out_str_size);
+}
+
int sbi_hart_protection_register(struct sbi_hart_protection *hprot)
{
struct sbi_hart_protection *pos = NULL;
@@ -170,7 +198,7 @@ int sbi_hart_protection_reconfigure(struct sbi_scratch *scratch,
int sbi_hart_protection_map_range(unsigned long base, unsigned long size)
{
- struct sbi_hart_protection *hprot = sbi_hart_memory_protection_best();
+ struct sbi_hart_protection *hprot = __hart_memory_protection_best();
if (!hprot || !hprot->map_range)
return 0;
@@ -180,7 +208,7 @@ int sbi_hart_protection_map_range(unsigned long base, unsigned long size)
int sbi_hart_protection_unmap_range(unsigned long base, unsigned long size)
{
- struct sbi_hart_protection *hprot = sbi_hart_memory_protection_best();
+ struct sbi_hart_protection *hprot = __hart_memory_protection_best();
if (!hprot || !hprot->unmap_range)
return 0;
diff --git a/lib/sbi/sbi_init.c b/lib/sbi/sbi_init.c
index 388f3ecb..acd2f8b6 100644
--- a/lib/sbi/sbi_init.c
+++ b/lib/sbi/sbi_init.c
@@ -76,7 +76,6 @@ static void sbi_boot_print_general(struct sbi_scratch *scratch)
const struct sbi_hsm_device *hdev;
const struct sbi_ipi_device *idev;
const struct sbi_timer_device *tdev;
- const struct sbi_hart_protection *hprot;
const struct sbi_console_device *cdev;
const struct sbi_system_reset_device *srdev;
const struct sbi_system_suspend_device *susp_dev;
@@ -93,9 +92,8 @@ static void sbi_boot_print_general(struct sbi_scratch *scratch)
sbi_printf("Platform Features : %s\n", str);
sbi_printf("Platform HART Count : %u\n",
sbi_platform_hart_count(plat));
- hprot = sbi_hart_memory_protection_best();
- sbi_printf("Platform HART Protection : %s\n",
- (hprot) ? hprot->name : "---");
+ sbi_hart_protection_get_str(str, sizeof(str));
+ sbi_printf("Platform HART Protection : %s\n", str);
idev = sbi_ipi_get_device();
sbi_printf("Platform IPI Device : %s\n",
(idev) ? idev->name : "---");
diff --git a/lib/sbi/sbi_irqchip.c b/lib/sbi/sbi_irqchip.c
index b490a3ad..1664bd2d 100644
--- a/lib/sbi/sbi_irqchip.c
+++ b/lib/sbi/sbi_irqchip.c
@@ -185,6 +185,9 @@ int sbi_irqchip_raw_handler_default(struct sbi_irqchip_device *chip, u32 hwirq)
return SBI_EINVAL;
h = sbi_irqchip_find_handler(chip, hwirq);
+ if (!h)
+ return SBI_EINVAL;
+
if (h->callback)
rc = h->callback(hwirq, h->priv);
diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c
index a0f6d2fa..49d52066 100644
--- a/lib/sbi/sbi_pmu.c
+++ b/lib/sbi/sbi_pmu.c
@@ -574,6 +574,9 @@ int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
if (!pmu_ctr_idx_validate(cbase, cmask))
return ret;
+ if (flags & ~SBI_PMU_START_FLAGS_MASK)
+ return SBI_ERR_INVALID_PARAM;
+
if (flags & SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT)
return SBI_ENO_SHMEM;
@@ -592,6 +595,8 @@ int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
: 0x0;
ret = pmu_ctr_start_fw(phs, cidx, event_code, edata,
ival, bUpdate);
+ if (ret)
+ return ret;
} else {
if (cidx >= 3) {
struct sbi_pmu_hw_event_config *ev_cfg =
@@ -605,6 +610,8 @@ int sbi_pmu_ctr_start(unsigned long cbase, unsigned long cmask,
return ret;
}
ret = pmu_ctr_start_hw(cidx, ival, bUpdate);
+ if (ret)
+ return ret;
}
}
@@ -693,6 +700,9 @@ int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
if (!pmu_ctr_idx_validate(cbase, cmask))
return ret;
+ if (flag & ~SBI_PMU_STOP_FLAGS_MASK)
+ return SBI_ERR_INVALID_PARAM;
+
if (flag & SBI_PMU_STOP_FLAG_TAKE_SNAPSHOT)
return SBI_ENO_SHMEM;
@@ -708,6 +718,9 @@ int sbi_pmu_ctr_stop(unsigned long cbase, unsigned long cmask,
else
ret = pmu_ctr_stop_hw(cidx);
+ if(ret)
+ return ret;
+
if (cidx > (CSR_INSTRET - CSR_CYCLE) && flag & SBI_PMU_STOP_FLAG_RESET) {
phs->active_events[cidx] = SBI_PMU_EVENT_IDX_INVALID;
pmu_reset_hw_mhpmevent(cidx);
@@ -1098,15 +1111,29 @@ int sbi_pmu_event_get_info(unsigned long shmem_phys_lo, unsigned long shmem_phys
einfo = (struct sbi_pmu_event_info *)(shmem_phys_lo);
for (i = 0; i < num_events; i++) {
event_idx = einfo[i].event_idx;
+ /* Any must-be-zero event_idx bits set should return INVALID_PARAM per-spec */
+ if (event_idx & SBI_PMU_EVENT_IDX_MBZ_MASK)
+ return SBI_ERR_INVALID_PARAM;
event_type = pmu_event_validate(phs, event_idx, einfo[i].event_data);
if (event_type < 0) {
einfo[i].output = 0;
+ } else if (event_type == SBI_PMU_EVENT_TYPE_FW) {
+ einfo[i].output = 1;
} else {
for (j = 0; j < num_hw_events; j++) {
temp = &hw_event_map[j];
/* For raw events, event data is used as the select value */
if (event_idx == SBI_PMU_EVENT_RAW_IDX ||
event_idx == SBI_PMU_EVENT_RAW_V2_IDX) {
+ /*
+ * Only a raw event map entry carries a
+ * meaningful select/select_mask pair, so
+ * skip any entry which does not cover the
+ * raw event index.
+ */
+ if (temp->start_idx > event_idx ||
+ event_idx > temp->end_idx)
+ continue;
/* just match the selector */
if (temp->select == (einfo[i].event_data &
temp->select_mask)) {
diff --git a/lib/sbi/sbi_sse.c b/lib/sbi/sbi_sse.c
index 818afb87..13d78e7c 100644
--- a/lib/sbi/sbi_sse.c
+++ b/lib/sbi/sbi_sse.c
@@ -563,19 +563,14 @@ static void sse_event_inject(struct sbi_sse_event *e,
if (misa_extension('H')) {
unsigned long hstatus = csr_read(CSR_HSTATUS);
-
-#if __riscv_xlen == 64
- if (regs->mstatus & MSTATUS_MPV)
-#elif __riscv_xlen == 32
- if (regs->mstatusH & MSTATUSH_MPV)
-#else
-#error "Unexpected __riscv_xlen"
-#endif
+ if (sbi_regs_from_virt(regs))
hstatus |= HSTATUS_SPV;
+ else
+ hstatus &= ~HSTATUS_SPV;
hstatus &= ~HSTATUS_SPVP;
if (hstatus & HSTATUS_SPV && regs->mstatus & SSTATUS_SPP)
- hstatus |= HSTATUS_SPVP;
+ hstatus |= HSTATUS_SPVP;
csr_write(CSR_HSTATUS, hstatus);
}
diff --git a/lib/sbi/sbi_timer.c b/lib/sbi/sbi_timer.c
index 4d737d22..8abb5e7b 100644
--- a/lib/sbi/sbi_timer.c
+++ b/lib/sbi/sbi_timer.c
@@ -104,6 +104,18 @@ bool sbi_timer_waitms_until(bool (*predicate)(void *), void *arg,
return true;
}
+unsigned long sbi_timer_frequency(void)
+{
+ const struct sbi_timer_device *timer_dev = sbi_timer_get_device();
+
+ if (!timer_dev) {
+ sbi_printf("%s: called without timer device\n", __func__);
+ return 0;
+ }
+
+ return timer_dev->timer_freq;
+}
+
u64 sbi_timer_value(void)
{
if (get_time_val)
diff --git a/lib/sbi/sbi_tlb.c b/lib/sbi/sbi_tlb.c
index ada60c32..ab1e3a99 100644
--- a/lib/sbi/sbi_tlb.c
+++ b/lib/sbi/sbi_tlb.c
@@ -29,6 +29,38 @@ static unsigned long tlb_fifo_off;
static unsigned long tlb_fifo_mem_off;
static unsigned long tlb_range_flush_limit;
+static void sbi_tlb_local_fence_i(struct sbi_tlb_info *tinfo);
+static void sbi_tlb_local_sfence_vma(struct sbi_tlb_info *tinfo);
+static void sbi_tlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo);
+static void sbi_tlb_local_hfence_gvma_vmid(struct sbi_tlb_info *tinfo);
+static void sbi_tlb_local_hfence_gvma(struct sbi_tlb_info *tinfo);
+static void sbi_tlb_local_hfence_vvma_asid(struct sbi_tlb_info *tinfo);
+static void sbi_tlb_local_hfence_vvma(struct sbi_tlb_info *tinfo);
+
+static struct sbi_tlb_local_operations default_tlb_local_ops = {
+ .local_fence_i = sbi_tlb_local_fence_i,
+ .local_sfence_vma = sbi_tlb_local_sfence_vma,
+ .local_sfence_vma_asid = sbi_tlb_local_sfence_vma_asid,
+ .local_hfence_gvma_vmid = sbi_tlb_local_hfence_gvma_vmid,
+ .local_hfence_gvma = sbi_tlb_local_hfence_gvma,
+ .local_hfence_vvma_asid = sbi_tlb_local_hfence_vvma_asid,
+ .local_hfence_vvma = sbi_tlb_local_hfence_vvma
+};
+
+static const struct sbi_tlb_local_operations *tlb_local_ops =
+ &default_tlb_local_ops;
+
+const struct sbi_tlb_local_operations *sbi_tlb_get_local_operations(void)
+{
+ return tlb_local_ops;
+}
+
+void sbi_tlb_set_local_operations(
+ const struct sbi_tlb_local_operations *ops)
+{
+ tlb_local_ops = ops;
+}
+
void __sbi_sfence_vma_all(void)
{
__asm__ __volatile("sfence.vma");
@@ -183,25 +215,25 @@ static void tlb_entry_local_process(struct sbi_tlb_info *data)
switch (data->type) {
case SBI_TLB_FENCE_I:
- sbi_tlb_local_fence_i(data);
+ tlb_local_ops->local_fence_i(data);
break;
case SBI_TLB_SFENCE_VMA:
- sbi_tlb_local_sfence_vma(data);
+ tlb_local_ops->local_sfence_vma(data);
break;
case SBI_TLB_SFENCE_VMA_ASID:
- sbi_tlb_local_sfence_vma_asid(data);
+ tlb_local_ops->local_sfence_vma_asid(data);
break;
case SBI_TLB_HFENCE_GVMA_VMID:
- sbi_tlb_local_hfence_gvma_vmid(data);
+ tlb_local_ops->local_hfence_gvma_vmid(data);
break;
case SBI_TLB_HFENCE_GVMA:
- sbi_tlb_local_hfence_gvma(data);
+ tlb_local_ops->local_hfence_gvma(data);
break;
case SBI_TLB_HFENCE_VVMA_ASID:
- sbi_tlb_local_hfence_vvma_asid(data);
+ tlb_local_ops->local_hfence_vvma_asid(data);
break;
case SBI_TLB_HFENCE_VVMA:
- sbi_tlb_local_hfence_vvma(data);
+ tlb_local_ops->local_hfence_vvma(data);
break;
default:
break;
diff --git a/lib/sbi/tests/sbi_ecall_test.c b/lib/sbi/tests/sbi_ecall_test.c
index 5b6ce37c..f5c553e9 100644
--- a/lib/sbi/tests/sbi_ecall_test.c
+++ b/lib/sbi/tests/sbi_ecall_test.c
@@ -40,10 +40,69 @@ static void test_sbi_ecall_register_find_extension(struct sbiunit_test_case *tes
SBIUNIT_EXPECT_EQ(test, sbi_ecall_find_extension(SBI_EXT_EXPERIMENTAL_START), NULL);
}
+static void test_sbi_ecall_get_extensions_str_bounds(struct sbiunit_test_case *test)
+{
+ struct sbi_ecall_extension e1 = {
+ .extid_start = SBI_EXT_EXPERIMENTAL_START,
+ .extid_end = SBI_EXT_EXPERIMENTAL_START,
+ .name = "Alpha",
+ .handle = dummy_handler,
+ .experimental = false,
+ };
+ struct sbi_ecall_extension e2 = {
+ .extid_start = SBI_EXT_EXPERIMENTAL_START + 1,
+ .extid_end = SBI_EXT_EXPERIMENTAL_START + 1,
+ .name = "Bravo",
+ .handle = dummy_handler,
+ .experimental = false,
+ };
+ struct sbi_ecall_extension e3 = {
+ .extid_start = SBI_EXT_EXPERIMENTAL_START + 2,
+ .extid_end = SBI_EXT_EXPERIMENTAL_START + 2,
+ .name = "Charli",
+ .handle = dummy_handler,
+ .experimental = false,
+ };
+ char storage[16 + 16];
+ char *buf = storage;
+ char big[128];
+ int i;
+ int found_alpha = 0;
+
+ SBIUNIT_EXPECT_EQ(test, sbi_ecall_register_extension(&e1), 0);
+ SBIUNIT_EXPECT_EQ(test, sbi_ecall_register_extension(&e2), 0);
+ SBIUNIT_EXPECT_EQ(test, sbi_ecall_register_extension(&e3), 0);
+
+ for (i = 16; i < 32; i++)
+ storage[i] = (char)0xA5;
+
+ /* Undersized buffer must not write past the caller-provided size. */
+ sbi_ecall_get_extensions_str(buf, 16, false);
+ SBIUNIT_EXPECT_EQ(test, buf[15], '\0');
+ for (i = 16; i < 32; i++)
+ SBIUNIT_EXPECT_EQ(test, (unsigned char)storage[i], 0xA5);
+
+ /* Negative control: room for the full list, including registered names. */
+ sbi_ecall_get_extensions_str(big, sizeof(big), false);
+ SBIUNIT_EXPECT_NE(test, sbi_strlen(big), 0);
+ for (i = 0; big[i] != '\0'; i++) {
+ if (sbi_strncmp(&big[i], "Alpha", 5) == 0) {
+ found_alpha = 1;
+ break;
+ }
+ }
+ SBIUNIT_EXPECT_EQ(test, found_alpha, 1);
+
+ sbi_ecall_unregister_extension(&e1);
+ sbi_ecall_unregister_extension(&e2);
+ sbi_ecall_unregister_extension(&e3);
+}
+
static struct sbiunit_test_case ecall_tests[] = {
SBIUNIT_TEST_CASE(test_sbi_ecall_version),
SBIUNIT_TEST_CASE(test_sbi_ecall_impid),
SBIUNIT_TEST_CASE(test_sbi_ecall_register_find_extension),
+ SBIUNIT_TEST_CASE(test_sbi_ecall_get_extensions_str_bounds),
SBIUNIT_END_CASE,
};
diff --git a/lib/utils/cache/fdt_andes_llcache.c b/lib/utils/cache/fdt_andes_llcache.c
index 490503ee..7a61b6ab 100644
--- a/lib/utils/cache/fdt_andes_llcache.c
+++ b/lib/utils/cache/fdt_andes_llcache.c
@@ -5,8 +5,11 @@
*/
#include <sbi/riscv_io.h>
+#include <sbi/sbi_domain.h>
#include <sbi/sbi_error.h>
+#include <sbi/sbi_hart.h>
#include <sbi/sbi_heap.h>
+#include <sbi/sbi_scratch.h>
#include <sbi_utils/cache/fdt_cache.h>
#include <sbi_utils/fdt/fdt_driver.h>
#include <sbi_utils/hsm/fdt_hsm_andes_atcsmu.h>
@@ -102,7 +105,10 @@ static int andes_llcache_enable(struct cache_device *dev, bool enable)
}
llcache_ctrl = readl(ctrl_addr);
- return enable == EXTRACT_FIELD(llcache_ctrl, LLCACHE_REG_CTRL_EN_MASK);
+ if (enable != EXTRACT_FIELD(llcache_ctrl, LLCACHE_REG_CTRL_EN_MASK))
+ return SBI_EFAIL;
+
+ return SBI_OK;
}
static struct cache_ops andes_llcache_ops = {
@@ -113,13 +119,13 @@ static struct cache_ops andes_llcache_ops = {
static int andes_llcache_probe(const void *fdt, int nodeoff, const struct fdt_match *match)
{
int rc;
- u64 llcache_base = 0;
+ u64 llcache_base = 0, size;
struct andes_llcache *llcache;
struct cache_device *dev;
uint32_t llcache_cfg;
- rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &llcache_base, NULL);
- if (rc < 0 || !llcache_base)
+ rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &llcache_base, &size);
+ if (rc < 0 || !llcache_base || !size)
return SBI_ENODEV;
llcache = sbi_zalloc(sizeof(*llcache));
@@ -148,6 +154,15 @@ static int andes_llcache_probe(const void *fdt, int nodeoff, const struct fdt_ma
llcache->status_core_stride = 4;
}
+ if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), SBI_HART_EXT_SMEPMP)) {
+ rc = sbi_domain_root_add_memrange(
+ (unsigned long)llcache->base, (unsigned long)size, PAGE_SIZE,
+ SBI_DOMAIN_MEMREGION_MMIO |
+ SBI_DOMAIN_MEMREGION_SHARED_SURW_MRW);
+ if (rc)
+ return rc;
+ }
+
/* Wait for the hardware initialization done */
while (!andes_llcache_init_done(llcache))
;
diff --git a/lib/utils/cppc/fdt_cppc_rpmi.c b/lib/utils/cppc/fdt_cppc_rpmi.c
index 7bd3b0f9..15c89351 100644
--- a/lib/utils/cppc/fdt_cppc_rpmi.c
+++ b/lib/utils/cppc/fdt_cppc_rpmi.c
@@ -12,6 +12,7 @@
#include <sbi/sbi_cppc.h>
#include <sbi/sbi_ecall_interface.h>
#include <sbi/sbi_scratch.h>
+#include <sbi/sbi_timer.h>
#include <sbi_utils/fdt/fdt_driver.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/mailbox/fdt_mailbox.h>
@@ -39,6 +40,8 @@ struct rpmi_cppc {
ulong fc_db_addr;
u64 fc_db_setmask;
u64 fc_db_preservemask;
+ u64 prev_time_csr;
+ u64 prev_delivered_counter;
};
static unsigned long rpmi_cppc_offset;
@@ -107,6 +110,7 @@ static void rpmi_cppc_fc_db_trigger(struct rpmi_cppc *cppc)
static int rpmi_cppc_read(unsigned long reg, u64 *val)
{
+ u64 curr_freq, curr_time_csr, time_csr_delta, freq;
int rc = SBI_SUCCESS;
struct rpmi_cppc_read_reg_req req;
struct rpmi_cppc_read_reg_resp resp;
@@ -116,12 +120,38 @@ static int rpmi_cppc_read(unsigned long reg, u64 *val)
req.reg_id = reg;
cppc = rpmi_cppc_get_pointer(req.hart_id);
- rc = rpmi_normal_request_with_status(
- cppc->chan, RPMI_CPPC_SRV_READ_REG,
- &req, rpmi_u32_count(req), rpmi_u32_count(req),
- &resp, rpmi_u32_count(resp), rpmi_u32_count(resp));
- if (rc)
- return rc;
+ if ((reg == SBI_CPPC_DELIVERED_CTR) && cppc->fc_supported) {
+#if __riscv_xlen != 32
+ curr_freq = readq((void *)cppc->fc_perf_feedback_addr);
+#else
+ curr_freq = readl((void *)cppc->fc_perf_feedback_addr + 4);
+ curr_freq <<= 32;
+ curr_freq |= readl((void *)cppc->fc_perf_feedback_addr);
+#endif
+ curr_time_csr = sbi_timer_value();
+ time_csr_delta = curr_time_csr - cppc->prev_time_csr;
+ cppc->prev_time_csr = curr_time_csr;
+
+ freq = sbi_timer_frequency();
+ if (freq)
+ cppc->prev_delivered_counter +=
+ curr_freq * (time_csr_delta / freq) +
+ (curr_freq * (time_csr_delta % freq)) / freq;
+
+ resp.data_lo = cpu_to_le32(cppc->prev_delivered_counter & UINT32_MAX);
+ resp.data_hi = cpu_to_le32(cppc->prev_delivered_counter >> 32);
+ } else if (reg == SBI_CPPC_REFERENCE_CTR) {
+ curr_time_csr = sbi_timer_value();
+ resp.data_lo = cpu_to_le32(curr_time_csr & UINT32_MAX);
+ resp.data_hi = cpu_to_le32(curr_time_csr >> 32);
+ } else {
+ rc = rpmi_normal_request_with_status(
+ cppc->chan, RPMI_CPPC_SRV_READ_REG,
+ &req, rpmi_u32_count(req), rpmi_u32_count(req),
+ &resp, rpmi_u32_count(resp), rpmi_u32_count(resp));
+ if (rc)
+ return rc;
+ }
#if __riscv_xlen == 32
*val = resp.data_lo;
@@ -276,6 +306,8 @@ static int rpmi_cppc_update_hart_scratch(struct mbox_chan *chan)
if (!cppc)
return SBI_ENOSYS;
+ cppc->prev_time_csr = 0;
+ cppc->prev_delivered_counter = 0;
cppc->chan = chan;
cppc->mode = cppc_mode;
cppc->fc_supported = fc_supported;
diff --git a/lib/utils/hsm/fdt_hsm_andes_atcsmu.c b/lib/utils/hsm/fdt_hsm_andes_atcsmu.c
index 115916d5..2885a983 100644
--- a/lib/utils/hsm/fdt_hsm_andes_atcsmu.c
+++ b/lib/utils/hsm/fdt_hsm_andes_atcsmu.c
@@ -56,7 +56,7 @@ bool atcsmu_support_sleep_mode(u32 sleep_type, u32 hartid)
void atcsmu_set_command(u32 pcs_ctl, u32 hartid)
{
- writel_relaxed(pcs_ctl, (char *)atcsmu_base + PCSm_CTL_OFFSET(hartid));
+ writel(pcs_ctl, (char *)atcsmu_base + PCSm_CTL_OFFSET(hartid));
}
int atcsmu_set_reset_vector(u64 wakeup_addr, u32 hartid)
@@ -94,42 +94,24 @@ u32 atcsmu_read_scratch(void)
return readl_relaxed((char *)atcsmu_base + SCRATCH_PAD_OFFSET);
}
-bool atcsmu_pcs_is_sleep(u32 hartid, bool deep_sleep)
+bool atcsmu_hart_is_sleep(void *opaque)
{
- u32 pcs_status = readl_relaxed((char *)atcsmu_base + PCSm_STATUS_OFFSET(hartid));
- u32 pd_status = deep_sleep ? PD_STATUS_DEEP_SLEEP : PD_STATUS_LIGHT_SLEEP;
+ struct atcsmu_sleep_arg *arg = opaque;
- if (EXTRACT_FIELD(pcs_status, PD_TYPE_MASK) != PD_TYPE_SLEEP) {
- sbi_printf("ATCSMU: hart%d (PCS%d): failed to sleep\n", hartid, hartid + 3);
- return false;
- }
-
- if (EXTRACT_FIELD(pcs_status, PD_STATUS_MASK) != pd_status) {
- sbi_printf("ATCSMU: hart%d (PCS%d): failed to enter %s sleep\n",
- hartid, hartid + 3, deep_sleep ? "deep" : "light");
- return false;
- }
+ u32 pcs_status = readl_relaxed((char *)atcsmu_base +
+ PCSm_STATUS_OFFSET(arg->hartid));
+ u32 pd_status = arg->deep_sleep ? PD_STATUS_DEEP_SLEEP :
+ PD_STATUS_LIGHT_SLEEP;
- return true;
+ return EXTRACT_FIELD(pcs_status, PD_TYPE_MASK) == PD_TYPE_SLEEP &&
+ EXTRACT_FIELD(pcs_status, PD_STATUS_MASK) == pd_status;
}
static int ae350_hart_start(u32 hartid, ulong saddr)
{
u32 hartindex = sbi_hartid_to_hartindex(hartid);
- u32 sleep_type = atcsmu_get_sleep_type(hartid);
-
- /*
- * Don't send wakeup command when:
- * 1) boot time
- * 2) the target hart is non-sleepable 25-series hart0
- * 3) light sleep
- */
- if (!sbi_init_count(hartindex) || (is_andes(25) && hartid == 0) ||
- sleep_type == SBI_SUSP_AE350_LIGHT_SLEEP)
- return sbi_ipi_raw_send(hartindex, false);
- atcsmu_set_command(WAKEUP_CMD, hartid);
- return 0;
+ return sbi_ipi_raw_send(hartindex, false);
}
static int ae350_hart_stop(void)
@@ -152,18 +134,19 @@ static int ae350_hart_stop(void)
/* Prevent the core leaving the WFI mode unexpectedly */
csr_write(CSR_MIE, 0);
+ atcsmu_set_wakeup_events(PCS_WAKEUP_MSIP_MASK | PCS_WAKEUP_MEIP_MASK, hartid);
if (sleep_type == SBI_SUSP_AE350_LIGHT_SLEEP) {
- csr_write(CSR_MIE, MIP_MSIP);
- atcsmu_set_wakeup_events(PCS_WAKEUP_MSIP_MASK, hartid);
+ /* Clock-gated only: needs MSI or MEI set to resume past the WFI */
+ csr_set(CSR_MIE, MIP_MSIP | MIP_MEIP);
atcsmu_set_command(LIGHT_SLEEP_CMD, hartid);
} else if (sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND) {
- atcsmu_set_wakeup_events(0x0, hartid);
- atcsmu_set_command(DEEP_SLEEP_CMD, hartid);
+ /* Power-gated: SMU wakes it via cold reset, interrupts not needed */
rc = atcsmu_set_reset_vector((ulong)ae350_enable_coherency_warmboot, hartid);
if (rc)
return SBI_EFAIL;
ae350_non_ret_save(sbi_scratch_thishart_ptr());
+ atcsmu_set_command(DEEP_SLEEP_CMD, hartid);
}
ae350_disable_coherency();
@@ -184,18 +167,27 @@ static const struct sbi_hsm_device hsm_andes_atcsmu = {
static int hsm_andes_atcsmu_probe(const void *fdt, int nodeoff, const struct fdt_match *match)
{
int poff, rc;
- u64 addr;
+ u64 addr, size;
/* Need to find the parent for the address property */
poff = fdt_parent_offset(fdt, nodeoff);
if (poff < 0)
return SBI_EINVAL;
- rc = fdt_get_node_addr_size(fdt, poff, 0, &addr, NULL);
- if (rc < 0 || !addr)
+ rc = fdt_get_node_addr_size(fdt, poff, 0, &addr, &size);
+ if (rc < 0 || !addr || !size)
return SBI_ENODEV;
- atcsmu_base = addr;
+ if (sbi_hart_has_extension(sbi_scratch_thishart_ptr(), SBI_HART_EXT_SMEPMP)) {
+ rc = sbi_domain_root_add_memrange(
+ (unsigned long)addr, (unsigned long)size, PAGE_SIZE,
+ SBI_DOMAIN_MEMREGION_MMIO |
+ SBI_DOMAIN_MEMREGION_SHARED_SURW_MRW);
+ if (rc)
+ return rc;
+ }
+
+ atcsmu_base = addr;
sbi_hsm_set_device(&hsm_andes_atcsmu);
return 0;
}
diff --git a/lib/utils/mpxy/fdt_mpxy_rpmi_logging.c b/lib/utils/mpxy/fdt_mpxy_rpmi_logging.c
index e8f085eb..1e92053d 100644
--- a/lib/utils/mpxy/fdt_mpxy_rpmi_logging.c
+++ b/lib/utils/mpxy/fdt_mpxy_rpmi_logging.c
@@ -11,7 +11,7 @@
static struct mpxy_rpmi_service_data logging_services[] = {
[0] = {
- .id = RPMI_MM_SRV_ENABLE_NOTIFICATION,
+ .id = RPMI_LOGGING_SRV_ENABLE_NOTIFICATION,
.min_tx_len = sizeof(struct rpmi_enable_notification_req),
.max_tx_len = sizeof(struct rpmi_enable_notification_req),
.min_rx_len = sizeof(struct rpmi_enable_notification_resp),
diff --git a/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c b/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c
index d7176aca..7185f3f3 100644
--- a/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c
+++ b/lib/utils/mpxy/fdt_mpxy_rpmi_mm.c
@@ -15,13 +15,20 @@ static struct rpmi_mm_get_attributes_rsp rsp;
static struct mpxy_rpmi_service_data mm_srvcdata[] = {
[0] = {
+ .id = RPMI_MM_SRV_ENABLE_NOTIFICATION,
+ .min_tx_len = sizeof(struct rpmi_enable_notification_req),
+ .max_tx_len = sizeof(struct rpmi_enable_notification_req),
+ .min_rx_len = sizeof(struct rpmi_enable_notification_resp),
+ .max_rx_len = sizeof(struct rpmi_enable_notification_resp),
+ },
+ [1] = {
.id = RPMI_MM_SRV_GET_ATTRIBUTES,
.min_tx_len = 0,
.max_tx_len = 0,
.min_rx_len = sizeof(struct rpmi_mm_get_attributes_rsp),
.max_rx_len = sizeof(struct rpmi_mm_get_attributes_rsp),
},
- [1] = {
+ [2] = {
.id = RPMI_MM_SRV_COMMUNICATE,
.min_tx_len = sizeof(struct rpmi_mm_communicate_req),
.max_tx_len = sizeof(struct rpmi_mm_communicate_req),
diff --git a/lib/utils/reset/Kconfig b/lib/utils/reset/Kconfig
index b2ac120e..8988548c 100644
--- a/lib/utils/reset/Kconfig
+++ b/lib/utils/reset/Kconfig
@@ -29,6 +29,10 @@ config FDT_RESET_LITEX
depends on FDT_GPIO
default n
+config FDT_RESET_METANOIA
+ bool "Metanoia FDT reset driver"
+ default n
+
config FDT_RESET_RPMI
bool "RPMI FDT reset driver"
depends on FDT_MAILBOX && RPMI_MAILBOX
diff --git a/lib/utils/reset/fdt_reset_metanoia.c b/lib/utils/reset/fdt_reset_metanoia.c
new file mode 100644
index 00000000..6f75a893
--- /dev/null
+++ b/lib/utils/reset/fdt_reset_metanoia.c
@@ -0,0 +1,72 @@
+/*
+ * SPDX-License-Identifier: BSD-2-Clause
+ *
+ * Copyright (c) 2026 Metanoia Communications Inc.
+ *
+ */
+
+#include <sbi/riscv_io.h>
+#include <sbi/sbi_platform.h>
+#include <sbi/sbi_system.h>
+#include <sbi_utils/fdt/fdt_helper.h>
+#include <sbi_utils/fdt/fdt_driver.h>
+
+#define SWRSTREQ_CTRL_REG_OFFSET 0x00
+#define SWRSTREQ_REG_OFFSET 0x04
+
+struct reset_metanoia_data {
+ u8 *reg_base;
+};
+
+static struct reset_metanoia_data reset_data;
+
+static int metanoia_system_reset_check(u32 type, u32 reason)
+{
+ switch (type) {
+ case SBI_SRST_RESET_TYPE_WARM_REBOOT:
+ case SBI_SRST_RESET_TYPE_COLD_REBOOT:
+ return 1;
+ case SBI_SRST_RESET_TYPE_SHUTDOWN:
+ default:
+ return 0;
+ }
+}
+
+static void metanoia_system_reset(u32 type, u32 reason)
+{
+ writew(0x1, reset_data.reg_base + SWRSTREQ_CTRL_REG_OFFSET);
+ writew(0x1, reset_data.reg_base + SWRSTREQ_REG_OFFSET);
+}
+
+static struct sbi_system_reset_device metanoia_reset = {
+ .name = "metanoia-mt2824-reboot",
+ .system_reset_check = metanoia_system_reset_check,
+ .system_reset = metanoia_system_reset,
+};
+
+static int metanoia_reset_init(const void *fdt, int nodeoff,
+ const struct fdt_match *match)
+{
+ u64 reg_addr;
+ int rc;
+
+ rc = fdt_get_node_addr_size(fdt, nodeoff, 0, &reg_addr, NULL);
+ if (rc < 0 || !reg_addr)
+ return SBI_ENODEV;
+
+ reset_data.reg_base = (u8 *)(ulong)reg_addr;
+
+ sbi_system_reset_add_device(&metanoia_reset);
+
+ return 0;
+}
+
+static const struct fdt_match metanoia_reset_match[] = {
+ { .compatible = "metanoia,mt2824-reboot" },
+ { /* sentinel */ }
+};
+
+const struct fdt_driver fdt_reset_metanoia = {
+ .match_table = metanoia_reset_match,
+ .init = metanoia_reset_init,
+};
diff --git a/lib/utils/reset/objects.mk b/lib/utils/reset/objects.mk
index 38b4e306..a9b6edad 100644
--- a/lib/utils/reset/objects.mk
+++ b/lib/utils/reset/objects.mk
@@ -20,6 +20,9 @@ libsbiutils-objs-$(CONFIG_FDT_RESET_HTIF) += reset/fdt_reset_htif.o
carray-fdt_early_drivers-$(CONFIG_FDT_RESET_LITEX) += fdt_reset_litex
libsbiutils-objs-$(CONFIG_FDT_RESET_LITEX) += reset/fdt_reset_litex.o
+carray-fdt_early_drivers-$(CONFIG_FDT_RESET_METANOIA) += fdt_reset_metanoia
+libsbiutils-objs-$(CONFIG_FDT_RESET_METANOIA) += reset/fdt_reset_metanoia.o
+
carray-fdt_early_drivers-$(CONFIG_FDT_RESET_SG2042_HWMON_MCU) += fdt_reset_sg2042_mcu
libsbiutils-objs-$(CONFIG_FDT_RESET_SG2042_HWMON_MCU) += reset/fdt_reset_sg2042_hwmon_mcu.o
diff --git a/lib/utils/suspend/fdt_suspend_andes_atcsmu.c b/lib/utils/suspend/fdt_suspend_andes_atcsmu.c
index 072accc0..86cdf955 100644
--- a/lib/utils/suspend/fdt_suspend_andes_atcsmu.c
+++ b/lib/utils/suspend/fdt_suspend_andes_atcsmu.c
@@ -12,22 +12,37 @@
#include <sbi/sbi_ecall_interface.h>
#include <sbi/sbi_hart.h>
#include <sbi/sbi_system.h>
+#include <sbi/sbi_timer.h>
#include <sbi_utils/cache/fdt_cmo_helper.h>
#include <sbi_utils/fdt/fdt_driver.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/hsm/fdt_hsm_andes_atcsmu.h>
-static int check_secondary_harts_sleep(u32 hartid, bool deep_sleep)
+#define HART_SLEEP_TIMEOUT_MS 1000
+
+static int wait_secondary_harts_sleep(u32 hartid, bool deep_sleep)
{
const struct sbi_domain *dom = &root;
unsigned long i;
u32 target;
+ struct atcsmu_sleep_arg arg;
+
+ arg.deep_sleep = deep_sleep;
- /* Ensure the secondary harts entering the corresponding sleep state */
+ /* Wait for the secondary harts entering the corresponding sleep state */
sbi_hartmask_for_each_hartindex(i, dom->possible_harts) {
target = sbi_hartindex_to_hartid(i);
- if (target != hartid && !atcsmu_pcs_is_sleep(target, deep_sleep))
- return SBI_EFAIL;
+ if (target == hartid)
+ continue;
+
+ arg.hartid = target;
+ if (!sbi_timer_waitms_until(atcsmu_hart_is_sleep, &arg,
+ HART_SLEEP_TIMEOUT_MS)) {
+ sbi_printf("ATCSMU: hart%u (PCS%u): timed out waiting for %s sleep\n",
+ target, target + 3,
+ deep_sleep ? "deep" : "light");
+ return SBI_ETIMEOUT;
+ }
}
return SBI_OK;
@@ -42,39 +57,45 @@ static int ae350_system_suspend_check(u32 sleep_type)
static int ae350_system_suspend(u32 sleep_type, unsigned long addr)
{
u32 hartid = current_hartid();
+ unsigned long saved_mie;
int rc;
/* Prevent the core leaving the WFI mode unexpectedly */
+ saved_mie = csr_read(CSR_MIE);
csr_write(CSR_MIE, 0);
- /*
- * Only allow the S-mode external interrupts (UART2 and RTC alarm) to
- * wake up the primary hart
- */
- csr_set(CSR_SIE, MIP_SEIP);
+ /* SMU wakes the primary hart on RTC alarm / UART2 */
atcsmu_set_wakeup_events(PCS_WAKEUP_RTC_ALARM_MASK | PCS_WAKEUP_UART2_MASK, hartid);
if (sleep_type == SBI_SUSP_AE350_LIGHT_SLEEP) {
- rc = check_secondary_harts_sleep(hartid, false);
+ rc = wait_secondary_harts_sleep(hartid, false);
if (rc)
- return rc;
+ goto err_restore_mie;
+ /* Clock-gated only: enable SEI to resume past the WFI */
+ csr_set(CSR_MIE, MIP_SEIP);
atcsmu_set_command(LIGHT_SLEEP_CMD, hartid);
} else if (sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND) {
- rc = check_secondary_harts_sleep(hartid, true);
+ rc = wait_secondary_harts_sleep(hartid, true);
if (rc)
- return rc;
+ goto err_restore_mie;
- atcsmu_set_command(DEEP_SLEEP_CMD, hartid);
rc = atcsmu_set_reset_vector((ulong)ae350_enable_coherency_warmboot, hartid);
if (rc)
- return rc;
+ goto err_restore_mie;
ae350_non_ret_save(sbi_scratch_thishart_ptr());
- fdt_cmo_llc_enable(false);
+
+ /* No LLC is fine; only fail on real errors */
+ rc = fdt_cmo_llc_enable(false);
+ if (rc && rc != SBI_ENODEV)
+ goto err_discard_save;
+
rc = fdt_cmo_llc_flush_all();
- if (rc)
- return rc;
+ if (rc && rc != SBI_ENODEV)
+ goto err_enable_llc;
+
+ atcsmu_set_command(DEEP_SLEEP_CMD, hartid);
}
ae350_disable_coherency();
@@ -84,6 +105,15 @@ static int ae350_system_suspend(u32 sleep_type, unsigned long addr)
ae350_enable_coherency();
return SBI_OK;
+
+err_enable_llc:
+ fdt_cmo_llc_enable(true);
+err_discard_save:
+ ae350_non_ret_discard(sbi_scratch_thishart_ptr());
+err_restore_mie:
+ csr_write(CSR_MIE, saved_mie);
+
+ return rc;
}
static void ae350_system_resume(void)
@@ -91,8 +121,10 @@ static void ae350_system_resume(void)
u32 hartid = current_hartid();
u32 sleep_type = atcsmu_get_sleep_type(hartid);
- if (sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND)
+ if (sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND) {
fdt_cmo_llc_enable(true);
+ ae350_non_ret_restore(sbi_scratch_thishart_ptr());
+ }
}
static struct sbi_system_suspend_device suspend_andes_atcsmu = {
diff --git a/platform/generic/Kconfig b/platform/generic/Kconfig
index d594b140..64fc4a64 100644
--- a/platform/generic/Kconfig
+++ b/platform/generic/Kconfig
@@ -51,6 +51,13 @@ config PLATFORM_ESWIN_EIC770X
bool "ESWIN EIC770X support"
default n
+config PLATFORM_METANOIA_MT2824
+ bool "Metanoia MT2824 support"
+ select ANDES_PMA
+ select ANDES_SBI
+ select ANDES_PMU
+ default n
+
config PLATFORM_OPENHWGROUP_OPENPITON
bool "OpenHWGroup Openpiton support"
default n
diff --git a/platform/generic/andes/ae350.c b/platform/generic/andes/ae350.c
index 4cf7e26d..7aafe545 100644
--- a/platform/generic/andes/ae350.c
+++ b/platform/generic/andes/ae350.c
@@ -34,6 +34,16 @@ void ae350_non_ret_save(struct sbi_scratch *scratch)
andes_hdata->pmacfg2 = csr_read_num(CSR_PMACFG0 + 2);
for (int i = 0; i < 16; i++)
andes_hdata->pmaaddrX[i] = csr_read_num(CSR_PMAADDR0 + i);
+
+ andes_hdata->saved = true;
+}
+
+void ae350_non_ret_discard(struct sbi_scratch *scratch)
+{
+ struct andes_hart_data *andes_hdata = sbi_scratch_offset_ptr(scratch,
+ andes_hart_data_offset);
+
+ andes_hdata->saved = false;
}
void ae350_non_ret_restore(struct sbi_scratch *scratch)
@@ -41,6 +51,9 @@ void ae350_non_ret_restore(struct sbi_scratch *scratch)
struct andes_hart_data *andes_hdata = sbi_scratch_offset_ptr(scratch,
andes_hart_data_offset);
+ if (!andes_hdata->saved)
+ return;
+
csr_write(CSR_MCACHE_CTL, andes_hdata->mcache_ctl);
csr_write(CSR_MMISC_CTL, andes_hdata->mmisc_ctl);
csr_write(CSR_MPFT_CTL, andes_hdata->mpft_ctl);
@@ -52,6 +65,8 @@ void ae350_non_ret_restore(struct sbi_scratch *scratch)
csr_write_num(CSR_PMACFG0 + 2, andes_hdata->pmacfg2);
for (int i = 0; i < 16; i++)
csr_write_num(CSR_PMAADDR0 + i, andes_hdata->pmaaddrX[i]);
+
+ andes_hdata->saved = false;
}
void ae350_enable_coherency_warmboot(void)
@@ -62,18 +77,17 @@ void ae350_enable_coherency_warmboot(void)
static int ae350_early_init(bool cold_boot)
{
- u32 hartid = current_hartid();
- u32 sleep_type = atcsmu_get_sleep_type(hartid);
-
if (cold_boot) {
andes_hart_data_offset = sbi_scratch_alloc_offset(sizeof(struct andes_hart_data));
if (!andes_hart_data_offset)
return SBI_ENOMEM;
}
- /* Don't restore Andes CSRs during boot or wake up from light sleep */
- if (sbi_init_count(current_hartindex()) && sleep_type == SBI_SUSP_SLEEP_TYPE_SUSPEND)
- ae350_non_ret_restore(sbi_scratch_thishart_ptr());
+ /*
+ * This is a no-op unless this hart actually saved them, so it is safe
+ * to call on every path.
+ */
+ ae350_non_ret_restore(sbi_scratch_thishart_ptr());
return generic_early_init(cold_boot);
}
diff --git a/platform/generic/andes/andes_pma.c b/platform/generic/andes/andes_pma.c
index ba9a35bb..28d33a2f 100644
--- a/platform/generic/andes/andes_pma.c
+++ b/platform/generic/andes/andes_pma.c
@@ -82,8 +82,8 @@ static void decode_pmaaddrx(int entry_id, unsigned long *start,
*/
pmaaddr = csr_read_num(CSR_PMAADDR0 + entry_id);
k = sbi_ffz(pmaaddr);
- *size = 1 << (k + 3);
- *start = (pmaaddr - (1 << k) + 1) << 2;
+ *size = 1UL << (k + 3);
+ *start = (pmaaddr - (1UL << k) + 1) << 2;
}
static bool has_pma_region_overlap(unsigned long start, unsigned long size)
diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig
index 341f6b25..98829884 100644
--- a/platform/generic/configs/defconfig
+++ b/platform/generic/configs/defconfig
@@ -2,6 +2,7 @@ CONFIG_PLATFORM_ALLWINNER_D1=y
CONFIG_PLATFORM_ANDES_AE350=y
CONFIG_PLATFORM_ANDES_QILAI=y
CONFIG_PLATFORM_ESWIN_EIC770X=y
+CONFIG_PLATFORM_METANOIA_MT2824=y
CONFIG_PLATFORM_OPENHWGROUP_ARIANE=y
CONFIG_PLATFORM_OPENHWGROUP_OPENPITON=y
CONFIG_PLATFORM_RENESAS_RZFIVE=y
@@ -51,6 +52,7 @@ CONFIG_FDT_RESET_ATCWDT200=y
CONFIG_FDT_RESET_GPIO=y
CONFIG_FDT_RESET_LITEX=y
CONFIG_FDT_RESET_HTIF=y
+CONFIG_FDT_RESET_METANOIA=y
CONFIG_FDT_RESET_RPMI=y
CONFIG_FDT_RESET_SG2042_HWMON_MCU=y
CONFIG_FDT_RESET_SPACEMIT_P1=y
diff --git a/platform/generic/include/andes/andes.h b/platform/generic/include/andes/andes.h
index dd245171..dfa9f3c1 100644
--- a/platform/generic/include/andes/andes.h
+++ b/platform/generic/include/andes/andes.h
@@ -96,9 +96,13 @@ struct andes_hart_data {
unsigned long pmacfg0;
unsigned long pmacfg2;
unsigned long pmaaddrX[16];
+
+ /* Set when the CSRs above hold a saved copy awaiting restore */
+ bool saved;
};
void ae350_non_ret_save(struct sbi_scratch *scratch);
+void ae350_non_ret_discard(struct sbi_scratch *scratch);
void ae350_non_ret_restore(struct sbi_scratch *scratch);
void ae350_enable_coherency_warmboot(void);
diff --git a/platform/generic/include/thead/c9xx_errata.h b/platform/generic/include/thead/c9xx_errata.h
index 40c1587b..7a419e41 100644
--- a/platform/generic/include/thead/c9xx_errata.h
+++ b/platform/generic/include/thead/c9xx_errata.h
@@ -8,6 +8,7 @@
*/
#define THEAD_QUIRK_ERRATA_TLB_FLUSH BIT(0)
#define THEAD_QUIRK_ERRATA_THEAD_PMU BIT(1)
+#define THEAD_QUIRK_ERRATA_JTLB BIT(2)
void thead_register_tlb_flush_trap_handler(void);
diff --git a/platform/generic/metanoia/mt2824/mt2824.c b/platform/generic/metanoia/mt2824/mt2824.c
new file mode 100644
index 00000000..a34304b9
--- /dev/null
+++ b/platform/generic/metanoia/mt2824/mt2824.c
@@ -0,0 +1,31 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (C) 2026 Metanoia Communication Inc.
+ *
+ * Authors:
+ * Jun Chang <[email protected]>
+ */
+
+#include <andes/andes_pmu.h>
+#include <andes/andes_sbi.h>
+#include <platform_override.h>
+
+static int metanoia_mt2824_platform_init(const void *fdt, int nodeoff,
+ const struct fdt_match *match)
+{
+ generic_platform_ops.extensions_init = andes_pmu_extensions_init;
+ generic_platform_ops.pmu_init = andes_pmu_init;
+ generic_platform_ops.vendor_ext_provider = andes_sbi_vendor_ext_provider;
+
+ return 0;
+}
+
+static const struct fdt_match metanoia_mt2824_match[] = {
+ { .compatible = "metanoia,mt2824" },
+ { /* sentinel */ }
+};
+
+const struct fdt_driver metanoia_mt2824 = {
+ .match_table = metanoia_mt2824_match,
+ .init = metanoia_mt2824_platform_init,
+};
diff --git a/platform/generic/metanoia/mt2824/objects.mk b/platform/generic/metanoia/mt2824/objects.mk
new file mode 100644
index 00000000..b97c7775
--- /dev/null
+++ b/platform/generic/metanoia/mt2824/objects.mk
@@ -0,0 +1,10 @@
+#
+# SPDX-License-Identifier: BSD-2-Clause
+#
+# Copyright (C) 2026 Metanoia Communication Inc.
+#
+
+ifeq ($(PLATFORM_RISCV_XLEN), 64)
+carray-platform_override_modules-$(CONFIG_PLATFORM_METANOIA_MT2824) += metanoia_mt2824
+platform-objs-$(CONFIG_PLATFORM_METANOIA_MT2824) += metanoia/mt2824/mt2824.o
+endif
diff --git a/platform/generic/sophgo/sg2042.c b/platform/generic/sophgo/sg2042.c
index ac8840e8..81b28376 100644
--- a/platform/generic/sophgo/sg2042.c
+++ b/platform/generic/sophgo/sg2042.c
@@ -31,21 +31,6 @@ static int sophgo_sg2042_early_init(bool cold_boot)
thead_register_tlb_flush_trap_handler();
- /*
- * Sophgo sg2042 soc use separate 16 timers while initiating,
- * merge them as a single domain to avoid wasting.
- */
- if (cold_boot)
- return sbi_domain_root_add_memrange(
- (ulong)SOPHGO_SG2042_TIMER_BASE,
- SOPHGO_SG2042_TIMER_SIZE *
- SOPHGO_SG2042_TIMER_NUM,
- MTIMER_REGION_ALIGN,
- (SBI_DOMAIN_MEMREGION_MMIO |
- SBI_DOMAIN_MEMREGION_M_READABLE |
- SBI_DOMAIN_MEMREGION_M_WRITABLE));
-
-
return 0;
}
@@ -57,6 +42,23 @@ static int sophgo_sg2042_extensions_init(bool cold_boot)
if (rc)
return rc;
+ /*
+ * SG2042 has 16 separate timers. Add one combined region before the
+ * MTIMER driver adds the individual regions.
+ */
+ if (cold_boot) {
+ rc = sbi_domain_root_add_memrange(
+ (ulong)SOPHGO_SG2042_TIMER_BASE,
+ SOPHGO_SG2042_TIMER_SIZE *
+ SOPHGO_SG2042_TIMER_NUM,
+ MTIMER_REGION_ALIGN,
+ (SBI_DOMAIN_MEMREGION_MMIO |
+ SBI_DOMAIN_MEMREGION_M_READABLE |
+ SBI_DOMAIN_MEMREGION_M_WRITABLE));
+ if (rc)
+ return rc;
+ }
+
thead_c9xx_register_pmu_device();
return 0;
}
diff --git a/platform/generic/thead/thead-generic.c b/platform/generic/thead/thead-generic.c
index fef0871b..c83eb55c 100644
--- a/platform/generic/thead/thead-generic.c
+++ b/platform/generic/thead/thead-generic.c
@@ -11,8 +11,10 @@
#include <thead/c9xx_pmu.h>
#include <sbi/sbi_const.h>
#include <sbi/sbi_platform.h>
+#include <sbi/sbi_pmu.h>
#include <sbi/sbi_scratch.h>
#include <sbi/sbi_string.h>
+#include <sbi/sbi_tlb.h>
#include <sbi_utils/fdt/fdt_helper.h>
struct thead_generic_quirks {
@@ -39,15 +41,38 @@ static int thead_pmu_extensions_init(bool cold_boot)
return 0;
}
+static void thead_jtlb_local_sfence_vma(struct sbi_tlb_info *tinfo)
+{
+ sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_RCVD);
+ __asm__ __volatile__("sfence.vma");
+}
+
+static void thead_jtlb_local_sfence_vma_asid(struct sbi_tlb_info *tinfo)
+{
+ sbi_pmu_ctr_incr_fw(SBI_PMU_FW_SFENCE_VMA_ASID_RCVD);
+ /* Flush entire MM context for a given ASID */
+ __asm__ __volatile__("sfence.vma x0, %0"
+ :
+ : "r"(tinfo->asid)
+ : "memory");
+}
+
static int thead_generic_platform_init(const void *fdt, int nodeoff,
const struct fdt_match *match)
{
const struct thead_generic_quirks *quirks = match->data;
+ static struct sbi_tlb_local_operations tlb_local_ops;
if (quirks->errata & THEAD_QUIRK_ERRATA_TLB_FLUSH)
generic_platform_ops.early_init = thead_tlb_flush_early_init;
if (quirks->errata & THEAD_QUIRK_ERRATA_THEAD_PMU)
generic_platform_ops.extensions_init = thead_pmu_extensions_init;
+ if (quirks->errata &THEAD_QUIRK_ERRATA_JTLB) {
+ sbi_memcpy(&tlb_local_ops, sbi_tlb_get_local_operations(), sizeof(tlb_local_ops));
+ tlb_local_ops.local_sfence_vma = thead_jtlb_local_sfence_vma;
+ tlb_local_ops.local_sfence_vma_asid = thead_jtlb_local_sfence_vma_asid;
+ sbi_tlb_set_local_operations(&tlb_local_ops);
+ }
return 0;
}
@@ -60,13 +85,17 @@ static const struct thead_generic_quirks thead_pmu_quirks = {
.errata = THEAD_QUIRK_ERRATA_THEAD_PMU,
};
+static const struct thead_generic_quirks thead_pmu_jtlb_quirks = {
+ .errata = THEAD_QUIRK_ERRATA_THEAD_PMU | THEAD_QUIRK_ERRATA_JTLB,
+};
+
static const struct fdt_match thead_generic_match[] = {
{ .compatible = "canaan,kendryte-k230", .data = &thead_pmu_quirks },
{ .compatible = "sophgo,cv1800b", .data = &thead_pmu_quirks },
{ .compatible = "sophgo,cv1812h", .data = &thead_pmu_quirks },
{ .compatible = "sophgo,sg2000", .data = &thead_pmu_quirks },
{ .compatible = "sophgo,sg2002", .data = &thead_pmu_quirks },
- { .compatible = "sophgo,sg2044", .data = &thead_pmu_quirks },
+ { .compatible = "sophgo,sg2044", .data = &thead_pmu_jtlb_quirks },
{ .compatible = "thead,th1520", .data = &thead_th1520_quirks },
{ },
};