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