/* SPDX-License-Identifier: BSD-3-Clause
 * Copyright(c) 2015 Cavium, Inc
 * Copyright(c) 2022 StarFive
 * Copyright(c) 2022 SiFive
 * Copyright(c) 2022 Semihalf
 */

#ifndef RTE_CYCLES_RISCV_H
#define RTE_CYCLES_RISCV_H

#include "generic/rte_cycles.h"

#ifdef __cplusplus
extern "C" {
#endif

#ifndef RTE_RISCV_RDTSC_USE_HPM
#define RTE_RISCV_RDTSC_USE_HPM 0
#endif

/** Read wall time counter */
static __rte_always_inline uint64_t
__rte_riscv_rdtime(void)
{
	uint64_t tsc;
	asm volatile("csrr %0, time" : "=r" (tsc) : : "memory");
	return tsc;
}

/** Read wall time counter ensuring no re-ordering */
static __rte_always_inline uint64_t
__rte_riscv_rdtime_precise(void)
{
	asm volatile("fence" : : : "memory");
	return __rte_riscv_rdtime();
}

/** Read hart cycle counter */
static __rte_always_inline uint64_t
__rte_riscv_rdcycle(void)
{
	uint64_t tsc;
	asm volatile("csrr %0, cycle" : "=r" (tsc) : : "memory");
	return tsc;
}

/** Read hart cycle counter ensuring no re-ordering */
static __rte_always_inline uint64_t
__rte_riscv_rdcycle_precise(void)
{
	asm volatile("fence" : : : "memory");
	return __rte_riscv_rdcycle();
}

/**
 * Read the time base register.
 *
 * @return
 *   The time base for this lcore.
 */
static __rte_always_inline uint64_t
rte_rdtsc(void)
{
	/**
	 * By default TIME userspace counter is used. It is stable and shared
	 * across cores. Although it's frequency may not be enough for all
	 * applications.
	 */
	if (!RTE_RISCV_RDTSC_USE_HPM)
		return __rte_riscv_rdtime();
	/**
	 * Alternatively HPM's CYCLE counter may be used. However this counter
	 * is not guaranteed by ISA to either be stable frequency or always
	 * enabled for userspace access (it may trap to kernel or firmware,
	 * though as of Linux kernel 5.13 it doesn't).
	 * It is also highly probable that values of this counter are not
	 * synchronized across cores. Therefore if it is to be used as a timer,
	 * it can only be used in the scope of a single core.
	 */
	return __rte_riscv_rdcycle();
}

static inline uint64_t
rte_rdtsc_precise(void)
{
	if (!RTE_RISCV_RDTSC_USE_HPM)
		return __rte_riscv_rdtime_precise();
	return __rte_riscv_rdcycle_precise();
}

static __rte_always_inline uint64_t
rte_get_tsc_cycles(void)
{
	return rte_rdtsc();
}

#ifdef __cplusplus
}
#endif

#endif /* RTE_CYCLES_RISCV_H */
