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 <chao.wei@sophgo.com>
This commit is contained in:
Chao Wei
2025-10-13 17:17:43 +08:00
committed by xingxg2022
parent 506f47f9fa
commit 6eef399cfc
13 changed files with 293 additions and 6 deletions

View File

@@ -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) */

View File

@@ -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
*

View File

@@ -0,0 +1,21 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2025 SOPHGO Corporation
*
* Authors:
* Chao Wei <chao.wei@sophgo.com>
*/
#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__ */

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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

View File

@@ -0,0 +1,61 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Andes Technology Corporation
*
* Authors:
* Yu Chien Peter Lin <peterlin@andestech.com>
*/
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/timer/fdt_timer.h>
#include <sbi_utils/timer/sg2042_gmt.h>
#include <sbi/sbi_error.h>
#include <libfdt.h>
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,
};

View File

@@ -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

View File

@@ -0,0 +1,135 @@
/*
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2022 Andes Technology Corporation
*
* Authors:
* Yu Chien Peter Lin <peterlin@andestech.com>
*/
#include <sbi/riscv_asm.h>
#include <sbi/riscv_io.h>
#include <sbi/sbi_domain.h>
#include <sbi/sbi_error.h>
#include <sbi/sbi_timer.h>
#include <sbi/sbi_console.h>
#include <sbi_utils/timer/sg2042_gmt.h>
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;
}

View File

@@ -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

View File

@@ -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);

View File

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

View File

@@ -20,12 +20,14 @@
#include <sbi/sbi_platform.h>
#include <sbi_utils/fdt/fdt_helper.h>
#include <sbi_utils/timer/aclint_mtimer.h>
#include <libfdt.h>
#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,
};