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/kvm_csr.h> |
8 | #include <asm/kvm_vcpu.h> |
9 | |
10 | /* |
11 | * ktime_to_tick() - Scale ktime_t to timer tick value. |
12 | */ |
13 | static inline u64 ktime_to_tick(struct kvm_vcpu *vcpu, ktime_t now) |
14 | { |
15 | u64 delta; |
16 | |
17 | delta = ktime_to_ns(kt: now); |
18 | return div_u64(dividend: delta * vcpu->arch.timer_mhz, divisor: MNSEC_PER_SEC); |
19 | } |
20 | |
21 | static inline u64 tick_to_ns(struct kvm_vcpu *vcpu, u64 tick) |
22 | { |
23 | return div_u64(dividend: tick * MNSEC_PER_SEC, divisor: vcpu->arch.timer_mhz); |
24 | } |
25 | |
26 | /* Low level hrtimer wake routine */ |
27 | enum hrtimer_restart kvm_swtimer_wakeup(struct hrtimer *timer) |
28 | { |
29 | struct kvm_vcpu *vcpu; |
30 | |
31 | vcpu = container_of(timer, struct kvm_vcpu, arch.swtimer); |
32 | kvm_queue_irq(vcpu, INT_TI); |
33 | rcuwait_wake_up(w: &vcpu->wait); |
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(timer: &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 | gcsr_write(CSR_TINTCLR_TI, LOONGARCH_CSR_TINTCLR); |
106 | return; |
107 | } |
108 | |
109 | /* |
110 | * Set remainder tick value if not expired |
111 | */ |
112 | delta = 0; |
113 | now = ktime_get(); |
114 | expire = vcpu->arch.expire; |
115 | if (ktime_before(cmp1: now, cmp2: expire)) |
116 | delta = ktime_to_tick(vcpu, ktime_sub(expire, now)); |
117 | else if (cfg & CSR_TCFG_PERIOD) { |
118 | period = cfg & CSR_TCFG_VAL; |
119 | delta = ktime_to_tick(vcpu, ktime_sub(now, expire)); |
120 | delta = period - (delta % period); |
121 | |
122 | /* |
123 | * Inject timer here though sw timer should inject timer |
124 | * interrupt async already, since sw timer may be cancelled |
125 | * during injecting intr async |
126 | */ |
127 | kvm_queue_irq(vcpu, INT_TI); |
128 | } |
129 | |
130 | write_gcsr_timertick(delta); |
131 | } |
132 | |
133 | /* |
134 | * Save guest timer state and switch to software emulation of guest |
135 | * timer. The hard timer must already be in use, so preemption should be |
136 | * disabled. |
137 | */ |
138 | static void _kvm_save_timer(struct kvm_vcpu *vcpu) |
139 | { |
140 | unsigned long ticks, delta, cfg; |
141 | ktime_t expire; |
142 | struct loongarch_csrs *csr = vcpu->arch.csr; |
143 | |
144 | cfg = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG); |
145 | ticks = kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TVAL); |
146 | |
147 | /* |
148 | * From LoongArch Reference Manual Volume 1 Chapter 7.6.2 |
149 | * If period timer is fired, CSR TVAL will be reloaded from CSR TCFG |
150 | * If oneshot timer is fired, CSR TVAL will be -1 |
151 | * Here judge one-shot timer fired by checking whether TVAL is larger |
152 | * than TCFG |
153 | */ |
154 | if (ticks < cfg) |
155 | delta = tick_to_ns(vcpu, tick: ticks); |
156 | else |
157 | delta = 0; |
158 | |
159 | expire = ktime_add_ns(ktime_get(), delta); |
160 | vcpu->arch.expire = expire; |
161 | if (kvm_vcpu_is_blocking(vcpu)) { |
162 | |
163 | /* |
164 | * HRTIMER_MODE_PINNED is suggested since vcpu may run in |
165 | * the same physical cpu in next time |
166 | */ |
167 | hrtimer_start(timer: &vcpu->arch.swtimer, tim: expire, mode: HRTIMER_MODE_ABS_PINNED); |
168 | } |
169 | } |
170 | |
171 | /* |
172 | * Save guest timer state and switch to soft guest timer if hard timer was in |
173 | * use. |
174 | */ |
175 | void kvm_save_timer(struct kvm_vcpu *vcpu) |
176 | { |
177 | struct loongarch_csrs *csr = vcpu->arch.csr; |
178 | |
179 | preempt_disable(); |
180 | |
181 | /* Save hard timer state */ |
182 | kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TCFG); |
183 | kvm_save_hw_gcsr(csr, LOONGARCH_CSR_TVAL); |
184 | if (kvm_read_sw_gcsr(csr, LOONGARCH_CSR_TCFG) & CSR_TCFG_EN) |
185 | _kvm_save_timer(vcpu); |
186 | |
187 | /* Save timer-related state to vCPU context */ |
188 | kvm_save_hw_gcsr(csr, LOONGARCH_CSR_ESTAT); |
189 | preempt_enable(); |
190 | } |
191 | |
192 | void kvm_reset_timer(struct kvm_vcpu *vcpu) |
193 | { |
194 | write_gcsr_timercfg(0); |
195 | kvm_write_sw_gcsr(vcpu->arch.csr, LOONGARCH_CSR_TCFG, 0); |
196 | hrtimer_cancel(timer: &vcpu->arch.swtimer); |
197 | } |
198 | |