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