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:
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user