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 <linux/sched/task_stack.h> 11 #include <asm/cpu_ops.h> 12 #include <asm/cpu_ops_sbi.h> 13 #include <asm/sbi.h> 14 #include <asm/smp.h> 15 16 extern char secondary_start_sbi[]; 17 const struct cpu_operations cpu_ops_sbi; 18 19 /* 20 * Ordered booting via HSM brings one cpu at a time. However, cpu hotplug can 21 * be invoked from multiple threads in parallel. Define an array of boot data 22 * to handle that. 23 */ 24 static struct sbi_hart_boot_data boot_data[NR_CPUS]; 25 26 static int sbi_hsm_hart_start(unsigned long hartid, unsigned long saddr, 27 unsigned long priv) 28 { 29 struct sbiret ret; 30 31 ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_START, 32 hartid, saddr, priv, 0, 0, 0); 33 34 return sbi_err_map_linux_errno(ret.error); 35 } 36 37 #ifdef CONFIG_HOTPLUG_CPU 38 static int sbi_hsm_hart_stop(void) 39 { 40 struct sbiret ret; 41 42 ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STOP, 0, 0, 0, 0, 0, 0); 43 44 return sbi_err_map_linux_errno(ret.error); 45 } 46 47 static int sbi_hsm_hart_get_status(unsigned long hartid) 48 { 49 struct sbiret ret; 50 51 ret = sbi_ecall(SBI_EXT_HSM, SBI_EXT_HSM_HART_STATUS, 52 hartid, 0, 0, 0, 0, 0); 53 if (ret.error) 54 return sbi_err_map_linux_errno(ret.error); 55 else 56 return ret.value; 57 } 58 #endif 59 60 static int sbi_cpu_start(unsigned int cpuid, struct task_struct *tidle) 61 { 62 unsigned long boot_addr = __pa_symbol(secondary_start_sbi); 63 unsigned long hartid = cpuid_to_hartid_map(cpuid); 64 unsigned long hsm_data; 65 struct sbi_hart_boot_data *bdata = &boot_data[cpuid]; 66 67 /* Make sure tidle is updated */ 68 smp_mb(); 69 bdata->task_ptr = tidle; 70 bdata->stack_ptr = task_pt_regs(tidle); 71 /* Make sure boot data is updated */ 72 smp_mb(); 73 hsm_data = __pa(bdata); 74 return sbi_hsm_hart_start(hartid, boot_addr, hsm_data); 75 } 76 77 #ifdef CONFIG_HOTPLUG_CPU 78 static void sbi_cpu_stop(void) 79 { 80 int ret; 81 82 ret = sbi_hsm_hart_stop(); 83 pr_crit("Unable to stop the cpu %d (%d)\n", smp_processor_id(), ret); 84 } 85 86 static bool sbi_cpu_is_stopped(unsigned int cpuid) 87 { 88 int rc; 89 unsigned long hartid = cpuid_to_hartid_map(cpuid); 90 91 rc = sbi_hsm_hart_get_status(hartid); 92 93 if (rc != SBI_HSM_STATE_STOPPED) { 94 pr_warn("HART%lu isn't stopped; status %d\n", hartid, rc); 95 return false; 96 } 97 98 return true; 99 } 100 #endif 101 102 const struct cpu_operations cpu_ops_sbi = { 103 .cpu_start = sbi_cpu_start, 104 #ifdef CONFIG_HOTPLUG_CPU 105 .cpu_stop = sbi_cpu_stop, 106 .cpu_is_stopped = sbi_cpu_is_stopped, 107 #endif 108 }; 109