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