18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * 48c2ecf20Sopenharmony_ci * Copyright (C) 2015 ARM Limited 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#define pr_fmt(fmt) "psci: " fmt 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/acpi.h> 108c2ecf20Sopenharmony_ci#include <linux/arm-smccc.h> 118c2ecf20Sopenharmony_ci#include <linux/cpuidle.h> 128c2ecf20Sopenharmony_ci#include <linux/errno.h> 138c2ecf20Sopenharmony_ci#include <linux/linkage.h> 148c2ecf20Sopenharmony_ci#include <linux/of.h> 158c2ecf20Sopenharmony_ci#include <linux/pm.h> 168c2ecf20Sopenharmony_ci#include <linux/printk.h> 178c2ecf20Sopenharmony_ci#include <linux/psci.h> 188c2ecf20Sopenharmony_ci#include <linux/reboot.h> 198c2ecf20Sopenharmony_ci#include <linux/slab.h> 208c2ecf20Sopenharmony_ci#include <linux/suspend.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#include <uapi/linux/psci.h> 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ci#include <asm/cpuidle.h> 258c2ecf20Sopenharmony_ci#include <asm/cputype.h> 268c2ecf20Sopenharmony_ci#include <asm/system_misc.h> 278c2ecf20Sopenharmony_ci#include <asm/smp_plat.h> 288c2ecf20Sopenharmony_ci#include <asm/suspend.h> 298c2ecf20Sopenharmony_ci 308c2ecf20Sopenharmony_ci/* 318c2ecf20Sopenharmony_ci * While a 64-bit OS can make calls with SMC32 calling conventions, for some 328c2ecf20Sopenharmony_ci * calls it is necessary to use SMC64 to pass or return 64-bit values. 338c2ecf20Sopenharmony_ci * For such calls PSCI_FN_NATIVE(version, name) will choose the appropriate 348c2ecf20Sopenharmony_ci * (native-width) function ID. 358c2ecf20Sopenharmony_ci */ 368c2ecf20Sopenharmony_ci#ifdef CONFIG_64BIT 378c2ecf20Sopenharmony_ci#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN64_##name 388c2ecf20Sopenharmony_ci#else 398c2ecf20Sopenharmony_ci#define PSCI_FN_NATIVE(version, name) PSCI_##version##_FN_##name 408c2ecf20Sopenharmony_ci#endif 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci/* 438c2ecf20Sopenharmony_ci * The CPU any Trusted OS is resident on. The trusted OS may reject CPU_OFF 448c2ecf20Sopenharmony_ci * calls to its resident CPU, so we must avoid issuing those. We never migrate 458c2ecf20Sopenharmony_ci * a Trusted OS even if it claims to be capable of migration -- doing so will 468c2ecf20Sopenharmony_ci * require cooperation with a Trusted OS driver. 478c2ecf20Sopenharmony_ci */ 488c2ecf20Sopenharmony_cistatic int resident_cpu = -1; 498c2ecf20Sopenharmony_cistruct psci_operations psci_ops; 508c2ecf20Sopenharmony_cistatic enum arm_smccc_conduit psci_conduit = SMCCC_CONDUIT_NONE; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_cibool psci_tos_resident_on(int cpu) 538c2ecf20Sopenharmony_ci{ 548c2ecf20Sopenharmony_ci return cpu == resident_cpu; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_citypedef unsigned long (psci_fn)(unsigned long, unsigned long, 588c2ecf20Sopenharmony_ci unsigned long, unsigned long); 598c2ecf20Sopenharmony_cistatic psci_fn *invoke_psci_fn; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_cienum psci_function { 628c2ecf20Sopenharmony_ci PSCI_FN_CPU_SUSPEND, 638c2ecf20Sopenharmony_ci PSCI_FN_CPU_ON, 648c2ecf20Sopenharmony_ci PSCI_FN_CPU_OFF, 658c2ecf20Sopenharmony_ci PSCI_FN_MIGRATE, 668c2ecf20Sopenharmony_ci PSCI_FN_MAX, 678c2ecf20Sopenharmony_ci}; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_cistatic u32 psci_function_id[PSCI_FN_MAX]; 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_ci#define PSCI_0_2_POWER_STATE_MASK \ 728c2ecf20Sopenharmony_ci (PSCI_0_2_POWER_STATE_ID_MASK | \ 738c2ecf20Sopenharmony_ci PSCI_0_2_POWER_STATE_TYPE_MASK | \ 748c2ecf20Sopenharmony_ci PSCI_0_2_POWER_STATE_AFFL_MASK) 758c2ecf20Sopenharmony_ci 768c2ecf20Sopenharmony_ci#define PSCI_1_0_EXT_POWER_STATE_MASK \ 778c2ecf20Sopenharmony_ci (PSCI_1_0_EXT_POWER_STATE_ID_MASK | \ 788c2ecf20Sopenharmony_ci PSCI_1_0_EXT_POWER_STATE_TYPE_MASK) 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_cistatic u32 psci_cpu_suspend_feature; 818c2ecf20Sopenharmony_cistatic bool psci_system_reset2_supported; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cistatic inline bool psci_has_ext_power_state(void) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci return psci_cpu_suspend_feature & 868c2ecf20Sopenharmony_ci PSCI_1_0_FEATURES_CPU_SUSPEND_PF_MASK; 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_cibool psci_has_osi_support(void) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci return psci_cpu_suspend_feature & PSCI_1_0_OS_INITIATED; 928c2ecf20Sopenharmony_ci} 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_cistatic inline bool psci_power_state_loses_context(u32 state) 958c2ecf20Sopenharmony_ci{ 968c2ecf20Sopenharmony_ci const u32 mask = psci_has_ext_power_state() ? 978c2ecf20Sopenharmony_ci PSCI_1_0_EXT_POWER_STATE_TYPE_MASK : 988c2ecf20Sopenharmony_ci PSCI_0_2_POWER_STATE_TYPE_MASK; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci return state & mask; 1018c2ecf20Sopenharmony_ci} 1028c2ecf20Sopenharmony_ci 1038c2ecf20Sopenharmony_cibool psci_power_state_is_valid(u32 state) 1048c2ecf20Sopenharmony_ci{ 1058c2ecf20Sopenharmony_ci const u32 valid_mask = psci_has_ext_power_state() ? 1068c2ecf20Sopenharmony_ci PSCI_1_0_EXT_POWER_STATE_MASK : 1078c2ecf20Sopenharmony_ci PSCI_0_2_POWER_STATE_MASK; 1088c2ecf20Sopenharmony_ci 1098c2ecf20Sopenharmony_ci return !(state & ~valid_mask); 1108c2ecf20Sopenharmony_ci} 1118c2ecf20Sopenharmony_ci 1128c2ecf20Sopenharmony_cistatic unsigned long __invoke_psci_fn_hvc(unsigned long function_id, 1138c2ecf20Sopenharmony_ci unsigned long arg0, unsigned long arg1, 1148c2ecf20Sopenharmony_ci unsigned long arg2) 1158c2ecf20Sopenharmony_ci{ 1168c2ecf20Sopenharmony_ci struct arm_smccc_res res; 1178c2ecf20Sopenharmony_ci 1188c2ecf20Sopenharmony_ci arm_smccc_hvc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); 1198c2ecf20Sopenharmony_ci return res.a0; 1208c2ecf20Sopenharmony_ci} 1218c2ecf20Sopenharmony_ci 1228c2ecf20Sopenharmony_cistatic unsigned long __invoke_psci_fn_smc(unsigned long function_id, 1238c2ecf20Sopenharmony_ci unsigned long arg0, unsigned long arg1, 1248c2ecf20Sopenharmony_ci unsigned long arg2) 1258c2ecf20Sopenharmony_ci{ 1268c2ecf20Sopenharmony_ci struct arm_smccc_res res; 1278c2ecf20Sopenharmony_ci 1288c2ecf20Sopenharmony_ci arm_smccc_smc(function_id, arg0, arg1, arg2, 0, 0, 0, 0, &res); 1298c2ecf20Sopenharmony_ci return res.a0; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic int psci_to_linux_errno(int errno) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci switch (errno) { 1358c2ecf20Sopenharmony_ci case PSCI_RET_SUCCESS: 1368c2ecf20Sopenharmony_ci return 0; 1378c2ecf20Sopenharmony_ci case PSCI_RET_NOT_SUPPORTED: 1388c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 1398c2ecf20Sopenharmony_ci case PSCI_RET_INVALID_PARAMS: 1408c2ecf20Sopenharmony_ci case PSCI_RET_INVALID_ADDRESS: 1418c2ecf20Sopenharmony_ci return -EINVAL; 1428c2ecf20Sopenharmony_ci case PSCI_RET_DENIED: 1438c2ecf20Sopenharmony_ci return -EPERM; 1448c2ecf20Sopenharmony_ci }; 1458c2ecf20Sopenharmony_ci 1468c2ecf20Sopenharmony_ci return -EINVAL; 1478c2ecf20Sopenharmony_ci} 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_cistatic u32 psci_get_version(void) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci return invoke_psci_fn(PSCI_0_2_FN_PSCI_VERSION, 0, 0, 0); 1528c2ecf20Sopenharmony_ci} 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ciint psci_set_osi_mode(bool enable) 1558c2ecf20Sopenharmony_ci{ 1568c2ecf20Sopenharmony_ci unsigned long suspend_mode; 1578c2ecf20Sopenharmony_ci int err; 1588c2ecf20Sopenharmony_ci 1598c2ecf20Sopenharmony_ci suspend_mode = enable ? PSCI_1_0_SUSPEND_MODE_OSI : 1608c2ecf20Sopenharmony_ci PSCI_1_0_SUSPEND_MODE_PC; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci err = invoke_psci_fn(PSCI_1_0_FN_SET_SUSPEND_MODE, suspend_mode, 0, 0); 1638c2ecf20Sopenharmony_ci return psci_to_linux_errno(err); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int psci_cpu_suspend(u32 state, unsigned long entry_point) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci int err; 1698c2ecf20Sopenharmony_ci u32 fn; 1708c2ecf20Sopenharmony_ci 1718c2ecf20Sopenharmony_ci fn = psci_function_id[PSCI_FN_CPU_SUSPEND]; 1728c2ecf20Sopenharmony_ci err = invoke_psci_fn(fn, state, entry_point, 0); 1738c2ecf20Sopenharmony_ci return psci_to_linux_errno(err); 1748c2ecf20Sopenharmony_ci} 1758c2ecf20Sopenharmony_ci 1768c2ecf20Sopenharmony_cistatic int psci_cpu_off(u32 state) 1778c2ecf20Sopenharmony_ci{ 1788c2ecf20Sopenharmony_ci int err; 1798c2ecf20Sopenharmony_ci u32 fn; 1808c2ecf20Sopenharmony_ci 1818c2ecf20Sopenharmony_ci fn = psci_function_id[PSCI_FN_CPU_OFF]; 1828c2ecf20Sopenharmony_ci err = invoke_psci_fn(fn, state, 0, 0); 1838c2ecf20Sopenharmony_ci return psci_to_linux_errno(err); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ci 1868c2ecf20Sopenharmony_cistatic int psci_cpu_on(unsigned long cpuid, unsigned long entry_point) 1878c2ecf20Sopenharmony_ci{ 1888c2ecf20Sopenharmony_ci int err; 1898c2ecf20Sopenharmony_ci u32 fn; 1908c2ecf20Sopenharmony_ci 1918c2ecf20Sopenharmony_ci fn = psci_function_id[PSCI_FN_CPU_ON]; 1928c2ecf20Sopenharmony_ci err = invoke_psci_fn(fn, cpuid, entry_point, 0); 1938c2ecf20Sopenharmony_ci return psci_to_linux_errno(err); 1948c2ecf20Sopenharmony_ci} 1958c2ecf20Sopenharmony_ci 1968c2ecf20Sopenharmony_cistatic int psci_migrate(unsigned long cpuid) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci int err; 1998c2ecf20Sopenharmony_ci u32 fn; 2008c2ecf20Sopenharmony_ci 2018c2ecf20Sopenharmony_ci fn = psci_function_id[PSCI_FN_MIGRATE]; 2028c2ecf20Sopenharmony_ci err = invoke_psci_fn(fn, cpuid, 0, 0); 2038c2ecf20Sopenharmony_ci return psci_to_linux_errno(err); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic int psci_affinity_info(unsigned long target_affinity, 2078c2ecf20Sopenharmony_ci unsigned long lowest_affinity_level) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci return invoke_psci_fn(PSCI_FN_NATIVE(0_2, AFFINITY_INFO), 2108c2ecf20Sopenharmony_ci target_affinity, lowest_affinity_level, 0); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic int psci_migrate_info_type(void) 2148c2ecf20Sopenharmony_ci{ 2158c2ecf20Sopenharmony_ci return invoke_psci_fn(PSCI_0_2_FN_MIGRATE_INFO_TYPE, 0, 0, 0); 2168c2ecf20Sopenharmony_ci} 2178c2ecf20Sopenharmony_ci 2188c2ecf20Sopenharmony_cistatic unsigned long psci_migrate_info_up_cpu(void) 2198c2ecf20Sopenharmony_ci{ 2208c2ecf20Sopenharmony_ci return invoke_psci_fn(PSCI_FN_NATIVE(0_2, MIGRATE_INFO_UP_CPU), 2218c2ecf20Sopenharmony_ci 0, 0, 0); 2228c2ecf20Sopenharmony_ci} 2238c2ecf20Sopenharmony_ci 2248c2ecf20Sopenharmony_cistatic void set_conduit(enum arm_smccc_conduit conduit) 2258c2ecf20Sopenharmony_ci{ 2268c2ecf20Sopenharmony_ci switch (conduit) { 2278c2ecf20Sopenharmony_ci case SMCCC_CONDUIT_HVC: 2288c2ecf20Sopenharmony_ci invoke_psci_fn = __invoke_psci_fn_hvc; 2298c2ecf20Sopenharmony_ci break; 2308c2ecf20Sopenharmony_ci case SMCCC_CONDUIT_SMC: 2318c2ecf20Sopenharmony_ci invoke_psci_fn = __invoke_psci_fn_smc; 2328c2ecf20Sopenharmony_ci break; 2338c2ecf20Sopenharmony_ci default: 2348c2ecf20Sopenharmony_ci WARN(1, "Unexpected PSCI conduit %d\n", conduit); 2358c2ecf20Sopenharmony_ci } 2368c2ecf20Sopenharmony_ci 2378c2ecf20Sopenharmony_ci psci_conduit = conduit; 2388c2ecf20Sopenharmony_ci} 2398c2ecf20Sopenharmony_ci 2408c2ecf20Sopenharmony_cistatic int get_set_conduit_method(struct device_node *np) 2418c2ecf20Sopenharmony_ci{ 2428c2ecf20Sopenharmony_ci const char *method; 2438c2ecf20Sopenharmony_ci 2448c2ecf20Sopenharmony_ci pr_info("probing for conduit method from DT.\n"); 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci if (of_property_read_string(np, "method", &method)) { 2478c2ecf20Sopenharmony_ci pr_warn("missing \"method\" property\n"); 2488c2ecf20Sopenharmony_ci return -ENXIO; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci if (!strcmp("hvc", method)) { 2528c2ecf20Sopenharmony_ci set_conduit(SMCCC_CONDUIT_HVC); 2538c2ecf20Sopenharmony_ci } else if (!strcmp("smc", method)) { 2548c2ecf20Sopenharmony_ci set_conduit(SMCCC_CONDUIT_SMC); 2558c2ecf20Sopenharmony_ci } else { 2568c2ecf20Sopenharmony_ci pr_warn("invalid \"method\" property: %s\n", method); 2578c2ecf20Sopenharmony_ci return -EINVAL; 2588c2ecf20Sopenharmony_ci } 2598c2ecf20Sopenharmony_ci return 0; 2608c2ecf20Sopenharmony_ci} 2618c2ecf20Sopenharmony_ci 2628c2ecf20Sopenharmony_cistatic void psci_sys_reset(enum reboot_mode reboot_mode, const char *cmd) 2638c2ecf20Sopenharmony_ci{ 2648c2ecf20Sopenharmony_ci if ((reboot_mode == REBOOT_WARM || reboot_mode == REBOOT_SOFT) && 2658c2ecf20Sopenharmony_ci psci_system_reset2_supported) { 2668c2ecf20Sopenharmony_ci /* 2678c2ecf20Sopenharmony_ci * reset_type[31] = 0 (architectural) 2688c2ecf20Sopenharmony_ci * reset_type[30:0] = 0 (SYSTEM_WARM_RESET) 2698c2ecf20Sopenharmony_ci * cookie = 0 (ignored by the implementation) 2708c2ecf20Sopenharmony_ci */ 2718c2ecf20Sopenharmony_ci invoke_psci_fn(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2), 0, 0, 0); 2728c2ecf20Sopenharmony_ci } else { 2738c2ecf20Sopenharmony_ci invoke_psci_fn(PSCI_0_2_FN_SYSTEM_RESET, 0, 0, 0); 2748c2ecf20Sopenharmony_ci } 2758c2ecf20Sopenharmony_ci} 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_cistatic void psci_sys_poweroff(void) 2788c2ecf20Sopenharmony_ci{ 2798c2ecf20Sopenharmony_ci invoke_psci_fn(PSCI_0_2_FN_SYSTEM_OFF, 0, 0, 0); 2808c2ecf20Sopenharmony_ci} 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_cistatic int __init psci_features(u32 psci_func_id) 2838c2ecf20Sopenharmony_ci{ 2848c2ecf20Sopenharmony_ci return invoke_psci_fn(PSCI_1_0_FN_PSCI_FEATURES, 2858c2ecf20Sopenharmony_ci psci_func_id, 0, 0); 2868c2ecf20Sopenharmony_ci} 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci#ifdef CONFIG_CPU_IDLE 2898c2ecf20Sopenharmony_cistatic int psci_suspend_finisher(unsigned long state) 2908c2ecf20Sopenharmony_ci{ 2918c2ecf20Sopenharmony_ci u32 power_state = state; 2928c2ecf20Sopenharmony_ci phys_addr_t pa_cpu_resume = __pa_symbol(function_nocfi(cpu_resume)); 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci return psci_ops.cpu_suspend(power_state, pa_cpu_resume); 2958c2ecf20Sopenharmony_ci} 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ciint psci_cpu_suspend_enter(u32 state) 2988c2ecf20Sopenharmony_ci{ 2998c2ecf20Sopenharmony_ci int ret; 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci if (!psci_power_state_loses_context(state)) 3028c2ecf20Sopenharmony_ci ret = psci_ops.cpu_suspend(state, 0); 3038c2ecf20Sopenharmony_ci else 3048c2ecf20Sopenharmony_ci ret = cpu_suspend(state, psci_suspend_finisher); 3058c2ecf20Sopenharmony_ci 3068c2ecf20Sopenharmony_ci return ret; 3078c2ecf20Sopenharmony_ci} 3088c2ecf20Sopenharmony_ci#endif 3098c2ecf20Sopenharmony_ci 3108c2ecf20Sopenharmony_cistatic int psci_system_suspend(unsigned long unused) 3118c2ecf20Sopenharmony_ci{ 3128c2ecf20Sopenharmony_ci phys_addr_t pa_cpu_resume = __pa_symbol(function_nocfi(cpu_resume)); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci return invoke_psci_fn(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND), 3158c2ecf20Sopenharmony_ci pa_cpu_resume, 0, 0); 3168c2ecf20Sopenharmony_ci} 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_cistatic int psci_system_suspend_enter(suspend_state_t state) 3198c2ecf20Sopenharmony_ci{ 3208c2ecf20Sopenharmony_ci return cpu_suspend(0, psci_system_suspend); 3218c2ecf20Sopenharmony_ci} 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_cistatic const struct platform_suspend_ops psci_suspend_ops = { 3248c2ecf20Sopenharmony_ci .valid = suspend_valid_only_mem, 3258c2ecf20Sopenharmony_ci .enter = psci_system_suspend_enter, 3268c2ecf20Sopenharmony_ci}; 3278c2ecf20Sopenharmony_ci 3288c2ecf20Sopenharmony_cistatic void __init psci_init_system_reset2(void) 3298c2ecf20Sopenharmony_ci{ 3308c2ecf20Sopenharmony_ci int ret; 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci ret = psci_features(PSCI_FN_NATIVE(1_1, SYSTEM_RESET2)); 3338c2ecf20Sopenharmony_ci 3348c2ecf20Sopenharmony_ci if (ret != PSCI_RET_NOT_SUPPORTED) 3358c2ecf20Sopenharmony_ci psci_system_reset2_supported = true; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic void __init psci_init_system_suspend(void) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci int ret; 3418c2ecf20Sopenharmony_ci 3428c2ecf20Sopenharmony_ci if (!IS_ENABLED(CONFIG_SUSPEND)) 3438c2ecf20Sopenharmony_ci return; 3448c2ecf20Sopenharmony_ci 3458c2ecf20Sopenharmony_ci ret = psci_features(PSCI_FN_NATIVE(1_0, SYSTEM_SUSPEND)); 3468c2ecf20Sopenharmony_ci 3478c2ecf20Sopenharmony_ci if (ret != PSCI_RET_NOT_SUPPORTED) 3488c2ecf20Sopenharmony_ci suspend_set_ops(&psci_suspend_ops); 3498c2ecf20Sopenharmony_ci} 3508c2ecf20Sopenharmony_ci 3518c2ecf20Sopenharmony_cistatic void __init psci_init_cpu_suspend(void) 3528c2ecf20Sopenharmony_ci{ 3538c2ecf20Sopenharmony_ci int feature = psci_features(psci_function_id[PSCI_FN_CPU_SUSPEND]); 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci if (feature != PSCI_RET_NOT_SUPPORTED) 3568c2ecf20Sopenharmony_ci psci_cpu_suspend_feature = feature; 3578c2ecf20Sopenharmony_ci} 3588c2ecf20Sopenharmony_ci 3598c2ecf20Sopenharmony_ci/* 3608c2ecf20Sopenharmony_ci * Detect the presence of a resident Trusted OS which may cause CPU_OFF to 3618c2ecf20Sopenharmony_ci * return DENIED (which would be fatal). 3628c2ecf20Sopenharmony_ci */ 3638c2ecf20Sopenharmony_cistatic void __init psci_init_migrate(void) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci unsigned long cpuid; 3668c2ecf20Sopenharmony_ci int type, cpu = -1; 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_ci type = psci_ops.migrate_info_type(); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_ci if (type == PSCI_0_2_TOS_MP) { 3718c2ecf20Sopenharmony_ci pr_info("Trusted OS migration not required\n"); 3728c2ecf20Sopenharmony_ci return; 3738c2ecf20Sopenharmony_ci } 3748c2ecf20Sopenharmony_ci 3758c2ecf20Sopenharmony_ci if (type == PSCI_RET_NOT_SUPPORTED) { 3768c2ecf20Sopenharmony_ci pr_info("MIGRATE_INFO_TYPE not supported.\n"); 3778c2ecf20Sopenharmony_ci return; 3788c2ecf20Sopenharmony_ci } 3798c2ecf20Sopenharmony_ci 3808c2ecf20Sopenharmony_ci if (type != PSCI_0_2_TOS_UP_MIGRATE && 3818c2ecf20Sopenharmony_ci type != PSCI_0_2_TOS_UP_NO_MIGRATE) { 3828c2ecf20Sopenharmony_ci pr_err("MIGRATE_INFO_TYPE returned unknown type (%d)\n", type); 3838c2ecf20Sopenharmony_ci return; 3848c2ecf20Sopenharmony_ci } 3858c2ecf20Sopenharmony_ci 3868c2ecf20Sopenharmony_ci cpuid = psci_migrate_info_up_cpu(); 3878c2ecf20Sopenharmony_ci if (cpuid & ~MPIDR_HWID_BITMASK) { 3888c2ecf20Sopenharmony_ci pr_warn("MIGRATE_INFO_UP_CPU reported invalid physical ID (0x%lx)\n", 3898c2ecf20Sopenharmony_ci cpuid); 3908c2ecf20Sopenharmony_ci return; 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci cpu = get_logical_index(cpuid); 3948c2ecf20Sopenharmony_ci resident_cpu = cpu >= 0 ? cpu : -1; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci pr_info("Trusted OS resident on physical CPU 0x%lx\n", cpuid); 3978c2ecf20Sopenharmony_ci} 3988c2ecf20Sopenharmony_ci 3998c2ecf20Sopenharmony_cistatic void __init psci_init_smccc(void) 4008c2ecf20Sopenharmony_ci{ 4018c2ecf20Sopenharmony_ci u32 ver = ARM_SMCCC_VERSION_1_0; 4028c2ecf20Sopenharmony_ci int feature; 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci feature = psci_features(ARM_SMCCC_VERSION_FUNC_ID); 4058c2ecf20Sopenharmony_ci 4068c2ecf20Sopenharmony_ci if (feature != PSCI_RET_NOT_SUPPORTED) { 4078c2ecf20Sopenharmony_ci u32 ret; 4088c2ecf20Sopenharmony_ci ret = invoke_psci_fn(ARM_SMCCC_VERSION_FUNC_ID, 0, 0, 0); 4098c2ecf20Sopenharmony_ci if (ret >= ARM_SMCCC_VERSION_1_1) { 4108c2ecf20Sopenharmony_ci arm_smccc_version_init(ret, psci_conduit); 4118c2ecf20Sopenharmony_ci ver = ret; 4128c2ecf20Sopenharmony_ci } 4138c2ecf20Sopenharmony_ci } 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci /* 4168c2ecf20Sopenharmony_ci * Conveniently, the SMCCC and PSCI versions are encoded the 4178c2ecf20Sopenharmony_ci * same way. No, this isn't accidental. 4188c2ecf20Sopenharmony_ci */ 4198c2ecf20Sopenharmony_ci pr_info("SMC Calling Convention v%d.%d\n", 4208c2ecf20Sopenharmony_ci PSCI_VERSION_MAJOR(ver), PSCI_VERSION_MINOR(ver)); 4218c2ecf20Sopenharmony_ci 4228c2ecf20Sopenharmony_ci} 4238c2ecf20Sopenharmony_ci 4248c2ecf20Sopenharmony_cistatic void __init psci_0_2_set_functions(void) 4258c2ecf20Sopenharmony_ci{ 4268c2ecf20Sopenharmony_ci pr_info("Using standard PSCI v0.2 function IDs\n"); 4278c2ecf20Sopenharmony_ci psci_ops.get_version = psci_get_version; 4288c2ecf20Sopenharmony_ci 4298c2ecf20Sopenharmony_ci psci_function_id[PSCI_FN_CPU_SUSPEND] = 4308c2ecf20Sopenharmony_ci PSCI_FN_NATIVE(0_2, CPU_SUSPEND); 4318c2ecf20Sopenharmony_ci psci_ops.cpu_suspend = psci_cpu_suspend; 4328c2ecf20Sopenharmony_ci 4338c2ecf20Sopenharmony_ci psci_function_id[PSCI_FN_CPU_OFF] = PSCI_0_2_FN_CPU_OFF; 4348c2ecf20Sopenharmony_ci psci_ops.cpu_off = psci_cpu_off; 4358c2ecf20Sopenharmony_ci 4368c2ecf20Sopenharmony_ci psci_function_id[PSCI_FN_CPU_ON] = PSCI_FN_NATIVE(0_2, CPU_ON); 4378c2ecf20Sopenharmony_ci psci_ops.cpu_on = psci_cpu_on; 4388c2ecf20Sopenharmony_ci 4398c2ecf20Sopenharmony_ci psci_function_id[PSCI_FN_MIGRATE] = PSCI_FN_NATIVE(0_2, MIGRATE); 4408c2ecf20Sopenharmony_ci psci_ops.migrate = psci_migrate; 4418c2ecf20Sopenharmony_ci 4428c2ecf20Sopenharmony_ci psci_ops.affinity_info = psci_affinity_info; 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci psci_ops.migrate_info_type = psci_migrate_info_type; 4458c2ecf20Sopenharmony_ci 4468c2ecf20Sopenharmony_ci arm_pm_restart = psci_sys_reset; 4478c2ecf20Sopenharmony_ci 4488c2ecf20Sopenharmony_ci pm_power_off = psci_sys_poweroff; 4498c2ecf20Sopenharmony_ci} 4508c2ecf20Sopenharmony_ci 4518c2ecf20Sopenharmony_ci/* 4528c2ecf20Sopenharmony_ci * Probe function for PSCI firmware versions >= 0.2 4538c2ecf20Sopenharmony_ci */ 4548c2ecf20Sopenharmony_cistatic int __init psci_probe(void) 4558c2ecf20Sopenharmony_ci{ 4568c2ecf20Sopenharmony_ci u32 ver = psci_get_version(); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_ci pr_info("PSCIv%d.%d detected in firmware.\n", 4598c2ecf20Sopenharmony_ci PSCI_VERSION_MAJOR(ver), 4608c2ecf20Sopenharmony_ci PSCI_VERSION_MINOR(ver)); 4618c2ecf20Sopenharmony_ci 4628c2ecf20Sopenharmony_ci if (PSCI_VERSION_MAJOR(ver) == 0 && PSCI_VERSION_MINOR(ver) < 2) { 4638c2ecf20Sopenharmony_ci pr_err("Conflicting PSCI version detected.\n"); 4648c2ecf20Sopenharmony_ci return -EINVAL; 4658c2ecf20Sopenharmony_ci } 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci psci_0_2_set_functions(); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci psci_init_migrate(); 4708c2ecf20Sopenharmony_ci 4718c2ecf20Sopenharmony_ci if (PSCI_VERSION_MAJOR(ver) >= 1) { 4728c2ecf20Sopenharmony_ci psci_init_smccc(); 4738c2ecf20Sopenharmony_ci psci_init_cpu_suspend(); 4748c2ecf20Sopenharmony_ci psci_init_system_suspend(); 4758c2ecf20Sopenharmony_ci psci_init_system_reset2(); 4768c2ecf20Sopenharmony_ci } 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return 0; 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ci 4818c2ecf20Sopenharmony_citypedef int (*psci_initcall_t)(const struct device_node *); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci/* 4848c2ecf20Sopenharmony_ci * PSCI init function for PSCI versions >=0.2 4858c2ecf20Sopenharmony_ci * 4868c2ecf20Sopenharmony_ci * Probe based on PSCI PSCI_VERSION function 4878c2ecf20Sopenharmony_ci */ 4888c2ecf20Sopenharmony_cistatic int __init psci_0_2_init(struct device_node *np) 4898c2ecf20Sopenharmony_ci{ 4908c2ecf20Sopenharmony_ci int err; 4918c2ecf20Sopenharmony_ci 4928c2ecf20Sopenharmony_ci err = get_set_conduit_method(np); 4938c2ecf20Sopenharmony_ci if (err) 4948c2ecf20Sopenharmony_ci return err; 4958c2ecf20Sopenharmony_ci 4968c2ecf20Sopenharmony_ci /* 4978c2ecf20Sopenharmony_ci * Starting with v0.2, the PSCI specification introduced a call 4988c2ecf20Sopenharmony_ci * (PSCI_VERSION) that allows probing the firmware version, so 4998c2ecf20Sopenharmony_ci * that PSCI function IDs and version specific initialization 5008c2ecf20Sopenharmony_ci * can be carried out according to the specific version reported 5018c2ecf20Sopenharmony_ci * by firmware 5028c2ecf20Sopenharmony_ci */ 5038c2ecf20Sopenharmony_ci return psci_probe(); 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ci 5068c2ecf20Sopenharmony_ci/* 5078c2ecf20Sopenharmony_ci * PSCI < v0.2 get PSCI Function IDs via DT. 5088c2ecf20Sopenharmony_ci */ 5098c2ecf20Sopenharmony_cistatic int __init psci_0_1_init(struct device_node *np) 5108c2ecf20Sopenharmony_ci{ 5118c2ecf20Sopenharmony_ci u32 id; 5128c2ecf20Sopenharmony_ci int err; 5138c2ecf20Sopenharmony_ci 5148c2ecf20Sopenharmony_ci err = get_set_conduit_method(np); 5158c2ecf20Sopenharmony_ci if (err) 5168c2ecf20Sopenharmony_ci return err; 5178c2ecf20Sopenharmony_ci 5188c2ecf20Sopenharmony_ci pr_info("Using PSCI v0.1 Function IDs from DT\n"); 5198c2ecf20Sopenharmony_ci 5208c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "cpu_suspend", &id)) { 5218c2ecf20Sopenharmony_ci psci_function_id[PSCI_FN_CPU_SUSPEND] = id; 5228c2ecf20Sopenharmony_ci psci_ops.cpu_suspend = psci_cpu_suspend; 5238c2ecf20Sopenharmony_ci } 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "cpu_off", &id)) { 5268c2ecf20Sopenharmony_ci psci_function_id[PSCI_FN_CPU_OFF] = id; 5278c2ecf20Sopenharmony_ci psci_ops.cpu_off = psci_cpu_off; 5288c2ecf20Sopenharmony_ci } 5298c2ecf20Sopenharmony_ci 5308c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "cpu_on", &id)) { 5318c2ecf20Sopenharmony_ci psci_function_id[PSCI_FN_CPU_ON] = id; 5328c2ecf20Sopenharmony_ci psci_ops.cpu_on = psci_cpu_on; 5338c2ecf20Sopenharmony_ci } 5348c2ecf20Sopenharmony_ci 5358c2ecf20Sopenharmony_ci if (!of_property_read_u32(np, "migrate", &id)) { 5368c2ecf20Sopenharmony_ci psci_function_id[PSCI_FN_MIGRATE] = id; 5378c2ecf20Sopenharmony_ci psci_ops.migrate = psci_migrate; 5388c2ecf20Sopenharmony_ci } 5398c2ecf20Sopenharmony_ci 5408c2ecf20Sopenharmony_ci return 0; 5418c2ecf20Sopenharmony_ci} 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_cistatic int __init psci_1_0_init(struct device_node *np) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci int err; 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci err = psci_0_2_init(np); 5488c2ecf20Sopenharmony_ci if (err) 5498c2ecf20Sopenharmony_ci return err; 5508c2ecf20Sopenharmony_ci 5518c2ecf20Sopenharmony_ci if (psci_has_osi_support()) { 5528c2ecf20Sopenharmony_ci pr_info("OSI mode supported.\n"); 5538c2ecf20Sopenharmony_ci 5548c2ecf20Sopenharmony_ci /* Default to PC mode. */ 5558c2ecf20Sopenharmony_ci psci_set_osi_mode(false); 5568c2ecf20Sopenharmony_ci } 5578c2ecf20Sopenharmony_ci 5588c2ecf20Sopenharmony_ci return 0; 5598c2ecf20Sopenharmony_ci} 5608c2ecf20Sopenharmony_ci 5618c2ecf20Sopenharmony_cistatic const struct of_device_id psci_of_match[] __initconst = { 5628c2ecf20Sopenharmony_ci { .compatible = "arm,psci", .data = psci_0_1_init}, 5638c2ecf20Sopenharmony_ci { .compatible = "arm,psci-0.2", .data = psci_0_2_init}, 5648c2ecf20Sopenharmony_ci { .compatible = "arm,psci-1.0", .data = psci_1_0_init}, 5658c2ecf20Sopenharmony_ci {}, 5668c2ecf20Sopenharmony_ci}; 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_ciint __init psci_dt_init(void) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci struct device_node *np; 5718c2ecf20Sopenharmony_ci const struct of_device_id *matched_np; 5728c2ecf20Sopenharmony_ci psci_initcall_t init_fn; 5738c2ecf20Sopenharmony_ci int ret; 5748c2ecf20Sopenharmony_ci 5758c2ecf20Sopenharmony_ci np = of_find_matching_node_and_match(NULL, psci_of_match, &matched_np); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (!np || !of_device_is_available(np)) 5788c2ecf20Sopenharmony_ci return -ENODEV; 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci init_fn = (psci_initcall_t)matched_np->data; 5818c2ecf20Sopenharmony_ci ret = init_fn(np); 5828c2ecf20Sopenharmony_ci 5838c2ecf20Sopenharmony_ci of_node_put(np); 5848c2ecf20Sopenharmony_ci return ret; 5858c2ecf20Sopenharmony_ci} 5868c2ecf20Sopenharmony_ci 5878c2ecf20Sopenharmony_ci#ifdef CONFIG_ACPI 5888c2ecf20Sopenharmony_ci/* 5898c2ecf20Sopenharmony_ci * We use PSCI 0.2+ when ACPI is deployed on ARM64 and it's 5908c2ecf20Sopenharmony_ci * explicitly clarified in SBBR 5918c2ecf20Sopenharmony_ci */ 5928c2ecf20Sopenharmony_ciint __init psci_acpi_init(void) 5938c2ecf20Sopenharmony_ci{ 5948c2ecf20Sopenharmony_ci if (!acpi_psci_present()) { 5958c2ecf20Sopenharmony_ci pr_info("is not implemented in ACPI.\n"); 5968c2ecf20Sopenharmony_ci return -EOPNOTSUPP; 5978c2ecf20Sopenharmony_ci } 5988c2ecf20Sopenharmony_ci 5998c2ecf20Sopenharmony_ci pr_info("probing for conduit method from ACPI.\n"); 6008c2ecf20Sopenharmony_ci 6018c2ecf20Sopenharmony_ci if (acpi_psci_use_hvc()) 6028c2ecf20Sopenharmony_ci set_conduit(SMCCC_CONDUIT_HVC); 6038c2ecf20Sopenharmony_ci else 6048c2ecf20Sopenharmony_ci set_conduit(SMCCC_CONDUIT_SMC); 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci return psci_probe(); 6078c2ecf20Sopenharmony_ci} 6088c2ecf20Sopenharmony_ci#endif 609