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 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 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 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 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