19ccb00e4SAndrew Jones // SPDX-License-Identifier: GPL-2.0-only 29ccb00e4SAndrew Jones #include <libcflat.h> 325475fa5SAndrew Jones #include <cpumask.h> 425475fa5SAndrew Jones #include <limits.h> 598ea1f96SClément Léger #include <asm/io.h> 69ccb00e4SAndrew Jones #include <asm/sbi.h> 725475fa5SAndrew Jones #include <asm/setup.h> 89ccb00e4SAndrew Jones 99ccb00e4SAndrew Jones struct sbiret sbi_ecall(int ext, int fid, unsigned long arg0, 109ccb00e4SAndrew Jones unsigned long arg1, unsigned long arg2, 119ccb00e4SAndrew Jones unsigned long arg3, unsigned long arg4, 129ccb00e4SAndrew Jones unsigned long arg5) 139ccb00e4SAndrew Jones { 149ccb00e4SAndrew Jones register uintptr_t a0 asm ("a0") = (uintptr_t)(arg0); 159ccb00e4SAndrew Jones register uintptr_t a1 asm ("a1") = (uintptr_t)(arg1); 169ccb00e4SAndrew Jones register uintptr_t a2 asm ("a2") = (uintptr_t)(arg2); 179ccb00e4SAndrew Jones register uintptr_t a3 asm ("a3") = (uintptr_t)(arg3); 189ccb00e4SAndrew Jones register uintptr_t a4 asm ("a4") = (uintptr_t)(arg4); 199ccb00e4SAndrew Jones register uintptr_t a5 asm ("a5") = (uintptr_t)(arg5); 209ccb00e4SAndrew Jones register uintptr_t a6 asm ("a6") = (uintptr_t)(fid); 219ccb00e4SAndrew Jones register uintptr_t a7 asm ("a7") = (uintptr_t)(ext); 229ccb00e4SAndrew Jones struct sbiret ret; 239ccb00e4SAndrew Jones 249ccb00e4SAndrew Jones asm volatile ( 259ccb00e4SAndrew Jones "ecall" 269ccb00e4SAndrew Jones : "+r" (a0), "+r" (a1) 279ccb00e4SAndrew Jones : "r" (a2), "r" (a3), "r" (a4), "r" (a5), "r" (a6), "r" (a7) 289ccb00e4SAndrew Jones : "memory"); 299ccb00e4SAndrew Jones ret.error = a0; 309ccb00e4SAndrew Jones ret.value = a1; 319ccb00e4SAndrew Jones 329ccb00e4SAndrew Jones return ret; 339ccb00e4SAndrew Jones } 349ccb00e4SAndrew Jones 3598ea1f96SClément Léger struct sbiret sbi_sse_read_attrs_raw(unsigned long event_id, unsigned long base_attr_id, 3698ea1f96SClément Léger unsigned long attr_count, unsigned long phys_lo, 3798ea1f96SClément Léger unsigned long phys_hi) 3898ea1f96SClément Léger { 3998ea1f96SClément Léger return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_READ_ATTRS, event_id, base_attr_id, attr_count, 4098ea1f96SClément Léger phys_lo, phys_hi, 0); 4198ea1f96SClément Léger } 4298ea1f96SClément Léger 4398ea1f96SClément Léger struct sbiret sbi_sse_read_attrs(unsigned long event_id, unsigned long base_attr_id, 4498ea1f96SClément Léger unsigned long attr_count, unsigned long *values) 4598ea1f96SClément Léger { 4698ea1f96SClément Léger phys_addr_t p = virt_to_phys(values); 4798ea1f96SClément Léger 4898ea1f96SClément Léger return sbi_sse_read_attrs_raw(event_id, base_attr_id, attr_count, lower_32_bits(p), 4998ea1f96SClément Léger upper_32_bits(p)); 5098ea1f96SClément Léger } 5198ea1f96SClément Léger 5298ea1f96SClément Léger struct sbiret sbi_sse_write_attrs_raw(unsigned long event_id, unsigned long base_attr_id, 5398ea1f96SClément Léger unsigned long attr_count, unsigned long phys_lo, 5498ea1f96SClément Léger unsigned long phys_hi) 5598ea1f96SClément Léger { 5698ea1f96SClément Léger return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_WRITE_ATTRS, event_id, base_attr_id, attr_count, 5798ea1f96SClément Léger phys_lo, phys_hi, 0); 5898ea1f96SClément Léger } 5998ea1f96SClément Léger 6098ea1f96SClément Léger struct sbiret sbi_sse_write_attrs(unsigned long event_id, unsigned long base_attr_id, 6198ea1f96SClément Léger unsigned long attr_count, unsigned long *values) 6298ea1f96SClément Léger { 6398ea1f96SClément Léger phys_addr_t p = virt_to_phys(values); 6498ea1f96SClément Léger 6598ea1f96SClément Léger return sbi_sse_write_attrs_raw(event_id, base_attr_id, attr_count, lower_32_bits(p), 6698ea1f96SClément Léger upper_32_bits(p)); 6798ea1f96SClément Léger } 6898ea1f96SClément Léger 6998ea1f96SClément Léger struct sbiret sbi_sse_register_raw(unsigned long event_id, unsigned long entry_pc, 7098ea1f96SClément Léger unsigned long entry_arg) 7198ea1f96SClément Léger { 7298ea1f96SClément Léger return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_REGISTER, event_id, entry_pc, entry_arg, 0, 0, 0); 7398ea1f96SClément Léger } 7498ea1f96SClément Léger 7598ea1f96SClément Léger struct sbiret sbi_sse_register(unsigned long event_id, struct sbi_sse_handler_arg *arg) 7698ea1f96SClément Léger { 7798ea1f96SClément Léger return sbi_sse_register_raw(event_id, (unsigned long)sbi_sse_entry, (unsigned long)arg); 7898ea1f96SClément Léger } 7998ea1f96SClément Léger 8098ea1f96SClément Léger struct sbiret sbi_sse_unregister(unsigned long event_id) 8198ea1f96SClément Léger { 8298ea1f96SClément Léger return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_UNREGISTER, event_id, 0, 0, 0, 0, 0); 8398ea1f96SClément Léger } 8498ea1f96SClément Léger 8598ea1f96SClément Léger struct sbiret sbi_sse_enable(unsigned long event_id) 8698ea1f96SClément Léger { 8798ea1f96SClément Léger return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_ENABLE, event_id, 0, 0, 0, 0, 0); 8898ea1f96SClément Léger } 8998ea1f96SClément Léger 9098ea1f96SClément Léger struct sbiret sbi_sse_disable(unsigned long event_id) 9198ea1f96SClément Léger { 9298ea1f96SClément Léger return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_DISABLE, event_id, 0, 0, 0, 0, 0); 9398ea1f96SClément Léger } 9498ea1f96SClément Léger 9598ea1f96SClément Léger struct sbiret sbi_sse_hart_mask(void) 9698ea1f96SClément Léger { 9798ea1f96SClément Léger return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_HART_MASK, 0, 0, 0, 0, 0, 0); 9898ea1f96SClément Léger } 9998ea1f96SClément Léger 10098ea1f96SClément Léger struct sbiret sbi_sse_hart_unmask(void) 10198ea1f96SClément Léger { 10298ea1f96SClément Léger return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_HART_UNMASK, 0, 0, 0, 0, 0, 0); 10398ea1f96SClément Léger } 10498ea1f96SClément Léger 10598ea1f96SClément Léger struct sbiret sbi_sse_inject(unsigned long event_id, unsigned long hart_id) 10698ea1f96SClément Léger { 10798ea1f96SClément Léger return sbi_ecall(SBI_EXT_SSE, SBI_EXT_SSE_INJECT, event_id, hart_id, 0, 0, 0, 0); 10898ea1f96SClément Léger } 10998ea1f96SClément Léger 1109ccb00e4SAndrew Jones void sbi_shutdown(void) 1119ccb00e4SAndrew Jones { 1129ccb00e4SAndrew Jones sbi_ecall(SBI_EXT_SRST, 0, 0, 0, 0, 0, 0, 0); 1139ccb00e4SAndrew Jones puts("SBI shutdown failed!\n"); 1149ccb00e4SAndrew Jones } 1159c92b28eSAndrew Jones 1169c92b28eSAndrew Jones struct sbiret sbi_hart_start(unsigned long hartid, unsigned long entry, unsigned long sp) 1179c92b28eSAndrew Jones { 1189c92b28eSAndrew Jones return sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_START, hartid, entry, sp, 0, 0, 0); 1199c92b28eSAndrew Jones } 1207040d2a9SJames Raphael Tiovalen 12167b8f462SJames Raphael Tiovalen struct sbiret sbi_hart_stop(void) 12267b8f462SJames Raphael Tiovalen { 12367b8f462SJames Raphael Tiovalen return sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STOP, 0, 0, 0, 0, 0, 0); 12467b8f462SJames Raphael Tiovalen } 12567b8f462SJames Raphael Tiovalen 12667b8f462SJames Raphael Tiovalen struct sbiret sbi_hart_get_status(unsigned long hartid) 12767b8f462SJames Raphael Tiovalen { 12867b8f462SJames Raphael Tiovalen return sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STATUS, hartid, 0, 0, 0, 0, 0); 12967b8f462SJames Raphael Tiovalen } 13067b8f462SJames Raphael Tiovalen 1316489b8b0SJames Raphael Tiovalen struct sbiret sbi_send_ipi(unsigned long hart_mask, unsigned long hart_mask_base) 1326489b8b0SJames Raphael Tiovalen { 1336489b8b0SJames Raphael Tiovalen return sbi_ecall(SBI_EXT_IPI, SBI_EXT_IPI_SEND_IPI, hart_mask, hart_mask_base, 0, 0, 0, 0); 1346489b8b0SJames Raphael Tiovalen } 1356489b8b0SJames Raphael Tiovalen 13625475fa5SAndrew Jones struct sbiret sbi_send_ipi_cpu(int cpu) 13725475fa5SAndrew Jones { 13825475fa5SAndrew Jones return sbi_send_ipi(1UL, cpus[cpu].hartid); 13925475fa5SAndrew Jones } 14025475fa5SAndrew Jones 14156ca8093SAndrew Jones struct sbiret sbi_send_ipi_broadcast(void) 14256ca8093SAndrew Jones { 14356ca8093SAndrew Jones return sbi_send_ipi(0, -1UL); 14456ca8093SAndrew Jones } 14556ca8093SAndrew Jones 14625475fa5SAndrew Jones struct sbiret sbi_send_ipi_cpumask(const cpumask_t *mask) 14725475fa5SAndrew Jones { 14825475fa5SAndrew Jones struct sbiret ret; 14925475fa5SAndrew Jones cpumask_t tmp; 15025475fa5SAndrew Jones 15125475fa5SAndrew Jones if (cpumask_full(mask)) 15256ca8093SAndrew Jones return sbi_send_ipi_broadcast(); 15325475fa5SAndrew Jones 15425475fa5SAndrew Jones cpumask_copy(&tmp, mask); 15525475fa5SAndrew Jones 15625475fa5SAndrew Jones while (!cpumask_empty(&tmp)) { 15725475fa5SAndrew Jones unsigned long base = ULONG_MAX; 15825475fa5SAndrew Jones unsigned long mask = 0; 15925475fa5SAndrew Jones int cpu; 16025475fa5SAndrew Jones 16125475fa5SAndrew Jones for_each_cpu(cpu, &tmp) { 16225475fa5SAndrew Jones if (base > cpus[cpu].hartid) 16325475fa5SAndrew Jones base = cpus[cpu].hartid; 16425475fa5SAndrew Jones } 16525475fa5SAndrew Jones 16625475fa5SAndrew Jones for_each_cpu(cpu, &tmp) { 16725475fa5SAndrew Jones if (cpus[cpu].hartid < base + BITS_PER_LONG) { 16825475fa5SAndrew Jones mask |= 1UL << (cpus[cpu].hartid - base); 16925475fa5SAndrew Jones cpumask_clear_cpu(cpu, &tmp); 17025475fa5SAndrew Jones } 17125475fa5SAndrew Jones } 17225475fa5SAndrew Jones 17325475fa5SAndrew Jones ret = sbi_send_ipi(mask, base); 17425475fa5SAndrew Jones if (ret.error) 17525475fa5SAndrew Jones break; 17625475fa5SAndrew Jones } 17725475fa5SAndrew Jones 17825475fa5SAndrew Jones return ret; 17925475fa5SAndrew Jones } 18025475fa5SAndrew Jones 1819340e4b7SAndrew Jones struct sbiret sbi_set_timer(unsigned long stime_value) 1829340e4b7SAndrew Jones { 1839340e4b7SAndrew Jones return sbi_ecall(SBI_EXT_TIME, SBI_EXT_TIME_SET_TIMER, stime_value, 0, 0, 0, 0, 0); 1849340e4b7SAndrew Jones } 1859340e4b7SAndrew Jones 186*a3fc8778SAndrew Jones struct sbiret sbi_get_imp_version(void) 187*a3fc8778SAndrew Jones { 188*a3fc8778SAndrew Jones return sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_IMP_VERSION, 0, 0, 0, 0, 0, 0); 189*a3fc8778SAndrew Jones } 190*a3fc8778SAndrew Jones 191*a3fc8778SAndrew Jones struct sbiret sbi_get_imp_id(void) 192*a3fc8778SAndrew Jones { 193*a3fc8778SAndrew Jones return sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_IMP_ID, 0, 0, 0, 0, 0, 0); 194*a3fc8778SAndrew Jones } 195*a3fc8778SAndrew Jones 196*a3fc8778SAndrew Jones unsigned long __sbi_get_imp_version(void) 197240729eeSClément Léger { 198240729eeSClément Léger struct sbiret ret; 199240729eeSClément Léger 200*a3fc8778SAndrew Jones ret = sbi_get_imp_version(); 201240729eeSClément Léger assert(!ret.error); 202240729eeSClément Léger 203240729eeSClément Léger return ret.value; 204240729eeSClément Léger } 205240729eeSClément Léger 206*a3fc8778SAndrew Jones unsigned long __sbi_get_imp_id(void) 207240729eeSClément Léger { 208240729eeSClément Léger struct sbiret ret; 209240729eeSClément Léger 210*a3fc8778SAndrew Jones ret = sbi_get_imp_id(); 211240729eeSClément Léger assert(!ret.error); 212240729eeSClément Léger 213240729eeSClément Léger return ret.value; 214240729eeSClément Léger } 215240729eeSClément Léger 216b9d58c27SClément Léger struct sbiret sbi_get_spec_version(void) 217b9d58c27SClément Léger { 218b9d58c27SClément Léger return sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_GET_SPEC_VERSION, 0, 0, 0, 0, 0, 0); 219b9d58c27SClément Léger } 220b9d58c27SClément Léger 2217040d2a9SJames Raphael Tiovalen long sbi_probe(int ext) 2227040d2a9SJames Raphael Tiovalen { 2237040d2a9SJames Raphael Tiovalen struct sbiret ret; 2247040d2a9SJames Raphael Tiovalen 225b9d58c27SClément Léger ret = sbi_get_spec_version(); 226b9d58c27SClément Léger assert(!ret.error && (ret.value & SBI_SPEC_VERSION_MASK) >= sbi_mk_version(0, 2)); 2277040d2a9SJames Raphael Tiovalen 2287040d2a9SJames Raphael Tiovalen ret = sbi_ecall(SBI_EXT_BASE, SBI_EXT_BASE_PROBE_EXT, ext, 0, 0, 0, 0, 0); 2297040d2a9SJames Raphael Tiovalen assert(!ret.error); 2307040d2a9SJames Raphael Tiovalen 2317040d2a9SJames Raphael Tiovalen return ret.value; 2327040d2a9SJames Raphael Tiovalen } 233