1 /* $NetBSD: psci.c,v 1.9 2026/05/15 22:44:58 jmcneill Exp $ */ 2 3 /*- 4 * Copyright (c) 2017-2026 Jared McNeill <jmcneill (at) invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/cdefs.h> 30 __KERNEL_RCSID(0, "$NetBSD: psci.c,v 1.9 2026/05/15 22:44:58 jmcneill Exp $"); 31 32 #include <sys/param.h> 33 #include <sys/bus.h> 34 #include <sys/cpu.h> 35 #include <sys/device.h> 36 #include <sys/xcall.h> 37 38 #include <arm/arm/psci.h> 39 40 /* PSCI methods */ 41 #define PSCI_VERSION 0x84000000 42 #define PSCI_CPU_OFF 0x84000002 43 #define PSCI_SYSTEM_OFF 0x84000008 44 #define PSCI_SYSTEM_RESET 0x84000009 45 #define PSCI_FEATURES 0x8400000a 46 47 #if defined(__aarch64__) 48 #define PSCI_CPU_SUSPEND 0xc4000001 49 #define PSCI_CPU_ON 0xc4000003 50 #define PSCI_AFFINITY_INFO 0xc4000004 51 #else 52 #define PSCI_CPU_SUSPEND 0x84000001 53 #define PSCI_CPU_ON 0x84000003 54 #define PSCI_AFFINITY_INFO 0x84000004 55 #endif 56 57 static psci_fn psci_call_fn; 58 59 static uint32_t psci_functions[PSCI_FUNC_MAX] = { 60 [PSCI_FUNC_VERSION] = PSCI_VERSION, 61 [PSCI_FUNC_SYSTEM_OFF] = PSCI_SYSTEM_OFF, 62 [PSCI_FUNC_SYSTEM_RESET] = PSCI_SYSTEM_RESET, 63 [PSCI_FUNC_CPU_SUSPEND] = PSCI_CPU_SUSPEND, 64 [PSCI_FUNC_CPU_ON] = PSCI_CPU_ON, 65 [PSCI_FUNC_CPU_OFF] = PSCI_CPU_OFF, 66 [PSCI_FUNC_AFFINITY_INFO] = PSCI_AFFINITY_INFO, 67 [PSCI_FUNC_FEATURES] = PSCI_FEATURES, 68 }; 69 70 int 71 psci_call(register_t fid, register_t arg1, register_t arg2, register_t arg3) 72 { 73 KASSERT(psci_call_fn != NULL); 74 75 if (fid == 0) 76 return PSCI_NOT_SUPPORTED; 77 78 return psci_call_fn(fid, arg1, arg2, arg3); 79 } 80 81 enum psci_conduit 82 psci_conduit(void) 83 { 84 if (psci_call_fn == psci_call_smc) { 85 return PSCI_CONDUIT_SMC; 86 } else if (psci_call_fn == psci_call_hvc) { 87 return PSCI_CONDUIT_HVC; 88 } else { 89 return PSCI_CONDUIT_NONE; 90 } 91 } 92 93 uint32_t 94 psci_version(void) 95 { 96 if (psci_functions[PSCI_FUNC_VERSION] == 0) { 97 /* Pre-0.2 implementation */ 98 return __SHIFTIN(0, PSCI_VERSION_MAJOR) | 99 __SHIFTIN(1, PSCI_VERSION_MINOR); 100 } 101 102 return psci_call(psci_functions[PSCI_FUNC_VERSION], 0, 0, 0); 103 } 104 105 int 106 psci_cpu_on(register_t target_cpu, register_t entry_point_address, 107 register_t context_id) 108 { 109 return psci_call(psci_functions[PSCI_FUNC_CPU_ON], target_cpu, 110 entry_point_address, context_id); 111 } 112 113 int 114 psci_cpu_off(void) 115 { 116 return psci_call(psci_functions[PSCI_FUNC_CPU_OFF], 0, 0, 0); 117 } 118 119 int 120 psci_cpu_suspend(uint32_t power_state) 121 { 122 return psci_call(psci_functions[PSCI_FUNC_CPU_SUSPEND], power_state, 123 0, 0); 124 } 125 126 int 127 psci_affinity_info(uint64_t target_affinity, uint32_t lowest_affinity_level) 128 { 129 return psci_call(psci_functions[PSCI_FUNC_AFFINITY_INFO], 130 target_affinity, lowest_affinity_level, 0); 131 } 132 133 134 void 135 psci_system_off(void) 136 { 137 psci_call(psci_functions[PSCI_FUNC_SYSTEM_OFF], 0, 0, 0); 138 } 139 140 void 141 psci_system_reset(void) 142 { 143 psci_call(psci_functions[PSCI_FUNC_SYSTEM_RESET], 0, 0, 0); 144 } 145 146 int 147 psci_features(uint32_t fid) 148 { 149 if (psci_functions[PSCI_FUNC_FEATURES] == 0) { 150 return PSCI_NOT_SUPPORTED; 151 } 152 return psci_call(psci_functions[PSCI_FUNC_FEATURES], fid, 0, 0); 153 } 154 155 void 156 psci_init(psci_fn fn) 157 { 158 psci_call_fn = fn; 159 } 160 161 bool 162 psci_available(void) 163 { 164 return psci_call_fn != NULL; 165 } 166 167 void 168 psci_clearfunc(void) 169 { 170 for (int i = 0; i < PSCI_FUNC_MAX; i++) 171 psci_setfunc(i, 0); 172 } 173 174 void 175 psci_setfunc(enum psci_function func, uint32_t fid) 176 { 177 psci_functions[func] = fid; 178 } 179 180 static void 181 psci_cpu_off_xcall(void *arg1, void *arg2) 182 { 183 if (curcpu() != arg1) { 184 aprint_debug("%s: power down\n", cpu_name(curcpu())); 185 psci_cpu_off(); 186 printf("WARNING: %s power down failed\n", cpu_name(curcpu())); 187 } 188 } 189 190 void 191 psci_poweroff(void) 192 { 193 if (ncpuonline > 1) { 194 CPU_INFO_ITERATOR cii; 195 struct cpu_info *ci; 196 int ret; 197 198 /* Call PSCI_CPU_OFF on all CPUs except for ourselves. */ 199 xc_broadcast(0, psci_cpu_off_xcall, curcpu(), NULL); 200 201 /* Wait for all CPUs to enter OFF state. */ 202 for (CPU_INFO_FOREACH(cii, ci)) { 203 if (ci == curcpu()) { 204 continue; 205 } 206 for (;;) { 207 ret = psci_affinity_info(ci->ci_cpuid, 0); 208 if (ret < 0) { 209 printf("WARNING: %s state %d\n", 210 cpu_name(ci), ret); 211 } 212 if (ret != PSCI_AFFINITY_INFO_ON) { 213 break; 214 } 215 } 216 } 217 } 218 219 /* Power off the system. */ 220 psci_system_off(); 221 } 222