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:
@@ -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) */
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
21
include/sbi_utils/timer/sg2042_gmt.h
Normal file
21
include/sbi_utils/timer/sg2042_gmt.h
Normal 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__ */
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
61
lib/utils/timer/fdt_timer_sg2042_gmt.c
Normal file
61
lib/utils/timer/fdt_timer_sg2042_gmt.c
Normal 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,
|
||||
};
|
||||
@@ -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
|
||||
|
||||
135
lib/utils/timer/sg2042_gmt.c
Normal file
135
lib/utils/timer/sg2042_gmt.c
Normal 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;
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user