// SPDX-License-Identifier: GPL-2.01/*2* Copyright (C) 2020-2023 Loongson Technology Corporation Limited3*/45#include <linux/kvm_host.h>6#include <asm/delay.h>7#include <asm/kvm_csr.h>8#include <asm/kvm_vcpu.h>910/*11* ktime_to_tick() - Scale ktime_t to timer tick value.12*/13static inline u64 ktime_to_tick(struct kvm_vcpu *vcpu, ktime_t now)14{15u64 delta;1617delta = ktime_to_ns(now);18return div_u64(delta * vcpu->arch.timer_mhz, MNSEC_PER_SEC);19}2021static inline u64 tick_to_ns(struct kvm_vcpu *vcpu, u64 tick)22{23return div_u64(tick * MNSEC_PER_SEC, vcpu->arch.timer_mhz);24}2526/* Low level hrtimer wake routine */27enum hrtimer_restart kvm_swtimer_wakeup(struct hrtimer *timer)28{29struct kvm_vcpu *vcpu;3031vcpu = container_of(timer, struct kvm_vcpu, arch.swtimer);32kvm_queue_irq(vcpu, INT_TI);33rcuwait_wake_up(&vcpu->wait);3435return HRTIMER_NORESTART;36}3738/*39* Initialise the timer to the specified frequency, zero it40*/41void kvm_init_timer(struct kvm_vcpu *vcpu, unsigned long timer_hz)42{43vcpu->arch.timer_mhz = timer_hz >> 20;4445/* Starting at 0 */46kvm_write_sw_gcsr(vcpu->arch.csr, LOONGARCH_CSR_TVAL, 0);47}4849/*50* Restore soft timer state from saved context.51*/52void kvm_restore_timer(struct kvm_vcpu *vcpu)53{54unsigned long cfg, estat;55unsigned long ticks, delta, period;56ktime_t expire, now;57struct loongarch_csrs *csr = vcpu->arch.csr;5859/*60* Set guest stable timer cfg csr61* Disable timer before restore estat CSR register, avoid to62* get invalid timer interrupt for old timer cfg63*/64cfg = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG);6566write_gcsr_timercfg(0);67kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_ESTAT);68kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TCFG);69if (!(cfg & CSR_TCFG_EN)) {70/* Guest timer is disabled, just restore timer registers */71kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TVAL);72return;73}7475/*76* Freeze the soft-timer and sync the guest stable timer with it.77*/78if (kvm_vcpu_is_blocking(vcpu))79hrtimer_cancel(&vcpu->arch.swtimer);8081/*82* From LoongArch Reference Manual Volume 1 Chapter 7.6.283* If oneshot timer is fired, CSR TVAL will be -1, there are two84* conditions:85* 1) timer is fired during exiting to host86* 2) timer is fired and vm is doing timer irq, and then exiting to87* host. Host should not inject timer irq to avoid spurious88* timer interrupt again89*/90ticks = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TVAL);91estat = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT);92if (!(cfg & CSR_TCFG_PERIOD) && (ticks > cfg)) {93/*94* Writing 0 to LOONGARCH_CSR_TVAL will inject timer irq95* and set CSR TVAL with -196*/97write_gcsr_timertick(0);98__delay(2); /* Wait cycles until timer interrupt injected */99100/*101* Writing CSR_TINTCLR_TI to LOONGARCH_CSR_TINTCLR will clear102* timer interrupt, and CSR TVAL keeps unchanged with -1, it103* avoids spurious timer interrupt104*/105if (!(estat & CPU_TIMER))106gcsr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);107return;108}109110/*111* Set remainder tick value if not expired112*/113delta = 0;114now = ktime_get();115expire = vcpu->arch.expire;116if (ktime_before(now, expire))117delta = ktime_to_tick(vcpu, ktime_sub(expire, now));118else if (cfg & CSR_TCFG_PERIOD) {119period = cfg & CSR_TCFG_VAL;120delta = ktime_to_tick(vcpu, ktime_sub(now, expire));121delta = period - (delta % period);122123/*124* Inject timer here though sw timer should inject timer125* interrupt async already, since sw timer may be cancelled126* during injecting intr async127*/128kvm_queue_irq(vcpu, INT_TI);129}130131write_gcsr_timertick(delta);132}133134/*135* Save guest timer state and switch to software emulation of guest136* timer. The hard timer must already be in use, so preemption should be137* disabled.138*/139static void _kvm_save_timer(struct kvm_vcpu *vcpu)140{141unsigned long ticks, delta, cfg;142ktime_t expire;143struct loongarch_csrs *csr = vcpu->arch.csr;144145cfg = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG);146ticks = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TVAL);147148/*149* From LoongArch Reference Manual Volume 1 Chapter 7.6.2150* If period timer is fired, CSR TVAL will be reloaded from CSR TCFG151* If oneshot timer is fired, CSR TVAL will be -1152* Here judge one-shot timer fired by checking whether TVAL is larger153* than TCFG154*/155if (ticks < cfg)156delta = tick_to_ns(vcpu, ticks);157else158delta = 0;159160expire = ktime_add_ns(ktime_get(), delta);161vcpu->arch.expire = expire;162if (kvm_vcpu_is_blocking(vcpu)) {163164/*165* HRTIMER_MODE_PINNED_HARD is suggested since vcpu may run in166* the same physical cpu in next time, and the timer should run167* in hardirq context even in the PREEMPT_RT case.168*/169hrtimer_start(&vcpu->arch.swtimer, expire, HRTIMER_MODE_ABS_PINNED_HARD);170}171}172173/*174* Save guest timer state and switch to soft guest timer if hard timer was in175* use.176*/177void kvm_save_timer(struct kvm_vcpu *vcpu)178{179struct loongarch_csrs *csr = vcpu->arch.csr;180181preempt_disable();182183/* Save hard timer state */184kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TCFG);185kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TVAL);186if (kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG) & CSR_TCFG_EN)187_kvm_save_timer(vcpu);188189/* Save timer-related state to vCPU context */190kvm_save_hw_gcsr(csr, LOONGARCH_CSR_ESTAT);191preempt_enable();192}193194195