/* SPDX-License-Identifier: GPL-2.0 */ /* * LoongArch Constant Timer specific interface */ #ifndef SELFTEST_KVM_ARCH_TIMER_H #define SELFTEST_KVM_ARCH_TIMER_H #include "processor.h" /* LoongArch timer frequency is constant 100MHZ */ #define TIMER_FREQ (100UL << 20) #define msec_to_cycles(msec) (TIMER_FREQ * (unsigned long)(msec) / 1000) #define usec_to_cycles(usec) (TIMER_FREQ * (unsigned long)(usec) / 1000000) #define cycles_to_usec(cycles) ((unsigned long)(cycles) * 1000000 / TIMER_FREQ) static inline unsigned long timer_get_cycles(void) { unsigned long val = 0; __asm__ __volatile__( "rdtime.d %0, $zero\n\t" : "=r"(val) : ); return val; } static inline unsigned long timer_get_cfg(void) { return csr_read(LOONGARCH_CSR_TCFG); } static inline unsigned long timer_get_val(void) { return csr_read(LOONGARCH_CSR_TVAL); } static inline void disable_timer(void) { csr_write(0, LOONGARCH_CSR_TCFG); } static inline void timer_irq_enable(void) { unsigned long val; val = csr_read(LOONGARCH_CSR_ECFG); val |= ECFGF_TIMER; csr_write(val, LOONGARCH_CSR_ECFG); } static inline void timer_irq_disable(void) { unsigned long val; val = csr_read(LOONGARCH_CSR_ECFG); val &= ~ECFGF_TIMER; csr_write(val, LOONGARCH_CSR_ECFG); } static inline void timer_set_next_cmp_ms(unsigned int msec, bool period) { unsigned long val; val = msec_to_cycles(msec) & CSR_TCFG_VAL; val |= CSR_TCFG_EN; if (period) val |= CSR_TCFG_PERIOD; csr_write(val, LOONGARCH_CSR_TCFG); } static inline void __delay(uint64_t cycles) { uint64_t start = timer_get_cycles(); while ((timer_get_cycles() - start) < cycles) cpu_relax(); } static inline void udelay(unsigned long usec) { __delay(usec_to_cycles(usec)); } #endif /* SELFTEST_KVM_ARCH_TIMER_H */