1*5c23704eSSong Gao /* SPDX-License-Identifier: GPL-2.0-or-later */
2*5c23704eSSong Gao /*
3*5c23704eSSong Gao  * QEMU LoongArch constant timer support
4*5c23704eSSong Gao  *
5*5c23704eSSong Gao  * Copyright (c) 2021 Loongson Technology Corporation Limited
6*5c23704eSSong Gao  */
7*5c23704eSSong Gao 
8*5c23704eSSong Gao #include "qemu/osdep.h"
9*5c23704eSSong Gao #include "qemu/timer.h"
10*5c23704eSSong Gao #include "cpu.h"
11*5c23704eSSong Gao #include "internals.h"
12*5c23704eSSong Gao #include "cpu-csr.h"
13*5c23704eSSong Gao 
14*5c23704eSSong Gao #define TIMER_PERIOD                10 /* 10 ns period for 100 MHz frequency */
15*5c23704eSSong Gao #define CONSTANT_TIMER_TICK_MASK    0xfffffffffffcUL
16*5c23704eSSong Gao #define CONSTANT_TIMER_ENABLE       0x1UL
17*5c23704eSSong Gao 
cpu_loongarch_get_constant_timer_counter(LoongArchCPU * cpu)18*5c23704eSSong Gao uint64_t cpu_loongarch_get_constant_timer_counter(LoongArchCPU *cpu)
19*5c23704eSSong Gao {
20*5c23704eSSong Gao     return qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL) / TIMER_PERIOD;
21*5c23704eSSong Gao }
22*5c23704eSSong Gao 
cpu_loongarch_get_constant_timer_ticks(LoongArchCPU * cpu)23*5c23704eSSong Gao uint64_t cpu_loongarch_get_constant_timer_ticks(LoongArchCPU *cpu)
24*5c23704eSSong Gao {
25*5c23704eSSong Gao     uint64_t now, expire;
26*5c23704eSSong Gao 
27*5c23704eSSong Gao     now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
28*5c23704eSSong Gao     expire = timer_expire_time_ns(&cpu->timer);
29*5c23704eSSong Gao 
30*5c23704eSSong Gao     return (expire - now) / TIMER_PERIOD;
31*5c23704eSSong Gao }
32*5c23704eSSong Gao 
cpu_loongarch_store_constant_timer_config(LoongArchCPU * cpu,uint64_t value)33*5c23704eSSong Gao void cpu_loongarch_store_constant_timer_config(LoongArchCPU *cpu,
34*5c23704eSSong Gao                                                uint64_t value)
35*5c23704eSSong Gao {
36*5c23704eSSong Gao     CPULoongArchState *env = &cpu->env;
37*5c23704eSSong Gao     uint64_t now, next;
38*5c23704eSSong Gao 
39*5c23704eSSong Gao     env->CSR_TCFG = value;
40*5c23704eSSong Gao     if (value & CONSTANT_TIMER_ENABLE) {
41*5c23704eSSong Gao         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
42*5c23704eSSong Gao         next = now + (value & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD;
43*5c23704eSSong Gao         timer_mod(&cpu->timer, next);
44*5c23704eSSong Gao     } else {
45*5c23704eSSong Gao         timer_del(&cpu->timer);
46*5c23704eSSong Gao     }
47*5c23704eSSong Gao }
48*5c23704eSSong Gao 
loongarch_constant_timer_cb(void * opaque)49*5c23704eSSong Gao void loongarch_constant_timer_cb(void *opaque)
50*5c23704eSSong Gao {
51*5c23704eSSong Gao     LoongArchCPU *cpu  = opaque;
52*5c23704eSSong Gao     CPULoongArchState *env = &cpu->env;
53*5c23704eSSong Gao     uint64_t now, next;
54*5c23704eSSong Gao 
55*5c23704eSSong Gao     if (FIELD_EX64(env->CSR_TCFG, CSR_TCFG, PERIODIC)) {
56*5c23704eSSong Gao         now = qemu_clock_get_ns(QEMU_CLOCK_VIRTUAL);
57*5c23704eSSong Gao         next = now + (env->CSR_TCFG & CONSTANT_TIMER_TICK_MASK) * TIMER_PERIOD;
58*5c23704eSSong Gao         timer_mod(&cpu->timer, next);
59*5c23704eSSong Gao     } else {
60*5c23704eSSong Gao         env->CSR_TCFG = FIELD_DP64(env->CSR_TCFG, CSR_TCFG, EN, 0);
61*5c23704eSSong Gao     }
62*5c23704eSSong Gao 
63*5c23704eSSong Gao     loongarch_cpu_set_irq(opaque, IRQ_TIMER, 1);
64*5c23704eSSong Gao }
65