xref: /openbmc/linux/arch/riscv/kernel/sbi.c (revision 695c312ec5a68e4373d063ee649c7b925ffb5da7)
13320648eSChristoph Hellwig // SPDX-License-Identifier: GPL-2.0-only
2b9dcd9e4SAtish Patra /*
3b9dcd9e4SAtish Patra  * SBI initialilization and all extension implementation.
4b9dcd9e4SAtish Patra  *
5b9dcd9e4SAtish Patra  * Copyright (c) 2020 Western Digital Corporation or its affiliates.
6b9dcd9e4SAtish Patra  */
73320648eSChristoph Hellwig 
812f4a665SGeert Uytterhoeven #include <linux/bits.h>
93320648eSChristoph Hellwig #include <linux/init.h>
103320648eSChristoph Hellwig #include <linux/pm.h>
11b579dfe7SAnup Patel #include <linux/reboot.h>
123320648eSChristoph Hellwig #include <asm/sbi.h>
131ef46c23SAtish Patra #include <asm/smp.h>
14*53a38f8fSAlexandre Ghiti #include <asm/tlbflush.h>
153320648eSChristoph Hellwig 
16b9dcd9e4SAtish Patra /* default SBI version is 0.1 */
17de31ea4aSJisheng Zhang unsigned long sbi_spec_version __ro_after_init = SBI_SPEC_VERSION_DEFAULT;
18b9dcd9e4SAtish Patra EXPORT_SYMBOL(sbi_spec_version);
19b9dcd9e4SAtish Patra 
20de31ea4aSJisheng Zhang static void (*__sbi_set_timer)(uint64_t stime) __ro_after_init;
21832f15f4SAnup Patel static void (*__sbi_send_ipi)(unsigned int cpu) __ro_after_init;
2226fb751cSAtish Patra static int (*__sbi_rfence)(int fid, const struct cpumask *cpu_mask,
23efca1398SAtish Patra 			   unsigned long start, unsigned long size,
24de31ea4aSJisheng Zhang 			   unsigned long arg4, unsigned long arg5) __ro_after_init;
25efca1398SAtish Patra 
sbi_ecall(int ext,int fid,unsigned long arg0,unsigned long arg1,unsigned long arg2,unsigned long arg3,unsigned long arg4,unsigned long arg5)26b9dcd9e4SAtish Patra struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0,
27b9dcd9e4SAtish Patra 			unsigned long arg1, unsigned long arg2,
28b9dcd9e4SAtish Patra 			unsigned long arg3, unsigned long arg4,
29b9dcd9e4SAtish Patra 			unsigned long arg5)
30b9dcd9e4SAtish Patra {
31b9dcd9e4SAtish Patra 	struct sbiret ret;
32b9dcd9e4SAtish Patra 
33b9dcd9e4SAtish Patra 	register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0);
34b9dcd9e4SAtish Patra 	register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1);
35b9dcd9e4SAtish Patra 	register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2);
36b9dcd9e4SAtish Patra 	register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3);
37b9dcd9e4SAtish Patra 	register uintptr_t a4 asm ("a4") = (uintptr_t)(arg4);
38b9dcd9e4SAtish Patra 	register uintptr_t a5 asm ("a5") = (uintptr_t)(arg5);
39b9dcd9e4SAtish Patra 	register uintptr_t a6 asm ("a6") = (uintptr_t)(fid);
40b9dcd9e4SAtish Patra 	register uintptr_t a7 asm ("a7") = (uintptr_t)(ext);
41b9dcd9e4SAtish Patra 	asm volatile ("ecall"
42b9dcd9e4SAtish Patra 		      : "+r" (a0), "+r" (a1)
43b9dcd9e4SAtish Patra 		      : "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7)
44b9dcd9e4SAtish Patra 		      : "memory");
45b9dcd9e4SAtish Patra 	ret.error = a0;
46b9dcd9e4SAtish Patra 	ret.value = a1;
47b9dcd9e4SAtish Patra 
48b9dcd9e4SAtish Patra 	return ret;
49b9dcd9e4SAtish Patra }
50b9dcd9e4SAtish Patra EXPORT_SYMBOL(sbi_ecall);
51b9dcd9e4SAtish Patra 
sbi_err_map_linux_errno(int err)52f90b43ceSAtish Patra int sbi_err_map_linux_errno(int err)
53b9dcd9e4SAtish Patra {
54b9dcd9e4SAtish Patra 	switch (err) {
55b9dcd9e4SAtish Patra 	case SBI_SUCCESS:
56b9dcd9e4SAtish Patra 		return 0;
57b9dcd9e4SAtish Patra 	case SBI_ERR_DENIED:
58b9dcd9e4SAtish Patra 		return -EPERM;
59b9dcd9e4SAtish Patra 	case SBI_ERR_INVALID_PARAM:
60b9dcd9e4SAtish Patra 		return -EINVAL;
61b9dcd9e4SAtish Patra 	case SBI_ERR_INVALID_ADDRESS:
62b9dcd9e4SAtish Patra 		return -EFAULT;
63b9dcd9e4SAtish Patra 	case SBI_ERR_NOT_SUPPORTED:
64b9dcd9e4SAtish Patra 	case SBI_ERR_FAILURE:
65b9dcd9e4SAtish Patra 	default:
66b9dcd9e4SAtish Patra 		return -ENOTSUPP;
67b9dcd9e4SAtish Patra 	};
68b9dcd9e4SAtish Patra }
69f90b43ceSAtish Patra EXPORT_SYMBOL(sbi_err_map_linux_errno);
70b9dcd9e4SAtish Patra 
71efca1398SAtish Patra #ifdef CONFIG_RISCV_SBI_V01
__sbi_v01_cpumask_to_hartmask(const struct cpumask * cpu_mask)7226fb751cSAtish Patra static unsigned long __sbi_v01_cpumask_to_hartmask(const struct cpumask *cpu_mask)
7326fb751cSAtish Patra {
7426fb751cSAtish Patra 	unsigned long cpuid, hartid;
7526fb751cSAtish Patra 	unsigned long hmask = 0;
7626fb751cSAtish Patra 
7726fb751cSAtish Patra 	/*
7826fb751cSAtish Patra 	 * There is no maximum hartid concept in RISC-V and NR_CPUS must not be
7926fb751cSAtish Patra 	 * associated with hartid. As SBI v0.1 is only kept for backward compatibility
8026fb751cSAtish Patra 	 * and will be removed in the future, there is no point in supporting hartid
8126fb751cSAtish Patra 	 * greater than BITS_PER_LONG (32 for RV32 and 64 for RV64). Ideally, SBI v0.2
8226fb751cSAtish Patra 	 * should be used for platforms with hartid greater than BITS_PER_LONG.
8326fb751cSAtish Patra 	 */
8426fb751cSAtish Patra 	for_each_cpu(cpuid, cpu_mask) {
8526fb751cSAtish Patra 		hartid = cpuid_to_hartid_map(cpuid);
8626fb751cSAtish Patra 		if (hartid >= BITS_PER_LONG) {
8726fb751cSAtish Patra 			pr_warn("Unable to send any request to hartid > BITS_PER_LONG for SBI v0.1\n");
8826fb751cSAtish Patra 			break;
8926fb751cSAtish Patra 		}
9012f4a665SGeert Uytterhoeven 		hmask |= BIT(hartid);
9126fb751cSAtish Patra 	}
9226fb751cSAtish Patra 
9326fb751cSAtish Patra 	return hmask;
9426fb751cSAtish Patra }
9526fb751cSAtish Patra 
96b9dcd9e4SAtish Patra /**
97b9dcd9e4SAtish Patra  * sbi_console_putchar() - Writes given character to the console device.
98b9dcd9e4SAtish Patra  * @ch: The data to be written to the console.
99b9dcd9e4SAtish Patra  *
100b9dcd9e4SAtish Patra  * Return: None
101b9dcd9e4SAtish Patra  */
sbi_console_putchar(int ch)102b9dcd9e4SAtish Patra void sbi_console_putchar(int ch)
103b9dcd9e4SAtish Patra {
104b9dcd9e4SAtish Patra 	sbi_ecall(SBI_EXT_0_1_CONSOLE_PUTCHAR, 0, ch, 0, 0, 0, 0, 0);
105b9dcd9e4SAtish Patra }
106b9dcd9e4SAtish Patra EXPORT_SYMBOL(sbi_console_putchar);
107b9dcd9e4SAtish Patra 
108b9dcd9e4SAtish Patra /**
109b9dcd9e4SAtish Patra  * sbi_console_getchar() - Reads a byte from console device.
110b9dcd9e4SAtish Patra  *
111b9dcd9e4SAtish Patra  * Returns the value read from console.
112b9dcd9e4SAtish Patra  */
sbi_console_getchar(void)113b9dcd9e4SAtish Patra int sbi_console_getchar(void)
114b9dcd9e4SAtish Patra {
115b9dcd9e4SAtish Patra 	struct sbiret ret;
116b9dcd9e4SAtish Patra 
117b9dcd9e4SAtish Patra 	ret = sbi_ecall(SBI_EXT_0_1_CONSOLE_GETCHAR, 0, 0, 0, 0, 0, 0, 0);
118b9dcd9e4SAtish Patra 
119b9dcd9e4SAtish Patra 	return ret.error;
120b9dcd9e4SAtish Patra }
121b9dcd9e4SAtish Patra EXPORT_SYMBOL(sbi_console_getchar);
122b9dcd9e4SAtish Patra 
123b9dcd9e4SAtish Patra /**
124b9dcd9e4SAtish Patra  * sbi_shutdown() - Remove all the harts from executing supervisor code.
125b9dcd9e4SAtish Patra  *
126b9dcd9e4SAtish Patra  * Return: None
127b9dcd9e4SAtish Patra  */
sbi_shutdown(void)128b9dcd9e4SAtish Patra void sbi_shutdown(void)
129b9dcd9e4SAtish Patra {
130b9dcd9e4SAtish Patra 	sbi_ecall(SBI_EXT_0_1_SHUTDOWN, 0, 0, 0, 0, 0, 0, 0);
131b9dcd9e4SAtish Patra }
13272df61d9SKefeng Wang EXPORT_SYMBOL(sbi_shutdown);
133b9dcd9e4SAtish Patra 
134b9dcd9e4SAtish Patra /**
13556a6c37fSNanyong Sun  * __sbi_set_timer_v01() - Program the timer for next timer event.
136efca1398SAtish Patra  * @stime_value: The value after which next timer event should fire.
137efca1398SAtish Patra  *
138efca1398SAtish Patra  * Return: None
139efca1398SAtish Patra  */
__sbi_set_timer_v01(uint64_t stime_value)140efca1398SAtish Patra static void __sbi_set_timer_v01(uint64_t stime_value)
141efca1398SAtish Patra {
142efca1398SAtish Patra #if __riscv_xlen == 32
143efca1398SAtish Patra 	sbi_ecall(SBI_EXT_0_1_SET_TIMER, 0, stime_value,
144efca1398SAtish Patra 		  stime_value >> 32, 0, 0, 0, 0);
145efca1398SAtish Patra #else
146efca1398SAtish Patra 	sbi_ecall(SBI_EXT_0_1_SET_TIMER, 0, stime_value, 0, 0, 0, 0, 0);
147efca1398SAtish Patra #endif
148efca1398SAtish Patra }
149efca1398SAtish Patra 
__sbi_send_ipi_v01(unsigned int cpu)150832f15f4SAnup Patel static void __sbi_send_ipi_v01(unsigned int cpu)
151efca1398SAtish Patra {
152832f15f4SAnup Patel 	unsigned long hart_mask =
153832f15f4SAnup Patel 		__sbi_v01_cpumask_to_hartmask(cpumask_of(cpu));
15426fb751cSAtish Patra 	sbi_ecall(SBI_EXT_0_1_SEND_IPI, 0, (unsigned long)(&hart_mask),
155efca1398SAtish Patra 		  0, 0, 0, 0, 0);
156efca1398SAtish Patra }
157efca1398SAtish Patra 
__sbi_rfence_v01(int fid,const struct cpumask * cpu_mask,unsigned long start,unsigned long size,unsigned long arg4,unsigned long arg5)15826fb751cSAtish Patra static int __sbi_rfence_v01(int fid, const struct cpumask *cpu_mask,
159efca1398SAtish Patra 			    unsigned long start, unsigned long size,
160efca1398SAtish Patra 			    unsigned long arg4, unsigned long arg5)
161efca1398SAtish Patra {
162efca1398SAtish Patra 	int result = 0;
16326fb751cSAtish Patra 	unsigned long hart_mask;
16426fb751cSAtish Patra 
1652b35d5b7SGeert Uytterhoeven 	if (!cpu_mask || cpumask_empty(cpu_mask))
16626fb751cSAtish Patra 		cpu_mask = cpu_online_mask;
16726fb751cSAtish Patra 	hart_mask = __sbi_v01_cpumask_to_hartmask(cpu_mask);
168efca1398SAtish Patra 
169efca1398SAtish Patra 	/* v0.2 function IDs are equivalent to v0.1 extension IDs */
170efca1398SAtish Patra 	switch (fid) {
171efca1398SAtish Patra 	case SBI_EXT_RFENCE_REMOTE_FENCE_I:
172efca1398SAtish Patra 		sbi_ecall(SBI_EXT_0_1_REMOTE_FENCE_I, 0,
17326fb751cSAtish Patra 			  (unsigned long)&hart_mask, 0, 0, 0, 0, 0);
174efca1398SAtish Patra 		break;
175efca1398SAtish Patra 	case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA:
176efca1398SAtish Patra 		sbi_ecall(SBI_EXT_0_1_REMOTE_SFENCE_VMA, 0,
17726fb751cSAtish Patra 			  (unsigned long)&hart_mask, start, size,
178efca1398SAtish Patra 			  0, 0, 0);
179efca1398SAtish Patra 		break;
180efca1398SAtish Patra 	case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID:
181efca1398SAtish Patra 		sbi_ecall(SBI_EXT_0_1_REMOTE_SFENCE_VMA_ASID, 0,
18226fb751cSAtish Patra 			  (unsigned long)&hart_mask, start, size,
183efca1398SAtish Patra 			  arg4, 0, 0);
184efca1398SAtish Patra 		break;
185efca1398SAtish Patra 	default:
186efca1398SAtish Patra 		pr_err("SBI call [%d]not supported in SBI v0.1\n", fid);
187efca1398SAtish Patra 		result = -EINVAL;
188efca1398SAtish Patra 	}
189efca1398SAtish Patra 
190efca1398SAtish Patra 	return result;
191efca1398SAtish Patra }
1927d0ce3b2SKefeng Wang 
sbi_set_power_off(void)1937d0ce3b2SKefeng Wang static void sbi_set_power_off(void)
1947d0ce3b2SKefeng Wang {
1957d0ce3b2SKefeng Wang 	pm_power_off = sbi_shutdown;
1967d0ce3b2SKefeng Wang }
197efca1398SAtish Patra #else
__sbi_set_timer_v01(uint64_t stime_value)198efca1398SAtish Patra static void __sbi_set_timer_v01(uint64_t stime_value)
199efca1398SAtish Patra {
200efca1398SAtish Patra 	pr_warn("Timer extension is not available in SBI v%lu.%lu\n",
201efca1398SAtish Patra 		sbi_major_version(), sbi_minor_version());
202efca1398SAtish Patra }
203efca1398SAtish Patra 
__sbi_send_ipi_v01(unsigned int cpu)204832f15f4SAnup Patel static void __sbi_send_ipi_v01(unsigned int cpu)
205efca1398SAtish Patra {
206efca1398SAtish Patra 	pr_warn("IPI extension is not available in SBI v%lu.%lu\n",
207efca1398SAtish Patra 		sbi_major_version(), sbi_minor_version());
208efca1398SAtish Patra }
209efca1398SAtish Patra 
__sbi_rfence_v01(int fid,const struct cpumask * cpu_mask,unsigned long start,unsigned long size,unsigned long arg4,unsigned long arg5)21026fb751cSAtish Patra static int __sbi_rfence_v01(int fid, const struct cpumask *cpu_mask,
211efca1398SAtish Patra 			    unsigned long start, unsigned long size,
212efca1398SAtish Patra 			    unsigned long arg4, unsigned long arg5)
213efca1398SAtish Patra {
214efca1398SAtish Patra 	pr_warn("remote fence extension is not available in SBI v%lu.%lu\n",
215efca1398SAtish Patra 		sbi_major_version(), sbi_minor_version());
216efca1398SAtish Patra 
217efca1398SAtish Patra 	return 0;
218efca1398SAtish Patra }
2197d0ce3b2SKefeng Wang 
sbi_set_power_off(void)2207d0ce3b2SKefeng Wang static void sbi_set_power_off(void) {}
221efca1398SAtish Patra #endif /* CONFIG_RISCV_SBI_V01 */
222efca1398SAtish Patra 
__sbi_set_timer_v02(uint64_t stime_value)2231ef46c23SAtish Patra static void __sbi_set_timer_v02(uint64_t stime_value)
2241ef46c23SAtish Patra {
2251ef46c23SAtish Patra #if __riscv_xlen == 32
2261ef46c23SAtish Patra 	sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value,
2271ef46c23SAtish Patra 		  stime_value >> 32, 0, 0, 0, 0);
2281ef46c23SAtish Patra #else
2291ef46c23SAtish Patra 	sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value, 0,
2301ef46c23SAtish Patra 		  0, 0, 0, 0);
2311ef46c23SAtish Patra #endif
2321ef46c23SAtish Patra }
2331ef46c23SAtish Patra 
__sbi_send_ipi_v02(unsigned int cpu)234832f15f4SAnup Patel static void __sbi_send_ipi_v02(unsigned int cpu)
2351ef46c23SAtish Patra {
2361ef46c23SAtish Patra 	int result;
237832f15f4SAnup Patel 	struct sbiret ret = {0};
2381ef46c23SAtish Patra 
2391ef46c23SAtish Patra 	ret = sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SEND_IPI,
240832f15f4SAnup Patel 			1UL, cpuid_to_hartid_map(cpu), 0, 0, 0, 0);
241832f15f4SAnup Patel 	if (ret.error) {
2421ef46c23SAtish Patra 		result = sbi_err_map_linux_errno(ret.error);
243832f15f4SAnup Patel 		pr_err("%s: hbase = [%lu] failed (error [%d])\n",
244832f15f4SAnup Patel 			__func__, cpuid_to_hartid_map(cpu), result);
245832f15f4SAnup Patel 	}
2461ef46c23SAtish Patra }
2471ef46c23SAtish Patra 
__sbi_rfence_v02_call(unsigned long fid,unsigned long hmask,unsigned long hbase,unsigned long start,unsigned long size,unsigned long arg4,unsigned long arg5)24826fb751cSAtish Patra static int __sbi_rfence_v02_call(unsigned long fid, unsigned long hmask,
2491ef46c23SAtish Patra 				 unsigned long hbase, unsigned long start,
2501ef46c23SAtish Patra 				 unsigned long size, unsigned long arg4,
2511ef46c23SAtish Patra 				 unsigned long arg5)
2521ef46c23SAtish Patra {
2531ef46c23SAtish Patra 	struct sbiret ret = {0};
2541ef46c23SAtish Patra 	int ext = SBI_EXT_RFENCE;
2551ef46c23SAtish Patra 	int result = 0;
2561ef46c23SAtish Patra 
2571ef46c23SAtish Patra 	switch (fid) {
2581ef46c23SAtish Patra 	case SBI_EXT_RFENCE_REMOTE_FENCE_I:
25926fb751cSAtish Patra 		ret = sbi_ecall(ext, fid, hmask, hbase, 0, 0, 0, 0);
2601ef46c23SAtish Patra 		break;
2611ef46c23SAtish Patra 	case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA:
26226fb751cSAtish Patra 		ret = sbi_ecall(ext, fid, hmask, hbase, start,
2631ef46c23SAtish Patra 				size, 0, 0);
2641ef46c23SAtish Patra 		break;
2651ef46c23SAtish Patra 	case SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID:
26626fb751cSAtish Patra 		ret = sbi_ecall(ext, fid, hmask, hbase, start,
2671ef46c23SAtish Patra 				size, arg4, 0);
2681ef46c23SAtish Patra 		break;
2691ef46c23SAtish Patra 
2701ef46c23SAtish Patra 	case SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA:
27126fb751cSAtish Patra 		ret = sbi_ecall(ext, fid, hmask, hbase, start,
2721ef46c23SAtish Patra 				size, 0, 0);
2731ef46c23SAtish Patra 		break;
2741ef46c23SAtish Patra 	case SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID:
27526fb751cSAtish Patra 		ret = sbi_ecall(ext, fid, hmask, hbase, start,
2761ef46c23SAtish Patra 				size, arg4, 0);
2771ef46c23SAtish Patra 		break;
2781ef46c23SAtish Patra 	case SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA:
27926fb751cSAtish Patra 		ret = sbi_ecall(ext, fid, hmask, hbase, start,
2801ef46c23SAtish Patra 				size, 0, 0);
2811ef46c23SAtish Patra 		break;
2821ef46c23SAtish Patra 	case SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID:
28326fb751cSAtish Patra 		ret = sbi_ecall(ext, fid, hmask, hbase, start,
2841ef46c23SAtish Patra 				size, arg4, 0);
2851ef46c23SAtish Patra 		break;
2861ef46c23SAtish Patra 	default:
2871ef46c23SAtish Patra 		pr_err("unknown function ID [%lu] for SBI extension [%d]\n",
2881ef46c23SAtish Patra 		       fid, ext);
2891ef46c23SAtish Patra 		result = -EINVAL;
2901ef46c23SAtish Patra 	}
2911ef46c23SAtish Patra 
2921ef46c23SAtish Patra 	if (ret.error) {
2931ef46c23SAtish Patra 		result = sbi_err_map_linux_errno(ret.error);
2941ef46c23SAtish Patra 		pr_err("%s: hbase = [%lu] hmask = [0x%lx] failed (error [%d])\n",
29526fb751cSAtish Patra 		       __func__, hbase, hmask, result);
2961ef46c23SAtish Patra 	}
2971ef46c23SAtish Patra 
2981ef46c23SAtish Patra 	return result;
2991ef46c23SAtish Patra }
3001ef46c23SAtish Patra 
__sbi_rfence_v02(int fid,const struct cpumask * cpu_mask,unsigned long start,unsigned long size,unsigned long arg4,unsigned long arg5)30126fb751cSAtish Patra static int __sbi_rfence_v02(int fid, const struct cpumask *cpu_mask,
3021ef46c23SAtish Patra 			    unsigned long start, unsigned long size,
3031ef46c23SAtish Patra 			    unsigned long arg4, unsigned long arg5)
3041ef46c23SAtish Patra {
3055feef64fSGeert Uytterhoeven 	unsigned long hartid, cpuid, hmask = 0, hbase = 0, htop = 0;
3061ef46c23SAtish Patra 	int result;
3071ef46c23SAtish Patra 
3082b35d5b7SGeert Uytterhoeven 	if (!cpu_mask || cpumask_empty(cpu_mask))
30926fb751cSAtish Patra 		cpu_mask = cpu_online_mask;
3101ef46c23SAtish Patra 
31126fb751cSAtish Patra 	for_each_cpu(cpuid, cpu_mask) {
31226fb751cSAtish Patra 		hartid = cpuid_to_hartid_map(cpuid);
3135feef64fSGeert Uytterhoeven 		if (hmask) {
3145feef64fSGeert Uytterhoeven 			if (hartid + BITS_PER_LONG <= htop ||
3155feef64fSGeert Uytterhoeven 			    hbase + BITS_PER_LONG <= hartid) {
3165feef64fSGeert Uytterhoeven 				result = __sbi_rfence_v02_call(fid, hmask,
3175feef64fSGeert Uytterhoeven 						hbase, start, size, arg4, arg5);
3181ef46c23SAtish Patra 				if (result)
3191ef46c23SAtish Patra 					return result;
32026fb751cSAtish Patra 				hmask = 0;
3215feef64fSGeert Uytterhoeven 			} else if (hartid < hbase) {
3225feef64fSGeert Uytterhoeven 				/* shift the mask to fit lower hartid */
3235feef64fSGeert Uytterhoeven 				hmask <<= hbase - hartid;
3241ef46c23SAtish Patra 				hbase = hartid;
3255feef64fSGeert Uytterhoeven 			}
3265feef64fSGeert Uytterhoeven 		}
3275feef64fSGeert Uytterhoeven 		if (!hmask) {
3285feef64fSGeert Uytterhoeven 			hbase = hartid;
3295feef64fSGeert Uytterhoeven 			htop = hartid;
3305feef64fSGeert Uytterhoeven 		} else if (hartid > htop) {
3315feef64fSGeert Uytterhoeven 			htop = hartid;
3325feef64fSGeert Uytterhoeven 		}
33312f4a665SGeert Uytterhoeven 		hmask |= BIT(hartid - hbase);
3341ef46c23SAtish Patra 	}
3351ef46c23SAtish Patra 
33626fb751cSAtish Patra 	if (hmask) {
33726fb751cSAtish Patra 		result = __sbi_rfence_v02_call(fid, hmask, hbase,
3381ef46c23SAtish Patra 					       start, size, arg4, arg5);
3391ef46c23SAtish Patra 		if (result)
3401ef46c23SAtish Patra 			return result;
3411ef46c23SAtish Patra 	}
3421ef46c23SAtish Patra 
3431ef46c23SAtish Patra 	return 0;
3441ef46c23SAtish Patra }
3451ef46c23SAtish Patra 
346efca1398SAtish Patra /**
347efca1398SAtish Patra  * sbi_set_timer() - Program the timer for next timer event.
348efca1398SAtish Patra  * @stime_value: The value after which next timer event should fire.
349efca1398SAtish Patra  *
3504bb87563SAtish Patra  * Return: None.
351efca1398SAtish Patra  */
sbi_set_timer(uint64_t stime_value)352efca1398SAtish Patra void sbi_set_timer(uint64_t stime_value)
353efca1398SAtish Patra {
354efca1398SAtish Patra 	__sbi_set_timer(stime_value);
355efca1398SAtish Patra }
356b9dcd9e4SAtish Patra 
357b9dcd9e4SAtish Patra /**
358b9dcd9e4SAtish Patra  * sbi_send_ipi() - Send an IPI to any hart.
359832f15f4SAnup Patel  * @cpu: Logical id of the target CPU.
360b9dcd9e4SAtish Patra  */
sbi_send_ipi(unsigned int cpu)361832f15f4SAnup Patel void sbi_send_ipi(unsigned int cpu)
362b9dcd9e4SAtish Patra {
363832f15f4SAnup Patel 	__sbi_send_ipi(cpu);
364b9dcd9e4SAtish Patra }
365b9dcd9e4SAtish Patra EXPORT_SYMBOL(sbi_send_ipi);
366b9dcd9e4SAtish Patra 
367b9dcd9e4SAtish Patra /**
368b9dcd9e4SAtish Patra  * sbi_remote_fence_i() - Execute FENCE.I instruction on given remote harts.
36926fb751cSAtish Patra  * @cpu_mask: A cpu mask containing all the target harts.
370b9dcd9e4SAtish Patra  *
3714bb87563SAtish Patra  * Return: 0 on success, appropriate linux error code otherwise.
372b9dcd9e4SAtish Patra  */
sbi_remote_fence_i(const struct cpumask * cpu_mask)37326fb751cSAtish Patra int sbi_remote_fence_i(const struct cpumask *cpu_mask)
374b9dcd9e4SAtish Patra {
3754bb87563SAtish Patra 	return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_FENCE_I,
37626fb751cSAtish Patra 			    cpu_mask, 0, 0, 0, 0);
377b9dcd9e4SAtish Patra }
378b9dcd9e4SAtish Patra EXPORT_SYMBOL(sbi_remote_fence_i);
379b9dcd9e4SAtish Patra 
380b9dcd9e4SAtish Patra /**
381b9dcd9e4SAtish Patra  * sbi_remote_sfence_vma_asid() - Execute SFENCE.VMA instructions on given
382*53a38f8fSAlexandre Ghiti  * remote harts for a virtual address range belonging to a specific ASID or not.
383b9dcd9e4SAtish Patra  *
38426fb751cSAtish Patra  * @cpu_mask: A cpu mask containing all the target harts.
385b9dcd9e4SAtish Patra  * @start: Start of the virtual address
386b9dcd9e4SAtish Patra  * @size: Total size of the virtual address range.
387*53a38f8fSAlexandre Ghiti  * @asid: The value of address space identifier (ASID), or FLUSH_TLB_NO_ASID
388*53a38f8fSAlexandre Ghiti  * for flushing all address spaces.
389b9dcd9e4SAtish Patra  *
3904bb87563SAtish Patra  * Return: 0 on success, appropriate linux error code otherwise.
391b9dcd9e4SAtish Patra  */
sbi_remote_sfence_vma_asid(const struct cpumask * cpu_mask,unsigned long start,unsigned long size,unsigned long asid)39226fb751cSAtish Patra int sbi_remote_sfence_vma_asid(const struct cpumask *cpu_mask,
393b9dcd9e4SAtish Patra 				unsigned long start,
394b9dcd9e4SAtish Patra 				unsigned long size,
395b9dcd9e4SAtish Patra 				unsigned long asid)
396b9dcd9e4SAtish Patra {
397*53a38f8fSAlexandre Ghiti 	if (asid == FLUSH_TLB_NO_ASID)
398*53a38f8fSAlexandre Ghiti 		return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA,
399*53a38f8fSAlexandre Ghiti 				    cpu_mask, start, size, 0, 0);
400*53a38f8fSAlexandre Ghiti 	else
4014bb87563SAtish Patra 		return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_SFENCE_VMA_ASID,
40226fb751cSAtish Patra 				    cpu_mask, start, size, asid, 0);
403b9dcd9e4SAtish Patra }
404b9dcd9e4SAtish Patra EXPORT_SYMBOL(sbi_remote_sfence_vma_asid);
405b9dcd9e4SAtish Patra 
406b9dcd9e4SAtish Patra /**
4071ef46c23SAtish Patra  * sbi_remote_hfence_gvma() - Execute HFENCE.GVMA instructions on given remote
4081ef46c23SAtish Patra  *			   harts for the specified guest physical address range.
40926fb751cSAtish Patra  * @cpu_mask: A cpu mask containing all the target harts.
4101ef46c23SAtish Patra  * @start: Start of the guest physical address
4111ef46c23SAtish Patra  * @size: Total size of the guest physical address range.
4121ef46c23SAtish Patra  *
4131ef46c23SAtish Patra  * Return: None
4141ef46c23SAtish Patra  */
sbi_remote_hfence_gvma(const struct cpumask * cpu_mask,unsigned long start,unsigned long size)41526fb751cSAtish Patra int sbi_remote_hfence_gvma(const struct cpumask *cpu_mask,
4161ef46c23SAtish Patra 			   unsigned long start,
4171ef46c23SAtish Patra 			   unsigned long size)
4181ef46c23SAtish Patra {
4191ef46c23SAtish Patra 	return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA,
42026fb751cSAtish Patra 			    cpu_mask, start, size, 0, 0);
4211ef46c23SAtish Patra }
4221ef46c23SAtish Patra EXPORT_SYMBOL_GPL(sbi_remote_hfence_gvma);
4231ef46c23SAtish Patra 
4241ef46c23SAtish Patra /**
4251ef46c23SAtish Patra  * sbi_remote_hfence_gvma_vmid() - Execute HFENCE.GVMA instructions on given
4261ef46c23SAtish Patra  * remote harts for a guest physical address range belonging to a specific VMID.
4271ef46c23SAtish Patra  *
42826fb751cSAtish Patra  * @cpu_mask: A cpu mask containing all the target harts.
4291ef46c23SAtish Patra  * @start: Start of the guest physical address
4301ef46c23SAtish Patra  * @size: Total size of the guest physical address range.
4311ef46c23SAtish Patra  * @vmid: The value of guest ID (VMID).
4321ef46c23SAtish Patra  *
4331ef46c23SAtish Patra  * Return: 0 if success, Error otherwise.
4341ef46c23SAtish Patra  */
sbi_remote_hfence_gvma_vmid(const struct cpumask * cpu_mask,unsigned long start,unsigned long size,unsigned long vmid)43526fb751cSAtish Patra int sbi_remote_hfence_gvma_vmid(const struct cpumask *cpu_mask,
4361ef46c23SAtish Patra 				unsigned long start,
4371ef46c23SAtish Patra 				unsigned long size,
4381ef46c23SAtish Patra 				unsigned long vmid)
4391ef46c23SAtish Patra {
4401ef46c23SAtish Patra 	return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_HFENCE_GVMA_VMID,
44126fb751cSAtish Patra 			    cpu_mask, start, size, vmid, 0);
4421ef46c23SAtish Patra }
4431ef46c23SAtish Patra EXPORT_SYMBOL(sbi_remote_hfence_gvma_vmid);
4441ef46c23SAtish Patra 
4451ef46c23SAtish Patra /**
4461ef46c23SAtish Patra  * sbi_remote_hfence_vvma() - Execute HFENCE.VVMA instructions on given remote
4471ef46c23SAtish Patra  *			     harts for the current guest virtual address range.
44826fb751cSAtish Patra  * @cpu_mask: A cpu mask containing all the target harts.
4491ef46c23SAtish Patra  * @start: Start of the current guest virtual address
4501ef46c23SAtish Patra  * @size: Total size of the current guest virtual address range.
4511ef46c23SAtish Patra  *
4521ef46c23SAtish Patra  * Return: None
4531ef46c23SAtish Patra  */
sbi_remote_hfence_vvma(const struct cpumask * cpu_mask,unsigned long start,unsigned long size)45426fb751cSAtish Patra int sbi_remote_hfence_vvma(const struct cpumask *cpu_mask,
4551ef46c23SAtish Patra 			   unsigned long start,
4561ef46c23SAtish Patra 			   unsigned long size)
4571ef46c23SAtish Patra {
4581ef46c23SAtish Patra 	return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA,
45926fb751cSAtish Patra 			    cpu_mask, start, size, 0, 0);
4601ef46c23SAtish Patra }
4611ef46c23SAtish Patra EXPORT_SYMBOL(sbi_remote_hfence_vvma);
4621ef46c23SAtish Patra 
4631ef46c23SAtish Patra /**
4641ef46c23SAtish Patra  * sbi_remote_hfence_vvma_asid() - Execute HFENCE.VVMA instructions on given
4651ef46c23SAtish Patra  * remote harts for current guest virtual address range belonging to a specific
4661ef46c23SAtish Patra  * ASID.
4671ef46c23SAtish Patra  *
46826fb751cSAtish Patra  * @cpu_mask: A cpu mask containing all the target harts.
4691ef46c23SAtish Patra  * @start: Start of the current guest virtual address
4701ef46c23SAtish Patra  * @size: Total size of the current guest virtual address range.
4711ef46c23SAtish Patra  * @asid: The value of address space identifier (ASID).
4721ef46c23SAtish Patra  *
4731ef46c23SAtish Patra  * Return: None
4741ef46c23SAtish Patra  */
sbi_remote_hfence_vvma_asid(const struct cpumask * cpu_mask,unsigned long start,unsigned long size,unsigned long asid)47526fb751cSAtish Patra int sbi_remote_hfence_vvma_asid(const struct cpumask *cpu_mask,
4761ef46c23SAtish Patra 				unsigned long start,
4771ef46c23SAtish Patra 				unsigned long size,
4781ef46c23SAtish Patra 				unsigned long asid)
4791ef46c23SAtish Patra {
4801ef46c23SAtish Patra 	return __sbi_rfence(SBI_EXT_RFENCE_REMOTE_HFENCE_VVMA_ASID,
48126fb751cSAtish Patra 			    cpu_mask, start, size, asid, 0);
4821ef46c23SAtish Patra }
4831ef46c23SAtish Patra EXPORT_SYMBOL(sbi_remote_hfence_vvma_asid);
4841ef46c23SAtish Patra 
sbi_srst_reset(unsigned long type,unsigned long reason)485b579dfe7SAnup Patel static void sbi_srst_reset(unsigned long type, unsigned long reason)
486b579dfe7SAnup Patel {
487b579dfe7SAnup Patel 	sbi_ecall(SBI_EXT_SRST, SBI_EXT_SRST_RESET, type, reason,
488b579dfe7SAnup Patel 		  0, 0, 0, 0);
489b579dfe7SAnup Patel 	pr_warn("%s: type=0x%lx reason=0x%lx failed\n",
490b579dfe7SAnup Patel 		__func__, type, reason);
491b579dfe7SAnup Patel }
492b579dfe7SAnup Patel 
sbi_srst_reboot(struct notifier_block * this,unsigned long mode,void * cmd)493b579dfe7SAnup Patel static int sbi_srst_reboot(struct notifier_block *this,
494b579dfe7SAnup Patel 			   unsigned long mode, void *cmd)
495b579dfe7SAnup Patel {
496b579dfe7SAnup Patel 	sbi_srst_reset((mode == REBOOT_WARM || mode == REBOOT_SOFT) ?
497b579dfe7SAnup Patel 		       SBI_SRST_RESET_TYPE_WARM_REBOOT :
498b579dfe7SAnup Patel 		       SBI_SRST_RESET_TYPE_COLD_REBOOT,
499b579dfe7SAnup Patel 		       SBI_SRST_RESET_REASON_NONE);
500b579dfe7SAnup Patel 	return NOTIFY_DONE;
501b579dfe7SAnup Patel }
502b579dfe7SAnup Patel 
503b579dfe7SAnup Patel static struct notifier_block sbi_srst_reboot_nb;
504b579dfe7SAnup Patel 
sbi_srst_power_off(void)505b579dfe7SAnup Patel static void sbi_srst_power_off(void)
506b579dfe7SAnup Patel {
507b579dfe7SAnup Patel 	sbi_srst_reset(SBI_SRST_RESET_TYPE_SHUTDOWN,
508b579dfe7SAnup Patel 		       SBI_SRST_RESET_REASON_NONE);
509b579dfe7SAnup Patel }
510b579dfe7SAnup Patel 
5111ef46c23SAtish Patra /**
512b9dcd9e4SAtish Patra  * sbi_probe_extension() - Check if an SBI extension ID is supported or not.
513b9dcd9e4SAtish Patra  * @extid: The extension ID to be probed.
514b9dcd9e4SAtish Patra  *
51541cad828SAndrew Jones  * Return: 1 or an extension specific nonzero value if yes, 0 otherwise.
516b9dcd9e4SAtish Patra  */
sbi_probe_extension(int extid)51741cad828SAndrew Jones long sbi_probe_extension(int extid)
518b9dcd9e4SAtish Patra {
519b9dcd9e4SAtish Patra 	struct sbiret ret;
520b9dcd9e4SAtish Patra 
521b9dcd9e4SAtish Patra 	ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_PROBE_EXT, extid,
522b9dcd9e4SAtish Patra 			0, 0, 0, 0, 0);
523b9dcd9e4SAtish Patra 	if (!ret.error)
524b9dcd9e4SAtish Patra 		return ret.value;
525b9dcd9e4SAtish Patra 
52641cad828SAndrew Jones 	return 0;
527b9dcd9e4SAtish Patra }
528b9dcd9e4SAtish Patra EXPORT_SYMBOL(sbi_probe_extension);
529b9dcd9e4SAtish Patra 
__sbi_base_ecall(int fid)530b9dcd9e4SAtish Patra static long __sbi_base_ecall(int fid)
531b9dcd9e4SAtish Patra {
532b9dcd9e4SAtish Patra 	struct sbiret ret;
533b9dcd9e4SAtish Patra 
534b9dcd9e4SAtish Patra 	ret = sbi_ecall(SBI_EXT_BASE, fid, 0, 0, 0, 0, 0, 0);
535b9dcd9e4SAtish Patra 	if (!ret.error)
536b9dcd9e4SAtish Patra 		return ret.value;
537b9dcd9e4SAtish Patra 	else
538b9dcd9e4SAtish Patra 		return sbi_err_map_linux_errno(ret.error);
539b9dcd9e4SAtish Patra }
540b9dcd9e4SAtish Patra 
sbi_get_spec_version(void)541b9dcd9e4SAtish Patra static inline long sbi_get_spec_version(void)
542b9dcd9e4SAtish Patra {
543b9dcd9e4SAtish Patra 	return __sbi_base_ecall(SBI_EXT_BASE_GET_SPEC_VERSION);
544b9dcd9e4SAtish Patra }
545b9dcd9e4SAtish Patra 
sbi_get_firmware_id(void)546b9dcd9e4SAtish Patra static inline long sbi_get_firmware_id(void)
547b9dcd9e4SAtish Patra {
548b9dcd9e4SAtish Patra 	return __sbi_base_ecall(SBI_EXT_BASE_GET_IMP_ID);
549b9dcd9e4SAtish Patra }
550b9dcd9e4SAtish Patra 
sbi_get_firmware_version(void)551b9dcd9e4SAtish Patra static inline long sbi_get_firmware_version(void)
552b9dcd9e4SAtish Patra {
553b9dcd9e4SAtish Patra 	return __sbi_base_ecall(SBI_EXT_BASE_GET_IMP_VERSION);
554b9dcd9e4SAtish Patra }
555b9dcd9e4SAtish Patra 
sbi_get_mvendorid(void)556183787c6SVincent Chen long sbi_get_mvendorid(void)
557183787c6SVincent Chen {
558183787c6SVincent Chen 	return __sbi_base_ecall(SBI_EXT_BASE_GET_MVENDORID);
559183787c6SVincent Chen }
560a1a44e22SAnup Patel EXPORT_SYMBOL_GPL(sbi_get_mvendorid);
561183787c6SVincent Chen 
sbi_get_marchid(void)562183787c6SVincent Chen long sbi_get_marchid(void)
563183787c6SVincent Chen {
564183787c6SVincent Chen 	return __sbi_base_ecall(SBI_EXT_BASE_GET_MARCHID);
565183787c6SVincent Chen }
566a1a44e22SAnup Patel EXPORT_SYMBOL_GPL(sbi_get_marchid);
567183787c6SVincent Chen 
sbi_get_mimpid(void)568183787c6SVincent Chen long sbi_get_mimpid(void)
569183787c6SVincent Chen {
570183787c6SVincent Chen 	return __sbi_base_ecall(SBI_EXT_BASE_GET_MIMPID);
571183787c6SVincent Chen }
572a1a44e22SAnup Patel EXPORT_SYMBOL_GPL(sbi_get_mimpid);
573183787c6SVincent Chen 
sbi_init(void)574641e8cd2SKefeng Wang void __init sbi_init(void)
5753320648eSChristoph Hellwig {
576b9dcd9e4SAtish Patra 	int ret;
577b9dcd9e4SAtish Patra 
5787d0ce3b2SKefeng Wang 	sbi_set_power_off();
579b9dcd9e4SAtish Patra 	ret = sbi_get_spec_version();
580b9dcd9e4SAtish Patra 	if (ret > 0)
581b9dcd9e4SAtish Patra 		sbi_spec_version = ret;
582b9dcd9e4SAtish Patra 
583b9dcd9e4SAtish Patra 	pr_info("SBI specification v%lu.%lu detected\n",
584b9dcd9e4SAtish Patra 		sbi_major_version(), sbi_minor_version());
585efca1398SAtish Patra 
586efca1398SAtish Patra 	if (!sbi_spec_is_0_1()) {
587b9dcd9e4SAtish Patra 		pr_info("SBI implementation ID=0x%lx Version=0x%lx\n",
588b9dcd9e4SAtish Patra 			sbi_get_firmware_id(), sbi_get_firmware_version());
58941cad828SAndrew Jones 		if (sbi_probe_extension(SBI_EXT_TIME)) {
5901ef46c23SAtish Patra 			__sbi_set_timer = __sbi_set_timer_v02;
591f35bb4b8SAnup Patel 			pr_info("SBI TIME extension detected\n");
5921ef46c23SAtish Patra 		} else {
5931ef46c23SAtish Patra 			__sbi_set_timer = __sbi_set_timer_v01;
594efca1398SAtish Patra 		}
59541cad828SAndrew Jones 		if (sbi_probe_extension(SBI_EXT_IPI)) {
5961ef46c23SAtish Patra 			__sbi_send_ipi	= __sbi_send_ipi_v02;
597f35bb4b8SAnup Patel 			pr_info("SBI IPI extension detected\n");
5981ef46c23SAtish Patra 		} else {
5991ef46c23SAtish Patra 			__sbi_send_ipi	= __sbi_send_ipi_v01;
6001ef46c23SAtish Patra 		}
60141cad828SAndrew Jones 		if (sbi_probe_extension(SBI_EXT_RFENCE)) {
6021ef46c23SAtish Patra 			__sbi_rfence	= __sbi_rfence_v02;
603f35bb4b8SAnup Patel 			pr_info("SBI RFENCE extension detected\n");
6041ef46c23SAtish Patra 		} else {
6051ef46c23SAtish Patra 			__sbi_rfence	= __sbi_rfence_v01;
6061ef46c23SAtish Patra 		}
607b579dfe7SAnup Patel 		if ((sbi_spec_version >= sbi_mk_version(0, 3)) &&
60841cad828SAndrew Jones 		    sbi_probe_extension(SBI_EXT_SRST)) {
609b579dfe7SAnup Patel 			pr_info("SBI SRST extension detected\n");
610b579dfe7SAnup Patel 			pm_power_off = sbi_srst_power_off;
611b579dfe7SAnup Patel 			sbi_srst_reboot_nb.notifier_call = sbi_srst_reboot;
612b579dfe7SAnup Patel 			sbi_srst_reboot_nb.priority = 192;
613b579dfe7SAnup Patel 			register_restart_handler(&sbi_srst_reboot_nb);
614b579dfe7SAnup Patel 		}
6151ef46c23SAtish Patra 	} else {
616efca1398SAtish Patra 		__sbi_set_timer = __sbi_set_timer_v01;
617efca1398SAtish Patra 		__sbi_send_ipi	= __sbi_send_ipi_v01;
618efca1398SAtish Patra 		__sbi_rfence	= __sbi_rfence_v01;
6191ef46c23SAtish Patra 	}
6203320648eSChristoph Hellwig }
621