diff options
Diffstat (limited to 'roms/opensbi/lib/sbi/sbi_timer.c')
-rw-r--r-- | roms/opensbi/lib/sbi/sbi_timer.c | 136 |
1 files changed, 136 insertions, 0 deletions
diff --git a/roms/opensbi/lib/sbi/sbi_timer.c b/roms/opensbi/lib/sbi/sbi_timer.c new file mode 100644 index 000000000..b571b1740 --- /dev/null +++ b/roms/opensbi/lib/sbi/sbi_timer.c @@ -0,0 +1,136 @@ +/* + * SPDX-License-Identifier: BSD-2-Clause + * + * Copyright (c) 2019 Western Digital Corporation or its affiliates. + * + * Authors: + * Anup Patel <anup.patel@wdc.com> + */ + +#include <sbi/riscv_asm.h> +#include <sbi/riscv_encoding.h> +#include <sbi/sbi_error.h> +#include <sbi/sbi_hart.h> +#include <sbi/sbi_platform.h> +#include <sbi/sbi_scratch.h> +#include <sbi/sbi_timer.h> + +static unsigned long time_delta_off; +static u64 (*get_time_val)(const struct sbi_platform *plat); + +#if __riscv_xlen == 32 +static u64 get_ticks(const struct sbi_platform *plat) +{ + u32 lo, hi, tmp; + __asm__ __volatile__("1:\n" + "rdtimeh %0\n" + "rdtime %1\n" + "rdtimeh %2\n" + "bne %0, %2, 1b" + : "=&r"(hi), "=&r"(lo), "=&r"(tmp)); + return ((u64)hi << 32) | lo; +} +#else +static u64 get_ticks(const struct sbi_platform *plat) +{ + unsigned long n; + + __asm__ __volatile__("rdtime %0" : "=r"(n)); + return n; +} +#endif + +u64 sbi_timer_value(void) +{ + return get_time_val(sbi_platform_thishart_ptr()); +} + +u64 sbi_timer_virt_value(void) +{ + u64 *time_delta = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(), + time_delta_off); + + return sbi_timer_value() + *time_delta; +} + +u64 sbi_timer_get_delta(void) +{ + u64 *time_delta = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(), + time_delta_off); + + return *time_delta; +} + +void sbi_timer_set_delta(ulong delta) +{ + u64 *time_delta = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(), + time_delta_off); + + *time_delta = (u64)delta; +} + +void sbi_timer_set_delta_upper(ulong delta_upper) +{ + u64 *time_delta = sbi_scratch_offset_ptr(sbi_scratch_thishart_ptr(), + time_delta_off); + + *time_delta &= 0xffffffffULL; + *time_delta |= ((u64)delta_upper << 32); +} + +void sbi_timer_event_start(u64 next_event) +{ + sbi_platform_timer_event_start(sbi_platform_thishart_ptr(), next_event); + csr_clear(CSR_MIP, MIP_STIP); + csr_set(CSR_MIE, MIP_MTIP); +} + +void sbi_timer_process(void) +{ + csr_clear(CSR_MIE, MIP_MTIP); + csr_set(CSR_MIP, MIP_STIP); +} + +int sbi_timer_init(struct sbi_scratch *scratch, bool cold_boot) +{ + u64 *time_delta; + const struct sbi_platform *plat = sbi_platform_ptr(scratch); + int ret; + + if (cold_boot) { + time_delta_off = sbi_scratch_alloc_offset(sizeof(*time_delta), + "TIME_DELTA"); + if (!time_delta_off) + return SBI_ENOMEM; + } else { + if (!time_delta_off) + return SBI_ENOMEM; + } + + time_delta = sbi_scratch_offset_ptr(scratch, time_delta_off); + *time_delta = 0; + + ret = sbi_platform_timer_init(plat, cold_boot); + if (ret) + return ret; + + if (sbi_hart_has_feature(scratch, SBI_HART_HAS_TIME)) + get_time_val = get_ticks; + else if (sbi_platform_has_timer_value(plat)) + get_time_val = sbi_platform_timer_value; + else + /* There is no method to provide timer value */ + return SBI_ENODEV; + + return 0; +} + +void sbi_timer_exit(struct sbi_scratch *scratch) +{ + sbi_platform_timer_event_stop(sbi_platform_ptr(scratch)); + + csr_clear(CSR_MIP, MIP_STIP); + csr_clear(CSR_MIE, MIP_MTIP); + + sbi_platform_timer_exit(sbi_platform_ptr(scratch)); +} |