File Index
Files and directories
Sign in
FILE

timer.c

File preview with syntax highlighting.

linux-6.19.14/arch/loongarch/kvm/timer.c C
  1// SPDX-License-Identifier: GPL-2.0
  2/*
  3 * Copyright (C) 2020-2023 Loongson Technology Corporation Limited
  4 */
  5
  6#include <linux/kvm_host.h>
  7#include <asm/delay.h>
  8#include <asm/kvm_csr.h>
  9#include <asm/kvm_vcpu.h>
 10
 11/*
 12 * ktime_to_tick() - Scale ktime_t to timer tick value.
 13 */
 14static inline u64 ktime_to_tick(struct kvm_vcpu *vcpu, ktime_t now)
 15{
 16	u64 delta;
 17
 18	delta = ktime_to_ns(now);
 19	return div_u64(delta * vcpu->arch.timer_mhz, MNSEC_PER_SEC);
 20}
 21
 22static inline u64 tick_to_ns(struct kvm_vcpu *vcpu, u64 tick)
 23{
 24	return div_u64(tick * MNSEC_PER_SEC, vcpu->arch.timer_mhz);
 25}
 26
 27/* Low level hrtimer wake routine */
 28enum hrtimer_restart kvm_swtimer_wakeup(struct hrtimer *timer)
 29{
 30	struct kvm_vcpu *vcpu;
 31
 32	vcpu = container_of(timer, struct kvm_vcpu, arch.swtimer);
 33	kvm_queue_irq(vcpu, INT_TI);
 34	rcuwait_wake_up(&vcpu->wait);
 35
 36	return HRTIMER_NORESTART;
 37}
 38
 39/*
 40 * Initialise the timer to the specified frequency, zero it
 41 */
 42void kvm_init_timer(struct kvm_vcpu *vcpu, unsigned long timer_hz)
 43{
 44	vcpu->arch.timer_mhz = timer_hz >> 20;
 45
 46	/* Starting at 0 */
 47	kvm_write_sw_gcsr(vcpu->arch.csr, LOONGARCH_CSR_TVAL, 0);
 48}
 49
 50/*
 51 * Restore soft timer state from saved context.
 52 */
 53void kvm_restore_timer(struct kvm_vcpu *vcpu)
 54{
 55	unsigned long cfg, estat;
 56	unsigned long ticks, delta, period;
 57	ktime_t expire, now;
 58	struct loongarch_csrs *csr = vcpu->arch.csr;
 59
 60	/*
 61	 * Set guest stable timer cfg csr
 62	 * Disable timer before restore estat CSR register, avoid to
 63	 * get invalid timer interrupt for old timer cfg
 64	 */
 65	cfg = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG);
 66
 67	write_gcsr_timercfg(0);
 68	kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_ESTAT);
 69	kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TCFG);
 70	if (!(cfg & CSR_TCFG_EN)) {
 71		/* Guest timer is disabled, just restore timer registers */
 72		kvm_restore_hw_gcsr(csr, LOONGARCH_CSR_TVAL);
 73		return;
 74	}
 75
 76	/*
 77	 * Freeze the soft-timer and sync the guest stable timer with it.
 78	 */
 79	if (kvm_vcpu_is_blocking(vcpu))
 80		hrtimer_cancel(&vcpu->arch.swtimer);
 81
 82	/*
 83	 * From LoongArch Reference Manual Volume 1 Chapter 7.6.2
 84	 * If oneshot timer is fired, CSR TVAL will be -1, there are two
 85	 * conditions:
 86	 *  1) timer is fired during exiting to host
 87	 *  2) timer is fired and vm is doing timer irq, and then exiting to
 88	 *     host. Host should not inject timer irq to avoid spurious
 89	 *     timer interrupt again
 90	 */
 91	ticks = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TVAL);
 92	estat = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_ESTAT);
 93	if (!(cfg & CSR_TCFG_PERIOD) && (ticks > cfg)) {
 94		/*
 95		 * Writing 0 to LOONGARCH_CSR_TVAL will inject timer irq
 96		 * and set CSR TVAL with -1
 97		 */
 98		write_gcsr_timertick(0);
 99		__delay(2); /* Wait cycles until timer interrupt injected */
100
101		/*
102		 * Writing CSR_TINTCLR_TI to LOONGARCH_CSR_TINTCLR will clear
103		 * timer interrupt, and CSR TVAL keeps unchanged with -1, it
104		 * avoids spurious timer interrupt
105		 */
106		if (!(estat & CPU_TIMER))
107			gcsr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR);
108		return;
109	}
110
111	/*
112	 * Set remainder tick value if not expired
113	 */
114	delta = 0;
115	now = ktime_get();
116	expire = vcpu->arch.expire;
117	if (ktime_before(now, expire))
118		delta = ktime_to_tick(vcpu, ktime_sub(expire, now));
119	else if (cfg & CSR_TCFG_PERIOD) {
120		period = cfg & CSR_TCFG_VAL;
121		delta = ktime_to_tick(vcpu, ktime_sub(now, expire));
122		delta = period - (delta % period);
123
124		/*
125		 * Inject timer here though sw timer should inject timer
126		 * interrupt async already, since sw timer may be cancelled
127		 * during injecting intr async
128		 */
129		kvm_queue_irq(vcpu, INT_TI);
130	}
131
132	write_gcsr_timertick(delta);
133}
134
135/*
136 * Save guest timer state and switch to software emulation of guest
137 * timer. The hard timer must already be in use, so preemption should be
138 * disabled.
139 */
140static void _kvm_save_timer(struct kvm_vcpu *vcpu)
141{
142	unsigned long ticks, delta, cfg;
143	ktime_t expire;
144	struct loongarch_csrs *csr = vcpu->arch.csr;
145
146	cfg = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG);
147	ticks = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TVAL);
148
149	/*
150	 * From LoongArch Reference Manual Volume 1 Chapter 7.6.2
151	 * If period timer is fired, CSR TVAL will be reloaded from CSR TCFG
152	 * If oneshot timer is fired, CSR TVAL will be -1
153	 * Here judge one-shot timer fired by checking whether TVAL is larger
154	 * than TCFG
155	 */
156	if (ticks < cfg)
157		delta = tick_to_ns(vcpu, ticks);
158	else
159		delta = 0;
160
161	expire = ktime_add_ns(ktime_get(), delta);
162	vcpu->arch.expire = expire;
163	if (kvm_vcpu_is_blocking(vcpu)) {
164
165		/*
166		 * HRTIMER_MODE_PINNED_HARD is suggested since vcpu may run in
167		 * the same physical cpu in next time, and the timer should run
168		 * in hardirq context even in the PREEMPT_RT case.
169		 */
170		hrtimer_start(&vcpu->arch.swtimer, expire, HRTIMER_MODE_ABS_PINNED_HARD);
171	}
172}
173
174/*
175 * Save guest timer state and switch to soft guest timer if hard timer was in
176 * use.
177 */
178void kvm_save_timer(struct kvm_vcpu *vcpu)
179{
180	struct loongarch_csrs *csr = vcpu->arch.csr;
181
182	preempt_disable();
183
184	/* Save hard timer state */
185	kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TCFG);
186	kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TVAL);
187	if (kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG) & CSR_TCFG_EN)
188		_kvm_save_timer(vcpu);
189
190	/* Save timer-related state to vCPU context */
191	kvm_save_hw_gcsr(csr, LOONGARCH_CSR_ESTAT);
192	preempt_enable();
193}
~/ Read-only file preview.