Commit | Line | Data |
---|---|---|
3320648e | 1 | // SPDX-License-Identifier: GPL-2.0-only |
b9dcd9e4 AP |
2 | /* |
3 | * SBI initialilization and all extension implementation. | |
4 | * | |
5 | * Copyright (c) 2020 Western Digital Corporation or its affiliates. | |
6 | */ | |
3320648e | 7 | |
12f4a665 | 8 | #include <linux/bits.h> |
3320648e | 9 | #include <linux/init.h> |
f43fabf4 | 10 | #include <linux/mm.h> |
3320648e | 11 | #include <linux/pm.h> |
b579dfe7 | 12 | #include <linux/reboot.h> |
3320648e | 13 | #include <asm/sbi.h> |
1ef46c23 | 14 | #include <asm/smp.h> |
9d4e8d5f | 15 | #include <asm/tlbflush.h> |
3320648e | 16 | |
b9dcd9e4 | 17 | /* default SBI version is 0.1 */ |
de31ea4a | 18 | unsigned long sbi_spec_version __ro_after_init = SBI_SPEC_VERSION_DEFAULT; |
b9dcd9e4 AP |
19 | EXPORT_SYMBOL(sbi_spec_version); |
20 | ||
de31ea4a | 21 | static void (*__sbi_set_timer)(uint64_t stime) __ro_after_init; |
832f15f4 | 22 | static void (*__sbi_send_ipi)(unsigned int cpu) __ro_after_init; |
26fb751c | 23 | static int (*__sbi_rfence)(int fid, const struct cpumask *cpu_mask, |
efca1398 | 24 | unsigned long start, unsigned long size, |
de31ea4a | 25 | unsigned long arg4, unsigned long arg5) __ro_after_init; |
efca1398 | 26 | |
efca1398 | 27 | #ifdef CONFIG_RISCV_SBI_V01 |
26fb751c AP |
28 | static unsigned long __sbi_v01_cpumask_to_hartmask(const struct cpumask *cpu_mask) |
29 | { | |
30 | unsigned long cpuid, hartid; | |
31 | unsigned long hmask = 0; | |
32 | ||
33 | /* | |
34 | * There is no maximum hartid concept in RISC-V and NR_CPUS must not be | |
35 | * associated with hartid. As SBI v0.1 is only kept for backward compatibility | |
36 | * and will be removed in the future, there is no point in supporting hartid | |
37 | * greater than BITS_PER_LONG (32 for RV32 and 64 for RV64). Ideally, SBI v0.2 | |
38 | * should be used for platforms with hartid greater than BITS_PER_LONG. | |
39 | */ | |
40 | for_each_cpu(cpuid, cpu_mask) { | |
41 | hartid = cpuid_to_hartid_map(cpuid); | |
42 | if (hartid >= BITS_PER_LONG) { | |
43 | pr_warn("Unable to send any request to hartid > BITS_PER_LONG for SBI v0.1\n"); | |
44 | break; | |
45 | } | |
12f4a665 | 46 | hmask |= BIT(hartid); |
26fb751c AP |
47 | } |
48 | ||
49 | return hmask; | |
50 | } | |
51 | ||
b9dcd9e4 AP |
52 | /** |
53 | * sbi_console_putchar() - Writes given character to the console device. | |
54 | * @ch: The data to be written to the console. | |
55 | * | |
56 | * Return: None | |
57 | */ | |
58 | void sbi_console_putchar(int ch) | |
59 | { | |
60 | sbi_ecall(SBI_EXT_0_1_CONSOLE_PUTCHAR, 0, ch, 0, 0, 0, 0, 0); | |
61 | } | |
62 | EXPORT_SYMBOL(sbi_console_putchar); | |
63 | ||
64 | /** | |
65 | * sbi_console_getchar() - Reads a byte from console device. | |
66 | * | |
67 | * Returns the value read from console. | |
68 | */ | |
69 | int sbi_console_getchar(void) | |
70 | { | |
71 | struct sbiret ret; | |
72 | ||
73 | ret = sbi_ecall(SBI_EXT_0_1_CONSOLE_GETCHAR, 0, 0, 0, 0, 0, 0, 0); | |
74 | ||
75 | return ret.error; | |
76 | } | |
77 | EXPORT_SYMBOL(sbi_console_getchar); | |
78 | ||
79 | /** | |
efca1398 AP |
80 | * sbi_shutdown() - Remove all the harts from executing supervisor code. |
81 | * | |
82 | * Return: None | |
83 | */ | |
84 | void sbi_shutdown(void) | |
85 | { | |
86 | sbi_ecall(SBI_EXT_0_1_SHUTDOWN, 0, 0, 0, 0, 0, 0, 0); | |
87 | } | |
72df61d9 | 88 | EXPORT_SYMBOL(sbi_shutdown); |
efca1398 | 89 | |
efca1398 | 90 | /** |
56a6c37f | 91 | * __sbi_set_timer_v01() - Program the timer for next timer event. |
b9dcd9e4 AP |
92 | * @stime_value: The value after which next timer event should fire. |
93 | * | |
94 | * Return: None | |
95 | */ | |
efca1398 | 96 | static void __sbi_set_timer_v01(uint64_t stime_value) |
b9dcd9e4 AP |
97 | { |
98 | #if __riscv_xlen == 32 | |
99 | sbi_ecall(SBI_EXT_0_1_SET_TIMER, 0, stime_value, | |
100 | stime_value >> 32, 0, 0, 0, 0); | |
101 | #else | |
102 | sbi_ecall(SBI_EXT_0_1_SET_TIMER, 0, stime_value, 0, 0, 0, 0, 0); | |
103 | #endif | |
104 | } | |
b9dcd9e4 | 105 | |
832f15f4 | 106 | static void __sbi_send_ipi_v01(unsigned int cpu) |
b9dcd9e4 | 107 | { |
832f15f4 AP |
108 | unsigned long hart_mask = |
109 | __sbi_v01_cpumask_to_hartmask(cpumask_of(cpu)); | |
26fb751c | 110 | sbi_ecall(SBI_EXT_0_1_SEND_IPI, 0, (unsigned long)(&hart_mask), |
efca1398 | 111 | 0, 0, 0, 0, 0); |
b9dcd9e4 | 112 | } |
efca1398 | 113 | |
26fb751c | 114 | static int __sbi_rfence_v01(int fid, const struct cpumask *cpu_mask, |
efca1398 AP |
115 | unsigned long start, unsigned long size, |
116 | unsigned long arg4, unsigned long arg5) | |
117 | { | |
118 | int result = 0; | |
26fb751c AP |
119 | unsigned long hart_mask; |
120 | ||
2b35d5b7 | 121 | if (!cpu_mask || cpumask_empty(cpu_mask)) |
26fb751c AP |
122 | cpu_mask = cpu_online_mask; |
123 | hart_mask = __sbi_v01_cpumask_to_hartmask(cpu_mask); | |
efca1398 AP |
124 | |
125 | /* v0.2 function IDs are equivalent to v0.1 extension IDs */ | |
126 | switch (fid) { | |
127 | case SBI_EXT_RFENCE_REMOTE_FENCE_I: | |
128 | sbi_ecall(SBI_EXT_0_1_REMOTE_FENCE_I, 0, | |
26fb751c | 129 | (unsigned long)&hart_mask, 0, 0, 0, 0, 0); |
efca1398 AP |
130 | break; |
131 | case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA: | |
132 | sbi_ecall(SBI_EXT_0_1_REMOTE_SFENCE_VMA, 0, | |
26fb751c | 133 | (unsigned long)&hart_mask, start, size, |
efca1398 AP |
134 | 0, 0, 0); |
135 | break; | |
136 | case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID: | |
137 | sbi_ecall(SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID, 0, | |
26fb751c | 138 | (unsigned long)&hart_mask, start, size, |
efca1398 AP |
139 | arg4, 0, 0); |
140 | break; | |
141 | default: | |
142 | pr_err("SBI call [%d]not supported in SBI v0.1\n", fid); | |
143 | result = -EINVAL; | |
144 | } | |
145 | ||
146 | return result; | |
147 | } | |
7d0ce3b2 KW |
148 | |
149 | static void sbi_set_power_off(void) | |
150 | { | |
151 | pm_power_off = sbi_shutdown; | |
152 | } | |
efca1398 AP |
153 | #else |
154 | static void __sbi_set_timer_v01(uint64_t stime_value) | |
155 | { | |
156 | pr_warn("Timer extension is not available in SBI v%lu.%lu\n", | |
157 | sbi_major_version(), sbi_minor_version()); | |
158 | } | |
159 | ||
832f15f4 | 160 | static void __sbi_send_ipi_v01(unsigned int cpu) |
efca1398 AP |
161 | { |
162 | pr_warn("IPI extension is not available in SBI v%lu.%lu\n", | |
163 | sbi_major_version(), sbi_minor_version()); | |
efca1398 AP |
164 | } |
165 | ||
26fb751c | 166 | static int __sbi_rfence_v01(int fid, const struct cpumask *cpu_mask, |
efca1398 AP |
167 | unsigned long start, unsigned long size, |
168 | unsigned long arg4, unsigned long arg5) | |
169 | { | |
170 | pr_warn("remote fence extension is not available in SBI v%lu.%lu\n", | |
171 | sbi_major_version(), sbi_minor_version()); | |
172 | ||
173 | return 0; | |
174 | } | |
7d0ce3b2 KW |
175 | |
176 | static void sbi_set_power_off(void) {} | |
efca1398 | 177 | #endif /* CONFIG_RISCV_SBI_V01 */ |
b9dcd9e4 | 178 | |
1ef46c23 AP |
179 | static void __sbi_set_timer_v02(uint64_t stime_value) |
180 | { | |
181 | #if __riscv_xlen == 32 | |
182 | sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value, | |
183 | stime_value >> 32, 0, 0, 0, 0); | |
184 | #else | |
185 | sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value, 0, | |
186 | 0, 0, 0, 0); | |
187 | #endif | |
188 | } | |
189 | ||
832f15f4 | 190 | static void __sbi_send_ipi_v02(unsigned int cpu) |
1ef46c23 | 191 | { |
1ef46c23 | 192 | int result; |
832f15f4 | 193 | struct sbiret ret = {0}; |
1ef46c23 | 194 | |
832f15f4 AP |
195 | ret = sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SEND_IPI, |
196 | 1UL, cpuid_to_hartid_map(cpu), 0, 0, 0, 0); | |
197 | if (ret.error) { | |
198 | result = sbi_err_map_linux_errno(ret.error); | |
199 | pr_err("%s: hbase = [%lu] failed (error [%d])\n", | |
200 | __func__, cpuid_to_hartid_map(cpu), result); | |
1ef46c23 | 201 | } |
1ef46c23 AP |
202 | } |
203 | ||
26fb751c | 204 | static int __sbi_rfence_v02_call(unsigned long fid, unsigned long hmask, |
1ef46c23 AP |
205 | unsigned long hbase, unsigned long start, |
206 | unsigned long size, unsigned long arg4, | |
207 | unsigned long arg5) | |
208 | { | |
209 | struct sbiret ret = {0}; | |
210 | int ext = SBI_EXT_RFENCE; | |
211 | int result = 0; | |
212 | ||
213 | switch (fid) { | |
214 | case SBI_EXT_RFENCE_REMOTE_FENCE_I: | |
26fb751c | 215 | ret = sbi_ecall(ext, fid, hmask, hbase, 0, 0, 0, 0); |
1ef46c23 AP |
216 | break; |
217 | case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA: | |
26fb751c | 218 | ret = sbi_ecall(ext, fid, hmask, hbase, start, |
1ef46c23 AP |
219 | size, 0, 0); |
220 | break; | |
221 | case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID: | |
26fb751c | 222 | ret = sbi_ecall(ext, fid, hmask, hbase, start, |
1ef46c23 AP |
223 | size, arg4, 0); |
224 | break; | |
225 | ||
226 | case SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA: | |
26fb751c | 227 | ret = sbi_ecall(ext, fid, hmask, hbase, start, |
1ef46c23 AP |
228 | size, 0, 0); |
229 | break; | |
230 | case SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID: | |
26fb751c | 231 | ret = sbi_ecall(ext, fid, hmask, hbase, start, |
1ef46c23 AP |
232 | size, arg4, 0); |
233 | break; | |
234 | case SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA: | |
26fb751c | 235 | ret = sbi_ecall(ext, fid, hmask, hbase, start, |
1ef46c23 AP |
236 | size, 0, 0); |
237 | break; | |
238 | case SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID: | |
26fb751c | 239 | ret = sbi_ecall(ext, fid, hmask, hbase, start, |
1ef46c23 AP |
240 | size, arg4, 0); |
241 | break; | |
242 | default: | |
243 | pr_err("unknown function ID [%lu] for SBI extension [%d]\n", | |
244 | fid, ext); | |
245 | result = -EINVAL; | |
246 | } | |
247 | ||
248 | if (ret.error) { | |
249 | result = sbi_err_map_linux_errno(ret.error); | |
250 | pr_err("%s: hbase = [%lu] hmask = [0x%lx] failed (error [%d])\n", | |
26fb751c | 251 | __func__, hbase, hmask, result); |
1ef46c23 AP |
252 | } |
253 | ||
254 | return result; | |
255 | } | |
256 | ||
26fb751c | 257 | static int __sbi_rfence_v02(int fid, const struct cpumask *cpu_mask, |
1ef46c23 AP |
258 | unsigned long start, unsigned long size, |
259 | unsigned long arg4, unsigned long arg5) | |
260 | { | |
5feef64f | 261 | unsigned long hartid, cpuid, hmask = 0, hbase = 0, htop = 0; |
1ef46c23 AP |
262 | int result; |
263 | ||
2b35d5b7 | 264 | if (!cpu_mask || cpumask_empty(cpu_mask)) |
26fb751c | 265 | cpu_mask = cpu_online_mask; |
1ef46c23 | 266 | |
26fb751c AP |
267 | for_each_cpu(cpuid, cpu_mask) { |
268 | hartid = cpuid_to_hartid_map(cpuid); | |
5feef64f GU |
269 | if (hmask) { |
270 | if (hartid + BITS_PER_LONG <= htop || | |
271 | hbase + BITS_PER_LONG <= hartid) { | |
272 | result = __sbi_rfence_v02_call(fid, hmask, | |
273 | hbase, start, size, arg4, arg5); | |
274 | if (result) | |
275 | return result; | |
276 | hmask = 0; | |
277 | } else if (hartid < hbase) { | |
278 | /* shift the mask to fit lower hartid */ | |
279 | hmask <<= hbase - hartid; | |
280 | hbase = hartid; | |
281 | } | |
1ef46c23 | 282 | } |
5feef64f | 283 | if (!hmask) { |
1ef46c23 | 284 | hbase = hartid; |
5feef64f GU |
285 | htop = hartid; |
286 | } else if (hartid > htop) { | |
287 | htop = hartid; | |
288 | } | |
12f4a665 | 289 | hmask |= BIT(hartid - hbase); |
1ef46c23 AP |
290 | } |
291 | ||
26fb751c AP |
292 | if (hmask) { |
293 | result = __sbi_rfence_v02_call(fid, hmask, hbase, | |
1ef46c23 AP |
294 | start, size, arg4, arg5); |
295 | if (result) | |
296 | return result; | |
297 | } | |
298 | ||
299 | return 0; | |
300 | } | |
301 | ||
c4a50db1 CL |
302 | static bool sbi_fwft_supported; |
303 | ||
6d6d0641 CL |
304 | struct fwft_set_req { |
305 | u32 feature; | |
306 | unsigned long value; | |
307 | unsigned long flags; | |
308 | atomic_t error; | |
309 | }; | |
310 | ||
311 | static void cpu_sbi_fwft_set(void *arg) | |
312 | { | |
313 | struct fwft_set_req *req = arg; | |
314 | int ret; | |
315 | ||
316 | ret = sbi_fwft_set(req->feature, req->value, req->flags); | |
317 | if (ret) | |
318 | atomic_set(&req->error, ret); | |
319 | } | |
320 | ||
321 | /** | |
322 | * sbi_fwft_set() - Set a feature on the local hart | |
323 | * @feature: The feature ID to be set | |
324 | * @value: The feature value to be set | |
325 | * @flags: FWFT feature set flags | |
326 | * | |
327 | * Return: 0 on success, appropriate linux error code otherwise. | |
328 | */ | |
329 | int sbi_fwft_set(u32 feature, unsigned long value, unsigned long flags) | |
330 | { | |
c4a50db1 CL |
331 | struct sbiret ret; |
332 | ||
333 | if (!sbi_fwft_supported) | |
334 | return -EOPNOTSUPP; | |
335 | ||
336 | ret = sbi_ecall(SBI_EXT_FWFT, SBI_EXT_FWFT_SET, | |
337 | feature, value, flags, 0, 0, 0); | |
338 | ||
339 | return sbi_err_map_linux_errno(ret.error); | |
6d6d0641 CL |
340 | } |
341 | ||
342 | /** | |
343 | * sbi_fwft_set_cpumask() - Set a feature for the specified cpumask | |
344 | * @mask: CPU mask of cpus that need the feature to be set | |
345 | * @feature: The feature ID to be set | |
346 | * @value: The feature value to be set | |
347 | * @flags: FWFT feature set flags | |
348 | * | |
349 | * Return: 0 on success, appropriate linux error code otherwise. | |
350 | */ | |
351 | int sbi_fwft_set_cpumask(const cpumask_t *mask, u32 feature, | |
352 | unsigned long value, unsigned long flags) | |
353 | { | |
354 | struct fwft_set_req req = { | |
355 | .feature = feature, | |
356 | .value = value, | |
357 | .flags = flags, | |
358 | .error = ATOMIC_INIT(0), | |
359 | }; | |
360 | ||
c4a50db1 CL |
361 | if (!sbi_fwft_supported) |
362 | return -EOPNOTSUPP; | |
363 | ||
6d6d0641 CL |
364 | if (feature & SBI_FWFT_GLOBAL_FEATURE_BIT) |
365 | return -EINVAL; | |
366 | ||
367 | on_each_cpu_mask(mask, cpu_sbi_fwft_set, &req, 1); | |
368 | ||
369 | return atomic_read(&req.error); | |
370 | } | |
371 | ||
b9dcd9e4 | 372 | /** |
efca1398 AP |
373 | * sbi_set_timer() - Program the timer for next timer event. |
374 | * @stime_value: The value after which next timer event should fire. | |
b9dcd9e4 | 375 | * |
4bb87563 | 376 | * Return: None. |
b9dcd9e4 | 377 | */ |
efca1398 | 378 | void sbi_set_timer(uint64_t stime_value) |
b9dcd9e4 | 379 | { |
efca1398 | 380 | __sbi_set_timer(stime_value); |
b9dcd9e4 AP |
381 | } |
382 | ||
383 | /** | |
384 | * sbi_send_ipi() - Send an IPI to any hart. | |
832f15f4 | 385 | * @cpu: Logical id of the target CPU. |
b9dcd9e4 | 386 | */ |
832f15f4 | 387 | void sbi_send_ipi(unsigned int cpu) |
b9dcd9e4 | 388 | { |
832f15f4 | 389 | __sbi_send_ipi(cpu); |
b9dcd9e4 AP |
390 | } |
391 | EXPORT_SYMBOL(sbi_send_ipi); | |
392 | ||
393 | /** | |
394 | * sbi_remote_fence_i() - Execute FENCE.I instruction on given remote harts. | |
26fb751c | 395 | * @cpu_mask: A cpu mask containing all the target harts. |
b9dcd9e4 | 396 | * |
4bb87563 | 397 | * Return: 0 on success, appropriate linux error code otherwise. |
b9dcd9e4 | 398 | */ |
26fb751c | 399 | int sbi_remote_fence_i(const struct cpumask *cpu_mask) |
b9dcd9e4 | 400 | { |
4bb87563 | 401 | return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_FENCE_I, |
26fb751c | 402 | cpu_mask, 0, 0, 0, 0); |
b9dcd9e4 AP |
403 | } |
404 | EXPORT_SYMBOL(sbi_remote_fence_i); | |
405 | ||
b9dcd9e4 AP |
406 | /** |
407 | * sbi_remote_sfence_vma_asid() - Execute SFENCE.VMA instructions on given | |
9d4e8d5f | 408 | * remote harts for a virtual address range belonging to a specific ASID or not. |
b9dcd9e4 | 409 | * |
26fb751c | 410 | * @cpu_mask: A cpu mask containing all the target harts. |
b9dcd9e4 AP |
411 | * @start: Start of the virtual address |
412 | * @size: Total size of the virtual address range. | |
9d4e8d5f AG |
413 | * @asid: The value of address space identifier (ASID), or FLUSH_TLB_NO_ASID |
414 | * for flushing all address spaces. | |
b9dcd9e4 | 415 | * |
4bb87563 | 416 | * Return: 0 on success, appropriate linux error code otherwise. |
b9dcd9e4 | 417 | */ |
26fb751c | 418 | int sbi_remote_sfence_vma_asid(const struct cpumask *cpu_mask, |
b9dcd9e4 AP |
419 | unsigned long start, |
420 | unsigned long size, | |
421 | unsigned long asid) | |
422 | { | |
9d4e8d5f AG |
423 | if (asid == FLUSH_TLB_NO_ASID) |
424 | return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA, | |
425 | cpu_mask, start, size, 0, 0); | |
426 | else | |
427 | return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID, | |
428 | cpu_mask, start, size, asid, 0); | |
b9dcd9e4 AP |
429 | } |
430 | EXPORT_SYMBOL(sbi_remote_sfence_vma_asid); | |
431 | ||
1ef46c23 AP |
432 | /** |
433 | * sbi_remote_hfence_gvma() - Execute HFENCE.GVMA instructions on given remote | |
434 | * harts for the specified guest physical address range. | |
26fb751c | 435 | * @cpu_mask: A cpu mask containing all the target harts. |
1ef46c23 AP |
436 | * @start: Start of the guest physical address |
437 | * @size: Total size of the guest physical address range. | |
438 | * | |
439 | * Return: None | |
440 | */ | |
26fb751c | 441 | int sbi_remote_hfence_gvma(const struct cpumask *cpu_mask, |
1ef46c23 AP |
442 | unsigned long start, |
443 | unsigned long size) | |
444 | { | |
445 | return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA, | |
26fb751c | 446 | cpu_mask, start, size, 0, 0); |
1ef46c23 AP |
447 | } |
448 | EXPORT_SYMBOL_GPL(sbi_remote_hfence_gvma); | |
449 | ||
450 | /** | |
451 | * sbi_remote_hfence_gvma_vmid() - Execute HFENCE.GVMA instructions on given | |
452 | * remote harts for a guest physical address range belonging to a specific VMID. | |
453 | * | |
26fb751c | 454 | * @cpu_mask: A cpu mask containing all the target harts. |
1ef46c23 AP |
455 | * @start: Start of the guest physical address |
456 | * @size: Total size of the guest physical address range. | |
457 | * @vmid: The value of guest ID (VMID). | |
458 | * | |
459 | * Return: 0 if success, Error otherwise. | |
460 | */ | |
26fb751c | 461 | int sbi_remote_hfence_gvma_vmid(const struct cpumask *cpu_mask, |
1ef46c23 AP |
462 | unsigned long start, |
463 | unsigned long size, | |
464 | unsigned long vmid) | |
465 | { | |
466 | return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID, | |
26fb751c | 467 | cpu_mask, start, size, vmid, 0); |
1ef46c23 AP |
468 | } |
469 | EXPORT_SYMBOL(sbi_remote_hfence_gvma_vmid); | |
470 | ||
471 | /** | |
472 | * sbi_remote_hfence_vvma() - Execute HFENCE.VVMA instructions on given remote | |
473 | * harts for the current guest virtual address range. | |
26fb751c | 474 | * @cpu_mask: A cpu mask containing all the target harts. |
1ef46c23 AP |
475 | * @start: Start of the current guest virtual address |
476 | * @size: Total size of the current guest virtual address range. | |
477 | * | |
478 | * Return: None | |
479 | */ | |
26fb751c | 480 | int sbi_remote_hfence_vvma(const struct cpumask *cpu_mask, |
1ef46c23 AP |
481 | unsigned long start, |
482 | unsigned long size) | |
483 | { | |
484 | return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA, | |
26fb751c | 485 | cpu_mask, start, size, 0, 0); |
1ef46c23 AP |
486 | } |
487 | EXPORT_SYMBOL(sbi_remote_hfence_vvma); | |
488 | ||
489 | /** | |
490 | * sbi_remote_hfence_vvma_asid() - Execute HFENCE.VVMA instructions on given | |
491 | * remote harts for current guest virtual address range belonging to a specific | |
492 | * ASID. | |
493 | * | |
26fb751c | 494 | * @cpu_mask: A cpu mask containing all the target harts. |
1ef46c23 AP |
495 | * @start: Start of the current guest virtual address |
496 | * @size: Total size of the current guest virtual address range. | |
497 | * @asid: The value of address space identifier (ASID). | |
498 | * | |
499 | * Return: None | |
500 | */ | |
26fb751c | 501 | int sbi_remote_hfence_vvma_asid(const struct cpumask *cpu_mask, |
1ef46c23 AP |
502 | unsigned long start, |
503 | unsigned long size, | |
504 | unsigned long asid) | |
505 | { | |
506 | return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID, | |
26fb751c | 507 | cpu_mask, start, size, asid, 0); |
1ef46c23 AP |
508 | } |
509 | EXPORT_SYMBOL(sbi_remote_hfence_vvma_asid); | |
510 | ||
b579dfe7 AP |
511 | static void sbi_srst_reset(unsigned long type, unsigned long reason) |
512 | { | |
513 | sbi_ecall(SBI_EXT_SRST, SBI_EXT_SRST_RESET, type, reason, | |
514 | 0, 0, 0, 0); | |
515 | pr_warn("%s: type=0x%lx reason=0x%lx failed\n", | |
516 | __func__, type, reason); | |
517 | } | |
518 | ||
519 | static int sbi_srst_reboot(struct notifier_block *this, | |
520 | unsigned long mode, void *cmd) | |
521 | { | |
522 | sbi_srst_reset((mode == REBOOT_WARM || mode == REBOOT_SOFT) ? | |
523 | SBI_SRST_RESET_TYPE_WARM_REBOOT : | |
524 | SBI_SRST_RESET_TYPE_COLD_REBOOT, | |
525 | SBI_SRST_RESET_REASON_NONE); | |
526 | return NOTIFY_DONE; | |
527 | } | |
528 | ||
529 | static struct notifier_block sbi_srst_reboot_nb; | |
530 | ||
531 | static void sbi_srst_power_off(void) | |
532 | { | |
533 | sbi_srst_reset(SBI_SRST_RESET_TYPE_SHUTDOWN, | |
534 | SBI_SRST_RESET_REASON_NONE); | |
535 | } | |
536 | ||
b9dcd9e4 AP |
537 | /** |
538 | * sbi_probe_extension() - Check if an SBI extension ID is supported or not. | |
539 | * @extid: The extension ID to be probed. | |
540 | * | |
41cad828 | 541 | * Return: 1 or an extension specific nonzero value if yes, 0 otherwise. |
b9dcd9e4 | 542 | */ |
41cad828 | 543 | long sbi_probe_extension(int extid) |
b9dcd9e4 AP |
544 | { |
545 | struct sbiret ret; | |
546 | ||
547 | ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_PROBE_EXT, extid, | |
548 | 0, 0, 0, 0, 0); | |
549 | if (!ret.error) | |
41cad828 | 550 | return ret.value; |
b9dcd9e4 | 551 | |
41cad828 | 552 | return 0; |
b9dcd9e4 AP |
553 | } |
554 | EXPORT_SYMBOL(sbi_probe_extension); | |
555 | ||
b9dcd9e4 AP |
556 | static inline long sbi_get_spec_version(void) |
557 | { | |
558 | return __sbi_base_ecall(SBI_EXT_BASE_GET_SPEC_VERSION); | |
559 | } | |
560 | ||
561 | static inline long sbi_get_firmware_id(void) | |
562 | { | |
563 | return __sbi_base_ecall(SBI_EXT_BASE_GET_IMP_ID); | |
564 | } | |
565 | ||
566 | static inline long sbi_get_firmware_version(void) | |
567 | { | |
568 | return __sbi_base_ecall(SBI_EXT_BASE_GET_IMP_VERSION); | |
569 | } | |
570 | ||
183787c6 VC |
571 | long sbi_get_mvendorid(void) |
572 | { | |
573 | return __sbi_base_ecall(SBI_EXT_BASE_GET_MVENDORID); | |
574 | } | |
a1a44e22 | 575 | EXPORT_SYMBOL_GPL(sbi_get_mvendorid); |
183787c6 VC |
576 | |
577 | long sbi_get_marchid(void) | |
578 | { | |
579 | return __sbi_base_ecall(SBI_EXT_BASE_GET_MARCHID); | |
580 | } | |
a1a44e22 | 581 | EXPORT_SYMBOL_GPL(sbi_get_marchid); |
183787c6 VC |
582 | |
583 | long sbi_get_mimpid(void) | |
584 | { | |
585 | return __sbi_base_ecall(SBI_EXT_BASE_GET_MIMPID); | |
586 | } | |
a1a44e22 | 587 | EXPORT_SYMBOL_GPL(sbi_get_mimpid); |
183787c6 | 588 | |
f43fabf4 AP |
589 | bool sbi_debug_console_available; |
590 | ||
591 | int sbi_debug_console_write(const char *bytes, unsigned int num_bytes) | |
592 | { | |
593 | phys_addr_t base_addr; | |
594 | struct sbiret ret; | |
595 | ||
596 | if (!sbi_debug_console_available) | |
597 | return -EOPNOTSUPP; | |
598 | ||
599 | if (is_vmalloc_addr(bytes)) | |
600 | base_addr = page_to_phys(vmalloc_to_page(bytes)) + | |
601 | offset_in_page(bytes); | |
602 | else | |
603 | base_addr = __pa(bytes); | |
604 | if (PAGE_SIZE < (offset_in_page(bytes) + num_bytes)) | |
605 | num_bytes = PAGE_SIZE - offset_in_page(bytes); | |
606 | ||
607 | if (IS_ENABLED(CONFIG_32BIT)) | |
608 | ret = sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_WRITE, | |
609 | num_bytes, lower_32_bits(base_addr), | |
610 | upper_32_bits(base_addr), 0, 0, 0); | |
611 | else | |
612 | ret = sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_WRITE, | |
613 | num_bytes, base_addr, 0, 0, 0, 0); | |
614 | ||
615 | if (ret.error == SBI_ERR_FAILURE) | |
616 | return -EIO; | |
617 | return ret.error ? sbi_err_map_linux_errno(ret.error) : ret.value; | |
618 | } | |
619 | ||
620 | int sbi_debug_console_read(char *bytes, unsigned int num_bytes) | |
621 | { | |
622 | phys_addr_t base_addr; | |
623 | struct sbiret ret; | |
624 | ||
625 | if (!sbi_debug_console_available) | |
626 | return -EOPNOTSUPP; | |
627 | ||
628 | if (is_vmalloc_addr(bytes)) | |
629 | base_addr = page_to_phys(vmalloc_to_page(bytes)) + | |
630 | offset_in_page(bytes); | |
631 | else | |
632 | base_addr = __pa(bytes); | |
633 | if (PAGE_SIZE < (offset_in_page(bytes) + num_bytes)) | |
634 | num_bytes = PAGE_SIZE - offset_in_page(bytes); | |
635 | ||
636 | if (IS_ENABLED(CONFIG_32BIT)) | |
637 | ret = sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_READ, | |
638 | num_bytes, lower_32_bits(base_addr), | |
639 | upper_32_bits(base_addr), 0, 0, 0); | |
640 | else | |
641 | ret = sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_READ, | |
642 | num_bytes, base_addr, 0, 0, 0, 0); | |
643 | ||
644 | if (ret.error == SBI_ERR_FAILURE) | |
645 | return -EIO; | |
646 | return ret.error ? sbi_err_map_linux_errno(ret.error) : ret.value; | |
647 | } | |
648 | ||
641e8cd2 | 649 | void __init sbi_init(void) |
3320648e | 650 | { |
b9dcd9e4 AP |
651 | int ret; |
652 | ||
7d0ce3b2 | 653 | sbi_set_power_off(); |
b9dcd9e4 AP |
654 | ret = sbi_get_spec_version(); |
655 | if (ret > 0) | |
656 | sbi_spec_version = ret; | |
657 | ||
658 | pr_info("SBI specification v%lu.%lu detected\n", | |
659 | sbi_major_version(), sbi_minor_version()); | |
efca1398 AP |
660 | |
661 | if (!sbi_spec_is_0_1()) { | |
b9dcd9e4 AP |
662 | pr_info("SBI implementation ID=0x%lx Version=0x%lx\n", |
663 | sbi_get_firmware_id(), sbi_get_firmware_version()); | |
41cad828 | 664 | if (sbi_probe_extension(SBI_EXT_TIME)) { |
1ef46c23 | 665 | __sbi_set_timer = __sbi_set_timer_v02; |
f35bb4b8 | 666 | pr_info("SBI TIME extension detected\n"); |
1ef46c23 AP |
667 | } else { |
668 | __sbi_set_timer = __sbi_set_timer_v01; | |
669 | } | |
41cad828 | 670 | if (sbi_probe_extension(SBI_EXT_IPI)) { |
1ef46c23 | 671 | __sbi_send_ipi = __sbi_send_ipi_v02; |
f35bb4b8 | 672 | pr_info("SBI IPI extension detected\n"); |
1ef46c23 AP |
673 | } else { |
674 | __sbi_send_ipi = __sbi_send_ipi_v01; | |
675 | } | |
41cad828 | 676 | if (sbi_probe_extension(SBI_EXT_RFENCE)) { |
1ef46c23 | 677 | __sbi_rfence = __sbi_rfence_v02; |
f35bb4b8 | 678 | pr_info("SBI RFENCE extension detected\n"); |
1ef46c23 AP |
679 | } else { |
680 | __sbi_rfence = __sbi_rfence_v01; | |
681 | } | |
a7cd450f | 682 | if (sbi_spec_version >= sbi_mk_version(0, 3) && |
41cad828 | 683 | sbi_probe_extension(SBI_EXT_SRST)) { |
b579dfe7 AP |
684 | pr_info("SBI SRST extension detected\n"); |
685 | pm_power_off = sbi_srst_power_off; | |
686 | sbi_srst_reboot_nb.notifier_call = sbi_srst_reboot; | |
687 | sbi_srst_reboot_nb.priority = 192; | |
688 | register_restart_handler(&sbi_srst_reboot_nb); | |
689 | } | |
a7cd450f CL |
690 | if (sbi_spec_version >= sbi_mk_version(2, 0) && |
691 | sbi_probe_extension(SBI_EXT_DBCN) > 0) { | |
f43fabf4 AP |
692 | pr_info("SBI DBCN extension detected\n"); |
693 | sbi_debug_console_available = true; | |
694 | } | |
c4a50db1 CL |
695 | if (sbi_spec_version >= sbi_mk_version(3, 0) && |
696 | sbi_probe_extension(SBI_EXT_FWFT)) { | |
697 | pr_info("SBI FWFT extension detected\n"); | |
698 | sbi_fwft_supported = true; | |
699 | } | |
1ef46c23 AP |
700 | } else { |
701 | __sbi_set_timer = __sbi_set_timer_v01; | |
702 | __sbi_send_ipi = __sbi_send_ipi_v01; | |
703 | __sbi_rfence = __sbi_rfence_v01; | |
efca1398 | 704 | } |
3320648e | 705 | } |