1 // SPDX-License-Identifier: GPL-2.0-only 2 /* 3 * HSM extension and cpu_ops implementation. 4 * 5 * Copyright (c) 2020 Western Digital Corporation or its affiliates. 6 */ 7 8 #include <linux/init.h> 9 #include <linux/mm.h> 10 #include <asm/cpu_ops.h> 11 #include <asm/sbi.h> 12 #include <asm/smp.h> 13 14 extern char secondary_start_sbi[]; 15 const struct cpu_operations cpu_ops_sbi; 16 17 static int sbi_hsm_hart_start(unsigned long hartid, unsigned long saddr, 18 unsigned long priv) 19 { 20 struct sbiret ret; 21 22 ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_START, 23 hartid, saddr, priv, 0, 0, 0); 24 if (ret.error) 25 return sbi_err_map_linux_errno(ret.error); 26 else 27 return 0; 28 } 29 30 #ifdef CONFIG_HOTPLUG_CPU 31 static int sbi_hsm_hart_stop(void) 32 { 33 struct sbiret ret; 34 35 ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STOP, 0, 0, 0, 0, 0, 0); 36 37 if (ret.error) 38 return sbi_err_map_linux_errno(ret.error); 39 else 40 return 0; 41 } 42 43 static int sbi_hsm_hart_get_status(unsigned long hartid) 44 { 45 struct sbiret ret; 46 47 ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STATUS, 48 hartid, 0, 0, 0, 0, 0); 49 if (ret.error) 50 return sbi_err_map_linux_errno(ret.error); 51 else 52 return ret.value; 53 } 54 #endif 55 56 static int sbi_cpu_start(unsigned int cpuid, struct task_struct *tidle) 57 { 58 int rc; 59 unsigned long boot_addr = __pa_symbol(secondary_start_sbi); 60 int hartid = cpuid_to_hartid_map(cpuid); 61 62 cpu_update_secondary_bootdata(cpuid, tidle); 63 rc = sbi_hsm_hart_start(hartid, boot_addr, 0); 64 65 return rc; 66 } 67 68 static int sbi_cpu_prepare(unsigned int cpuid) 69 { 70 if (!cpu_ops_sbi.cpu_start) { 71 pr_err("cpu start method not defined for CPU [%d]\n", cpuid); 72 return -ENODEV; 73 } 74 return 0; 75 } 76 77 #ifdef CONFIG_HOTPLUG_CPU 78 static int sbi_cpu_disable(unsigned int cpuid) 79 { 80 if (!cpu_ops_sbi.cpu_stop) 81 return -EOPNOTSUPP; 82 return 0; 83 } 84 85 static void sbi_cpu_stop(void) 86 { 87 int ret; 88 89 ret = sbi_hsm_hart_stop(); 90 pr_crit("Unable to stop the cpu %u (%d)\n", smp_processor_id(), ret); 91 } 92 93 static int sbi_cpu_is_stopped(unsigned int cpuid) 94 { 95 int rc; 96 int hartid = cpuid_to_hartid_map(cpuid); 97 98 rc = sbi_hsm_hart_get_status(hartid); 99 100 if (rc == SBI_HSM_HART_STATUS_STOPPED) 101 return 0; 102 return rc; 103 } 104 #endif 105 106 const struct cpu_operations cpu_ops_sbi = { 107 .name = "sbi", 108 .cpu_prepare = sbi_cpu_prepare, 109 .cpu_start = sbi_cpu_start, 110 #ifdef CONFIG_HOTPLUG_CPU 111 .cpu_disable = sbi_cpu_disable, 112 .cpu_stop = sbi_cpu_stop, 113 .cpu_is_stopped = sbi_cpu_is_stopped, 114 #endif 115 }; 116