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