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 | ||
b9dcd9e4 | 302 | /** |
efca1398 AP |
303 | * sbi_set_timer() - Program the timer for next timer event. |
304 | * @stime_value: The value after which next timer event should fire. | |
b9dcd9e4 | 305 | * |
4bb87563 | 306 | * Return: None. |
b9dcd9e4 | 307 | */ |
efca1398 | 308 | void sbi_set_timer(uint64_t stime_value) |
b9dcd9e4 | 309 | { |
efca1398 | 310 | __sbi_set_timer(stime_value); |
b9dcd9e4 AP |
311 | } |
312 | ||
313 | /** | |
314 | * sbi_send_ipi() - Send an IPI to any hart. | |
832f15f4 | 315 | * @cpu: Logical id of the target CPU. |
b9dcd9e4 | 316 | */ |
832f15f4 | 317 | void sbi_send_ipi(unsigned int cpu) |
b9dcd9e4 | 318 | { |
832f15f4 | 319 | __sbi_send_ipi(cpu); |
b9dcd9e4 AP |
320 | } |
321 | EXPORT_SYMBOL(sbi_send_ipi); | |
322 | ||
323 | /** | |
324 | * sbi_remote_fence_i() - Execute FENCE.I instruction on given remote harts. | |
26fb751c | 325 | * @cpu_mask: A cpu mask containing all the target harts. |
b9dcd9e4 | 326 | * |
4bb87563 | 327 | * Return: 0 on success, appropriate linux error code otherwise. |
b9dcd9e4 | 328 | */ |
26fb751c | 329 | int sbi_remote_fence_i(const struct cpumask *cpu_mask) |
b9dcd9e4 | 330 | { |
4bb87563 | 331 | return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_FENCE_I, |
26fb751c | 332 | cpu_mask, 0, 0, 0, 0); |
b9dcd9e4 AP |
333 | } |
334 | EXPORT_SYMBOL(sbi_remote_fence_i); | |
335 | ||
b9dcd9e4 AP |
336 | /** |
337 | * sbi_remote_sfence_vma_asid() - Execute SFENCE.VMA instructions on given | |
9d4e8d5f | 338 | * remote harts for a virtual address range belonging to a specific ASID or not. |
b9dcd9e4 | 339 | * |
26fb751c | 340 | * @cpu_mask: A cpu mask containing all the target harts. |
b9dcd9e4 AP |
341 | * @start: Start of the virtual address |
342 | * @size: Total size of the virtual address range. | |
9d4e8d5f AG |
343 | * @asid: The value of address space identifier (ASID), or FLUSH_TLB_NO_ASID |
344 | * for flushing all address spaces. | |
b9dcd9e4 | 345 | * |
4bb87563 | 346 | * Return: 0 on success, appropriate linux error code otherwise. |
b9dcd9e4 | 347 | */ |
26fb751c | 348 | int sbi_remote_sfence_vma_asid(const struct cpumask *cpu_mask, |
b9dcd9e4 AP |
349 | unsigned long start, |
350 | unsigned long size, | |
351 | unsigned long asid) | |
352 | { | |
9d4e8d5f AG |
353 | if (asid == FLUSH_TLB_NO_ASID) |
354 | return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA, | |
355 | cpu_mask, start, size, 0, 0); | |
356 | else | |
357 | return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID, | |
358 | cpu_mask, start, size, asid, 0); | |
b9dcd9e4 AP |
359 | } |
360 | EXPORT_SYMBOL(sbi_remote_sfence_vma_asid); | |
361 | ||
1ef46c23 AP |
362 | /** |
363 | * sbi_remote_hfence_gvma() - Execute HFENCE.GVMA instructions on given remote | |
364 | * harts for the specified guest physical address range. | |
26fb751c | 365 | * @cpu_mask: A cpu mask containing all the target harts. |
1ef46c23 AP |
366 | * @start: Start of the guest physical address |
367 | * @size: Total size of the guest physical address range. | |
368 | * | |
369 | * Return: None | |
370 | */ | |
26fb751c | 371 | int sbi_remote_hfence_gvma(const struct cpumask *cpu_mask, |
1ef46c23 AP |
372 | unsigned long start, |
373 | unsigned long size) | |
374 | { | |
375 | return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA, | |
26fb751c | 376 | cpu_mask, start, size, 0, 0); |
1ef46c23 AP |
377 | } |
378 | EXPORT_SYMBOL_GPL(sbi_remote_hfence_gvma); | |
379 | ||
380 | /** | |
381 | * sbi_remote_hfence_gvma_vmid() - Execute HFENCE.GVMA instructions on given | |
382 | * remote harts for a guest physical address range belonging to a specific VMID. | |
383 | * | |
26fb751c | 384 | * @cpu_mask: A cpu mask containing all the target harts. |
1ef46c23 AP |
385 | * @start: Start of the guest physical address |
386 | * @size: Total size of the guest physical address range. | |
387 | * @vmid: The value of guest ID (VMID). | |
388 | * | |
389 | * Return: 0 if success, Error otherwise. | |
390 | */ | |
26fb751c | 391 | int sbi_remote_hfence_gvma_vmid(const struct cpumask *cpu_mask, |
1ef46c23 AP |
392 | unsigned long start, |
393 | unsigned long size, | |
394 | unsigned long vmid) | |
395 | { | |
396 | return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID, | |
26fb751c | 397 | cpu_mask, start, size, vmid, 0); |
1ef46c23 AP |
398 | } |
399 | EXPORT_SYMBOL(sbi_remote_hfence_gvma_vmid); | |
400 | ||
401 | /** | |
402 | * sbi_remote_hfence_vvma() - Execute HFENCE.VVMA instructions on given remote | |
403 | * harts for the current guest virtual address range. | |
26fb751c | 404 | * @cpu_mask: A cpu mask containing all the target harts. |
1ef46c23 AP |
405 | * @start: Start of the current guest virtual address |
406 | * @size: Total size of the current guest virtual address range. | |
407 | * | |
408 | * Return: None | |
409 | */ | |
26fb751c | 410 | int sbi_remote_hfence_vvma(const struct cpumask *cpu_mask, |
1ef46c23 AP |
411 | unsigned long start, |
412 | unsigned long size) | |
413 | { | |
414 | return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA, | |
26fb751c | 415 | cpu_mask, start, size, 0, 0); |
1ef46c23 AP |
416 | } |
417 | EXPORT_SYMBOL(sbi_remote_hfence_vvma); | |
418 | ||
419 | /** | |
420 | * sbi_remote_hfence_vvma_asid() - Execute HFENCE.VVMA instructions on given | |
421 | * remote harts for current guest virtual address range belonging to a specific | |
422 | * ASID. | |
423 | * | |
26fb751c | 424 | * @cpu_mask: A cpu mask containing all the target harts. |
1ef46c23 AP |
425 | * @start: Start of the current guest virtual address |
426 | * @size: Total size of the current guest virtual address range. | |
427 | * @asid: The value of address space identifier (ASID). | |
428 | * | |
429 | * Return: None | |
430 | */ | |
26fb751c | 431 | int sbi_remote_hfence_vvma_asid(const struct cpumask *cpu_mask, |
1ef46c23 AP |
432 | unsigned long start, |
433 | unsigned long size, | |
434 | unsigned long asid) | |
435 | { | |
436 | return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID, | |
26fb751c | 437 | cpu_mask, start, size, asid, 0); |
1ef46c23 AP |
438 | } |
439 | EXPORT_SYMBOL(sbi_remote_hfence_vvma_asid); | |
440 | ||
b579dfe7 AP |
441 | static void sbi_srst_reset(unsigned long type, unsigned long reason) |
442 | { | |
443 | sbi_ecall(SBI_EXT_SRST, SBI_EXT_SRST_RESET, type, reason, | |
444 | 0, 0, 0, 0); | |
445 | pr_warn("%s: type=0x%lx reason=0x%lx failed\n", | |
446 | __func__, type, reason); | |
447 | } | |
448 | ||
449 | static int sbi_srst_reboot(struct notifier_block *this, | |
450 | unsigned long mode, void *cmd) | |
451 | { | |
452 | sbi_srst_reset((mode == REBOOT_WARM || mode == REBOOT_SOFT) ? | |
453 | SBI_SRST_RESET_TYPE_WARM_REBOOT : | |
454 | SBI_SRST_RESET_TYPE_COLD_REBOOT, | |
455 | SBI_SRST_RESET_REASON_NONE); | |
456 | return NOTIFY_DONE; | |
457 | } | |
458 | ||
459 | static struct notifier_block sbi_srst_reboot_nb; | |
460 | ||
461 | static void sbi_srst_power_off(void) | |
462 | { | |
463 | sbi_srst_reset(SBI_SRST_RESET_TYPE_SHUTDOWN, | |
464 | SBI_SRST_RESET_REASON_NONE); | |
465 | } | |
466 | ||
b9dcd9e4 AP |
467 | /** |
468 | * sbi_probe_extension() - Check if an SBI extension ID is supported or not. | |
469 | * @extid: The extension ID to be probed. | |
470 | * | |
41cad828 | 471 | * Return: 1 or an extension specific nonzero value if yes, 0 otherwise. |
b9dcd9e4 | 472 | */ |
41cad828 | 473 | long sbi_probe_extension(int extid) |
b9dcd9e4 AP |
474 | { |
475 | struct sbiret ret; | |
476 | ||
477 | ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_PROBE_EXT, extid, | |
478 | 0, 0, 0, 0, 0); | |
479 | if (!ret.error) | |
41cad828 | 480 | return ret.value; |
b9dcd9e4 | 481 | |
41cad828 | 482 | return 0; |
b9dcd9e4 AP |
483 | } |
484 | EXPORT_SYMBOL(sbi_probe_extension); | |
485 | ||
b9dcd9e4 AP |
486 | static inline long sbi_get_spec_version(void) |
487 | { | |
488 | return __sbi_base_ecall(SBI_EXT_BASE_GET_SPEC_VERSION); | |
489 | } | |
490 | ||
491 | static inline long sbi_get_firmware_id(void) | |
492 | { | |
493 | return __sbi_base_ecall(SBI_EXT_BASE_GET_IMP_ID); | |
494 | } | |
495 | ||
496 | static inline long sbi_get_firmware_version(void) | |
497 | { | |
498 | return __sbi_base_ecall(SBI_EXT_BASE_GET_IMP_VERSION); | |
499 | } | |
500 | ||
183787c6 VC |
501 | long sbi_get_mvendorid(void) |
502 | { | |
503 | return __sbi_base_ecall(SBI_EXT_BASE_GET_MVENDORID); | |
504 | } | |
a1a44e22 | 505 | EXPORT_SYMBOL_GPL(sbi_get_mvendorid); |
183787c6 VC |
506 | |
507 | long sbi_get_marchid(void) | |
508 | { | |
509 | return __sbi_base_ecall(SBI_EXT_BASE_GET_MARCHID); | |
510 | } | |
a1a44e22 | 511 | EXPORT_SYMBOL_GPL(sbi_get_marchid); |
183787c6 VC |
512 | |
513 | long sbi_get_mimpid(void) | |
514 | { | |
515 | return __sbi_base_ecall(SBI_EXT_BASE_GET_MIMPID); | |
516 | } | |
a1a44e22 | 517 | EXPORT_SYMBOL_GPL(sbi_get_mimpid); |
183787c6 | 518 | |
f43fabf4 AP |
519 | bool sbi_debug_console_available; |
520 | ||
521 | int sbi_debug_console_write(const char *bytes, unsigned int num_bytes) | |
522 | { | |
523 | phys_addr_t base_addr; | |
524 | struct sbiret ret; | |
525 | ||
526 | if (!sbi_debug_console_available) | |
527 | return -EOPNOTSUPP; | |
528 | ||
529 | if (is_vmalloc_addr(bytes)) | |
530 | base_addr = page_to_phys(vmalloc_to_page(bytes)) + | |
531 | offset_in_page(bytes); | |
532 | else | |
533 | base_addr = __pa(bytes); | |
534 | if (PAGE_SIZE < (offset_in_page(bytes) + num_bytes)) | |
535 | num_bytes = PAGE_SIZE - offset_in_page(bytes); | |
536 | ||
537 | if (IS_ENABLED(CONFIG_32BIT)) | |
538 | ret = sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_WRITE, | |
539 | num_bytes, lower_32_bits(base_addr), | |
540 | upper_32_bits(base_addr), 0, 0, 0); | |
541 | else | |
542 | ret = sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_WRITE, | |
543 | num_bytes, base_addr, 0, 0, 0, 0); | |
544 | ||
545 | if (ret.error == SBI_ERR_FAILURE) | |
546 | return -EIO; | |
547 | return ret.error ? sbi_err_map_linux_errno(ret.error) : ret.value; | |
548 | } | |
549 | ||
550 | int sbi_debug_console_read(char *bytes, unsigned int num_bytes) | |
551 | { | |
552 | phys_addr_t base_addr; | |
553 | struct sbiret ret; | |
554 | ||
555 | if (!sbi_debug_console_available) | |
556 | return -EOPNOTSUPP; | |
557 | ||
558 | if (is_vmalloc_addr(bytes)) | |
559 | base_addr = page_to_phys(vmalloc_to_page(bytes)) + | |
560 | offset_in_page(bytes); | |
561 | else | |
562 | base_addr = __pa(bytes); | |
563 | if (PAGE_SIZE < (offset_in_page(bytes) + num_bytes)) | |
564 | num_bytes = PAGE_SIZE - offset_in_page(bytes); | |
565 | ||
566 | if (IS_ENABLED(CONFIG_32BIT)) | |
567 | ret = sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_READ, | |
568 | num_bytes, lower_32_bits(base_addr), | |
569 | upper_32_bits(base_addr), 0, 0, 0); | |
570 | else | |
571 | ret = sbi_ecall(SBI_EXT_DBCN, SBI_EXT_DBCN_CONSOLE_READ, | |
572 | num_bytes, base_addr, 0, 0, 0, 0); | |
573 | ||
574 | if (ret.error == SBI_ERR_FAILURE) | |
575 | return -EIO; | |
576 | return ret.error ? sbi_err_map_linux_errno(ret.error) : ret.value; | |
577 | } | |
578 | ||
641e8cd2 | 579 | void __init sbi_init(void) |
3320648e | 580 | { |
b9dcd9e4 AP |
581 | int ret; |
582 | ||
7d0ce3b2 | 583 | sbi_set_power_off(); |
b9dcd9e4 AP |
584 | ret = sbi_get_spec_version(); |
585 | if (ret > 0) | |
586 | sbi_spec_version = ret; | |
587 | ||
588 | pr_info("SBI specification v%lu.%lu detected\n", | |
589 | sbi_major_version(), sbi_minor_version()); | |
efca1398 AP |
590 | |
591 | if (!sbi_spec_is_0_1()) { | |
b9dcd9e4 AP |
592 | pr_info("SBI implementation ID=0x%lx Version=0x%lx\n", |
593 | sbi_get_firmware_id(), sbi_get_firmware_version()); | |
41cad828 | 594 | if (sbi_probe_extension(SBI_EXT_TIME)) { |
1ef46c23 | 595 | __sbi_set_timer = __sbi_set_timer_v02; |
f35bb4b8 | 596 | pr_info("SBI TIME extension detected\n"); |
1ef46c23 AP |
597 | } else { |
598 | __sbi_set_timer = __sbi_set_timer_v01; | |
599 | } | |
41cad828 | 600 | if (sbi_probe_extension(SBI_EXT_IPI)) { |
1ef46c23 | 601 | __sbi_send_ipi = __sbi_send_ipi_v02; |
f35bb4b8 | 602 | pr_info("SBI IPI extension detected\n"); |
1ef46c23 AP |
603 | } else { |
604 | __sbi_send_ipi = __sbi_send_ipi_v01; | |
605 | } | |
41cad828 | 606 | if (sbi_probe_extension(SBI_EXT_RFENCE)) { |
1ef46c23 | 607 | __sbi_rfence = __sbi_rfence_v02; |
f35bb4b8 | 608 | pr_info("SBI RFENCE extension detected\n"); |
1ef46c23 AP |
609 | } else { |
610 | __sbi_rfence = __sbi_rfence_v01; | |
611 | } | |
b579dfe7 | 612 | if ((sbi_spec_version >= sbi_mk_version(0, 3)) && |
41cad828 | 613 | sbi_probe_extension(SBI_EXT_SRST)) { |
b579dfe7 AP |
614 | pr_info("SBI SRST extension detected\n"); |
615 | pm_power_off = sbi_srst_power_off; | |
616 | sbi_srst_reboot_nb.notifier_call = sbi_srst_reboot; | |
617 | sbi_srst_reboot_nb.priority = 192; | |
618 | register_restart_handler(&sbi_srst_reboot_nb); | |
619 | } | |
f43fabf4 AP |
620 | if ((sbi_spec_version >= sbi_mk_version(2, 0)) && |
621 | (sbi_probe_extension(SBI_EXT_DBCN) > 0)) { | |
622 | pr_info("SBI DBCN extension detected\n"); | |
623 | sbi_debug_console_available = true; | |
624 | } | |
1ef46c23 AP |
625 | } else { |
626 | __sbi_set_timer = __sbi_set_timer_v01; | |
627 | __sbi_send_ipi = __sbi_send_ipi_v01; | |
628 | __sbi_rfence = __sbi_rfence_v01; | |
efca1398 | 629 | } |
3320648e | 630 | } |