From 6eef399cfc3c2d11f18a3a229ccc9500431f098f Mon Sep 17 00:00:00 2001 From: Chao Wei Date: Mon, 13 Oct 2025 17:17:43 +0800 Subject: [PATCH] SG2042: add virtual global timer device driver Add a virtual global timer device driver. It uses a global timer register as timer counter, and it uses mtimecmp CSR of clint as interrupt source. Using this virtul timer resolves dual way timer sync issue. On dual way system, we should enable this driver and on single way system, we should use standard risc-v clint timer. 1. Add a virtual global timer device driver, it is probed by fdt sg2042-global-mtimer@70300101c0 { compatible = "sophgo,sg2042-global-mtimer"; reg = < 0x00000070 0x300101c0 0x00000000 0x00000008 0x00000070 0xac000000 0x00000000 0x00200000>; clock-frequency = <50000000 50000000>; }; Its compatible should be "sophgo,sg2042-global-mtimer". And the first region delcared in reg prop is the address of global timer. The second region delcared in reg prop is the base of clint timers. The first clock in clock-frequency is the actual frequency of global timer, the second clock is the actual frequency of clint timer. 2. Add a quirk in platform specific code. Add a function sbi_platform_force_emulate_time_csr, it indicates if opensbi should skip time CSR detection and make software in supervisor and user mode use emulated time CSR. Signed-off-by: Chao Wei --- include/sbi/riscv_encoding.h | 2 + include/sbi/sbi_platform.h | 17 +++ include/sbi_utils/timer/sg2042_gmt.h | 21 +++ lib/sbi/sbi_hart.c | 17 ++- lib/sbi/sbi_pmu.c | 5 + lib/utils/timer/Kconfig | 9 ++ lib/utils/timer/fdt_timer_sg2042_gmt.c | 61 +++++++++ lib/utils/timer/objects.mk | 4 + lib/utils/timer/sg2042_gmt.c | 135 +++++++++++++++++++ platform/generic/configs/defconfig | 1 + platform/generic/include/platform_override.h | 1 + platform/generic/platform.c | 9 ++ platform/generic/sophgo/mango.c | 17 ++- 13 files changed, 293 insertions(+), 6 deletions(-) create mode 100644 include/sbi_utils/timer/sg2042_gmt.h create mode 100644 lib/utils/timer/fdt_timer_sg2042_gmt.c create mode 100644 lib/utils/timer/sg2042_gmt.c 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, };