diff --git a/include/sbi/riscv_encoding.h b/include/sbi/riscv_encoding.h index 4ebed97a..07122069 100644 --- a/include/sbi/riscv_encoding.h +++ b/include/sbi/riscv_encoding.h @@ -223,6 +223,8 @@ #define ENVCFG_CBIE_INV _UL(0x3) #define ENVCFG_FIOM _UL(0x1) +#define MCOUNTEREN_TM (_UL(1) << 1) + /* ===== User-level CSRs ===== */ /* User Trap Setup (N-extension) */ diff --git a/include/sbi/sbi_platform.h b/include/sbi/sbi_platform.h index 546c0a61..49667230 100644 --- a/include/sbi/sbi_platform.h +++ b/include/sbi/sbi_platform.h @@ -68,6 +68,9 @@ struct sbi_platform_operations { /* Check if specified HART is allowed to do cold boot */ bool (*cold_boot_allowed)(u32 hartid); + /* Check if platform force emulate time csr, instead of using csr directly */ + bool (*force_emulate_time_csr)(void); + /* Platform nascent initialization */ int (*nascent_init)(void); @@ -376,6 +379,20 @@ static inline bool sbi_platform_cold_boot_allowed( return true; } +/** + * Check whether given platform should force emulate time CSR + * + * @param plat pointer to struct sbi_platform + * + * @return true such platform force emulate time CSR, false otherwise + */ +static inline bool sbi_platform_force_emulate_time_csr(const struct sbi_platform *plat) +{ + if (plat && sbi_platform_ops(plat)->force_emulate_time_csr) + return sbi_platform_ops(plat)->force_emulate_time_csr(); + return false; +} + /** * Nascent (very early) initialization for current HART * diff --git a/include/sbi_utils/timer/sg2042_gmt.h b/include/sbi_utils/timer/sg2042_gmt.h new file mode 100644 index 00000000..cef24f5a --- /dev/null +++ b/include/sbi_utils/timer/sg2042_gmt.h @@ -0,0 +1,21 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2025 SOPHGO Corporation + * + * Authors: + * Chao Wei + */ + +#ifndef __SG2042_GMT_H__ +#define __SG2042_GMT_H__ + +int sg2042gmt_cold_timer_init(unsigned long mtimer_base, + unsigned long mtimecmp_base, + unsigned long mtimecmp_size, + unsigned long delcared_freq, + unsigned long actual_freq, + unsigned long timecmp_freq); +int sg2042gmt_warm_timer_init(void); + +#endif /* __SG2042_GMT_H__ */ diff --git a/lib/sbi/sbi_hart.c b/lib/sbi/sbi_hart.c index 5e06918c..a8e75e83 100644 --- a/lib/sbi/sbi_hart.c +++ b/lib/sbi/sbi_hart.c @@ -33,6 +33,7 @@ static unsigned long hart_features_offset; static void mstatus_init(struct sbi_scratch *scratch) { + const struct sbi_platform *plat = sbi_platform_ptr(scratch); unsigned long menvcfg_val, mstatus_val = 0; int cidx; unsigned int num_mhpm = sbi_hart_mhpm_count(scratch); @@ -62,6 +63,11 @@ static void mstatus_init(struct sbi_scratch *scratch) if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10) csr_write(CSR_MCOUNTEREN, -1); + if (sbi_platform_force_emulate_time_csr(plat)) { + csr_clear(CSR_MCOUNTEREN, MCOUNTEREN_TM); + csr_clear(CSR_SCOUNTEREN, MCOUNTEREN_TM); + } + /* All programmable counters will start running at runtime after S-mode request */ if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11) csr_write(CSR_MCOUNTINHIBIT, 0xFFFFFFF8); @@ -558,6 +564,7 @@ static int hart_pmu_get_allowed_bits(void) static int hart_detect_features(struct sbi_scratch *scratch) { struct sbi_trap_info trap = {0}; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); struct sbi_hart_features *hfeatures = sbi_scratch_offset_ptr(scratch, hart_features_offset); unsigned long val, oldval; @@ -673,10 +680,12 @@ __mhpm_skip: } /* Detect if hart supports time CSR */ - csr_read_allowed(CSR_TIME, (unsigned long)&trap); - if (!trap.cause) - __sbi_hart_update_extension(hfeatures, - SBI_HART_EXT_TIME, true); + if (!sbi_platform_force_emulate_time_csr(plat)) { + csr_read_allowed(CSR_TIME, (unsigned long)&trap); + if (!trap.cause) + __sbi_hart_update_extension(hfeatures, + SBI_HART_EXT_TIME, true); + } /* Detect if hart has AIA local interrupt CSRs */ csr_read_allowed(CSR_MTOPI, (unsigned long)&trap); diff --git a/lib/sbi/sbi_pmu.c b/lib/sbi/sbi_pmu.c index 108c56e1..8aa8c258 100644 --- a/lib/sbi/sbi_pmu.c +++ b/lib/sbi/sbi_pmu.c @@ -882,12 +882,17 @@ void sbi_pmu_set_device(const struct sbi_pmu_device *dev) void sbi_pmu_exit(struct sbi_scratch *scratch) { u32 hartid = current_hartid(); + const struct sbi_platform *plat = sbi_platform_ptr(scratch); if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_11) csr_write(CSR_MCOUNTINHIBIT, 0xFFFFFFF8); if (sbi_hart_priv_version(scratch) >= SBI_HART_PRIV_VER_1_10) csr_write(CSR_MCOUNTEREN, -1); + + if (sbi_platform_force_emulate_time_csr(plat)) + csr_clear(CSR_MCOUNTEREN, MCOUNTEREN_TM); + pmu_reset_event_map(hartid); } diff --git a/lib/utils/timer/Kconfig b/lib/utils/timer/Kconfig index ba211b62..a8ab0dc8 100644 --- a/lib/utils/timer/Kconfig +++ b/lib/utils/timer/Kconfig @@ -19,6 +19,11 @@ config FDT_TIMER_PLMT select TIMER_PLMT default n +config FDT_TIMER_SG2042_GMT + bool "SOPHGO SG2042 global mtimer FDT driver" + select TIMER_SG2042_GMT + default n + endif config TIMER_MTIMER @@ -29,4 +34,8 @@ config TIMER_PLMT bool "Andes PLMT support" default n +config TIMER_SG2042_GMT + bool "SOPHGO SG2042 GMT support" + default n + endmenu diff --git a/lib/utils/timer/fdt_timer_sg2042_gmt.c b/lib/utils/timer/fdt_timer_sg2042_gmt.c new file mode 100644 index 00000000..015b3879 --- /dev/null +++ b/lib/utils/timer/fdt_timer_sg2042_gmt.c @@ -0,0 +1,61 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 Andes Technology Corporation + * + * Authors: + * Yu Chien Peter Lin + */ + +#include +#include +#include +#include +#include + +static int fdt_sg2042gmt_cold_timer_init(void *fdt, int nodeoff, + const struct fdt_match *match) +{ + int err; + unsigned long mtimer_base, mtimer_size, + mtimecmp_base, mtimecmp_size, + declared_freq, actual_freq, timecmp_freq; + const fdt32_t *val; + int len; + + err = fdt_get_node_addr_size(fdt, nodeoff, 0, &mtimer_base, &mtimer_size); + if (err) + return err; + + err = fdt_get_node_addr_size(fdt, nodeoff, 1, &mtimecmp_base, &mtimecmp_size); + if (err) + return err; + + + err = fdt_parse_timebase_frequency(fdt, &declared_freq); + if (err) + return err; + + val = (fdt32_t *)fdt_getprop(fdt, nodeoff, "clock-frequency", &len); + if (len < 8 || val == NULL) + return SBI_EINVAL; + + actual_freq = fdt32_to_cpu(*val); + timecmp_freq = fdt32_to_cpu(*(val + 1)); + + return sg2042gmt_cold_timer_init(mtimer_base, + mtimecmp_base, mtimecmp_size, + declared_freq, actual_freq, timecmp_freq); +} + +static const struct fdt_match timer_sg2042gmt_match[] = { + { .compatible = "sophgo,sg2042-global-mtimer" }, + {}, +}; + +struct fdt_timer fdt_timer_sg2042_gmt = { + .match_table = timer_sg2042gmt_match, + .cold_init = fdt_sg2042gmt_cold_timer_init, + .warm_init = sg2042gmt_warm_timer_init, + .exit = NULL, +}; diff --git a/lib/utils/timer/objects.mk b/lib/utils/timer/objects.mk index 9360a762..a80ed594 100644 --- a/lib/utils/timer/objects.mk +++ b/lib/utils/timer/objects.mk @@ -9,6 +9,7 @@ libsbiutils-objs-$(CONFIG_TIMER_MTIMER) += timer/aclint_mtimer.o libsbiutils-objs-$(CONFIG_TIMER_PLMT) += timer/andes_plmt.o +libsbiutils-objs-$(CONFIG_TIMER_SG2042_GMT) += timer/sg2042_gmt.o libsbiutils-objs-$(CONFIG_FDT_TIMER) += timer/fdt_timer.o libsbiutils-objs-$(CONFIG_FDT_TIMER) += timer/fdt_timer_drivers.o @@ -18,3 +19,6 @@ libsbiutils-objs-$(CONFIG_FDT_TIMER_MTIMER) += timer/fdt_timer_mtimer.o carray-fdt_timer_drivers-$(CONFIG_FDT_TIMER_PLMT) += fdt_timer_plmt libsbiutils-objs-$(CONFIG_FDT_TIMER_PLMT) += timer/fdt_timer_plmt.o + +carray-fdt_timer_drivers-$(CONFIG_FDT_TIMER_SG2042_GMT) += fdt_timer_sg2042_gmt +libsbiutils-objs-$(CONFIG_FDT_TIMER_SG2042_GMT) += timer/fdt_timer_sg2042_gmt.o diff --git a/lib/utils/timer/sg2042_gmt.c b/lib/utils/timer/sg2042_gmt.c new file mode 100644 index 00000000..b1371589 --- /dev/null +++ b/lib/utils/timer/sg2042_gmt.c @@ -0,0 +1,135 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2022 Andes Technology Corporation + * + * Authors: + * Yu Chien Peter Lin + */ + +#include +#include +#include +#include +#include +#include +#include + +struct sg2042gmt_data { + unsigned long mtimer_base; + unsigned long mtimecmp_base; + unsigned long mtimecmp_size; + unsigned long declared_freq; + unsigned long actual_freq; + unsigned long timecmp_freq; +} sg2042gmt; + +#define SG2042GMT_TIMECMP_CLUSTER_SIZE 0x10000 +#define SG2042GMT_TIMECMP_CLUSTER_CORE_COUNT 4 + +static void *timecmp_addr(unsigned int hart_id) +{ + unsigned int cluster_id = hart_id / SG2042GMT_TIMECMP_CLUSTER_CORE_COUNT; + unsigned int cluster_offset = hart_id % SG2042GMT_TIMECMP_CLUSTER_CORE_COUNT; + + return (void *)(sg2042gmt.mtimecmp_base + 0x4000 + + cluster_id * SG2042GMT_TIMECMP_CLUSTER_SIZE + + cluster_offset * 8); +} + +static u64 sg2042gmt_timer_value(void) +{ + u64 global_timer = readq_relaxed((void *)sg2042gmt.mtimer_base); + + /* convert global timer to declared frequency */ + return global_timer / (sg2042gmt.actual_freq / sg2042gmt.declared_freq); +} + +static void sg2042gmt_timer_event_stop(void) +{ + unsigned int hart_id = current_hartid(); + + writel_relaxed(-1U, timecmp_addr(hart_id)); + writel_relaxed(-1U, timecmp_addr(hart_id) + 4); +} + +static void sg2042gmt_timer_event_start(u64 next_event) +{ + unsigned int hart_id = current_hartid(); + + u64 global_timer = readq_relaxed((void *)sg2042gmt.mtimer_base); + u64 clint_timer = csr_read(CSR_TIME); + u64 delta_global_timer, delta_clint_timer; + + delta_global_timer = + (next_event * (sg2042gmt.actual_freq / sg2042gmt.declared_freq)) - global_timer; + + delta_clint_timer = delta_global_timer * (sg2042gmt.timecmp_freq / sg2042gmt.actual_freq); + + next_event = clint_timer + delta_clint_timer; + + writel_relaxed(next_event >> 32, timecmp_addr(hart_id) + 4); + writel_relaxed(next_event & 0xffffffff, timecmp_addr(hart_id)); +} + +static struct sbi_timer_device sg2042gmt_timer = { + .name = "sg2042gmt", + .timer_freq = 50000000, + .timer_value = sg2042gmt_timer_value, + .timer_event_start = sg2042gmt_timer_event_start, + .timer_event_stop = sg2042gmt_timer_event_stop +}; + +int sg2042gmt_cold_timer_init(unsigned long mtimer_base, + unsigned long mtimecmp_base, + unsigned long mtimecmp_size, + unsigned long declared_freq, + unsigned long actual_freq, + unsigned long timecmp_freq) +{ + int err; + + /* Add SG2042 GMT timer and time compare region to the root domain */ +#if 0 + err = sbi_domain_root_add_memrange( + mtimer_base, 4096, 4096, + SBI_DOMAIN_MEMREGION_MMIO | SBI_DOMAIN_MEMREGION_READABLE); + if (err) { + sbi_printf("add timer to root domain failed\n"); + return err; + } +#endif + + err = sbi_domain_root_add_memrange( + mtimecmp_base, mtimecmp_size, 4096, + SBI_DOMAIN_MEMREGION_MMIO | + SBI_DOMAIN_MEMREGION_READABLE | + SBI_DOMAIN_MEMREGION_WRITEABLE); + if (err) { + return err; + } + + + sg2042gmt_timer.timer_freq = declared_freq; + + sg2042gmt.mtimer_base = mtimer_base; + sg2042gmt.mtimecmp_base = mtimecmp_base; + sg2042gmt.mtimecmp_size = mtimecmp_size; + sg2042gmt.declared_freq = declared_freq; + sg2042gmt.actual_freq = actual_freq; + sg2042gmt.timecmp_freq = timecmp_freq; + + sbi_timer_set_device(&sg2042gmt_timer); + + return 0; +} + +int sg2042gmt_warm_timer_init(void) +{ + if (!sg2042gmt.mtimer_base) + return SBI_ENODEV; + + sg2042gmt_timer_event_stop(); + + return 0; +} diff --git a/platform/generic/configs/defconfig b/platform/generic/configs/defconfig index ed52c92d..500e7a71 100644 --- a/platform/generic/configs/defconfig +++ b/platform/generic/configs/defconfig @@ -42,4 +42,5 @@ CONFIG_FDT_SERIAL_XILINX_UARTLITE=y CONFIG_FDT_TIMER=y CONFIG_FDT_TIMER_MTIMER=y CONFIG_FDT_TIMER_PLMT=y +CONFIG_FDT_TIMER_SG2042_GMT=y CONFIG_SERIAL_SEMIHOSTING=y diff --git a/platform/generic/include/platform_override.h b/platform/generic/include/platform_override.h index 0d9e5ee6..8e76f4f3 100644 --- a/platform/generic/include/platform_override.h +++ b/platform/generic/include/platform_override.h @@ -19,6 +19,7 @@ struct platform_override { u64 (*features)(const struct fdt_match *match); u64 (*tlbr_flush_limit)(const struct fdt_match *match); bool (*cold_boot_allowed)(u32 hartid, const struct fdt_match *match); + bool (*force_emulate_time_csr)(const struct fdt_match *match); int (*early_init)(bool cold_boot, const struct fdt_match *match); int (*final_init)(bool cold_boot, const struct fdt_match *match); void (*early_exit)(const struct fdt_match *match); diff --git a/platform/generic/platform.c b/platform/generic/platform.c index eeefef4c..29565e04 100644 --- a/platform/generic/platform.c +++ b/platform/generic/platform.c @@ -134,6 +134,14 @@ static bool generic_cold_boot_allowed(u32 hartid) return true; } +static bool generic_force_emulate_time_csr(void) +{ + if (generic_plat && generic_plat->force_emulate_time_csr) + return generic_plat->force_emulate_time_csr(generic_plat_match); + + return false; +} + static int generic_nascent_init(void) { if (platform_has_mlevel_imsic) @@ -284,6 +292,7 @@ static int generic_console_init(void) const struct sbi_platform_operations platform_ops = { .cold_boot_allowed = generic_cold_boot_allowed, + .force_emulate_time_csr = generic_force_emulate_time_csr, .nascent_init = generic_nascent_init, .early_init = generic_early_init, .final_init = generic_final_init, diff --git a/platform/generic/sophgo/mango.c b/platform/generic/sophgo/mango.c index 34f62ab6..81a17dd1 100644 --- a/platform/generic/sophgo/mango.c +++ b/platform/generic/sophgo/mango.c @@ -20,12 +20,14 @@ #include #include #include +#include #define SOPHGO_MANGO_TIMER_BASE 0x70ac000000UL #define SOPHGO_MANGO_TIMER_OFFSET 0x10000UL extern struct sbi_platform platform; static u32 selected_hartid = -1; +static bool force_emulate_time_csr; static bool mango_cold_boot_allowed(u32 hartid, const struct fdt_match *match) @@ -69,6 +71,16 @@ static int mango_extensions_init(const struct fdt_match *match, static void mango_fw_init(void *fdt, const struct fdt_match *match) { platform.hart_stack_size = 16384; + + if (fdt_node_offset_by_compatible(fdt, 0, "sophgo,sg2042-global-mtimer") > 0) + force_emulate_time_csr = true; + else + force_emulate_time_csr = false; +} + +static bool mango_force_emulate_time_csr(const struct fdt_match *match) +{ + return force_emulate_time_csr; } static const struct fdt_match sophgo_mango_match[] = { @@ -78,8 +90,9 @@ static const struct fdt_match sophgo_mango_match[] = { const struct platform_override sophgo_mango = { .match_table = sophgo_mango_match, - .fw_init = mango_fw_init, - .cold_boot_allowed = mango_cold_boot_allowed, + .fw_init = mango_fw_init, + .cold_boot_allowed = mango_cold_boot_allowed, + .force_emulate_time_csr = mango_force_emulate_time_csr, .early_init = mango_early_init, .extensions_init = mango_extensions_init, };